2 * Samsung Laptop driver
4 * Copyright (C) 2009,2011 Greg Kroah-Hartman (gregkh@suse.de)
5 * Copyright (C) 2009,2011 Novell Inc.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published by
9 * the Free Software Foundation.
12 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
14 #include <linux/kernel.h>
15 #include <linux/init.h>
16 #include <linux/module.h>
17 #include <linux/delay.h>
18 #include <linux/pci.h>
19 #include <linux/backlight.h>
20 #include <linux/leds.h>
22 #include <linux/dmi.h>
23 #include <linux/platform_device.h>
24 #include <linux/rfkill.h>
25 #include <linux/acpi.h>
26 #include <linux/seq_file.h>
27 #include <linux/debugfs.h>
30 * This driver is needed because a number of Samsung laptops do not hook
31 * their control settings through ACPI. So we have to poke around in the
32 * BIOS to do things like brightness values, and "special" key controls.
36 * We have 0 - 8 as valid brightness levels. The specs say that level 0 should
37 * be reserved by the BIOS (which really doesn't make much sense), we tell
38 * userspace that the value is 0 - 7 and then just tell the hardware 1 - 8
40 #define MAX_BRIGHT 0x07
43 #define SABI_IFACE_MAIN 0x00
44 #define SABI_IFACE_SUB 0x02
45 #define SABI_IFACE_COMPLETE 0x04
46 #define SABI_IFACE_DATA 0x05
48 /* Structure get/set data using sabi */
61 struct sabi_header_offsets {
70 struct sabi_commands {
72 * Brightness is 0 - 8, as described above.
73 * Value 0 is for the BIOS to use
80 * 0x00 - wireless is off
81 * 0x01 - wireless is on
85 * TODO, verify 3G is correct, that doesn't seem right...
87 u16 get_wireless_button;
88 u16 set_wireless_button;
90 /* 0 is off, 1 is on */
95 * 0x80 or 0x00 - no action
96 * 0x81 - recovery key pressed
98 u16 get_recovery_mode;
99 u16 set_recovery_mode;
102 * on seclinux: 0 is low, 1 is high,
103 * on swsmi: 0 is normal, 1 is silent, 2 is turbo
105 u16 get_performance_level;
106 u16 set_performance_level;
108 /* 0x80 is off, 0x81 is on */
109 u16 get_battery_life_extender;
110 u16 set_battery_life_extender;
112 /* 0x80 is off, 0x81 is on */
116 /* 0x81 to read, (0x82 | level << 8) to set, 0xaabb to enable */
120 * Tell the BIOS that Linux is running on this machine.
121 * 81 is on, 80 is off
126 struct sabi_performance_level {
132 const char *test_string;
134 const struct sabi_header_offsets header_offsets;
135 const struct sabi_commands commands;
136 const struct sabi_performance_level performance_levels[4];
141 static const struct sabi_config sabi_configs[] = {
143 .test_string = "SECLINUX",
145 .main_function = 0x4c49,
153 .data_segment = 0x07,
157 .get_brightness = 0x00,
158 .set_brightness = 0x01,
160 .get_wireless_button = 0x02,
161 .set_wireless_button = 0x03,
163 .get_backlight = 0x04,
164 .set_backlight = 0x05,
166 .get_recovery_mode = 0x06,
167 .set_recovery_mode = 0x07,
169 .get_performance_level = 0x08,
170 .set_performance_level = 0x09,
172 .get_battery_life_extender = 0xFFFF,
173 .set_battery_life_extender = 0xFFFF,
175 .get_usb_charge = 0xFFFF,
176 .set_usb_charge = 0xFFFF,
178 .kbd_backlight = 0xFFFF,
183 .performance_levels = {
198 .test_string = "SwSmi@",
200 .main_function = 0x5843,
208 .data_segment = 0x07,
212 .get_brightness = 0x10,
213 .set_brightness = 0x11,
215 .get_wireless_button = 0x12,
216 .set_wireless_button = 0x13,
218 .get_backlight = 0x2d,
219 .set_backlight = 0x2e,
221 .get_recovery_mode = 0xff,
222 .set_recovery_mode = 0xff,
224 .get_performance_level = 0x31,
225 .set_performance_level = 0x32,
227 .get_battery_life_extender = 0x65,
228 .set_battery_life_extender = 0x66,
230 .get_usb_charge = 0x67,
231 .set_usb_charge = 0x68,
233 .kbd_backlight = 0x78,
238 .performance_levels = {
260 * samsung-laptop/ - debugfs root directory
261 * f0000_segment - dump f0000 segment
262 * command - current command
263 * data - current data
264 * d0, d1, d2, d3 - data fields
265 * call - call SABI using command and data
267 * This allow to call arbitrary sabi commands wihout
268 * modifying the driver at all.
269 * For example, setting the keyboard backlight brightness to 5
271 * echo 0x78 > command
279 struct samsung_laptop_debug {
281 struct sabi_data data;
284 struct debugfs_blob_wrapper f0000_wrapper;
285 struct debugfs_blob_wrapper data_wrapper;
288 struct samsung_laptop {
289 const struct sabi_config *config;
292 void __iomem *sabi_iface;
293 void __iomem *f0000_segment;
295 struct mutex sabi_mutex;
297 struct platform_device *platform_device;
298 struct backlight_device *backlight_device;
301 struct led_classdev kbd_led;
303 struct workqueue_struct *led_workqueue;
304 struct work_struct kbd_led_work;
306 struct samsung_laptop_debug debug;
308 bool handle_backlight;
309 bool has_stepping_quirk;
315 module_param(force, bool, 0);
316 MODULE_PARM_DESC(force,
317 "Disable the DMI check and forces the driver to be loaded");
320 module_param(debug, bool, S_IRUGO | S_IWUSR);
321 MODULE_PARM_DESC(debug, "Debug enabled or not");
323 static int sabi_command(struct samsung_laptop *samsung, u16 command,
324 struct sabi_data *in,
325 struct sabi_data *out)
327 const struct sabi_config *config = samsung->config;
329 u16 port = readw(samsung->sabi + config->header_offsets.port);
330 u8 complete, iface_data;
332 mutex_lock(&samsung->sabi_mutex);
336 pr_info("SABI 0x%04x {0x%08x, 0x%08x, 0x%04x, 0x%02x}",
337 command, in->d0, in->d1, in->d2, in->d3);
339 pr_info("SABI 0x%04x", command);
342 /* enable memory to be able to write to it */
343 outb(readb(samsung->sabi + config->header_offsets.en_mem), port);
345 /* write out the command */
346 writew(config->main_function, samsung->sabi_iface + SABI_IFACE_MAIN);
347 writew(command, samsung->sabi_iface + SABI_IFACE_SUB);
348 writeb(0, samsung->sabi_iface + SABI_IFACE_COMPLETE);
350 writel(in->d0, samsung->sabi_iface + SABI_IFACE_DATA);
351 writel(in->d1, samsung->sabi_iface + SABI_IFACE_DATA + 4);
352 writew(in->d2, samsung->sabi_iface + SABI_IFACE_DATA + 8);
353 writeb(in->d3, samsung->sabi_iface + SABI_IFACE_DATA + 10);
355 outb(readb(samsung->sabi + config->header_offsets.iface_func), port);
357 /* write protect memory to make it safe */
358 outb(readb(samsung->sabi + config->header_offsets.re_mem), port);
360 /* see if the command actually succeeded */
361 complete = readb(samsung->sabi_iface + SABI_IFACE_COMPLETE);
362 iface_data = readb(samsung->sabi_iface + SABI_IFACE_DATA);
363 if (complete != 0xaa || iface_data == 0xff) {
364 pr_warn("SABI command 0x%04x failed with"
365 " completion flag 0x%02x and interface data 0x%02x",
366 command, complete, iface_data);
372 out->d0 = readl(samsung->sabi_iface + SABI_IFACE_DATA);
373 out->d1 = readl(samsung->sabi_iface + SABI_IFACE_DATA + 4);
374 out->d2 = readw(samsung->sabi_iface + SABI_IFACE_DATA + 2);
375 out->d3 = readb(samsung->sabi_iface + SABI_IFACE_DATA + 1);
379 pr_info("SABI {0x%08x, 0x%08x, 0x%04x, 0x%02x}",
380 out->d0, out->d1, out->d2, out->d3);
384 mutex_unlock(&samsung->sabi_mutex);
388 /* simple wrappers usable with most commands */
389 static int sabi_set_commandb(struct samsung_laptop *samsung,
390 u16 command, u8 data)
392 struct sabi_data in = { .d0 = 0, .d1 = 0, .d2 = 0, .d3 = 0 };
395 return sabi_command(samsung, command, &in, NULL);
398 static int read_brightness(struct samsung_laptop *samsung)
400 const struct sabi_config *config = samsung->config;
401 const struct sabi_commands *commands = &samsung->config->commands;
402 struct sabi_data sretval;
403 int user_brightness = 0;
406 retval = sabi_command(samsung, commands->get_brightness,
411 user_brightness = sretval.data[0];
412 if (user_brightness > config->min_brightness)
413 user_brightness -= config->min_brightness;
417 return user_brightness;
420 static void set_brightness(struct samsung_laptop *samsung, u8 user_brightness)
422 const struct sabi_config *config = samsung->config;
423 const struct sabi_commands *commands = &samsung->config->commands;
424 u8 user_level = user_brightness + config->min_brightness;
426 if (samsung->has_stepping_quirk && user_level != 0) {
428 * short circuit if the specified level is what's already set
429 * to prevent the screen from flickering needlessly
431 if (user_brightness == read_brightness(samsung))
434 sabi_set_commandb(samsung, commands->set_brightness, 0);
437 sabi_set_commandb(samsung, commands->set_brightness, user_level);
440 static int get_brightness(struct backlight_device *bd)
442 struct samsung_laptop *samsung = bl_get_data(bd);
444 return read_brightness(samsung);
447 static void check_for_stepping_quirk(struct samsung_laptop *samsung)
451 int orig_level = read_brightness(samsung);
454 * Some laptops exhibit the strange behaviour of stepping toward
455 * (rather than setting) the brightness except when changing to/from
456 * brightness level 0. This behaviour is checked for here and worked
457 * around in set_brightness.
461 set_brightness(samsung, 1);
463 initial_level = read_brightness(samsung);
465 if (initial_level <= 2)
466 check_level = initial_level + 2;
468 check_level = initial_level - 2;
470 samsung->has_stepping_quirk = false;
471 set_brightness(samsung, check_level);
473 if (read_brightness(samsung) != check_level) {
474 samsung->has_stepping_quirk = true;
475 pr_info("enabled workaround for brightness stepping quirk\n");
478 set_brightness(samsung, orig_level);
481 static int update_status(struct backlight_device *bd)
483 struct samsung_laptop *samsung = bl_get_data(bd);
484 const struct sabi_commands *commands = &samsung->config->commands;
486 set_brightness(samsung, bd->props.brightness);
488 if (bd->props.power == FB_BLANK_UNBLANK)
489 sabi_set_commandb(samsung, commands->set_backlight, 1);
491 sabi_set_commandb(samsung, commands->set_backlight, 0);
496 static const struct backlight_ops backlight_ops = {
497 .get_brightness = get_brightness,
498 .update_status = update_status,
501 static int rfkill_set(void *data, bool blocked)
503 struct samsung_laptop *samsung = data;
504 const struct sabi_commands *commands = &samsung->config->commands;
506 /* Do something with blocked...*/
508 * blocked == false is on
509 * blocked == true is off
512 sabi_set_commandb(samsung, commands->set_wireless_button, 0);
514 sabi_set_commandb(samsung, commands->set_wireless_button, 1);
519 static struct rfkill_ops rfkill_ops = {
520 .set_block = rfkill_set,
523 static ssize_t get_performance_level(struct device *dev,
524 struct device_attribute *attr, char *buf)
526 struct samsung_laptop *samsung = dev_get_drvdata(dev);
527 const struct sabi_config *config = samsung->config;
528 const struct sabi_commands *commands = &config->commands;
529 struct sabi_data sretval;
534 retval = sabi_command(samsung, commands->get_performance_level,
539 /* The logic is backwards, yeah, lots of fun... */
540 for (i = 0; config->performance_levels[i].name; ++i) {
541 if (sretval.data[0] == config->performance_levels[i].value)
542 return sprintf(buf, "%s\n", config->performance_levels[i].name);
544 return sprintf(buf, "%s\n", "unknown");
547 static ssize_t set_performance_level(struct device *dev,
548 struct device_attribute *attr, const char *buf,
551 struct samsung_laptop *samsung = dev_get_drvdata(dev);
552 const struct sabi_config *config = samsung->config;
553 const struct sabi_commands *commands = &config->commands;
559 for (i = 0; config->performance_levels[i].name; ++i) {
560 const struct sabi_performance_level *level =
561 &config->performance_levels[i];
562 if (!strncasecmp(level->name, buf, strlen(level->name))) {
563 sabi_set_commandb(samsung,
564 commands->set_performance_level,
570 if (!config->performance_levels[i].name)
576 static DEVICE_ATTR(performance_level, S_IWUSR | S_IRUGO,
577 get_performance_level, set_performance_level);
579 static int read_battery_life_extender(struct samsung_laptop *samsung)
581 const struct sabi_commands *commands = &samsung->config->commands;
582 struct sabi_data data;
585 if (commands->get_battery_life_extender == 0xFFFF)
588 memset(&data, 0, sizeof(data));
590 retval = sabi_command(samsung, commands->get_battery_life_extender,
596 if (data.data[0] != 0 && data.data[0] != 1)
602 static int write_battery_life_extender(struct samsung_laptop *samsung,
605 const struct sabi_commands *commands = &samsung->config->commands;
606 struct sabi_data data;
608 memset(&data, 0, sizeof(data));
609 data.data[0] = 0x80 | enabled;
610 return sabi_command(samsung, commands->set_battery_life_extender,
614 static ssize_t get_battery_life_extender(struct device *dev,
615 struct device_attribute *attr,
618 struct samsung_laptop *samsung = dev_get_drvdata(dev);
621 ret = read_battery_life_extender(samsung);
625 return sprintf(buf, "%d\n", ret);
628 static ssize_t set_battery_life_extender(struct device *dev,
629 struct device_attribute *attr,
630 const char *buf, size_t count)
632 struct samsung_laptop *samsung = dev_get_drvdata(dev);
635 if (!count || sscanf(buf, "%i", &value) != 1)
638 ret = write_battery_life_extender(samsung, !!value);
645 static DEVICE_ATTR(battery_life_extender, S_IWUSR | S_IRUGO,
646 get_battery_life_extender, set_battery_life_extender);
648 static int read_usb_charge(struct samsung_laptop *samsung)
650 const struct sabi_commands *commands = &samsung->config->commands;
651 struct sabi_data data;
654 if (commands->get_usb_charge == 0xFFFF)
657 memset(&data, 0, sizeof(data));
659 retval = sabi_command(samsung, commands->get_usb_charge,
665 if (data.data[0] != 0 && data.data[0] != 1)
671 static int write_usb_charge(struct samsung_laptop *samsung,
674 const struct sabi_commands *commands = &samsung->config->commands;
675 struct sabi_data data;
677 memset(&data, 0, sizeof(data));
678 data.data[0] = 0x80 | enabled;
679 return sabi_command(samsung, commands->set_usb_charge,
683 static ssize_t get_usb_charge(struct device *dev,
684 struct device_attribute *attr,
687 struct samsung_laptop *samsung = dev_get_drvdata(dev);
690 ret = read_usb_charge(samsung);
694 return sprintf(buf, "%d\n", ret);
697 static ssize_t set_usb_charge(struct device *dev,
698 struct device_attribute *attr,
699 const char *buf, size_t count)
701 struct samsung_laptop *samsung = dev_get_drvdata(dev);
704 if (!count || sscanf(buf, "%i", &value) != 1)
707 ret = write_usb_charge(samsung, !!value);
714 static DEVICE_ATTR(usb_charge, S_IWUSR | S_IRUGO,
715 get_usb_charge, set_usb_charge);
717 static struct attribute *platform_attributes[] = {
718 &dev_attr_performance_level.attr,
719 &dev_attr_battery_life_extender.attr,
720 &dev_attr_usb_charge.attr,
724 static int find_signature(void __iomem *memcheck, const char *testStr)
729 for (loca = 0; loca < 0xffff; loca++) {
730 char temp = readb(memcheck + loca);
732 if (temp == testStr[i]) {
733 if (i == strlen(testStr)-1)
743 static void samsung_rfkill_exit(struct samsung_laptop *samsung)
746 rfkill_unregister(samsung->rfk);
747 rfkill_destroy(samsung->rfk);
752 static int __init samsung_rfkill_init(struct samsung_laptop *samsung)
756 samsung->rfk = rfkill_alloc("samsung-wifi",
757 &samsung->platform_device->dev,
759 &rfkill_ops, samsung);
763 retval = rfkill_register(samsung->rfk);
765 rfkill_destroy(samsung->rfk);
773 static int kbd_backlight_enable(struct samsung_laptop *samsung)
775 const struct sabi_commands *commands = &samsung->config->commands;
776 struct sabi_data data;
779 if (commands->kbd_backlight == 0xFFFF)
782 memset(&data, 0, sizeof(data));
784 retval = sabi_command(samsung, commands->kbd_backlight,
790 if (data.d0 != 0xccdd)
795 static int kbd_backlight_read(struct samsung_laptop *samsung)
797 const struct sabi_commands *commands = &samsung->config->commands;
798 struct sabi_data data;
801 memset(&data, 0, sizeof(data));
803 retval = sabi_command(samsung, commands->kbd_backlight,
812 static int kbd_backlight_write(struct samsung_laptop *samsung, int brightness)
814 const struct sabi_commands *commands = &samsung->config->commands;
815 struct sabi_data data;
817 memset(&data, 0, sizeof(data));
818 data.d0 = 0x82 | ((brightness & 0xFF) << 8);
819 return sabi_command(samsung, commands->kbd_backlight,
823 static void kbd_led_update(struct work_struct *work)
825 struct samsung_laptop *samsung;
827 samsung = container_of(work, struct samsung_laptop, kbd_led_work);
828 kbd_backlight_write(samsung, samsung->kbd_led_wk);
831 static void kbd_led_set(struct led_classdev *led_cdev,
832 enum led_brightness value)
834 struct samsung_laptop *samsung;
836 samsung = container_of(led_cdev, struct samsung_laptop, kbd_led);
838 if (value > samsung->kbd_led.max_brightness)
839 value = samsung->kbd_led.max_brightness;
843 samsung->kbd_led_wk = value;
844 queue_work(samsung->led_workqueue, &samsung->kbd_led_work);
847 static enum led_brightness kbd_led_get(struct led_classdev *led_cdev)
849 struct samsung_laptop *samsung;
851 samsung = container_of(led_cdev, struct samsung_laptop, kbd_led);
852 return kbd_backlight_read(samsung);
855 static void samsung_leds_exit(struct samsung_laptop *samsung)
857 if (!IS_ERR_OR_NULL(samsung->kbd_led.dev))
858 led_classdev_unregister(&samsung->kbd_led);
859 if (samsung->led_workqueue)
860 destroy_workqueue(samsung->led_workqueue);
863 static int __init samsung_leds_init(struct samsung_laptop *samsung)
867 samsung->led_workqueue = create_singlethread_workqueue("led_workqueue");
868 if (!samsung->led_workqueue)
871 if (kbd_backlight_enable(samsung) >= 0) {
872 INIT_WORK(&samsung->kbd_led_work, kbd_led_update);
874 samsung->kbd_led.name = "samsung::kbd_backlight";
875 samsung->kbd_led.brightness_set = kbd_led_set;
876 samsung->kbd_led.brightness_get = kbd_led_get;
877 samsung->kbd_led.max_brightness = 8;
879 ret = led_classdev_register(&samsung->platform_device->dev,
884 samsung_leds_exit(samsung);
889 static void samsung_backlight_exit(struct samsung_laptop *samsung)
891 if (samsung->backlight_device) {
892 backlight_device_unregister(samsung->backlight_device);
893 samsung->backlight_device = NULL;
897 static int __init samsung_backlight_init(struct samsung_laptop *samsung)
899 struct backlight_device *bd;
900 struct backlight_properties props;
902 if (!samsung->handle_backlight)
905 memset(&props, 0, sizeof(struct backlight_properties));
906 props.type = BACKLIGHT_PLATFORM;
907 props.max_brightness = samsung->config->max_brightness -
908 samsung->config->min_brightness;
910 bd = backlight_device_register("samsung",
911 &samsung->platform_device->dev,
912 samsung, &backlight_ops,
917 samsung->backlight_device = bd;
918 samsung->backlight_device->props.brightness = read_brightness(samsung);
919 samsung->backlight_device->props.power = FB_BLANK_UNBLANK;
920 backlight_update_status(samsung->backlight_device);
925 static mode_t samsung_sysfs_is_visible(struct kobject *kobj,
926 struct attribute *attr, int idx)
928 struct device *dev = container_of(kobj, struct device, kobj);
929 struct platform_device *pdev = to_platform_device(dev);
930 struct samsung_laptop *samsung = platform_get_drvdata(pdev);
933 if (attr == &dev_attr_performance_level.attr)
934 ok = !!samsung->config->performance_levels[0].name;
935 if (attr == &dev_attr_battery_life_extender.attr)
936 ok = !!(read_battery_life_extender(samsung) >= 0);
937 if (attr == &dev_attr_usb_charge.attr)
938 ok = !!(read_usb_charge(samsung) >= 0);
940 return ok ? attr->mode : 0;
943 static struct attribute_group platform_attribute_group = {
944 .is_visible = samsung_sysfs_is_visible,
945 .attrs = platform_attributes
948 static void samsung_sysfs_exit(struct samsung_laptop *samsung)
950 struct platform_device *device = samsung->platform_device;
952 sysfs_remove_group(&device->dev.kobj, &platform_attribute_group);
955 static int __init samsung_sysfs_init(struct samsung_laptop *samsung)
957 struct platform_device *device = samsung->platform_device;
959 return sysfs_create_group(&device->dev.kobj, &platform_attribute_group);
963 static int show_call(struct seq_file *m, void *data)
965 struct samsung_laptop *samsung = m->private;
966 struct sabi_data *sdata = &samsung->debug.data;
969 seq_printf(m, "SABI 0x%04x {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n",
970 samsung->debug.command,
971 sdata->d0, sdata->d1, sdata->d2, sdata->d3);
973 ret = sabi_command(samsung, samsung->debug.command, sdata, sdata);
976 seq_printf(m, "SABI command 0x%04x failed\n",
977 samsung->debug.command);
981 seq_printf(m, "SABI {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n",
982 sdata->d0, sdata->d1, sdata->d2, sdata->d3);
986 static int samsung_debugfs_open(struct inode *inode, struct file *file)
988 return single_open(file, show_call, inode->i_private);
991 static const struct file_operations samsung_laptop_call_io_ops = {
992 .owner = THIS_MODULE,
993 .open = samsung_debugfs_open,
996 .release = single_release,
999 static void samsung_debugfs_exit(struct samsung_laptop *samsung)
1001 debugfs_remove_recursive(samsung->debug.root);
1004 static int samsung_debugfs_init(struct samsung_laptop *samsung)
1006 struct dentry *dent;
1008 samsung->debug.root = debugfs_create_dir("samsung-laptop", NULL);
1009 if (!samsung->debug.root) {
1010 pr_err("failed to create debugfs directory");
1014 samsung->debug.f0000_wrapper.data = samsung->f0000_segment;
1015 samsung->debug.f0000_wrapper.size = 0xffff;
1017 samsung->debug.data_wrapper.data = &samsung->debug.data;
1018 samsung->debug.data_wrapper.size = sizeof(samsung->debug.data);
1020 dent = debugfs_create_u16("command", S_IRUGO | S_IWUSR,
1021 samsung->debug.root, &samsung->debug.command);
1025 dent = debugfs_create_u32("d0", S_IRUGO | S_IWUSR, samsung->debug.root,
1026 &samsung->debug.data.d0);
1030 dent = debugfs_create_u32("d1", S_IRUGO | S_IWUSR, samsung->debug.root,
1031 &samsung->debug.data.d1);
1035 dent = debugfs_create_u16("d2", S_IRUGO | S_IWUSR, samsung->debug.root,
1036 &samsung->debug.data.d2);
1040 dent = debugfs_create_u8("d3", S_IRUGO | S_IWUSR, samsung->debug.root,
1041 &samsung->debug.data.d3);
1045 dent = debugfs_create_blob("data", S_IRUGO | S_IWUSR,
1046 samsung->debug.root,
1047 &samsung->debug.data_wrapper);
1051 dent = debugfs_create_blob("f0000_segment", S_IRUSR | S_IWUSR,
1052 samsung->debug.root,
1053 &samsung->debug.f0000_wrapper);
1057 dent = debugfs_create_file("call", S_IFREG | S_IRUGO,
1058 samsung->debug.root, samsung,
1059 &samsung_laptop_call_io_ops);
1066 samsung_debugfs_exit(samsung);
1070 static void samsung_sabi_exit(struct samsung_laptop *samsung)
1072 const struct sabi_config *config = samsung->config;
1074 /* Turn off "Linux" mode in the BIOS */
1075 if (config && config->commands.set_linux != 0xff)
1076 sabi_set_commandb(samsung, config->commands.set_linux, 0x80);
1078 if (samsung->sabi_iface) {
1079 iounmap(samsung->sabi_iface);
1080 samsung->sabi_iface = NULL;
1082 if (samsung->f0000_segment) {
1083 iounmap(samsung->f0000_segment);
1084 samsung->f0000_segment = NULL;
1087 samsung->config = NULL;
1090 static __init void samsung_sabi_infos(struct samsung_laptop *samsung, int loca,
1091 unsigned int ifaceP)
1093 const struct sabi_config *config = samsung->config;
1095 printk(KERN_DEBUG "This computer supports SABI==%x\n",
1096 loca + 0xf0000 - 6);
1098 printk(KERN_DEBUG "SABI header:\n");
1099 printk(KERN_DEBUG " SMI Port Number = 0x%04x\n",
1100 readw(samsung->sabi + config->header_offsets.port));
1101 printk(KERN_DEBUG " SMI Interface Function = 0x%02x\n",
1102 readb(samsung->sabi + config->header_offsets.iface_func));
1103 printk(KERN_DEBUG " SMI enable memory buffer = 0x%02x\n",
1104 readb(samsung->sabi + config->header_offsets.en_mem));
1105 printk(KERN_DEBUG " SMI restore memory buffer = 0x%02x\n",
1106 readb(samsung->sabi + config->header_offsets.re_mem));
1107 printk(KERN_DEBUG " SABI data offset = 0x%04x\n",
1108 readw(samsung->sabi + config->header_offsets.data_offset));
1109 printk(KERN_DEBUG " SABI data segment = 0x%04x\n",
1110 readw(samsung->sabi + config->header_offsets.data_segment));
1112 printk(KERN_DEBUG " SABI pointer = 0x%08x\n", ifaceP);
1115 static int __init samsung_sabi_init(struct samsung_laptop *samsung)
1117 const struct sabi_config *config = NULL;
1118 const struct sabi_commands *commands;
1119 unsigned int ifaceP;
1124 samsung->f0000_segment = ioremap_nocache(0xf0000, 0xffff);
1125 if (!samsung->f0000_segment) {
1126 pr_err("Can't map the segment at 0xf0000\n");
1131 /* Try to find one of the signatures in memory to find the header */
1132 for (i = 0; sabi_configs[i].test_string != 0; ++i) {
1133 samsung->config = &sabi_configs[i];
1134 loca = find_signature(samsung->f0000_segment,
1135 samsung->config->test_string);
1140 if (loca == 0xffff) {
1141 pr_err("This computer does not support SABI\n");
1146 config = samsung->config;
1147 commands = &config->commands;
1149 /* point to the SMI port Number */
1151 samsung->sabi = (samsung->f0000_segment + loca);
1153 /* Get a pointer to the SABI Interface */
1154 ifaceP = (readw(samsung->sabi + config->header_offsets.data_segment) & 0x0ffff) << 4;
1155 ifaceP += readw(samsung->sabi + config->header_offsets.data_offset) & 0x0ffff;
1158 samsung_sabi_infos(samsung, loca, ifaceP);
1160 samsung->sabi_iface = ioremap_nocache(ifaceP, 16);
1161 if (!samsung->sabi_iface) {
1162 pr_err("Can't remap %x\n", ifaceP);
1167 /* Turn on "Linux" mode in the BIOS */
1168 if (commands->set_linux != 0xff) {
1169 int retval = sabi_set_commandb(samsung,
1170 commands->set_linux, 0x81);
1172 pr_warn("Linux mode was not set!\n");
1178 /* Check for stepping quirk */
1179 if (samsung->handle_backlight)
1180 check_for_stepping_quirk(samsung);
1184 samsung_sabi_exit(samsung);
1189 static void samsung_platform_exit(struct samsung_laptop *samsung)
1191 if (samsung->platform_device) {
1192 platform_device_unregister(samsung->platform_device);
1193 samsung->platform_device = NULL;
1197 static int __init samsung_platform_init(struct samsung_laptop *samsung)
1199 struct platform_device *pdev;
1201 pdev = platform_device_register_simple("samsung", -1, NULL, 0);
1203 return PTR_ERR(pdev);
1205 samsung->platform_device = pdev;
1206 platform_set_drvdata(samsung->platform_device, samsung);
1210 static int __init dmi_check_cb(const struct dmi_system_id *id)
1212 pr_info("found laptop model '%s'\n", id->ident);
1216 static struct dmi_system_id __initdata samsung_dmi_table[] = {
1220 DMI_MATCH(DMI_SYS_VENDOR,
1221 "SAMSUNG ELECTRONICS CO., LTD."),
1222 DMI_MATCH(DMI_PRODUCT_NAME, "N128"),
1223 DMI_MATCH(DMI_BOARD_NAME, "N128"),
1225 .callback = dmi_check_cb,
1230 DMI_MATCH(DMI_SYS_VENDOR,
1231 "SAMSUNG ELECTRONICS CO., LTD."),
1232 DMI_MATCH(DMI_PRODUCT_NAME, "N130"),
1233 DMI_MATCH(DMI_BOARD_NAME, "N130"),
1235 .callback = dmi_check_cb,
1240 DMI_MATCH(DMI_SYS_VENDOR,
1241 "SAMSUNG ELECTRONICS CO., LTD."),
1242 DMI_MATCH(DMI_PRODUCT_NAME, "N510"),
1243 DMI_MATCH(DMI_BOARD_NAME, "N510"),
1245 .callback = dmi_check_cb,
1250 DMI_MATCH(DMI_SYS_VENDOR,
1251 "SAMSUNG ELECTRONICS CO., LTD."),
1252 DMI_MATCH(DMI_PRODUCT_NAME, "X125"),
1253 DMI_MATCH(DMI_BOARD_NAME, "X125"),
1255 .callback = dmi_check_cb,
1258 .ident = "X120/X170",
1260 DMI_MATCH(DMI_SYS_VENDOR,
1261 "SAMSUNG ELECTRONICS CO., LTD."),
1262 DMI_MATCH(DMI_PRODUCT_NAME, "X120/X170"),
1263 DMI_MATCH(DMI_BOARD_NAME, "X120/X170"),
1265 .callback = dmi_check_cb,
1270 DMI_MATCH(DMI_SYS_VENDOR,
1271 "SAMSUNG ELECTRONICS CO., LTD."),
1272 DMI_MATCH(DMI_PRODUCT_NAME, "NC10"),
1273 DMI_MATCH(DMI_BOARD_NAME, "NC10"),
1275 .callback = dmi_check_cb,
1280 DMI_MATCH(DMI_SYS_VENDOR,
1281 "SAMSUNG ELECTRONICS CO., LTD."),
1282 DMI_MATCH(DMI_PRODUCT_NAME, "SQ45S70S"),
1283 DMI_MATCH(DMI_BOARD_NAME, "SQ45S70S"),
1285 .callback = dmi_check_cb,
1290 DMI_MATCH(DMI_SYS_VENDOR,
1291 "SAMSUNG ELECTRONICS CO., LTD."),
1292 DMI_MATCH(DMI_PRODUCT_NAME, "X360"),
1293 DMI_MATCH(DMI_BOARD_NAME, "X360"),
1295 .callback = dmi_check_cb,
1298 .ident = "R410 Plus",
1300 DMI_MATCH(DMI_SYS_VENDOR,
1301 "SAMSUNG ELECTRONICS CO., LTD."),
1302 DMI_MATCH(DMI_PRODUCT_NAME, "R410P"),
1303 DMI_MATCH(DMI_BOARD_NAME, "R460"),
1305 .callback = dmi_check_cb,
1310 DMI_MATCH(DMI_SYS_VENDOR,
1311 "SAMSUNG ELECTRONICS CO., LTD."),
1312 DMI_MATCH(DMI_PRODUCT_NAME, "R518"),
1313 DMI_MATCH(DMI_BOARD_NAME, "R518"),
1315 .callback = dmi_check_cb,
1318 .ident = "R519/R719",
1320 DMI_MATCH(DMI_SYS_VENDOR,
1321 "SAMSUNG ELECTRONICS CO., LTD."),
1322 DMI_MATCH(DMI_PRODUCT_NAME, "R519/R719"),
1323 DMI_MATCH(DMI_BOARD_NAME, "R519/R719"),
1325 .callback = dmi_check_cb,
1328 .ident = "N150/N210/N220",
1330 DMI_MATCH(DMI_SYS_VENDOR,
1331 "SAMSUNG ELECTRONICS CO., LTD."),
1332 DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220"),
1333 DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220"),
1335 .callback = dmi_check_cb,
1340 DMI_MATCH(DMI_SYS_VENDOR,
1341 "SAMSUNG ELECTRONICS CO., LTD."),
1342 DMI_MATCH(DMI_PRODUCT_NAME, "N220"),
1343 DMI_MATCH(DMI_BOARD_NAME, "N220"),
1345 .callback = dmi_check_cb,
1348 .ident = "N150/N210/N220/N230",
1350 DMI_MATCH(DMI_SYS_VENDOR,
1351 "SAMSUNG ELECTRONICS CO., LTD."),
1352 DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220/N230"),
1353 DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220/N230"),
1355 .callback = dmi_check_cb,
1358 .ident = "N150P/N210P/N220P",
1360 DMI_MATCH(DMI_SYS_VENDOR,
1361 "SAMSUNG ELECTRONICS CO., LTD."),
1362 DMI_MATCH(DMI_PRODUCT_NAME, "N150P/N210P/N220P"),
1363 DMI_MATCH(DMI_BOARD_NAME, "N150P/N210P/N220P"),
1365 .callback = dmi_check_cb,
1370 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
1371 DMI_MATCH(DMI_PRODUCT_NAME, "SR700"),
1372 DMI_MATCH(DMI_BOARD_NAME, "SR700"),
1374 .callback = dmi_check_cb,
1377 .ident = "R530/R730",
1379 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
1380 DMI_MATCH(DMI_PRODUCT_NAME, "R530/R730"),
1381 DMI_MATCH(DMI_BOARD_NAME, "R530/R730"),
1383 .callback = dmi_check_cb,
1386 .ident = "NF110/NF210/NF310",
1388 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
1389 DMI_MATCH(DMI_PRODUCT_NAME, "NF110/NF210/NF310"),
1390 DMI_MATCH(DMI_BOARD_NAME, "NF110/NF210/NF310"),
1392 .callback = dmi_check_cb,
1395 .ident = "N145P/N250P/N260P",
1397 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
1398 DMI_MATCH(DMI_PRODUCT_NAME, "N145P/N250P/N260P"),
1399 DMI_MATCH(DMI_BOARD_NAME, "N145P/N250P/N260P"),
1401 .callback = dmi_check_cb,
1406 DMI_MATCH(DMI_SYS_VENDOR,
1407 "SAMSUNG ELECTRONICS CO., LTD."),
1408 DMI_MATCH(DMI_PRODUCT_NAME, "R70/R71"),
1409 DMI_MATCH(DMI_BOARD_NAME, "R70/R71"),
1411 .callback = dmi_check_cb,
1416 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
1417 DMI_MATCH(DMI_PRODUCT_NAME, "P460"),
1418 DMI_MATCH(DMI_BOARD_NAME, "P460"),
1420 .callback = dmi_check_cb,
1423 .ident = "R528/R728",
1425 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
1426 DMI_MATCH(DMI_PRODUCT_NAME, "R528/R728"),
1427 DMI_MATCH(DMI_BOARD_NAME, "R528/R728"),
1429 .callback = dmi_check_cb,
1432 .ident = "NC210/NC110",
1434 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
1435 DMI_MATCH(DMI_PRODUCT_NAME, "NC210/NC110"),
1436 DMI_MATCH(DMI_BOARD_NAME, "NC210/NC110"),
1438 .callback = dmi_check_cb,
1443 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
1444 DMI_MATCH(DMI_PRODUCT_NAME, "X520"),
1445 DMI_MATCH(DMI_BOARD_NAME, "X520"),
1447 .callback = dmi_check_cb,
1451 MODULE_DEVICE_TABLE(dmi, samsung_dmi_table);
1453 static struct platform_device *samsung_platform_device;
1455 static int __init samsung_init(void)
1457 struct samsung_laptop *samsung;
1460 if (!force && !dmi_check_system(samsung_dmi_table))
1463 samsung = kzalloc(sizeof(*samsung), GFP_KERNEL);
1467 mutex_init(&samsung->sabi_mutex);
1468 samsung->handle_backlight = true;
1471 /* Don't handle backlight here if the acpi video already handle it */
1472 if (acpi_video_backlight_support()) {
1473 pr_info("Backlight controlled by ACPI video driver\n");
1474 samsung->handle_backlight = false;
1478 ret = samsung_platform_init(samsung);
1480 goto error_platform;
1482 ret = samsung_sabi_init(samsung);
1486 ret = samsung_sysfs_init(samsung);
1490 ret = samsung_backlight_init(samsung);
1492 goto error_backlight;
1494 ret = samsung_rfkill_init(samsung);
1498 ret = samsung_leds_init(samsung);
1502 ret = samsung_debugfs_init(samsung);
1506 samsung_platform_device = samsung->platform_device;
1510 samsung_leds_exit(samsung);
1512 samsung_rfkill_exit(samsung);
1514 samsung_backlight_exit(samsung);
1516 samsung_sysfs_exit(samsung);
1518 samsung_sabi_exit(samsung);
1520 samsung_platform_exit(samsung);
1526 static void __exit samsung_exit(void)
1528 struct samsung_laptop *samsung;
1530 samsung = platform_get_drvdata(samsung_platform_device);
1532 samsung_debugfs_exit(samsung);
1533 samsung_leds_exit(samsung);
1534 samsung_rfkill_exit(samsung);
1535 samsung_backlight_exit(samsung);
1536 samsung_sysfs_exit(samsung);
1537 samsung_sabi_exit(samsung);
1538 samsung_platform_exit(samsung);
1541 samsung_platform_device = NULL;
1544 module_init(samsung_init);
1545 module_exit(samsung_exit);
1547 MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@suse.de>");
1548 MODULE_DESCRIPTION("Samsung Backlight driver");
1549 MODULE_LICENSE("GPL");