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>
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 */
49 struct sabi_header_offsets
{
58 struct sabi_commands
{
60 * Brightness is 0 - 8, as described above.
61 * Value 0 is for the BIOS to use
68 * 0x00 - wireless is off
69 * 0x01 - wireless 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 */
83 * 0x80 or 0x00 - no action
84 * 0x81 - recovery key pressed
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.
103 struct sabi_performance_level
{
109 const char *test_string
;
111 const struct sabi_header_offsets header_offsets
;
112 const struct sabi_commands commands
;
113 const struct sabi_performance_level performance_levels
[4];
118 static const struct sabi_config sabi_configs
[] = {
120 .test_string
= "SECLINUX",
122 .main_function
= 0x4c49,
130 .data_segment
= 0x07,
134 .get_brightness
= 0x00,
135 .set_brightness
= 0x01,
137 .get_wireless_button
= 0x02,
138 .set_wireless_button
= 0x03,
140 .get_backlight
= 0x04,
141 .set_backlight
= 0x05,
143 .get_recovery_mode
= 0x06,
144 .set_recovery_mode
= 0x07,
146 .get_performance_level
= 0x08,
147 .set_performance_level
= 0x09,
152 .performance_levels
= {
167 .test_string
= "SwSmi@",
169 .main_function
= 0x5843,
177 .data_segment
= 0x07,
181 .get_brightness
= 0x10,
182 .set_brightness
= 0x11,
184 .get_wireless_button
= 0x12,
185 .set_wireless_button
= 0x13,
187 .get_backlight
= 0x2d,
188 .set_backlight
= 0x2e,
190 .get_recovery_mode
= 0xff,
191 .set_recovery_mode
= 0xff,
193 .get_performance_level
= 0x31,
194 .set_performance_level
= 0x32,
199 .performance_levels
= {
220 static const struct sabi_config
*sabi_config
;
222 static void __iomem
*sabi
;
223 static void __iomem
*sabi_iface
;
224 static void __iomem
*f0000_segment
;
225 static struct backlight_device
*backlight_device
;
226 static struct mutex sabi_mutex
;
227 static struct platform_device
*sdev
;
228 static struct rfkill
*rfk
;
231 module_param(force
, bool, 0);
232 MODULE_PARM_DESC(force
,
233 "Disable the DMI check and forces the driver to be loaded");
236 module_param(debug
, bool, S_IRUGO
| S_IWUSR
);
237 MODULE_PARM_DESC(debug
, "Debug enabled or not");
239 static int sabi_get_command(u8 command
, struct sabi_retval
*sretval
)
242 u16 port
= readw(sabi
+ sabi_config
->header_offsets
.port
);
243 u8 complete
, iface_data
;
245 mutex_lock(&sabi_mutex
);
247 /* enable memory to be able to write to it */
248 outb(readb(sabi
+ sabi_config
->header_offsets
.en_mem
), port
);
250 /* write out the command */
251 writew(sabi_config
->main_function
, sabi_iface
+ SABI_IFACE_MAIN
);
252 writew(command
, sabi_iface
+ SABI_IFACE_SUB
);
253 writeb(0, sabi_iface
+ SABI_IFACE_COMPLETE
);
254 outb(readb(sabi
+ sabi_config
->header_offsets
.iface_func
), port
);
256 /* write protect memory to make it safe */
257 outb(readb(sabi
+ sabi_config
->header_offsets
.re_mem
), port
);
259 /* see if the command actually succeeded */
260 complete
= readb(sabi_iface
+ SABI_IFACE_COMPLETE
);
261 iface_data
= readb(sabi_iface
+ SABI_IFACE_DATA
);
262 if (complete
!= 0xaa || iface_data
== 0xff) {
263 pr_warn("SABI get command 0x%02x failed with completion flag 0x%02x and data 0x%02x\n",
264 command
, complete
, iface_data
);
269 * Save off the data into a structure so the caller use it.
270 * Right now we only want the first 4 bytes,
271 * There are commands that need more, but not for the ones we
272 * currently care about.
274 sretval
->retval
[0] = readb(sabi_iface
+ SABI_IFACE_DATA
);
275 sretval
->retval
[1] = readb(sabi_iface
+ SABI_IFACE_DATA
+ 1);
276 sretval
->retval
[2] = readb(sabi_iface
+ SABI_IFACE_DATA
+ 2);
277 sretval
->retval
[3] = readb(sabi_iface
+ SABI_IFACE_DATA
+ 3);
280 mutex_unlock(&sabi_mutex
);
285 static int sabi_set_command(u8 command
, u8 data
)
288 u16 port
= readw(sabi
+ sabi_config
->header_offsets
.port
);
289 u8 complete
, iface_data
;
291 mutex_lock(&sabi_mutex
);
293 /* enable memory to be able to write to it */
294 outb(readb(sabi
+ sabi_config
->header_offsets
.en_mem
), port
);
296 /* write out the command */
297 writew(sabi_config
->main_function
, sabi_iface
+ SABI_IFACE_MAIN
);
298 writew(command
, sabi_iface
+ SABI_IFACE_SUB
);
299 writeb(0, sabi_iface
+ SABI_IFACE_COMPLETE
);
300 writeb(data
, sabi_iface
+ SABI_IFACE_DATA
);
301 outb(readb(sabi
+ sabi_config
->header_offsets
.iface_func
), port
);
303 /* write protect memory to make it safe */
304 outb(readb(sabi
+ sabi_config
->header_offsets
.re_mem
), port
);
306 /* see if the command actually succeeded */
307 complete
= readb(sabi_iface
+ SABI_IFACE_COMPLETE
);
308 iface_data
= readb(sabi_iface
+ SABI_IFACE_DATA
);
309 if (complete
!= 0xaa || iface_data
== 0xff) {
310 pr_warn("SABI set command 0x%02x failed with completion flag 0x%02x and data 0x%02x\n",
311 command
, complete
, iface_data
);
315 mutex_unlock(&sabi_mutex
);
319 static void test_backlight(void)
321 struct sabi_retval sretval
;
323 sabi_get_command(sabi_config
->commands
.get_backlight
, &sretval
);
324 printk(KERN_DEBUG
"backlight = 0x%02x\n", sretval
.retval
[0]);
326 sabi_set_command(sabi_config
->commands
.set_backlight
, 0);
327 printk(KERN_DEBUG
"backlight should be off\n");
329 sabi_get_command(sabi_config
->commands
.get_backlight
, &sretval
);
330 printk(KERN_DEBUG
"backlight = 0x%02x\n", sretval
.retval
[0]);
334 sabi_set_command(sabi_config
->commands
.set_backlight
, 1);
335 printk(KERN_DEBUG
"backlight should be on\n");
337 sabi_get_command(sabi_config
->commands
.get_backlight
, &sretval
);
338 printk(KERN_DEBUG
"backlight = 0x%02x\n", sretval
.retval
[0]);
341 static void test_wireless(void)
343 struct sabi_retval sretval
;
345 sabi_get_command(sabi_config
->commands
.get_wireless_button
, &sretval
);
346 printk(KERN_DEBUG
"wireless led = 0x%02x\n", sretval
.retval
[0]);
348 sabi_set_command(sabi_config
->commands
.set_wireless_button
, 0);
349 printk(KERN_DEBUG
"wireless led should be off\n");
351 sabi_get_command(sabi_config
->commands
.get_wireless_button
, &sretval
);
352 printk(KERN_DEBUG
"wireless led = 0x%02x\n", sretval
.retval
[0]);
356 sabi_set_command(sabi_config
->commands
.set_wireless_button
, 1);
357 printk(KERN_DEBUG
"wireless led should be on\n");
359 sabi_get_command(sabi_config
->commands
.get_wireless_button
, &sretval
);
360 printk(KERN_DEBUG
"wireless led = 0x%02x\n", sretval
.retval
[0]);
363 static u8
read_brightness(void)
365 struct sabi_retval sretval
;
366 int user_brightness
= 0;
369 retval
= sabi_get_command(sabi_config
->commands
.get_brightness
,
372 user_brightness
= sretval
.retval
[0];
373 if (user_brightness
> sabi_config
->min_brightness
)
374 user_brightness
-= sabi_config
->min_brightness
;
378 return user_brightness
;
381 static void set_brightness(u8 user_brightness
)
383 u8 user_level
= user_brightness
+ sabi_config
->min_brightness
;
385 sabi_set_command(sabi_config
->commands
.set_brightness
, user_level
);
388 static int get_brightness(struct backlight_device
*bd
)
390 return (int)read_brightness();
393 static int update_status(struct backlight_device
*bd
)
395 set_brightness(bd
->props
.brightness
);
397 if (bd
->props
.power
== FB_BLANK_UNBLANK
)
398 sabi_set_command(sabi_config
->commands
.set_backlight
, 1);
400 sabi_set_command(sabi_config
->commands
.set_backlight
, 0);
404 static const struct backlight_ops backlight_ops
= {
405 .get_brightness
= get_brightness
,
406 .update_status
= update_status
,
409 static int rfkill_set(void *data
, bool blocked
)
411 /* Do something with blocked...*/
413 * blocked == false is on
414 * blocked == true is off
417 sabi_set_command(sabi_config
->commands
.set_wireless_button
, 0);
419 sabi_set_command(sabi_config
->commands
.set_wireless_button
, 1);
424 static struct rfkill_ops rfkill_ops
= {
425 .set_block
= rfkill_set
,
428 static int init_wireless(struct platform_device
*sdev
)
432 rfk
= rfkill_alloc("samsung-wifi", &sdev
->dev
, RFKILL_TYPE_WLAN
,
437 retval
= rfkill_register(rfk
);
446 static void destroy_wireless(void)
448 rfkill_unregister(rfk
);
452 static ssize_t
get_performance_level(struct device
*dev
,
453 struct device_attribute
*attr
, char *buf
)
455 struct sabi_retval sretval
;
460 retval
= sabi_get_command(sabi_config
->commands
.get_performance_level
,
465 /* The logic is backwards, yeah, lots of fun... */
466 for (i
= 0; sabi_config
->performance_levels
[i
].name
; ++i
) {
467 if (sretval
.retval
[0] == sabi_config
->performance_levels
[i
].value
)
468 return sprintf(buf
, "%s\n", sabi_config
->performance_levels
[i
].name
);
470 return sprintf(buf
, "%s\n", "unknown");
473 static ssize_t
set_performance_level(struct device
*dev
,
474 struct device_attribute
*attr
, const char *buf
,
479 for (i
= 0; sabi_config
->performance_levels
[i
].name
; ++i
) {
480 const struct sabi_performance_level
*level
=
481 &sabi_config
->performance_levels
[i
];
482 if (!strncasecmp(level
->name
, buf
, strlen(level
->name
))) {
483 sabi_set_command(sabi_config
->commands
.set_performance_level
,
488 if (!sabi_config
->performance_levels
[i
].name
)
493 static DEVICE_ATTR(performance_level
, S_IWUSR
| S_IRUGO
,
494 get_performance_level
, set_performance_level
);
497 static int __init
dmi_check_cb(const struct dmi_system_id
*id
)
499 pr_info("found laptop model '%s'\n",
504 static struct dmi_system_id __initdata samsung_dmi_table
[] = {
508 DMI_MATCH(DMI_SYS_VENDOR
,
509 "SAMSUNG ELECTRONICS CO., LTD."),
510 DMI_MATCH(DMI_PRODUCT_NAME
, "N128"),
511 DMI_MATCH(DMI_BOARD_NAME
, "N128"),
513 .callback
= dmi_check_cb
,
518 DMI_MATCH(DMI_SYS_VENDOR
,
519 "SAMSUNG ELECTRONICS CO., LTD."),
520 DMI_MATCH(DMI_PRODUCT_NAME
, "N130"),
521 DMI_MATCH(DMI_BOARD_NAME
, "N130"),
523 .callback
= dmi_check_cb
,
528 DMI_MATCH(DMI_SYS_VENDOR
,
529 "SAMSUNG ELECTRONICS CO., LTD."),
530 DMI_MATCH(DMI_PRODUCT_NAME
, "X125"),
531 DMI_MATCH(DMI_BOARD_NAME
, "X125"),
533 .callback
= dmi_check_cb
,
536 .ident
= "X120/X170",
538 DMI_MATCH(DMI_SYS_VENDOR
,
539 "SAMSUNG ELECTRONICS CO., LTD."),
540 DMI_MATCH(DMI_PRODUCT_NAME
, "X120/X170"),
541 DMI_MATCH(DMI_BOARD_NAME
, "X120/X170"),
543 .callback
= dmi_check_cb
,
548 DMI_MATCH(DMI_SYS_VENDOR
,
549 "SAMSUNG ELECTRONICS CO., LTD."),
550 DMI_MATCH(DMI_PRODUCT_NAME
, "NC10"),
551 DMI_MATCH(DMI_BOARD_NAME
, "NC10"),
553 .callback
= dmi_check_cb
,
558 DMI_MATCH(DMI_SYS_VENDOR
,
559 "SAMSUNG ELECTRONICS CO., LTD."),
560 DMI_MATCH(DMI_PRODUCT_NAME
, "SQ45S70S"),
561 DMI_MATCH(DMI_BOARD_NAME
, "SQ45S70S"),
563 .callback
= dmi_check_cb
,
568 DMI_MATCH(DMI_SYS_VENDOR
,
569 "SAMSUNG ELECTRONICS CO., LTD."),
570 DMI_MATCH(DMI_PRODUCT_NAME
, "X360"),
571 DMI_MATCH(DMI_BOARD_NAME
, "X360"),
573 .callback
= dmi_check_cb
,
576 .ident
= "R410 Plus",
578 DMI_MATCH(DMI_SYS_VENDOR
,
579 "SAMSUNG ELECTRONICS CO., LTD."),
580 DMI_MATCH(DMI_PRODUCT_NAME
, "R410P"),
581 DMI_MATCH(DMI_BOARD_NAME
, "R460"),
583 .callback
= dmi_check_cb
,
588 DMI_MATCH(DMI_SYS_VENDOR
,
589 "SAMSUNG ELECTRONICS CO., LTD."),
590 DMI_MATCH(DMI_PRODUCT_NAME
, "R518"),
591 DMI_MATCH(DMI_BOARD_NAME
, "R518"),
593 .callback
= dmi_check_cb
,
596 .ident
= "R519/R719",
598 DMI_MATCH(DMI_SYS_VENDOR
,
599 "SAMSUNG ELECTRONICS CO., LTD."),
600 DMI_MATCH(DMI_PRODUCT_NAME
, "R519/R719"),
601 DMI_MATCH(DMI_BOARD_NAME
, "R519/R719"),
603 .callback
= dmi_check_cb
,
606 .ident
= "N150/N210/N220",
608 DMI_MATCH(DMI_SYS_VENDOR
,
609 "SAMSUNG ELECTRONICS CO., LTD."),
610 DMI_MATCH(DMI_PRODUCT_NAME
, "N150/N210/N220"),
611 DMI_MATCH(DMI_BOARD_NAME
, "N150/N210/N220"),
613 .callback
= dmi_check_cb
,
616 .ident
= "N150/N210/N220/N230",
618 DMI_MATCH(DMI_SYS_VENDOR
,
619 "SAMSUNG ELECTRONICS CO., LTD."),
620 DMI_MATCH(DMI_PRODUCT_NAME
, "N150/N210/N220/N230"),
621 DMI_MATCH(DMI_BOARD_NAME
, "N150/N210/N220/N230"),
623 .callback
= dmi_check_cb
,
626 .ident
= "N150P/N210P/N220P",
628 DMI_MATCH(DMI_SYS_VENDOR
,
629 "SAMSUNG ELECTRONICS CO., LTD."),
630 DMI_MATCH(DMI_PRODUCT_NAME
, "N150P/N210P/N220P"),
631 DMI_MATCH(DMI_BOARD_NAME
, "N150P/N210P/N220P"),
633 .callback
= dmi_check_cb
,
638 DMI_MATCH(DMI_SYS_VENDOR
, "SAMSUNG ELECTRONICS CO., LTD."),
639 DMI_MATCH(DMI_PRODUCT_NAME
, "SR700"),
640 DMI_MATCH(DMI_BOARD_NAME
, "SR700"),
642 .callback
= dmi_check_cb
,
645 .ident
= "R530/R730",
647 DMI_MATCH(DMI_SYS_VENDOR
, "SAMSUNG ELECTRONICS CO., LTD."),
648 DMI_MATCH(DMI_PRODUCT_NAME
, "R530/R730"),
649 DMI_MATCH(DMI_BOARD_NAME
, "R530/R730"),
651 .callback
= dmi_check_cb
,
654 .ident
= "NF110/NF210/NF310",
656 DMI_MATCH(DMI_SYS_VENDOR
, "SAMSUNG ELECTRONICS CO., LTD."),
657 DMI_MATCH(DMI_PRODUCT_NAME
, "NF110/NF210/NF310"),
658 DMI_MATCH(DMI_BOARD_NAME
, "NF110/NF210/NF310"),
660 .callback
= dmi_check_cb
,
663 .ident
= "N145P/N250P/N260P",
665 DMI_MATCH(DMI_SYS_VENDOR
, "SAMSUNG ELECTRONICS CO., LTD."),
666 DMI_MATCH(DMI_PRODUCT_NAME
, "N145P/N250P/N260P"),
667 DMI_MATCH(DMI_BOARD_NAME
, "N145P/N250P/N260P"),
669 .callback
= dmi_check_cb
,
674 DMI_MATCH(DMI_SYS_VENDOR
,
675 "SAMSUNG ELECTRONICS CO., LTD."),
676 DMI_MATCH(DMI_PRODUCT_NAME
, "R70/R71"),
677 DMI_MATCH(DMI_BOARD_NAME
, "R70/R71"),
679 .callback
= dmi_check_cb
,
684 DMI_MATCH(DMI_SYS_VENDOR
, "SAMSUNG ELECTRONICS CO., LTD."),
685 DMI_MATCH(DMI_PRODUCT_NAME
, "P460"),
686 DMI_MATCH(DMI_BOARD_NAME
, "P460"),
688 .callback
= dmi_check_cb
,
693 DMI_MATCH(DMI_SYS_VENDOR
, "SAMSUNG ELECTRONICS CO., LTD."),
694 DMI_MATCH(DMI_PRODUCT_NAME
, "X520"),
695 DMI_MATCH(DMI_BOARD_NAME
, "X520"),
697 .callback
= dmi_check_cb
,
700 .ident
= "R528/R728",
702 DMI_MATCH(DMI_SYS_VENDOR
, "SAMSUNG ELECTRONICS CO., LTD."),
703 DMI_MATCH(DMI_PRODUCT_NAME
, "R528/R728"),
704 DMI_MATCH(DMI_BOARD_NAME
, "R528/R728"),
706 .callback
= dmi_check_cb
,
710 MODULE_DEVICE_TABLE(dmi
, samsung_dmi_table
);
712 static int find_signature(void __iomem
*memcheck
, const char *testStr
)
717 for (loca
= 0; loca
< 0xffff; loca
++) {
718 char temp
= readb(memcheck
+ loca
);
720 if (temp
== testStr
[i
]) {
721 if (i
== strlen(testStr
)-1)
731 static int __init
samsung_init(void)
733 struct backlight_properties props
;
734 struct sabi_retval sretval
;
740 mutex_init(&sabi_mutex
);
742 if (!force
&& !dmi_check_system(samsung_dmi_table
))
745 f0000_segment
= ioremap_nocache(0xf0000, 0xffff);
746 if (!f0000_segment
) {
747 pr_err("Can't map the segment at 0xf0000\n");
751 /* Try to find one of the signatures in memory to find the header */
752 for (i
= 0; sabi_configs
[i
].test_string
!= 0; ++i
) {
753 sabi_config
= &sabi_configs
[i
];
754 loca
= find_signature(f0000_segment
, sabi_config
->test_string
);
759 if (loca
== 0xffff) {
760 pr_err("This computer does not support SABI\n");
761 goto error_no_signature
;
764 /* point to the SMI port Number */
766 sabi
= (f0000_segment
+ loca
);
769 printk(KERN_DEBUG
"This computer supports SABI==%x\n",
771 printk(KERN_DEBUG
"SABI header:\n");
772 printk(KERN_DEBUG
" SMI Port Number = 0x%04x\n",
773 readw(sabi
+ sabi_config
->header_offsets
.port
));
774 printk(KERN_DEBUG
" SMI Interface Function = 0x%02x\n",
775 readb(sabi
+ sabi_config
->header_offsets
.iface_func
));
776 printk(KERN_DEBUG
" SMI enable memory buffer = 0x%02x\n",
777 readb(sabi
+ sabi_config
->header_offsets
.en_mem
));
778 printk(KERN_DEBUG
" SMI restore memory buffer = 0x%02x\n",
779 readb(sabi
+ sabi_config
->header_offsets
.re_mem
));
780 printk(KERN_DEBUG
" SABI data offset = 0x%04x\n",
781 readw(sabi
+ sabi_config
->header_offsets
.data_offset
));
782 printk(KERN_DEBUG
" SABI data segment = 0x%04x\n",
783 readw(sabi
+ sabi_config
->header_offsets
.data_segment
));
786 /* Get a pointer to the SABI Interface */
787 ifaceP
= (readw(sabi
+ sabi_config
->header_offsets
.data_segment
) & 0x0ffff) << 4;
788 ifaceP
+= readw(sabi
+ sabi_config
->header_offsets
.data_offset
) & 0x0ffff;
789 sabi_iface
= ioremap_nocache(ifaceP
, 16);
791 pr_err("Can't remap %x\n", ifaceP
);
792 goto error_no_signature
;
795 printk(KERN_DEBUG
"ifaceP = 0x%08x\n", ifaceP
);
796 printk(KERN_DEBUG
"sabi_iface = %p\n", sabi_iface
);
801 retval
= sabi_get_command(sabi_config
->commands
.get_brightness
,
803 printk(KERN_DEBUG
"brightness = 0x%02x\n", sretval
.retval
[0]);
806 /* Turn on "Linux" mode in the BIOS */
807 if (sabi_config
->commands
.set_linux
!= 0xff) {
808 retval
= sabi_set_command(sabi_config
->commands
.set_linux
,
811 pr_warn("Linux mode was not set!\n");
812 goto error_no_platform
;
816 /* knock up a platform device to hang stuff off of */
817 sdev
= platform_device_register_simple("samsung", -1, NULL
, 0);
819 goto error_no_platform
;
821 /* create a backlight device to talk to this one */
822 memset(&props
, 0, sizeof(struct backlight_properties
));
823 props
.type
= BACKLIGHT_PLATFORM
;
824 props
.max_brightness
= sabi_config
->max_brightness
-
825 sabi_config
->min_brightness
;
826 backlight_device
= backlight_device_register("samsung", &sdev
->dev
,
827 NULL
, &backlight_ops
,
829 if (IS_ERR(backlight_device
))
830 goto error_no_backlight
;
832 backlight_device
->props
.brightness
= read_brightness();
833 backlight_device
->props
.power
= FB_BLANK_UNBLANK
;
834 backlight_update_status(backlight_device
);
836 retval
= init_wireless(sdev
);
840 retval
= device_create_file(&sdev
->dev
, &dev_attr_performance_level
);
842 goto error_file_create
;
850 backlight_device_unregister(backlight_device
);
853 platform_device_unregister(sdev
);
859 iounmap(f0000_segment
);
863 static void __exit
samsung_exit(void)
865 /* Turn off "Linux" mode in the BIOS */
866 if (sabi_config
->commands
.set_linux
!= 0xff)
867 sabi_set_command(sabi_config
->commands
.set_linux
, 0x80);
869 device_remove_file(&sdev
->dev
, &dev_attr_performance_level
);
870 backlight_device_unregister(backlight_device
);
873 iounmap(f0000_segment
);
874 platform_device_unregister(sdev
);
877 module_init(samsung_init
);
878 module_exit(samsung_exit
);
880 MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@suse.de>");
881 MODULE_DESCRIPTION("Samsung Backlight driver");
882 MODULE_LICENSE("GPL");