Staging: samsung-laptop: change sabi_configs to be in C99 format
[pandora-kernel.git] / drivers / staging / samsung-laptop / samsung-laptop.c
1 /*
2  * Samsung N130 Laptop driver
3  *
4  * Copyright (C) 2009 Greg Kroah-Hartman (gregkh@suse.de)
5  * Copyright (C) 2009 Novell Inc.
6  *
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.
10  *
11  */
12 #include <linux/kernel.h>
13 #include <linux/init.h>
14 #include <linux/module.h>
15 #include <linux/delay.h>
16 #include <linux/pci.h>
17 #include <linux/backlight.h>
18 #include <linux/fb.h>
19 #include <linux/dmi.h>
20 #include <linux/platform_device.h>
21 #include <linux/rfkill.h>
22
23 /*
24  * This driver is needed because a number of Samsung laptops do not hook
25  * their control settings through ACPI.  So we have to poke around in the
26  * BIOS to do things like brightness values, and "special" key controls.
27  */
28
29 /*
30  * We have 0 - 8 as valid brightness levels.  The specs say that level 0 should
31  * be reserved by the BIOS (which really doesn't make much sense), we tell
32  * userspace that the value is 0 - 7 and then just tell the hardware 1 - 8
33  */
34 #define MAX_BRIGHT      0x07
35
36
37 #define SABI_IFACE_MAIN                 0x00
38 #define SABI_IFACE_SUB                  0x02
39 #define SABI_IFACE_COMPLETE             0x04
40 #define SABI_IFACE_DATA                 0x05
41
42 /* Structure to get data back to the calling function */
43 struct sabi_retval {
44         u8 retval[20];
45 };
46
47 struct sabi_header_offsets {
48         u8 port;
49         u8 re_mem;
50         u8 iface_func;
51         u8 en_mem;
52         u8 data_offset;
53         u8 data_segment;
54 };
55
56 struct sabi_commands {
57         /* Brightness is 0 - 8, as described above. Value 0 is for the BIOS to use */
58         u8 get_brightness;
59         u8 set_brightness;
60
61         /* first byte:
62          * 0x00 - wireless is off
63          * 0x01 - wireless is on
64          * second byte:
65          * 0x02 - 3G is off
66          * 0x03 - 3G is on
67          * TODO, verify 3G is correct, that doesn't seem right...
68          */
69         u8 get_wireless_button;
70         u8 set_wireless_button;
71
72         /* 0 is off, 1 is on */
73         u8 get_backlight;
74         u8 set_backlight;
75
76         /*
77          * 0x80 or 0x00 - no action
78          * 0x81 - recovery key pressed
79          */
80         u8 get_recovery_mode;
81         u8 set_recovery_mode;
82
83         /*
84          * on seclinux: 0 is low, 1 is high,
85          * on swsmi: 0 is normal, 1 is silent, 2 is turbo
86          */
87         u8 get_performance_level;
88         u8 set_performance_level;
89
90         /*
91          * Tell the BIOS that Linux is running on this machine.
92          * 81 is on, 80 is off
93          */
94         u8 set_linux;
95 };
96
97 struct sabi_performance_level {
98         const char *name;
99         u8 value;
100 };
101
102 struct sabi_config {
103         const char *test_string;
104         u16 main_function;
105         struct sabi_header_offsets header_offsets;
106         struct sabi_commands commands;
107         struct sabi_performance_level performance_levels[4];
108 };
109
110 static struct sabi_config sabi_configs[] = {
111         {
112                 .test_string = "SECLINUX",
113
114                 .main_function = 0x4c59,
115
116                 .header_offsets = {
117                         .port = 0x00,
118                         .re_mem = 0x02,
119                         .iface_func = 0x03,
120                         .en_mem = 0x04,
121                         .data_offset = 0x05,
122                         .data_segment = 0x07,
123                 },
124
125                 .commands = {
126                         .get_brightness = 0x00,
127                         .set_brightness = 0x01,
128
129                         .get_wireless_button = 0x02,
130                         .set_wireless_button = 0x03,
131
132                         .get_backlight = 0x04,
133                         .set_backlight = 0x05,
134
135                         .get_recovery_mode = 0x06,
136                         .set_recovery_mode = 0x07,
137
138                         .get_performance_level = 0x08,
139                         .set_performance_level = 0x09,
140
141                         .set_linux = 0x0a,
142                 },
143
144                 .performance_levels = {
145                         {
146                                 .name = "silent",
147                                 .value = 0,
148                         },
149                         {
150                                 .name = "normal",
151                                 .value = 1,
152                         },
153                         { },
154                 },
155         },
156         {
157                 .test_string = "SwSmi@",
158
159                 .main_function = 0x5843,
160
161                 .header_offsets = {
162                         .port = 0x00,
163                         .re_mem = 0x04,
164                         .iface_func = 0x02,
165                         .en_mem = 0x03,
166                         .data_offset = 0x05,
167                         .data_segment = 0x07,
168                 },
169
170                 .commands = {
171                         .get_brightness = 0x10,
172                         .set_brightness = 0x11,
173
174                         .get_wireless_button = 0x12,
175                         .set_wireless_button = 0x13,
176
177                         .get_backlight = 0x2d,
178                         .set_backlight = 0x2e,
179
180                         .get_recovery_mode = 0xff,
181                         .set_recovery_mode = 0xff,
182
183                         .get_performance_level = 0x31,
184                         .set_performance_level = 0x32,
185
186                         .set_linux = 0xff,
187                 },
188
189                 .performance_levels = {
190                         {
191                                 .name = "normal",
192                                 .value = 0,
193                         },
194                         {
195                                 .name = "silent",
196                                 .value = 1,
197                         },
198                         {
199                                 .name = "overclock",
200                                 .value = 2,
201                         },
202                         { },
203                 },
204         },
205         { },
206 };
207
208 static struct sabi_config *sabi_config;
209
210 static void __iomem *sabi;
211 static void __iomem *sabi_iface;
212 static void __iomem *f0000_segment;
213 static struct backlight_device *backlight_device;
214 static struct mutex sabi_mutex;
215 static struct platform_device *sdev;
216 static struct rfkill *rfk;
217
218 static int force;
219 module_param(force, bool, 0);
220 MODULE_PARM_DESC(force,
221                 "Disable the DMI check and forces the driver to be loaded");
222
223 static int debug;
224 module_param(debug, bool, S_IRUGO | S_IWUSR);
225 MODULE_PARM_DESC(debug, "Debug enabled or not");
226
227 static int sabi_get_command(u8 command, struct sabi_retval *sretval)
228 {
229         int retval = 0;
230         u16 port = readw(sabi + sabi_config->header_offsets.port);
231
232         mutex_lock(&sabi_mutex);
233
234         /* enable memory to be able to write to it */
235         outb(readb(sabi + sabi_config->header_offsets.en_mem), port);
236
237         /* write out the command */
238         writew(sabi_config->main_function, sabi_iface + SABI_IFACE_MAIN);
239         writew(command, sabi_iface + SABI_IFACE_SUB);
240         writeb(0, sabi_iface + SABI_IFACE_COMPLETE);
241         outb(readb(sabi + sabi_config->header_offsets.iface_func), port);
242
243         /* write protect memory to make it safe */
244         outb(readb(sabi + sabi_config->header_offsets.re_mem), port);
245
246         /* see if the command actually succeeded */
247         if (readb(sabi_iface + SABI_IFACE_COMPLETE) == 0xaa &&
248             readb(sabi_iface + SABI_IFACE_DATA) != 0xff) {
249                 /*
250                  * It did!
251                  * Save off the data into a structure so the caller use it.
252                  * Right now we only care about the first 4 bytes,
253                  * I suppose there are commands that need more, but I don't
254                  * know about them.
255                  */
256                 sretval->retval[0] = readb(sabi_iface + SABI_IFACE_DATA);
257                 sretval->retval[1] = readb(sabi_iface + SABI_IFACE_DATA + 1);
258                 sretval->retval[2] = readb(sabi_iface + SABI_IFACE_DATA + 2);
259                 sretval->retval[3] = readb(sabi_iface + SABI_IFACE_DATA + 3);
260                 goto exit;
261         }
262
263         /* Something bad happened, so report it and error out */
264         printk(KERN_WARNING "SABI command 0x%02x failed with completion flag 0x%02x and output 0x%02x\n",
265                 command, readb(sabi_iface + SABI_IFACE_COMPLETE),
266                 readb(sabi_iface + SABI_IFACE_DATA));
267         retval = -EINVAL;
268 exit:
269         mutex_unlock(&sabi_mutex);
270         return retval;
271
272 }
273
274 static int sabi_set_command(u8 command, u8 data)
275 {
276         int retval = 0;
277         u16 port = readw(sabi + sabi_config->header_offsets.port);
278
279         mutex_lock(&sabi_mutex);
280
281         /* enable memory to be able to write to it */
282         outb(readb(sabi + sabi_config->header_offsets.en_mem), port);
283
284         /* write out the command */
285         writew(sabi_config->main_function, sabi_iface + SABI_IFACE_MAIN);
286         writew(command, sabi_iface + SABI_IFACE_SUB);
287         writeb(0, sabi_iface + SABI_IFACE_COMPLETE);
288         writeb(data, sabi_iface + SABI_IFACE_DATA);
289         outb(readb(sabi + sabi_config->header_offsets.iface_func), port);
290
291         /* write protect memory to make it safe */
292         outb(readb(sabi + sabi_config->header_offsets.re_mem), port);
293
294         /* see if the command actually succeeded */
295         if (readb(sabi_iface + SABI_IFACE_COMPLETE) == 0xaa &&
296             readb(sabi_iface + SABI_IFACE_DATA) != 0xff) {
297                 /* it did! */
298                 goto exit;
299         }
300
301         /* Something bad happened, so report it and error out */
302         printk(KERN_WARNING "SABI command 0x%02x failed with completion flag 0x%02x and output 0x%02x\n",
303                 command, readb(sabi_iface + SABI_IFACE_COMPLETE),
304                 readb(sabi_iface + SABI_IFACE_DATA));
305         retval = -EINVAL;
306 exit:
307         mutex_unlock(&sabi_mutex);
308         return retval;
309 }
310
311 static void test_backlight(void)
312 {
313         struct sabi_retval sretval;
314
315         sabi_get_command(sabi_config->commands.get_backlight, &sretval);
316         printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]);
317
318         sabi_set_command(sabi_config->commands.set_backlight, 0);
319         printk(KERN_DEBUG "backlight should be off\n");
320
321         sabi_get_command(sabi_config->commands.get_backlight, &sretval);
322         printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]);
323
324         msleep(1000);
325
326         sabi_set_command(sabi_config->commands.set_backlight, 1);
327         printk(KERN_DEBUG "backlight should be on\n");
328
329         sabi_get_command(sabi_config->commands.get_backlight, &sretval);
330         printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]);
331 }
332
333 static void test_wireless(void)
334 {
335         struct sabi_retval sretval;
336
337         sabi_get_command(sabi_config->commands.get_wireless_button, &sretval);
338         printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]);
339
340         sabi_set_command(sabi_config->commands.set_wireless_button, 0);
341         printk(KERN_DEBUG "wireless led should be off\n");
342
343         sabi_get_command(sabi_config->commands.get_wireless_button, &sretval);
344         printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]);
345
346         msleep(1000);
347
348         sabi_set_command(sabi_config->commands.set_wireless_button, 1);
349         printk(KERN_DEBUG "wireless led should be on\n");
350
351         sabi_get_command(sabi_config->commands.get_wireless_button, &sretval);
352         printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]);
353 }
354
355 static u8 read_brightness(void)
356 {
357         struct sabi_retval sretval;
358         int user_brightness = 0;
359         int retval;
360
361         retval = sabi_get_command(sabi_config->commands.get_brightness, &sretval);
362         if (!retval)
363                 user_brightness = sretval.retval[0];
364                 if (user_brightness != 0)
365                         --user_brightness;
366         return user_brightness;
367 }
368
369 static void set_brightness(u8 user_brightness)
370 {
371         sabi_set_command(sabi_config->commands.set_brightness, user_brightness + 1);
372 }
373
374 static int get_brightness(struct backlight_device *bd)
375 {
376         return (int)read_brightness();
377 }
378
379 static int update_status(struct backlight_device *bd)
380 {
381         set_brightness(bd->props.brightness);
382
383         if (bd->props.power == FB_BLANK_UNBLANK)
384                 sabi_set_command(sabi_config->commands.set_backlight, 1);
385         else
386                 sabi_set_command(sabi_config->commands.set_backlight, 0);
387         return 0;
388 }
389
390 static const struct backlight_ops backlight_ops = {
391         .get_brightness = get_brightness,
392         .update_status  = update_status,
393 };
394
395 static int rfkill_set(void *data, bool blocked)
396 {
397         /* Do something with blocked...*/
398         /*
399          * blocked == false is on
400          * blocked == true is off
401          */
402         if (blocked)
403                 sabi_set_command(sabi_config->commands.set_wireless_button, 0);
404         else
405                 sabi_set_command(sabi_config->commands.set_wireless_button, 1);
406
407         return 0;
408 }
409
410 static struct rfkill_ops rfkill_ops = {
411         .set_block = rfkill_set,
412 };
413
414 static int init_wireless(struct platform_device *sdev)
415 {
416         int retval;
417
418         rfk = rfkill_alloc("samsung-wifi", &sdev->dev, RFKILL_TYPE_WLAN,
419                            &rfkill_ops, NULL);
420         if (!rfk)
421                 return -ENOMEM;
422
423         retval = rfkill_register(rfk);
424         if (retval) {
425                 rfkill_destroy(rfk);
426                 return -ENODEV;
427         }
428
429         return 0;
430 }
431
432 static void destroy_wireless(void)
433 {
434         rfkill_unregister(rfk);
435         rfkill_destroy(rfk);
436 }
437
438 static ssize_t get_performance_level(struct device *dev,
439                                      struct device_attribute *attr, char *buf)
440 {
441         struct sabi_retval sretval;
442         int retval;
443         int pLevel;
444
445         /* Read the state */
446         retval = sabi_get_command(sabi_config->commands.get_performance_level, &sretval);
447         if (retval)
448                 return retval;
449
450         /* The logic is backwards, yeah, lots of fun... */
451         for (pLevel = 0; sabi_config->performance_levels[pLevel].name; ++pLevel) {
452                 if (sretval.retval[0] == sabi_config->performance_levels[pLevel].value)
453                         return sprintf(buf, "%s\n", sabi_config->performance_levels[pLevel].name);
454         }
455         return sprintf(buf, "%s\n", "unknown");
456 }
457
458 static ssize_t set_performance_level(struct device *dev,
459                                 struct device_attribute *attr, const char *buf,
460                                 size_t count)
461 {
462         if (count >= 1) {
463                 int pLevel;
464                 for (pLevel = 0; sabi_config->performance_levels[pLevel].name; ++pLevel) {
465                         struct sabi_performance_level *level =
466                                 &sabi_config->performance_levels[pLevel];
467                         if (!strncasecmp(level->name, buf, strlen(level->name))) {
468                                 sabi_set_command(sabi_config->commands.set_performance_level,
469                                                  level->value);
470                                 break;
471                         }
472                 }
473                 if (!sabi_config->performance_levels[pLevel].name)
474                         return -EINVAL;
475         }
476         return count;
477 }
478 static DEVICE_ATTR(performance_level, S_IWUSR | S_IRUGO,
479                    get_performance_level, set_performance_level);
480
481
482 static int __init dmi_check_cb(const struct dmi_system_id *id)
483 {
484         printk(KERN_INFO KBUILD_MODNAME ": found laptop model '%s'\n",
485                 id->ident);
486         return 0;
487 }
488
489 static struct dmi_system_id __initdata samsung_dmi_table[] = {
490         {
491                 .ident = "N128",
492                 .matches = {
493                         DMI_MATCH(DMI_SYS_VENDOR,
494                                         "SAMSUNG ELECTRONICS CO., LTD."),
495                         DMI_MATCH(DMI_PRODUCT_NAME, "N128"),
496                         DMI_MATCH(DMI_BOARD_NAME, "N128"),
497                 },
498                 .callback = dmi_check_cb,
499         },
500         {
501                 .ident = "N130",
502                 .matches = {
503                         DMI_MATCH(DMI_SYS_VENDOR,
504                                         "SAMSUNG ELECTRONICS CO., LTD."),
505                         DMI_MATCH(DMI_PRODUCT_NAME, "N130"),
506                         DMI_MATCH(DMI_BOARD_NAME, "N130"),
507                 },
508                 .callback = dmi_check_cb,
509         },
510         {
511                 .ident = "X125",
512                 .matches = {
513                         DMI_MATCH(DMI_SYS_VENDOR,
514                                         "SAMSUNG ELECTRONICS CO., LTD."),
515                         DMI_MATCH(DMI_PRODUCT_NAME, "X125"),
516                         DMI_MATCH(DMI_BOARD_NAME, "X125"),
517                 },
518                 .callback = dmi_check_cb,
519         },
520         { },
521 };
522 MODULE_DEVICE_TABLE(dmi, samsung_dmi_table);
523
524 static int find_signature(void __iomem *memcheck, const char *testStr)
525 {
526         int pStr;
527         int loca;
528         pStr = 0;
529         for (loca = 0; loca < 0xffff; loca++) {
530                 char temp = readb(memcheck + loca);
531
532                 if (temp == testStr[pStr]) {
533                         if (pStr == strlen(testStr)-1)
534                                 break;
535                         ++pStr;
536                 } else {
537                         pStr = 0;
538                 }
539         }
540         return loca;
541 }
542
543 static int __init samsung_init(void)
544 {
545         struct backlight_properties props;
546         struct sabi_retval sretval;
547         unsigned int ifaceP;
548         int pConfig;
549         int loca;
550         int retval;
551
552         mutex_init(&sabi_mutex);
553
554         if (!force && !dmi_check_system(samsung_dmi_table))
555                 return -ENODEV;
556
557         f0000_segment = ioremap(0xf0000, 0xffff);
558         if (!f0000_segment) {
559                 printk(KERN_ERR "Can't map the segment at 0xf0000\n");
560                 return -EINVAL;
561         }
562
563         /* Try to find one of the signatures in memory to find the header */
564         for (pConfig = 0; sabi_configs[pConfig].test_string != 0; ++pConfig) {
565                 sabi_config = &sabi_configs[pConfig];
566                 loca = find_signature(f0000_segment, sabi_config->test_string);
567                 if (loca != 0xffff)
568                         break;
569         }
570
571         if (loca == 0xffff) {
572                 printk(KERN_ERR "This computer does not support SABI\n");
573                 goto error_no_signature;
574         }
575
576         /* point to the SMI port Number */
577         loca += 1;
578         sabi = (f0000_segment + loca);
579
580         if (debug) {
581                 printk(KERN_DEBUG "This computer supports SABI==%x\n",
582                         loca + 0xf0000 - 6);
583                 printk(KERN_DEBUG "SABI header:\n");
584                 printk(KERN_DEBUG " SMI Port Number = 0x%04x\n",
585                         readw(sabi + sabi_config->header_offsets.port));
586                 printk(KERN_DEBUG " SMI Interface Function = 0x%02x\n",
587                         readb(sabi + sabi_config->header_offsets.iface_func));
588                 printk(KERN_DEBUG " SMI enable memory buffer = 0x%02x\n",
589                         readb(sabi + sabi_config->header_offsets.en_mem));
590                 printk(KERN_DEBUG " SMI restore memory buffer = 0x%02x\n",
591                         readb(sabi + sabi_config->header_offsets.re_mem));
592                 printk(KERN_DEBUG " SABI data offset = 0x%04x\n",
593                         readw(sabi + sabi_config->header_offsets.data_offset));
594                 printk(KERN_DEBUG " SABI data segment = 0x%04x\n",
595                         readw(sabi + sabi_config->header_offsets.data_segment));
596         }
597
598         /* Get a pointer to the SABI Interface */
599         ifaceP = (readw(sabi + sabi_config->header_offsets.data_segment) & 0x0ffff) << 4;
600         ifaceP += readw(sabi + sabi_config->header_offsets.data_offset) & 0x0ffff;
601         sabi_iface = ioremap(ifaceP, 16);
602         if (!sabi_iface) {
603                 printk(KERN_ERR "Can't remap %x\n", ifaceP);
604                 goto exit;
605         }
606         if (debug) {
607                 printk(KERN_DEBUG "ifaceP = 0x%08x\n", ifaceP);
608                 printk(KERN_DEBUG "sabi_iface = %p\n", sabi_iface);
609
610                 test_backlight();
611                 test_wireless();
612
613                 retval = sabi_get_command(sabi_config->commands.get_brightness, &sretval);
614                 printk(KERN_DEBUG "brightness = 0x%02x\n", sretval.retval[0]);
615         }
616
617         /* Turn on "Linux" mode in the BIOS */
618         if (sabi_config->commands.set_linux != 0xff) {
619                 retval = sabi_set_command(sabi_config->commands.set_linux, 0x81);
620                 if (retval) {
621                         printk(KERN_ERR KBUILD_MODNAME ": Linux mode was not set!\n");
622                         goto error_no_platform;
623                 }
624         }
625
626         /* knock up a platform device to hang stuff off of */
627         sdev = platform_device_register_simple("samsung", -1, NULL, 0);
628         if (IS_ERR(sdev))
629                 goto error_no_platform;
630
631         /* create a backlight device to talk to this one */
632         memset(&props, 0, sizeof(struct backlight_properties));
633         props.max_brightness = MAX_BRIGHT;
634         backlight_device = backlight_device_register("samsung", &sdev->dev,
635                                                      NULL, &backlight_ops,
636                                                      &props);
637         if (IS_ERR(backlight_device))
638                 goto error_no_backlight;
639
640         backlight_device->props.brightness = read_brightness();
641         backlight_device->props.power = FB_BLANK_UNBLANK;
642         backlight_update_status(backlight_device);
643
644         retval = init_wireless(sdev);
645         if (retval)
646                 goto error_no_rfk;
647
648         retval = device_create_file(&sdev->dev, &dev_attr_performance_level);
649         if (retval)
650                 goto error_file_create;
651
652 exit:
653         return 0;
654
655 error_file_create:
656         destroy_wireless();
657
658 error_no_rfk:
659         backlight_device_unregister(backlight_device);
660
661 error_no_backlight:
662         platform_device_unregister(sdev);
663
664 error_no_platform:
665         iounmap(sabi_iface);
666
667 error_no_signature:
668         iounmap(f0000_segment);
669         return -EINVAL;
670 }
671
672 static void __exit samsung_exit(void)
673 {
674         /* Turn off "Linux" mode in the BIOS */
675         if (sabi_config->commands.set_linux != 0xff)
676                 sabi_set_command(sabi_config->commands.set_linux, 0x80);
677
678         device_remove_file(&sdev->dev, &dev_attr_performance_level);
679         backlight_device_unregister(backlight_device);
680         destroy_wireless();
681         iounmap(sabi_iface);
682         iounmap(f0000_segment);
683         platform_device_unregister(sdev);
684 }
685
686 module_init(samsung_init);
687 module_exit(samsung_exit);
688
689 MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@suse.de>");
690 MODULE_DESCRIPTION("Samsung Backlight driver");
691 MODULE_LICENSE("GPL");