12d70b73aSGreg Kroah-Hartman /* 22d70b73aSGreg Kroah-Hartman * Samsung Laptop driver 32d70b73aSGreg Kroah-Hartman * 42d70b73aSGreg Kroah-Hartman * Copyright (C) 2009,2011 Greg Kroah-Hartman (gregkh@suse.de) 52d70b73aSGreg Kroah-Hartman * Copyright (C) 2009,2011 Novell Inc. 62d70b73aSGreg Kroah-Hartman * 72d70b73aSGreg Kroah-Hartman * This program is free software; you can redistribute it and/or modify it 82d70b73aSGreg Kroah-Hartman * under the terms of the GNU General Public License version 2 as published by 92d70b73aSGreg Kroah-Hartman * the Free Software Foundation. 102d70b73aSGreg Kroah-Hartman * 112d70b73aSGreg Kroah-Hartman */ 122d70b73aSGreg Kroah-Hartman #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 132d70b73aSGreg Kroah-Hartman 142d70b73aSGreg Kroah-Hartman #include <linux/kernel.h> 152d70b73aSGreg Kroah-Hartman #include <linux/init.h> 162d70b73aSGreg Kroah-Hartman #include <linux/module.h> 172d70b73aSGreg Kroah-Hartman #include <linux/delay.h> 182d70b73aSGreg Kroah-Hartman #include <linux/pci.h> 192d70b73aSGreg Kroah-Hartman #include <linux/backlight.h> 20f674ebf1SCorentin Chary #include <linux/leds.h> 212d70b73aSGreg Kroah-Hartman #include <linux/fb.h> 222d70b73aSGreg Kroah-Hartman #include <linux/dmi.h> 232d70b73aSGreg Kroah-Hartman #include <linux/platform_device.h> 242d70b73aSGreg Kroah-Hartman #include <linux/rfkill.h> 25f34cd9caSCorentin Chary #include <linux/acpi.h> 265b80fc40SCorentin Chary #include <linux/seq_file.h> 275b80fc40SCorentin Chary #include <linux/debugfs.h> 2882c333aaSDavid Rientjes #include <linux/ctype.h> 29e0094244SMatt Fleming #include <linux/efi.h> 300ca849eaSScott Thrasher #include <linux/suspend.h> 31a979e2e2SCorentin Chary #include <acpi/video.h> 322d70b73aSGreg Kroah-Hartman 332d70b73aSGreg Kroah-Hartman /* 342d70b73aSGreg Kroah-Hartman * This driver is needed because a number of Samsung laptops do not hook 352d70b73aSGreg Kroah-Hartman * their control settings through ACPI. So we have to poke around in the 362d70b73aSGreg Kroah-Hartman * BIOS to do things like brightness values, and "special" key controls. 372d70b73aSGreg Kroah-Hartman */ 382d70b73aSGreg Kroah-Hartman 392d70b73aSGreg Kroah-Hartman /* 402d70b73aSGreg Kroah-Hartman * We have 0 - 8 as valid brightness levels. The specs say that level 0 should 412d70b73aSGreg Kroah-Hartman * be reserved by the BIOS (which really doesn't make much sense), we tell 422d70b73aSGreg Kroah-Hartman * userspace that the value is 0 - 7 and then just tell the hardware 1 - 8 432d70b73aSGreg Kroah-Hartman */ 442d70b73aSGreg Kroah-Hartman #define MAX_BRIGHT 0x07 452d70b73aSGreg Kroah-Hartman 462d70b73aSGreg Kroah-Hartman 472d70b73aSGreg Kroah-Hartman #define SABI_IFACE_MAIN 0x00 482d70b73aSGreg Kroah-Hartman #define SABI_IFACE_SUB 0x02 492d70b73aSGreg Kroah-Hartman #define SABI_IFACE_COMPLETE 0x04 502d70b73aSGreg Kroah-Hartman #define SABI_IFACE_DATA 0x05 512d70b73aSGreg Kroah-Hartman 5284d482f2SCorentin Chary #define WL_STATUS_WLAN 0x0 5384d482f2SCorentin Chary #define WL_STATUS_BT 0x2 5484d482f2SCorentin Chary 557e960711SCorentin Chary /* Structure get/set data using sabi */ 567e960711SCorentin Chary struct sabi_data { 577e960711SCorentin Chary union { 587e960711SCorentin Chary struct { 597e960711SCorentin Chary u32 d0; 607e960711SCorentin Chary u32 d1; 617e960711SCorentin Chary u16 d2; 627e960711SCorentin Chary u8 d3; 637e960711SCorentin Chary }; 647e960711SCorentin Chary u8 data[11]; 657e960711SCorentin Chary }; 662d70b73aSGreg Kroah-Hartman }; 672d70b73aSGreg Kroah-Hartman 682d70b73aSGreg Kroah-Hartman struct sabi_header_offsets { 692d70b73aSGreg Kroah-Hartman u8 port; 702d70b73aSGreg Kroah-Hartman u8 re_mem; 712d70b73aSGreg Kroah-Hartman u8 iface_func; 722d70b73aSGreg Kroah-Hartman u8 en_mem; 732d70b73aSGreg Kroah-Hartman u8 data_offset; 742d70b73aSGreg Kroah-Hartman u8 data_segment; 752d70b73aSGreg Kroah-Hartman }; 762d70b73aSGreg Kroah-Hartman 772d70b73aSGreg Kroah-Hartman struct sabi_commands { 782d70b73aSGreg Kroah-Hartman /* 792d70b73aSGreg Kroah-Hartman * Brightness is 0 - 8, as described above. 802d70b73aSGreg Kroah-Hartman * Value 0 is for the BIOS to use 812d70b73aSGreg Kroah-Hartman */ 827e960711SCorentin Chary u16 get_brightness; 837e960711SCorentin Chary u16 set_brightness; 842d70b73aSGreg Kroah-Hartman 852d70b73aSGreg Kroah-Hartman /* 862d70b73aSGreg Kroah-Hartman * first byte: 872d70b73aSGreg Kroah-Hartman * 0x00 - wireless is off 882d70b73aSGreg Kroah-Hartman * 0x01 - wireless is on 892d70b73aSGreg Kroah-Hartman * second byte: 902d70b73aSGreg Kroah-Hartman * 0x02 - 3G is off 912d70b73aSGreg Kroah-Hartman * 0x03 - 3G is on 922d70b73aSGreg Kroah-Hartman * TODO, verify 3G is correct, that doesn't seem right... 932d70b73aSGreg Kroah-Hartman */ 947e960711SCorentin Chary u16 get_wireless_button; 957e960711SCorentin Chary u16 set_wireless_button; 962d70b73aSGreg Kroah-Hartman 972d70b73aSGreg Kroah-Hartman /* 0 is off, 1 is on */ 987e960711SCorentin Chary u16 get_backlight; 997e960711SCorentin Chary u16 set_backlight; 1002d70b73aSGreg Kroah-Hartman 1012d70b73aSGreg Kroah-Hartman /* 1022d70b73aSGreg Kroah-Hartman * 0x80 or 0x00 - no action 1032d70b73aSGreg Kroah-Hartman * 0x81 - recovery key pressed 1042d70b73aSGreg Kroah-Hartman */ 1057e960711SCorentin Chary u16 get_recovery_mode; 1067e960711SCorentin Chary u16 set_recovery_mode; 1072d70b73aSGreg Kroah-Hartman 1082d70b73aSGreg Kroah-Hartman /* 1092d70b73aSGreg Kroah-Hartman * on seclinux: 0 is low, 1 is high, 1102d70b73aSGreg Kroah-Hartman * on swsmi: 0 is normal, 1 is silent, 2 is turbo 1112d70b73aSGreg Kroah-Hartman */ 1127e960711SCorentin Chary u16 get_performance_level; 1137e960711SCorentin Chary u16 set_performance_level; 1142d70b73aSGreg Kroah-Hartman 115cb5b5c91SCorentin Chary /* 0x80 is off, 0x81 is on */ 116cb5b5c91SCorentin Chary u16 get_battery_life_extender; 117cb5b5c91SCorentin Chary u16 set_battery_life_extender; 118cb5b5c91SCorentin Chary 1193a75d378SCorentin Chary /* 0x80 is off, 0x81 is on */ 1203a75d378SCorentin Chary u16 get_usb_charge; 1213a75d378SCorentin Chary u16 set_usb_charge; 1223a75d378SCorentin Chary 12384d482f2SCorentin Chary /* the first byte is for bluetooth and the third one is for wlan */ 12484d482f2SCorentin Chary u16 get_wireless_status; 12584d482f2SCorentin Chary u16 set_wireless_status; 12684d482f2SCorentin Chary 127f674ebf1SCorentin Chary /* 0x81 to read, (0x82 | level << 8) to set, 0xaabb to enable */ 128f674ebf1SCorentin Chary u16 kbd_backlight; 129f674ebf1SCorentin Chary 1302d70b73aSGreg Kroah-Hartman /* 1312d70b73aSGreg Kroah-Hartman * Tell the BIOS that Linux is running on this machine. 1322d70b73aSGreg Kroah-Hartman * 81 is on, 80 is off 1332d70b73aSGreg Kroah-Hartman */ 1347e960711SCorentin Chary u16 set_linux; 1352d70b73aSGreg Kroah-Hartman }; 1362d70b73aSGreg Kroah-Hartman 1372d70b73aSGreg Kroah-Hartman struct sabi_performance_level { 1382d70b73aSGreg Kroah-Hartman const char *name; 1397e960711SCorentin Chary u16 value; 1402d70b73aSGreg Kroah-Hartman }; 1412d70b73aSGreg Kroah-Hartman 1422d70b73aSGreg Kroah-Hartman struct sabi_config { 14384d482f2SCorentin Chary int sabi_version; 1442d70b73aSGreg Kroah-Hartman const char *test_string; 1452d70b73aSGreg Kroah-Hartman u16 main_function; 1462d70b73aSGreg Kroah-Hartman const struct sabi_header_offsets header_offsets; 1472d70b73aSGreg Kroah-Hartman const struct sabi_commands commands; 1482d70b73aSGreg Kroah-Hartman const struct sabi_performance_level performance_levels[4]; 1492d70b73aSGreg Kroah-Hartman u8 min_brightness; 1502d70b73aSGreg Kroah-Hartman u8 max_brightness; 1512d70b73aSGreg Kroah-Hartman }; 1522d70b73aSGreg Kroah-Hartman 1532d70b73aSGreg Kroah-Hartman static const struct sabi_config sabi_configs[] = { 1542d70b73aSGreg Kroah-Hartman { 15584d482f2SCorentin Chary /* I don't know if it is really 2, but it it is 15684d482f2SCorentin Chary * less than 3 anyway */ 15784d482f2SCorentin Chary .sabi_version = 2, 15884d482f2SCorentin Chary 1592d70b73aSGreg Kroah-Hartman .test_string = "SECLINUX", 1602d70b73aSGreg Kroah-Hartman 1612d70b73aSGreg Kroah-Hartman .main_function = 0x4c49, 1622d70b73aSGreg Kroah-Hartman 1632d70b73aSGreg Kroah-Hartman .header_offsets = { 1642d70b73aSGreg Kroah-Hartman .port = 0x00, 1652d70b73aSGreg Kroah-Hartman .re_mem = 0x02, 1662d70b73aSGreg Kroah-Hartman .iface_func = 0x03, 1672d70b73aSGreg Kroah-Hartman .en_mem = 0x04, 1682d70b73aSGreg Kroah-Hartman .data_offset = 0x05, 1692d70b73aSGreg Kroah-Hartman .data_segment = 0x07, 1702d70b73aSGreg Kroah-Hartman }, 1712d70b73aSGreg Kroah-Hartman 1722d70b73aSGreg Kroah-Hartman .commands = { 1732d70b73aSGreg Kroah-Hartman .get_brightness = 0x00, 1742d70b73aSGreg Kroah-Hartman .set_brightness = 0x01, 1752d70b73aSGreg Kroah-Hartman 1762d70b73aSGreg Kroah-Hartman .get_wireless_button = 0x02, 1772d70b73aSGreg Kroah-Hartman .set_wireless_button = 0x03, 1782d70b73aSGreg Kroah-Hartman 1792d70b73aSGreg Kroah-Hartman .get_backlight = 0x04, 1802d70b73aSGreg Kroah-Hartman .set_backlight = 0x05, 1812d70b73aSGreg Kroah-Hartman 1822d70b73aSGreg Kroah-Hartman .get_recovery_mode = 0x06, 1832d70b73aSGreg Kroah-Hartman .set_recovery_mode = 0x07, 1842d70b73aSGreg Kroah-Hartman 1852d70b73aSGreg Kroah-Hartman .get_performance_level = 0x08, 1862d70b73aSGreg Kroah-Hartman .set_performance_level = 0x09, 1872d70b73aSGreg Kroah-Hartman 188cb5b5c91SCorentin Chary .get_battery_life_extender = 0xFFFF, 189cb5b5c91SCorentin Chary .set_battery_life_extender = 0xFFFF, 190cb5b5c91SCorentin Chary 1913a75d378SCorentin Chary .get_usb_charge = 0xFFFF, 1923a75d378SCorentin Chary .set_usb_charge = 0xFFFF, 1933a75d378SCorentin Chary 19484d482f2SCorentin Chary .get_wireless_status = 0xFFFF, 19584d482f2SCorentin Chary .set_wireless_status = 0xFFFF, 19684d482f2SCorentin Chary 197f674ebf1SCorentin Chary .kbd_backlight = 0xFFFF, 198f674ebf1SCorentin Chary 1992d70b73aSGreg Kroah-Hartman .set_linux = 0x0a, 2002d70b73aSGreg Kroah-Hartman }, 2012d70b73aSGreg Kroah-Hartman 2022d70b73aSGreg Kroah-Hartman .performance_levels = { 2032d70b73aSGreg Kroah-Hartman { 2042d70b73aSGreg Kroah-Hartman .name = "silent", 2052d70b73aSGreg Kroah-Hartman .value = 0, 2062d70b73aSGreg Kroah-Hartman }, 2072d70b73aSGreg Kroah-Hartman { 2082d70b73aSGreg Kroah-Hartman .name = "normal", 2092d70b73aSGreg Kroah-Hartman .value = 1, 2102d70b73aSGreg Kroah-Hartman }, 2112d70b73aSGreg Kroah-Hartman { }, 2122d70b73aSGreg Kroah-Hartman }, 2132d70b73aSGreg Kroah-Hartman .min_brightness = 1, 2142d70b73aSGreg Kroah-Hartman .max_brightness = 8, 2152d70b73aSGreg Kroah-Hartman }, 2162d70b73aSGreg Kroah-Hartman { 21784d482f2SCorentin Chary .sabi_version = 3, 21884d482f2SCorentin Chary 2192d70b73aSGreg Kroah-Hartman .test_string = "SwSmi@", 2202d70b73aSGreg Kroah-Hartman 2212d70b73aSGreg Kroah-Hartman .main_function = 0x5843, 2222d70b73aSGreg Kroah-Hartman 2232d70b73aSGreg Kroah-Hartman .header_offsets = { 2242d70b73aSGreg Kroah-Hartman .port = 0x00, 2252d70b73aSGreg Kroah-Hartman .re_mem = 0x04, 2262d70b73aSGreg Kroah-Hartman .iface_func = 0x02, 2272d70b73aSGreg Kroah-Hartman .en_mem = 0x03, 2282d70b73aSGreg Kroah-Hartman .data_offset = 0x05, 2292d70b73aSGreg Kroah-Hartman .data_segment = 0x07, 2302d70b73aSGreg Kroah-Hartman }, 2312d70b73aSGreg Kroah-Hartman 2322d70b73aSGreg Kroah-Hartman .commands = { 2332d70b73aSGreg Kroah-Hartman .get_brightness = 0x10, 2342d70b73aSGreg Kroah-Hartman .set_brightness = 0x11, 2352d70b73aSGreg Kroah-Hartman 2362d70b73aSGreg Kroah-Hartman .get_wireless_button = 0x12, 2372d70b73aSGreg Kroah-Hartman .set_wireless_button = 0x13, 2382d70b73aSGreg Kroah-Hartman 2392d70b73aSGreg Kroah-Hartman .get_backlight = 0x2d, 2402d70b73aSGreg Kroah-Hartman .set_backlight = 0x2e, 2412d70b73aSGreg Kroah-Hartman 2422d70b73aSGreg Kroah-Hartman .get_recovery_mode = 0xff, 2432d70b73aSGreg Kroah-Hartman .set_recovery_mode = 0xff, 2442d70b73aSGreg Kroah-Hartman 2452d70b73aSGreg Kroah-Hartman .get_performance_level = 0x31, 2462d70b73aSGreg Kroah-Hartman .set_performance_level = 0x32, 2472d70b73aSGreg Kroah-Hartman 248cb5b5c91SCorentin Chary .get_battery_life_extender = 0x65, 249cb5b5c91SCorentin Chary .set_battery_life_extender = 0x66, 250cb5b5c91SCorentin Chary 2513a75d378SCorentin Chary .get_usb_charge = 0x67, 2523a75d378SCorentin Chary .set_usb_charge = 0x68, 2533a75d378SCorentin Chary 25484d482f2SCorentin Chary .get_wireless_status = 0x69, 25584d482f2SCorentin Chary .set_wireless_status = 0x6a, 25684d482f2SCorentin Chary 257f674ebf1SCorentin Chary .kbd_backlight = 0x78, 258f674ebf1SCorentin Chary 2592d70b73aSGreg Kroah-Hartman .set_linux = 0xff, 2602d70b73aSGreg Kroah-Hartman }, 2612d70b73aSGreg Kroah-Hartman 2622d70b73aSGreg Kroah-Hartman .performance_levels = { 2632d70b73aSGreg Kroah-Hartman { 2642d70b73aSGreg Kroah-Hartman .name = "normal", 2652d70b73aSGreg Kroah-Hartman .value = 0, 2662d70b73aSGreg Kroah-Hartman }, 2672d70b73aSGreg Kroah-Hartman { 2682d70b73aSGreg Kroah-Hartman .name = "silent", 2692d70b73aSGreg Kroah-Hartman .value = 1, 2702d70b73aSGreg Kroah-Hartman }, 2712d70b73aSGreg Kroah-Hartman { 2722d70b73aSGreg Kroah-Hartman .name = "overclock", 2732d70b73aSGreg Kroah-Hartman .value = 2, 2742d70b73aSGreg Kroah-Hartman }, 2752d70b73aSGreg Kroah-Hartman { }, 2762d70b73aSGreg Kroah-Hartman }, 2772d70b73aSGreg Kroah-Hartman .min_brightness = 0, 2782d70b73aSGreg Kroah-Hartman .max_brightness = 8, 2792d70b73aSGreg Kroah-Hartman }, 2802d70b73aSGreg Kroah-Hartman { }, 2812d70b73aSGreg Kroah-Hartman }; 2822d70b73aSGreg Kroah-Hartman 2835b80fc40SCorentin Chary /* 2845b80fc40SCorentin Chary * samsung-laptop/ - debugfs root directory 2855b80fc40SCorentin Chary * f0000_segment - dump f0000 segment 2865b80fc40SCorentin Chary * command - current command 2875b80fc40SCorentin Chary * data - current data 2885b80fc40SCorentin Chary * d0, d1, d2, d3 - data fields 2895b80fc40SCorentin Chary * call - call SABI using command and data 2905b80fc40SCorentin Chary * 2915b80fc40SCorentin Chary * This allow to call arbitrary sabi commands wihout 2925b80fc40SCorentin Chary * modifying the driver at all. 2935b80fc40SCorentin Chary * For example, setting the keyboard backlight brightness to 5 2945b80fc40SCorentin Chary * 2955b80fc40SCorentin Chary * echo 0x78 > command 2965b80fc40SCorentin Chary * echo 0x0582 > d0 2975b80fc40SCorentin Chary * echo 0 > d1 2985b80fc40SCorentin Chary * echo 0 > d2 2995b80fc40SCorentin Chary * echo 0 > d3 3005b80fc40SCorentin Chary * cat call 3015b80fc40SCorentin Chary */ 3025b80fc40SCorentin Chary 3035b80fc40SCorentin Chary struct samsung_laptop_debug { 3045b80fc40SCorentin Chary struct dentry *root; 3055b80fc40SCorentin Chary struct sabi_data data; 3065b80fc40SCorentin Chary u16 command; 3075b80fc40SCorentin Chary 3085b80fc40SCorentin Chary struct debugfs_blob_wrapper f0000_wrapper; 3095b80fc40SCorentin Chary struct debugfs_blob_wrapper data_wrapper; 3106f6ae06eSCorentin Chary struct debugfs_blob_wrapper sdiag_wrapper; 3115b80fc40SCorentin Chary }; 3125b80fc40SCorentin Chary 31384d482f2SCorentin Chary struct samsung_laptop; 31484d482f2SCorentin Chary 31584d482f2SCorentin Chary struct samsung_rfkill { 31684d482f2SCorentin Chary struct samsung_laptop *samsung; 31784d482f2SCorentin Chary struct rfkill *rfkill; 31884d482f2SCorentin Chary enum rfkill_type type; 31984d482f2SCorentin Chary }; 32084d482f2SCorentin Chary 321a6df4894SCorentin Chary struct samsung_laptop { 322a6df4894SCorentin Chary const struct sabi_config *config; 3232d70b73aSGreg Kroah-Hartman 324a6df4894SCorentin Chary void __iomem *sabi; 325a6df4894SCorentin Chary void __iomem *sabi_iface; 326a6df4894SCorentin Chary void __iomem *f0000_segment; 327a6df4894SCorentin Chary 328a6df4894SCorentin Chary struct mutex sabi_mutex; 329a6df4894SCorentin Chary 3305dea7a20SCorentin Chary struct platform_device *platform_device; 331a6df4894SCorentin Chary struct backlight_device *backlight_device; 33284d482f2SCorentin Chary 33384d482f2SCorentin Chary struct samsung_rfkill wlan; 33484d482f2SCorentin Chary struct samsung_rfkill bluetooth; 335a6df4894SCorentin Chary 336f674ebf1SCorentin Chary struct led_classdev kbd_led; 337f674ebf1SCorentin Chary int kbd_led_wk; 338f674ebf1SCorentin Chary struct workqueue_struct *led_workqueue; 339f674ebf1SCorentin Chary struct work_struct kbd_led_work; 340f674ebf1SCorentin Chary 3415b80fc40SCorentin Chary struct samsung_laptop_debug debug; 342a979e2e2SCorentin Chary struct samsung_quirks *quirks; 3435b80fc40SCorentin Chary 3440ca849eaSScott Thrasher struct notifier_block pm_nb; 3450ca849eaSScott Thrasher 346f34cd9caSCorentin Chary bool handle_backlight; 347a6df4894SCorentin Chary bool has_stepping_quirk; 3486f6ae06eSCorentin Chary 3496f6ae06eSCorentin Chary char sdiag[64]; 350a6df4894SCorentin Chary }; 351a6df4894SCorentin Chary 352a979e2e2SCorentin Chary struct samsung_quirks { 353a979e2e2SCorentin Chary bool broken_acpi_video; 3540ca849eaSScott Thrasher bool four_kbd_backlight_levels; 3550ca849eaSScott Thrasher bool enable_kbd_backlight; 3564690555eSHans de Goede bool use_native_backlight; 357a979e2e2SCorentin Chary }; 3585dea7a20SCorentin Chary 359a979e2e2SCorentin Chary static struct samsung_quirks samsung_unknown = {}; 360a979e2e2SCorentin Chary 361a979e2e2SCorentin Chary static struct samsung_quirks samsung_broken_acpi_video = { 362a979e2e2SCorentin Chary .broken_acpi_video = true, 363a979e2e2SCorentin Chary }; 3642d70b73aSGreg Kroah-Hartman 3654690555eSHans de Goede static struct samsung_quirks samsung_use_native_backlight = { 3664690555eSHans de Goede .use_native_backlight = true, 3674690555eSHans de Goede }; 3684690555eSHans de Goede 3690ca849eaSScott Thrasher static struct samsung_quirks samsung_np740u3e = { 3700ca849eaSScott Thrasher .four_kbd_backlight_levels = true, 3710ca849eaSScott Thrasher .enable_kbd_backlight = true, 3720ca849eaSScott Thrasher }; 3730ca849eaSScott Thrasher 37490ab5ee9SRusty Russell static bool force; 3752d70b73aSGreg Kroah-Hartman module_param(force, bool, 0); 3762d70b73aSGreg Kroah-Hartman MODULE_PARM_DESC(force, 3772d70b73aSGreg Kroah-Hartman "Disable the DMI check and forces the driver to be loaded"); 3782d70b73aSGreg Kroah-Hartman 37990ab5ee9SRusty Russell static bool debug; 3802d70b73aSGreg Kroah-Hartman module_param(debug, bool, S_IRUGO | S_IWUSR); 3812d70b73aSGreg Kroah-Hartman MODULE_PARM_DESC(debug, "Debug enabled or not"); 3822d70b73aSGreg Kroah-Hartman 3837e960711SCorentin Chary static int sabi_command(struct samsung_laptop *samsung, u16 command, 3847e960711SCorentin Chary struct sabi_data *in, 3857e960711SCorentin Chary struct sabi_data *out) 3862d70b73aSGreg Kroah-Hartman { 387a6df4894SCorentin Chary const struct sabi_config *config = samsung->config; 3887e960711SCorentin Chary int ret = 0; 389a6df4894SCorentin Chary u16 port = readw(samsung->sabi + config->header_offsets.port); 3902d70b73aSGreg Kroah-Hartman u8 complete, iface_data; 3912d70b73aSGreg Kroah-Hartman 392a6df4894SCorentin Chary mutex_lock(&samsung->sabi_mutex); 3932d70b73aSGreg Kroah-Hartman 3947e960711SCorentin Chary if (debug) { 3957e960711SCorentin Chary if (in) 3962e777187SCorentin Chary pr_info("SABI command:0x%04x " 3972e777187SCorentin Chary "data:{0x%08x, 0x%08x, 0x%04x, 0x%02x}", 3987e960711SCorentin Chary command, in->d0, in->d1, in->d2, in->d3); 3997e960711SCorentin Chary else 4002e777187SCorentin Chary pr_info("SABI command:0x%04x", command); 4017e960711SCorentin Chary } 4027e960711SCorentin Chary 4032d70b73aSGreg Kroah-Hartman /* enable memory to be able to write to it */ 404a6df4894SCorentin Chary outb(readb(samsung->sabi + config->header_offsets.en_mem), port); 4052d70b73aSGreg Kroah-Hartman 4062d70b73aSGreg Kroah-Hartman /* write out the command */ 407a6df4894SCorentin Chary writew(config->main_function, samsung->sabi_iface + SABI_IFACE_MAIN); 408a6df4894SCorentin Chary writew(command, samsung->sabi_iface + SABI_IFACE_SUB); 409a6df4894SCorentin Chary writeb(0, samsung->sabi_iface + SABI_IFACE_COMPLETE); 4107e960711SCorentin Chary if (in) { 4117e960711SCorentin Chary writel(in->d0, samsung->sabi_iface + SABI_IFACE_DATA); 4127e960711SCorentin Chary writel(in->d1, samsung->sabi_iface + SABI_IFACE_DATA + 4); 4137e960711SCorentin Chary writew(in->d2, samsung->sabi_iface + SABI_IFACE_DATA + 8); 4147e960711SCorentin Chary writeb(in->d3, samsung->sabi_iface + SABI_IFACE_DATA + 10); 4157e960711SCorentin Chary } 416a6df4894SCorentin Chary outb(readb(samsung->sabi + config->header_offsets.iface_func), port); 4172d70b73aSGreg Kroah-Hartman 4182d70b73aSGreg Kroah-Hartman /* write protect memory to make it safe */ 419a6df4894SCorentin Chary outb(readb(samsung->sabi + config->header_offsets.re_mem), port); 4202d70b73aSGreg Kroah-Hartman 4212d70b73aSGreg Kroah-Hartman /* see if the command actually succeeded */ 422a6df4894SCorentin Chary complete = readb(samsung->sabi_iface + SABI_IFACE_COMPLETE); 423a6df4894SCorentin Chary iface_data = readb(samsung->sabi_iface + SABI_IFACE_DATA); 4242e777187SCorentin Chary 4252e777187SCorentin Chary /* iface_data = 0xFF happens when a command is not known 4262e777187SCorentin Chary * so we only add a warning in debug mode since we will 4272e777187SCorentin Chary * probably issue some unknown command at startup to find 4282e777187SCorentin Chary * out which features are supported */ 4292e777187SCorentin Chary if (complete != 0xaa || (iface_data == 0xff && debug)) 4307e960711SCorentin Chary pr_warn("SABI command 0x%04x failed with" 4317e960711SCorentin Chary " completion flag 0x%02x and interface data 0x%02x", 4322d70b73aSGreg Kroah-Hartman command, complete, iface_data); 4332e777187SCorentin Chary 4342e777187SCorentin Chary if (complete != 0xaa || iface_data == 0xff) { 4357e960711SCorentin Chary ret = -EINVAL; 4362d70b73aSGreg Kroah-Hartman goto exit; 4372d70b73aSGreg Kroah-Hartman } 4387e960711SCorentin Chary 4397e960711SCorentin Chary if (out) { 4407e960711SCorentin Chary out->d0 = readl(samsung->sabi_iface + SABI_IFACE_DATA); 4417e960711SCorentin Chary out->d1 = readl(samsung->sabi_iface + SABI_IFACE_DATA + 4); 4427e960711SCorentin Chary out->d2 = readw(samsung->sabi_iface + SABI_IFACE_DATA + 2); 4437e960711SCorentin Chary out->d3 = readb(samsung->sabi_iface + SABI_IFACE_DATA + 1); 4447e960711SCorentin Chary } 4457e960711SCorentin Chary 4467e960711SCorentin Chary if (debug && out) { 4472e777187SCorentin Chary pr_info("SABI return data:{0x%08x, 0x%08x, 0x%04x, 0x%02x}", 4487e960711SCorentin Chary out->d0, out->d1, out->d2, out->d3); 4497e960711SCorentin Chary } 4502d70b73aSGreg Kroah-Hartman 4512d70b73aSGreg Kroah-Hartman exit: 452a6df4894SCorentin Chary mutex_unlock(&samsung->sabi_mutex); 4537e960711SCorentin Chary return ret; 4542d70b73aSGreg Kroah-Hartman } 4552d70b73aSGreg Kroah-Hartman 4567e960711SCorentin Chary /* simple wrappers usable with most commands */ 4577e960711SCorentin Chary static int sabi_set_commandb(struct samsung_laptop *samsung, 4587e960711SCorentin Chary u16 command, u8 data) 4592d70b73aSGreg Kroah-Hartman { 46085229440SDavid Rientjes struct sabi_data in = { { { .d0 = 0, .d1 = 0, .d2 = 0, .d3 = 0 } } }; 4612d70b73aSGreg Kroah-Hartman 4627e960711SCorentin Chary in.data[0] = data; 4637e960711SCorentin Chary return sabi_command(samsung, command, &in, NULL); 4642d70b73aSGreg Kroah-Hartman } 4652d70b73aSGreg Kroah-Hartman 4665dea7a20SCorentin Chary static int read_brightness(struct samsung_laptop *samsung) 4672d70b73aSGreg Kroah-Hartman { 468a6df4894SCorentin Chary const struct sabi_config *config = samsung->config; 469a6df4894SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 4707e960711SCorentin Chary struct sabi_data sretval; 4712d70b73aSGreg Kroah-Hartman int user_brightness = 0; 4722d70b73aSGreg Kroah-Hartman int retval; 4732d70b73aSGreg Kroah-Hartman 4747e960711SCorentin Chary retval = sabi_command(samsung, commands->get_brightness, 4757e960711SCorentin Chary NULL, &sretval); 4767e960711SCorentin Chary if (retval) 4777e960711SCorentin Chary return retval; 4787e960711SCorentin Chary 4797e960711SCorentin Chary user_brightness = sretval.data[0]; 480a6df4894SCorentin Chary if (user_brightness > config->min_brightness) 481a6df4894SCorentin Chary user_brightness -= config->min_brightness; 482bee460beSJason Stubbs else 483bee460beSJason Stubbs user_brightness = 0; 4847e960711SCorentin Chary 4852d70b73aSGreg Kroah-Hartman return user_brightness; 4862d70b73aSGreg Kroah-Hartman } 4872d70b73aSGreg Kroah-Hartman 4885dea7a20SCorentin Chary static void set_brightness(struct samsung_laptop *samsung, u8 user_brightness) 4892d70b73aSGreg Kroah-Hartman { 490a6df4894SCorentin Chary const struct sabi_config *config = samsung->config; 491a6df4894SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 492a6df4894SCorentin Chary u8 user_level = user_brightness + config->min_brightness; 4932d70b73aSGreg Kroah-Hartman 494a6df4894SCorentin Chary if (samsung->has_stepping_quirk && user_level != 0) { 495ac080523SJason Stubbs /* 496ac080523SJason Stubbs * short circuit if the specified level is what's already set 497ac080523SJason Stubbs * to prevent the screen from flickering needlessly 498ac080523SJason Stubbs */ 4995dea7a20SCorentin Chary if (user_brightness == read_brightness(samsung)) 500ac080523SJason Stubbs return; 501ac080523SJason Stubbs 5027e960711SCorentin Chary sabi_set_commandb(samsung, commands->set_brightness, 0); 503ac080523SJason Stubbs } 504ac080523SJason Stubbs 5057e960711SCorentin Chary sabi_set_commandb(samsung, commands->set_brightness, user_level); 5062d70b73aSGreg Kroah-Hartman } 5072d70b73aSGreg Kroah-Hartman 5082d70b73aSGreg Kroah-Hartman static int get_brightness(struct backlight_device *bd) 5092d70b73aSGreg Kroah-Hartman { 5105dea7a20SCorentin Chary struct samsung_laptop *samsung = bl_get_data(bd); 5115dea7a20SCorentin Chary 5125dea7a20SCorentin Chary return read_brightness(samsung); 5132d70b73aSGreg Kroah-Hartman } 5142d70b73aSGreg Kroah-Hartman 5155dea7a20SCorentin Chary static void check_for_stepping_quirk(struct samsung_laptop *samsung) 516ac080523SJason Stubbs { 5175dea7a20SCorentin Chary int initial_level; 5185dea7a20SCorentin Chary int check_level; 5195dea7a20SCorentin Chary int orig_level = read_brightness(samsung); 520ac080523SJason Stubbs 521ac080523SJason Stubbs /* 522ac080523SJason Stubbs * Some laptops exhibit the strange behaviour of stepping toward 523ac080523SJason Stubbs * (rather than setting) the brightness except when changing to/from 524ac080523SJason Stubbs * brightness level 0. This behaviour is checked for here and worked 525ac080523SJason Stubbs * around in set_brightness. 526ac080523SJason Stubbs */ 527ac080523SJason Stubbs 528ba05b237SJohn Serock if (orig_level == 0) 5295dea7a20SCorentin Chary set_brightness(samsung, 1); 530ba05b237SJohn Serock 5315dea7a20SCorentin Chary initial_level = read_brightness(samsung); 532ba05b237SJohn Serock 533ac080523SJason Stubbs if (initial_level <= 2) 534ac080523SJason Stubbs check_level = initial_level + 2; 535ac080523SJason Stubbs else 536ac080523SJason Stubbs check_level = initial_level - 2; 537ac080523SJason Stubbs 538a6df4894SCorentin Chary samsung->has_stepping_quirk = false; 5395dea7a20SCorentin Chary set_brightness(samsung, check_level); 540ac080523SJason Stubbs 5415dea7a20SCorentin Chary if (read_brightness(samsung) != check_level) { 542a6df4894SCorentin Chary samsung->has_stepping_quirk = true; 543ac080523SJason Stubbs pr_info("enabled workaround for brightness stepping quirk\n"); 544ac080523SJason Stubbs } 545ac080523SJason Stubbs 5465dea7a20SCorentin Chary set_brightness(samsung, orig_level); 547ac080523SJason Stubbs } 548ac080523SJason Stubbs 5492d70b73aSGreg Kroah-Hartman static int update_status(struct backlight_device *bd) 5502d70b73aSGreg Kroah-Hartman { 5515dea7a20SCorentin Chary struct samsung_laptop *samsung = bl_get_data(bd); 552a6df4894SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 553a6df4894SCorentin Chary 5545dea7a20SCorentin Chary set_brightness(samsung, bd->props.brightness); 5552d70b73aSGreg Kroah-Hartman 5562d70b73aSGreg Kroah-Hartman if (bd->props.power == FB_BLANK_UNBLANK) 5577e960711SCorentin Chary sabi_set_commandb(samsung, commands->set_backlight, 1); 5582d70b73aSGreg Kroah-Hartman else 5597e960711SCorentin Chary sabi_set_commandb(samsung, commands->set_backlight, 0); 5605dea7a20SCorentin Chary 5612d70b73aSGreg Kroah-Hartman return 0; 5622d70b73aSGreg Kroah-Hartman } 5632d70b73aSGreg Kroah-Hartman 5642d70b73aSGreg Kroah-Hartman static const struct backlight_ops backlight_ops = { 5652d70b73aSGreg Kroah-Hartman .get_brightness = get_brightness, 5662d70b73aSGreg Kroah-Hartman .update_status = update_status, 5672d70b73aSGreg Kroah-Hartman }; 5682d70b73aSGreg Kroah-Hartman 56984d482f2SCorentin Chary static int seclinux_rfkill_set(void *data, bool blocked) 5702d70b73aSGreg Kroah-Hartman { 57120db88e3SCorentin Chary struct samsung_rfkill *srfkill = data; 57220db88e3SCorentin Chary struct samsung_laptop *samsung = srfkill->samsung; 573a6df4894SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 574a6df4894SCorentin Chary 57584d482f2SCorentin Chary return sabi_set_commandb(samsung, commands->set_wireless_button, 57684d482f2SCorentin Chary !blocked); 5772d70b73aSGreg Kroah-Hartman } 5782d70b73aSGreg Kroah-Hartman 57984d482f2SCorentin Chary static struct rfkill_ops seclinux_rfkill_ops = { 58084d482f2SCorentin Chary .set_block = seclinux_rfkill_set, 58184d482f2SCorentin Chary }; 58284d482f2SCorentin Chary 58384d482f2SCorentin Chary static int swsmi_wireless_status(struct samsung_laptop *samsung, 58484d482f2SCorentin Chary struct sabi_data *data) 58584d482f2SCorentin Chary { 58684d482f2SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 58784d482f2SCorentin Chary 58884d482f2SCorentin Chary return sabi_command(samsung, commands->get_wireless_status, 58984d482f2SCorentin Chary NULL, data); 59084d482f2SCorentin Chary } 59184d482f2SCorentin Chary 59284d482f2SCorentin Chary static int swsmi_rfkill_set(void *priv, bool blocked) 59384d482f2SCorentin Chary { 59484d482f2SCorentin Chary struct samsung_rfkill *srfkill = priv; 59584d482f2SCorentin Chary struct samsung_laptop *samsung = srfkill->samsung; 59684d482f2SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 59784d482f2SCorentin Chary struct sabi_data data; 59884d482f2SCorentin Chary int ret, i; 59984d482f2SCorentin Chary 60084d482f2SCorentin Chary ret = swsmi_wireless_status(samsung, &data); 60184d482f2SCorentin Chary if (ret) 60284d482f2SCorentin Chary return ret; 60384d482f2SCorentin Chary 60484d482f2SCorentin Chary /* Don't set the state for non-present devices */ 60584d482f2SCorentin Chary for (i = 0; i < 4; i++) 60684d482f2SCorentin Chary if (data.data[i] == 0x02) 60784d482f2SCorentin Chary data.data[1] = 0; 60884d482f2SCorentin Chary 60984d482f2SCorentin Chary if (srfkill->type == RFKILL_TYPE_WLAN) 61084d482f2SCorentin Chary data.data[WL_STATUS_WLAN] = !blocked; 61184d482f2SCorentin Chary else if (srfkill->type == RFKILL_TYPE_BLUETOOTH) 61284d482f2SCorentin Chary data.data[WL_STATUS_BT] = !blocked; 61384d482f2SCorentin Chary 61484d482f2SCorentin Chary return sabi_command(samsung, commands->set_wireless_status, 61584d482f2SCorentin Chary &data, &data); 61684d482f2SCorentin Chary } 61784d482f2SCorentin Chary 61884d482f2SCorentin Chary static void swsmi_rfkill_query(struct rfkill *rfkill, void *priv) 61984d482f2SCorentin Chary { 62084d482f2SCorentin Chary struct samsung_rfkill *srfkill = priv; 62184d482f2SCorentin Chary struct samsung_laptop *samsung = srfkill->samsung; 62284d482f2SCorentin Chary struct sabi_data data; 62384d482f2SCorentin Chary int ret; 62484d482f2SCorentin Chary 62584d482f2SCorentin Chary ret = swsmi_wireless_status(samsung, &data); 62684d482f2SCorentin Chary if (ret) 62784d482f2SCorentin Chary return ; 62884d482f2SCorentin Chary 62984d482f2SCorentin Chary if (srfkill->type == RFKILL_TYPE_WLAN) 63084d482f2SCorentin Chary ret = data.data[WL_STATUS_WLAN]; 63184d482f2SCorentin Chary else if (srfkill->type == RFKILL_TYPE_BLUETOOTH) 63284d482f2SCorentin Chary ret = data.data[WL_STATUS_BT]; 63384d482f2SCorentin Chary else 63484d482f2SCorentin Chary return ; 63584d482f2SCorentin Chary 63684d482f2SCorentin Chary rfkill_set_sw_state(rfkill, !ret); 63784d482f2SCorentin Chary } 63884d482f2SCorentin Chary 63984d482f2SCorentin Chary static struct rfkill_ops swsmi_rfkill_ops = { 64084d482f2SCorentin Chary .set_block = swsmi_rfkill_set, 64184d482f2SCorentin Chary .query = swsmi_rfkill_query, 6422d70b73aSGreg Kroah-Hartman }; 6432d70b73aSGreg Kroah-Hartman 6442d70b73aSGreg Kroah-Hartman static ssize_t get_performance_level(struct device *dev, 6452d70b73aSGreg Kroah-Hartman struct device_attribute *attr, char *buf) 6462d70b73aSGreg Kroah-Hartman { 6475dea7a20SCorentin Chary struct samsung_laptop *samsung = dev_get_drvdata(dev); 648a6df4894SCorentin Chary const struct sabi_config *config = samsung->config; 6495dea7a20SCorentin Chary const struct sabi_commands *commands = &config->commands; 6507e960711SCorentin Chary struct sabi_data sretval; 6512d70b73aSGreg Kroah-Hartman int retval; 6522d70b73aSGreg Kroah-Hartman int i; 6532d70b73aSGreg Kroah-Hartman 6542d70b73aSGreg Kroah-Hartman /* Read the state */ 6557e960711SCorentin Chary retval = sabi_command(samsung, commands->get_performance_level, 6567e960711SCorentin Chary NULL, &sretval); 6572d70b73aSGreg Kroah-Hartman if (retval) 6582d70b73aSGreg Kroah-Hartman return retval; 6592d70b73aSGreg Kroah-Hartman 6602d70b73aSGreg Kroah-Hartman /* The logic is backwards, yeah, lots of fun... */ 661a6df4894SCorentin Chary for (i = 0; config->performance_levels[i].name; ++i) { 6627e960711SCorentin Chary if (sretval.data[0] == config->performance_levels[i].value) 663a6df4894SCorentin Chary return sprintf(buf, "%s\n", config->performance_levels[i].name); 6642d70b73aSGreg Kroah-Hartman } 6652d70b73aSGreg Kroah-Hartman return sprintf(buf, "%s\n", "unknown"); 6662d70b73aSGreg Kroah-Hartman } 6672d70b73aSGreg Kroah-Hartman 6682d70b73aSGreg Kroah-Hartman static ssize_t set_performance_level(struct device *dev, 6692d70b73aSGreg Kroah-Hartman struct device_attribute *attr, const char *buf, 6702d70b73aSGreg Kroah-Hartman size_t count) 6712d70b73aSGreg Kroah-Hartman { 6725dea7a20SCorentin Chary struct samsung_laptop *samsung = dev_get_drvdata(dev); 673a6df4894SCorentin Chary const struct sabi_config *config = samsung->config; 6745dea7a20SCorentin Chary const struct sabi_commands *commands = &config->commands; 6752d70b73aSGreg Kroah-Hartman int i; 6765dea7a20SCorentin Chary 6775dea7a20SCorentin Chary if (count < 1) 6785dea7a20SCorentin Chary return count; 6795dea7a20SCorentin Chary 680a6df4894SCorentin Chary for (i = 0; config->performance_levels[i].name; ++i) { 6812d70b73aSGreg Kroah-Hartman const struct sabi_performance_level *level = 682a6df4894SCorentin Chary &config->performance_levels[i]; 6832d70b73aSGreg Kroah-Hartman if (!strncasecmp(level->name, buf, strlen(level->name))) { 6847e960711SCorentin Chary sabi_set_commandb(samsung, 6855dea7a20SCorentin Chary commands->set_performance_level, 6862d70b73aSGreg Kroah-Hartman level->value); 6872d70b73aSGreg Kroah-Hartman break; 6882d70b73aSGreg Kroah-Hartman } 6892d70b73aSGreg Kroah-Hartman } 6905dea7a20SCorentin Chary 691a6df4894SCorentin Chary if (!config->performance_levels[i].name) 6922d70b73aSGreg Kroah-Hartman return -EINVAL; 6935dea7a20SCorentin Chary 6942d70b73aSGreg Kroah-Hartman return count; 6952d70b73aSGreg Kroah-Hartman } 6965dea7a20SCorentin Chary 6972d70b73aSGreg Kroah-Hartman static DEVICE_ATTR(performance_level, S_IWUSR | S_IRUGO, 6982d70b73aSGreg Kroah-Hartman get_performance_level, set_performance_level); 6992d70b73aSGreg Kroah-Hartman 700cb5b5c91SCorentin Chary static int read_battery_life_extender(struct samsung_laptop *samsung) 701cb5b5c91SCorentin Chary { 702cb5b5c91SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 703cb5b5c91SCorentin Chary struct sabi_data data; 704cb5b5c91SCorentin Chary int retval; 705cb5b5c91SCorentin Chary 706cb5b5c91SCorentin Chary if (commands->get_battery_life_extender == 0xFFFF) 707cb5b5c91SCorentin Chary return -ENODEV; 708cb5b5c91SCorentin Chary 709cb5b5c91SCorentin Chary memset(&data, 0, sizeof(data)); 710cb5b5c91SCorentin Chary data.data[0] = 0x80; 711cb5b5c91SCorentin Chary retval = sabi_command(samsung, commands->get_battery_life_extender, 712cb5b5c91SCorentin Chary &data, &data); 713cb5b5c91SCorentin Chary 714cb5b5c91SCorentin Chary if (retval) 715cb5b5c91SCorentin Chary return retval; 716cb5b5c91SCorentin Chary 717cb5b5c91SCorentin Chary if (data.data[0] != 0 && data.data[0] != 1) 718cb5b5c91SCorentin Chary return -ENODEV; 719cb5b5c91SCorentin Chary 720cb5b5c91SCorentin Chary return data.data[0]; 721cb5b5c91SCorentin Chary } 722cb5b5c91SCorentin Chary 723cb5b5c91SCorentin Chary static int write_battery_life_extender(struct samsung_laptop *samsung, 724cb5b5c91SCorentin Chary int enabled) 725cb5b5c91SCorentin Chary { 726cb5b5c91SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 727cb5b5c91SCorentin Chary struct sabi_data data; 728cb5b5c91SCorentin Chary 729cb5b5c91SCorentin Chary memset(&data, 0, sizeof(data)); 730cb5b5c91SCorentin Chary data.data[0] = 0x80 | enabled; 731cb5b5c91SCorentin Chary return sabi_command(samsung, commands->set_battery_life_extender, 732cb5b5c91SCorentin Chary &data, NULL); 733cb5b5c91SCorentin Chary } 734cb5b5c91SCorentin Chary 735cb5b5c91SCorentin Chary static ssize_t get_battery_life_extender(struct device *dev, 736cb5b5c91SCorentin Chary struct device_attribute *attr, 737cb5b5c91SCorentin Chary char *buf) 738cb5b5c91SCorentin Chary { 739cb5b5c91SCorentin Chary struct samsung_laptop *samsung = dev_get_drvdata(dev); 740cb5b5c91SCorentin Chary int ret; 741cb5b5c91SCorentin Chary 742cb5b5c91SCorentin Chary ret = read_battery_life_extender(samsung); 743cb5b5c91SCorentin Chary if (ret < 0) 744cb5b5c91SCorentin Chary return ret; 745cb5b5c91SCorentin Chary 746cb5b5c91SCorentin Chary return sprintf(buf, "%d\n", ret); 747cb5b5c91SCorentin Chary } 748cb5b5c91SCorentin Chary 749cb5b5c91SCorentin Chary static ssize_t set_battery_life_extender(struct device *dev, 750cb5b5c91SCorentin Chary struct device_attribute *attr, 751cb5b5c91SCorentin Chary const char *buf, size_t count) 752cb5b5c91SCorentin Chary { 753cb5b5c91SCorentin Chary struct samsung_laptop *samsung = dev_get_drvdata(dev); 754cb5b5c91SCorentin Chary int ret, value; 755cb5b5c91SCorentin Chary 756cb5b5c91SCorentin Chary if (!count || sscanf(buf, "%i", &value) != 1) 757cb5b5c91SCorentin Chary return -EINVAL; 758cb5b5c91SCorentin Chary 759cb5b5c91SCorentin Chary ret = write_battery_life_extender(samsung, !!value); 760cb5b5c91SCorentin Chary if (ret < 0) 761cb5b5c91SCorentin Chary return ret; 762cb5b5c91SCorentin Chary 763cb5b5c91SCorentin Chary return count; 764cb5b5c91SCorentin Chary } 765cb5b5c91SCorentin Chary 766cb5b5c91SCorentin Chary static DEVICE_ATTR(battery_life_extender, S_IWUSR | S_IRUGO, 767cb5b5c91SCorentin Chary get_battery_life_extender, set_battery_life_extender); 768cb5b5c91SCorentin Chary 7693a75d378SCorentin Chary static int read_usb_charge(struct samsung_laptop *samsung) 7703a75d378SCorentin Chary { 7713a75d378SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 7723a75d378SCorentin Chary struct sabi_data data; 7733a75d378SCorentin Chary int retval; 7743a75d378SCorentin Chary 7753a75d378SCorentin Chary if (commands->get_usb_charge == 0xFFFF) 7763a75d378SCorentin Chary return -ENODEV; 7773a75d378SCorentin Chary 7783a75d378SCorentin Chary memset(&data, 0, sizeof(data)); 7793a75d378SCorentin Chary data.data[0] = 0x80; 7803a75d378SCorentin Chary retval = sabi_command(samsung, commands->get_usb_charge, 7813a75d378SCorentin Chary &data, &data); 7823a75d378SCorentin Chary 7833a75d378SCorentin Chary if (retval) 7843a75d378SCorentin Chary return retval; 7853a75d378SCorentin Chary 7863a75d378SCorentin Chary if (data.data[0] != 0 && data.data[0] != 1) 7873a75d378SCorentin Chary return -ENODEV; 7883a75d378SCorentin Chary 7893a75d378SCorentin Chary return data.data[0]; 7903a75d378SCorentin Chary } 7913a75d378SCorentin Chary 7923a75d378SCorentin Chary static int write_usb_charge(struct samsung_laptop *samsung, 7933a75d378SCorentin Chary int enabled) 7943a75d378SCorentin Chary { 7953a75d378SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 7963a75d378SCorentin Chary struct sabi_data data; 7973a75d378SCorentin Chary 7983a75d378SCorentin Chary memset(&data, 0, sizeof(data)); 7993a75d378SCorentin Chary data.data[0] = 0x80 | enabled; 8003a75d378SCorentin Chary return sabi_command(samsung, commands->set_usb_charge, 8013a75d378SCorentin Chary &data, NULL); 8023a75d378SCorentin Chary } 8033a75d378SCorentin Chary 8043a75d378SCorentin Chary static ssize_t get_usb_charge(struct device *dev, 8053a75d378SCorentin Chary struct device_attribute *attr, 8063a75d378SCorentin Chary char *buf) 8073a75d378SCorentin Chary { 8083a75d378SCorentin Chary struct samsung_laptop *samsung = dev_get_drvdata(dev); 8093a75d378SCorentin Chary int ret; 8103a75d378SCorentin Chary 8113a75d378SCorentin Chary ret = read_usb_charge(samsung); 8123a75d378SCorentin Chary if (ret < 0) 8133a75d378SCorentin Chary return ret; 8143a75d378SCorentin Chary 8153a75d378SCorentin Chary return sprintf(buf, "%d\n", ret); 8163a75d378SCorentin Chary } 8173a75d378SCorentin Chary 8183a75d378SCorentin Chary static ssize_t set_usb_charge(struct device *dev, 8193a75d378SCorentin Chary struct device_attribute *attr, 8203a75d378SCorentin Chary const char *buf, size_t count) 8213a75d378SCorentin Chary { 8223a75d378SCorentin Chary struct samsung_laptop *samsung = dev_get_drvdata(dev); 8233a75d378SCorentin Chary int ret, value; 8243a75d378SCorentin Chary 8253a75d378SCorentin Chary if (!count || sscanf(buf, "%i", &value) != 1) 8263a75d378SCorentin Chary return -EINVAL; 8273a75d378SCorentin Chary 8283a75d378SCorentin Chary ret = write_usb_charge(samsung, !!value); 8293a75d378SCorentin Chary if (ret < 0) 8303a75d378SCorentin Chary return ret; 8313a75d378SCorentin Chary 8323a75d378SCorentin Chary return count; 8333a75d378SCorentin Chary } 8343a75d378SCorentin Chary 8353a75d378SCorentin Chary static DEVICE_ATTR(usb_charge, S_IWUSR | S_IRUGO, 8363a75d378SCorentin Chary get_usb_charge, set_usb_charge); 8373a75d378SCorentin Chary 838a66c1662SCorentin Chary static struct attribute *platform_attributes[] = { 839a66c1662SCorentin Chary &dev_attr_performance_level.attr, 840cb5b5c91SCorentin Chary &dev_attr_battery_life_extender.attr, 8413a75d378SCorentin Chary &dev_attr_usb_charge.attr, 842a66c1662SCorentin Chary NULL 843a66c1662SCorentin Chary }; 8442d70b73aSGreg Kroah-Hartman 8455dea7a20SCorentin Chary static int find_signature(void __iomem *memcheck, const char *testStr) 8465dea7a20SCorentin Chary { 8475dea7a20SCorentin Chary int i = 0; 8485dea7a20SCorentin Chary int loca; 8495dea7a20SCorentin Chary 8505dea7a20SCorentin Chary for (loca = 0; loca < 0xffff; loca++) { 8515dea7a20SCorentin Chary char temp = readb(memcheck + loca); 8525dea7a20SCorentin Chary 8535dea7a20SCorentin Chary if (temp == testStr[i]) { 8545dea7a20SCorentin Chary if (i == strlen(testStr)-1) 8555dea7a20SCorentin Chary break; 8565dea7a20SCorentin Chary ++i; 8575dea7a20SCorentin Chary } else { 8585dea7a20SCorentin Chary i = 0; 8595dea7a20SCorentin Chary } 8605dea7a20SCorentin Chary } 8615dea7a20SCorentin Chary return loca; 8625dea7a20SCorentin Chary } 8635dea7a20SCorentin Chary 8645dea7a20SCorentin Chary static void samsung_rfkill_exit(struct samsung_laptop *samsung) 8655dea7a20SCorentin Chary { 86684d482f2SCorentin Chary if (samsung->wlan.rfkill) { 86784d482f2SCorentin Chary rfkill_unregister(samsung->wlan.rfkill); 86884d482f2SCorentin Chary rfkill_destroy(samsung->wlan.rfkill); 86984d482f2SCorentin Chary samsung->wlan.rfkill = NULL; 8705dea7a20SCorentin Chary } 87184d482f2SCorentin Chary if (samsung->bluetooth.rfkill) { 87284d482f2SCorentin Chary rfkill_unregister(samsung->bluetooth.rfkill); 87384d482f2SCorentin Chary rfkill_destroy(samsung->bluetooth.rfkill); 87484d482f2SCorentin Chary samsung->bluetooth.rfkill = NULL; 87584d482f2SCorentin Chary } 87684d482f2SCorentin Chary } 87784d482f2SCorentin Chary 87884d482f2SCorentin Chary static int samsung_new_rfkill(struct samsung_laptop *samsung, 87984d482f2SCorentin Chary struct samsung_rfkill *arfkill, 88084d482f2SCorentin Chary const char *name, enum rfkill_type type, 88184d482f2SCorentin Chary const struct rfkill_ops *ops, 88284d482f2SCorentin Chary int blocked) 88384d482f2SCorentin Chary { 88484d482f2SCorentin Chary struct rfkill **rfkill = &arfkill->rfkill; 88584d482f2SCorentin Chary int ret; 88684d482f2SCorentin Chary 88784d482f2SCorentin Chary arfkill->type = type; 88884d482f2SCorentin Chary arfkill->samsung = samsung; 88984d482f2SCorentin Chary 89084d482f2SCorentin Chary *rfkill = rfkill_alloc(name, &samsung->platform_device->dev, 89184d482f2SCorentin Chary type, ops, arfkill); 89284d482f2SCorentin Chary 89384d482f2SCorentin Chary if (!*rfkill) 89484d482f2SCorentin Chary return -EINVAL; 89584d482f2SCorentin Chary 89684d482f2SCorentin Chary if (blocked != -1) 89784d482f2SCorentin Chary rfkill_init_sw_state(*rfkill, blocked); 89884d482f2SCorentin Chary 89984d482f2SCorentin Chary ret = rfkill_register(*rfkill); 90084d482f2SCorentin Chary if (ret) { 90184d482f2SCorentin Chary rfkill_destroy(*rfkill); 90284d482f2SCorentin Chary *rfkill = NULL; 90384d482f2SCorentin Chary return ret; 90484d482f2SCorentin Chary } 90584d482f2SCorentin Chary return 0; 90684d482f2SCorentin Chary } 90784d482f2SCorentin Chary 90884d482f2SCorentin Chary static int __init samsung_rfkill_init_seclinux(struct samsung_laptop *samsung) 90984d482f2SCorentin Chary { 91084d482f2SCorentin Chary return samsung_new_rfkill(samsung, &samsung->wlan, "samsung-wlan", 91184d482f2SCorentin Chary RFKILL_TYPE_WLAN, &seclinux_rfkill_ops, -1); 91284d482f2SCorentin Chary } 91384d482f2SCorentin Chary 91484d482f2SCorentin Chary static int __init samsung_rfkill_init_swsmi(struct samsung_laptop *samsung) 91584d482f2SCorentin Chary { 91684d482f2SCorentin Chary struct sabi_data data; 91784d482f2SCorentin Chary int ret; 91884d482f2SCorentin Chary 91984d482f2SCorentin Chary ret = swsmi_wireless_status(samsung, &data); 92020db88e3SCorentin Chary if (ret) { 92120db88e3SCorentin Chary /* Some swsmi laptops use the old seclinux way to control 92220db88e3SCorentin Chary * wireless devices */ 92320db88e3SCorentin Chary if (ret == -EINVAL) 92420db88e3SCorentin Chary ret = samsung_rfkill_init_seclinux(samsung); 92584d482f2SCorentin Chary return ret; 92620db88e3SCorentin Chary } 92784d482f2SCorentin Chary 92884d482f2SCorentin Chary /* 0x02 seems to mean that the device is no present/available */ 92984d482f2SCorentin Chary 93084d482f2SCorentin Chary if (data.data[WL_STATUS_WLAN] != 0x02) 93184d482f2SCorentin Chary ret = samsung_new_rfkill(samsung, &samsung->wlan, 93284d482f2SCorentin Chary "samsung-wlan", 93384d482f2SCorentin Chary RFKILL_TYPE_WLAN, 93484d482f2SCorentin Chary &swsmi_rfkill_ops, 93584d482f2SCorentin Chary !data.data[WL_STATUS_WLAN]); 93684d482f2SCorentin Chary if (ret) 93784d482f2SCorentin Chary goto exit; 93884d482f2SCorentin Chary 93984d482f2SCorentin Chary if (data.data[WL_STATUS_BT] != 0x02) 94084d482f2SCorentin Chary ret = samsung_new_rfkill(samsung, &samsung->bluetooth, 94184d482f2SCorentin Chary "samsung-bluetooth", 94284d482f2SCorentin Chary RFKILL_TYPE_BLUETOOTH, 94384d482f2SCorentin Chary &swsmi_rfkill_ops, 94484d482f2SCorentin Chary !data.data[WL_STATUS_BT]); 94584d482f2SCorentin Chary if (ret) 94684d482f2SCorentin Chary goto exit; 94784d482f2SCorentin Chary 94884d482f2SCorentin Chary exit: 94984d482f2SCorentin Chary if (ret) 95084d482f2SCorentin Chary samsung_rfkill_exit(samsung); 95184d482f2SCorentin Chary 95284d482f2SCorentin Chary return ret; 9535dea7a20SCorentin Chary } 9545dea7a20SCorentin Chary 9555dea7a20SCorentin Chary static int __init samsung_rfkill_init(struct samsung_laptop *samsung) 9565dea7a20SCorentin Chary { 95784d482f2SCorentin Chary if (samsung->config->sabi_version == 2) 95884d482f2SCorentin Chary return samsung_rfkill_init_seclinux(samsung); 95984d482f2SCorentin Chary if (samsung->config->sabi_version == 3) 96084d482f2SCorentin Chary return samsung_rfkill_init_swsmi(samsung); 9615dea7a20SCorentin Chary return 0; 9625dea7a20SCorentin Chary } 9635dea7a20SCorentin Chary 964f674ebf1SCorentin Chary static int kbd_backlight_enable(struct samsung_laptop *samsung) 965f674ebf1SCorentin Chary { 966f674ebf1SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 967f674ebf1SCorentin Chary struct sabi_data data; 968f674ebf1SCorentin Chary int retval; 969f674ebf1SCorentin Chary 970f674ebf1SCorentin Chary if (commands->kbd_backlight == 0xFFFF) 971f674ebf1SCorentin Chary return -ENODEV; 972f674ebf1SCorentin Chary 973f674ebf1SCorentin Chary memset(&data, 0, sizeof(data)); 974f674ebf1SCorentin Chary data.d0 = 0xaabb; 975f674ebf1SCorentin Chary retval = sabi_command(samsung, commands->kbd_backlight, 976f674ebf1SCorentin Chary &data, &data); 977f674ebf1SCorentin Chary 978f674ebf1SCorentin Chary if (retval) 979f674ebf1SCorentin Chary return retval; 980f674ebf1SCorentin Chary 981f674ebf1SCorentin Chary if (data.d0 != 0xccdd) 982f674ebf1SCorentin Chary return -ENODEV; 983f674ebf1SCorentin Chary return 0; 984f674ebf1SCorentin Chary } 985f674ebf1SCorentin Chary 986f674ebf1SCorentin Chary static int kbd_backlight_read(struct samsung_laptop *samsung) 987f674ebf1SCorentin Chary { 988f674ebf1SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 989f674ebf1SCorentin Chary struct sabi_data data; 990f674ebf1SCorentin Chary int retval; 991f674ebf1SCorentin Chary 992f674ebf1SCorentin Chary memset(&data, 0, sizeof(data)); 993f674ebf1SCorentin Chary data.data[0] = 0x81; 994f674ebf1SCorentin Chary retval = sabi_command(samsung, commands->kbd_backlight, 995f674ebf1SCorentin Chary &data, &data); 996f674ebf1SCorentin Chary 997f674ebf1SCorentin Chary if (retval) 998f674ebf1SCorentin Chary return retval; 999f674ebf1SCorentin Chary 1000f674ebf1SCorentin Chary return data.data[0]; 1001f674ebf1SCorentin Chary } 1002f674ebf1SCorentin Chary 1003f674ebf1SCorentin Chary static int kbd_backlight_write(struct samsung_laptop *samsung, int brightness) 1004f674ebf1SCorentin Chary { 1005f674ebf1SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 1006f674ebf1SCorentin Chary struct sabi_data data; 1007f674ebf1SCorentin Chary 1008f674ebf1SCorentin Chary memset(&data, 0, sizeof(data)); 1009f674ebf1SCorentin Chary data.d0 = 0x82 | ((brightness & 0xFF) << 8); 1010f674ebf1SCorentin Chary return sabi_command(samsung, commands->kbd_backlight, 1011f674ebf1SCorentin Chary &data, NULL); 1012f674ebf1SCorentin Chary } 1013f674ebf1SCorentin Chary 1014f674ebf1SCorentin Chary static void kbd_led_update(struct work_struct *work) 1015f674ebf1SCorentin Chary { 1016f674ebf1SCorentin Chary struct samsung_laptop *samsung; 1017f674ebf1SCorentin Chary 1018f674ebf1SCorentin Chary samsung = container_of(work, struct samsung_laptop, kbd_led_work); 1019f674ebf1SCorentin Chary kbd_backlight_write(samsung, samsung->kbd_led_wk); 1020f674ebf1SCorentin Chary } 1021f674ebf1SCorentin Chary 1022f674ebf1SCorentin Chary static void kbd_led_set(struct led_classdev *led_cdev, 1023f674ebf1SCorentin Chary enum led_brightness value) 1024f674ebf1SCorentin Chary { 1025f674ebf1SCorentin Chary struct samsung_laptop *samsung; 1026f674ebf1SCorentin Chary 1027f674ebf1SCorentin Chary samsung = container_of(led_cdev, struct samsung_laptop, kbd_led); 1028f674ebf1SCorentin Chary 1029f674ebf1SCorentin Chary if (value > samsung->kbd_led.max_brightness) 1030f674ebf1SCorentin Chary value = samsung->kbd_led.max_brightness; 1031f674ebf1SCorentin Chary else if (value < 0) 1032f674ebf1SCorentin Chary value = 0; 1033f674ebf1SCorentin Chary 1034f674ebf1SCorentin Chary samsung->kbd_led_wk = value; 1035f674ebf1SCorentin Chary queue_work(samsung->led_workqueue, &samsung->kbd_led_work); 1036f674ebf1SCorentin Chary } 1037f674ebf1SCorentin Chary 1038f674ebf1SCorentin Chary static enum led_brightness kbd_led_get(struct led_classdev *led_cdev) 1039f674ebf1SCorentin Chary { 1040f674ebf1SCorentin Chary struct samsung_laptop *samsung; 1041f674ebf1SCorentin Chary 1042f674ebf1SCorentin Chary samsung = container_of(led_cdev, struct samsung_laptop, kbd_led); 1043f674ebf1SCorentin Chary return kbd_backlight_read(samsung); 1044f674ebf1SCorentin Chary } 1045f674ebf1SCorentin Chary 1046f674ebf1SCorentin Chary static void samsung_leds_exit(struct samsung_laptop *samsung) 1047f674ebf1SCorentin Chary { 1048f674ebf1SCorentin Chary if (!IS_ERR_OR_NULL(samsung->kbd_led.dev)) 1049f674ebf1SCorentin Chary led_classdev_unregister(&samsung->kbd_led); 1050f674ebf1SCorentin Chary if (samsung->led_workqueue) 1051f674ebf1SCorentin Chary destroy_workqueue(samsung->led_workqueue); 1052f674ebf1SCorentin Chary } 1053f674ebf1SCorentin Chary 1054f674ebf1SCorentin Chary static int __init samsung_leds_init(struct samsung_laptop *samsung) 1055f674ebf1SCorentin Chary { 1056f674ebf1SCorentin Chary int ret = 0; 1057f674ebf1SCorentin Chary 1058f674ebf1SCorentin Chary samsung->led_workqueue = create_singlethread_workqueue("led_workqueue"); 1059f674ebf1SCorentin Chary if (!samsung->led_workqueue) 1060f674ebf1SCorentin Chary return -ENOMEM; 1061f674ebf1SCorentin Chary 1062f674ebf1SCorentin Chary if (kbd_backlight_enable(samsung) >= 0) { 1063f674ebf1SCorentin Chary INIT_WORK(&samsung->kbd_led_work, kbd_led_update); 1064f674ebf1SCorentin Chary 1065f674ebf1SCorentin Chary samsung->kbd_led.name = "samsung::kbd_backlight"; 1066f674ebf1SCorentin Chary samsung->kbd_led.brightness_set = kbd_led_set; 1067f674ebf1SCorentin Chary samsung->kbd_led.brightness_get = kbd_led_get; 1068f674ebf1SCorentin Chary samsung->kbd_led.max_brightness = 8; 10690ca849eaSScott Thrasher if (samsung->quirks->four_kbd_backlight_levels) 10700ca849eaSScott Thrasher samsung->kbd_led.max_brightness = 4; 1071f674ebf1SCorentin Chary 1072f674ebf1SCorentin Chary ret = led_classdev_register(&samsung->platform_device->dev, 1073f674ebf1SCorentin Chary &samsung->kbd_led); 1074f674ebf1SCorentin Chary } 1075f674ebf1SCorentin Chary 1076f674ebf1SCorentin Chary if (ret) 1077f674ebf1SCorentin Chary samsung_leds_exit(samsung); 1078f674ebf1SCorentin Chary 1079f674ebf1SCorentin Chary return ret; 1080f674ebf1SCorentin Chary } 1081f674ebf1SCorentin Chary 10825dea7a20SCorentin Chary static void samsung_backlight_exit(struct samsung_laptop *samsung) 10835dea7a20SCorentin Chary { 10845dea7a20SCorentin Chary if (samsung->backlight_device) { 10855dea7a20SCorentin Chary backlight_device_unregister(samsung->backlight_device); 10865dea7a20SCorentin Chary samsung->backlight_device = NULL; 10875dea7a20SCorentin Chary } 10885dea7a20SCorentin Chary } 10895dea7a20SCorentin Chary 10905dea7a20SCorentin Chary static int __init samsung_backlight_init(struct samsung_laptop *samsung) 10915dea7a20SCorentin Chary { 10925dea7a20SCorentin Chary struct backlight_device *bd; 10935dea7a20SCorentin Chary struct backlight_properties props; 10945dea7a20SCorentin Chary 1095f34cd9caSCorentin Chary if (!samsung->handle_backlight) 1096f34cd9caSCorentin Chary return 0; 1097f34cd9caSCorentin Chary 10985dea7a20SCorentin Chary memset(&props, 0, sizeof(struct backlight_properties)); 10995dea7a20SCorentin Chary props.type = BACKLIGHT_PLATFORM; 11005dea7a20SCorentin Chary props.max_brightness = samsung->config->max_brightness - 11015dea7a20SCorentin Chary samsung->config->min_brightness; 11025dea7a20SCorentin Chary 11035dea7a20SCorentin Chary bd = backlight_device_register("samsung", 11045dea7a20SCorentin Chary &samsung->platform_device->dev, 11055dea7a20SCorentin Chary samsung, &backlight_ops, 11065dea7a20SCorentin Chary &props); 11075dea7a20SCorentin Chary if (IS_ERR(bd)) 11085dea7a20SCorentin Chary return PTR_ERR(bd); 11095dea7a20SCorentin Chary 11105dea7a20SCorentin Chary samsung->backlight_device = bd; 11115dea7a20SCorentin Chary samsung->backlight_device->props.brightness = read_brightness(samsung); 11125dea7a20SCorentin Chary samsung->backlight_device->props.power = FB_BLANK_UNBLANK; 11135dea7a20SCorentin Chary backlight_update_status(samsung->backlight_device); 11145dea7a20SCorentin Chary 11155dea7a20SCorentin Chary return 0; 11165dea7a20SCorentin Chary } 11175dea7a20SCorentin Chary 1118bde9e509SDan Carpenter static umode_t samsung_sysfs_is_visible(struct kobject *kobj, 1119a66c1662SCorentin Chary struct attribute *attr, int idx) 1120a66c1662SCorentin Chary { 1121a66c1662SCorentin Chary struct device *dev = container_of(kobj, struct device, kobj); 1122a66c1662SCorentin Chary struct platform_device *pdev = to_platform_device(dev); 1123a66c1662SCorentin Chary struct samsung_laptop *samsung = platform_get_drvdata(pdev); 1124a66c1662SCorentin Chary bool ok = true; 1125a66c1662SCorentin Chary 1126a66c1662SCorentin Chary if (attr == &dev_attr_performance_level.attr) 1127a66c1662SCorentin Chary ok = !!samsung->config->performance_levels[0].name; 1128cb5b5c91SCorentin Chary if (attr == &dev_attr_battery_life_extender.attr) 1129cb5b5c91SCorentin Chary ok = !!(read_battery_life_extender(samsung) >= 0); 11303a75d378SCorentin Chary if (attr == &dev_attr_usb_charge.attr) 11313a75d378SCorentin Chary ok = !!(read_usb_charge(samsung) >= 0); 1132a66c1662SCorentin Chary 1133a66c1662SCorentin Chary return ok ? attr->mode : 0; 1134a66c1662SCorentin Chary } 1135a66c1662SCorentin Chary 1136a66c1662SCorentin Chary static struct attribute_group platform_attribute_group = { 1137a66c1662SCorentin Chary .is_visible = samsung_sysfs_is_visible, 1138a66c1662SCorentin Chary .attrs = platform_attributes 1139a66c1662SCorentin Chary }; 1140a66c1662SCorentin Chary 11415dea7a20SCorentin Chary static void samsung_sysfs_exit(struct samsung_laptop *samsung) 11425dea7a20SCorentin Chary { 1143a66c1662SCorentin Chary struct platform_device *device = samsung->platform_device; 1144a66c1662SCorentin Chary 1145a66c1662SCorentin Chary sysfs_remove_group(&device->dev.kobj, &platform_attribute_group); 11465dea7a20SCorentin Chary } 11475dea7a20SCorentin Chary 11485dea7a20SCorentin Chary static int __init samsung_sysfs_init(struct samsung_laptop *samsung) 11495dea7a20SCorentin Chary { 1150a66c1662SCorentin Chary struct platform_device *device = samsung->platform_device; 1151a66c1662SCorentin Chary 1152a66c1662SCorentin Chary return sysfs_create_group(&device->dev.kobj, &platform_attribute_group); 1153a66c1662SCorentin Chary 11545dea7a20SCorentin Chary } 11555dea7a20SCorentin Chary 11565b80fc40SCorentin Chary static int show_call(struct seq_file *m, void *data) 11575b80fc40SCorentin Chary { 11585b80fc40SCorentin Chary struct samsung_laptop *samsung = m->private; 11595b80fc40SCorentin Chary struct sabi_data *sdata = &samsung->debug.data; 11605b80fc40SCorentin Chary int ret; 11615b80fc40SCorentin Chary 11625b80fc40SCorentin Chary seq_printf(m, "SABI 0x%04x {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n", 11635b80fc40SCorentin Chary samsung->debug.command, 11645b80fc40SCorentin Chary sdata->d0, sdata->d1, sdata->d2, sdata->d3); 11655b80fc40SCorentin Chary 11665b80fc40SCorentin Chary ret = sabi_command(samsung, samsung->debug.command, sdata, sdata); 11675b80fc40SCorentin Chary 11685b80fc40SCorentin Chary if (ret) { 11695b80fc40SCorentin Chary seq_printf(m, "SABI command 0x%04x failed\n", 11705b80fc40SCorentin Chary samsung->debug.command); 11715b80fc40SCorentin Chary return ret; 11725b80fc40SCorentin Chary } 11735b80fc40SCorentin Chary 11745b80fc40SCorentin Chary seq_printf(m, "SABI {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n", 11755b80fc40SCorentin Chary sdata->d0, sdata->d1, sdata->d2, sdata->d3); 11765b80fc40SCorentin Chary return 0; 11775b80fc40SCorentin Chary } 11785b80fc40SCorentin Chary 11795b80fc40SCorentin Chary static int samsung_debugfs_open(struct inode *inode, struct file *file) 11805b80fc40SCorentin Chary { 11815b80fc40SCorentin Chary return single_open(file, show_call, inode->i_private); 11825b80fc40SCorentin Chary } 11835b80fc40SCorentin Chary 11845b80fc40SCorentin Chary static const struct file_operations samsung_laptop_call_io_ops = { 11855b80fc40SCorentin Chary .owner = THIS_MODULE, 11865b80fc40SCorentin Chary .open = samsung_debugfs_open, 11875b80fc40SCorentin Chary .read = seq_read, 11885b80fc40SCorentin Chary .llseek = seq_lseek, 11895b80fc40SCorentin Chary .release = single_release, 11905b80fc40SCorentin Chary }; 11915b80fc40SCorentin Chary 11925b80fc40SCorentin Chary static void samsung_debugfs_exit(struct samsung_laptop *samsung) 11935b80fc40SCorentin Chary { 11945b80fc40SCorentin Chary debugfs_remove_recursive(samsung->debug.root); 11955b80fc40SCorentin Chary } 11965b80fc40SCorentin Chary 11975b80fc40SCorentin Chary static int samsung_debugfs_init(struct samsung_laptop *samsung) 11985b80fc40SCorentin Chary { 11995b80fc40SCorentin Chary struct dentry *dent; 12005b80fc40SCorentin Chary 12015b80fc40SCorentin Chary samsung->debug.root = debugfs_create_dir("samsung-laptop", NULL); 12025b80fc40SCorentin Chary if (!samsung->debug.root) { 12035b80fc40SCorentin Chary pr_err("failed to create debugfs directory"); 12045b80fc40SCorentin Chary goto error_debugfs; 12055b80fc40SCorentin Chary } 12065b80fc40SCorentin Chary 12075b80fc40SCorentin Chary samsung->debug.f0000_wrapper.data = samsung->f0000_segment; 12085b80fc40SCorentin Chary samsung->debug.f0000_wrapper.size = 0xffff; 12095b80fc40SCorentin Chary 12105b80fc40SCorentin Chary samsung->debug.data_wrapper.data = &samsung->debug.data; 12115b80fc40SCorentin Chary samsung->debug.data_wrapper.size = sizeof(samsung->debug.data); 12125b80fc40SCorentin Chary 12136f6ae06eSCorentin Chary samsung->debug.sdiag_wrapper.data = samsung->sdiag; 12146f6ae06eSCorentin Chary samsung->debug.sdiag_wrapper.size = strlen(samsung->sdiag); 12156f6ae06eSCorentin Chary 12165b80fc40SCorentin Chary dent = debugfs_create_u16("command", S_IRUGO | S_IWUSR, 12175b80fc40SCorentin Chary samsung->debug.root, &samsung->debug.command); 12185b80fc40SCorentin Chary if (!dent) 12195b80fc40SCorentin Chary goto error_debugfs; 12205b80fc40SCorentin Chary 12215b80fc40SCorentin Chary dent = debugfs_create_u32("d0", S_IRUGO | S_IWUSR, samsung->debug.root, 12225b80fc40SCorentin Chary &samsung->debug.data.d0); 12235b80fc40SCorentin Chary if (!dent) 12245b80fc40SCorentin Chary goto error_debugfs; 12255b80fc40SCorentin Chary 12265b80fc40SCorentin Chary dent = debugfs_create_u32("d1", S_IRUGO | S_IWUSR, samsung->debug.root, 12275b80fc40SCorentin Chary &samsung->debug.data.d1); 12285b80fc40SCorentin Chary if (!dent) 12295b80fc40SCorentin Chary goto error_debugfs; 12305b80fc40SCorentin Chary 12315b80fc40SCorentin Chary dent = debugfs_create_u16("d2", S_IRUGO | S_IWUSR, samsung->debug.root, 12325b80fc40SCorentin Chary &samsung->debug.data.d2); 12335b80fc40SCorentin Chary if (!dent) 12345b80fc40SCorentin Chary goto error_debugfs; 12355b80fc40SCorentin Chary 12365b80fc40SCorentin Chary dent = debugfs_create_u8("d3", S_IRUGO | S_IWUSR, samsung->debug.root, 12375b80fc40SCorentin Chary &samsung->debug.data.d3); 12385b80fc40SCorentin Chary if (!dent) 12395b80fc40SCorentin Chary goto error_debugfs; 12405b80fc40SCorentin Chary 12415b80fc40SCorentin Chary dent = debugfs_create_blob("data", S_IRUGO | S_IWUSR, 12425b80fc40SCorentin Chary samsung->debug.root, 12435b80fc40SCorentin Chary &samsung->debug.data_wrapper); 12445b80fc40SCorentin Chary if (!dent) 12455b80fc40SCorentin Chary goto error_debugfs; 12465b80fc40SCorentin Chary 12475b80fc40SCorentin Chary dent = debugfs_create_blob("f0000_segment", S_IRUSR | S_IWUSR, 12485b80fc40SCorentin Chary samsung->debug.root, 12495b80fc40SCorentin Chary &samsung->debug.f0000_wrapper); 12505b80fc40SCorentin Chary if (!dent) 12515b80fc40SCorentin Chary goto error_debugfs; 12525b80fc40SCorentin Chary 12535b80fc40SCorentin Chary dent = debugfs_create_file("call", S_IFREG | S_IRUGO, 12545b80fc40SCorentin Chary samsung->debug.root, samsung, 12555b80fc40SCorentin Chary &samsung_laptop_call_io_ops); 12565b80fc40SCorentin Chary if (!dent) 12575b80fc40SCorentin Chary goto error_debugfs; 12585b80fc40SCorentin Chary 12596f6ae06eSCorentin Chary dent = debugfs_create_blob("sdiag", S_IRUGO | S_IWUSR, 12606f6ae06eSCorentin Chary samsung->debug.root, 12616f6ae06eSCorentin Chary &samsung->debug.sdiag_wrapper); 12626f6ae06eSCorentin Chary if (!dent) 12636f6ae06eSCorentin Chary goto error_debugfs; 12646f6ae06eSCorentin Chary 12655b80fc40SCorentin Chary return 0; 12665b80fc40SCorentin Chary 12675b80fc40SCorentin Chary error_debugfs: 12685b80fc40SCorentin Chary samsung_debugfs_exit(samsung); 12695b80fc40SCorentin Chary return -ENOMEM; 12705b80fc40SCorentin Chary } 12715b80fc40SCorentin Chary 12725dea7a20SCorentin Chary static void samsung_sabi_exit(struct samsung_laptop *samsung) 12735dea7a20SCorentin Chary { 12745dea7a20SCorentin Chary const struct sabi_config *config = samsung->config; 12755dea7a20SCorentin Chary 12765dea7a20SCorentin Chary /* Turn off "Linux" mode in the BIOS */ 12775dea7a20SCorentin Chary if (config && config->commands.set_linux != 0xff) 12787e960711SCorentin Chary sabi_set_commandb(samsung, config->commands.set_linux, 0x80); 12795dea7a20SCorentin Chary 12805dea7a20SCorentin Chary if (samsung->sabi_iface) { 12815dea7a20SCorentin Chary iounmap(samsung->sabi_iface); 12825dea7a20SCorentin Chary samsung->sabi_iface = NULL; 12835dea7a20SCorentin Chary } 12845dea7a20SCorentin Chary if (samsung->f0000_segment) { 12855dea7a20SCorentin Chary iounmap(samsung->f0000_segment); 12865dea7a20SCorentin Chary samsung->f0000_segment = NULL; 12875dea7a20SCorentin Chary } 12885dea7a20SCorentin Chary 12895dea7a20SCorentin Chary samsung->config = NULL; 12905dea7a20SCorentin Chary } 12915dea7a20SCorentin Chary 129249dd7730SCorentin Chary static __init void samsung_sabi_infos(struct samsung_laptop *samsung, int loca, 129349dd7730SCorentin Chary unsigned int ifaceP) 12945dea7a20SCorentin Chary { 12955dea7a20SCorentin Chary const struct sabi_config *config = samsung->config; 12965dea7a20SCorentin Chary 12975dea7a20SCorentin Chary printk(KERN_DEBUG "This computer supports SABI==%x\n", 12985dea7a20SCorentin Chary loca + 0xf0000 - 6); 129949dd7730SCorentin Chary 13005dea7a20SCorentin Chary printk(KERN_DEBUG "SABI header:\n"); 13015dea7a20SCorentin Chary printk(KERN_DEBUG " SMI Port Number = 0x%04x\n", 13025dea7a20SCorentin Chary readw(samsung->sabi + config->header_offsets.port)); 13035dea7a20SCorentin Chary printk(KERN_DEBUG " SMI Interface Function = 0x%02x\n", 13045dea7a20SCorentin Chary readb(samsung->sabi + config->header_offsets.iface_func)); 13055dea7a20SCorentin Chary printk(KERN_DEBUG " SMI enable memory buffer = 0x%02x\n", 13065dea7a20SCorentin Chary readb(samsung->sabi + config->header_offsets.en_mem)); 13075dea7a20SCorentin Chary printk(KERN_DEBUG " SMI restore memory buffer = 0x%02x\n", 13085dea7a20SCorentin Chary readb(samsung->sabi + config->header_offsets.re_mem)); 13095dea7a20SCorentin Chary printk(KERN_DEBUG " SABI data offset = 0x%04x\n", 13105dea7a20SCorentin Chary readw(samsung->sabi + config->header_offsets.data_offset)); 13115dea7a20SCorentin Chary printk(KERN_DEBUG " SABI data segment = 0x%04x\n", 13125dea7a20SCorentin Chary readw(samsung->sabi + config->header_offsets.data_segment)); 13135dea7a20SCorentin Chary 131449dd7730SCorentin Chary printk(KERN_DEBUG " SABI pointer = 0x%08x\n", ifaceP); 13155dea7a20SCorentin Chary } 13165dea7a20SCorentin Chary 13176f6ae06eSCorentin Chary static void __init samsung_sabi_diag(struct samsung_laptop *samsung) 13186f6ae06eSCorentin Chary { 13196f6ae06eSCorentin Chary int loca = find_signature(samsung->f0000_segment, "SDiaG@"); 13206f6ae06eSCorentin Chary int i; 13216f6ae06eSCorentin Chary 13226f6ae06eSCorentin Chary if (loca == 0xffff) 13236f6ae06eSCorentin Chary return ; 13246f6ae06eSCorentin Chary 13256f6ae06eSCorentin Chary /* Example: 13266f6ae06eSCorentin Chary * Ident: @SDiaG@686XX-N90X3A/966-SEC-07HL-S90X3A 13276f6ae06eSCorentin Chary * 13286f6ae06eSCorentin Chary * Product name: 90X3A 13296f6ae06eSCorentin Chary * BIOS Version: 07HL 13306f6ae06eSCorentin Chary */ 13316f6ae06eSCorentin Chary loca += 1; 13326f6ae06eSCorentin Chary for (i = 0; loca < 0xffff && i < sizeof(samsung->sdiag) - 1; loca++) { 13336f6ae06eSCorentin Chary char temp = readb(samsung->f0000_segment + loca); 13346f6ae06eSCorentin Chary 13356f6ae06eSCorentin Chary if (isalnum(temp) || temp == '/' || temp == '-') 13366f6ae06eSCorentin Chary samsung->sdiag[i++] = temp; 13376f6ae06eSCorentin Chary else 13386f6ae06eSCorentin Chary break ; 13396f6ae06eSCorentin Chary } 13406f6ae06eSCorentin Chary 13416f6ae06eSCorentin Chary if (debug && samsung->sdiag[0]) 13426f6ae06eSCorentin Chary pr_info("sdiag: %s", samsung->sdiag); 13436f6ae06eSCorentin Chary } 13446f6ae06eSCorentin Chary 13455dea7a20SCorentin Chary static int __init samsung_sabi_init(struct samsung_laptop *samsung) 13465dea7a20SCorentin Chary { 13475dea7a20SCorentin Chary const struct sabi_config *config = NULL; 13485dea7a20SCorentin Chary const struct sabi_commands *commands; 13495dea7a20SCorentin Chary unsigned int ifaceP; 13505dea7a20SCorentin Chary int ret = 0; 13515dea7a20SCorentin Chary int i; 13525dea7a20SCorentin Chary int loca; 13535dea7a20SCorentin Chary 13545dea7a20SCorentin Chary samsung->f0000_segment = ioremap_nocache(0xf0000, 0xffff); 13555dea7a20SCorentin Chary if (!samsung->f0000_segment) { 13563be324a9SCorentin Chary if (debug || force) 13575dea7a20SCorentin Chary pr_err("Can't map the segment at 0xf0000\n"); 13585dea7a20SCorentin Chary ret = -EINVAL; 13595dea7a20SCorentin Chary goto exit; 13605dea7a20SCorentin Chary } 13615dea7a20SCorentin Chary 13626f6ae06eSCorentin Chary samsung_sabi_diag(samsung); 13636f6ae06eSCorentin Chary 13645dea7a20SCorentin Chary /* Try to find one of the signatures in memory to find the header */ 13655dea7a20SCorentin Chary for (i = 0; sabi_configs[i].test_string != 0; ++i) { 13665dea7a20SCorentin Chary samsung->config = &sabi_configs[i]; 13675dea7a20SCorentin Chary loca = find_signature(samsung->f0000_segment, 13685dea7a20SCorentin Chary samsung->config->test_string); 13695dea7a20SCorentin Chary if (loca != 0xffff) 13705dea7a20SCorentin Chary break; 13715dea7a20SCorentin Chary } 13725dea7a20SCorentin Chary 13735dea7a20SCorentin Chary if (loca == 0xffff) { 13743be324a9SCorentin Chary if (debug || force) 13755dea7a20SCorentin Chary pr_err("This computer does not support SABI\n"); 13765dea7a20SCorentin Chary ret = -ENODEV; 13775dea7a20SCorentin Chary goto exit; 13785dea7a20SCorentin Chary } 13795dea7a20SCorentin Chary 13805dea7a20SCorentin Chary config = samsung->config; 13815dea7a20SCorentin Chary commands = &config->commands; 13825dea7a20SCorentin Chary 13835dea7a20SCorentin Chary /* point to the SMI port Number */ 13845dea7a20SCorentin Chary loca += 1; 13855dea7a20SCorentin Chary samsung->sabi = (samsung->f0000_segment + loca); 13865dea7a20SCorentin Chary 13875dea7a20SCorentin Chary /* Get a pointer to the SABI Interface */ 13885dea7a20SCorentin Chary ifaceP = (readw(samsung->sabi + config->header_offsets.data_segment) & 0x0ffff) << 4; 13895dea7a20SCorentin Chary ifaceP += readw(samsung->sabi + config->header_offsets.data_offset) & 0x0ffff; 139049dd7730SCorentin Chary 139149dd7730SCorentin Chary if (debug) 139249dd7730SCorentin Chary samsung_sabi_infos(samsung, loca, ifaceP); 139349dd7730SCorentin Chary 13945dea7a20SCorentin Chary samsung->sabi_iface = ioremap_nocache(ifaceP, 16); 13955dea7a20SCorentin Chary if (!samsung->sabi_iface) { 13965dea7a20SCorentin Chary pr_err("Can't remap %x\n", ifaceP); 13975dea7a20SCorentin Chary ret = -EINVAL; 13985dea7a20SCorentin Chary goto exit; 13995dea7a20SCorentin Chary } 14005dea7a20SCorentin Chary 14015dea7a20SCorentin Chary /* Turn on "Linux" mode in the BIOS */ 14025dea7a20SCorentin Chary if (commands->set_linux != 0xff) { 14037e960711SCorentin Chary int retval = sabi_set_commandb(samsung, 14045dea7a20SCorentin Chary commands->set_linux, 0x81); 14055dea7a20SCorentin Chary if (retval) { 14065dea7a20SCorentin Chary pr_warn("Linux mode was not set!\n"); 14075dea7a20SCorentin Chary ret = -ENODEV; 14085dea7a20SCorentin Chary goto exit; 14095dea7a20SCorentin Chary } 14105dea7a20SCorentin Chary } 14115dea7a20SCorentin Chary 14125dea7a20SCorentin Chary /* Check for stepping quirk */ 1413f34cd9caSCorentin Chary if (samsung->handle_backlight) 14145dea7a20SCorentin Chary check_for_stepping_quirk(samsung); 14155dea7a20SCorentin Chary 14162e777187SCorentin Chary pr_info("detected SABI interface: %s\n", 14172e777187SCorentin Chary samsung->config->test_string); 14182e777187SCorentin Chary 14195dea7a20SCorentin Chary exit: 14205dea7a20SCorentin Chary if (ret) 14215dea7a20SCorentin Chary samsung_sabi_exit(samsung); 14225dea7a20SCorentin Chary 14235dea7a20SCorentin Chary return ret; 14245dea7a20SCorentin Chary } 14255dea7a20SCorentin Chary 14265dea7a20SCorentin Chary static void samsung_platform_exit(struct samsung_laptop *samsung) 14275dea7a20SCorentin Chary { 14285dea7a20SCorentin Chary if (samsung->platform_device) { 14295dea7a20SCorentin Chary platform_device_unregister(samsung->platform_device); 14305dea7a20SCorentin Chary samsung->platform_device = NULL; 14315dea7a20SCorentin Chary } 14325dea7a20SCorentin Chary } 14335dea7a20SCorentin Chary 14340ca849eaSScott Thrasher static int samsung_pm_notification(struct notifier_block *nb, 14350ca849eaSScott Thrasher unsigned long val, void *ptr) 14360ca849eaSScott Thrasher { 14370ca849eaSScott Thrasher struct samsung_laptop *samsung; 14380ca849eaSScott Thrasher 14390ca849eaSScott Thrasher samsung = container_of(nb, struct samsung_laptop, pm_nb); 14400ca849eaSScott Thrasher if (val == PM_POST_HIBERNATION && 14410ca849eaSScott Thrasher samsung->quirks->enable_kbd_backlight) 14420ca849eaSScott Thrasher kbd_backlight_enable(samsung); 14430ca849eaSScott Thrasher 14440ca849eaSScott Thrasher return 0; 14450ca849eaSScott Thrasher } 14460ca849eaSScott Thrasher 14475dea7a20SCorentin Chary static int __init samsung_platform_init(struct samsung_laptop *samsung) 14485dea7a20SCorentin Chary { 14495dea7a20SCorentin Chary struct platform_device *pdev; 14505dea7a20SCorentin Chary 14515dea7a20SCorentin Chary pdev = platform_device_register_simple("samsung", -1, NULL, 0); 14525dea7a20SCorentin Chary if (IS_ERR(pdev)) 14535dea7a20SCorentin Chary return PTR_ERR(pdev); 14545dea7a20SCorentin Chary 14555dea7a20SCorentin Chary samsung->platform_device = pdev; 14565dea7a20SCorentin Chary platform_set_drvdata(samsung->platform_device, samsung); 14575dea7a20SCorentin Chary return 0; 14585dea7a20SCorentin Chary } 14595dea7a20SCorentin Chary 1460a979e2e2SCorentin Chary static struct samsung_quirks *quirks; 1461a979e2e2SCorentin Chary 1462a979e2e2SCorentin Chary static int __init samsung_dmi_matched(const struct dmi_system_id *d) 1463a979e2e2SCorentin Chary { 1464a979e2e2SCorentin Chary quirks = d->driver_data; 1465a979e2e2SCorentin Chary return 0; 1466a979e2e2SCorentin Chary } 1467a979e2e2SCorentin Chary 14682d70b73aSGreg Kroah-Hartman static struct dmi_system_id __initdata samsung_dmi_table[] = { 14692d70b73aSGreg Kroah-Hartman { 14702d70b73aSGreg Kroah-Hartman .matches = { 14712d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_SYS_VENDOR, 14722d70b73aSGreg Kroah-Hartman "SAMSUNG ELECTRONICS CO., LTD."), 14733be324a9SCorentin Chary DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */ 14742d70b73aSGreg Kroah-Hartman }, 14752d70b73aSGreg Kroah-Hartman }, 14762d70b73aSGreg Kroah-Hartman { 14772d70b73aSGreg Kroah-Hartman .matches = { 14782d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_SYS_VENDOR, 14792d70b73aSGreg Kroah-Hartman "SAMSUNG ELECTRONICS CO., LTD."), 14803be324a9SCorentin Chary DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /* Laptop */ 14812d70b73aSGreg Kroah-Hartman }, 14822d70b73aSGreg Kroah-Hartman }, 14832d70b73aSGreg Kroah-Hartman { 14844e2441c0SJ Witteveen .matches = { 14854e2441c0SJ Witteveen DMI_MATCH(DMI_SYS_VENDOR, 14864e2441c0SJ Witteveen "SAMSUNG ELECTRONICS CO., LTD."), 14873be324a9SCorentin Chary DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */ 14884e2441c0SJ Witteveen }, 14894e2441c0SJ Witteveen }, 14904e2441c0SJ Witteveen { 14912d70b73aSGreg Kroah-Hartman .matches = { 14922d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_SYS_VENDOR, 14932d70b73aSGreg Kroah-Hartman "SAMSUNG ELECTRONICS CO., LTD."), 14943be324a9SCorentin Chary DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */ 14952d70b73aSGreg Kroah-Hartman }, 14967500eeb0STommaso Massimi }, 1497e052067dSCorentin Chary /* DMI ids for laptops with bad Chassis Type */ 1498e052067dSCorentin Chary { 1499e052067dSCorentin Chary .ident = "R40/R41", 1500e052067dSCorentin Chary .matches = { 1501e052067dSCorentin Chary DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1502e052067dSCorentin Chary DMI_MATCH(DMI_PRODUCT_NAME, "R40/R41"), 1503e052067dSCorentin Chary DMI_MATCH(DMI_BOARD_NAME, "R40/R41"), 1504e052067dSCorentin Chary }, 1505e052067dSCorentin Chary }, 1506a979e2e2SCorentin Chary /* Specific DMI ids for laptop with quirks */ 1507a979e2e2SCorentin Chary { 1508a979e2e2SCorentin Chary .callback = samsung_dmi_matched, 1509a979e2e2SCorentin Chary .ident = "N150P", 1510a979e2e2SCorentin Chary .matches = { 1511a979e2e2SCorentin Chary DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1512a979e2e2SCorentin Chary DMI_MATCH(DMI_PRODUCT_NAME, "N150P"), 1513a979e2e2SCorentin Chary DMI_MATCH(DMI_BOARD_NAME, "N150P"), 1514a979e2e2SCorentin Chary }, 15154690555eSHans de Goede .driver_data = &samsung_use_native_backlight, 1516a979e2e2SCorentin Chary }, 1517a979e2e2SCorentin Chary { 1518a979e2e2SCorentin Chary .callback = samsung_dmi_matched, 1519a979e2e2SCorentin Chary .ident = "N145P/N250P/N260P", 1520a979e2e2SCorentin Chary .matches = { 1521a979e2e2SCorentin Chary DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1522a979e2e2SCorentin Chary DMI_MATCH(DMI_PRODUCT_NAME, "N145P/N250P/N260P"), 1523a979e2e2SCorentin Chary DMI_MATCH(DMI_BOARD_NAME, "N145P/N250P/N260P"), 1524a979e2e2SCorentin Chary }, 15254690555eSHans de Goede .driver_data = &samsung_use_native_backlight, 1526a979e2e2SCorentin Chary }, 1527a979e2e2SCorentin Chary { 1528a979e2e2SCorentin Chary .callback = samsung_dmi_matched, 1529a979e2e2SCorentin Chary .ident = "N150/N210/N220", 1530a979e2e2SCorentin Chary .matches = { 1531a979e2e2SCorentin Chary DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1532a979e2e2SCorentin Chary DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220"), 1533a979e2e2SCorentin Chary DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220"), 1534a979e2e2SCorentin Chary }, 1535a979e2e2SCorentin Chary .driver_data = &samsung_broken_acpi_video, 1536a979e2e2SCorentin Chary }, 1537a979e2e2SCorentin Chary { 1538a979e2e2SCorentin Chary .callback = samsung_dmi_matched, 1539a979e2e2SCorentin Chary .ident = "NF110/NF210/NF310", 1540a979e2e2SCorentin Chary .matches = { 1541a979e2e2SCorentin Chary DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1542a979e2e2SCorentin Chary DMI_MATCH(DMI_PRODUCT_NAME, "NF110/NF210/NF310"), 1543a979e2e2SCorentin Chary DMI_MATCH(DMI_BOARD_NAME, "NF110/NF210/NF310"), 1544a979e2e2SCorentin Chary }, 1545a979e2e2SCorentin Chary .driver_data = &samsung_broken_acpi_video, 1546a979e2e2SCorentin Chary }, 154709d5677cSCorentin Chary { 154809d5677cSCorentin Chary .callback = samsung_dmi_matched, 154909d5677cSCorentin Chary .ident = "X360", 155009d5677cSCorentin Chary .matches = { 155109d5677cSCorentin Chary DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 155209d5677cSCorentin Chary DMI_MATCH(DMI_PRODUCT_NAME, "X360"), 155309d5677cSCorentin Chary DMI_MATCH(DMI_BOARD_NAME, "X360"), 155409d5677cSCorentin Chary }, 155509d5677cSCorentin Chary .driver_data = &samsung_broken_acpi_video, 155609d5677cSCorentin Chary }, 1557e04c200fSSeth Forshee { 1558e04c200fSSeth Forshee .callback = samsung_dmi_matched, 1559e04c200fSSeth Forshee .ident = "N250P", 1560e04c200fSSeth Forshee .matches = { 1561e04c200fSSeth Forshee DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1562e04c200fSSeth Forshee DMI_MATCH(DMI_PRODUCT_NAME, "N250P"), 1563e04c200fSSeth Forshee DMI_MATCH(DMI_BOARD_NAME, "N250P"), 1564e04c200fSSeth Forshee }, 15654690555eSHans de Goede .driver_data = &samsung_use_native_backlight, 1566e04c200fSSeth Forshee }, 15670ca849eaSScott Thrasher { 15680ca849eaSScott Thrasher .callback = samsung_dmi_matched, 15695a1426c9SHans de Goede .ident = "NC210", 15705a1426c9SHans de Goede .matches = { 15715a1426c9SHans de Goede DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 15725a1426c9SHans de Goede DMI_MATCH(DMI_PRODUCT_NAME, "NC210/NC110"), 15735a1426c9SHans de Goede DMI_MATCH(DMI_BOARD_NAME, "NC210/NC110"), 15745a1426c9SHans de Goede }, 15755a1426c9SHans de Goede .driver_data = &samsung_broken_acpi_video, 15765a1426c9SHans de Goede }, 15775a1426c9SHans de Goede { 15785a1426c9SHans de Goede .callback = samsung_dmi_matched, 15790ca849eaSScott Thrasher .ident = "730U3E/740U3E", 15800ca849eaSScott Thrasher .matches = { 15810ca849eaSScott Thrasher DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 15820ca849eaSScott Thrasher DMI_MATCH(DMI_PRODUCT_NAME, "730U3E/740U3E"), 15830ca849eaSScott Thrasher }, 15840ca849eaSScott Thrasher .driver_data = &samsung_np740u3e, 15850ca849eaSScott Thrasher }, 15862d70b73aSGreg Kroah-Hartman { }, 15872d70b73aSGreg Kroah-Hartman }; 15882d70b73aSGreg Kroah-Hartman MODULE_DEVICE_TABLE(dmi, samsung_dmi_table); 15892d70b73aSGreg Kroah-Hartman 15905dea7a20SCorentin Chary static struct platform_device *samsung_platform_device; 15912d70b73aSGreg Kroah-Hartman 15922d70b73aSGreg Kroah-Hartman static int __init samsung_init(void) 15932d70b73aSGreg Kroah-Hartman { 15945dea7a20SCorentin Chary struct samsung_laptop *samsung; 15955dea7a20SCorentin Chary int ret; 15962d70b73aSGreg Kroah-Hartman 1597e0094244SMatt Fleming if (efi_enabled(EFI_BOOT)) 1598e0094244SMatt Fleming return -ENODEV; 1599e0094244SMatt Fleming 1600a979e2e2SCorentin Chary quirks = &samsung_unknown; 16012d70b73aSGreg Kroah-Hartman if (!force && !dmi_check_system(samsung_dmi_table)) 16022d70b73aSGreg Kroah-Hartman return -ENODEV; 16032d70b73aSGreg Kroah-Hartman 1604a6df4894SCorentin Chary samsung = kzalloc(sizeof(*samsung), GFP_KERNEL); 1605a6df4894SCorentin Chary if (!samsung) 1606a6df4894SCorentin Chary return -ENOMEM; 1607a6df4894SCorentin Chary 1608a6df4894SCorentin Chary mutex_init(&samsung->sabi_mutex); 1609f34cd9caSCorentin Chary samsung->handle_backlight = true; 1610a979e2e2SCorentin Chary samsung->quirks = quirks; 1611f34cd9caSCorentin Chary 1612a979e2e2SCorentin Chary 1613a60b2176SCorentin Chary #ifdef CONFIG_ACPI 1614a60b2176SCorentin Chary if (samsung->quirks->broken_acpi_video) 1615a60b2176SCorentin Chary acpi_video_dmi_promote_vendor(); 1616a60b2176SCorentin Chary 1617f34cd9caSCorentin Chary /* Don't handle backlight here if the acpi video already handle it */ 1618a979e2e2SCorentin Chary if (acpi_video_backlight_support()) { 1619f34cd9caSCorentin Chary samsung->handle_backlight = false; 1620a60b2176SCorentin Chary } else if (samsung->quirks->broken_acpi_video) { 1621a60b2176SCorentin Chary pr_info("Disabling ACPI video driver\n"); 1622a60b2176SCorentin Chary acpi_video_unregister(); 1623a979e2e2SCorentin Chary } 16244690555eSHans de Goede 16254690555eSHans de Goede if (samsung->quirks->use_native_backlight) { 16264690555eSHans de Goede pr_info("Using native backlight driver\n"); 16274690555eSHans de Goede /* Tell acpi-video to not handle the backlight */ 16284690555eSHans de Goede acpi_video_dmi_promote_vendor(); 16294690555eSHans de Goede acpi_video_unregister(); 16304690555eSHans de Goede /* And also do not handle it ourselves */ 16314690555eSHans de Goede samsung->handle_backlight = false; 16324690555eSHans de Goede } 1633f34cd9caSCorentin Chary #endif 1634a979e2e2SCorentin Chary 16355dea7a20SCorentin Chary ret = samsung_platform_init(samsung); 16365dea7a20SCorentin Chary if (ret) 16375dea7a20SCorentin Chary goto error_platform; 16382d70b73aSGreg Kroah-Hartman 16395dea7a20SCorentin Chary ret = samsung_sabi_init(samsung); 16405dea7a20SCorentin Chary if (ret) 16415dea7a20SCorentin Chary goto error_sabi; 16422d70b73aSGreg Kroah-Hartman 16433be324a9SCorentin Chary #ifdef CONFIG_ACPI 16443be324a9SCorentin Chary /* Only log that if we are really on a sabi platform */ 1645a60b2176SCorentin Chary if (acpi_video_backlight_support()) 16463be324a9SCorentin Chary pr_info("Backlight controlled by ACPI video driver\n"); 16473be324a9SCorentin Chary #endif 16483be324a9SCorentin Chary 16495dea7a20SCorentin Chary ret = samsung_sysfs_init(samsung); 16505dea7a20SCorentin Chary if (ret) 16515dea7a20SCorentin Chary goto error_sysfs; 16522d70b73aSGreg Kroah-Hartman 16535dea7a20SCorentin Chary ret = samsung_backlight_init(samsung); 16545dea7a20SCorentin Chary if (ret) 16555dea7a20SCorentin Chary goto error_backlight; 1656a6df4894SCorentin Chary 16575dea7a20SCorentin Chary ret = samsung_rfkill_init(samsung); 16585dea7a20SCorentin Chary if (ret) 16595dea7a20SCorentin Chary goto error_rfkill; 16602d70b73aSGreg Kroah-Hartman 1661f674ebf1SCorentin Chary ret = samsung_leds_init(samsung); 1662f674ebf1SCorentin Chary if (ret) 1663f674ebf1SCorentin Chary goto error_leds; 1664f674ebf1SCorentin Chary 16655b80fc40SCorentin Chary ret = samsung_debugfs_init(samsung); 16665b80fc40SCorentin Chary if (ret) 16675b80fc40SCorentin Chary goto error_debugfs; 16685b80fc40SCorentin Chary 16690ca849eaSScott Thrasher samsung->pm_nb.notifier_call = samsung_pm_notification; 16700ca849eaSScott Thrasher register_pm_notifier(&samsung->pm_nb); 16710ca849eaSScott Thrasher 16725dea7a20SCorentin Chary samsung_platform_device = samsung->platform_device; 16735dea7a20SCorentin Chary return ret; 16742d70b73aSGreg Kroah-Hartman 16755b80fc40SCorentin Chary error_debugfs: 1676f674ebf1SCorentin Chary samsung_leds_exit(samsung); 1677f674ebf1SCorentin Chary error_leds: 16785b80fc40SCorentin Chary samsung_rfkill_exit(samsung); 16795dea7a20SCorentin Chary error_rfkill: 16805dea7a20SCorentin Chary samsung_backlight_exit(samsung); 16815dea7a20SCorentin Chary error_backlight: 16825dea7a20SCorentin Chary samsung_sysfs_exit(samsung); 16835dea7a20SCorentin Chary error_sysfs: 16845dea7a20SCorentin Chary samsung_sabi_exit(samsung); 16855dea7a20SCorentin Chary error_sabi: 16865dea7a20SCorentin Chary samsung_platform_exit(samsung); 16875dea7a20SCorentin Chary error_platform: 1688a6df4894SCorentin Chary kfree(samsung); 16895dea7a20SCorentin Chary return ret; 16902d70b73aSGreg Kroah-Hartman } 16912d70b73aSGreg Kroah-Hartman 16922d70b73aSGreg Kroah-Hartman static void __exit samsung_exit(void) 16932d70b73aSGreg Kroah-Hartman { 16945dea7a20SCorentin Chary struct samsung_laptop *samsung; 16952d70b73aSGreg Kroah-Hartman 16965dea7a20SCorentin Chary samsung = platform_get_drvdata(samsung_platform_device); 16970ca849eaSScott Thrasher unregister_pm_notifier(&samsung->pm_nb); 1698a6df4894SCorentin Chary 16995b80fc40SCorentin Chary samsung_debugfs_exit(samsung); 1700f674ebf1SCorentin Chary samsung_leds_exit(samsung); 17015dea7a20SCorentin Chary samsung_rfkill_exit(samsung); 17025dea7a20SCorentin Chary samsung_backlight_exit(samsung); 17035dea7a20SCorentin Chary samsung_sysfs_exit(samsung); 17045dea7a20SCorentin Chary samsung_sabi_exit(samsung); 17055dea7a20SCorentin Chary samsung_platform_exit(samsung); 17065dea7a20SCorentin Chary 1707a6df4894SCorentin Chary kfree(samsung); 17085dea7a20SCorentin Chary samsung_platform_device = NULL; 17092d70b73aSGreg Kroah-Hartman } 17102d70b73aSGreg Kroah-Hartman 17112d70b73aSGreg Kroah-Hartman module_init(samsung_init); 17122d70b73aSGreg Kroah-Hartman module_exit(samsung_exit); 17132d70b73aSGreg Kroah-Hartman 17142d70b73aSGreg Kroah-Hartman MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@suse.de>"); 17152d70b73aSGreg Kroah-Hartman MODULE_DESCRIPTION("Samsung Backlight driver"); 17162d70b73aSGreg Kroah-Hartman MODULE_LICENSE("GPL"); 1717