staging: samsung-laptop: address review comments
[wandboard.git] / drivers / staging / samsung-laptop / samsung-laptop.c
bloba8e82b8eb5d7ce780c40efbaf43a63112eb56cd5
1 /*
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/fb.h>
21 #include <linux/dmi.h>
22 #include <linux/platform_device.h>
23 #include <linux/rfkill.h>
26 * This driver is needed because a number of Samsung laptops do not hook
27 * their control settings through ACPI. So we have to poke around in the
28 * BIOS to do things like brightness values, and "special" key controls.
32 * We have 0 - 8 as valid brightness levels. The specs say that level 0 should
33 * be reserved by the BIOS (which really doesn't make much sense), we tell
34 * userspace that the value is 0 - 7 and then just tell the hardware 1 - 8
36 #define MAX_BRIGHT 0x07
39 #define SABI_IFACE_MAIN 0x00
40 #define SABI_IFACE_SUB 0x02
41 #define SABI_IFACE_COMPLETE 0x04
42 #define SABI_IFACE_DATA 0x05
44 /* Structure to get data back to the calling function */
45 struct sabi_retval {
46 u8 retval[20];
49 struct sabi_header_offsets {
50 u8 port;
51 u8 re_mem;
52 u8 iface_func;
53 u8 en_mem;
54 u8 data_offset;
55 u8 data_segment;
58 struct sabi_commands {
60 * Brightness is 0 - 8, as described above.
61 * Value 0 is for the BIOS to use
63 u8 get_brightness;
64 u8 set_brightness;
67 * first byte:
68 * 0x00 - wireless is off
69 * 0x01 - wireless is on
70 * second byte:
71 * 0x02 - 3G is off
72 * 0x03 - 3G is on
73 * TODO, verify 3G is correct, that doesn't seem right...
75 u8 get_wireless_button;
76 u8 set_wireless_button;
78 /* 0 is off, 1 is on */
79 u8 get_backlight;
80 u8 set_backlight;
83 * 0x80 or 0x00 - no action
84 * 0x81 - recovery key pressed
86 u8 get_recovery_mode;
87 u8 set_recovery_mode;
90 * on seclinux: 0 is low, 1 is high,
91 * on swsmi: 0 is normal, 1 is silent, 2 is turbo
93 u8 get_performance_level;
94 u8 set_performance_level;
97 * Tell the BIOS that Linux is running on this machine.
98 * 81 is on, 80 is off
100 u8 set_linux;
103 struct sabi_performance_level {
104 const char *name;
105 u8 value;
108 struct sabi_config {
109 const char *test_string;
110 u16 main_function;
111 struct sabi_header_offsets header_offsets;
112 struct sabi_commands commands;
113 struct sabi_performance_level performance_levels[4];
116 static struct sabi_config sabi_configs[] = {
118 .test_string = "SECLINUX",
120 .main_function = 0x4c49,
122 .header_offsets = {
123 .port = 0x00,
124 .re_mem = 0x02,
125 .iface_func = 0x03,
126 .en_mem = 0x04,
127 .data_offset = 0x05,
128 .data_segment = 0x07,
131 .commands = {
132 .get_brightness = 0x00,
133 .set_brightness = 0x01,
135 .get_wireless_button = 0x02,
136 .set_wireless_button = 0x03,
138 .get_backlight = 0x04,
139 .set_backlight = 0x05,
141 .get_recovery_mode = 0x06,
142 .set_recovery_mode = 0x07,
144 .get_performance_level = 0x08,
145 .set_performance_level = 0x09,
147 .set_linux = 0x0a,
150 .performance_levels = {
152 .name = "silent",
153 .value = 0,
156 .name = "normal",
157 .value = 1,
159 { },
163 .test_string = "SwSmi@",
165 .main_function = 0x5843,
167 .header_offsets = {
168 .port = 0x00,
169 .re_mem = 0x04,
170 .iface_func = 0x02,
171 .en_mem = 0x03,
172 .data_offset = 0x05,
173 .data_segment = 0x07,
176 .commands = {
177 .get_brightness = 0x10,
178 .set_brightness = 0x11,
180 .get_wireless_button = 0x12,
181 .set_wireless_button = 0x13,
183 .get_backlight = 0x2d,
184 .set_backlight = 0x2e,
186 .get_recovery_mode = 0xff,
187 .set_recovery_mode = 0xff,
189 .get_performance_level = 0x31,
190 .set_performance_level = 0x32,
192 .set_linux = 0xff,
195 .performance_levels = {
197 .name = "normal",
198 .value = 0,
201 .name = "silent",
202 .value = 1,
205 .name = "overclock",
206 .value = 2,
208 { },
211 { },
214 static struct sabi_config *sabi_config;
216 static void __iomem *sabi;
217 static void __iomem *sabi_iface;
218 static void __iomem *f0000_segment;
219 static struct backlight_device *backlight_device;
220 static struct mutex sabi_mutex;
221 static struct platform_device *sdev;
222 static struct rfkill *rfk;
224 static int force;
225 module_param(force, bool, 0);
226 MODULE_PARM_DESC(force,
227 "Disable the DMI check and forces the driver to be loaded");
229 static int debug;
230 module_param(debug, bool, S_IRUGO | S_IWUSR);
231 MODULE_PARM_DESC(debug, "Debug enabled or not");
233 static int sabi_get_command(u8 command, struct sabi_retval *sretval)
235 int retval = 0;
236 u16 port = readw(sabi + sabi_config->header_offsets.port);
237 u8 complete, iface_data;
239 mutex_lock(&sabi_mutex);
241 /* enable memory to be able to write to it */
242 outb(readb(sabi + sabi_config->header_offsets.en_mem), port);
244 /* write out the command */
245 writew(sabi_config->main_function, sabi_iface + SABI_IFACE_MAIN);
246 writew(command, sabi_iface + SABI_IFACE_SUB);
247 writeb(0, sabi_iface + SABI_IFACE_COMPLETE);
248 outb(readb(sabi + sabi_config->header_offsets.iface_func), port);
250 /* write protect memory to make it safe */
251 outb(readb(sabi + sabi_config->header_offsets.re_mem), port);
253 /* see if the command actually succeeded */
254 complete = readb(sabi_iface + SABI_IFACE_COMPLETE);
255 iface_data = readb(sabi_iface + SABI_IFACE_DATA);
256 if (complete != 0xaa || iface_data == 0xff) {
257 pr_warn("SABI get command 0x%02x failed with completion flag 0x%02x and data 0x%02x\n",
258 command, complete, iface_data);
259 retval = -EINVAL;
260 goto exit;
263 * Save off the data into a structure so the caller use it.
264 * Right now we only want the first 4 bytes,
265 * There are commands that need more, but not for the ones we
266 * currently care about.
268 sretval->retval[0] = readb(sabi_iface + SABI_IFACE_DATA);
269 sretval->retval[1] = readb(sabi_iface + SABI_IFACE_DATA + 1);
270 sretval->retval[2] = readb(sabi_iface + SABI_IFACE_DATA + 2);
271 sretval->retval[3] = readb(sabi_iface + SABI_IFACE_DATA + 3);
273 exit:
274 mutex_unlock(&sabi_mutex);
275 return retval;
279 static int sabi_set_command(u8 command, u8 data)
281 int retval = 0;
282 u16 port = readw(sabi + sabi_config->header_offsets.port);
283 u8 complete, iface_data;
285 mutex_lock(&sabi_mutex);
287 /* enable memory to be able to write to it */
288 outb(readb(sabi + sabi_config->header_offsets.en_mem), port);
290 /* write out the command */
291 writew(sabi_config->main_function, sabi_iface + SABI_IFACE_MAIN);
292 writew(command, sabi_iface + SABI_IFACE_SUB);
293 writeb(0, sabi_iface + SABI_IFACE_COMPLETE);
294 writeb(data, sabi_iface + SABI_IFACE_DATA);
295 outb(readb(sabi + sabi_config->header_offsets.iface_func), port);
297 /* write protect memory to make it safe */
298 outb(readb(sabi + sabi_config->header_offsets.re_mem), port);
300 /* see if the command actually succeeded */
301 complete = readb(sabi_iface + SABI_IFACE_COMPLETE);
302 iface_data = readb(sabi_iface + SABI_IFACE_DATA);
303 if (complete != 0xaa || iface_data == 0xff) {
304 pr_warn("SABI set command 0x%02x failed with completion flag 0x%02x and data 0x%02x\n",
305 command, complete, iface_data);
306 retval = -EINVAL;
309 mutex_unlock(&sabi_mutex);
310 return retval;
313 static void test_backlight(void)
315 struct sabi_retval sretval;
317 sabi_get_command(sabi_config->commands.get_backlight, &sretval);
318 printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]);
320 sabi_set_command(sabi_config->commands.set_backlight, 0);
321 printk(KERN_DEBUG "backlight should be off\n");
323 sabi_get_command(sabi_config->commands.get_backlight, &sretval);
324 printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]);
326 msleep(1000);
328 sabi_set_command(sabi_config->commands.set_backlight, 1);
329 printk(KERN_DEBUG "backlight should be on\n");
331 sabi_get_command(sabi_config->commands.get_backlight, &sretval);
332 printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]);
335 static void test_wireless(void)
337 struct sabi_retval sretval;
339 sabi_get_command(sabi_config->commands.get_wireless_button, &sretval);
340 printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]);
342 sabi_set_command(sabi_config->commands.set_wireless_button, 0);
343 printk(KERN_DEBUG "wireless led should be off\n");
345 sabi_get_command(sabi_config->commands.get_wireless_button, &sretval);
346 printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]);
348 msleep(1000);
350 sabi_set_command(sabi_config->commands.set_wireless_button, 1);
351 printk(KERN_DEBUG "wireless led should be on\n");
353 sabi_get_command(sabi_config->commands.get_wireless_button, &sretval);
354 printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]);
357 static u8 read_brightness(void)
359 struct sabi_retval sretval;
360 int user_brightness = 0;
361 int retval;
363 retval = sabi_get_command(sabi_config->commands.get_brightness,
364 &sretval);
365 if (!retval)
366 user_brightness = sretval.retval[0];
367 if (user_brightness != 0)
368 --user_brightness;
369 return user_brightness;
372 static void set_brightness(u8 user_brightness)
374 sabi_set_command(sabi_config->commands.set_brightness,
375 user_brightness + 1);
378 static int get_brightness(struct backlight_device *bd)
380 return (int)read_brightness();
383 static int update_status(struct backlight_device *bd)
385 set_brightness(bd->props.brightness);
387 if (bd->props.power == FB_BLANK_UNBLANK)
388 sabi_set_command(sabi_config->commands.set_backlight, 1);
389 else
390 sabi_set_command(sabi_config->commands.set_backlight, 0);
391 return 0;
394 static const struct backlight_ops backlight_ops = {
395 .get_brightness = get_brightness,
396 .update_status = update_status,
399 static int rfkill_set(void *data, bool blocked)
401 /* Do something with blocked...*/
403 * blocked == false is on
404 * blocked == true is off
406 if (blocked)
407 sabi_set_command(sabi_config->commands.set_wireless_button, 0);
408 else
409 sabi_set_command(sabi_config->commands.set_wireless_button, 1);
411 return 0;
414 static struct rfkill_ops rfkill_ops = {
415 .set_block = rfkill_set,
418 static int init_wireless(struct platform_device *sdev)
420 int retval;
422 rfk = rfkill_alloc("samsung-wifi", &sdev->dev, RFKILL_TYPE_WLAN,
423 &rfkill_ops, NULL);
424 if (!rfk)
425 return -ENOMEM;
427 retval = rfkill_register(rfk);
428 if (retval) {
429 rfkill_destroy(rfk);
430 return -ENODEV;
433 return 0;
436 static void destroy_wireless(void)
438 rfkill_unregister(rfk);
439 rfkill_destroy(rfk);
442 static ssize_t get_performance_level(struct device *dev,
443 struct device_attribute *attr, char *buf)
445 struct sabi_retval sretval;
446 int retval;
447 int i;
449 /* Read the state */
450 retval = sabi_get_command(sabi_config->commands.get_performance_level,
451 &sretval);
452 if (retval)
453 return retval;
455 /* The logic is backwards, yeah, lots of fun... */
456 for (i = 0; sabi_config->performance_levels[i].name; ++i) {
457 if (sretval.retval[0] == sabi_config->performance_levels[i].value)
458 return sprintf(buf, "%s\n", sabi_config->performance_levels[i].name);
460 return sprintf(buf, "%s\n", "unknown");
463 static ssize_t set_performance_level(struct device *dev,
464 struct device_attribute *attr, const char *buf,
465 size_t count)
467 if (count >= 1) {
468 int i;
469 for (i = 0; sabi_config->performance_levels[i].name; ++i) {
470 struct sabi_performance_level *level =
471 &sabi_config->performance_levels[i];
472 if (!strncasecmp(level->name, buf, strlen(level->name))) {
473 sabi_set_command(sabi_config->commands.set_performance_level,
474 level->value);
475 break;
478 if (!sabi_config->performance_levels[i].name)
479 return -EINVAL;
481 return count;
483 static DEVICE_ATTR(performance_level, S_IWUSR | S_IRUGO,
484 get_performance_level, set_performance_level);
487 static int __init dmi_check_cb(const struct dmi_system_id *id)
489 pr_info("found laptop model '%s'\n",
490 id->ident);
491 return 0;
494 static struct dmi_system_id __initdata samsung_dmi_table[] = {
496 .ident = "N128",
497 .matches = {
498 DMI_MATCH(DMI_SYS_VENDOR,
499 "SAMSUNG ELECTRONICS CO., LTD."),
500 DMI_MATCH(DMI_PRODUCT_NAME, "N128"),
501 DMI_MATCH(DMI_BOARD_NAME, "N128"),
503 .callback = dmi_check_cb,
506 .ident = "N130",
507 .matches = {
508 DMI_MATCH(DMI_SYS_VENDOR,
509 "SAMSUNG ELECTRONICS CO., LTD."),
510 DMI_MATCH(DMI_PRODUCT_NAME, "N130"),
511 DMI_MATCH(DMI_BOARD_NAME, "N130"),
513 .callback = dmi_check_cb,
516 .ident = "X125",
517 .matches = {
518 DMI_MATCH(DMI_SYS_VENDOR,
519 "SAMSUNG ELECTRONICS CO., LTD."),
520 DMI_MATCH(DMI_PRODUCT_NAME, "X125"),
521 DMI_MATCH(DMI_BOARD_NAME, "X125"),
523 .callback = dmi_check_cb,
526 .ident = "X120/X170",
527 .matches = {
528 DMI_MATCH(DMI_SYS_VENDOR,
529 "SAMSUNG ELECTRONICS CO., LTD."),
530 DMI_MATCH(DMI_PRODUCT_NAME, "X120/X170"),
531 DMI_MATCH(DMI_BOARD_NAME, "X120/X170"),
533 .callback = dmi_check_cb,
536 .ident = "NC10",
537 .matches = {
538 DMI_MATCH(DMI_SYS_VENDOR,
539 "SAMSUNG ELECTRONICS CO., LTD."),
540 DMI_MATCH(DMI_PRODUCT_NAME, "NC10"),
541 DMI_MATCH(DMI_BOARD_NAME, "NC10"),
543 .callback = dmi_check_cb,
546 .ident = "NP-Q45",
547 .matches = {
548 DMI_MATCH(DMI_SYS_VENDOR,
549 "SAMSUNG ELECTRONICS CO., LTD."),
550 DMI_MATCH(DMI_PRODUCT_NAME, "SQ45S70S"),
551 DMI_MATCH(DMI_BOARD_NAME, "SQ45S70S"),
553 .callback = dmi_check_cb,
556 .ident = "X360",
557 .matches = {
558 DMI_MATCH(DMI_SYS_VENDOR,
559 "SAMSUNG ELECTRONICS CO., LTD."),
560 DMI_MATCH(DMI_PRODUCT_NAME, "X360"),
561 DMI_MATCH(DMI_BOARD_NAME, "X360"),
563 .callback = dmi_check_cb,
566 .ident = "R518",
567 .matches = {
568 DMI_MATCH(DMI_SYS_VENDOR,
569 "SAMSUNG ELECTRONICS CO., LTD."),
570 DMI_MATCH(DMI_PRODUCT_NAME, "R518"),
571 DMI_MATCH(DMI_BOARD_NAME, "R518"),
573 .callback = dmi_check_cb,
576 .ident = "R519/R719",
577 .matches = {
578 DMI_MATCH(DMI_SYS_VENDOR,
579 "SAMSUNG ELECTRONICS CO., LTD."),
580 DMI_MATCH(DMI_PRODUCT_NAME, "R519/R719"),
581 DMI_MATCH(DMI_BOARD_NAME, "R519/R719"),
583 .callback = dmi_check_cb,
586 .ident = "N150/N210/N220",
587 .matches = {
588 DMI_MATCH(DMI_SYS_VENDOR,
589 "SAMSUNG ELECTRONICS CO., LTD."),
590 DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220"),
591 DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220"),
593 .callback = dmi_check_cb,
596 .ident = "R530/R730",
597 .matches = {
598 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
599 DMI_MATCH(DMI_PRODUCT_NAME, "R530/R730"),
600 DMI_MATCH(DMI_BOARD_NAME, "R530/R730"),
602 .callback = dmi_check_cb,
605 .ident = "NF110/NF210/NF310",
606 .matches = {
607 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
608 DMI_MATCH(DMI_PRODUCT_NAME, "NF110/NF210/NF310"),
609 DMI_MATCH(DMI_BOARD_NAME, "NF110/NF210/NF310"),
611 .callback = dmi_check_cb,
614 .ident = "N145P/N250P/N260P",
615 .matches = {
616 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
617 DMI_MATCH(DMI_PRODUCT_NAME, "N145P/N250P/N260P"),
618 DMI_MATCH(DMI_BOARD_NAME, "N145P/N250P/N260P"),
620 .callback = dmi_check_cb,
623 .ident = "R70/R71",
624 .matches = {
625 DMI_MATCH(DMI_SYS_VENDOR,
626 "SAMSUNG ELECTRONICS CO., LTD."),
627 DMI_MATCH(DMI_PRODUCT_NAME, "R70/R71"),
628 DMI_MATCH(DMI_BOARD_NAME, "R70/R71"),
630 .callback = dmi_check_cb,
632 { },
634 MODULE_DEVICE_TABLE(dmi, samsung_dmi_table);
636 static int find_signature(void __iomem *memcheck, const char *testStr)
638 int i = 0;
639 int loca;
641 for (loca = 0; loca < 0xffff; loca++) {
642 char temp = readb(memcheck + loca);
644 if (temp == testStr[i]) {
645 if (i == strlen(testStr)-1)
646 break;
647 ++i;
648 } else {
649 i = 0;
652 return loca;
655 static int __init samsung_init(void)
657 struct backlight_properties props;
658 struct sabi_retval sretval;
659 unsigned int ifaceP;
660 int i;
661 int loca;
662 int retval;
664 mutex_init(&sabi_mutex);
666 if (!force && !dmi_check_system(samsung_dmi_table))
667 return -ENODEV;
669 f0000_segment = ioremap(0xf0000, 0xffff);
670 if (!f0000_segment) {
671 pr_err("Can't map the segment at 0xf0000\n");
672 return -EINVAL;
675 /* Try to find one of the signatures in memory to find the header */
676 for (i = 0; sabi_configs[i].test_string != 0; ++i) {
677 sabi_config = &sabi_configs[i];
678 loca = find_signature(f0000_segment, sabi_config->test_string);
679 if (loca != 0xffff)
680 break;
683 if (loca == 0xffff) {
684 pr_err("This computer does not support SABI\n");
685 goto error_no_signature;
688 /* point to the SMI port Number */
689 loca += 1;
690 sabi = (f0000_segment + loca);
692 if (debug) {
693 printk(KERN_DEBUG "This computer supports SABI==%x\n",
694 loca + 0xf0000 - 6);
695 printk(KERN_DEBUG "SABI header:\n");
696 printk(KERN_DEBUG " SMI Port Number = 0x%04x\n",
697 readw(sabi + sabi_config->header_offsets.port));
698 printk(KERN_DEBUG " SMI Interface Function = 0x%02x\n",
699 readb(sabi + sabi_config->header_offsets.iface_func));
700 printk(KERN_DEBUG " SMI enable memory buffer = 0x%02x\n",
701 readb(sabi + sabi_config->header_offsets.en_mem));
702 printk(KERN_DEBUG " SMI restore memory buffer = 0x%02x\n",
703 readb(sabi + sabi_config->header_offsets.re_mem));
704 printk(KERN_DEBUG " SABI data offset = 0x%04x\n",
705 readw(sabi + sabi_config->header_offsets.data_offset));
706 printk(KERN_DEBUG " SABI data segment = 0x%04x\n",
707 readw(sabi + sabi_config->header_offsets.data_segment));
710 /* Get a pointer to the SABI Interface */
711 ifaceP = (readw(sabi + sabi_config->header_offsets.data_segment) & 0x0ffff) << 4;
712 ifaceP += readw(sabi + sabi_config->header_offsets.data_offset) & 0x0ffff;
713 sabi_iface = ioremap(ifaceP, 16);
714 if (!sabi_iface) {
715 pr_err("Can't remap %x\n", ifaceP);
716 goto exit;
718 if (debug) {
719 printk(KERN_DEBUG "ifaceP = 0x%08x\n", ifaceP);
720 printk(KERN_DEBUG "sabi_iface = %p\n", sabi_iface);
722 test_backlight();
723 test_wireless();
725 retval = sabi_get_command(sabi_config->commands.get_brightness,
726 &sretval);
727 printk(KERN_DEBUG "brightness = 0x%02x\n", sretval.retval[0]);
730 /* Turn on "Linux" mode in the BIOS */
731 if (sabi_config->commands.set_linux != 0xff) {
732 retval = sabi_set_command(sabi_config->commands.set_linux,
733 0x81);
734 if (retval) {
735 pr_warn("Linux mode was not set!\n");
736 goto error_no_platform;
740 /* knock up a platform device to hang stuff off of */
741 sdev = platform_device_register_simple("samsung", -1, NULL, 0);
742 if (IS_ERR(sdev))
743 goto error_no_platform;
745 /* create a backlight device to talk to this one */
746 memset(&props, 0, sizeof(struct backlight_properties));
747 props.max_brightness = MAX_BRIGHT;
748 backlight_device = backlight_device_register("samsung", &sdev->dev,
749 NULL, &backlight_ops,
750 &props);
751 if (IS_ERR(backlight_device))
752 goto error_no_backlight;
754 backlight_device->props.brightness = read_brightness();
755 backlight_device->props.power = FB_BLANK_UNBLANK;
756 backlight_update_status(backlight_device);
758 retval = init_wireless(sdev);
759 if (retval)
760 goto error_no_rfk;
762 retval = device_create_file(&sdev->dev, &dev_attr_performance_level);
763 if (retval)
764 goto error_file_create;
766 exit:
767 return 0;
769 error_file_create:
770 destroy_wireless();
772 error_no_rfk:
773 backlight_device_unregister(backlight_device);
775 error_no_backlight:
776 platform_device_unregister(sdev);
778 error_no_platform:
779 iounmap(sabi_iface);
781 error_no_signature:
782 iounmap(f0000_segment);
783 return -EINVAL;
786 static void __exit samsung_exit(void)
788 /* Turn off "Linux" mode in the BIOS */
789 if (sabi_config->commands.set_linux != 0xff)
790 sabi_set_command(sabi_config->commands.set_linux, 0x80);
792 device_remove_file(&sdev->dev, &dev_attr_performance_level);
793 backlight_device_unregister(backlight_device);
794 destroy_wireless();
795 iounmap(sabi_iface);
796 iounmap(f0000_segment);
797 platform_device_unregister(sdev);
800 module_init(samsung_init);
801 module_exit(samsung_exit);
803 MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@suse.de>");
804 MODULE_DESCRIPTION("Samsung Backlight driver");
805 MODULE_LICENSE("GPL");