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 59484d482f2SCorentin Chary static 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 65484d482f2SCorentin Chary static 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); 1219a66c1662SCorentin Chary struct platform_device *pdev = to_platform_device(dev); 1220a66c1662SCorentin Chary struct samsung_laptop *samsung = platform_get_drvdata(pdev); 1221a66c1662SCorentin Chary bool ok = true; 1222a66c1662SCorentin Chary 1223a66c1662SCorentin Chary if (attr == &dev_attr_performance_level.attr) 1224a66c1662SCorentin Chary ok = !!samsung->config->performance_levels[0].name; 1225cb5b5c91SCorentin Chary if (attr == &dev_attr_battery_life_extender.attr) 1226cb5b5c91SCorentin Chary ok = !!(read_battery_life_extender(samsung) >= 0); 12273a75d378SCorentin Chary if (attr == &dev_attr_usb_charge.attr) 12283a75d378SCorentin Chary ok = !!(read_usb_charge(samsung) >= 0); 1229b0dcaf4fSJulijonas Kikutis if (attr == &dev_attr_lid_handling.attr) 1230b0dcaf4fSJulijonas Kikutis ok = !!(read_lid_handling(samsung) >= 0); 1231a66c1662SCorentin Chary 1232a66c1662SCorentin Chary return ok ? attr->mode : 0; 1233a66c1662SCorentin Chary } 1234a66c1662SCorentin Chary 1235a66c1662SCorentin Chary static struct attribute_group platform_attribute_group = { 1236a66c1662SCorentin Chary .is_visible = samsung_sysfs_is_visible, 1237a66c1662SCorentin Chary .attrs = platform_attributes 1238a66c1662SCorentin Chary }; 1239a66c1662SCorentin Chary 12405dea7a20SCorentin Chary static void samsung_sysfs_exit(struct samsung_laptop *samsung) 12415dea7a20SCorentin Chary { 1242a66c1662SCorentin Chary struct platform_device *device = samsung->platform_device; 1243a66c1662SCorentin Chary 1244a66c1662SCorentin Chary sysfs_remove_group(&device->dev.kobj, &platform_attribute_group); 12455dea7a20SCorentin Chary } 12465dea7a20SCorentin Chary 12475dea7a20SCorentin Chary static int __init samsung_sysfs_init(struct samsung_laptop *samsung) 12485dea7a20SCorentin Chary { 1249a66c1662SCorentin Chary struct platform_device *device = samsung->platform_device; 1250a66c1662SCorentin Chary 1251a66c1662SCorentin Chary return sysfs_create_group(&device->dev.kobj, &platform_attribute_group); 1252a66c1662SCorentin Chary 12535dea7a20SCorentin Chary } 12545dea7a20SCorentin Chary 12555b80fc40SCorentin Chary static int show_call(struct seq_file *m, void *data) 12565b80fc40SCorentin Chary { 12575b80fc40SCorentin Chary struct samsung_laptop *samsung = m->private; 12585b80fc40SCorentin Chary struct sabi_data *sdata = &samsung->debug.data; 12595b80fc40SCorentin Chary int ret; 12605b80fc40SCorentin Chary 12615b80fc40SCorentin Chary seq_printf(m, "SABI 0x%04x {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n", 12625b80fc40SCorentin Chary samsung->debug.command, 12635b80fc40SCorentin Chary sdata->d0, sdata->d1, sdata->d2, sdata->d3); 12645b80fc40SCorentin Chary 12655b80fc40SCorentin Chary ret = sabi_command(samsung, samsung->debug.command, sdata, sdata); 12665b80fc40SCorentin Chary 12675b80fc40SCorentin Chary if (ret) { 12685b80fc40SCorentin Chary seq_printf(m, "SABI command 0x%04x failed\n", 12695b80fc40SCorentin Chary samsung->debug.command); 12705b80fc40SCorentin Chary return ret; 12715b80fc40SCorentin Chary } 12725b80fc40SCorentin Chary 12735b80fc40SCorentin Chary seq_printf(m, "SABI {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n", 12745b80fc40SCorentin Chary sdata->d0, sdata->d1, sdata->d2, sdata->d3); 12755b80fc40SCorentin Chary return 0; 12765b80fc40SCorentin Chary } 12775b80fc40SCorentin Chary 12785b80fc40SCorentin Chary static int samsung_debugfs_open(struct inode *inode, struct file *file) 12795b80fc40SCorentin Chary { 12805b80fc40SCorentin Chary return single_open(file, show_call, inode->i_private); 12815b80fc40SCorentin Chary } 12825b80fc40SCorentin Chary 12835b80fc40SCorentin Chary static const struct file_operations samsung_laptop_call_io_ops = { 12845b80fc40SCorentin Chary .owner = THIS_MODULE, 12855b80fc40SCorentin Chary .open = samsung_debugfs_open, 12865b80fc40SCorentin Chary .read = seq_read, 12875b80fc40SCorentin Chary .llseek = seq_lseek, 12885b80fc40SCorentin Chary .release = single_release, 12895b80fc40SCorentin Chary }; 12905b80fc40SCorentin Chary 12915b80fc40SCorentin Chary static void samsung_debugfs_exit(struct samsung_laptop *samsung) 12925b80fc40SCorentin Chary { 12935b80fc40SCorentin Chary debugfs_remove_recursive(samsung->debug.root); 12945b80fc40SCorentin Chary } 12955b80fc40SCorentin Chary 12965b80fc40SCorentin Chary static int samsung_debugfs_init(struct samsung_laptop *samsung) 12975b80fc40SCorentin Chary { 12985b80fc40SCorentin Chary struct dentry *dent; 12995b80fc40SCorentin Chary 13005b80fc40SCorentin Chary samsung->debug.root = debugfs_create_dir("samsung-laptop", NULL); 13015b80fc40SCorentin Chary if (!samsung->debug.root) { 13025b80fc40SCorentin Chary pr_err("failed to create debugfs directory"); 13035b80fc40SCorentin Chary goto error_debugfs; 13045b80fc40SCorentin Chary } 13055b80fc40SCorentin Chary 13065b80fc40SCorentin Chary samsung->debug.f0000_wrapper.data = samsung->f0000_segment; 13075b80fc40SCorentin Chary samsung->debug.f0000_wrapper.size = 0xffff; 13085b80fc40SCorentin Chary 13095b80fc40SCorentin Chary samsung->debug.data_wrapper.data = &samsung->debug.data; 13105b80fc40SCorentin Chary samsung->debug.data_wrapper.size = sizeof(samsung->debug.data); 13115b80fc40SCorentin Chary 13126f6ae06eSCorentin Chary samsung->debug.sdiag_wrapper.data = samsung->sdiag; 13136f6ae06eSCorentin Chary samsung->debug.sdiag_wrapper.size = strlen(samsung->sdiag); 13146f6ae06eSCorentin Chary 13155b80fc40SCorentin Chary dent = debugfs_create_u16("command", S_IRUGO | S_IWUSR, 13165b80fc40SCorentin Chary samsung->debug.root, &samsung->debug.command); 13175b80fc40SCorentin Chary if (!dent) 13185b80fc40SCorentin Chary goto error_debugfs; 13195b80fc40SCorentin Chary 13205b80fc40SCorentin Chary dent = debugfs_create_u32("d0", S_IRUGO | S_IWUSR, samsung->debug.root, 13215b80fc40SCorentin Chary &samsung->debug.data.d0); 13225b80fc40SCorentin Chary if (!dent) 13235b80fc40SCorentin Chary goto error_debugfs; 13245b80fc40SCorentin Chary 13255b80fc40SCorentin Chary dent = debugfs_create_u32("d1", S_IRUGO | S_IWUSR, samsung->debug.root, 13265b80fc40SCorentin Chary &samsung->debug.data.d1); 13275b80fc40SCorentin Chary if (!dent) 13285b80fc40SCorentin Chary goto error_debugfs; 13295b80fc40SCorentin Chary 13305b80fc40SCorentin Chary dent = debugfs_create_u16("d2", S_IRUGO | S_IWUSR, samsung->debug.root, 13315b80fc40SCorentin Chary &samsung->debug.data.d2); 13325b80fc40SCorentin Chary if (!dent) 13335b80fc40SCorentin Chary goto error_debugfs; 13345b80fc40SCorentin Chary 13355b80fc40SCorentin Chary dent = debugfs_create_u8("d3", S_IRUGO | S_IWUSR, samsung->debug.root, 13365b80fc40SCorentin Chary &samsung->debug.data.d3); 13375b80fc40SCorentin Chary if (!dent) 13385b80fc40SCorentin Chary goto error_debugfs; 13395b80fc40SCorentin Chary 13405b80fc40SCorentin Chary dent = debugfs_create_blob("data", S_IRUGO | S_IWUSR, 13415b80fc40SCorentin Chary samsung->debug.root, 13425b80fc40SCorentin Chary &samsung->debug.data_wrapper); 13435b80fc40SCorentin Chary if (!dent) 13445b80fc40SCorentin Chary goto error_debugfs; 13455b80fc40SCorentin Chary 13465b80fc40SCorentin Chary dent = debugfs_create_blob("f0000_segment", S_IRUSR | S_IWUSR, 13475b80fc40SCorentin Chary samsung->debug.root, 13485b80fc40SCorentin Chary &samsung->debug.f0000_wrapper); 13495b80fc40SCorentin Chary if (!dent) 13505b80fc40SCorentin Chary goto error_debugfs; 13515b80fc40SCorentin Chary 13525b80fc40SCorentin Chary dent = debugfs_create_file("call", S_IFREG | S_IRUGO, 13535b80fc40SCorentin Chary samsung->debug.root, samsung, 13545b80fc40SCorentin Chary &samsung_laptop_call_io_ops); 13555b80fc40SCorentin Chary if (!dent) 13565b80fc40SCorentin Chary goto error_debugfs; 13575b80fc40SCorentin Chary 13586f6ae06eSCorentin Chary dent = debugfs_create_blob("sdiag", S_IRUGO | S_IWUSR, 13596f6ae06eSCorentin Chary samsung->debug.root, 13606f6ae06eSCorentin Chary &samsung->debug.sdiag_wrapper); 13616f6ae06eSCorentin Chary if (!dent) 13626f6ae06eSCorentin Chary goto error_debugfs; 13636f6ae06eSCorentin Chary 13645b80fc40SCorentin Chary return 0; 13655b80fc40SCorentin Chary 13665b80fc40SCorentin Chary error_debugfs: 13675b80fc40SCorentin Chary samsung_debugfs_exit(samsung); 13685b80fc40SCorentin Chary return -ENOMEM; 13695b80fc40SCorentin Chary } 13705b80fc40SCorentin Chary 13715dea7a20SCorentin Chary static void samsung_sabi_exit(struct samsung_laptop *samsung) 13725dea7a20SCorentin Chary { 13735dea7a20SCorentin Chary const struct sabi_config *config = samsung->config; 13745dea7a20SCorentin Chary 13755dea7a20SCorentin Chary /* Turn off "Linux" mode in the BIOS */ 13765dea7a20SCorentin Chary if (config && config->commands.set_linux != 0xff) 13777e960711SCorentin Chary sabi_set_commandb(samsung, config->commands.set_linux, 0x80); 13785dea7a20SCorentin Chary 13795dea7a20SCorentin Chary if (samsung->sabi_iface) { 13805dea7a20SCorentin Chary iounmap(samsung->sabi_iface); 13815dea7a20SCorentin Chary samsung->sabi_iface = NULL; 13825dea7a20SCorentin Chary } 13835dea7a20SCorentin Chary if (samsung->f0000_segment) { 13845dea7a20SCorentin Chary iounmap(samsung->f0000_segment); 13855dea7a20SCorentin Chary samsung->f0000_segment = NULL; 13865dea7a20SCorentin Chary } 13875dea7a20SCorentin Chary 13885dea7a20SCorentin Chary samsung->config = NULL; 13895dea7a20SCorentin Chary } 13905dea7a20SCorentin Chary 139149dd7730SCorentin Chary static __init void samsung_sabi_infos(struct samsung_laptop *samsung, int loca, 139249dd7730SCorentin Chary unsigned int ifaceP) 13935dea7a20SCorentin Chary { 13945dea7a20SCorentin Chary const struct sabi_config *config = samsung->config; 13955dea7a20SCorentin Chary 13965dea7a20SCorentin Chary printk(KERN_DEBUG "This computer supports SABI==%x\n", 13975dea7a20SCorentin Chary loca + 0xf0000 - 6); 139849dd7730SCorentin Chary 13995dea7a20SCorentin Chary printk(KERN_DEBUG "SABI header:\n"); 14005dea7a20SCorentin Chary printk(KERN_DEBUG " SMI Port Number = 0x%04x\n", 14015dea7a20SCorentin Chary readw(samsung->sabi + config->header_offsets.port)); 14025dea7a20SCorentin Chary printk(KERN_DEBUG " SMI Interface Function = 0x%02x\n", 14035dea7a20SCorentin Chary readb(samsung->sabi + config->header_offsets.iface_func)); 14045dea7a20SCorentin Chary printk(KERN_DEBUG " SMI enable memory buffer = 0x%02x\n", 14055dea7a20SCorentin Chary readb(samsung->sabi + config->header_offsets.en_mem)); 14065dea7a20SCorentin Chary printk(KERN_DEBUG " SMI restore memory buffer = 0x%02x\n", 14075dea7a20SCorentin Chary readb(samsung->sabi + config->header_offsets.re_mem)); 14085dea7a20SCorentin Chary printk(KERN_DEBUG " SABI data offset = 0x%04x\n", 14095dea7a20SCorentin Chary readw(samsung->sabi + config->header_offsets.data_offset)); 14105dea7a20SCorentin Chary printk(KERN_DEBUG " SABI data segment = 0x%04x\n", 14115dea7a20SCorentin Chary readw(samsung->sabi + config->header_offsets.data_segment)); 14125dea7a20SCorentin Chary 141349dd7730SCorentin Chary printk(KERN_DEBUG " SABI pointer = 0x%08x\n", ifaceP); 14145dea7a20SCorentin Chary } 14155dea7a20SCorentin Chary 14166f6ae06eSCorentin Chary static void __init samsung_sabi_diag(struct samsung_laptop *samsung) 14176f6ae06eSCorentin Chary { 14186f6ae06eSCorentin Chary int loca = find_signature(samsung->f0000_segment, "SDiaG@"); 14196f6ae06eSCorentin Chary int i; 14206f6ae06eSCorentin Chary 14216f6ae06eSCorentin Chary if (loca == 0xffff) 14226f6ae06eSCorentin Chary return ; 14236f6ae06eSCorentin Chary 14246f6ae06eSCorentin Chary /* Example: 14256f6ae06eSCorentin Chary * Ident: @SDiaG@686XX-N90X3A/966-SEC-07HL-S90X3A 14266f6ae06eSCorentin Chary * 14276f6ae06eSCorentin Chary * Product name: 90X3A 14286f6ae06eSCorentin Chary * BIOS Version: 07HL 14296f6ae06eSCorentin Chary */ 14306f6ae06eSCorentin Chary loca += 1; 14316f6ae06eSCorentin Chary for (i = 0; loca < 0xffff && i < sizeof(samsung->sdiag) - 1; loca++) { 14326f6ae06eSCorentin Chary char temp = readb(samsung->f0000_segment + loca); 14336f6ae06eSCorentin Chary 14346f6ae06eSCorentin Chary if (isalnum(temp) || temp == '/' || temp == '-') 14356f6ae06eSCorentin Chary samsung->sdiag[i++] = temp; 14366f6ae06eSCorentin Chary else 14376f6ae06eSCorentin Chary break ; 14386f6ae06eSCorentin Chary } 14396f6ae06eSCorentin Chary 14406f6ae06eSCorentin Chary if (debug && samsung->sdiag[0]) 14416f6ae06eSCorentin Chary pr_info("sdiag: %s", samsung->sdiag); 14426f6ae06eSCorentin Chary } 14436f6ae06eSCorentin Chary 14445dea7a20SCorentin Chary static int __init samsung_sabi_init(struct samsung_laptop *samsung) 14455dea7a20SCorentin Chary { 14465dea7a20SCorentin Chary const struct sabi_config *config = NULL; 14475dea7a20SCorentin Chary const struct sabi_commands *commands; 14485dea7a20SCorentin Chary unsigned int ifaceP; 14495dea7a20SCorentin Chary int ret = 0; 14505dea7a20SCorentin Chary int i; 14515dea7a20SCorentin Chary int loca; 14525dea7a20SCorentin Chary 14535dea7a20SCorentin Chary samsung->f0000_segment = ioremap_nocache(0xf0000, 0xffff); 14545dea7a20SCorentin Chary if (!samsung->f0000_segment) { 14553be324a9SCorentin Chary if (debug || force) 14565dea7a20SCorentin Chary pr_err("Can't map the segment at 0xf0000\n"); 14575dea7a20SCorentin Chary ret = -EINVAL; 14585dea7a20SCorentin Chary goto exit; 14595dea7a20SCorentin Chary } 14605dea7a20SCorentin Chary 14616f6ae06eSCorentin Chary samsung_sabi_diag(samsung); 14626f6ae06eSCorentin Chary 14635dea7a20SCorentin Chary /* Try to find one of the signatures in memory to find the header */ 14644e7f09adSLad, Prabhakar for (i = 0; sabi_configs[i].test_string != NULL; ++i) { 14655dea7a20SCorentin Chary samsung->config = &sabi_configs[i]; 14665dea7a20SCorentin Chary loca = find_signature(samsung->f0000_segment, 14675dea7a20SCorentin Chary samsung->config->test_string); 14685dea7a20SCorentin Chary if (loca != 0xffff) 14695dea7a20SCorentin Chary break; 14705dea7a20SCorentin Chary } 14715dea7a20SCorentin Chary 14725dea7a20SCorentin Chary if (loca == 0xffff) { 14733be324a9SCorentin Chary if (debug || force) 14745dea7a20SCorentin Chary pr_err("This computer does not support SABI\n"); 14755dea7a20SCorentin Chary ret = -ENODEV; 14765dea7a20SCorentin Chary goto exit; 14775dea7a20SCorentin Chary } 14785dea7a20SCorentin Chary 14795dea7a20SCorentin Chary config = samsung->config; 14805dea7a20SCorentin Chary commands = &config->commands; 14815dea7a20SCorentin Chary 14825dea7a20SCorentin Chary /* point to the SMI port Number */ 14835dea7a20SCorentin Chary loca += 1; 14845dea7a20SCorentin Chary samsung->sabi = (samsung->f0000_segment + loca); 14855dea7a20SCorentin Chary 14865dea7a20SCorentin Chary /* Get a pointer to the SABI Interface */ 14875dea7a20SCorentin Chary ifaceP = (readw(samsung->sabi + config->header_offsets.data_segment) & 0x0ffff) << 4; 14885dea7a20SCorentin Chary ifaceP += readw(samsung->sabi + config->header_offsets.data_offset) & 0x0ffff; 148949dd7730SCorentin Chary 149049dd7730SCorentin Chary if (debug) 149149dd7730SCorentin Chary samsung_sabi_infos(samsung, loca, ifaceP); 149249dd7730SCorentin Chary 14935dea7a20SCorentin Chary samsung->sabi_iface = ioremap_nocache(ifaceP, 16); 14945dea7a20SCorentin Chary if (!samsung->sabi_iface) { 14955dea7a20SCorentin Chary pr_err("Can't remap %x\n", ifaceP); 14965dea7a20SCorentin Chary ret = -EINVAL; 14975dea7a20SCorentin Chary goto exit; 14985dea7a20SCorentin Chary } 14995dea7a20SCorentin Chary 15005dea7a20SCorentin Chary /* Turn on "Linux" mode in the BIOS */ 15015dea7a20SCorentin Chary if (commands->set_linux != 0xff) { 15027e960711SCorentin Chary int retval = sabi_set_commandb(samsung, 15035dea7a20SCorentin Chary commands->set_linux, 0x81); 15045dea7a20SCorentin Chary if (retval) { 15055dea7a20SCorentin Chary pr_warn("Linux mode was not set!\n"); 15065dea7a20SCorentin Chary ret = -ENODEV; 15075dea7a20SCorentin Chary goto exit; 15085dea7a20SCorentin Chary } 15095dea7a20SCorentin Chary } 15105dea7a20SCorentin Chary 15115dea7a20SCorentin Chary /* Check for stepping quirk */ 1512f34cd9caSCorentin Chary if (samsung->handle_backlight) 15135dea7a20SCorentin Chary check_for_stepping_quirk(samsung); 15145dea7a20SCorentin Chary 15152e777187SCorentin Chary pr_info("detected SABI interface: %s\n", 15162e777187SCorentin Chary samsung->config->test_string); 15172e777187SCorentin Chary 15185dea7a20SCorentin Chary exit: 15195dea7a20SCorentin Chary if (ret) 15205dea7a20SCorentin Chary samsung_sabi_exit(samsung); 15215dea7a20SCorentin Chary 15225dea7a20SCorentin Chary return ret; 15235dea7a20SCorentin Chary } 15245dea7a20SCorentin Chary 15255dea7a20SCorentin Chary static void samsung_platform_exit(struct samsung_laptop *samsung) 15265dea7a20SCorentin Chary { 15275dea7a20SCorentin Chary if (samsung->platform_device) { 15285dea7a20SCorentin Chary platform_device_unregister(samsung->platform_device); 15295dea7a20SCorentin Chary samsung->platform_device = NULL; 15305dea7a20SCorentin Chary } 15315dea7a20SCorentin Chary } 15325dea7a20SCorentin Chary 15330ca849eaSScott Thrasher static int samsung_pm_notification(struct notifier_block *nb, 15340ca849eaSScott Thrasher unsigned long val, void *ptr) 15350ca849eaSScott Thrasher { 15360ca849eaSScott Thrasher struct samsung_laptop *samsung; 15370ca849eaSScott Thrasher 15380ca849eaSScott Thrasher samsung = container_of(nb, struct samsung_laptop, pm_nb); 15390ca849eaSScott Thrasher if (val == PM_POST_HIBERNATION && 15400ca849eaSScott Thrasher samsung->quirks->enable_kbd_backlight) 15410ca849eaSScott Thrasher kbd_backlight_enable(samsung); 15420ca849eaSScott Thrasher 1543b0dcaf4fSJulijonas Kikutis if (val == PM_POST_HIBERNATION && samsung->quirks->lid_handling) 1544b0dcaf4fSJulijonas Kikutis write_lid_handling(samsung, 1); 1545b0dcaf4fSJulijonas Kikutis 15460ca849eaSScott Thrasher return 0; 15470ca849eaSScott Thrasher } 15480ca849eaSScott Thrasher 15495dea7a20SCorentin Chary static int __init samsung_platform_init(struct samsung_laptop *samsung) 15505dea7a20SCorentin Chary { 15515dea7a20SCorentin Chary struct platform_device *pdev; 15525dea7a20SCorentin Chary 15535dea7a20SCorentin Chary pdev = platform_device_register_simple("samsung", -1, NULL, 0); 15545dea7a20SCorentin Chary if (IS_ERR(pdev)) 15555dea7a20SCorentin Chary return PTR_ERR(pdev); 15565dea7a20SCorentin Chary 15575dea7a20SCorentin Chary samsung->platform_device = pdev; 15585dea7a20SCorentin Chary platform_set_drvdata(samsung->platform_device, samsung); 15595dea7a20SCorentin Chary return 0; 15605dea7a20SCorentin Chary } 15615dea7a20SCorentin Chary 1562a979e2e2SCorentin Chary static struct samsung_quirks *quirks; 1563a979e2e2SCorentin Chary 1564a979e2e2SCorentin Chary static int __init samsung_dmi_matched(const struct dmi_system_id *d) 1565a979e2e2SCorentin Chary { 1566a979e2e2SCorentin Chary quirks = d->driver_data; 1567a979e2e2SCorentin Chary return 0; 1568a979e2e2SCorentin Chary } 1569a979e2e2SCorentin Chary 15702d70b73aSGreg Kroah-Hartman static struct dmi_system_id __initdata samsung_dmi_table[] = { 15712d70b73aSGreg Kroah-Hartman { 15722d70b73aSGreg Kroah-Hartman .matches = { 15732d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_SYS_VENDOR, 15742d70b73aSGreg Kroah-Hartman "SAMSUNG ELECTRONICS CO., LTD."), 15753be324a9SCorentin Chary DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */ 15762d70b73aSGreg Kroah-Hartman }, 15772d70b73aSGreg Kroah-Hartman }, 15782d70b73aSGreg Kroah-Hartman { 15792d70b73aSGreg Kroah-Hartman .matches = { 15802d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_SYS_VENDOR, 15812d70b73aSGreg Kroah-Hartman "SAMSUNG ELECTRONICS CO., LTD."), 15823be324a9SCorentin Chary DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /* Laptop */ 15832d70b73aSGreg Kroah-Hartman }, 15842d70b73aSGreg Kroah-Hartman }, 15852d70b73aSGreg Kroah-Hartman { 15864e2441c0SJ Witteveen .matches = { 15874e2441c0SJ Witteveen DMI_MATCH(DMI_SYS_VENDOR, 15884e2441c0SJ Witteveen "SAMSUNG ELECTRONICS CO., LTD."), 15893be324a9SCorentin Chary DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */ 15904e2441c0SJ Witteveen }, 15914e2441c0SJ Witteveen }, 15924e2441c0SJ Witteveen { 15932d70b73aSGreg Kroah-Hartman .matches = { 15942d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_SYS_VENDOR, 15952d70b73aSGreg Kroah-Hartman "SAMSUNG ELECTRONICS CO., LTD."), 15963be324a9SCorentin Chary DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */ 15972d70b73aSGreg Kroah-Hartman }, 15987500eeb0STommaso Massimi }, 1599e052067dSCorentin Chary /* DMI ids for laptops with bad Chassis Type */ 1600e052067dSCorentin Chary { 1601e052067dSCorentin Chary .ident = "R40/R41", 1602e052067dSCorentin Chary .matches = { 1603e052067dSCorentin Chary DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1604e052067dSCorentin Chary DMI_MATCH(DMI_PRODUCT_NAME, "R40/R41"), 1605e052067dSCorentin Chary DMI_MATCH(DMI_BOARD_NAME, "R40/R41"), 1606e052067dSCorentin Chary }, 1607e052067dSCorentin Chary }, 1608a979e2e2SCorentin Chary /* Specific DMI ids for laptop with quirks */ 1609a979e2e2SCorentin Chary { 1610a979e2e2SCorentin Chary .callback = samsung_dmi_matched, 1611a979e2e2SCorentin Chary .ident = "N150P", 1612a979e2e2SCorentin Chary .matches = { 1613a979e2e2SCorentin Chary DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1614a979e2e2SCorentin Chary DMI_MATCH(DMI_PRODUCT_NAME, "N150P"), 1615a979e2e2SCorentin Chary DMI_MATCH(DMI_BOARD_NAME, "N150P"), 1616a979e2e2SCorentin Chary }, 16174690555eSHans de Goede .driver_data = &samsung_use_native_backlight, 1618a979e2e2SCorentin Chary }, 1619a979e2e2SCorentin Chary { 1620a979e2e2SCorentin Chary .callback = samsung_dmi_matched, 1621a979e2e2SCorentin Chary .ident = "N145P/N250P/N260P", 1622a979e2e2SCorentin Chary .matches = { 1623a979e2e2SCorentin Chary DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1624a979e2e2SCorentin Chary DMI_MATCH(DMI_PRODUCT_NAME, "N145P/N250P/N260P"), 1625a979e2e2SCorentin Chary DMI_MATCH(DMI_BOARD_NAME, "N145P/N250P/N260P"), 1626a979e2e2SCorentin Chary }, 16274690555eSHans de Goede .driver_data = &samsung_use_native_backlight, 1628a979e2e2SCorentin Chary }, 1629a979e2e2SCorentin Chary { 1630a979e2e2SCorentin Chary .callback = samsung_dmi_matched, 1631a979e2e2SCorentin Chary .ident = "N150/N210/N220", 1632a979e2e2SCorentin Chary .matches = { 1633a979e2e2SCorentin Chary DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1634a979e2e2SCorentin Chary DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220"), 1635a979e2e2SCorentin Chary DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220"), 1636a979e2e2SCorentin Chary }, 1637a979e2e2SCorentin Chary .driver_data = &samsung_broken_acpi_video, 1638a979e2e2SCorentin Chary }, 1639a979e2e2SCorentin Chary { 1640a979e2e2SCorentin Chary .callback = samsung_dmi_matched, 1641a979e2e2SCorentin Chary .ident = "NF110/NF210/NF310", 1642a979e2e2SCorentin Chary .matches = { 1643a979e2e2SCorentin Chary DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1644a979e2e2SCorentin Chary DMI_MATCH(DMI_PRODUCT_NAME, "NF110/NF210/NF310"), 1645a979e2e2SCorentin Chary DMI_MATCH(DMI_BOARD_NAME, "NF110/NF210/NF310"), 1646a979e2e2SCorentin Chary }, 1647a979e2e2SCorentin Chary .driver_data = &samsung_broken_acpi_video, 1648a979e2e2SCorentin Chary }, 164909d5677cSCorentin Chary { 165009d5677cSCorentin Chary .callback = samsung_dmi_matched, 165109d5677cSCorentin Chary .ident = "X360", 165209d5677cSCorentin Chary .matches = { 165309d5677cSCorentin Chary DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 165409d5677cSCorentin Chary DMI_MATCH(DMI_PRODUCT_NAME, "X360"), 165509d5677cSCorentin Chary DMI_MATCH(DMI_BOARD_NAME, "X360"), 165609d5677cSCorentin Chary }, 165709d5677cSCorentin Chary .driver_data = &samsung_broken_acpi_video, 165809d5677cSCorentin Chary }, 1659e04c200fSSeth Forshee { 1660e04c200fSSeth Forshee .callback = samsung_dmi_matched, 1661e04c200fSSeth Forshee .ident = "N250P", 1662e04c200fSSeth Forshee .matches = { 1663e04c200fSSeth Forshee DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1664e04c200fSSeth Forshee DMI_MATCH(DMI_PRODUCT_NAME, "N250P"), 1665e04c200fSSeth Forshee DMI_MATCH(DMI_BOARD_NAME, "N250P"), 1666e04c200fSSeth Forshee }, 16674690555eSHans de Goede .driver_data = &samsung_use_native_backlight, 1668e04c200fSSeth Forshee }, 16690ca849eaSScott Thrasher { 16700ca849eaSScott Thrasher .callback = samsung_dmi_matched, 16715a1426c9SHans de Goede .ident = "NC210", 16725a1426c9SHans de Goede .matches = { 16735a1426c9SHans de Goede DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 16745a1426c9SHans de Goede DMI_MATCH(DMI_PRODUCT_NAME, "NC210/NC110"), 16755a1426c9SHans de Goede DMI_MATCH(DMI_BOARD_NAME, "NC210/NC110"), 16765a1426c9SHans de Goede }, 16775a1426c9SHans de Goede .driver_data = &samsung_broken_acpi_video, 16785a1426c9SHans de Goede }, 16795a1426c9SHans de Goede { 16805a1426c9SHans de Goede .callback = samsung_dmi_matched, 16810ca849eaSScott Thrasher .ident = "730U3E/740U3E", 16820ca849eaSScott Thrasher .matches = { 16830ca849eaSScott Thrasher DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 16840ca849eaSScott Thrasher DMI_MATCH(DMI_PRODUCT_NAME, "730U3E/740U3E"), 16850ca849eaSScott Thrasher }, 16860ca849eaSScott Thrasher .driver_data = &samsung_np740u3e, 16870ca849eaSScott Thrasher }, 1688b0dcaf4fSJulijonas Kikutis { 1689b0dcaf4fSJulijonas Kikutis .callback = samsung_dmi_matched, 1690b0dcaf4fSJulijonas Kikutis .ident = "300V3Z/300V4Z/300V5Z", 1691b0dcaf4fSJulijonas Kikutis .matches = { 1692b0dcaf4fSJulijonas Kikutis DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1693b0dcaf4fSJulijonas Kikutis DMI_MATCH(DMI_PRODUCT_NAME, "300V3Z/300V4Z/300V5Z"), 1694b0dcaf4fSJulijonas Kikutis }, 1695b0dcaf4fSJulijonas Kikutis .driver_data = &samsung_lid_handling, 1696b0dcaf4fSJulijonas Kikutis }, 16972d70b73aSGreg Kroah-Hartman { }, 16982d70b73aSGreg Kroah-Hartman }; 16992d70b73aSGreg Kroah-Hartman MODULE_DEVICE_TABLE(dmi, samsung_dmi_table); 17002d70b73aSGreg Kroah-Hartman 17015dea7a20SCorentin Chary static struct platform_device *samsung_platform_device; 17022d70b73aSGreg Kroah-Hartman 17032d70b73aSGreg Kroah-Hartman static int __init samsung_init(void) 17042d70b73aSGreg Kroah-Hartman { 17055dea7a20SCorentin Chary struct samsung_laptop *samsung; 17065dea7a20SCorentin Chary int ret; 17072d70b73aSGreg Kroah-Hartman 1708e0094244SMatt Fleming if (efi_enabled(EFI_BOOT)) 1709e0094244SMatt Fleming return -ENODEV; 1710e0094244SMatt Fleming 1711a979e2e2SCorentin Chary quirks = &samsung_unknown; 17122d70b73aSGreg Kroah-Hartman if (!force && !dmi_check_system(samsung_dmi_table)) 17132d70b73aSGreg Kroah-Hartman return -ENODEV; 17142d70b73aSGreg Kroah-Hartman 1715a6df4894SCorentin Chary samsung = kzalloc(sizeof(*samsung), GFP_KERNEL); 1716a6df4894SCorentin Chary if (!samsung) 1717a6df4894SCorentin Chary return -ENOMEM; 1718a6df4894SCorentin Chary 1719a6df4894SCorentin Chary mutex_init(&samsung->sabi_mutex); 1720f34cd9caSCorentin Chary samsung->handle_backlight = true; 1721a979e2e2SCorentin Chary samsung->quirks = quirks; 1722f34cd9caSCorentin Chary 1723a979e2e2SCorentin Chary 1724a60b2176SCorentin Chary #ifdef CONFIG_ACPI 1725a60b2176SCorentin Chary if (samsung->quirks->broken_acpi_video) 1726a60b2176SCorentin Chary acpi_video_dmi_promote_vendor(); 1727a60b2176SCorentin Chary 1728f34cd9caSCorentin Chary /* Don't handle backlight here if the acpi video already handle it */ 1729a979e2e2SCorentin Chary if (acpi_video_backlight_support()) { 1730f34cd9caSCorentin Chary samsung->handle_backlight = false; 1731a60b2176SCorentin Chary } else if (samsung->quirks->broken_acpi_video) { 1732a60b2176SCorentin Chary pr_info("Disabling ACPI video driver\n"); 1733a60b2176SCorentin Chary acpi_video_unregister(); 1734a979e2e2SCorentin Chary } 17354690555eSHans de Goede 17364690555eSHans de Goede if (samsung->quirks->use_native_backlight) { 17374690555eSHans de Goede pr_info("Using native backlight driver\n"); 17384690555eSHans de Goede /* Tell acpi-video to not handle the backlight */ 17394690555eSHans de Goede acpi_video_dmi_promote_vendor(); 17404690555eSHans de Goede acpi_video_unregister(); 17414690555eSHans de Goede /* And also do not handle it ourselves */ 17424690555eSHans de Goede samsung->handle_backlight = false; 17434690555eSHans de Goede } 1744f34cd9caSCorentin Chary #endif 1745a979e2e2SCorentin Chary 17465dea7a20SCorentin Chary ret = samsung_platform_init(samsung); 17475dea7a20SCorentin Chary if (ret) 17485dea7a20SCorentin Chary goto error_platform; 17492d70b73aSGreg Kroah-Hartman 17505dea7a20SCorentin Chary ret = samsung_sabi_init(samsung); 17515dea7a20SCorentin Chary if (ret) 17525dea7a20SCorentin Chary goto error_sabi; 17532d70b73aSGreg Kroah-Hartman 17543be324a9SCorentin Chary #ifdef CONFIG_ACPI 17553be324a9SCorentin Chary /* Only log that if we are really on a sabi platform */ 1756a60b2176SCorentin Chary if (acpi_video_backlight_support()) 17573be324a9SCorentin Chary pr_info("Backlight controlled by ACPI video driver\n"); 17583be324a9SCorentin Chary #endif 17593be324a9SCorentin Chary 17605dea7a20SCorentin Chary ret = samsung_sysfs_init(samsung); 17615dea7a20SCorentin Chary if (ret) 17625dea7a20SCorentin Chary goto error_sysfs; 17632d70b73aSGreg Kroah-Hartman 17645dea7a20SCorentin Chary ret = samsung_backlight_init(samsung); 17655dea7a20SCorentin Chary if (ret) 17665dea7a20SCorentin Chary goto error_backlight; 1767a6df4894SCorentin Chary 17685dea7a20SCorentin Chary ret = samsung_rfkill_init(samsung); 17695dea7a20SCorentin Chary if (ret) 17705dea7a20SCorentin Chary goto error_rfkill; 17712d70b73aSGreg Kroah-Hartman 1772f674ebf1SCorentin Chary ret = samsung_leds_init(samsung); 1773f674ebf1SCorentin Chary if (ret) 1774f674ebf1SCorentin Chary goto error_leds; 1775f674ebf1SCorentin Chary 1776b0dcaf4fSJulijonas Kikutis ret = samsung_lid_handling_init(samsung); 1777b0dcaf4fSJulijonas Kikutis if (ret) 1778b0dcaf4fSJulijonas Kikutis goto error_lid_handling; 1779b0dcaf4fSJulijonas Kikutis 17805b80fc40SCorentin Chary ret = samsung_debugfs_init(samsung); 17815b80fc40SCorentin Chary if (ret) 17825b80fc40SCorentin Chary goto error_debugfs; 17835b80fc40SCorentin Chary 17840ca849eaSScott Thrasher samsung->pm_nb.notifier_call = samsung_pm_notification; 17850ca849eaSScott Thrasher register_pm_notifier(&samsung->pm_nb); 17860ca849eaSScott Thrasher 17875dea7a20SCorentin Chary samsung_platform_device = samsung->platform_device; 17885dea7a20SCorentin Chary return ret; 17892d70b73aSGreg Kroah-Hartman 17905b80fc40SCorentin Chary error_debugfs: 1791b0dcaf4fSJulijonas Kikutis samsung_lid_handling_exit(samsung); 1792b0dcaf4fSJulijonas Kikutis error_lid_handling: 1793f674ebf1SCorentin Chary samsung_leds_exit(samsung); 1794f674ebf1SCorentin Chary error_leds: 17955b80fc40SCorentin Chary samsung_rfkill_exit(samsung); 17965dea7a20SCorentin Chary error_rfkill: 17975dea7a20SCorentin Chary samsung_backlight_exit(samsung); 17985dea7a20SCorentin Chary error_backlight: 17995dea7a20SCorentin Chary samsung_sysfs_exit(samsung); 18005dea7a20SCorentin Chary error_sysfs: 18015dea7a20SCorentin Chary samsung_sabi_exit(samsung); 18025dea7a20SCorentin Chary error_sabi: 18035dea7a20SCorentin Chary samsung_platform_exit(samsung); 18045dea7a20SCorentin Chary error_platform: 1805a6df4894SCorentin Chary kfree(samsung); 18065dea7a20SCorentin Chary return ret; 18072d70b73aSGreg Kroah-Hartman } 18082d70b73aSGreg Kroah-Hartman 18092d70b73aSGreg Kroah-Hartman static void __exit samsung_exit(void) 18102d70b73aSGreg Kroah-Hartman { 18115dea7a20SCorentin Chary struct samsung_laptop *samsung; 18122d70b73aSGreg Kroah-Hartman 18135dea7a20SCorentin Chary samsung = platform_get_drvdata(samsung_platform_device); 18140ca849eaSScott Thrasher unregister_pm_notifier(&samsung->pm_nb); 1815a6df4894SCorentin Chary 18165b80fc40SCorentin Chary samsung_debugfs_exit(samsung); 1817b0dcaf4fSJulijonas Kikutis samsung_lid_handling_exit(samsung); 1818f674ebf1SCorentin Chary samsung_leds_exit(samsung); 18195dea7a20SCorentin Chary samsung_rfkill_exit(samsung); 18205dea7a20SCorentin Chary samsung_backlight_exit(samsung); 18215dea7a20SCorentin Chary samsung_sysfs_exit(samsung); 18225dea7a20SCorentin Chary samsung_sabi_exit(samsung); 18235dea7a20SCorentin Chary samsung_platform_exit(samsung); 18245dea7a20SCorentin Chary 1825a6df4894SCorentin Chary kfree(samsung); 18265dea7a20SCorentin Chary samsung_platform_device = NULL; 18272d70b73aSGreg Kroah-Hartman } 18282d70b73aSGreg Kroah-Hartman 18292d70b73aSGreg Kroah-Hartman module_init(samsung_init); 18302d70b73aSGreg Kroah-Hartman module_exit(samsung_exit); 18312d70b73aSGreg Kroah-Hartman 18322d70b73aSGreg Kroah-Hartman MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@suse.de>"); 18332d70b73aSGreg Kroah-Hartman MODULE_DESCRIPTION("Samsung Backlight driver"); 18342d70b73aSGreg Kroah-Hartman MODULE_LICENSE("GPL"); 1835