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 127b0dcaf4fSJulijonas Kikutis /* 0x80 is off, 0x81 is on */ 128b0dcaf4fSJulijonas Kikutis u16 get_lid_handling; 129b0dcaf4fSJulijonas Kikutis u16 set_lid_handling; 130b0dcaf4fSJulijonas Kikutis 131f674ebf1SCorentin Chary /* 0x81 to read, (0x82 | level << 8) to set, 0xaabb to enable */ 132f674ebf1SCorentin Chary u16 kbd_backlight; 133f674ebf1SCorentin Chary 1342d70b73aSGreg Kroah-Hartman /* 1352d70b73aSGreg Kroah-Hartman * Tell the BIOS that Linux is running on this machine. 1362d70b73aSGreg Kroah-Hartman * 81 is on, 80 is off 1372d70b73aSGreg Kroah-Hartman */ 1387e960711SCorentin Chary u16 set_linux; 1392d70b73aSGreg Kroah-Hartman }; 1402d70b73aSGreg Kroah-Hartman 1412d70b73aSGreg Kroah-Hartman struct sabi_performance_level { 1422d70b73aSGreg Kroah-Hartman const char *name; 1437e960711SCorentin Chary u16 value; 1442d70b73aSGreg Kroah-Hartman }; 1452d70b73aSGreg Kroah-Hartman 1462d70b73aSGreg Kroah-Hartman struct sabi_config { 14784d482f2SCorentin Chary int sabi_version; 1482d70b73aSGreg Kroah-Hartman const char *test_string; 1492d70b73aSGreg Kroah-Hartman u16 main_function; 1502d70b73aSGreg Kroah-Hartman const struct sabi_header_offsets header_offsets; 1512d70b73aSGreg Kroah-Hartman const struct sabi_commands commands; 1522d70b73aSGreg Kroah-Hartman const struct sabi_performance_level performance_levels[4]; 1532d70b73aSGreg Kroah-Hartman u8 min_brightness; 1542d70b73aSGreg Kroah-Hartman u8 max_brightness; 1552d70b73aSGreg Kroah-Hartman }; 1562d70b73aSGreg Kroah-Hartman 1572d70b73aSGreg Kroah-Hartman static const struct sabi_config sabi_configs[] = { 1582d70b73aSGreg Kroah-Hartman { 15984d482f2SCorentin Chary /* I don't know if it is really 2, but it it is 16084d482f2SCorentin Chary * less than 3 anyway */ 16184d482f2SCorentin Chary .sabi_version = 2, 16284d482f2SCorentin Chary 1632d70b73aSGreg Kroah-Hartman .test_string = "SECLINUX", 1642d70b73aSGreg Kroah-Hartman 1652d70b73aSGreg Kroah-Hartman .main_function = 0x4c49, 1662d70b73aSGreg Kroah-Hartman 1672d70b73aSGreg Kroah-Hartman .header_offsets = { 1682d70b73aSGreg Kroah-Hartman .port = 0x00, 1692d70b73aSGreg Kroah-Hartman .re_mem = 0x02, 1702d70b73aSGreg Kroah-Hartman .iface_func = 0x03, 1712d70b73aSGreg Kroah-Hartman .en_mem = 0x04, 1722d70b73aSGreg Kroah-Hartman .data_offset = 0x05, 1732d70b73aSGreg Kroah-Hartman .data_segment = 0x07, 1742d70b73aSGreg Kroah-Hartman }, 1752d70b73aSGreg Kroah-Hartman 1762d70b73aSGreg Kroah-Hartman .commands = { 1772d70b73aSGreg Kroah-Hartman .get_brightness = 0x00, 1782d70b73aSGreg Kroah-Hartman .set_brightness = 0x01, 1792d70b73aSGreg Kroah-Hartman 1802d70b73aSGreg Kroah-Hartman .get_wireless_button = 0x02, 1812d70b73aSGreg Kroah-Hartman .set_wireless_button = 0x03, 1822d70b73aSGreg Kroah-Hartman 1832d70b73aSGreg Kroah-Hartman .get_backlight = 0x04, 1842d70b73aSGreg Kroah-Hartman .set_backlight = 0x05, 1852d70b73aSGreg Kroah-Hartman 1862d70b73aSGreg Kroah-Hartman .get_recovery_mode = 0x06, 1872d70b73aSGreg Kroah-Hartman .set_recovery_mode = 0x07, 1882d70b73aSGreg Kroah-Hartman 1892d70b73aSGreg Kroah-Hartman .get_performance_level = 0x08, 1902d70b73aSGreg Kroah-Hartman .set_performance_level = 0x09, 1912d70b73aSGreg Kroah-Hartman 192cb5b5c91SCorentin Chary .get_battery_life_extender = 0xFFFF, 193cb5b5c91SCorentin Chary .set_battery_life_extender = 0xFFFF, 194cb5b5c91SCorentin Chary 1953a75d378SCorentin Chary .get_usb_charge = 0xFFFF, 1963a75d378SCorentin Chary .set_usb_charge = 0xFFFF, 1973a75d378SCorentin Chary 19884d482f2SCorentin Chary .get_wireless_status = 0xFFFF, 19984d482f2SCorentin Chary .set_wireless_status = 0xFFFF, 20084d482f2SCorentin Chary 201b0dcaf4fSJulijonas Kikutis .get_lid_handling = 0xFFFF, 202b0dcaf4fSJulijonas Kikutis .set_lid_handling = 0xFFFF, 203b0dcaf4fSJulijonas Kikutis 204f674ebf1SCorentin Chary .kbd_backlight = 0xFFFF, 205f674ebf1SCorentin Chary 2062d70b73aSGreg Kroah-Hartman .set_linux = 0x0a, 2072d70b73aSGreg Kroah-Hartman }, 2082d70b73aSGreg Kroah-Hartman 2092d70b73aSGreg Kroah-Hartman .performance_levels = { 2102d70b73aSGreg Kroah-Hartman { 2112d70b73aSGreg Kroah-Hartman .name = "silent", 2122d70b73aSGreg Kroah-Hartman .value = 0, 2132d70b73aSGreg Kroah-Hartman }, 2142d70b73aSGreg Kroah-Hartman { 2152d70b73aSGreg Kroah-Hartman .name = "normal", 2162d70b73aSGreg Kroah-Hartman .value = 1, 2172d70b73aSGreg Kroah-Hartman }, 2182d70b73aSGreg Kroah-Hartman { }, 2192d70b73aSGreg Kroah-Hartman }, 2202d70b73aSGreg Kroah-Hartman .min_brightness = 1, 2212d70b73aSGreg Kroah-Hartman .max_brightness = 8, 2222d70b73aSGreg Kroah-Hartman }, 2232d70b73aSGreg Kroah-Hartman { 22484d482f2SCorentin Chary .sabi_version = 3, 22584d482f2SCorentin Chary 2262d70b73aSGreg Kroah-Hartman .test_string = "SwSmi@", 2272d70b73aSGreg Kroah-Hartman 2282d70b73aSGreg Kroah-Hartman .main_function = 0x5843, 2292d70b73aSGreg Kroah-Hartman 2302d70b73aSGreg Kroah-Hartman .header_offsets = { 2312d70b73aSGreg Kroah-Hartman .port = 0x00, 2322d70b73aSGreg Kroah-Hartman .re_mem = 0x04, 2332d70b73aSGreg Kroah-Hartman .iface_func = 0x02, 2342d70b73aSGreg Kroah-Hartman .en_mem = 0x03, 2352d70b73aSGreg Kroah-Hartman .data_offset = 0x05, 2362d70b73aSGreg Kroah-Hartman .data_segment = 0x07, 2372d70b73aSGreg Kroah-Hartman }, 2382d70b73aSGreg Kroah-Hartman 2392d70b73aSGreg Kroah-Hartman .commands = { 2402d70b73aSGreg Kroah-Hartman .get_brightness = 0x10, 2412d70b73aSGreg Kroah-Hartman .set_brightness = 0x11, 2422d70b73aSGreg Kroah-Hartman 2432d70b73aSGreg Kroah-Hartman .get_wireless_button = 0x12, 2442d70b73aSGreg Kroah-Hartman .set_wireless_button = 0x13, 2452d70b73aSGreg Kroah-Hartman 2462d70b73aSGreg Kroah-Hartman .get_backlight = 0x2d, 2472d70b73aSGreg Kroah-Hartman .set_backlight = 0x2e, 2482d70b73aSGreg Kroah-Hartman 2492d70b73aSGreg Kroah-Hartman .get_recovery_mode = 0xff, 2502d70b73aSGreg Kroah-Hartman .set_recovery_mode = 0xff, 2512d70b73aSGreg Kroah-Hartman 2522d70b73aSGreg Kroah-Hartman .get_performance_level = 0x31, 2532d70b73aSGreg Kroah-Hartman .set_performance_level = 0x32, 2542d70b73aSGreg Kroah-Hartman 255cb5b5c91SCorentin Chary .get_battery_life_extender = 0x65, 256cb5b5c91SCorentin Chary .set_battery_life_extender = 0x66, 257cb5b5c91SCorentin Chary 2583a75d378SCorentin Chary .get_usb_charge = 0x67, 2593a75d378SCorentin Chary .set_usb_charge = 0x68, 2603a75d378SCorentin Chary 26184d482f2SCorentin Chary .get_wireless_status = 0x69, 26284d482f2SCorentin Chary .set_wireless_status = 0x6a, 26384d482f2SCorentin Chary 264b0dcaf4fSJulijonas Kikutis .get_lid_handling = 0x6d, 265b0dcaf4fSJulijonas Kikutis .set_lid_handling = 0x6e, 266b0dcaf4fSJulijonas Kikutis 267f674ebf1SCorentin Chary .kbd_backlight = 0x78, 268f674ebf1SCorentin Chary 2692d70b73aSGreg Kroah-Hartman .set_linux = 0xff, 2702d70b73aSGreg Kroah-Hartman }, 2712d70b73aSGreg Kroah-Hartman 2722d70b73aSGreg Kroah-Hartman .performance_levels = { 2732d70b73aSGreg Kroah-Hartman { 2742d70b73aSGreg Kroah-Hartman .name = "normal", 2752d70b73aSGreg Kroah-Hartman .value = 0, 2762d70b73aSGreg Kroah-Hartman }, 2772d70b73aSGreg Kroah-Hartman { 2782d70b73aSGreg Kroah-Hartman .name = "silent", 2792d70b73aSGreg Kroah-Hartman .value = 1, 2802d70b73aSGreg Kroah-Hartman }, 2812d70b73aSGreg Kroah-Hartman { 2822d70b73aSGreg Kroah-Hartman .name = "overclock", 2832d70b73aSGreg Kroah-Hartman .value = 2, 2842d70b73aSGreg Kroah-Hartman }, 2852d70b73aSGreg Kroah-Hartman { }, 2862d70b73aSGreg Kroah-Hartman }, 2872d70b73aSGreg Kroah-Hartman .min_brightness = 0, 2882d70b73aSGreg Kroah-Hartman .max_brightness = 8, 2892d70b73aSGreg Kroah-Hartman }, 2902d70b73aSGreg Kroah-Hartman { }, 2912d70b73aSGreg Kroah-Hartman }; 2922d70b73aSGreg Kroah-Hartman 2935b80fc40SCorentin Chary /* 2945b80fc40SCorentin Chary * samsung-laptop/ - debugfs root directory 2955b80fc40SCorentin Chary * f0000_segment - dump f0000 segment 2965b80fc40SCorentin Chary * command - current command 2975b80fc40SCorentin Chary * data - current data 2985b80fc40SCorentin Chary * d0, d1, d2, d3 - data fields 2995b80fc40SCorentin Chary * call - call SABI using command and data 3005b80fc40SCorentin Chary * 3015b80fc40SCorentin Chary * This allow to call arbitrary sabi commands wihout 3025b80fc40SCorentin Chary * modifying the driver at all. 3035b80fc40SCorentin Chary * For example, setting the keyboard backlight brightness to 5 3045b80fc40SCorentin Chary * 3055b80fc40SCorentin Chary * echo 0x78 > command 3065b80fc40SCorentin Chary * echo 0x0582 > d0 3075b80fc40SCorentin Chary * echo 0 > d1 3085b80fc40SCorentin Chary * echo 0 > d2 3095b80fc40SCorentin Chary * echo 0 > d3 3105b80fc40SCorentin Chary * cat call 3115b80fc40SCorentin Chary */ 3125b80fc40SCorentin Chary 3135b80fc40SCorentin Chary struct samsung_laptop_debug { 3145b80fc40SCorentin Chary struct dentry *root; 3155b80fc40SCorentin Chary struct sabi_data data; 3165b80fc40SCorentin Chary u16 command; 3175b80fc40SCorentin Chary 3185b80fc40SCorentin Chary struct debugfs_blob_wrapper f0000_wrapper; 3195b80fc40SCorentin Chary struct debugfs_blob_wrapper data_wrapper; 3206f6ae06eSCorentin Chary struct debugfs_blob_wrapper sdiag_wrapper; 3215b80fc40SCorentin Chary }; 3225b80fc40SCorentin Chary 32384d482f2SCorentin Chary struct samsung_laptop; 32484d482f2SCorentin Chary 32584d482f2SCorentin Chary struct samsung_rfkill { 32684d482f2SCorentin Chary struct samsung_laptop *samsung; 32784d482f2SCorentin Chary struct rfkill *rfkill; 32884d482f2SCorentin Chary enum rfkill_type type; 32984d482f2SCorentin Chary }; 33084d482f2SCorentin Chary 331a6df4894SCorentin Chary struct samsung_laptop { 332a6df4894SCorentin Chary const struct sabi_config *config; 3332d70b73aSGreg Kroah-Hartman 334a6df4894SCorentin Chary void __iomem *sabi; 335a6df4894SCorentin Chary void __iomem *sabi_iface; 336a6df4894SCorentin Chary void __iomem *f0000_segment; 337a6df4894SCorentin Chary 338a6df4894SCorentin Chary struct mutex sabi_mutex; 339a6df4894SCorentin Chary 3405dea7a20SCorentin Chary struct platform_device *platform_device; 341a6df4894SCorentin Chary struct backlight_device *backlight_device; 34284d482f2SCorentin Chary 34384d482f2SCorentin Chary struct samsung_rfkill wlan; 34484d482f2SCorentin Chary struct samsung_rfkill bluetooth; 345a6df4894SCorentin Chary 346f674ebf1SCorentin Chary struct led_classdev kbd_led; 347f674ebf1SCorentin Chary int kbd_led_wk; 348f674ebf1SCorentin Chary struct workqueue_struct *led_workqueue; 349f674ebf1SCorentin Chary struct work_struct kbd_led_work; 350f674ebf1SCorentin Chary 3515b80fc40SCorentin Chary struct samsung_laptop_debug debug; 352a979e2e2SCorentin Chary struct samsung_quirks *quirks; 3535b80fc40SCorentin Chary 3540ca849eaSScott Thrasher struct notifier_block pm_nb; 3550ca849eaSScott Thrasher 356f34cd9caSCorentin Chary bool handle_backlight; 357a6df4894SCorentin Chary bool has_stepping_quirk; 3586f6ae06eSCorentin Chary 3596f6ae06eSCorentin Chary char sdiag[64]; 360a6df4894SCorentin Chary }; 361a6df4894SCorentin Chary 362a979e2e2SCorentin Chary struct samsung_quirks { 363a979e2e2SCorentin Chary bool broken_acpi_video; 3640ca849eaSScott Thrasher bool four_kbd_backlight_levels; 3650ca849eaSScott Thrasher bool enable_kbd_backlight; 3664690555eSHans de Goede bool use_native_backlight; 367b0dcaf4fSJulijonas Kikutis bool lid_handling; 368a979e2e2SCorentin Chary }; 3695dea7a20SCorentin Chary 370a979e2e2SCorentin Chary static struct samsung_quirks samsung_unknown = {}; 371a979e2e2SCorentin Chary 372a979e2e2SCorentin Chary static struct samsung_quirks samsung_broken_acpi_video = { 373a979e2e2SCorentin Chary .broken_acpi_video = true, 374a979e2e2SCorentin Chary }; 3752d70b73aSGreg Kroah-Hartman 3764690555eSHans de Goede static struct samsung_quirks samsung_use_native_backlight = { 3774690555eSHans de Goede .use_native_backlight = true, 3784690555eSHans de Goede }; 3794690555eSHans de Goede 3800ca849eaSScott Thrasher static struct samsung_quirks samsung_np740u3e = { 3810ca849eaSScott Thrasher .four_kbd_backlight_levels = true, 3820ca849eaSScott Thrasher .enable_kbd_backlight = true, 3830ca849eaSScott Thrasher }; 3840ca849eaSScott Thrasher 385b0dcaf4fSJulijonas Kikutis static struct samsung_quirks samsung_lid_handling = { 386b0dcaf4fSJulijonas Kikutis .lid_handling = true, 387b0dcaf4fSJulijonas Kikutis }; 388b0dcaf4fSJulijonas Kikutis 38990ab5ee9SRusty Russell static bool force; 3902d70b73aSGreg Kroah-Hartman module_param(force, bool, 0); 3912d70b73aSGreg Kroah-Hartman MODULE_PARM_DESC(force, 3922d70b73aSGreg Kroah-Hartman "Disable the DMI check and forces the driver to be loaded"); 3932d70b73aSGreg Kroah-Hartman 39490ab5ee9SRusty Russell static bool debug; 3952d70b73aSGreg Kroah-Hartman module_param(debug, bool, S_IRUGO | S_IWUSR); 3962d70b73aSGreg Kroah-Hartman MODULE_PARM_DESC(debug, "Debug enabled or not"); 3972d70b73aSGreg Kroah-Hartman 3987e960711SCorentin Chary static int sabi_command(struct samsung_laptop *samsung, u16 command, 3997e960711SCorentin Chary struct sabi_data *in, 4007e960711SCorentin Chary struct sabi_data *out) 4012d70b73aSGreg Kroah-Hartman { 402a6df4894SCorentin Chary const struct sabi_config *config = samsung->config; 4037e960711SCorentin Chary int ret = 0; 404a6df4894SCorentin Chary u16 port = readw(samsung->sabi + config->header_offsets.port); 4052d70b73aSGreg Kroah-Hartman u8 complete, iface_data; 4062d70b73aSGreg Kroah-Hartman 407a6df4894SCorentin Chary mutex_lock(&samsung->sabi_mutex); 4082d70b73aSGreg Kroah-Hartman 4097e960711SCorentin Chary if (debug) { 4107e960711SCorentin Chary if (in) 4112e777187SCorentin Chary pr_info("SABI command:0x%04x " 4122e777187SCorentin Chary "data:{0x%08x, 0x%08x, 0x%04x, 0x%02x}", 4137e960711SCorentin Chary command, in->d0, in->d1, in->d2, in->d3); 4147e960711SCorentin Chary else 4152e777187SCorentin Chary pr_info("SABI command:0x%04x", command); 4167e960711SCorentin Chary } 4177e960711SCorentin Chary 4182d70b73aSGreg Kroah-Hartman /* enable memory to be able to write to it */ 419a6df4894SCorentin Chary outb(readb(samsung->sabi + config->header_offsets.en_mem), port); 4202d70b73aSGreg Kroah-Hartman 4212d70b73aSGreg Kroah-Hartman /* write out the command */ 422a6df4894SCorentin Chary writew(config->main_function, samsung->sabi_iface + SABI_IFACE_MAIN); 423a6df4894SCorentin Chary writew(command, samsung->sabi_iface + SABI_IFACE_SUB); 424a6df4894SCorentin Chary writeb(0, samsung->sabi_iface + SABI_IFACE_COMPLETE); 4257e960711SCorentin Chary if (in) { 4267e960711SCorentin Chary writel(in->d0, samsung->sabi_iface + SABI_IFACE_DATA); 4277e960711SCorentin Chary writel(in->d1, samsung->sabi_iface + SABI_IFACE_DATA + 4); 4287e960711SCorentin Chary writew(in->d2, samsung->sabi_iface + SABI_IFACE_DATA + 8); 4297e960711SCorentin Chary writeb(in->d3, samsung->sabi_iface + SABI_IFACE_DATA + 10); 4307e960711SCorentin Chary } 431a6df4894SCorentin Chary outb(readb(samsung->sabi + config->header_offsets.iface_func), port); 4322d70b73aSGreg Kroah-Hartman 4332d70b73aSGreg Kroah-Hartman /* write protect memory to make it safe */ 434a6df4894SCorentin Chary outb(readb(samsung->sabi + config->header_offsets.re_mem), port); 4352d70b73aSGreg Kroah-Hartman 4362d70b73aSGreg Kroah-Hartman /* see if the command actually succeeded */ 437a6df4894SCorentin Chary complete = readb(samsung->sabi_iface + SABI_IFACE_COMPLETE); 438a6df4894SCorentin Chary iface_data = readb(samsung->sabi_iface + SABI_IFACE_DATA); 4392e777187SCorentin Chary 4402e777187SCorentin Chary /* iface_data = 0xFF happens when a command is not known 4412e777187SCorentin Chary * so we only add a warning in debug mode since we will 4422e777187SCorentin Chary * probably issue some unknown command at startup to find 4432e777187SCorentin Chary * out which features are supported */ 4442e777187SCorentin Chary if (complete != 0xaa || (iface_data == 0xff && debug)) 4457e960711SCorentin Chary pr_warn("SABI command 0x%04x failed with" 4467e960711SCorentin Chary " completion flag 0x%02x and interface data 0x%02x", 4472d70b73aSGreg Kroah-Hartman command, complete, iface_data); 4482e777187SCorentin Chary 4492e777187SCorentin Chary if (complete != 0xaa || iface_data == 0xff) { 4507e960711SCorentin Chary ret = -EINVAL; 4512d70b73aSGreg Kroah-Hartman goto exit; 4522d70b73aSGreg Kroah-Hartman } 4537e960711SCorentin Chary 4547e960711SCorentin Chary if (out) { 4557e960711SCorentin Chary out->d0 = readl(samsung->sabi_iface + SABI_IFACE_DATA); 4567e960711SCorentin Chary out->d1 = readl(samsung->sabi_iface + SABI_IFACE_DATA + 4); 4577e960711SCorentin Chary out->d2 = readw(samsung->sabi_iface + SABI_IFACE_DATA + 2); 4587e960711SCorentin Chary out->d3 = readb(samsung->sabi_iface + SABI_IFACE_DATA + 1); 4597e960711SCorentin Chary } 4607e960711SCorentin Chary 4617e960711SCorentin Chary if (debug && out) { 4622e777187SCorentin Chary pr_info("SABI return data:{0x%08x, 0x%08x, 0x%04x, 0x%02x}", 4637e960711SCorentin Chary out->d0, out->d1, out->d2, out->d3); 4647e960711SCorentin Chary } 4652d70b73aSGreg Kroah-Hartman 4662d70b73aSGreg Kroah-Hartman exit: 467a6df4894SCorentin Chary mutex_unlock(&samsung->sabi_mutex); 4687e960711SCorentin Chary return ret; 4692d70b73aSGreg Kroah-Hartman } 4702d70b73aSGreg Kroah-Hartman 4717e960711SCorentin Chary /* simple wrappers usable with most commands */ 4727e960711SCorentin Chary static int sabi_set_commandb(struct samsung_laptop *samsung, 4737e960711SCorentin Chary u16 command, u8 data) 4742d70b73aSGreg Kroah-Hartman { 47585229440SDavid Rientjes struct sabi_data in = { { { .d0 = 0, .d1 = 0, .d2 = 0, .d3 = 0 } } }; 4762d70b73aSGreg Kroah-Hartman 4777e960711SCorentin Chary in.data[0] = data; 4787e960711SCorentin Chary return sabi_command(samsung, command, &in, NULL); 4792d70b73aSGreg Kroah-Hartman } 4802d70b73aSGreg Kroah-Hartman 4815dea7a20SCorentin Chary static int read_brightness(struct samsung_laptop *samsung) 4822d70b73aSGreg Kroah-Hartman { 483a6df4894SCorentin Chary const struct sabi_config *config = samsung->config; 484a6df4894SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 4857e960711SCorentin Chary struct sabi_data sretval; 4862d70b73aSGreg Kroah-Hartman int user_brightness = 0; 4872d70b73aSGreg Kroah-Hartman int retval; 4882d70b73aSGreg Kroah-Hartman 4897e960711SCorentin Chary retval = sabi_command(samsung, commands->get_brightness, 4907e960711SCorentin Chary NULL, &sretval); 4917e960711SCorentin Chary if (retval) 4927e960711SCorentin Chary return retval; 4937e960711SCorentin Chary 4947e960711SCorentin Chary user_brightness = sretval.data[0]; 495a6df4894SCorentin Chary if (user_brightness > config->min_brightness) 496a6df4894SCorentin Chary user_brightness -= config->min_brightness; 497bee460beSJason Stubbs else 498bee460beSJason Stubbs user_brightness = 0; 4997e960711SCorentin Chary 5002d70b73aSGreg Kroah-Hartman return user_brightness; 5012d70b73aSGreg Kroah-Hartman } 5022d70b73aSGreg Kroah-Hartman 5035dea7a20SCorentin Chary static void set_brightness(struct samsung_laptop *samsung, u8 user_brightness) 5042d70b73aSGreg Kroah-Hartman { 505a6df4894SCorentin Chary const struct sabi_config *config = samsung->config; 506a6df4894SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 507a6df4894SCorentin Chary u8 user_level = user_brightness + config->min_brightness; 5082d70b73aSGreg Kroah-Hartman 509a6df4894SCorentin Chary if (samsung->has_stepping_quirk && user_level != 0) { 510ac080523SJason Stubbs /* 511ac080523SJason Stubbs * short circuit if the specified level is what's already set 512ac080523SJason Stubbs * to prevent the screen from flickering needlessly 513ac080523SJason Stubbs */ 5145dea7a20SCorentin Chary if (user_brightness == read_brightness(samsung)) 515ac080523SJason Stubbs return; 516ac080523SJason Stubbs 5177e960711SCorentin Chary sabi_set_commandb(samsung, commands->set_brightness, 0); 518ac080523SJason Stubbs } 519ac080523SJason Stubbs 5207e960711SCorentin Chary sabi_set_commandb(samsung, commands->set_brightness, user_level); 5212d70b73aSGreg Kroah-Hartman } 5222d70b73aSGreg Kroah-Hartman 5232d70b73aSGreg Kroah-Hartman static int get_brightness(struct backlight_device *bd) 5242d70b73aSGreg Kroah-Hartman { 5255dea7a20SCorentin Chary struct samsung_laptop *samsung = bl_get_data(bd); 5265dea7a20SCorentin Chary 5275dea7a20SCorentin Chary return read_brightness(samsung); 5282d70b73aSGreg Kroah-Hartman } 5292d70b73aSGreg Kroah-Hartman 5305dea7a20SCorentin Chary static void check_for_stepping_quirk(struct samsung_laptop *samsung) 531ac080523SJason Stubbs { 5325dea7a20SCorentin Chary int initial_level; 5335dea7a20SCorentin Chary int check_level; 5345dea7a20SCorentin Chary int orig_level = read_brightness(samsung); 535ac080523SJason Stubbs 536ac080523SJason Stubbs /* 537ac080523SJason Stubbs * Some laptops exhibit the strange behaviour of stepping toward 538ac080523SJason Stubbs * (rather than setting) the brightness except when changing to/from 539ac080523SJason Stubbs * brightness level 0. This behaviour is checked for here and worked 540ac080523SJason Stubbs * around in set_brightness. 541ac080523SJason Stubbs */ 542ac080523SJason Stubbs 543ba05b237SJohn Serock if (orig_level == 0) 5445dea7a20SCorentin Chary set_brightness(samsung, 1); 545ba05b237SJohn Serock 5465dea7a20SCorentin Chary initial_level = read_brightness(samsung); 547ba05b237SJohn Serock 548ac080523SJason Stubbs if (initial_level <= 2) 549ac080523SJason Stubbs check_level = initial_level + 2; 550ac080523SJason Stubbs else 551ac080523SJason Stubbs check_level = initial_level - 2; 552ac080523SJason Stubbs 553a6df4894SCorentin Chary samsung->has_stepping_quirk = false; 5545dea7a20SCorentin Chary set_brightness(samsung, check_level); 555ac080523SJason Stubbs 5565dea7a20SCorentin Chary if (read_brightness(samsung) != check_level) { 557a6df4894SCorentin Chary samsung->has_stepping_quirk = true; 558ac080523SJason Stubbs pr_info("enabled workaround for brightness stepping quirk\n"); 559ac080523SJason Stubbs } 560ac080523SJason Stubbs 5615dea7a20SCorentin Chary set_brightness(samsung, orig_level); 562ac080523SJason Stubbs } 563ac080523SJason Stubbs 5642d70b73aSGreg Kroah-Hartman static int update_status(struct backlight_device *bd) 5652d70b73aSGreg Kroah-Hartman { 5665dea7a20SCorentin Chary struct samsung_laptop *samsung = bl_get_data(bd); 567a6df4894SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 568a6df4894SCorentin Chary 5695dea7a20SCorentin Chary set_brightness(samsung, bd->props.brightness); 5702d70b73aSGreg Kroah-Hartman 5712d70b73aSGreg Kroah-Hartman if (bd->props.power == FB_BLANK_UNBLANK) 5727e960711SCorentin Chary sabi_set_commandb(samsung, commands->set_backlight, 1); 5732d70b73aSGreg Kroah-Hartman else 5747e960711SCorentin Chary sabi_set_commandb(samsung, commands->set_backlight, 0); 5755dea7a20SCorentin Chary 5762d70b73aSGreg Kroah-Hartman return 0; 5772d70b73aSGreg Kroah-Hartman } 5782d70b73aSGreg Kroah-Hartman 5792d70b73aSGreg Kroah-Hartman static const struct backlight_ops backlight_ops = { 5802d70b73aSGreg Kroah-Hartman .get_brightness = get_brightness, 5812d70b73aSGreg Kroah-Hartman .update_status = update_status, 5822d70b73aSGreg Kroah-Hartman }; 5832d70b73aSGreg Kroah-Hartman 58484d482f2SCorentin Chary static int seclinux_rfkill_set(void *data, bool blocked) 5852d70b73aSGreg Kroah-Hartman { 58620db88e3SCorentin Chary struct samsung_rfkill *srfkill = data; 58720db88e3SCorentin Chary struct samsung_laptop *samsung = srfkill->samsung; 588a6df4894SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 589a6df4894SCorentin Chary 59084d482f2SCorentin Chary return sabi_set_commandb(samsung, commands->set_wireless_button, 59184d482f2SCorentin Chary !blocked); 5922d70b73aSGreg Kroah-Hartman } 5932d70b73aSGreg Kroah-Hartman 5948314a1c8SBhumika Goyal static const struct rfkill_ops seclinux_rfkill_ops = { 59584d482f2SCorentin Chary .set_block = seclinux_rfkill_set, 59684d482f2SCorentin Chary }; 59784d482f2SCorentin Chary 59884d482f2SCorentin Chary static int swsmi_wireless_status(struct samsung_laptop *samsung, 59984d482f2SCorentin Chary struct sabi_data *data) 60084d482f2SCorentin Chary { 60184d482f2SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 60284d482f2SCorentin Chary 60384d482f2SCorentin Chary return sabi_command(samsung, commands->get_wireless_status, 60484d482f2SCorentin Chary NULL, data); 60584d482f2SCorentin Chary } 60684d482f2SCorentin Chary 60784d482f2SCorentin Chary static int swsmi_rfkill_set(void *priv, bool blocked) 60884d482f2SCorentin Chary { 60984d482f2SCorentin Chary struct samsung_rfkill *srfkill = priv; 61084d482f2SCorentin Chary struct samsung_laptop *samsung = srfkill->samsung; 61184d482f2SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 61284d482f2SCorentin Chary struct sabi_data data; 61384d482f2SCorentin Chary int ret, i; 61484d482f2SCorentin Chary 61584d482f2SCorentin Chary ret = swsmi_wireless_status(samsung, &data); 61684d482f2SCorentin Chary if (ret) 61784d482f2SCorentin Chary return ret; 61884d482f2SCorentin Chary 61984d482f2SCorentin Chary /* Don't set the state for non-present devices */ 62084d482f2SCorentin Chary for (i = 0; i < 4; i++) 62184d482f2SCorentin Chary if (data.data[i] == 0x02) 62284d482f2SCorentin Chary data.data[1] = 0; 62384d482f2SCorentin Chary 62484d482f2SCorentin Chary if (srfkill->type == RFKILL_TYPE_WLAN) 62584d482f2SCorentin Chary data.data[WL_STATUS_WLAN] = !blocked; 62684d482f2SCorentin Chary else if (srfkill->type == RFKILL_TYPE_BLUETOOTH) 62784d482f2SCorentin Chary data.data[WL_STATUS_BT] = !blocked; 62884d482f2SCorentin Chary 62984d482f2SCorentin Chary return sabi_command(samsung, commands->set_wireless_status, 63084d482f2SCorentin Chary &data, &data); 63184d482f2SCorentin Chary } 63284d482f2SCorentin Chary 63384d482f2SCorentin Chary static void swsmi_rfkill_query(struct rfkill *rfkill, void *priv) 63484d482f2SCorentin Chary { 63584d482f2SCorentin Chary struct samsung_rfkill *srfkill = priv; 63684d482f2SCorentin Chary struct samsung_laptop *samsung = srfkill->samsung; 63784d482f2SCorentin Chary struct sabi_data data; 63884d482f2SCorentin Chary int ret; 63984d482f2SCorentin Chary 64084d482f2SCorentin Chary ret = swsmi_wireless_status(samsung, &data); 64184d482f2SCorentin Chary if (ret) 64284d482f2SCorentin Chary return ; 64384d482f2SCorentin Chary 64484d482f2SCorentin Chary if (srfkill->type == RFKILL_TYPE_WLAN) 64584d482f2SCorentin Chary ret = data.data[WL_STATUS_WLAN]; 64684d482f2SCorentin Chary else if (srfkill->type == RFKILL_TYPE_BLUETOOTH) 64784d482f2SCorentin Chary ret = data.data[WL_STATUS_BT]; 64884d482f2SCorentin Chary else 64984d482f2SCorentin Chary return ; 65084d482f2SCorentin Chary 65184d482f2SCorentin Chary rfkill_set_sw_state(rfkill, !ret); 65284d482f2SCorentin Chary } 65384d482f2SCorentin Chary 6548314a1c8SBhumika Goyal static const struct rfkill_ops swsmi_rfkill_ops = { 65584d482f2SCorentin Chary .set_block = swsmi_rfkill_set, 65684d482f2SCorentin Chary .query = swsmi_rfkill_query, 6572d70b73aSGreg Kroah-Hartman }; 6582d70b73aSGreg Kroah-Hartman 6592d70b73aSGreg Kroah-Hartman static ssize_t get_performance_level(struct device *dev, 6602d70b73aSGreg Kroah-Hartman struct device_attribute *attr, char *buf) 6612d70b73aSGreg Kroah-Hartman { 6625dea7a20SCorentin Chary struct samsung_laptop *samsung = dev_get_drvdata(dev); 663a6df4894SCorentin Chary const struct sabi_config *config = samsung->config; 6645dea7a20SCorentin Chary const struct sabi_commands *commands = &config->commands; 6657e960711SCorentin Chary struct sabi_data sretval; 6662d70b73aSGreg Kroah-Hartman int retval; 6672d70b73aSGreg Kroah-Hartman int i; 6682d70b73aSGreg Kroah-Hartman 6692d70b73aSGreg Kroah-Hartman /* Read the state */ 6707e960711SCorentin Chary retval = sabi_command(samsung, commands->get_performance_level, 6717e960711SCorentin Chary NULL, &sretval); 6722d70b73aSGreg Kroah-Hartman if (retval) 6732d70b73aSGreg Kroah-Hartman return retval; 6742d70b73aSGreg Kroah-Hartman 6752d70b73aSGreg Kroah-Hartman /* The logic is backwards, yeah, lots of fun... */ 676a6df4894SCorentin Chary for (i = 0; config->performance_levels[i].name; ++i) { 6777e960711SCorentin Chary if (sretval.data[0] == config->performance_levels[i].value) 678a6df4894SCorentin Chary return sprintf(buf, "%s\n", config->performance_levels[i].name); 6792d70b73aSGreg Kroah-Hartman } 6802d70b73aSGreg Kroah-Hartman return sprintf(buf, "%s\n", "unknown"); 6812d70b73aSGreg Kroah-Hartman } 6822d70b73aSGreg Kroah-Hartman 6832d70b73aSGreg Kroah-Hartman static ssize_t set_performance_level(struct device *dev, 6842d70b73aSGreg Kroah-Hartman struct device_attribute *attr, const char *buf, 6852d70b73aSGreg Kroah-Hartman size_t count) 6862d70b73aSGreg Kroah-Hartman { 6875dea7a20SCorentin Chary struct samsung_laptop *samsung = dev_get_drvdata(dev); 688a6df4894SCorentin Chary const struct sabi_config *config = samsung->config; 6895dea7a20SCorentin Chary const struct sabi_commands *commands = &config->commands; 6902d70b73aSGreg Kroah-Hartman int i; 6915dea7a20SCorentin Chary 6925dea7a20SCorentin Chary if (count < 1) 6935dea7a20SCorentin Chary return count; 6945dea7a20SCorentin Chary 695a6df4894SCorentin Chary for (i = 0; config->performance_levels[i].name; ++i) { 6962d70b73aSGreg Kroah-Hartman const struct sabi_performance_level *level = 697a6df4894SCorentin Chary &config->performance_levels[i]; 6982d70b73aSGreg Kroah-Hartman if (!strncasecmp(level->name, buf, strlen(level->name))) { 6997e960711SCorentin Chary sabi_set_commandb(samsung, 7005dea7a20SCorentin Chary commands->set_performance_level, 7012d70b73aSGreg Kroah-Hartman level->value); 7022d70b73aSGreg Kroah-Hartman break; 7032d70b73aSGreg Kroah-Hartman } 7042d70b73aSGreg Kroah-Hartman } 7055dea7a20SCorentin Chary 706a6df4894SCorentin Chary if (!config->performance_levels[i].name) 7072d70b73aSGreg Kroah-Hartman return -EINVAL; 7085dea7a20SCorentin Chary 7092d70b73aSGreg Kroah-Hartman return count; 7102d70b73aSGreg Kroah-Hartman } 7115dea7a20SCorentin Chary 7122d70b73aSGreg Kroah-Hartman static DEVICE_ATTR(performance_level, S_IWUSR | S_IRUGO, 7132d70b73aSGreg Kroah-Hartman get_performance_level, set_performance_level); 7142d70b73aSGreg Kroah-Hartman 715cb5b5c91SCorentin Chary static int read_battery_life_extender(struct samsung_laptop *samsung) 716cb5b5c91SCorentin Chary { 717cb5b5c91SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 718cb5b5c91SCorentin Chary struct sabi_data data; 719cb5b5c91SCorentin Chary int retval; 720cb5b5c91SCorentin Chary 721cb5b5c91SCorentin Chary if (commands->get_battery_life_extender == 0xFFFF) 722cb5b5c91SCorentin Chary return -ENODEV; 723cb5b5c91SCorentin Chary 724cb5b5c91SCorentin Chary memset(&data, 0, sizeof(data)); 725cb5b5c91SCorentin Chary data.data[0] = 0x80; 726cb5b5c91SCorentin Chary retval = sabi_command(samsung, commands->get_battery_life_extender, 727cb5b5c91SCorentin Chary &data, &data); 728cb5b5c91SCorentin Chary 729cb5b5c91SCorentin Chary if (retval) 730cb5b5c91SCorentin Chary return retval; 731cb5b5c91SCorentin Chary 732cb5b5c91SCorentin Chary if (data.data[0] != 0 && data.data[0] != 1) 733cb5b5c91SCorentin Chary return -ENODEV; 734cb5b5c91SCorentin Chary 735cb5b5c91SCorentin Chary return data.data[0]; 736cb5b5c91SCorentin Chary } 737cb5b5c91SCorentin Chary 738cb5b5c91SCorentin Chary static int write_battery_life_extender(struct samsung_laptop *samsung, 739cb5b5c91SCorentin Chary int enabled) 740cb5b5c91SCorentin Chary { 741cb5b5c91SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 742cb5b5c91SCorentin Chary struct sabi_data data; 743cb5b5c91SCorentin Chary 744cb5b5c91SCorentin Chary memset(&data, 0, sizeof(data)); 745cb5b5c91SCorentin Chary data.data[0] = 0x80 | enabled; 746cb5b5c91SCorentin Chary return sabi_command(samsung, commands->set_battery_life_extender, 747cb5b5c91SCorentin Chary &data, NULL); 748cb5b5c91SCorentin Chary } 749cb5b5c91SCorentin Chary 750cb5b5c91SCorentin Chary static ssize_t get_battery_life_extender(struct device *dev, 751cb5b5c91SCorentin Chary struct device_attribute *attr, 752cb5b5c91SCorentin Chary char *buf) 753cb5b5c91SCorentin Chary { 754cb5b5c91SCorentin Chary struct samsung_laptop *samsung = dev_get_drvdata(dev); 755cb5b5c91SCorentin Chary int ret; 756cb5b5c91SCorentin Chary 757cb5b5c91SCorentin Chary ret = read_battery_life_extender(samsung); 758cb5b5c91SCorentin Chary if (ret < 0) 759cb5b5c91SCorentin Chary return ret; 760cb5b5c91SCorentin Chary 761cb5b5c91SCorentin Chary return sprintf(buf, "%d\n", ret); 762cb5b5c91SCorentin Chary } 763cb5b5c91SCorentin Chary 764cb5b5c91SCorentin Chary static ssize_t set_battery_life_extender(struct device *dev, 765cb5b5c91SCorentin Chary struct device_attribute *attr, 766cb5b5c91SCorentin Chary const char *buf, size_t count) 767cb5b5c91SCorentin Chary { 768cb5b5c91SCorentin Chary struct samsung_laptop *samsung = dev_get_drvdata(dev); 769cb5b5c91SCorentin Chary int ret, value; 770cb5b5c91SCorentin Chary 771802cf2e1SDarren Hart if (!count || kstrtoint(buf, 0, &value) != 0) 772cb5b5c91SCorentin Chary return -EINVAL; 773cb5b5c91SCorentin Chary 774cb5b5c91SCorentin Chary ret = write_battery_life_extender(samsung, !!value); 775cb5b5c91SCorentin Chary if (ret < 0) 776cb5b5c91SCorentin Chary return ret; 777cb5b5c91SCorentin Chary 778cb5b5c91SCorentin Chary return count; 779cb5b5c91SCorentin Chary } 780cb5b5c91SCorentin Chary 781cb5b5c91SCorentin Chary static DEVICE_ATTR(battery_life_extender, S_IWUSR | S_IRUGO, 782cb5b5c91SCorentin Chary get_battery_life_extender, set_battery_life_extender); 783cb5b5c91SCorentin Chary 7843a75d378SCorentin Chary static int read_usb_charge(struct samsung_laptop *samsung) 7853a75d378SCorentin Chary { 7863a75d378SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 7873a75d378SCorentin Chary struct sabi_data data; 7883a75d378SCorentin Chary int retval; 7893a75d378SCorentin Chary 7903a75d378SCorentin Chary if (commands->get_usb_charge == 0xFFFF) 7913a75d378SCorentin Chary return -ENODEV; 7923a75d378SCorentin Chary 7933a75d378SCorentin Chary memset(&data, 0, sizeof(data)); 7943a75d378SCorentin Chary data.data[0] = 0x80; 7953a75d378SCorentin Chary retval = sabi_command(samsung, commands->get_usb_charge, 7963a75d378SCorentin Chary &data, &data); 7973a75d378SCorentin Chary 7983a75d378SCorentin Chary if (retval) 7993a75d378SCorentin Chary return retval; 8003a75d378SCorentin Chary 8013a75d378SCorentin Chary if (data.data[0] != 0 && data.data[0] != 1) 8023a75d378SCorentin Chary return -ENODEV; 8033a75d378SCorentin Chary 8043a75d378SCorentin Chary return data.data[0]; 8053a75d378SCorentin Chary } 8063a75d378SCorentin Chary 8073a75d378SCorentin Chary static int write_usb_charge(struct samsung_laptop *samsung, 8083a75d378SCorentin Chary int enabled) 8093a75d378SCorentin Chary { 8103a75d378SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 8113a75d378SCorentin Chary struct sabi_data data; 8123a75d378SCorentin Chary 8133a75d378SCorentin Chary memset(&data, 0, sizeof(data)); 8143a75d378SCorentin Chary data.data[0] = 0x80 | enabled; 8153a75d378SCorentin Chary return sabi_command(samsung, commands->set_usb_charge, 8163a75d378SCorentin Chary &data, NULL); 8173a75d378SCorentin Chary } 8183a75d378SCorentin Chary 8193a75d378SCorentin Chary static ssize_t get_usb_charge(struct device *dev, 8203a75d378SCorentin Chary struct device_attribute *attr, 8213a75d378SCorentin Chary char *buf) 8223a75d378SCorentin Chary { 8233a75d378SCorentin Chary struct samsung_laptop *samsung = dev_get_drvdata(dev); 8243a75d378SCorentin Chary int ret; 8253a75d378SCorentin Chary 8263a75d378SCorentin Chary ret = read_usb_charge(samsung); 8273a75d378SCorentin Chary if (ret < 0) 8283a75d378SCorentin Chary return ret; 8293a75d378SCorentin Chary 8303a75d378SCorentin Chary return sprintf(buf, "%d\n", ret); 8313a75d378SCorentin Chary } 8323a75d378SCorentin Chary 8333a75d378SCorentin Chary static ssize_t set_usb_charge(struct device *dev, 8343a75d378SCorentin Chary struct device_attribute *attr, 8353a75d378SCorentin Chary const char *buf, size_t count) 8363a75d378SCorentin Chary { 8373a75d378SCorentin Chary struct samsung_laptop *samsung = dev_get_drvdata(dev); 8383a75d378SCorentin Chary int ret, value; 8393a75d378SCorentin Chary 840802cf2e1SDarren Hart if (!count || kstrtoint(buf, 0, &value) != 0) 8413a75d378SCorentin Chary return -EINVAL; 8423a75d378SCorentin Chary 8433a75d378SCorentin Chary ret = write_usb_charge(samsung, !!value); 8443a75d378SCorentin Chary if (ret < 0) 8453a75d378SCorentin Chary return ret; 8463a75d378SCorentin Chary 8473a75d378SCorentin Chary return count; 8483a75d378SCorentin Chary } 8493a75d378SCorentin Chary 8503a75d378SCorentin Chary static DEVICE_ATTR(usb_charge, S_IWUSR | S_IRUGO, 8513a75d378SCorentin Chary get_usb_charge, set_usb_charge); 8523a75d378SCorentin Chary 853b0dcaf4fSJulijonas Kikutis static int read_lid_handling(struct samsung_laptop *samsung) 854b0dcaf4fSJulijonas Kikutis { 855b0dcaf4fSJulijonas Kikutis const struct sabi_commands *commands = &samsung->config->commands; 856b0dcaf4fSJulijonas Kikutis struct sabi_data data; 857b0dcaf4fSJulijonas Kikutis int retval; 858b0dcaf4fSJulijonas Kikutis 859b0dcaf4fSJulijonas Kikutis if (commands->get_lid_handling == 0xFFFF) 860b0dcaf4fSJulijonas Kikutis return -ENODEV; 861b0dcaf4fSJulijonas Kikutis 862b0dcaf4fSJulijonas Kikutis memset(&data, 0, sizeof(data)); 863b0dcaf4fSJulijonas Kikutis retval = sabi_command(samsung, commands->get_lid_handling, 864b0dcaf4fSJulijonas Kikutis &data, &data); 865b0dcaf4fSJulijonas Kikutis 866b0dcaf4fSJulijonas Kikutis if (retval) 867b0dcaf4fSJulijonas Kikutis return retval; 868b0dcaf4fSJulijonas Kikutis 869b0dcaf4fSJulijonas Kikutis return data.data[0] & 0x1; 870b0dcaf4fSJulijonas Kikutis } 871b0dcaf4fSJulijonas Kikutis 872b0dcaf4fSJulijonas Kikutis static int write_lid_handling(struct samsung_laptop *samsung, 873b0dcaf4fSJulijonas Kikutis int enabled) 874b0dcaf4fSJulijonas Kikutis { 875b0dcaf4fSJulijonas Kikutis const struct sabi_commands *commands = &samsung->config->commands; 876b0dcaf4fSJulijonas Kikutis struct sabi_data data; 877b0dcaf4fSJulijonas Kikutis 878b0dcaf4fSJulijonas Kikutis memset(&data, 0, sizeof(data)); 879b0dcaf4fSJulijonas Kikutis data.data[0] = 0x80 | enabled; 880b0dcaf4fSJulijonas Kikutis return sabi_command(samsung, commands->set_lid_handling, 881b0dcaf4fSJulijonas Kikutis &data, NULL); 882b0dcaf4fSJulijonas Kikutis } 883b0dcaf4fSJulijonas Kikutis 884b0dcaf4fSJulijonas Kikutis static ssize_t get_lid_handling(struct device *dev, 885b0dcaf4fSJulijonas Kikutis struct device_attribute *attr, 886b0dcaf4fSJulijonas Kikutis char *buf) 887b0dcaf4fSJulijonas Kikutis { 888b0dcaf4fSJulijonas Kikutis struct samsung_laptop *samsung = dev_get_drvdata(dev); 889b0dcaf4fSJulijonas Kikutis int ret; 890b0dcaf4fSJulijonas Kikutis 891b0dcaf4fSJulijonas Kikutis ret = read_lid_handling(samsung); 892b0dcaf4fSJulijonas Kikutis if (ret < 0) 893b0dcaf4fSJulijonas Kikutis return ret; 894b0dcaf4fSJulijonas Kikutis 895b0dcaf4fSJulijonas Kikutis return sprintf(buf, "%d\n", ret); 896b0dcaf4fSJulijonas Kikutis } 897b0dcaf4fSJulijonas Kikutis 898b0dcaf4fSJulijonas Kikutis static ssize_t set_lid_handling(struct device *dev, 899b0dcaf4fSJulijonas Kikutis struct device_attribute *attr, 900b0dcaf4fSJulijonas Kikutis const char *buf, size_t count) 901b0dcaf4fSJulijonas Kikutis { 902b0dcaf4fSJulijonas Kikutis struct samsung_laptop *samsung = dev_get_drvdata(dev); 903b0dcaf4fSJulijonas Kikutis int ret, value; 904b0dcaf4fSJulijonas Kikutis 905b0dcaf4fSJulijonas Kikutis if (!count || kstrtoint(buf, 0, &value) != 0) 906b0dcaf4fSJulijonas Kikutis return -EINVAL; 907b0dcaf4fSJulijonas Kikutis 908b0dcaf4fSJulijonas Kikutis ret = write_lid_handling(samsung, !!value); 909b0dcaf4fSJulijonas Kikutis if (ret < 0) 910b0dcaf4fSJulijonas Kikutis return ret; 911b0dcaf4fSJulijonas Kikutis 912b0dcaf4fSJulijonas Kikutis return count; 913b0dcaf4fSJulijonas Kikutis } 914b0dcaf4fSJulijonas Kikutis 915b0dcaf4fSJulijonas Kikutis static DEVICE_ATTR(lid_handling, S_IWUSR | S_IRUGO, 916b0dcaf4fSJulijonas Kikutis get_lid_handling, set_lid_handling); 917b0dcaf4fSJulijonas Kikutis 918a66c1662SCorentin Chary static struct attribute *platform_attributes[] = { 919a66c1662SCorentin Chary &dev_attr_performance_level.attr, 920cb5b5c91SCorentin Chary &dev_attr_battery_life_extender.attr, 9213a75d378SCorentin Chary &dev_attr_usb_charge.attr, 922b0dcaf4fSJulijonas Kikutis &dev_attr_lid_handling.attr, 923a66c1662SCorentin Chary NULL 924a66c1662SCorentin Chary }; 9252d70b73aSGreg Kroah-Hartman 9265dea7a20SCorentin Chary static int find_signature(void __iomem *memcheck, const char *testStr) 9275dea7a20SCorentin Chary { 9285dea7a20SCorentin Chary int i = 0; 9295dea7a20SCorentin Chary int loca; 9305dea7a20SCorentin Chary 9315dea7a20SCorentin Chary for (loca = 0; loca < 0xffff; loca++) { 9325dea7a20SCorentin Chary char temp = readb(memcheck + loca); 9335dea7a20SCorentin Chary 9345dea7a20SCorentin Chary if (temp == testStr[i]) { 9355dea7a20SCorentin Chary if (i == strlen(testStr)-1) 9365dea7a20SCorentin Chary break; 9375dea7a20SCorentin Chary ++i; 9385dea7a20SCorentin Chary } else { 9395dea7a20SCorentin Chary i = 0; 9405dea7a20SCorentin Chary } 9415dea7a20SCorentin Chary } 9425dea7a20SCorentin Chary return loca; 9435dea7a20SCorentin Chary } 9445dea7a20SCorentin Chary 9455dea7a20SCorentin Chary static void samsung_rfkill_exit(struct samsung_laptop *samsung) 9465dea7a20SCorentin Chary { 94784d482f2SCorentin Chary if (samsung->wlan.rfkill) { 94884d482f2SCorentin Chary rfkill_unregister(samsung->wlan.rfkill); 94984d482f2SCorentin Chary rfkill_destroy(samsung->wlan.rfkill); 95084d482f2SCorentin Chary samsung->wlan.rfkill = NULL; 9515dea7a20SCorentin Chary } 95284d482f2SCorentin Chary if (samsung->bluetooth.rfkill) { 95384d482f2SCorentin Chary rfkill_unregister(samsung->bluetooth.rfkill); 95484d482f2SCorentin Chary rfkill_destroy(samsung->bluetooth.rfkill); 95584d482f2SCorentin Chary samsung->bluetooth.rfkill = NULL; 95684d482f2SCorentin Chary } 95784d482f2SCorentin Chary } 95884d482f2SCorentin Chary 95984d482f2SCorentin Chary static int samsung_new_rfkill(struct samsung_laptop *samsung, 96084d482f2SCorentin Chary struct samsung_rfkill *arfkill, 96184d482f2SCorentin Chary const char *name, enum rfkill_type type, 96284d482f2SCorentin Chary const struct rfkill_ops *ops, 96384d482f2SCorentin Chary int blocked) 96484d482f2SCorentin Chary { 96584d482f2SCorentin Chary struct rfkill **rfkill = &arfkill->rfkill; 96684d482f2SCorentin Chary int ret; 96784d482f2SCorentin Chary 96884d482f2SCorentin Chary arfkill->type = type; 96984d482f2SCorentin Chary arfkill->samsung = samsung; 97084d482f2SCorentin Chary 97184d482f2SCorentin Chary *rfkill = rfkill_alloc(name, &samsung->platform_device->dev, 97284d482f2SCorentin Chary type, ops, arfkill); 97384d482f2SCorentin Chary 97484d482f2SCorentin Chary if (!*rfkill) 97584d482f2SCorentin Chary return -EINVAL; 97684d482f2SCorentin Chary 97784d482f2SCorentin Chary if (blocked != -1) 97884d482f2SCorentin Chary rfkill_init_sw_state(*rfkill, blocked); 97984d482f2SCorentin Chary 98084d482f2SCorentin Chary ret = rfkill_register(*rfkill); 98184d482f2SCorentin Chary if (ret) { 98284d482f2SCorentin Chary rfkill_destroy(*rfkill); 98384d482f2SCorentin Chary *rfkill = NULL; 98484d482f2SCorentin Chary return ret; 98584d482f2SCorentin Chary } 98684d482f2SCorentin Chary return 0; 98784d482f2SCorentin Chary } 98884d482f2SCorentin Chary 98984d482f2SCorentin Chary static int __init samsung_rfkill_init_seclinux(struct samsung_laptop *samsung) 99084d482f2SCorentin Chary { 99184d482f2SCorentin Chary return samsung_new_rfkill(samsung, &samsung->wlan, "samsung-wlan", 99284d482f2SCorentin Chary RFKILL_TYPE_WLAN, &seclinux_rfkill_ops, -1); 99384d482f2SCorentin Chary } 99484d482f2SCorentin Chary 99584d482f2SCorentin Chary static int __init samsung_rfkill_init_swsmi(struct samsung_laptop *samsung) 99684d482f2SCorentin Chary { 99784d482f2SCorentin Chary struct sabi_data data; 99884d482f2SCorentin Chary int ret; 99984d482f2SCorentin Chary 100084d482f2SCorentin Chary ret = swsmi_wireless_status(samsung, &data); 100120db88e3SCorentin Chary if (ret) { 100220db88e3SCorentin Chary /* Some swsmi laptops use the old seclinux way to control 100320db88e3SCorentin Chary * wireless devices */ 100420db88e3SCorentin Chary if (ret == -EINVAL) 100520db88e3SCorentin Chary ret = samsung_rfkill_init_seclinux(samsung); 100684d482f2SCorentin Chary return ret; 100720db88e3SCorentin Chary } 100884d482f2SCorentin Chary 100984d482f2SCorentin Chary /* 0x02 seems to mean that the device is no present/available */ 101084d482f2SCorentin Chary 101184d482f2SCorentin Chary if (data.data[WL_STATUS_WLAN] != 0x02) 101284d482f2SCorentin Chary ret = samsung_new_rfkill(samsung, &samsung->wlan, 101384d482f2SCorentin Chary "samsung-wlan", 101484d482f2SCorentin Chary RFKILL_TYPE_WLAN, 101584d482f2SCorentin Chary &swsmi_rfkill_ops, 101684d482f2SCorentin Chary !data.data[WL_STATUS_WLAN]); 101784d482f2SCorentin Chary if (ret) 101884d482f2SCorentin Chary goto exit; 101984d482f2SCorentin Chary 102084d482f2SCorentin Chary if (data.data[WL_STATUS_BT] != 0x02) 102184d482f2SCorentin Chary ret = samsung_new_rfkill(samsung, &samsung->bluetooth, 102284d482f2SCorentin Chary "samsung-bluetooth", 102384d482f2SCorentin Chary RFKILL_TYPE_BLUETOOTH, 102484d482f2SCorentin Chary &swsmi_rfkill_ops, 102584d482f2SCorentin Chary !data.data[WL_STATUS_BT]); 102684d482f2SCorentin Chary if (ret) 102784d482f2SCorentin Chary goto exit; 102884d482f2SCorentin Chary 102984d482f2SCorentin Chary exit: 103084d482f2SCorentin Chary if (ret) 103184d482f2SCorentin Chary samsung_rfkill_exit(samsung); 103284d482f2SCorentin Chary 103384d482f2SCorentin Chary return ret; 10345dea7a20SCorentin Chary } 10355dea7a20SCorentin Chary 10365dea7a20SCorentin Chary static int __init samsung_rfkill_init(struct samsung_laptop *samsung) 10375dea7a20SCorentin Chary { 103884d482f2SCorentin Chary if (samsung->config->sabi_version == 2) 103984d482f2SCorentin Chary return samsung_rfkill_init_seclinux(samsung); 104084d482f2SCorentin Chary if (samsung->config->sabi_version == 3) 104184d482f2SCorentin Chary return samsung_rfkill_init_swsmi(samsung); 10425dea7a20SCorentin Chary return 0; 10435dea7a20SCorentin Chary } 10445dea7a20SCorentin Chary 1045b0dcaf4fSJulijonas Kikutis static void samsung_lid_handling_exit(struct samsung_laptop *samsung) 1046b0dcaf4fSJulijonas Kikutis { 1047b0dcaf4fSJulijonas Kikutis if (samsung->quirks->lid_handling) 1048b0dcaf4fSJulijonas Kikutis write_lid_handling(samsung, 0); 1049b0dcaf4fSJulijonas Kikutis } 1050b0dcaf4fSJulijonas Kikutis 1051b0dcaf4fSJulijonas Kikutis static int __init samsung_lid_handling_init(struct samsung_laptop *samsung) 1052b0dcaf4fSJulijonas Kikutis { 1053b0dcaf4fSJulijonas Kikutis int retval = 0; 1054b0dcaf4fSJulijonas Kikutis 1055b0dcaf4fSJulijonas Kikutis if (samsung->quirks->lid_handling) 1056b0dcaf4fSJulijonas Kikutis retval = write_lid_handling(samsung, 1); 1057b0dcaf4fSJulijonas Kikutis 1058b0dcaf4fSJulijonas Kikutis return retval; 1059b0dcaf4fSJulijonas Kikutis } 1060b0dcaf4fSJulijonas Kikutis 1061f674ebf1SCorentin Chary static int kbd_backlight_enable(struct samsung_laptop *samsung) 1062f674ebf1SCorentin Chary { 1063f674ebf1SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 1064f674ebf1SCorentin Chary struct sabi_data data; 1065f674ebf1SCorentin Chary int retval; 1066f674ebf1SCorentin Chary 1067f674ebf1SCorentin Chary if (commands->kbd_backlight == 0xFFFF) 1068f674ebf1SCorentin Chary return -ENODEV; 1069f674ebf1SCorentin Chary 1070f674ebf1SCorentin Chary memset(&data, 0, sizeof(data)); 1071f674ebf1SCorentin Chary data.d0 = 0xaabb; 1072f674ebf1SCorentin Chary retval = sabi_command(samsung, commands->kbd_backlight, 1073f674ebf1SCorentin Chary &data, &data); 1074f674ebf1SCorentin Chary 1075f674ebf1SCorentin Chary if (retval) 1076f674ebf1SCorentin Chary return retval; 1077f674ebf1SCorentin Chary 1078f674ebf1SCorentin Chary if (data.d0 != 0xccdd) 1079f674ebf1SCorentin Chary return -ENODEV; 1080f674ebf1SCorentin Chary return 0; 1081f674ebf1SCorentin Chary } 1082f674ebf1SCorentin Chary 1083f674ebf1SCorentin Chary static int kbd_backlight_read(struct samsung_laptop *samsung) 1084f674ebf1SCorentin Chary { 1085f674ebf1SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 1086f674ebf1SCorentin Chary struct sabi_data data; 1087f674ebf1SCorentin Chary int retval; 1088f674ebf1SCorentin Chary 1089f674ebf1SCorentin Chary memset(&data, 0, sizeof(data)); 1090f674ebf1SCorentin Chary data.data[0] = 0x81; 1091f674ebf1SCorentin Chary retval = sabi_command(samsung, commands->kbd_backlight, 1092f674ebf1SCorentin Chary &data, &data); 1093f674ebf1SCorentin Chary 1094f674ebf1SCorentin Chary if (retval) 1095f674ebf1SCorentin Chary return retval; 1096f674ebf1SCorentin Chary 1097f674ebf1SCorentin Chary return data.data[0]; 1098f674ebf1SCorentin Chary } 1099f674ebf1SCorentin Chary 1100f674ebf1SCorentin Chary static int kbd_backlight_write(struct samsung_laptop *samsung, int brightness) 1101f674ebf1SCorentin Chary { 1102f674ebf1SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 1103f674ebf1SCorentin Chary struct sabi_data data; 1104f674ebf1SCorentin Chary 1105f674ebf1SCorentin Chary memset(&data, 0, sizeof(data)); 1106f674ebf1SCorentin Chary data.d0 = 0x82 | ((brightness & 0xFF) << 8); 1107f674ebf1SCorentin Chary return sabi_command(samsung, commands->kbd_backlight, 1108f674ebf1SCorentin Chary &data, NULL); 1109f674ebf1SCorentin Chary } 1110f674ebf1SCorentin Chary 1111f674ebf1SCorentin Chary static void kbd_led_update(struct work_struct *work) 1112f674ebf1SCorentin Chary { 1113f674ebf1SCorentin Chary struct samsung_laptop *samsung; 1114f674ebf1SCorentin Chary 1115f674ebf1SCorentin Chary samsung = container_of(work, struct samsung_laptop, kbd_led_work); 1116f674ebf1SCorentin Chary kbd_backlight_write(samsung, samsung->kbd_led_wk); 1117f674ebf1SCorentin Chary } 1118f674ebf1SCorentin Chary 1119f674ebf1SCorentin Chary static void kbd_led_set(struct led_classdev *led_cdev, 1120f674ebf1SCorentin Chary enum led_brightness value) 1121f674ebf1SCorentin Chary { 1122f674ebf1SCorentin Chary struct samsung_laptop *samsung; 1123f674ebf1SCorentin Chary 1124f674ebf1SCorentin Chary samsung = container_of(led_cdev, struct samsung_laptop, kbd_led); 1125f674ebf1SCorentin Chary 1126f674ebf1SCorentin Chary if (value > samsung->kbd_led.max_brightness) 1127f674ebf1SCorentin Chary value = samsung->kbd_led.max_brightness; 1128f674ebf1SCorentin Chary else if (value < 0) 1129f674ebf1SCorentin Chary value = 0; 1130f674ebf1SCorentin Chary 1131f674ebf1SCorentin Chary samsung->kbd_led_wk = value; 1132f674ebf1SCorentin Chary queue_work(samsung->led_workqueue, &samsung->kbd_led_work); 1133f674ebf1SCorentin Chary } 1134f674ebf1SCorentin Chary 1135f674ebf1SCorentin Chary static enum led_brightness kbd_led_get(struct led_classdev *led_cdev) 1136f674ebf1SCorentin Chary { 1137f674ebf1SCorentin Chary struct samsung_laptop *samsung; 1138f674ebf1SCorentin Chary 1139f674ebf1SCorentin Chary samsung = container_of(led_cdev, struct samsung_laptop, kbd_led); 1140f674ebf1SCorentin Chary return kbd_backlight_read(samsung); 1141f674ebf1SCorentin Chary } 1142f674ebf1SCorentin Chary 1143f674ebf1SCorentin Chary static void samsung_leds_exit(struct samsung_laptop *samsung) 1144f674ebf1SCorentin Chary { 1145f674ebf1SCorentin Chary if (!IS_ERR_OR_NULL(samsung->kbd_led.dev)) 1146f674ebf1SCorentin Chary led_classdev_unregister(&samsung->kbd_led); 1147f674ebf1SCorentin Chary if (samsung->led_workqueue) 1148f674ebf1SCorentin Chary destroy_workqueue(samsung->led_workqueue); 1149f674ebf1SCorentin Chary } 1150f674ebf1SCorentin Chary 1151f674ebf1SCorentin Chary static int __init samsung_leds_init(struct samsung_laptop *samsung) 1152f674ebf1SCorentin Chary { 1153f674ebf1SCorentin Chary int ret = 0; 1154f674ebf1SCorentin Chary 1155f674ebf1SCorentin Chary samsung->led_workqueue = create_singlethread_workqueue("led_workqueue"); 1156f674ebf1SCorentin Chary if (!samsung->led_workqueue) 1157f674ebf1SCorentin Chary return -ENOMEM; 1158f674ebf1SCorentin Chary 1159f674ebf1SCorentin Chary if (kbd_backlight_enable(samsung) >= 0) { 1160f674ebf1SCorentin Chary INIT_WORK(&samsung->kbd_led_work, kbd_led_update); 1161f674ebf1SCorentin Chary 1162f674ebf1SCorentin Chary samsung->kbd_led.name = "samsung::kbd_backlight"; 1163f674ebf1SCorentin Chary samsung->kbd_led.brightness_set = kbd_led_set; 1164f674ebf1SCorentin Chary samsung->kbd_led.brightness_get = kbd_led_get; 1165f674ebf1SCorentin Chary samsung->kbd_led.max_brightness = 8; 11660ca849eaSScott Thrasher if (samsung->quirks->four_kbd_backlight_levels) 11670ca849eaSScott Thrasher samsung->kbd_led.max_brightness = 4; 1168f674ebf1SCorentin Chary 1169f674ebf1SCorentin Chary ret = led_classdev_register(&samsung->platform_device->dev, 1170f674ebf1SCorentin Chary &samsung->kbd_led); 1171f674ebf1SCorentin Chary } 1172f674ebf1SCorentin Chary 1173f674ebf1SCorentin Chary if (ret) 1174f674ebf1SCorentin Chary samsung_leds_exit(samsung); 1175f674ebf1SCorentin Chary 1176f674ebf1SCorentin Chary return ret; 1177f674ebf1SCorentin Chary } 1178f674ebf1SCorentin Chary 11795dea7a20SCorentin Chary static void samsung_backlight_exit(struct samsung_laptop *samsung) 11805dea7a20SCorentin Chary { 11815dea7a20SCorentin Chary if (samsung->backlight_device) { 11825dea7a20SCorentin Chary backlight_device_unregister(samsung->backlight_device); 11835dea7a20SCorentin Chary samsung->backlight_device = NULL; 11845dea7a20SCorentin Chary } 11855dea7a20SCorentin Chary } 11865dea7a20SCorentin Chary 11875dea7a20SCorentin Chary static int __init samsung_backlight_init(struct samsung_laptop *samsung) 11885dea7a20SCorentin Chary { 11895dea7a20SCorentin Chary struct backlight_device *bd; 11905dea7a20SCorentin Chary struct backlight_properties props; 11915dea7a20SCorentin Chary 1192f34cd9caSCorentin Chary if (!samsung->handle_backlight) 1193f34cd9caSCorentin Chary return 0; 1194f34cd9caSCorentin Chary 11955dea7a20SCorentin Chary memset(&props, 0, sizeof(struct backlight_properties)); 11965dea7a20SCorentin Chary props.type = BACKLIGHT_PLATFORM; 11975dea7a20SCorentin Chary props.max_brightness = samsung->config->max_brightness - 11985dea7a20SCorentin Chary samsung->config->min_brightness; 11995dea7a20SCorentin Chary 12005dea7a20SCorentin Chary bd = backlight_device_register("samsung", 12015dea7a20SCorentin Chary &samsung->platform_device->dev, 12025dea7a20SCorentin Chary samsung, &backlight_ops, 12035dea7a20SCorentin Chary &props); 12045dea7a20SCorentin Chary if (IS_ERR(bd)) 12055dea7a20SCorentin Chary return PTR_ERR(bd); 12065dea7a20SCorentin Chary 12075dea7a20SCorentin Chary samsung->backlight_device = bd; 12085dea7a20SCorentin Chary samsung->backlight_device->props.brightness = read_brightness(samsung); 12095dea7a20SCorentin Chary samsung->backlight_device->props.power = FB_BLANK_UNBLANK; 12105dea7a20SCorentin Chary backlight_update_status(samsung->backlight_device); 12115dea7a20SCorentin Chary 12125dea7a20SCorentin Chary return 0; 12135dea7a20SCorentin Chary } 12145dea7a20SCorentin Chary 1215bde9e509SDan Carpenter static umode_t samsung_sysfs_is_visible(struct kobject *kobj, 1216a66c1662SCorentin Chary struct attribute *attr, int idx) 1217a66c1662SCorentin Chary { 1218a66c1662SCorentin Chary struct device *dev = container_of(kobj, struct device, kobj); 1219d605ca29SWolfram Sang struct samsung_laptop *samsung = dev_get_drvdata(dev); 1220a66c1662SCorentin Chary bool ok = true; 1221a66c1662SCorentin Chary 1222a66c1662SCorentin Chary if (attr == &dev_attr_performance_level.attr) 1223a66c1662SCorentin Chary ok = !!samsung->config->performance_levels[0].name; 1224cb5b5c91SCorentin Chary if (attr == &dev_attr_battery_life_extender.attr) 1225cb5b5c91SCorentin Chary ok = !!(read_battery_life_extender(samsung) >= 0); 12263a75d378SCorentin Chary if (attr == &dev_attr_usb_charge.attr) 12273a75d378SCorentin Chary ok = !!(read_usb_charge(samsung) >= 0); 1228b0dcaf4fSJulijonas Kikutis if (attr == &dev_attr_lid_handling.attr) 1229b0dcaf4fSJulijonas Kikutis ok = !!(read_lid_handling(samsung) >= 0); 1230a66c1662SCorentin Chary 1231a66c1662SCorentin Chary return ok ? attr->mode : 0; 1232a66c1662SCorentin Chary } 1233a66c1662SCorentin Chary 12348546268eSArvind Yadav static const struct attribute_group platform_attribute_group = { 1235a66c1662SCorentin Chary .is_visible = samsung_sysfs_is_visible, 1236a66c1662SCorentin Chary .attrs = platform_attributes 1237a66c1662SCorentin Chary }; 1238a66c1662SCorentin Chary 12395dea7a20SCorentin Chary static void samsung_sysfs_exit(struct samsung_laptop *samsung) 12405dea7a20SCorentin Chary { 1241a66c1662SCorentin Chary struct platform_device *device = samsung->platform_device; 1242a66c1662SCorentin Chary 1243a66c1662SCorentin Chary sysfs_remove_group(&device->dev.kobj, &platform_attribute_group); 12445dea7a20SCorentin Chary } 12455dea7a20SCorentin Chary 12465dea7a20SCorentin Chary static int __init samsung_sysfs_init(struct samsung_laptop *samsung) 12475dea7a20SCorentin Chary { 1248a66c1662SCorentin Chary struct platform_device *device = samsung->platform_device; 1249a66c1662SCorentin Chary 1250a66c1662SCorentin Chary return sysfs_create_group(&device->dev.kobj, &platform_attribute_group); 1251a66c1662SCorentin Chary 12525dea7a20SCorentin Chary } 12535dea7a20SCorentin Chary 12549a7c551bSAndy Shevchenko static int samsung_laptop_call_show(struct seq_file *m, void *data) 12555b80fc40SCorentin Chary { 12565b80fc40SCorentin Chary struct samsung_laptop *samsung = m->private; 12575b80fc40SCorentin Chary struct sabi_data *sdata = &samsung->debug.data; 12585b80fc40SCorentin Chary int ret; 12595b80fc40SCorentin Chary 12605b80fc40SCorentin Chary seq_printf(m, "SABI 0x%04x {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n", 12615b80fc40SCorentin Chary samsung->debug.command, 12625b80fc40SCorentin Chary sdata->d0, sdata->d1, sdata->d2, sdata->d3); 12635b80fc40SCorentin Chary 12645b80fc40SCorentin Chary ret = sabi_command(samsung, samsung->debug.command, sdata, sdata); 12655b80fc40SCorentin Chary 12665b80fc40SCorentin Chary if (ret) { 12675b80fc40SCorentin Chary seq_printf(m, "SABI command 0x%04x failed\n", 12685b80fc40SCorentin Chary samsung->debug.command); 12695b80fc40SCorentin Chary return ret; 12705b80fc40SCorentin Chary } 12715b80fc40SCorentin Chary 12725b80fc40SCorentin Chary seq_printf(m, "SABI {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n", 12735b80fc40SCorentin Chary sdata->d0, sdata->d1, sdata->d2, sdata->d3); 12745b80fc40SCorentin Chary return 0; 12755b80fc40SCorentin Chary } 12769a7c551bSAndy Shevchenko DEFINE_SHOW_ATTRIBUTE(samsung_laptop_call); 12775b80fc40SCorentin Chary 12785b80fc40SCorentin Chary static void samsung_debugfs_exit(struct samsung_laptop *samsung) 12795b80fc40SCorentin Chary { 12805b80fc40SCorentin Chary debugfs_remove_recursive(samsung->debug.root); 12815b80fc40SCorentin Chary } 12825b80fc40SCorentin Chary 12835b80fc40SCorentin Chary static int samsung_debugfs_init(struct samsung_laptop *samsung) 12845b80fc40SCorentin Chary { 12855b80fc40SCorentin Chary struct dentry *dent; 12865b80fc40SCorentin Chary 12875b80fc40SCorentin Chary samsung->debug.root = debugfs_create_dir("samsung-laptop", NULL); 12885b80fc40SCorentin Chary if (!samsung->debug.root) { 12895b80fc40SCorentin Chary pr_err("failed to create debugfs directory"); 12905b80fc40SCorentin Chary goto error_debugfs; 12915b80fc40SCorentin Chary } 12925b80fc40SCorentin Chary 12935b80fc40SCorentin Chary samsung->debug.f0000_wrapper.data = samsung->f0000_segment; 12945b80fc40SCorentin Chary samsung->debug.f0000_wrapper.size = 0xffff; 12955b80fc40SCorentin Chary 12965b80fc40SCorentin Chary samsung->debug.data_wrapper.data = &samsung->debug.data; 12975b80fc40SCorentin Chary samsung->debug.data_wrapper.size = sizeof(samsung->debug.data); 12985b80fc40SCorentin Chary 12996f6ae06eSCorentin Chary samsung->debug.sdiag_wrapper.data = samsung->sdiag; 13006f6ae06eSCorentin Chary samsung->debug.sdiag_wrapper.size = strlen(samsung->sdiag); 13016f6ae06eSCorentin Chary 13025b80fc40SCorentin Chary dent = debugfs_create_u16("command", S_IRUGO | S_IWUSR, 13035b80fc40SCorentin Chary samsung->debug.root, &samsung->debug.command); 13045b80fc40SCorentin Chary if (!dent) 13055b80fc40SCorentin Chary goto error_debugfs; 13065b80fc40SCorentin Chary 13075b80fc40SCorentin Chary dent = debugfs_create_u32("d0", S_IRUGO | S_IWUSR, samsung->debug.root, 13085b80fc40SCorentin Chary &samsung->debug.data.d0); 13095b80fc40SCorentin Chary if (!dent) 13105b80fc40SCorentin Chary goto error_debugfs; 13115b80fc40SCorentin Chary 13125b80fc40SCorentin Chary dent = debugfs_create_u32("d1", S_IRUGO | S_IWUSR, samsung->debug.root, 13135b80fc40SCorentin Chary &samsung->debug.data.d1); 13145b80fc40SCorentin Chary if (!dent) 13155b80fc40SCorentin Chary goto error_debugfs; 13165b80fc40SCorentin Chary 13175b80fc40SCorentin Chary dent = debugfs_create_u16("d2", S_IRUGO | S_IWUSR, samsung->debug.root, 13185b80fc40SCorentin Chary &samsung->debug.data.d2); 13195b80fc40SCorentin Chary if (!dent) 13205b80fc40SCorentin Chary goto error_debugfs; 13215b80fc40SCorentin Chary 13225b80fc40SCorentin Chary dent = debugfs_create_u8("d3", S_IRUGO | S_IWUSR, samsung->debug.root, 13235b80fc40SCorentin Chary &samsung->debug.data.d3); 13245b80fc40SCorentin Chary if (!dent) 13255b80fc40SCorentin Chary goto error_debugfs; 13265b80fc40SCorentin Chary 13275b80fc40SCorentin Chary dent = debugfs_create_blob("data", S_IRUGO | S_IWUSR, 13285b80fc40SCorentin Chary samsung->debug.root, 13295b80fc40SCorentin Chary &samsung->debug.data_wrapper); 13305b80fc40SCorentin Chary if (!dent) 13315b80fc40SCorentin Chary goto error_debugfs; 13325b80fc40SCorentin Chary 13335b80fc40SCorentin Chary dent = debugfs_create_blob("f0000_segment", S_IRUSR | S_IWUSR, 13345b80fc40SCorentin Chary samsung->debug.root, 13355b80fc40SCorentin Chary &samsung->debug.f0000_wrapper); 13365b80fc40SCorentin Chary if (!dent) 13375b80fc40SCorentin Chary goto error_debugfs; 13385b80fc40SCorentin Chary 13395b80fc40SCorentin Chary dent = debugfs_create_file("call", S_IFREG | S_IRUGO, 13405b80fc40SCorentin Chary samsung->debug.root, samsung, 13419a7c551bSAndy Shevchenko &samsung_laptop_call_fops); 13425b80fc40SCorentin Chary if (!dent) 13435b80fc40SCorentin Chary goto error_debugfs; 13445b80fc40SCorentin Chary 13456f6ae06eSCorentin Chary dent = debugfs_create_blob("sdiag", S_IRUGO | S_IWUSR, 13466f6ae06eSCorentin Chary samsung->debug.root, 13476f6ae06eSCorentin Chary &samsung->debug.sdiag_wrapper); 13486f6ae06eSCorentin Chary if (!dent) 13496f6ae06eSCorentin Chary goto error_debugfs; 13506f6ae06eSCorentin Chary 13515b80fc40SCorentin Chary return 0; 13525b80fc40SCorentin Chary 13535b80fc40SCorentin Chary error_debugfs: 13545b80fc40SCorentin Chary samsung_debugfs_exit(samsung); 13555b80fc40SCorentin Chary return -ENOMEM; 13565b80fc40SCorentin Chary } 13575b80fc40SCorentin Chary 13585dea7a20SCorentin Chary static void samsung_sabi_exit(struct samsung_laptop *samsung) 13595dea7a20SCorentin Chary { 13605dea7a20SCorentin Chary const struct sabi_config *config = samsung->config; 13615dea7a20SCorentin Chary 13625dea7a20SCorentin Chary /* Turn off "Linux" mode in the BIOS */ 13635dea7a20SCorentin Chary if (config && config->commands.set_linux != 0xff) 13647e960711SCorentin Chary sabi_set_commandb(samsung, config->commands.set_linux, 0x80); 13655dea7a20SCorentin Chary 13665dea7a20SCorentin Chary if (samsung->sabi_iface) { 13675dea7a20SCorentin Chary iounmap(samsung->sabi_iface); 13685dea7a20SCorentin Chary samsung->sabi_iface = NULL; 13695dea7a20SCorentin Chary } 13705dea7a20SCorentin Chary if (samsung->f0000_segment) { 13715dea7a20SCorentin Chary iounmap(samsung->f0000_segment); 13725dea7a20SCorentin Chary samsung->f0000_segment = NULL; 13735dea7a20SCorentin Chary } 13745dea7a20SCorentin Chary 13755dea7a20SCorentin Chary samsung->config = NULL; 13765dea7a20SCorentin Chary } 13775dea7a20SCorentin Chary 137849dd7730SCorentin Chary static __init void samsung_sabi_infos(struct samsung_laptop *samsung, int loca, 137949dd7730SCorentin Chary unsigned int ifaceP) 13805dea7a20SCorentin Chary { 13815dea7a20SCorentin Chary const struct sabi_config *config = samsung->config; 13825dea7a20SCorentin Chary 13835dea7a20SCorentin Chary printk(KERN_DEBUG "This computer supports SABI==%x\n", 13845dea7a20SCorentin Chary loca + 0xf0000 - 6); 138549dd7730SCorentin Chary 13865dea7a20SCorentin Chary printk(KERN_DEBUG "SABI header:\n"); 13875dea7a20SCorentin Chary printk(KERN_DEBUG " SMI Port Number = 0x%04x\n", 13885dea7a20SCorentin Chary readw(samsung->sabi + config->header_offsets.port)); 13895dea7a20SCorentin Chary printk(KERN_DEBUG " SMI Interface Function = 0x%02x\n", 13905dea7a20SCorentin Chary readb(samsung->sabi + config->header_offsets.iface_func)); 13915dea7a20SCorentin Chary printk(KERN_DEBUG " SMI enable memory buffer = 0x%02x\n", 13925dea7a20SCorentin Chary readb(samsung->sabi + config->header_offsets.en_mem)); 13935dea7a20SCorentin Chary printk(KERN_DEBUG " SMI restore memory buffer = 0x%02x\n", 13945dea7a20SCorentin Chary readb(samsung->sabi + config->header_offsets.re_mem)); 13955dea7a20SCorentin Chary printk(KERN_DEBUG " SABI data offset = 0x%04x\n", 13965dea7a20SCorentin Chary readw(samsung->sabi + config->header_offsets.data_offset)); 13975dea7a20SCorentin Chary printk(KERN_DEBUG " SABI data segment = 0x%04x\n", 13985dea7a20SCorentin Chary readw(samsung->sabi + config->header_offsets.data_segment)); 13995dea7a20SCorentin Chary 140049dd7730SCorentin Chary printk(KERN_DEBUG " SABI pointer = 0x%08x\n", ifaceP); 14015dea7a20SCorentin Chary } 14025dea7a20SCorentin Chary 14036f6ae06eSCorentin Chary static void __init samsung_sabi_diag(struct samsung_laptop *samsung) 14046f6ae06eSCorentin Chary { 14056f6ae06eSCorentin Chary int loca = find_signature(samsung->f0000_segment, "SDiaG@"); 14066f6ae06eSCorentin Chary int i; 14076f6ae06eSCorentin Chary 14086f6ae06eSCorentin Chary if (loca == 0xffff) 14096f6ae06eSCorentin Chary return ; 14106f6ae06eSCorentin Chary 14116f6ae06eSCorentin Chary /* Example: 14126f6ae06eSCorentin Chary * Ident: @SDiaG@686XX-N90X3A/966-SEC-07HL-S90X3A 14136f6ae06eSCorentin Chary * 14146f6ae06eSCorentin Chary * Product name: 90X3A 14156f6ae06eSCorentin Chary * BIOS Version: 07HL 14166f6ae06eSCorentin Chary */ 14176f6ae06eSCorentin Chary loca += 1; 14186f6ae06eSCorentin Chary for (i = 0; loca < 0xffff && i < sizeof(samsung->sdiag) - 1; loca++) { 14196f6ae06eSCorentin Chary char temp = readb(samsung->f0000_segment + loca); 14206f6ae06eSCorentin Chary 14216f6ae06eSCorentin Chary if (isalnum(temp) || temp == '/' || temp == '-') 14226f6ae06eSCorentin Chary samsung->sdiag[i++] = temp; 14236f6ae06eSCorentin Chary else 14246f6ae06eSCorentin Chary break ; 14256f6ae06eSCorentin Chary } 14266f6ae06eSCorentin Chary 14276f6ae06eSCorentin Chary if (debug && samsung->sdiag[0]) 14286f6ae06eSCorentin Chary pr_info("sdiag: %s", samsung->sdiag); 14296f6ae06eSCorentin Chary } 14306f6ae06eSCorentin Chary 14315dea7a20SCorentin Chary static int __init samsung_sabi_init(struct samsung_laptop *samsung) 14325dea7a20SCorentin Chary { 14335dea7a20SCorentin Chary const struct sabi_config *config = NULL; 14345dea7a20SCorentin Chary const struct sabi_commands *commands; 14355dea7a20SCorentin Chary unsigned int ifaceP; 14360d2c9535SAndy Shevchenko int loca = 0xffff; 14375dea7a20SCorentin Chary int ret = 0; 14385dea7a20SCorentin Chary int i; 14395dea7a20SCorentin Chary 14405dea7a20SCorentin Chary samsung->f0000_segment = ioremap_nocache(0xf0000, 0xffff); 14415dea7a20SCorentin Chary if (!samsung->f0000_segment) { 14423be324a9SCorentin Chary if (debug || force) 14435dea7a20SCorentin Chary pr_err("Can't map the segment at 0xf0000\n"); 14445dea7a20SCorentin Chary ret = -EINVAL; 14455dea7a20SCorentin Chary goto exit; 14465dea7a20SCorentin Chary } 14475dea7a20SCorentin Chary 14486f6ae06eSCorentin Chary samsung_sabi_diag(samsung); 14496f6ae06eSCorentin Chary 14505dea7a20SCorentin Chary /* Try to find one of the signatures in memory to find the header */ 14514e7f09adSLad, Prabhakar for (i = 0; sabi_configs[i].test_string != NULL; ++i) { 14525dea7a20SCorentin Chary samsung->config = &sabi_configs[i]; 14535dea7a20SCorentin Chary loca = find_signature(samsung->f0000_segment, 14545dea7a20SCorentin Chary samsung->config->test_string); 14555dea7a20SCorentin Chary if (loca != 0xffff) 14565dea7a20SCorentin Chary break; 14575dea7a20SCorentin Chary } 14585dea7a20SCorentin Chary 14595dea7a20SCorentin Chary if (loca == 0xffff) { 14603be324a9SCorentin Chary if (debug || force) 14615dea7a20SCorentin Chary pr_err("This computer does not support SABI\n"); 14625dea7a20SCorentin Chary ret = -ENODEV; 14635dea7a20SCorentin Chary goto exit; 14645dea7a20SCorentin Chary } 14655dea7a20SCorentin Chary 14665dea7a20SCorentin Chary config = samsung->config; 14675dea7a20SCorentin Chary commands = &config->commands; 14685dea7a20SCorentin Chary 14695dea7a20SCorentin Chary /* point to the SMI port Number */ 14705dea7a20SCorentin Chary loca += 1; 14715dea7a20SCorentin Chary samsung->sabi = (samsung->f0000_segment + loca); 14725dea7a20SCorentin Chary 14735dea7a20SCorentin Chary /* Get a pointer to the SABI Interface */ 14745dea7a20SCorentin Chary ifaceP = (readw(samsung->sabi + config->header_offsets.data_segment) & 0x0ffff) << 4; 14755dea7a20SCorentin Chary ifaceP += readw(samsung->sabi + config->header_offsets.data_offset) & 0x0ffff; 147649dd7730SCorentin Chary 147749dd7730SCorentin Chary if (debug) 147849dd7730SCorentin Chary samsung_sabi_infos(samsung, loca, ifaceP); 147949dd7730SCorentin Chary 14805dea7a20SCorentin Chary samsung->sabi_iface = ioremap_nocache(ifaceP, 16); 14815dea7a20SCorentin Chary if (!samsung->sabi_iface) { 14825dea7a20SCorentin Chary pr_err("Can't remap %x\n", ifaceP); 14835dea7a20SCorentin Chary ret = -EINVAL; 14845dea7a20SCorentin Chary goto exit; 14855dea7a20SCorentin Chary } 14865dea7a20SCorentin Chary 14875dea7a20SCorentin Chary /* Turn on "Linux" mode in the BIOS */ 14885dea7a20SCorentin Chary if (commands->set_linux != 0xff) { 14897e960711SCorentin Chary int retval = sabi_set_commandb(samsung, 14905dea7a20SCorentin Chary commands->set_linux, 0x81); 14915dea7a20SCorentin Chary if (retval) { 14925dea7a20SCorentin Chary pr_warn("Linux mode was not set!\n"); 14935dea7a20SCorentin Chary ret = -ENODEV; 14945dea7a20SCorentin Chary goto exit; 14955dea7a20SCorentin Chary } 14965dea7a20SCorentin Chary } 14975dea7a20SCorentin Chary 14985dea7a20SCorentin Chary /* Check for stepping quirk */ 1499f34cd9caSCorentin Chary if (samsung->handle_backlight) 15005dea7a20SCorentin Chary check_for_stepping_quirk(samsung); 15015dea7a20SCorentin Chary 15022e777187SCorentin Chary pr_info("detected SABI interface: %s\n", 15032e777187SCorentin Chary samsung->config->test_string); 15042e777187SCorentin Chary 15055dea7a20SCorentin Chary exit: 15065dea7a20SCorentin Chary if (ret) 15075dea7a20SCorentin Chary samsung_sabi_exit(samsung); 15085dea7a20SCorentin Chary 15095dea7a20SCorentin Chary return ret; 15105dea7a20SCorentin Chary } 15115dea7a20SCorentin Chary 15125dea7a20SCorentin Chary static void samsung_platform_exit(struct samsung_laptop *samsung) 15135dea7a20SCorentin Chary { 15145dea7a20SCorentin Chary if (samsung->platform_device) { 15155dea7a20SCorentin Chary platform_device_unregister(samsung->platform_device); 15165dea7a20SCorentin Chary samsung->platform_device = NULL; 15175dea7a20SCorentin Chary } 15185dea7a20SCorentin Chary } 15195dea7a20SCorentin Chary 15200ca849eaSScott Thrasher static int samsung_pm_notification(struct notifier_block *nb, 15210ca849eaSScott Thrasher unsigned long val, void *ptr) 15220ca849eaSScott Thrasher { 15230ca849eaSScott Thrasher struct samsung_laptop *samsung; 15240ca849eaSScott Thrasher 15250ca849eaSScott Thrasher samsung = container_of(nb, struct samsung_laptop, pm_nb); 15260ca849eaSScott Thrasher if (val == PM_POST_HIBERNATION && 15270ca849eaSScott Thrasher samsung->quirks->enable_kbd_backlight) 15280ca849eaSScott Thrasher kbd_backlight_enable(samsung); 15290ca849eaSScott Thrasher 1530b0dcaf4fSJulijonas Kikutis if (val == PM_POST_HIBERNATION && samsung->quirks->lid_handling) 1531b0dcaf4fSJulijonas Kikutis write_lid_handling(samsung, 1); 1532b0dcaf4fSJulijonas Kikutis 15330ca849eaSScott Thrasher return 0; 15340ca849eaSScott Thrasher } 15350ca849eaSScott Thrasher 15365dea7a20SCorentin Chary static int __init samsung_platform_init(struct samsung_laptop *samsung) 15375dea7a20SCorentin Chary { 15385dea7a20SCorentin Chary struct platform_device *pdev; 15395dea7a20SCorentin Chary 15405dea7a20SCorentin Chary pdev = platform_device_register_simple("samsung", -1, NULL, 0); 15415dea7a20SCorentin Chary if (IS_ERR(pdev)) 15425dea7a20SCorentin Chary return PTR_ERR(pdev); 15435dea7a20SCorentin Chary 15445dea7a20SCorentin Chary samsung->platform_device = pdev; 15455dea7a20SCorentin Chary platform_set_drvdata(samsung->platform_device, samsung); 15465dea7a20SCorentin Chary return 0; 15475dea7a20SCorentin Chary } 15485dea7a20SCorentin Chary 1549a979e2e2SCorentin Chary static struct samsung_quirks *quirks; 1550a979e2e2SCorentin Chary 1551a979e2e2SCorentin Chary static int __init samsung_dmi_matched(const struct dmi_system_id *d) 1552a979e2e2SCorentin Chary { 1553a979e2e2SCorentin Chary quirks = d->driver_data; 1554a979e2e2SCorentin Chary return 0; 1555a979e2e2SCorentin Chary } 1556a979e2e2SCorentin Chary 15576faadbbbSChristoph Hellwig static const struct dmi_system_id samsung_dmi_table[] __initconst = { 15582d70b73aSGreg Kroah-Hartman { 15592d70b73aSGreg Kroah-Hartman .matches = { 15602d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_SYS_VENDOR, 15612d70b73aSGreg Kroah-Hartman "SAMSUNG ELECTRONICS CO., LTD."), 15623be324a9SCorentin Chary DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */ 15632d70b73aSGreg Kroah-Hartman }, 15642d70b73aSGreg Kroah-Hartman }, 15652d70b73aSGreg Kroah-Hartman { 15662d70b73aSGreg Kroah-Hartman .matches = { 15672d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_SYS_VENDOR, 15682d70b73aSGreg Kroah-Hartman "SAMSUNG ELECTRONICS CO., LTD."), 15693be324a9SCorentin Chary DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /* Laptop */ 15702d70b73aSGreg Kroah-Hartman }, 15712d70b73aSGreg Kroah-Hartman }, 15722d70b73aSGreg Kroah-Hartman { 15734e2441c0SJ Witteveen .matches = { 15744e2441c0SJ Witteveen DMI_MATCH(DMI_SYS_VENDOR, 15754e2441c0SJ Witteveen "SAMSUNG ELECTRONICS CO., LTD."), 15763be324a9SCorentin Chary DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */ 15774e2441c0SJ Witteveen }, 15784e2441c0SJ Witteveen }, 15794e2441c0SJ Witteveen { 15802d70b73aSGreg Kroah-Hartman .matches = { 15812d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_SYS_VENDOR, 15822d70b73aSGreg Kroah-Hartman "SAMSUNG ELECTRONICS CO., LTD."), 15833be324a9SCorentin Chary DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */ 15842d70b73aSGreg Kroah-Hartman }, 15857500eeb0STommaso Massimi }, 1586e052067dSCorentin Chary /* DMI ids for laptops with bad Chassis Type */ 1587e052067dSCorentin Chary { 1588e052067dSCorentin Chary .ident = "R40/R41", 1589e052067dSCorentin Chary .matches = { 1590e052067dSCorentin Chary DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1591e052067dSCorentin Chary DMI_MATCH(DMI_PRODUCT_NAME, "R40/R41"), 1592e052067dSCorentin Chary DMI_MATCH(DMI_BOARD_NAME, "R40/R41"), 1593e052067dSCorentin Chary }, 1594e052067dSCorentin Chary }, 1595a979e2e2SCorentin Chary /* Specific DMI ids for laptop with quirks */ 1596a979e2e2SCorentin Chary { 1597a979e2e2SCorentin Chary .callback = samsung_dmi_matched, 1598a979e2e2SCorentin Chary .ident = "N150P", 1599a979e2e2SCorentin Chary .matches = { 1600a979e2e2SCorentin Chary DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1601a979e2e2SCorentin Chary DMI_MATCH(DMI_PRODUCT_NAME, "N150P"), 1602a979e2e2SCorentin Chary DMI_MATCH(DMI_BOARD_NAME, "N150P"), 1603a979e2e2SCorentin Chary }, 16044690555eSHans de Goede .driver_data = &samsung_use_native_backlight, 1605a979e2e2SCorentin Chary }, 1606a979e2e2SCorentin Chary { 1607a979e2e2SCorentin Chary .callback = samsung_dmi_matched, 1608a979e2e2SCorentin Chary .ident = "N145P/N250P/N260P", 1609a979e2e2SCorentin Chary .matches = { 1610a979e2e2SCorentin Chary DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1611a979e2e2SCorentin Chary DMI_MATCH(DMI_PRODUCT_NAME, "N145P/N250P/N260P"), 1612a979e2e2SCorentin Chary DMI_MATCH(DMI_BOARD_NAME, "N145P/N250P/N260P"), 1613a979e2e2SCorentin Chary }, 16144690555eSHans de Goede .driver_data = &samsung_use_native_backlight, 1615a979e2e2SCorentin Chary }, 1616a979e2e2SCorentin Chary { 1617a979e2e2SCorentin Chary .callback = samsung_dmi_matched, 1618a979e2e2SCorentin Chary .ident = "N150/N210/N220", 1619a979e2e2SCorentin Chary .matches = { 1620a979e2e2SCorentin Chary DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1621a979e2e2SCorentin Chary DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220"), 1622a979e2e2SCorentin Chary DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220"), 1623a979e2e2SCorentin Chary }, 1624a979e2e2SCorentin Chary .driver_data = &samsung_broken_acpi_video, 1625a979e2e2SCorentin Chary }, 1626a979e2e2SCorentin Chary { 1627a979e2e2SCorentin Chary .callback = samsung_dmi_matched, 1628a979e2e2SCorentin Chary .ident = "NF110/NF210/NF310", 1629a979e2e2SCorentin Chary .matches = { 1630a979e2e2SCorentin Chary DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1631a979e2e2SCorentin Chary DMI_MATCH(DMI_PRODUCT_NAME, "NF110/NF210/NF310"), 1632a979e2e2SCorentin Chary DMI_MATCH(DMI_BOARD_NAME, "NF110/NF210/NF310"), 1633a979e2e2SCorentin Chary }, 1634a979e2e2SCorentin Chary .driver_data = &samsung_broken_acpi_video, 1635a979e2e2SCorentin Chary }, 163609d5677cSCorentin Chary { 163709d5677cSCorentin Chary .callback = samsung_dmi_matched, 163809d5677cSCorentin Chary .ident = "X360", 163909d5677cSCorentin Chary .matches = { 164009d5677cSCorentin Chary DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 164109d5677cSCorentin Chary DMI_MATCH(DMI_PRODUCT_NAME, "X360"), 164209d5677cSCorentin Chary DMI_MATCH(DMI_BOARD_NAME, "X360"), 164309d5677cSCorentin Chary }, 164409d5677cSCorentin Chary .driver_data = &samsung_broken_acpi_video, 164509d5677cSCorentin Chary }, 1646e04c200fSSeth Forshee { 1647e04c200fSSeth Forshee .callback = samsung_dmi_matched, 1648e04c200fSSeth Forshee .ident = "N250P", 1649e04c200fSSeth Forshee .matches = { 1650e04c200fSSeth Forshee DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1651e04c200fSSeth Forshee DMI_MATCH(DMI_PRODUCT_NAME, "N250P"), 1652e04c200fSSeth Forshee DMI_MATCH(DMI_BOARD_NAME, "N250P"), 1653e04c200fSSeth Forshee }, 16544690555eSHans de Goede .driver_data = &samsung_use_native_backlight, 1655e04c200fSSeth Forshee }, 16560ca849eaSScott Thrasher { 16570ca849eaSScott Thrasher .callback = samsung_dmi_matched, 16585a1426c9SHans de Goede .ident = "NC210", 16595a1426c9SHans de Goede .matches = { 16605a1426c9SHans de Goede DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 16615a1426c9SHans de Goede DMI_MATCH(DMI_PRODUCT_NAME, "NC210/NC110"), 16625a1426c9SHans de Goede DMI_MATCH(DMI_BOARD_NAME, "NC210/NC110"), 16635a1426c9SHans de Goede }, 16645a1426c9SHans de Goede .driver_data = &samsung_broken_acpi_video, 16655a1426c9SHans de Goede }, 16665a1426c9SHans de Goede { 16675a1426c9SHans de Goede .callback = samsung_dmi_matched, 16680ca849eaSScott Thrasher .ident = "730U3E/740U3E", 16690ca849eaSScott Thrasher .matches = { 16700ca849eaSScott Thrasher DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 16710ca849eaSScott Thrasher DMI_MATCH(DMI_PRODUCT_NAME, "730U3E/740U3E"), 16720ca849eaSScott Thrasher }, 16730ca849eaSScott Thrasher .driver_data = &samsung_np740u3e, 16740ca849eaSScott Thrasher }, 1675b0dcaf4fSJulijonas Kikutis { 1676b0dcaf4fSJulijonas Kikutis .callback = samsung_dmi_matched, 1677b0dcaf4fSJulijonas Kikutis .ident = "300V3Z/300V4Z/300V5Z", 1678b0dcaf4fSJulijonas Kikutis .matches = { 1679b0dcaf4fSJulijonas Kikutis DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1680b0dcaf4fSJulijonas Kikutis DMI_MATCH(DMI_PRODUCT_NAME, "300V3Z/300V4Z/300V5Z"), 1681b0dcaf4fSJulijonas Kikutis }, 1682b0dcaf4fSJulijonas Kikutis .driver_data = &samsung_lid_handling, 1683b0dcaf4fSJulijonas Kikutis }, 16842d70b73aSGreg Kroah-Hartman { }, 16852d70b73aSGreg Kroah-Hartman }; 16862d70b73aSGreg Kroah-Hartman MODULE_DEVICE_TABLE(dmi, samsung_dmi_table); 16872d70b73aSGreg Kroah-Hartman 16885dea7a20SCorentin Chary static struct platform_device *samsung_platform_device; 16892d70b73aSGreg Kroah-Hartman 16902d70b73aSGreg Kroah-Hartman static int __init samsung_init(void) 16912d70b73aSGreg Kroah-Hartman { 16925dea7a20SCorentin Chary struct samsung_laptop *samsung; 16935dea7a20SCorentin Chary int ret; 16942d70b73aSGreg Kroah-Hartman 1695e0094244SMatt Fleming if (efi_enabled(EFI_BOOT)) 1696e0094244SMatt Fleming return -ENODEV; 1697e0094244SMatt Fleming 1698a979e2e2SCorentin Chary quirks = &samsung_unknown; 16992d70b73aSGreg Kroah-Hartman if (!force && !dmi_check_system(samsung_dmi_table)) 17002d70b73aSGreg Kroah-Hartman return -ENODEV; 17012d70b73aSGreg Kroah-Hartman 1702a6df4894SCorentin Chary samsung = kzalloc(sizeof(*samsung), GFP_KERNEL); 1703a6df4894SCorentin Chary if (!samsung) 1704a6df4894SCorentin Chary return -ENOMEM; 1705a6df4894SCorentin Chary 1706a6df4894SCorentin Chary mutex_init(&samsung->sabi_mutex); 1707f34cd9caSCorentin Chary samsung->handle_backlight = true; 1708a979e2e2SCorentin Chary samsung->quirks = quirks; 1709f34cd9caSCorentin Chary 1710a60b2176SCorentin Chary #ifdef CONFIG_ACPI 1711a60b2176SCorentin Chary if (samsung->quirks->broken_acpi_video) 17126c072299SHans de Goede acpi_video_set_dmi_backlight_type(acpi_backlight_vendor); 17136c072299SHans de Goede if (samsung->quirks->use_native_backlight) 17146c072299SHans de Goede acpi_video_set_dmi_backlight_type(acpi_backlight_native); 1715a60b2176SCorentin Chary 17166c072299SHans de Goede if (acpi_video_get_backlight_type() != acpi_backlight_vendor) 1717f34cd9caSCorentin Chary samsung->handle_backlight = false; 1718f34cd9caSCorentin Chary #endif 1719a979e2e2SCorentin Chary 17205dea7a20SCorentin Chary ret = samsung_platform_init(samsung); 17215dea7a20SCorentin Chary if (ret) 17225dea7a20SCorentin Chary goto error_platform; 17232d70b73aSGreg Kroah-Hartman 17245dea7a20SCorentin Chary ret = samsung_sabi_init(samsung); 17255dea7a20SCorentin Chary if (ret) 17265dea7a20SCorentin Chary goto error_sabi; 17272d70b73aSGreg Kroah-Hartman 17285dea7a20SCorentin Chary ret = samsung_sysfs_init(samsung); 17295dea7a20SCorentin Chary if (ret) 17305dea7a20SCorentin Chary goto error_sysfs; 17312d70b73aSGreg Kroah-Hartman 17325dea7a20SCorentin Chary ret = samsung_backlight_init(samsung); 17335dea7a20SCorentin Chary if (ret) 17345dea7a20SCorentin Chary goto error_backlight; 1735a6df4894SCorentin Chary 17365dea7a20SCorentin Chary ret = samsung_rfkill_init(samsung); 17375dea7a20SCorentin Chary if (ret) 17385dea7a20SCorentin Chary goto error_rfkill; 17392d70b73aSGreg Kroah-Hartman 1740f674ebf1SCorentin Chary ret = samsung_leds_init(samsung); 1741f674ebf1SCorentin Chary if (ret) 1742f674ebf1SCorentin Chary goto error_leds; 1743f674ebf1SCorentin Chary 1744b0dcaf4fSJulijonas Kikutis ret = samsung_lid_handling_init(samsung); 1745b0dcaf4fSJulijonas Kikutis if (ret) 1746b0dcaf4fSJulijonas Kikutis goto error_lid_handling; 1747b0dcaf4fSJulijonas Kikutis 17485b80fc40SCorentin Chary ret = samsung_debugfs_init(samsung); 17495b80fc40SCorentin Chary if (ret) 17505b80fc40SCorentin Chary goto error_debugfs; 17515b80fc40SCorentin Chary 17520ca849eaSScott Thrasher samsung->pm_nb.notifier_call = samsung_pm_notification; 17530ca849eaSScott Thrasher register_pm_notifier(&samsung->pm_nb); 17540ca849eaSScott Thrasher 17555dea7a20SCorentin Chary samsung_platform_device = samsung->platform_device; 17565dea7a20SCorentin Chary return ret; 17572d70b73aSGreg Kroah-Hartman 17585b80fc40SCorentin Chary error_debugfs: 1759b0dcaf4fSJulijonas Kikutis samsung_lid_handling_exit(samsung); 1760b0dcaf4fSJulijonas Kikutis error_lid_handling: 1761f674ebf1SCorentin Chary samsung_leds_exit(samsung); 1762f674ebf1SCorentin Chary error_leds: 17635b80fc40SCorentin Chary samsung_rfkill_exit(samsung); 17645dea7a20SCorentin Chary error_rfkill: 17655dea7a20SCorentin Chary samsung_backlight_exit(samsung); 17665dea7a20SCorentin Chary error_backlight: 17675dea7a20SCorentin Chary samsung_sysfs_exit(samsung); 17685dea7a20SCorentin Chary error_sysfs: 17695dea7a20SCorentin Chary samsung_sabi_exit(samsung); 17705dea7a20SCorentin Chary error_sabi: 17715dea7a20SCorentin Chary samsung_platform_exit(samsung); 17725dea7a20SCorentin Chary error_platform: 1773a6df4894SCorentin Chary kfree(samsung); 17745dea7a20SCorentin Chary return ret; 17752d70b73aSGreg Kroah-Hartman } 17762d70b73aSGreg Kroah-Hartman 17772d70b73aSGreg Kroah-Hartman static void __exit samsung_exit(void) 17782d70b73aSGreg Kroah-Hartman { 17795dea7a20SCorentin Chary struct samsung_laptop *samsung; 17802d70b73aSGreg Kroah-Hartman 17815dea7a20SCorentin Chary samsung = platform_get_drvdata(samsung_platform_device); 17820ca849eaSScott Thrasher unregister_pm_notifier(&samsung->pm_nb); 1783a6df4894SCorentin Chary 17845b80fc40SCorentin Chary samsung_debugfs_exit(samsung); 1785b0dcaf4fSJulijonas Kikutis samsung_lid_handling_exit(samsung); 1786f674ebf1SCorentin Chary samsung_leds_exit(samsung); 17875dea7a20SCorentin Chary samsung_rfkill_exit(samsung); 17885dea7a20SCorentin Chary samsung_backlight_exit(samsung); 17895dea7a20SCorentin Chary samsung_sysfs_exit(samsung); 17905dea7a20SCorentin Chary samsung_sabi_exit(samsung); 17915dea7a20SCorentin Chary samsung_platform_exit(samsung); 17925dea7a20SCorentin Chary 1793a6df4894SCorentin Chary kfree(samsung); 17945dea7a20SCorentin Chary samsung_platform_device = NULL; 17952d70b73aSGreg Kroah-Hartman } 17962d70b73aSGreg Kroah-Hartman 17972d70b73aSGreg Kroah-Hartman module_init(samsung_init); 17982d70b73aSGreg Kroah-Hartman module_exit(samsung_exit); 17992d70b73aSGreg Kroah-Hartman 18002d70b73aSGreg Kroah-Hartman MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@suse.de>"); 18012d70b73aSGreg Kroah-Hartman MODULE_DESCRIPTION("Samsung Backlight driver"); 18022d70b73aSGreg Kroah-Hartman MODULE_LICENSE("GPL"); 1803