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> 282d70b73aSGreg Kroah-Hartman 292d70b73aSGreg Kroah-Hartman /* 302d70b73aSGreg Kroah-Hartman * This driver is needed because a number of Samsung laptops do not hook 312d70b73aSGreg Kroah-Hartman * their control settings through ACPI. So we have to poke around in the 322d70b73aSGreg Kroah-Hartman * BIOS to do things like brightness values, and "special" key controls. 332d70b73aSGreg Kroah-Hartman */ 342d70b73aSGreg Kroah-Hartman 352d70b73aSGreg Kroah-Hartman /* 362d70b73aSGreg Kroah-Hartman * We have 0 - 8 as valid brightness levels. The specs say that level 0 should 372d70b73aSGreg Kroah-Hartman * be reserved by the BIOS (which really doesn't make much sense), we tell 382d70b73aSGreg Kroah-Hartman * userspace that the value is 0 - 7 and then just tell the hardware 1 - 8 392d70b73aSGreg Kroah-Hartman */ 402d70b73aSGreg Kroah-Hartman #define MAX_BRIGHT 0x07 412d70b73aSGreg Kroah-Hartman 422d70b73aSGreg Kroah-Hartman 432d70b73aSGreg Kroah-Hartman #define SABI_IFACE_MAIN 0x00 442d70b73aSGreg Kroah-Hartman #define SABI_IFACE_SUB 0x02 452d70b73aSGreg Kroah-Hartman #define SABI_IFACE_COMPLETE 0x04 462d70b73aSGreg Kroah-Hartman #define SABI_IFACE_DATA 0x05 472d70b73aSGreg Kroah-Hartman 487e960711SCorentin Chary /* Structure get/set data using sabi */ 497e960711SCorentin Chary struct sabi_data { 507e960711SCorentin Chary union { 517e960711SCorentin Chary struct { 527e960711SCorentin Chary u32 d0; 537e960711SCorentin Chary u32 d1; 547e960711SCorentin Chary u16 d2; 557e960711SCorentin Chary u8 d3; 567e960711SCorentin Chary }; 577e960711SCorentin Chary u8 data[11]; 587e960711SCorentin Chary }; 592d70b73aSGreg Kroah-Hartman }; 602d70b73aSGreg Kroah-Hartman 612d70b73aSGreg Kroah-Hartman struct sabi_header_offsets { 622d70b73aSGreg Kroah-Hartman u8 port; 632d70b73aSGreg Kroah-Hartman u8 re_mem; 642d70b73aSGreg Kroah-Hartman u8 iface_func; 652d70b73aSGreg Kroah-Hartman u8 en_mem; 662d70b73aSGreg Kroah-Hartman u8 data_offset; 672d70b73aSGreg Kroah-Hartman u8 data_segment; 682d70b73aSGreg Kroah-Hartman }; 692d70b73aSGreg Kroah-Hartman 702d70b73aSGreg Kroah-Hartman struct sabi_commands { 712d70b73aSGreg Kroah-Hartman /* 722d70b73aSGreg Kroah-Hartman * Brightness is 0 - 8, as described above. 732d70b73aSGreg Kroah-Hartman * Value 0 is for the BIOS to use 742d70b73aSGreg Kroah-Hartman */ 757e960711SCorentin Chary u16 get_brightness; 767e960711SCorentin Chary u16 set_brightness; 772d70b73aSGreg Kroah-Hartman 782d70b73aSGreg Kroah-Hartman /* 792d70b73aSGreg Kroah-Hartman * first byte: 802d70b73aSGreg Kroah-Hartman * 0x00 - wireless is off 812d70b73aSGreg Kroah-Hartman * 0x01 - wireless is on 822d70b73aSGreg Kroah-Hartman * second byte: 832d70b73aSGreg Kroah-Hartman * 0x02 - 3G is off 842d70b73aSGreg Kroah-Hartman * 0x03 - 3G is on 852d70b73aSGreg Kroah-Hartman * TODO, verify 3G is correct, that doesn't seem right... 862d70b73aSGreg Kroah-Hartman */ 877e960711SCorentin Chary u16 get_wireless_button; 887e960711SCorentin Chary u16 set_wireless_button; 892d70b73aSGreg Kroah-Hartman 902d70b73aSGreg Kroah-Hartman /* 0 is off, 1 is on */ 917e960711SCorentin Chary u16 get_backlight; 927e960711SCorentin Chary u16 set_backlight; 932d70b73aSGreg Kroah-Hartman 942d70b73aSGreg Kroah-Hartman /* 952d70b73aSGreg Kroah-Hartman * 0x80 or 0x00 - no action 962d70b73aSGreg Kroah-Hartman * 0x81 - recovery key pressed 972d70b73aSGreg Kroah-Hartman */ 987e960711SCorentin Chary u16 get_recovery_mode; 997e960711SCorentin Chary u16 set_recovery_mode; 1002d70b73aSGreg Kroah-Hartman 1012d70b73aSGreg Kroah-Hartman /* 1022d70b73aSGreg Kroah-Hartman * on seclinux: 0 is low, 1 is high, 1032d70b73aSGreg Kroah-Hartman * on swsmi: 0 is normal, 1 is silent, 2 is turbo 1042d70b73aSGreg Kroah-Hartman */ 1057e960711SCorentin Chary u16 get_performance_level; 1067e960711SCorentin Chary u16 set_performance_level; 1072d70b73aSGreg Kroah-Hartman 108cb5b5c91SCorentin Chary /* 0x80 is off, 0x81 is on */ 109cb5b5c91SCorentin Chary u16 get_battery_life_extender; 110cb5b5c91SCorentin Chary u16 set_battery_life_extender; 111cb5b5c91SCorentin Chary 1123a75d378SCorentin Chary /* 0x80 is off, 0x81 is on */ 1133a75d378SCorentin Chary u16 get_usb_charge; 1143a75d378SCorentin Chary u16 set_usb_charge; 1153a75d378SCorentin Chary 116f674ebf1SCorentin Chary /* 0x81 to read, (0x82 | level << 8) to set, 0xaabb to enable */ 117f674ebf1SCorentin Chary u16 kbd_backlight; 118f674ebf1SCorentin Chary 1192d70b73aSGreg Kroah-Hartman /* 1202d70b73aSGreg Kroah-Hartman * Tell the BIOS that Linux is running on this machine. 1212d70b73aSGreg Kroah-Hartman * 81 is on, 80 is off 1222d70b73aSGreg Kroah-Hartman */ 1237e960711SCorentin Chary u16 set_linux; 1242d70b73aSGreg Kroah-Hartman }; 1252d70b73aSGreg Kroah-Hartman 1262d70b73aSGreg Kroah-Hartman struct sabi_performance_level { 1272d70b73aSGreg Kroah-Hartman const char *name; 1287e960711SCorentin Chary u16 value; 1292d70b73aSGreg Kroah-Hartman }; 1302d70b73aSGreg Kroah-Hartman 1312d70b73aSGreg Kroah-Hartman struct sabi_config { 1322d70b73aSGreg Kroah-Hartman const char *test_string; 1332d70b73aSGreg Kroah-Hartman u16 main_function; 1342d70b73aSGreg Kroah-Hartman const struct sabi_header_offsets header_offsets; 1352d70b73aSGreg Kroah-Hartman const struct sabi_commands commands; 1362d70b73aSGreg Kroah-Hartman const struct sabi_performance_level performance_levels[4]; 1372d70b73aSGreg Kroah-Hartman u8 min_brightness; 1382d70b73aSGreg Kroah-Hartman u8 max_brightness; 1392d70b73aSGreg Kroah-Hartman }; 1402d70b73aSGreg Kroah-Hartman 1412d70b73aSGreg Kroah-Hartman static const struct sabi_config sabi_configs[] = { 1422d70b73aSGreg Kroah-Hartman { 1432d70b73aSGreg Kroah-Hartman .test_string = "SECLINUX", 1442d70b73aSGreg Kroah-Hartman 1452d70b73aSGreg Kroah-Hartman .main_function = 0x4c49, 1462d70b73aSGreg Kroah-Hartman 1472d70b73aSGreg Kroah-Hartman .header_offsets = { 1482d70b73aSGreg Kroah-Hartman .port = 0x00, 1492d70b73aSGreg Kroah-Hartman .re_mem = 0x02, 1502d70b73aSGreg Kroah-Hartman .iface_func = 0x03, 1512d70b73aSGreg Kroah-Hartman .en_mem = 0x04, 1522d70b73aSGreg Kroah-Hartman .data_offset = 0x05, 1532d70b73aSGreg Kroah-Hartman .data_segment = 0x07, 1542d70b73aSGreg Kroah-Hartman }, 1552d70b73aSGreg Kroah-Hartman 1562d70b73aSGreg Kroah-Hartman .commands = { 1572d70b73aSGreg Kroah-Hartman .get_brightness = 0x00, 1582d70b73aSGreg Kroah-Hartman .set_brightness = 0x01, 1592d70b73aSGreg Kroah-Hartman 1602d70b73aSGreg Kroah-Hartman .get_wireless_button = 0x02, 1612d70b73aSGreg Kroah-Hartman .set_wireless_button = 0x03, 1622d70b73aSGreg Kroah-Hartman 1632d70b73aSGreg Kroah-Hartman .get_backlight = 0x04, 1642d70b73aSGreg Kroah-Hartman .set_backlight = 0x05, 1652d70b73aSGreg Kroah-Hartman 1662d70b73aSGreg Kroah-Hartman .get_recovery_mode = 0x06, 1672d70b73aSGreg Kroah-Hartman .set_recovery_mode = 0x07, 1682d70b73aSGreg Kroah-Hartman 1692d70b73aSGreg Kroah-Hartman .get_performance_level = 0x08, 1702d70b73aSGreg Kroah-Hartman .set_performance_level = 0x09, 1712d70b73aSGreg Kroah-Hartman 172cb5b5c91SCorentin Chary .get_battery_life_extender = 0xFFFF, 173cb5b5c91SCorentin Chary .set_battery_life_extender = 0xFFFF, 174cb5b5c91SCorentin Chary 1753a75d378SCorentin Chary .get_usb_charge = 0xFFFF, 1763a75d378SCorentin Chary .set_usb_charge = 0xFFFF, 1773a75d378SCorentin Chary 178f674ebf1SCorentin Chary .kbd_backlight = 0xFFFF, 179f674ebf1SCorentin Chary 1802d70b73aSGreg Kroah-Hartman .set_linux = 0x0a, 1812d70b73aSGreg Kroah-Hartman }, 1822d70b73aSGreg Kroah-Hartman 1832d70b73aSGreg Kroah-Hartman .performance_levels = { 1842d70b73aSGreg Kroah-Hartman { 1852d70b73aSGreg Kroah-Hartman .name = "silent", 1862d70b73aSGreg Kroah-Hartman .value = 0, 1872d70b73aSGreg Kroah-Hartman }, 1882d70b73aSGreg Kroah-Hartman { 1892d70b73aSGreg Kroah-Hartman .name = "normal", 1902d70b73aSGreg Kroah-Hartman .value = 1, 1912d70b73aSGreg Kroah-Hartman }, 1922d70b73aSGreg Kroah-Hartman { }, 1932d70b73aSGreg Kroah-Hartman }, 1942d70b73aSGreg Kroah-Hartman .min_brightness = 1, 1952d70b73aSGreg Kroah-Hartman .max_brightness = 8, 1962d70b73aSGreg Kroah-Hartman }, 1972d70b73aSGreg Kroah-Hartman { 1982d70b73aSGreg Kroah-Hartman .test_string = "SwSmi@", 1992d70b73aSGreg Kroah-Hartman 2002d70b73aSGreg Kroah-Hartman .main_function = 0x5843, 2012d70b73aSGreg Kroah-Hartman 2022d70b73aSGreg Kroah-Hartman .header_offsets = { 2032d70b73aSGreg Kroah-Hartman .port = 0x00, 2042d70b73aSGreg Kroah-Hartman .re_mem = 0x04, 2052d70b73aSGreg Kroah-Hartman .iface_func = 0x02, 2062d70b73aSGreg Kroah-Hartman .en_mem = 0x03, 2072d70b73aSGreg Kroah-Hartman .data_offset = 0x05, 2082d70b73aSGreg Kroah-Hartman .data_segment = 0x07, 2092d70b73aSGreg Kroah-Hartman }, 2102d70b73aSGreg Kroah-Hartman 2112d70b73aSGreg Kroah-Hartman .commands = { 2122d70b73aSGreg Kroah-Hartman .get_brightness = 0x10, 2132d70b73aSGreg Kroah-Hartman .set_brightness = 0x11, 2142d70b73aSGreg Kroah-Hartman 2152d70b73aSGreg Kroah-Hartman .get_wireless_button = 0x12, 2162d70b73aSGreg Kroah-Hartman .set_wireless_button = 0x13, 2172d70b73aSGreg Kroah-Hartman 2182d70b73aSGreg Kroah-Hartman .get_backlight = 0x2d, 2192d70b73aSGreg Kroah-Hartman .set_backlight = 0x2e, 2202d70b73aSGreg Kroah-Hartman 2212d70b73aSGreg Kroah-Hartman .get_recovery_mode = 0xff, 2222d70b73aSGreg Kroah-Hartman .set_recovery_mode = 0xff, 2232d70b73aSGreg Kroah-Hartman 2242d70b73aSGreg Kroah-Hartman .get_performance_level = 0x31, 2252d70b73aSGreg Kroah-Hartman .set_performance_level = 0x32, 2262d70b73aSGreg Kroah-Hartman 227cb5b5c91SCorentin Chary .get_battery_life_extender = 0x65, 228cb5b5c91SCorentin Chary .set_battery_life_extender = 0x66, 229cb5b5c91SCorentin Chary 2303a75d378SCorentin Chary .get_usb_charge = 0x67, 2313a75d378SCorentin Chary .set_usb_charge = 0x68, 2323a75d378SCorentin Chary 233f674ebf1SCorentin Chary .kbd_backlight = 0x78, 234f674ebf1SCorentin Chary 2352d70b73aSGreg Kroah-Hartman .set_linux = 0xff, 2362d70b73aSGreg Kroah-Hartman }, 2372d70b73aSGreg Kroah-Hartman 2382d70b73aSGreg Kroah-Hartman .performance_levels = { 2392d70b73aSGreg Kroah-Hartman { 2402d70b73aSGreg Kroah-Hartman .name = "normal", 2412d70b73aSGreg Kroah-Hartman .value = 0, 2422d70b73aSGreg Kroah-Hartman }, 2432d70b73aSGreg Kroah-Hartman { 2442d70b73aSGreg Kroah-Hartman .name = "silent", 2452d70b73aSGreg Kroah-Hartman .value = 1, 2462d70b73aSGreg Kroah-Hartman }, 2472d70b73aSGreg Kroah-Hartman { 2482d70b73aSGreg Kroah-Hartman .name = "overclock", 2492d70b73aSGreg Kroah-Hartman .value = 2, 2502d70b73aSGreg Kroah-Hartman }, 2512d70b73aSGreg Kroah-Hartman { }, 2522d70b73aSGreg Kroah-Hartman }, 2532d70b73aSGreg Kroah-Hartman .min_brightness = 0, 2542d70b73aSGreg Kroah-Hartman .max_brightness = 8, 2552d70b73aSGreg Kroah-Hartman }, 2562d70b73aSGreg Kroah-Hartman { }, 2572d70b73aSGreg Kroah-Hartman }; 2582d70b73aSGreg Kroah-Hartman 2595b80fc40SCorentin Chary /* 2605b80fc40SCorentin Chary * samsung-laptop/ - debugfs root directory 2615b80fc40SCorentin Chary * f0000_segment - dump f0000 segment 2625b80fc40SCorentin Chary * command - current command 2635b80fc40SCorentin Chary * data - current data 2645b80fc40SCorentin Chary * d0, d1, d2, d3 - data fields 2655b80fc40SCorentin Chary * call - call SABI using command and data 2665b80fc40SCorentin Chary * 2675b80fc40SCorentin Chary * This allow to call arbitrary sabi commands wihout 2685b80fc40SCorentin Chary * modifying the driver at all. 2695b80fc40SCorentin Chary * For example, setting the keyboard backlight brightness to 5 2705b80fc40SCorentin Chary * 2715b80fc40SCorentin Chary * echo 0x78 > command 2725b80fc40SCorentin Chary * echo 0x0582 > d0 2735b80fc40SCorentin Chary * echo 0 > d1 2745b80fc40SCorentin Chary * echo 0 > d2 2755b80fc40SCorentin Chary * echo 0 > d3 2765b80fc40SCorentin Chary * cat call 2775b80fc40SCorentin Chary */ 2785b80fc40SCorentin Chary 2795b80fc40SCorentin Chary struct samsung_laptop_debug { 2805b80fc40SCorentin Chary struct dentry *root; 2815b80fc40SCorentin Chary struct sabi_data data; 2825b80fc40SCorentin Chary u16 command; 2835b80fc40SCorentin Chary 2845b80fc40SCorentin Chary struct debugfs_blob_wrapper f0000_wrapper; 2855b80fc40SCorentin Chary struct debugfs_blob_wrapper data_wrapper; 2865b80fc40SCorentin Chary }; 2875b80fc40SCorentin Chary 288a6df4894SCorentin Chary struct samsung_laptop { 289a6df4894SCorentin Chary const struct sabi_config *config; 2902d70b73aSGreg Kroah-Hartman 291a6df4894SCorentin Chary void __iomem *sabi; 292a6df4894SCorentin Chary void __iomem *sabi_iface; 293a6df4894SCorentin Chary void __iomem *f0000_segment; 294a6df4894SCorentin Chary 295a6df4894SCorentin Chary struct mutex sabi_mutex; 296a6df4894SCorentin Chary 2975dea7a20SCorentin Chary struct platform_device *platform_device; 298a6df4894SCorentin Chary struct backlight_device *backlight_device; 299a6df4894SCorentin Chary struct rfkill *rfk; 300a6df4894SCorentin Chary 301f674ebf1SCorentin Chary struct led_classdev kbd_led; 302f674ebf1SCorentin Chary int kbd_led_wk; 303f674ebf1SCorentin Chary struct workqueue_struct *led_workqueue; 304f674ebf1SCorentin Chary struct work_struct kbd_led_work; 305f674ebf1SCorentin Chary 3065b80fc40SCorentin Chary struct samsung_laptop_debug debug; 3075b80fc40SCorentin Chary 308f34cd9caSCorentin Chary bool handle_backlight; 309a6df4894SCorentin Chary bool has_stepping_quirk; 310a6df4894SCorentin Chary }; 311a6df4894SCorentin Chary 3125dea7a20SCorentin Chary 3132d70b73aSGreg Kroah-Hartman 31490ab5ee9SRusty Russell static bool force; 3152d70b73aSGreg Kroah-Hartman module_param(force, bool, 0); 3162d70b73aSGreg Kroah-Hartman MODULE_PARM_DESC(force, 3172d70b73aSGreg Kroah-Hartman "Disable the DMI check and forces the driver to be loaded"); 3182d70b73aSGreg Kroah-Hartman 31990ab5ee9SRusty Russell static bool debug; 3202d70b73aSGreg Kroah-Hartman module_param(debug, bool, S_IRUGO | S_IWUSR); 3212d70b73aSGreg Kroah-Hartman MODULE_PARM_DESC(debug, "Debug enabled or not"); 3222d70b73aSGreg Kroah-Hartman 3237e960711SCorentin Chary static int sabi_command(struct samsung_laptop *samsung, u16 command, 3247e960711SCorentin Chary struct sabi_data *in, 3257e960711SCorentin Chary struct sabi_data *out) 3262d70b73aSGreg Kroah-Hartman { 327a6df4894SCorentin Chary const struct sabi_config *config = samsung->config; 3287e960711SCorentin Chary int ret = 0; 329a6df4894SCorentin Chary u16 port = readw(samsung->sabi + config->header_offsets.port); 3302d70b73aSGreg Kroah-Hartman u8 complete, iface_data; 3312d70b73aSGreg Kroah-Hartman 332a6df4894SCorentin Chary mutex_lock(&samsung->sabi_mutex); 3332d70b73aSGreg Kroah-Hartman 3347e960711SCorentin Chary if (debug) { 3357e960711SCorentin Chary if (in) 3367e960711SCorentin Chary pr_info("SABI 0x%04x {0x%08x, 0x%08x, 0x%04x, 0x%02x}", 3377e960711SCorentin Chary command, in->d0, in->d1, in->d2, in->d3); 3387e960711SCorentin Chary else 3397e960711SCorentin Chary pr_info("SABI 0x%04x", command); 3407e960711SCorentin Chary } 3417e960711SCorentin Chary 3422d70b73aSGreg Kroah-Hartman /* enable memory to be able to write to it */ 343a6df4894SCorentin Chary outb(readb(samsung->sabi + config->header_offsets.en_mem), port); 3442d70b73aSGreg Kroah-Hartman 3452d70b73aSGreg Kroah-Hartman /* write out the command */ 346a6df4894SCorentin Chary writew(config->main_function, samsung->sabi_iface + SABI_IFACE_MAIN); 347a6df4894SCorentin Chary writew(command, samsung->sabi_iface + SABI_IFACE_SUB); 348a6df4894SCorentin Chary writeb(0, samsung->sabi_iface + SABI_IFACE_COMPLETE); 3497e960711SCorentin Chary if (in) { 3507e960711SCorentin Chary writel(in->d0, samsung->sabi_iface + SABI_IFACE_DATA); 3517e960711SCorentin Chary writel(in->d1, samsung->sabi_iface + SABI_IFACE_DATA + 4); 3527e960711SCorentin Chary writew(in->d2, samsung->sabi_iface + SABI_IFACE_DATA + 8); 3537e960711SCorentin Chary writeb(in->d3, samsung->sabi_iface + SABI_IFACE_DATA + 10); 3547e960711SCorentin Chary } 355a6df4894SCorentin Chary outb(readb(samsung->sabi + config->header_offsets.iface_func), port); 3562d70b73aSGreg Kroah-Hartman 3572d70b73aSGreg Kroah-Hartman /* write protect memory to make it safe */ 358a6df4894SCorentin Chary outb(readb(samsung->sabi + config->header_offsets.re_mem), port); 3592d70b73aSGreg Kroah-Hartman 3602d70b73aSGreg Kroah-Hartman /* see if the command actually succeeded */ 361a6df4894SCorentin Chary complete = readb(samsung->sabi_iface + SABI_IFACE_COMPLETE); 362a6df4894SCorentin Chary iface_data = readb(samsung->sabi_iface + SABI_IFACE_DATA); 3632d70b73aSGreg Kroah-Hartman if (complete != 0xaa || iface_data == 0xff) { 3647e960711SCorentin Chary pr_warn("SABI command 0x%04x failed with" 3657e960711SCorentin Chary " completion flag 0x%02x and interface data 0x%02x", 3662d70b73aSGreg Kroah-Hartman command, complete, iface_data); 3677e960711SCorentin Chary ret = -EINVAL; 3682d70b73aSGreg Kroah-Hartman goto exit; 3692d70b73aSGreg Kroah-Hartman } 3707e960711SCorentin Chary 3717e960711SCorentin Chary if (out) { 3727e960711SCorentin Chary out->d0 = readl(samsung->sabi_iface + SABI_IFACE_DATA); 3737e960711SCorentin Chary out->d1 = readl(samsung->sabi_iface + SABI_IFACE_DATA + 4); 3747e960711SCorentin Chary out->d2 = readw(samsung->sabi_iface + SABI_IFACE_DATA + 2); 3757e960711SCorentin Chary out->d3 = readb(samsung->sabi_iface + SABI_IFACE_DATA + 1); 3767e960711SCorentin Chary } 3777e960711SCorentin Chary 3787e960711SCorentin Chary if (debug && out) { 3797e960711SCorentin Chary pr_info("SABI {0x%08x, 0x%08x, 0x%04x, 0x%02x}", 3807e960711SCorentin Chary out->d0, out->d1, out->d2, out->d3); 3817e960711SCorentin Chary } 3822d70b73aSGreg Kroah-Hartman 3832d70b73aSGreg Kroah-Hartman exit: 384a6df4894SCorentin Chary mutex_unlock(&samsung->sabi_mutex); 3857e960711SCorentin Chary return ret; 3862d70b73aSGreg Kroah-Hartman } 3872d70b73aSGreg Kroah-Hartman 3887e960711SCorentin Chary /* simple wrappers usable with most commands */ 3897e960711SCorentin Chary static int sabi_set_commandb(struct samsung_laptop *samsung, 3907e960711SCorentin Chary u16 command, u8 data) 3912d70b73aSGreg Kroah-Hartman { 3927e960711SCorentin Chary struct sabi_data in = { .d0 = 0, .d1 = 0, .d2 = 0, .d3 = 0 }; 3932d70b73aSGreg Kroah-Hartman 3947e960711SCorentin Chary in.data[0] = data; 3957e960711SCorentin Chary return sabi_command(samsung, command, &in, NULL); 3962d70b73aSGreg Kroah-Hartman } 3972d70b73aSGreg Kroah-Hartman 3985dea7a20SCorentin Chary static int read_brightness(struct samsung_laptop *samsung) 3992d70b73aSGreg Kroah-Hartman { 400a6df4894SCorentin Chary const struct sabi_config *config = samsung->config; 401a6df4894SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 4027e960711SCorentin Chary struct sabi_data sretval; 4032d70b73aSGreg Kroah-Hartman int user_brightness = 0; 4042d70b73aSGreg Kroah-Hartman int retval; 4052d70b73aSGreg Kroah-Hartman 4067e960711SCorentin Chary retval = sabi_command(samsung, commands->get_brightness, 4077e960711SCorentin Chary NULL, &sretval); 4087e960711SCorentin Chary if (retval) 4097e960711SCorentin Chary return retval; 4107e960711SCorentin Chary 4117e960711SCorentin Chary user_brightness = sretval.data[0]; 412a6df4894SCorentin Chary if (user_brightness > config->min_brightness) 413a6df4894SCorentin Chary user_brightness -= config->min_brightness; 414bee460beSJason Stubbs else 415bee460beSJason Stubbs user_brightness = 0; 4167e960711SCorentin Chary 4172d70b73aSGreg Kroah-Hartman return user_brightness; 4182d70b73aSGreg Kroah-Hartman } 4192d70b73aSGreg Kroah-Hartman 4205dea7a20SCorentin Chary static void set_brightness(struct samsung_laptop *samsung, u8 user_brightness) 4212d70b73aSGreg Kroah-Hartman { 422a6df4894SCorentin Chary const struct sabi_config *config = samsung->config; 423a6df4894SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 424a6df4894SCorentin Chary u8 user_level = user_brightness + config->min_brightness; 4252d70b73aSGreg Kroah-Hartman 426a6df4894SCorentin Chary if (samsung->has_stepping_quirk && user_level != 0) { 427ac080523SJason Stubbs /* 428ac080523SJason Stubbs * short circuit if the specified level is what's already set 429ac080523SJason Stubbs * to prevent the screen from flickering needlessly 430ac080523SJason Stubbs */ 4315dea7a20SCorentin Chary if (user_brightness == read_brightness(samsung)) 432ac080523SJason Stubbs return; 433ac080523SJason Stubbs 4347e960711SCorentin Chary sabi_set_commandb(samsung, commands->set_brightness, 0); 435ac080523SJason Stubbs } 436ac080523SJason Stubbs 4377e960711SCorentin Chary sabi_set_commandb(samsung, commands->set_brightness, user_level); 4382d70b73aSGreg Kroah-Hartman } 4392d70b73aSGreg Kroah-Hartman 4402d70b73aSGreg Kroah-Hartman static int get_brightness(struct backlight_device *bd) 4412d70b73aSGreg Kroah-Hartman { 4425dea7a20SCorentin Chary struct samsung_laptop *samsung = bl_get_data(bd); 4435dea7a20SCorentin Chary 4445dea7a20SCorentin Chary return read_brightness(samsung); 4452d70b73aSGreg Kroah-Hartman } 4462d70b73aSGreg Kroah-Hartman 4475dea7a20SCorentin Chary static void check_for_stepping_quirk(struct samsung_laptop *samsung) 448ac080523SJason Stubbs { 4495dea7a20SCorentin Chary int initial_level; 4505dea7a20SCorentin Chary int check_level; 4515dea7a20SCorentin Chary int orig_level = read_brightness(samsung); 452ac080523SJason Stubbs 453ac080523SJason Stubbs /* 454ac080523SJason Stubbs * Some laptops exhibit the strange behaviour of stepping toward 455ac080523SJason Stubbs * (rather than setting) the brightness except when changing to/from 456ac080523SJason Stubbs * brightness level 0. This behaviour is checked for here and worked 457ac080523SJason Stubbs * around in set_brightness. 458ac080523SJason Stubbs */ 459ac080523SJason Stubbs 460ba05b237SJohn Serock if (orig_level == 0) 4615dea7a20SCorentin Chary set_brightness(samsung, 1); 462ba05b237SJohn Serock 4635dea7a20SCorentin Chary initial_level = read_brightness(samsung); 464ba05b237SJohn Serock 465ac080523SJason Stubbs if (initial_level <= 2) 466ac080523SJason Stubbs check_level = initial_level + 2; 467ac080523SJason Stubbs else 468ac080523SJason Stubbs check_level = initial_level - 2; 469ac080523SJason Stubbs 470a6df4894SCorentin Chary samsung->has_stepping_quirk = false; 4715dea7a20SCorentin Chary set_brightness(samsung, check_level); 472ac080523SJason Stubbs 4735dea7a20SCorentin Chary if (read_brightness(samsung) != check_level) { 474a6df4894SCorentin Chary samsung->has_stepping_quirk = true; 475ac080523SJason Stubbs pr_info("enabled workaround for brightness stepping quirk\n"); 476ac080523SJason Stubbs } 477ac080523SJason Stubbs 4785dea7a20SCorentin Chary set_brightness(samsung, orig_level); 479ac080523SJason Stubbs } 480ac080523SJason Stubbs 4812d70b73aSGreg Kroah-Hartman static int update_status(struct backlight_device *bd) 4822d70b73aSGreg Kroah-Hartman { 4835dea7a20SCorentin Chary struct samsung_laptop *samsung = bl_get_data(bd); 484a6df4894SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 485a6df4894SCorentin Chary 4865dea7a20SCorentin Chary set_brightness(samsung, bd->props.brightness); 4872d70b73aSGreg Kroah-Hartman 4882d70b73aSGreg Kroah-Hartman if (bd->props.power == FB_BLANK_UNBLANK) 4897e960711SCorentin Chary sabi_set_commandb(samsung, commands->set_backlight, 1); 4902d70b73aSGreg Kroah-Hartman else 4917e960711SCorentin Chary sabi_set_commandb(samsung, commands->set_backlight, 0); 4925dea7a20SCorentin Chary 4932d70b73aSGreg Kroah-Hartman return 0; 4942d70b73aSGreg Kroah-Hartman } 4952d70b73aSGreg Kroah-Hartman 4962d70b73aSGreg Kroah-Hartman static const struct backlight_ops backlight_ops = { 4972d70b73aSGreg Kroah-Hartman .get_brightness = get_brightness, 4982d70b73aSGreg Kroah-Hartman .update_status = update_status, 4992d70b73aSGreg Kroah-Hartman }; 5002d70b73aSGreg Kroah-Hartman 5012d70b73aSGreg Kroah-Hartman static int rfkill_set(void *data, bool blocked) 5022d70b73aSGreg Kroah-Hartman { 5035dea7a20SCorentin Chary struct samsung_laptop *samsung = data; 504a6df4894SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 505a6df4894SCorentin Chary 5062d70b73aSGreg Kroah-Hartman /* Do something with blocked...*/ 5072d70b73aSGreg Kroah-Hartman /* 5082d70b73aSGreg Kroah-Hartman * blocked == false is on 5092d70b73aSGreg Kroah-Hartman * blocked == true is off 5102d70b73aSGreg Kroah-Hartman */ 5112d70b73aSGreg Kroah-Hartman if (blocked) 5127e960711SCorentin Chary sabi_set_commandb(samsung, commands->set_wireless_button, 0); 5132d70b73aSGreg Kroah-Hartman else 5147e960711SCorentin Chary sabi_set_commandb(samsung, commands->set_wireless_button, 1); 5152d70b73aSGreg Kroah-Hartman 5162d70b73aSGreg Kroah-Hartman return 0; 5172d70b73aSGreg Kroah-Hartman } 5182d70b73aSGreg Kroah-Hartman 5192d70b73aSGreg Kroah-Hartman static struct rfkill_ops rfkill_ops = { 5202d70b73aSGreg Kroah-Hartman .set_block = rfkill_set, 5212d70b73aSGreg Kroah-Hartman }; 5222d70b73aSGreg Kroah-Hartman 5232d70b73aSGreg Kroah-Hartman static ssize_t get_performance_level(struct device *dev, 5242d70b73aSGreg Kroah-Hartman struct device_attribute *attr, char *buf) 5252d70b73aSGreg Kroah-Hartman { 5265dea7a20SCorentin Chary struct samsung_laptop *samsung = dev_get_drvdata(dev); 527a6df4894SCorentin Chary const struct sabi_config *config = samsung->config; 5285dea7a20SCorentin Chary const struct sabi_commands *commands = &config->commands; 5297e960711SCorentin Chary struct sabi_data sretval; 5302d70b73aSGreg Kroah-Hartman int retval; 5312d70b73aSGreg Kroah-Hartman int i; 5322d70b73aSGreg Kroah-Hartman 5332d70b73aSGreg Kroah-Hartman /* Read the state */ 5347e960711SCorentin Chary retval = sabi_command(samsung, commands->get_performance_level, 5357e960711SCorentin Chary NULL, &sretval); 5362d70b73aSGreg Kroah-Hartman if (retval) 5372d70b73aSGreg Kroah-Hartman return retval; 5382d70b73aSGreg Kroah-Hartman 5392d70b73aSGreg Kroah-Hartman /* The logic is backwards, yeah, lots of fun... */ 540a6df4894SCorentin Chary for (i = 0; config->performance_levels[i].name; ++i) { 5417e960711SCorentin Chary if (sretval.data[0] == config->performance_levels[i].value) 542a6df4894SCorentin Chary return sprintf(buf, "%s\n", config->performance_levels[i].name); 5432d70b73aSGreg Kroah-Hartman } 5442d70b73aSGreg Kroah-Hartman return sprintf(buf, "%s\n", "unknown"); 5452d70b73aSGreg Kroah-Hartman } 5462d70b73aSGreg Kroah-Hartman 5472d70b73aSGreg Kroah-Hartman static ssize_t set_performance_level(struct device *dev, 5482d70b73aSGreg Kroah-Hartman struct device_attribute *attr, const char *buf, 5492d70b73aSGreg Kroah-Hartman size_t count) 5502d70b73aSGreg Kroah-Hartman { 5515dea7a20SCorentin Chary struct samsung_laptop *samsung = dev_get_drvdata(dev); 552a6df4894SCorentin Chary const struct sabi_config *config = samsung->config; 5535dea7a20SCorentin Chary const struct sabi_commands *commands = &config->commands; 5542d70b73aSGreg Kroah-Hartman int i; 5555dea7a20SCorentin Chary 5565dea7a20SCorentin Chary if (count < 1) 5575dea7a20SCorentin Chary return count; 5585dea7a20SCorentin Chary 559a6df4894SCorentin Chary for (i = 0; config->performance_levels[i].name; ++i) { 5602d70b73aSGreg Kroah-Hartman const struct sabi_performance_level *level = 561a6df4894SCorentin Chary &config->performance_levels[i]; 5622d70b73aSGreg Kroah-Hartman if (!strncasecmp(level->name, buf, strlen(level->name))) { 5637e960711SCorentin Chary sabi_set_commandb(samsung, 5645dea7a20SCorentin Chary commands->set_performance_level, 5652d70b73aSGreg Kroah-Hartman level->value); 5662d70b73aSGreg Kroah-Hartman break; 5672d70b73aSGreg Kroah-Hartman } 5682d70b73aSGreg Kroah-Hartman } 5695dea7a20SCorentin Chary 570a6df4894SCorentin Chary if (!config->performance_levels[i].name) 5712d70b73aSGreg Kroah-Hartman return -EINVAL; 5725dea7a20SCorentin Chary 5732d70b73aSGreg Kroah-Hartman return count; 5742d70b73aSGreg Kroah-Hartman } 5755dea7a20SCorentin Chary 5762d70b73aSGreg Kroah-Hartman static DEVICE_ATTR(performance_level, S_IWUSR | S_IRUGO, 5772d70b73aSGreg Kroah-Hartman get_performance_level, set_performance_level); 5782d70b73aSGreg Kroah-Hartman 579cb5b5c91SCorentin Chary static int read_battery_life_extender(struct samsung_laptop *samsung) 580cb5b5c91SCorentin Chary { 581cb5b5c91SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 582cb5b5c91SCorentin Chary struct sabi_data data; 583cb5b5c91SCorentin Chary int retval; 584cb5b5c91SCorentin Chary 585cb5b5c91SCorentin Chary if (commands->get_battery_life_extender == 0xFFFF) 586cb5b5c91SCorentin Chary return -ENODEV; 587cb5b5c91SCorentin Chary 588cb5b5c91SCorentin Chary memset(&data, 0, sizeof(data)); 589cb5b5c91SCorentin Chary data.data[0] = 0x80; 590cb5b5c91SCorentin Chary retval = sabi_command(samsung, commands->get_battery_life_extender, 591cb5b5c91SCorentin Chary &data, &data); 592cb5b5c91SCorentin Chary 593cb5b5c91SCorentin Chary if (retval) 594cb5b5c91SCorentin Chary return retval; 595cb5b5c91SCorentin Chary 596cb5b5c91SCorentin Chary if (data.data[0] != 0 && data.data[0] != 1) 597cb5b5c91SCorentin Chary return -ENODEV; 598cb5b5c91SCorentin Chary 599cb5b5c91SCorentin Chary return data.data[0]; 600cb5b5c91SCorentin Chary } 601cb5b5c91SCorentin Chary 602cb5b5c91SCorentin Chary static int write_battery_life_extender(struct samsung_laptop *samsung, 603cb5b5c91SCorentin Chary int enabled) 604cb5b5c91SCorentin Chary { 605cb5b5c91SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 606cb5b5c91SCorentin Chary struct sabi_data data; 607cb5b5c91SCorentin Chary 608cb5b5c91SCorentin Chary memset(&data, 0, sizeof(data)); 609cb5b5c91SCorentin Chary data.data[0] = 0x80 | enabled; 610cb5b5c91SCorentin Chary return sabi_command(samsung, commands->set_battery_life_extender, 611cb5b5c91SCorentin Chary &data, NULL); 612cb5b5c91SCorentin Chary } 613cb5b5c91SCorentin Chary 614cb5b5c91SCorentin Chary static ssize_t get_battery_life_extender(struct device *dev, 615cb5b5c91SCorentin Chary struct device_attribute *attr, 616cb5b5c91SCorentin Chary char *buf) 617cb5b5c91SCorentin Chary { 618cb5b5c91SCorentin Chary struct samsung_laptop *samsung = dev_get_drvdata(dev); 619cb5b5c91SCorentin Chary int ret; 620cb5b5c91SCorentin Chary 621cb5b5c91SCorentin Chary ret = read_battery_life_extender(samsung); 622cb5b5c91SCorentin Chary if (ret < 0) 623cb5b5c91SCorentin Chary return ret; 624cb5b5c91SCorentin Chary 625cb5b5c91SCorentin Chary return sprintf(buf, "%d\n", ret); 626cb5b5c91SCorentin Chary } 627cb5b5c91SCorentin Chary 628cb5b5c91SCorentin Chary static ssize_t set_battery_life_extender(struct device *dev, 629cb5b5c91SCorentin Chary struct device_attribute *attr, 630cb5b5c91SCorentin Chary const char *buf, size_t count) 631cb5b5c91SCorentin Chary { 632cb5b5c91SCorentin Chary struct samsung_laptop *samsung = dev_get_drvdata(dev); 633cb5b5c91SCorentin Chary int ret, value; 634cb5b5c91SCorentin Chary 635cb5b5c91SCorentin Chary if (!count || sscanf(buf, "%i", &value) != 1) 636cb5b5c91SCorentin Chary return -EINVAL; 637cb5b5c91SCorentin Chary 638cb5b5c91SCorentin Chary ret = write_battery_life_extender(samsung, !!value); 639cb5b5c91SCorentin Chary if (ret < 0) 640cb5b5c91SCorentin Chary return ret; 641cb5b5c91SCorentin Chary 642cb5b5c91SCorentin Chary return count; 643cb5b5c91SCorentin Chary } 644cb5b5c91SCorentin Chary 645cb5b5c91SCorentin Chary static DEVICE_ATTR(battery_life_extender, S_IWUSR | S_IRUGO, 646cb5b5c91SCorentin Chary get_battery_life_extender, set_battery_life_extender); 647cb5b5c91SCorentin Chary 6483a75d378SCorentin Chary static int read_usb_charge(struct samsung_laptop *samsung) 6493a75d378SCorentin Chary { 6503a75d378SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 6513a75d378SCorentin Chary struct sabi_data data; 6523a75d378SCorentin Chary int retval; 6533a75d378SCorentin Chary 6543a75d378SCorentin Chary if (commands->get_usb_charge == 0xFFFF) 6553a75d378SCorentin Chary return -ENODEV; 6563a75d378SCorentin Chary 6573a75d378SCorentin Chary memset(&data, 0, sizeof(data)); 6583a75d378SCorentin Chary data.data[0] = 0x80; 6593a75d378SCorentin Chary retval = sabi_command(samsung, commands->get_usb_charge, 6603a75d378SCorentin Chary &data, &data); 6613a75d378SCorentin Chary 6623a75d378SCorentin Chary if (retval) 6633a75d378SCorentin Chary return retval; 6643a75d378SCorentin Chary 6653a75d378SCorentin Chary if (data.data[0] != 0 && data.data[0] != 1) 6663a75d378SCorentin Chary return -ENODEV; 6673a75d378SCorentin Chary 6683a75d378SCorentin Chary return data.data[0]; 6693a75d378SCorentin Chary } 6703a75d378SCorentin Chary 6713a75d378SCorentin Chary static int write_usb_charge(struct samsung_laptop *samsung, 6723a75d378SCorentin Chary int enabled) 6733a75d378SCorentin Chary { 6743a75d378SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 6753a75d378SCorentin Chary struct sabi_data data; 6763a75d378SCorentin Chary 6773a75d378SCorentin Chary memset(&data, 0, sizeof(data)); 6783a75d378SCorentin Chary data.data[0] = 0x80 | enabled; 6793a75d378SCorentin Chary return sabi_command(samsung, commands->set_usb_charge, 6803a75d378SCorentin Chary &data, NULL); 6813a75d378SCorentin Chary } 6823a75d378SCorentin Chary 6833a75d378SCorentin Chary static ssize_t get_usb_charge(struct device *dev, 6843a75d378SCorentin Chary struct device_attribute *attr, 6853a75d378SCorentin Chary char *buf) 6863a75d378SCorentin Chary { 6873a75d378SCorentin Chary struct samsung_laptop *samsung = dev_get_drvdata(dev); 6883a75d378SCorentin Chary int ret; 6893a75d378SCorentin Chary 6903a75d378SCorentin Chary ret = read_usb_charge(samsung); 6913a75d378SCorentin Chary if (ret < 0) 6923a75d378SCorentin Chary return ret; 6933a75d378SCorentin Chary 6943a75d378SCorentin Chary return sprintf(buf, "%d\n", ret); 6953a75d378SCorentin Chary } 6963a75d378SCorentin Chary 6973a75d378SCorentin Chary static ssize_t set_usb_charge(struct device *dev, 6983a75d378SCorentin Chary struct device_attribute *attr, 6993a75d378SCorentin Chary const char *buf, size_t count) 7003a75d378SCorentin Chary { 7013a75d378SCorentin Chary struct samsung_laptop *samsung = dev_get_drvdata(dev); 7023a75d378SCorentin Chary int ret, value; 7033a75d378SCorentin Chary 7043a75d378SCorentin Chary if (!count || sscanf(buf, "%i", &value) != 1) 7053a75d378SCorentin Chary return -EINVAL; 7063a75d378SCorentin Chary 7073a75d378SCorentin Chary ret = write_usb_charge(samsung, !!value); 7083a75d378SCorentin Chary if (ret < 0) 7093a75d378SCorentin Chary return ret; 7103a75d378SCorentin Chary 7113a75d378SCorentin Chary return count; 7123a75d378SCorentin Chary } 7133a75d378SCorentin Chary 7143a75d378SCorentin Chary static DEVICE_ATTR(usb_charge, S_IWUSR | S_IRUGO, 7153a75d378SCorentin Chary get_usb_charge, set_usb_charge); 7163a75d378SCorentin Chary 717a66c1662SCorentin Chary static struct attribute *platform_attributes[] = { 718a66c1662SCorentin Chary &dev_attr_performance_level.attr, 719cb5b5c91SCorentin Chary &dev_attr_battery_life_extender.attr, 7203a75d378SCorentin Chary &dev_attr_usb_charge.attr, 721a66c1662SCorentin Chary NULL 722a66c1662SCorentin Chary }; 7232d70b73aSGreg Kroah-Hartman 7245dea7a20SCorentin Chary static int find_signature(void __iomem *memcheck, const char *testStr) 7255dea7a20SCorentin Chary { 7265dea7a20SCorentin Chary int i = 0; 7275dea7a20SCorentin Chary int loca; 7285dea7a20SCorentin Chary 7295dea7a20SCorentin Chary for (loca = 0; loca < 0xffff; loca++) { 7305dea7a20SCorentin Chary char temp = readb(memcheck + loca); 7315dea7a20SCorentin Chary 7325dea7a20SCorentin Chary if (temp == testStr[i]) { 7335dea7a20SCorentin Chary if (i == strlen(testStr)-1) 7345dea7a20SCorentin Chary break; 7355dea7a20SCorentin Chary ++i; 7365dea7a20SCorentin Chary } else { 7375dea7a20SCorentin Chary i = 0; 7385dea7a20SCorentin Chary } 7395dea7a20SCorentin Chary } 7405dea7a20SCorentin Chary return loca; 7415dea7a20SCorentin Chary } 7425dea7a20SCorentin Chary 7435dea7a20SCorentin Chary static void samsung_rfkill_exit(struct samsung_laptop *samsung) 7445dea7a20SCorentin Chary { 7455dea7a20SCorentin Chary if (samsung->rfk) { 7465dea7a20SCorentin Chary rfkill_unregister(samsung->rfk); 7475dea7a20SCorentin Chary rfkill_destroy(samsung->rfk); 7485dea7a20SCorentin Chary samsung->rfk = NULL; 7495dea7a20SCorentin Chary } 7505dea7a20SCorentin Chary } 7515dea7a20SCorentin Chary 7525dea7a20SCorentin Chary static int __init samsung_rfkill_init(struct samsung_laptop *samsung) 7535dea7a20SCorentin Chary { 7545dea7a20SCorentin Chary int retval; 7555dea7a20SCorentin Chary 7565dea7a20SCorentin Chary samsung->rfk = rfkill_alloc("samsung-wifi", 7575dea7a20SCorentin Chary &samsung->platform_device->dev, 7585dea7a20SCorentin Chary RFKILL_TYPE_WLAN, 7595dea7a20SCorentin Chary &rfkill_ops, samsung); 7605dea7a20SCorentin Chary if (!samsung->rfk) 7615dea7a20SCorentin Chary return -ENOMEM; 7625dea7a20SCorentin Chary 7635dea7a20SCorentin Chary retval = rfkill_register(samsung->rfk); 7645dea7a20SCorentin Chary if (retval) { 7655dea7a20SCorentin Chary rfkill_destroy(samsung->rfk); 7665dea7a20SCorentin Chary samsung->rfk = NULL; 7675dea7a20SCorentin Chary return -ENODEV; 7685dea7a20SCorentin Chary } 7695dea7a20SCorentin Chary 7705dea7a20SCorentin Chary return 0; 7715dea7a20SCorentin Chary } 7725dea7a20SCorentin Chary 773f674ebf1SCorentin Chary static int kbd_backlight_enable(struct samsung_laptop *samsung) 774f674ebf1SCorentin Chary { 775f674ebf1SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 776f674ebf1SCorentin Chary struct sabi_data data; 777f674ebf1SCorentin Chary int retval; 778f674ebf1SCorentin Chary 779f674ebf1SCorentin Chary if (commands->kbd_backlight == 0xFFFF) 780f674ebf1SCorentin Chary return -ENODEV; 781f674ebf1SCorentin Chary 782f674ebf1SCorentin Chary memset(&data, 0, sizeof(data)); 783f674ebf1SCorentin Chary data.d0 = 0xaabb; 784f674ebf1SCorentin Chary retval = sabi_command(samsung, commands->kbd_backlight, 785f674ebf1SCorentin Chary &data, &data); 786f674ebf1SCorentin Chary 787f674ebf1SCorentin Chary if (retval) 788f674ebf1SCorentin Chary return retval; 789f674ebf1SCorentin Chary 790f674ebf1SCorentin Chary if (data.d0 != 0xccdd) 791f674ebf1SCorentin Chary return -ENODEV; 792f674ebf1SCorentin Chary return 0; 793f674ebf1SCorentin Chary } 794f674ebf1SCorentin Chary 795f674ebf1SCorentin Chary static int kbd_backlight_read(struct samsung_laptop *samsung) 796f674ebf1SCorentin Chary { 797f674ebf1SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 798f674ebf1SCorentin Chary struct sabi_data data; 799f674ebf1SCorentin Chary int retval; 800f674ebf1SCorentin Chary 801f674ebf1SCorentin Chary memset(&data, 0, sizeof(data)); 802f674ebf1SCorentin Chary data.data[0] = 0x81; 803f674ebf1SCorentin Chary retval = sabi_command(samsung, commands->kbd_backlight, 804f674ebf1SCorentin Chary &data, &data); 805f674ebf1SCorentin Chary 806f674ebf1SCorentin Chary if (retval) 807f674ebf1SCorentin Chary return retval; 808f674ebf1SCorentin Chary 809f674ebf1SCorentin Chary return data.data[0]; 810f674ebf1SCorentin Chary } 811f674ebf1SCorentin Chary 812f674ebf1SCorentin Chary static int kbd_backlight_write(struct samsung_laptop *samsung, int brightness) 813f674ebf1SCorentin Chary { 814f674ebf1SCorentin Chary const struct sabi_commands *commands = &samsung->config->commands; 815f674ebf1SCorentin Chary struct sabi_data data; 816f674ebf1SCorentin Chary 817f674ebf1SCorentin Chary memset(&data, 0, sizeof(data)); 818f674ebf1SCorentin Chary data.d0 = 0x82 | ((brightness & 0xFF) << 8); 819f674ebf1SCorentin Chary return sabi_command(samsung, commands->kbd_backlight, 820f674ebf1SCorentin Chary &data, NULL); 821f674ebf1SCorentin Chary } 822f674ebf1SCorentin Chary 823f674ebf1SCorentin Chary static void kbd_led_update(struct work_struct *work) 824f674ebf1SCorentin Chary { 825f674ebf1SCorentin Chary struct samsung_laptop *samsung; 826f674ebf1SCorentin Chary 827f674ebf1SCorentin Chary samsung = container_of(work, struct samsung_laptop, kbd_led_work); 828f674ebf1SCorentin Chary kbd_backlight_write(samsung, samsung->kbd_led_wk); 829f674ebf1SCorentin Chary } 830f674ebf1SCorentin Chary 831f674ebf1SCorentin Chary static void kbd_led_set(struct led_classdev *led_cdev, 832f674ebf1SCorentin Chary enum led_brightness value) 833f674ebf1SCorentin Chary { 834f674ebf1SCorentin Chary struct samsung_laptop *samsung; 835f674ebf1SCorentin Chary 836f674ebf1SCorentin Chary samsung = container_of(led_cdev, struct samsung_laptop, kbd_led); 837f674ebf1SCorentin Chary 838f674ebf1SCorentin Chary if (value > samsung->kbd_led.max_brightness) 839f674ebf1SCorentin Chary value = samsung->kbd_led.max_brightness; 840f674ebf1SCorentin Chary else if (value < 0) 841f674ebf1SCorentin Chary value = 0; 842f674ebf1SCorentin Chary 843f674ebf1SCorentin Chary samsung->kbd_led_wk = value; 844f674ebf1SCorentin Chary queue_work(samsung->led_workqueue, &samsung->kbd_led_work); 845f674ebf1SCorentin Chary } 846f674ebf1SCorentin Chary 847f674ebf1SCorentin Chary static enum led_brightness kbd_led_get(struct led_classdev *led_cdev) 848f674ebf1SCorentin Chary { 849f674ebf1SCorentin Chary struct samsung_laptop *samsung; 850f674ebf1SCorentin Chary 851f674ebf1SCorentin Chary samsung = container_of(led_cdev, struct samsung_laptop, kbd_led); 852f674ebf1SCorentin Chary return kbd_backlight_read(samsung); 853f674ebf1SCorentin Chary } 854f674ebf1SCorentin Chary 855f674ebf1SCorentin Chary static void samsung_leds_exit(struct samsung_laptop *samsung) 856f674ebf1SCorentin Chary { 857f674ebf1SCorentin Chary if (!IS_ERR_OR_NULL(samsung->kbd_led.dev)) 858f674ebf1SCorentin Chary led_classdev_unregister(&samsung->kbd_led); 859f674ebf1SCorentin Chary if (samsung->led_workqueue) 860f674ebf1SCorentin Chary destroy_workqueue(samsung->led_workqueue); 861f674ebf1SCorentin Chary } 862f674ebf1SCorentin Chary 863f674ebf1SCorentin Chary static int __init samsung_leds_init(struct samsung_laptop *samsung) 864f674ebf1SCorentin Chary { 865f674ebf1SCorentin Chary int ret = 0; 866f674ebf1SCorentin Chary 867f674ebf1SCorentin Chary samsung->led_workqueue = create_singlethread_workqueue("led_workqueue"); 868f674ebf1SCorentin Chary if (!samsung->led_workqueue) 869f674ebf1SCorentin Chary return -ENOMEM; 870f674ebf1SCorentin Chary 871f674ebf1SCorentin Chary if (kbd_backlight_enable(samsung) >= 0) { 872f674ebf1SCorentin Chary INIT_WORK(&samsung->kbd_led_work, kbd_led_update); 873f674ebf1SCorentin Chary 874f674ebf1SCorentin Chary samsung->kbd_led.name = "samsung::kbd_backlight"; 875f674ebf1SCorentin Chary samsung->kbd_led.brightness_set = kbd_led_set; 876f674ebf1SCorentin Chary samsung->kbd_led.brightness_get = kbd_led_get; 877f674ebf1SCorentin Chary samsung->kbd_led.max_brightness = 8; 878f674ebf1SCorentin Chary 879f674ebf1SCorentin Chary ret = led_classdev_register(&samsung->platform_device->dev, 880f674ebf1SCorentin Chary &samsung->kbd_led); 881f674ebf1SCorentin Chary } 882f674ebf1SCorentin Chary 883f674ebf1SCorentin Chary if (ret) 884f674ebf1SCorentin Chary samsung_leds_exit(samsung); 885f674ebf1SCorentin Chary 886f674ebf1SCorentin Chary return ret; 887f674ebf1SCorentin Chary } 888f674ebf1SCorentin Chary 8895dea7a20SCorentin Chary static void samsung_backlight_exit(struct samsung_laptop *samsung) 8905dea7a20SCorentin Chary { 8915dea7a20SCorentin Chary if (samsung->backlight_device) { 8925dea7a20SCorentin Chary backlight_device_unregister(samsung->backlight_device); 8935dea7a20SCorentin Chary samsung->backlight_device = NULL; 8945dea7a20SCorentin Chary } 8955dea7a20SCorentin Chary } 8965dea7a20SCorentin Chary 8975dea7a20SCorentin Chary static int __init samsung_backlight_init(struct samsung_laptop *samsung) 8985dea7a20SCorentin Chary { 8995dea7a20SCorentin Chary struct backlight_device *bd; 9005dea7a20SCorentin Chary struct backlight_properties props; 9015dea7a20SCorentin Chary 902f34cd9caSCorentin Chary if (!samsung->handle_backlight) 903f34cd9caSCorentin Chary return 0; 904f34cd9caSCorentin Chary 9055dea7a20SCorentin Chary memset(&props, 0, sizeof(struct backlight_properties)); 9065dea7a20SCorentin Chary props.type = BACKLIGHT_PLATFORM; 9075dea7a20SCorentin Chary props.max_brightness = samsung->config->max_brightness - 9085dea7a20SCorentin Chary samsung->config->min_brightness; 9095dea7a20SCorentin Chary 9105dea7a20SCorentin Chary bd = backlight_device_register("samsung", 9115dea7a20SCorentin Chary &samsung->platform_device->dev, 9125dea7a20SCorentin Chary samsung, &backlight_ops, 9135dea7a20SCorentin Chary &props); 9145dea7a20SCorentin Chary if (IS_ERR(bd)) 9155dea7a20SCorentin Chary return PTR_ERR(bd); 9165dea7a20SCorentin Chary 9175dea7a20SCorentin Chary samsung->backlight_device = bd; 9185dea7a20SCorentin Chary samsung->backlight_device->props.brightness = read_brightness(samsung); 9195dea7a20SCorentin Chary samsung->backlight_device->props.power = FB_BLANK_UNBLANK; 9205dea7a20SCorentin Chary backlight_update_status(samsung->backlight_device); 9215dea7a20SCorentin Chary 9225dea7a20SCorentin Chary return 0; 9235dea7a20SCorentin Chary } 9245dea7a20SCorentin Chary 925a66c1662SCorentin Chary static mode_t samsung_sysfs_is_visible(struct kobject *kobj, 926a66c1662SCorentin Chary struct attribute *attr, int idx) 927a66c1662SCorentin Chary { 928a66c1662SCorentin Chary struct device *dev = container_of(kobj, struct device, kobj); 929a66c1662SCorentin Chary struct platform_device *pdev = to_platform_device(dev); 930a66c1662SCorentin Chary struct samsung_laptop *samsung = platform_get_drvdata(pdev); 931a66c1662SCorentin Chary bool ok = true; 932a66c1662SCorentin Chary 933a66c1662SCorentin Chary if (attr == &dev_attr_performance_level.attr) 934a66c1662SCorentin Chary ok = !!samsung->config->performance_levels[0].name; 935cb5b5c91SCorentin Chary if (attr == &dev_attr_battery_life_extender.attr) 936cb5b5c91SCorentin Chary ok = !!(read_battery_life_extender(samsung) >= 0); 9373a75d378SCorentin Chary if (attr == &dev_attr_usb_charge.attr) 9383a75d378SCorentin Chary ok = !!(read_usb_charge(samsung) >= 0); 939a66c1662SCorentin Chary 940a66c1662SCorentin Chary return ok ? attr->mode : 0; 941a66c1662SCorentin Chary } 942a66c1662SCorentin Chary 943a66c1662SCorentin Chary static struct attribute_group platform_attribute_group = { 944a66c1662SCorentin Chary .is_visible = samsung_sysfs_is_visible, 945a66c1662SCorentin Chary .attrs = platform_attributes 946a66c1662SCorentin Chary }; 947a66c1662SCorentin Chary 9485dea7a20SCorentin Chary static void samsung_sysfs_exit(struct samsung_laptop *samsung) 9495dea7a20SCorentin Chary { 950a66c1662SCorentin Chary struct platform_device *device = samsung->platform_device; 951a66c1662SCorentin Chary 952a66c1662SCorentin Chary sysfs_remove_group(&device->dev.kobj, &platform_attribute_group); 9535dea7a20SCorentin Chary } 9545dea7a20SCorentin Chary 9555dea7a20SCorentin Chary static int __init samsung_sysfs_init(struct samsung_laptop *samsung) 9565dea7a20SCorentin Chary { 957a66c1662SCorentin Chary struct platform_device *device = samsung->platform_device; 958a66c1662SCorentin Chary 959a66c1662SCorentin Chary return sysfs_create_group(&device->dev.kobj, &platform_attribute_group); 960a66c1662SCorentin Chary 9615dea7a20SCorentin Chary } 9625dea7a20SCorentin Chary 9635b80fc40SCorentin Chary static int show_call(struct seq_file *m, void *data) 9645b80fc40SCorentin Chary { 9655b80fc40SCorentin Chary struct samsung_laptop *samsung = m->private; 9665b80fc40SCorentin Chary struct sabi_data *sdata = &samsung->debug.data; 9675b80fc40SCorentin Chary int ret; 9685b80fc40SCorentin Chary 9695b80fc40SCorentin Chary seq_printf(m, "SABI 0x%04x {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n", 9705b80fc40SCorentin Chary samsung->debug.command, 9715b80fc40SCorentin Chary sdata->d0, sdata->d1, sdata->d2, sdata->d3); 9725b80fc40SCorentin Chary 9735b80fc40SCorentin Chary ret = sabi_command(samsung, samsung->debug.command, sdata, sdata); 9745b80fc40SCorentin Chary 9755b80fc40SCorentin Chary if (ret) { 9765b80fc40SCorentin Chary seq_printf(m, "SABI command 0x%04x failed\n", 9775b80fc40SCorentin Chary samsung->debug.command); 9785b80fc40SCorentin Chary return ret; 9795b80fc40SCorentin Chary } 9805b80fc40SCorentin Chary 9815b80fc40SCorentin Chary seq_printf(m, "SABI {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n", 9825b80fc40SCorentin Chary sdata->d0, sdata->d1, sdata->d2, sdata->d3); 9835b80fc40SCorentin Chary return 0; 9845b80fc40SCorentin Chary } 9855b80fc40SCorentin Chary 9865b80fc40SCorentin Chary static int samsung_debugfs_open(struct inode *inode, struct file *file) 9875b80fc40SCorentin Chary { 9885b80fc40SCorentin Chary return single_open(file, show_call, inode->i_private); 9895b80fc40SCorentin Chary } 9905b80fc40SCorentin Chary 9915b80fc40SCorentin Chary static const struct file_operations samsung_laptop_call_io_ops = { 9925b80fc40SCorentin Chary .owner = THIS_MODULE, 9935b80fc40SCorentin Chary .open = samsung_debugfs_open, 9945b80fc40SCorentin Chary .read = seq_read, 9955b80fc40SCorentin Chary .llseek = seq_lseek, 9965b80fc40SCorentin Chary .release = single_release, 9975b80fc40SCorentin Chary }; 9985b80fc40SCorentin Chary 9995b80fc40SCorentin Chary static void samsung_debugfs_exit(struct samsung_laptop *samsung) 10005b80fc40SCorentin Chary { 10015b80fc40SCorentin Chary debugfs_remove_recursive(samsung->debug.root); 10025b80fc40SCorentin Chary } 10035b80fc40SCorentin Chary 10045b80fc40SCorentin Chary static int samsung_debugfs_init(struct samsung_laptop *samsung) 10055b80fc40SCorentin Chary { 10065b80fc40SCorentin Chary struct dentry *dent; 10075b80fc40SCorentin Chary 10085b80fc40SCorentin Chary samsung->debug.root = debugfs_create_dir("samsung-laptop", NULL); 10095b80fc40SCorentin Chary if (!samsung->debug.root) { 10105b80fc40SCorentin Chary pr_err("failed to create debugfs directory"); 10115b80fc40SCorentin Chary goto error_debugfs; 10125b80fc40SCorentin Chary } 10135b80fc40SCorentin Chary 10145b80fc40SCorentin Chary samsung->debug.f0000_wrapper.data = samsung->f0000_segment; 10155b80fc40SCorentin Chary samsung->debug.f0000_wrapper.size = 0xffff; 10165b80fc40SCorentin Chary 10175b80fc40SCorentin Chary samsung->debug.data_wrapper.data = &samsung->debug.data; 10185b80fc40SCorentin Chary samsung->debug.data_wrapper.size = sizeof(samsung->debug.data); 10195b80fc40SCorentin Chary 10205b80fc40SCorentin Chary dent = debugfs_create_u16("command", S_IRUGO | S_IWUSR, 10215b80fc40SCorentin Chary samsung->debug.root, &samsung->debug.command); 10225b80fc40SCorentin Chary if (!dent) 10235b80fc40SCorentin Chary goto error_debugfs; 10245b80fc40SCorentin Chary 10255b80fc40SCorentin Chary dent = debugfs_create_u32("d0", S_IRUGO | S_IWUSR, samsung->debug.root, 10265b80fc40SCorentin Chary &samsung->debug.data.d0); 10275b80fc40SCorentin Chary if (!dent) 10285b80fc40SCorentin Chary goto error_debugfs; 10295b80fc40SCorentin Chary 10305b80fc40SCorentin Chary dent = debugfs_create_u32("d1", S_IRUGO | S_IWUSR, samsung->debug.root, 10315b80fc40SCorentin Chary &samsung->debug.data.d1); 10325b80fc40SCorentin Chary if (!dent) 10335b80fc40SCorentin Chary goto error_debugfs; 10345b80fc40SCorentin Chary 10355b80fc40SCorentin Chary dent = debugfs_create_u16("d2", S_IRUGO | S_IWUSR, samsung->debug.root, 10365b80fc40SCorentin Chary &samsung->debug.data.d2); 10375b80fc40SCorentin Chary if (!dent) 10385b80fc40SCorentin Chary goto error_debugfs; 10395b80fc40SCorentin Chary 10405b80fc40SCorentin Chary dent = debugfs_create_u8("d3", S_IRUGO | S_IWUSR, samsung->debug.root, 10415b80fc40SCorentin Chary &samsung->debug.data.d3); 10425b80fc40SCorentin Chary if (!dent) 10435b80fc40SCorentin Chary goto error_debugfs; 10445b80fc40SCorentin Chary 10455b80fc40SCorentin Chary dent = debugfs_create_blob("data", S_IRUGO | S_IWUSR, 10465b80fc40SCorentin Chary samsung->debug.root, 10475b80fc40SCorentin Chary &samsung->debug.data_wrapper); 10485b80fc40SCorentin Chary if (!dent) 10495b80fc40SCorentin Chary goto error_debugfs; 10505b80fc40SCorentin Chary 10515b80fc40SCorentin Chary dent = debugfs_create_blob("f0000_segment", S_IRUSR | S_IWUSR, 10525b80fc40SCorentin Chary samsung->debug.root, 10535b80fc40SCorentin Chary &samsung->debug.f0000_wrapper); 10545b80fc40SCorentin Chary if (!dent) 10555b80fc40SCorentin Chary goto error_debugfs; 10565b80fc40SCorentin Chary 10575b80fc40SCorentin Chary dent = debugfs_create_file("call", S_IFREG | S_IRUGO, 10585b80fc40SCorentin Chary samsung->debug.root, samsung, 10595b80fc40SCorentin Chary &samsung_laptop_call_io_ops); 10605b80fc40SCorentin Chary if (!dent) 10615b80fc40SCorentin Chary goto error_debugfs; 10625b80fc40SCorentin Chary 10635b80fc40SCorentin Chary return 0; 10645b80fc40SCorentin Chary 10655b80fc40SCorentin Chary error_debugfs: 10665b80fc40SCorentin Chary samsung_debugfs_exit(samsung); 10675b80fc40SCorentin Chary return -ENOMEM; 10685b80fc40SCorentin Chary } 10695b80fc40SCorentin Chary 10705dea7a20SCorentin Chary static void samsung_sabi_exit(struct samsung_laptop *samsung) 10715dea7a20SCorentin Chary { 10725dea7a20SCorentin Chary const struct sabi_config *config = samsung->config; 10735dea7a20SCorentin Chary 10745dea7a20SCorentin Chary /* Turn off "Linux" mode in the BIOS */ 10755dea7a20SCorentin Chary if (config && config->commands.set_linux != 0xff) 10767e960711SCorentin Chary sabi_set_commandb(samsung, config->commands.set_linux, 0x80); 10775dea7a20SCorentin Chary 10785dea7a20SCorentin Chary if (samsung->sabi_iface) { 10795dea7a20SCorentin Chary iounmap(samsung->sabi_iface); 10805dea7a20SCorentin Chary samsung->sabi_iface = NULL; 10815dea7a20SCorentin Chary } 10825dea7a20SCorentin Chary if (samsung->f0000_segment) { 10835dea7a20SCorentin Chary iounmap(samsung->f0000_segment); 10845dea7a20SCorentin Chary samsung->f0000_segment = NULL; 10855dea7a20SCorentin Chary } 10865dea7a20SCorentin Chary 10875dea7a20SCorentin Chary samsung->config = NULL; 10885dea7a20SCorentin Chary } 10895dea7a20SCorentin Chary 109049dd7730SCorentin Chary static __init void samsung_sabi_infos(struct samsung_laptop *samsung, int loca, 109149dd7730SCorentin Chary unsigned int ifaceP) 10925dea7a20SCorentin Chary { 10935dea7a20SCorentin Chary const struct sabi_config *config = samsung->config; 10945dea7a20SCorentin Chary 10955dea7a20SCorentin Chary printk(KERN_DEBUG "This computer supports SABI==%x\n", 10965dea7a20SCorentin Chary loca + 0xf0000 - 6); 109749dd7730SCorentin Chary 10985dea7a20SCorentin Chary printk(KERN_DEBUG "SABI header:\n"); 10995dea7a20SCorentin Chary printk(KERN_DEBUG " SMI Port Number = 0x%04x\n", 11005dea7a20SCorentin Chary readw(samsung->sabi + config->header_offsets.port)); 11015dea7a20SCorentin Chary printk(KERN_DEBUG " SMI Interface Function = 0x%02x\n", 11025dea7a20SCorentin Chary readb(samsung->sabi + config->header_offsets.iface_func)); 11035dea7a20SCorentin Chary printk(KERN_DEBUG " SMI enable memory buffer = 0x%02x\n", 11045dea7a20SCorentin Chary readb(samsung->sabi + config->header_offsets.en_mem)); 11055dea7a20SCorentin Chary printk(KERN_DEBUG " SMI restore memory buffer = 0x%02x\n", 11065dea7a20SCorentin Chary readb(samsung->sabi + config->header_offsets.re_mem)); 11075dea7a20SCorentin Chary printk(KERN_DEBUG " SABI data offset = 0x%04x\n", 11085dea7a20SCorentin Chary readw(samsung->sabi + config->header_offsets.data_offset)); 11095dea7a20SCorentin Chary printk(KERN_DEBUG " SABI data segment = 0x%04x\n", 11105dea7a20SCorentin Chary readw(samsung->sabi + config->header_offsets.data_segment)); 11115dea7a20SCorentin Chary 111249dd7730SCorentin Chary printk(KERN_DEBUG " SABI pointer = 0x%08x\n", ifaceP); 11135dea7a20SCorentin Chary } 11145dea7a20SCorentin Chary 11155dea7a20SCorentin Chary static int __init samsung_sabi_init(struct samsung_laptop *samsung) 11165dea7a20SCorentin Chary { 11175dea7a20SCorentin Chary const struct sabi_config *config = NULL; 11185dea7a20SCorentin Chary const struct sabi_commands *commands; 11195dea7a20SCorentin Chary unsigned int ifaceP; 11205dea7a20SCorentin Chary int ret = 0; 11215dea7a20SCorentin Chary int i; 11225dea7a20SCorentin Chary int loca; 11235dea7a20SCorentin Chary 11245dea7a20SCorentin Chary samsung->f0000_segment = ioremap_nocache(0xf0000, 0xffff); 11255dea7a20SCorentin Chary if (!samsung->f0000_segment) { 11265dea7a20SCorentin Chary pr_err("Can't map the segment at 0xf0000\n"); 11275dea7a20SCorentin Chary ret = -EINVAL; 11285dea7a20SCorentin Chary goto exit; 11295dea7a20SCorentin Chary } 11305dea7a20SCorentin Chary 11315dea7a20SCorentin Chary /* Try to find one of the signatures in memory to find the header */ 11325dea7a20SCorentin Chary for (i = 0; sabi_configs[i].test_string != 0; ++i) { 11335dea7a20SCorentin Chary samsung->config = &sabi_configs[i]; 11345dea7a20SCorentin Chary loca = find_signature(samsung->f0000_segment, 11355dea7a20SCorentin Chary samsung->config->test_string); 11365dea7a20SCorentin Chary if (loca != 0xffff) 11375dea7a20SCorentin Chary break; 11385dea7a20SCorentin Chary } 11395dea7a20SCorentin Chary 11405dea7a20SCorentin Chary if (loca == 0xffff) { 11415dea7a20SCorentin Chary pr_err("This computer does not support SABI\n"); 11425dea7a20SCorentin Chary ret = -ENODEV; 11435dea7a20SCorentin Chary goto exit; 11445dea7a20SCorentin Chary } 11455dea7a20SCorentin Chary 11465dea7a20SCorentin Chary config = samsung->config; 11475dea7a20SCorentin Chary commands = &config->commands; 11485dea7a20SCorentin Chary 11495dea7a20SCorentin Chary /* point to the SMI port Number */ 11505dea7a20SCorentin Chary loca += 1; 11515dea7a20SCorentin Chary samsung->sabi = (samsung->f0000_segment + loca); 11525dea7a20SCorentin Chary 11535dea7a20SCorentin Chary /* Get a pointer to the SABI Interface */ 11545dea7a20SCorentin Chary ifaceP = (readw(samsung->sabi + config->header_offsets.data_segment) & 0x0ffff) << 4; 11555dea7a20SCorentin Chary ifaceP += readw(samsung->sabi + config->header_offsets.data_offset) & 0x0ffff; 115649dd7730SCorentin Chary 115749dd7730SCorentin Chary if (debug) 115849dd7730SCorentin Chary samsung_sabi_infos(samsung, loca, ifaceP); 115949dd7730SCorentin Chary 11605dea7a20SCorentin Chary samsung->sabi_iface = ioremap_nocache(ifaceP, 16); 11615dea7a20SCorentin Chary if (!samsung->sabi_iface) { 11625dea7a20SCorentin Chary pr_err("Can't remap %x\n", ifaceP); 11635dea7a20SCorentin Chary ret = -EINVAL; 11645dea7a20SCorentin Chary goto exit; 11655dea7a20SCorentin Chary } 11665dea7a20SCorentin Chary 11675dea7a20SCorentin Chary /* Turn on "Linux" mode in the BIOS */ 11685dea7a20SCorentin Chary if (commands->set_linux != 0xff) { 11697e960711SCorentin Chary int retval = sabi_set_commandb(samsung, 11705dea7a20SCorentin Chary commands->set_linux, 0x81); 11715dea7a20SCorentin Chary if (retval) { 11725dea7a20SCorentin Chary pr_warn("Linux mode was not set!\n"); 11735dea7a20SCorentin Chary ret = -ENODEV; 11745dea7a20SCorentin Chary goto exit; 11755dea7a20SCorentin Chary } 11765dea7a20SCorentin Chary } 11775dea7a20SCorentin Chary 11785dea7a20SCorentin Chary /* Check for stepping quirk */ 1179f34cd9caSCorentin Chary if (samsung->handle_backlight) 11805dea7a20SCorentin Chary check_for_stepping_quirk(samsung); 11815dea7a20SCorentin Chary 11825dea7a20SCorentin Chary exit: 11835dea7a20SCorentin Chary if (ret) 11845dea7a20SCorentin Chary samsung_sabi_exit(samsung); 11855dea7a20SCorentin Chary 11865dea7a20SCorentin Chary return ret; 11875dea7a20SCorentin Chary } 11885dea7a20SCorentin Chary 11895dea7a20SCorentin Chary static void samsung_platform_exit(struct samsung_laptop *samsung) 11905dea7a20SCorentin Chary { 11915dea7a20SCorentin Chary if (samsung->platform_device) { 11925dea7a20SCorentin Chary platform_device_unregister(samsung->platform_device); 11935dea7a20SCorentin Chary samsung->platform_device = NULL; 11945dea7a20SCorentin Chary } 11955dea7a20SCorentin Chary } 11965dea7a20SCorentin Chary 11975dea7a20SCorentin Chary static int __init samsung_platform_init(struct samsung_laptop *samsung) 11985dea7a20SCorentin Chary { 11995dea7a20SCorentin Chary struct platform_device *pdev; 12005dea7a20SCorentin Chary 12015dea7a20SCorentin Chary pdev = platform_device_register_simple("samsung", -1, NULL, 0); 12025dea7a20SCorentin Chary if (IS_ERR(pdev)) 12035dea7a20SCorentin Chary return PTR_ERR(pdev); 12045dea7a20SCorentin Chary 12055dea7a20SCorentin Chary samsung->platform_device = pdev; 12065dea7a20SCorentin Chary platform_set_drvdata(samsung->platform_device, samsung); 12075dea7a20SCorentin Chary return 0; 12085dea7a20SCorentin Chary } 12095dea7a20SCorentin Chary 12102d70b73aSGreg Kroah-Hartman static int __init dmi_check_cb(const struct dmi_system_id *id) 12112d70b73aSGreg Kroah-Hartman { 12125dea7a20SCorentin Chary pr_info("found laptop model '%s'\n", id->ident); 121327836584SAxel Lin return 1; 12142d70b73aSGreg Kroah-Hartman } 12152d70b73aSGreg Kroah-Hartman 12162d70b73aSGreg Kroah-Hartman static struct dmi_system_id __initdata samsung_dmi_table[] = { 12172d70b73aSGreg Kroah-Hartman { 12182d70b73aSGreg Kroah-Hartman .ident = "N128", 12192d70b73aSGreg Kroah-Hartman .matches = { 12202d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_SYS_VENDOR, 12212d70b73aSGreg Kroah-Hartman "SAMSUNG ELECTRONICS CO., LTD."), 12222d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_PRODUCT_NAME, "N128"), 12232d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_BOARD_NAME, "N128"), 12242d70b73aSGreg Kroah-Hartman }, 12252d70b73aSGreg Kroah-Hartman .callback = dmi_check_cb, 12262d70b73aSGreg Kroah-Hartman }, 12272d70b73aSGreg Kroah-Hartman { 12282d70b73aSGreg Kroah-Hartman .ident = "N130", 12292d70b73aSGreg Kroah-Hartman .matches = { 12302d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_SYS_VENDOR, 12312d70b73aSGreg Kroah-Hartman "SAMSUNG ELECTRONICS CO., LTD."), 12322d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_PRODUCT_NAME, "N130"), 12332d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_BOARD_NAME, "N130"), 12342d70b73aSGreg Kroah-Hartman }, 12352d70b73aSGreg Kroah-Hartman .callback = dmi_check_cb, 12362d70b73aSGreg Kroah-Hartman }, 12372d70b73aSGreg Kroah-Hartman { 12384e2441c0SJ Witteveen .ident = "N510", 12394e2441c0SJ Witteveen .matches = { 12404e2441c0SJ Witteveen DMI_MATCH(DMI_SYS_VENDOR, 12414e2441c0SJ Witteveen "SAMSUNG ELECTRONICS CO., LTD."), 12424e2441c0SJ Witteveen DMI_MATCH(DMI_PRODUCT_NAME, "N510"), 12434e2441c0SJ Witteveen DMI_MATCH(DMI_BOARD_NAME, "N510"), 12444e2441c0SJ Witteveen }, 12454e2441c0SJ Witteveen .callback = dmi_check_cb, 12464e2441c0SJ Witteveen }, 12474e2441c0SJ Witteveen { 12482d70b73aSGreg Kroah-Hartman .ident = "X125", 12492d70b73aSGreg Kroah-Hartman .matches = { 12502d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_SYS_VENDOR, 12512d70b73aSGreg Kroah-Hartman "SAMSUNG ELECTRONICS CO., LTD."), 12522d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_PRODUCT_NAME, "X125"), 12532d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_BOARD_NAME, "X125"), 12542d70b73aSGreg Kroah-Hartman }, 12552d70b73aSGreg Kroah-Hartman .callback = dmi_check_cb, 12562d70b73aSGreg Kroah-Hartman }, 12572d70b73aSGreg Kroah-Hartman { 12582d70b73aSGreg Kroah-Hartman .ident = "X120/X170", 12592d70b73aSGreg Kroah-Hartman .matches = { 12602d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_SYS_VENDOR, 12612d70b73aSGreg Kroah-Hartman "SAMSUNG ELECTRONICS CO., LTD."), 12622d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_PRODUCT_NAME, "X120/X170"), 12632d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_BOARD_NAME, "X120/X170"), 12642d70b73aSGreg Kroah-Hartman }, 12652d70b73aSGreg Kroah-Hartman .callback = dmi_check_cb, 12662d70b73aSGreg Kroah-Hartman }, 12672d70b73aSGreg Kroah-Hartman { 12682d70b73aSGreg Kroah-Hartman .ident = "NC10", 12692d70b73aSGreg Kroah-Hartman .matches = { 12702d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_SYS_VENDOR, 12712d70b73aSGreg Kroah-Hartman "SAMSUNG ELECTRONICS CO., LTD."), 12722d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_PRODUCT_NAME, "NC10"), 12732d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_BOARD_NAME, "NC10"), 12742d70b73aSGreg Kroah-Hartman }, 12752d70b73aSGreg Kroah-Hartman .callback = dmi_check_cb, 12762d70b73aSGreg Kroah-Hartman }, 12772d70b73aSGreg Kroah-Hartman { 12782d70b73aSGreg Kroah-Hartman .ident = "NP-Q45", 12792d70b73aSGreg Kroah-Hartman .matches = { 12802d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_SYS_VENDOR, 12812d70b73aSGreg Kroah-Hartman "SAMSUNG ELECTRONICS CO., LTD."), 12822d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_PRODUCT_NAME, "SQ45S70S"), 12832d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_BOARD_NAME, "SQ45S70S"), 12842d70b73aSGreg Kroah-Hartman }, 12852d70b73aSGreg Kroah-Hartman .callback = dmi_check_cb, 12862d70b73aSGreg Kroah-Hartman }, 12872d70b73aSGreg Kroah-Hartman { 12882d70b73aSGreg Kroah-Hartman .ident = "X360", 12892d70b73aSGreg Kroah-Hartman .matches = { 12902d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_SYS_VENDOR, 12912d70b73aSGreg Kroah-Hartman "SAMSUNG ELECTRONICS CO., LTD."), 12922d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_PRODUCT_NAME, "X360"), 12932d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_BOARD_NAME, "X360"), 12942d70b73aSGreg Kroah-Hartman }, 12952d70b73aSGreg Kroah-Hartman .callback = dmi_check_cb, 12962d70b73aSGreg Kroah-Hartman }, 12972d70b73aSGreg Kroah-Hartman { 12983d536ed4SAlberto Mardegan .ident = "R410 Plus", 12993d536ed4SAlberto Mardegan .matches = { 13003d536ed4SAlberto Mardegan DMI_MATCH(DMI_SYS_VENDOR, 13013d536ed4SAlberto Mardegan "SAMSUNG ELECTRONICS CO., LTD."), 13023d536ed4SAlberto Mardegan DMI_MATCH(DMI_PRODUCT_NAME, "R410P"), 13033d536ed4SAlberto Mardegan DMI_MATCH(DMI_BOARD_NAME, "R460"), 13043d536ed4SAlberto Mardegan }, 13053d536ed4SAlberto Mardegan .callback = dmi_check_cb, 13063d536ed4SAlberto Mardegan }, 13073d536ed4SAlberto Mardegan { 13082d70b73aSGreg Kroah-Hartman .ident = "R518", 13092d70b73aSGreg Kroah-Hartman .matches = { 13102d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_SYS_VENDOR, 13112d70b73aSGreg Kroah-Hartman "SAMSUNG ELECTRONICS CO., LTD."), 13122d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_PRODUCT_NAME, "R518"), 13132d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_BOARD_NAME, "R518"), 13142d70b73aSGreg Kroah-Hartman }, 13152d70b73aSGreg Kroah-Hartman .callback = dmi_check_cb, 13162d70b73aSGreg Kroah-Hartman }, 13172d70b73aSGreg Kroah-Hartman { 13182d70b73aSGreg Kroah-Hartman .ident = "R519/R719", 13192d70b73aSGreg Kroah-Hartman .matches = { 13202d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_SYS_VENDOR, 13212d70b73aSGreg Kroah-Hartman "SAMSUNG ELECTRONICS CO., LTD."), 13222d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_PRODUCT_NAME, "R519/R719"), 13232d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_BOARD_NAME, "R519/R719"), 13242d70b73aSGreg Kroah-Hartman }, 13252d70b73aSGreg Kroah-Hartman .callback = dmi_check_cb, 13262d70b73aSGreg Kroah-Hartman }, 13272d70b73aSGreg Kroah-Hartman { 132878a7539bSThomas Courbon .ident = "N150/N210/N220", 132978a7539bSThomas Courbon .matches = { 133078a7539bSThomas Courbon DMI_MATCH(DMI_SYS_VENDOR, 133178a7539bSThomas Courbon "SAMSUNG ELECTRONICS CO., LTD."), 133278a7539bSThomas Courbon DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220"), 133378a7539bSThomas Courbon DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220"), 133478a7539bSThomas Courbon }, 133578a7539bSThomas Courbon .callback = dmi_check_cb, 133678a7539bSThomas Courbon }, 133778a7539bSThomas Courbon { 1338f689c875SRaul Gutierrez Segales .ident = "N220", 1339f689c875SRaul Gutierrez Segales .matches = { 1340f689c875SRaul Gutierrez Segales DMI_MATCH(DMI_SYS_VENDOR, 1341f689c875SRaul Gutierrez Segales "SAMSUNG ELECTRONICS CO., LTD."), 1342f689c875SRaul Gutierrez Segales DMI_MATCH(DMI_PRODUCT_NAME, "N220"), 1343f689c875SRaul Gutierrez Segales DMI_MATCH(DMI_BOARD_NAME, "N220"), 1344f689c875SRaul Gutierrez Segales }, 1345f689c875SRaul Gutierrez Segales .callback = dmi_check_cb, 1346f689c875SRaul Gutierrez Segales }, 1347f689c875SRaul Gutierrez Segales { 134810165072SGreg Kroah-Hartman .ident = "N150/N210/N220/N230", 13492d70b73aSGreg Kroah-Hartman .matches = { 13502d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_SYS_VENDOR, 13512d70b73aSGreg Kroah-Hartman "SAMSUNG ELECTRONICS CO., LTD."), 135210165072SGreg Kroah-Hartman DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220/N230"), 135310165072SGreg Kroah-Hartman DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220/N230"), 13542d70b73aSGreg Kroah-Hartman }, 13552d70b73aSGreg Kroah-Hartman .callback = dmi_check_cb, 13562d70b73aSGreg Kroah-Hartman }, 13572d70b73aSGreg Kroah-Hartman { 13582d70b73aSGreg Kroah-Hartman .ident = "N150P/N210P/N220P", 13592d70b73aSGreg Kroah-Hartman .matches = { 13602d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_SYS_VENDOR, 13612d70b73aSGreg Kroah-Hartman "SAMSUNG ELECTRONICS CO., LTD."), 13622d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_PRODUCT_NAME, "N150P/N210P/N220P"), 13632d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_BOARD_NAME, "N150P/N210P/N220P"), 13642d70b73aSGreg Kroah-Hartman }, 13652d70b73aSGreg Kroah-Hartman .callback = dmi_check_cb, 13662d70b73aSGreg Kroah-Hartman }, 13672d70b73aSGreg Kroah-Hartman { 1368f87d0299SStefan Beller .ident = "R700", 1369f87d0299SStefan Beller .matches = { 1370f87d0299SStefan Beller DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1371f87d0299SStefan Beller DMI_MATCH(DMI_PRODUCT_NAME, "SR700"), 1372f87d0299SStefan Beller DMI_MATCH(DMI_BOARD_NAME, "SR700"), 1373f87d0299SStefan Beller }, 1374f87d0299SStefan Beller .callback = dmi_check_cb, 1375f87d0299SStefan Beller }, 1376f87d0299SStefan Beller { 13772d70b73aSGreg Kroah-Hartman .ident = "R530/R730", 13782d70b73aSGreg Kroah-Hartman .matches = { 13792d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 13802d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_PRODUCT_NAME, "R530/R730"), 13812d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_BOARD_NAME, "R530/R730"), 13822d70b73aSGreg Kroah-Hartman }, 13832d70b73aSGreg Kroah-Hartman .callback = dmi_check_cb, 13842d70b73aSGreg Kroah-Hartman }, 13852d70b73aSGreg Kroah-Hartman { 13862d70b73aSGreg Kroah-Hartman .ident = "NF110/NF210/NF310", 13872d70b73aSGreg Kroah-Hartman .matches = { 13882d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 13892d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_PRODUCT_NAME, "NF110/NF210/NF310"), 13902d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_BOARD_NAME, "NF110/NF210/NF310"), 13912d70b73aSGreg Kroah-Hartman }, 13922d70b73aSGreg Kroah-Hartman .callback = dmi_check_cb, 13932d70b73aSGreg Kroah-Hartman }, 13942d70b73aSGreg Kroah-Hartman { 13952d70b73aSGreg Kroah-Hartman .ident = "N145P/N250P/N260P", 13962d70b73aSGreg Kroah-Hartman .matches = { 13972d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 13982d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_PRODUCT_NAME, "N145P/N250P/N260P"), 13992d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_BOARD_NAME, "N145P/N250P/N260P"), 14002d70b73aSGreg Kroah-Hartman }, 14012d70b73aSGreg Kroah-Hartman .callback = dmi_check_cb, 14022d70b73aSGreg Kroah-Hartman }, 14032d70b73aSGreg Kroah-Hartman { 14042d70b73aSGreg Kroah-Hartman .ident = "R70/R71", 14052d70b73aSGreg Kroah-Hartman .matches = { 14062d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_SYS_VENDOR, 14072d70b73aSGreg Kroah-Hartman "SAMSUNG ELECTRONICS CO., LTD."), 14082d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_PRODUCT_NAME, "R70/R71"), 14092d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_BOARD_NAME, "R70/R71"), 14102d70b73aSGreg Kroah-Hartman }, 14112d70b73aSGreg Kroah-Hartman .callback = dmi_check_cb, 14122d70b73aSGreg Kroah-Hartman }, 14132d70b73aSGreg Kroah-Hartman { 14142d70b73aSGreg Kroah-Hartman .ident = "P460", 14152d70b73aSGreg Kroah-Hartman .matches = { 14162d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 14172d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_PRODUCT_NAME, "P460"), 14182d70b73aSGreg Kroah-Hartman DMI_MATCH(DMI_BOARD_NAME, "P460"), 14192d70b73aSGreg Kroah-Hartman }, 14202d70b73aSGreg Kroah-Hartman .callback = dmi_check_cb, 14212d70b73aSGreg Kroah-Hartman }, 1422093ed561SSmelov Andrey { 1423093ed561SSmelov Andrey .ident = "R528/R728", 1424093ed561SSmelov Andrey .matches = { 1425093ed561SSmelov Andrey DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1426093ed561SSmelov Andrey DMI_MATCH(DMI_PRODUCT_NAME, "R528/R728"), 1427093ed561SSmelov Andrey DMI_MATCH(DMI_BOARD_NAME, "R528/R728"), 1428093ed561SSmelov Andrey }, 1429093ed561SSmelov Andrey .callback = dmi_check_cb, 1430093ed561SSmelov Andrey }, 14317b3c257cSJason Stubbs { 14327b3c257cSJason Stubbs .ident = "NC210/NC110", 14337b3c257cSJason Stubbs .matches = { 14347b3c257cSJason Stubbs DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 14357b3c257cSJason Stubbs DMI_MATCH(DMI_PRODUCT_NAME, "NC210/NC110"), 14367b3c257cSJason Stubbs DMI_MATCH(DMI_BOARD_NAME, "NC210/NC110"), 14377b3c257cSJason Stubbs }, 14387b3c257cSJason Stubbs .callback = dmi_check_cb, 14397b3c257cSJason Stubbs }, 14407500eeb0STommaso Massimi { 14417500eeb0STommaso Massimi .ident = "X520", 14427500eeb0STommaso Massimi .matches = { 14437500eeb0STommaso Massimi DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 14447500eeb0STommaso Massimi DMI_MATCH(DMI_PRODUCT_NAME, "X520"), 14457500eeb0STommaso Massimi DMI_MATCH(DMI_BOARD_NAME, "X520"), 14467500eeb0STommaso Massimi }, 14477500eeb0STommaso Massimi .callback = dmi_check_cb, 14487500eeb0STommaso Massimi }, 14492d70b73aSGreg Kroah-Hartman { }, 14502d70b73aSGreg Kroah-Hartman }; 14512d70b73aSGreg Kroah-Hartman MODULE_DEVICE_TABLE(dmi, samsung_dmi_table); 14522d70b73aSGreg Kroah-Hartman 14535dea7a20SCorentin Chary static struct platform_device *samsung_platform_device; 14542d70b73aSGreg Kroah-Hartman 14552d70b73aSGreg Kroah-Hartman static int __init samsung_init(void) 14562d70b73aSGreg Kroah-Hartman { 14575dea7a20SCorentin Chary struct samsung_laptop *samsung; 14585dea7a20SCorentin Chary int ret; 14592d70b73aSGreg Kroah-Hartman 14602d70b73aSGreg Kroah-Hartman if (!force && !dmi_check_system(samsung_dmi_table)) 14612d70b73aSGreg Kroah-Hartman return -ENODEV; 14622d70b73aSGreg Kroah-Hartman 1463a6df4894SCorentin Chary samsung = kzalloc(sizeof(*samsung), GFP_KERNEL); 1464a6df4894SCorentin Chary if (!samsung) 1465a6df4894SCorentin Chary return -ENOMEM; 1466a6df4894SCorentin Chary 1467a6df4894SCorentin Chary mutex_init(&samsung->sabi_mutex); 1468f34cd9caSCorentin Chary samsung->handle_backlight = true; 1469f34cd9caSCorentin Chary 1470f34cd9caSCorentin Chary #ifdef CONFIG_ACPI 1471f34cd9caSCorentin Chary /* Don't handle backlight here if the acpi video already handle it */ 1472f34cd9caSCorentin Chary if (acpi_video_backlight_support()) { 1473f34cd9caSCorentin Chary pr_info("Backlight controlled by ACPI video driver\n"); 1474f34cd9caSCorentin Chary samsung->handle_backlight = false; 1475f34cd9caSCorentin Chary } 1476f34cd9caSCorentin Chary #endif 1477a6df4894SCorentin Chary 14785dea7a20SCorentin Chary ret = samsung_platform_init(samsung); 14795dea7a20SCorentin Chary if (ret) 14805dea7a20SCorentin Chary goto error_platform; 14812d70b73aSGreg Kroah-Hartman 14825dea7a20SCorentin Chary ret = samsung_sabi_init(samsung); 14835dea7a20SCorentin Chary if (ret) 14845dea7a20SCorentin Chary goto error_sabi; 14852d70b73aSGreg Kroah-Hartman 14865dea7a20SCorentin Chary ret = samsung_sysfs_init(samsung); 14875dea7a20SCorentin Chary if (ret) 14885dea7a20SCorentin Chary goto error_sysfs; 14892d70b73aSGreg Kroah-Hartman 14905dea7a20SCorentin Chary ret = samsung_backlight_init(samsung); 14915dea7a20SCorentin Chary if (ret) 14925dea7a20SCorentin Chary goto error_backlight; 1493a6df4894SCorentin Chary 14945dea7a20SCorentin Chary ret = samsung_rfkill_init(samsung); 14955dea7a20SCorentin Chary if (ret) 14965dea7a20SCorentin Chary goto error_rfkill; 14972d70b73aSGreg Kroah-Hartman 1498f674ebf1SCorentin Chary ret = samsung_leds_init(samsung); 1499f674ebf1SCorentin Chary if (ret) 1500f674ebf1SCorentin Chary goto error_leds; 1501f674ebf1SCorentin Chary 15025b80fc40SCorentin Chary ret = samsung_debugfs_init(samsung); 15035b80fc40SCorentin Chary if (ret) 15045b80fc40SCorentin Chary goto error_debugfs; 15055b80fc40SCorentin Chary 15065dea7a20SCorentin Chary samsung_platform_device = samsung->platform_device; 15075dea7a20SCorentin Chary return ret; 15082d70b73aSGreg Kroah-Hartman 15095b80fc40SCorentin Chary error_debugfs: 1510f674ebf1SCorentin Chary samsung_leds_exit(samsung); 1511f674ebf1SCorentin Chary error_leds: 15125b80fc40SCorentin Chary samsung_rfkill_exit(samsung); 15135dea7a20SCorentin Chary error_rfkill: 15145dea7a20SCorentin Chary samsung_backlight_exit(samsung); 15155dea7a20SCorentin Chary error_backlight: 15165dea7a20SCorentin Chary samsung_sysfs_exit(samsung); 15175dea7a20SCorentin Chary error_sysfs: 15185dea7a20SCorentin Chary samsung_sabi_exit(samsung); 15195dea7a20SCorentin Chary error_sabi: 15205dea7a20SCorentin Chary samsung_platform_exit(samsung); 15215dea7a20SCorentin Chary error_platform: 1522a6df4894SCorentin Chary kfree(samsung); 15235dea7a20SCorentin Chary return ret; 15242d70b73aSGreg Kroah-Hartman } 15252d70b73aSGreg Kroah-Hartman 15262d70b73aSGreg Kroah-Hartman static void __exit samsung_exit(void) 15272d70b73aSGreg Kroah-Hartman { 15285dea7a20SCorentin Chary struct samsung_laptop *samsung; 15292d70b73aSGreg Kroah-Hartman 15305dea7a20SCorentin Chary samsung = platform_get_drvdata(samsung_platform_device); 1531a6df4894SCorentin Chary 15325b80fc40SCorentin Chary samsung_debugfs_exit(samsung); 1533f674ebf1SCorentin Chary samsung_leds_exit(samsung); 15345dea7a20SCorentin Chary samsung_rfkill_exit(samsung); 15355dea7a20SCorentin Chary samsung_backlight_exit(samsung); 15365dea7a20SCorentin Chary samsung_sysfs_exit(samsung); 15375dea7a20SCorentin Chary samsung_sabi_exit(samsung); 15385dea7a20SCorentin Chary samsung_platform_exit(samsung); 15395dea7a20SCorentin Chary 1540a6df4894SCorentin Chary kfree(samsung); 15415dea7a20SCorentin Chary samsung_platform_device = NULL; 15422d70b73aSGreg Kroah-Hartman } 15432d70b73aSGreg Kroah-Hartman 15442d70b73aSGreg Kroah-Hartman module_init(samsung_init); 15452d70b73aSGreg Kroah-Hartman module_exit(samsung_exit); 15462d70b73aSGreg Kroah-Hartman 15472d70b73aSGreg Kroah-Hartman MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@suse.de>"); 15482d70b73aSGreg Kroah-Hartman MODULE_DESCRIPTION("Samsung Backlight driver"); 15492d70b73aSGreg Kroah-Hartman MODULE_LICENSE("GPL"); 1550