aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform/x86/dell-laptop.c
diff options
context:
space:
mode:
authorAndrea Bastoni <bastoni@cs.unc.edu>2010-05-30 19:16:45 -0400
committerAndrea Bastoni <bastoni@cs.unc.edu>2010-05-30 19:16:45 -0400
commitada47b5fe13d89735805b566185f4885f5a3f750 (patch)
tree644b88f8a71896307d71438e9b3af49126ffb22b /drivers/platform/x86/dell-laptop.c
parent43e98717ad40a4ae64545b5ba047c7b86aa44f4f (diff)
parent3280f21d43ee541f97f8cda5792150d2dbec20d5 (diff)
Merge branch 'wip-2.6.34' into old-private-masterarchived-private-master
Diffstat (limited to 'drivers/platform/x86/dell-laptop.c')
-rw-r--r--drivers/platform/x86/dell-laptop.c353
1 files changed, 289 insertions, 64 deletions
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c
index 74909c4aaeea..661e3ac4d5b1 100644
--- a/drivers/platform/x86/dell-laptop.c
+++ b/drivers/platform/x86/dell-laptop.c
@@ -22,6 +22,9 @@
22#include <linux/rfkill.h> 22#include <linux/rfkill.h>
23#include <linux/power_supply.h> 23#include <linux/power_supply.h>
24#include <linux/acpi.h> 24#include <linux/acpi.h>
25#include <linux/mm.h>
26#include <linux/i8042.h>
27#include <linux/slab.h>
25#include "../../firmware/dcdbas.h" 28#include "../../firmware/dcdbas.h"
26 29
27#define BRIGHTNESS_TOKEN 0x7d 30#define BRIGHTNESS_TOKEN 0x7d
@@ -58,6 +61,14 @@ static int da_command_code;
58static int da_num_tokens; 61static int da_num_tokens;
59static struct calling_interface_token *da_tokens; 62static struct calling_interface_token *da_tokens;
60 63
64static struct platform_driver platform_driver = {
65 .driver = {
66 .name = "dell-laptop",
67 .owner = THIS_MODULE,
68 }
69};
70
71static struct platform_device *platform_device;
61static struct backlight_device *dell_backlight_device; 72static struct backlight_device *dell_backlight_device;
62static struct rfkill *wifi_rfkill; 73static struct rfkill *wifi_rfkill;
63static struct rfkill *bluetooth_rfkill; 74static struct rfkill *bluetooth_rfkill;
@@ -71,10 +82,74 @@ static const struct dmi_system_id __initdata dell_device_table[] = {
71 DMI_MATCH(DMI_CHASSIS_TYPE, "8"), 82 DMI_MATCH(DMI_CHASSIS_TYPE, "8"),
72 }, 83 },
73 }, 84 },
85 {
86 .ident = "Dell Computer Corporation",
87 .matches = {
88 DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
89 DMI_MATCH(DMI_CHASSIS_TYPE, "8"),
90 },
91 },
74 { } 92 { }
75}; 93};
76 94
77static void parse_da_table(const struct dmi_header *dm) 95static struct dmi_system_id __devinitdata dell_blacklist[] = {
96 /* Supported by compal-laptop */
97 {
98 .ident = "Dell Mini 9",
99 .matches = {
100 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
101 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 910"),
102 },
103 },
104 {
105 .ident = "Dell Mini 10",
106 .matches = {
107 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
108 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1010"),
109 },
110 },
111 {
112 .ident = "Dell Mini 10v",
113 .matches = {
114 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
115 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1011"),
116 },
117 },
118 {
119 .ident = "Dell Inspiron 11z",
120 .matches = {
121 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
122 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1110"),
123 },
124 },
125 {
126 .ident = "Dell Mini 12",
127 .matches = {
128 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
129 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1210"),
130 },
131 },
132 {}
133};
134
135static struct calling_interface_buffer *buffer;
136static struct page *bufferpage;
137static DEFINE_MUTEX(buffer_mutex);
138
139static int hwswitch_state;
140
141static void get_buffer(void)
142{
143 mutex_lock(&buffer_mutex);
144 memset(buffer, 0, sizeof(struct calling_interface_buffer));
145}
146
147static void release_buffer(void)
148{
149 mutex_unlock(&buffer_mutex);
150}
151
152static void __init parse_da_table(const struct dmi_header *dm)
78{ 153{
79 /* Final token is a terminator, so we don't want to copy it */ 154 /* Final token is a terminator, so we don't want to copy it */
80 int tokens = (dm->length-11)/sizeof(struct calling_interface_token)-1; 155 int tokens = (dm->length-11)/sizeof(struct calling_interface_token)-1;
@@ -103,7 +178,7 @@ static void parse_da_table(const struct dmi_header *dm)
103 da_num_tokens += tokens; 178 da_num_tokens += tokens;
104} 179}
105 180
106static void find_tokens(const struct dmi_header *dm, void *dummy) 181static void __init find_tokens(const struct dmi_header *dm, void *dummy)
107{ 182{
108 switch (dm->type) { 183 switch (dm->type) {
109 case 0xd4: /* Indexed IO */ 184 case 0xd4: /* Indexed IO */
@@ -152,6 +227,8 @@ dell_send_request(struct calling_interface_buffer *buffer, int class,
152/* Derived from information in DellWirelessCtl.cpp: 227/* Derived from information in DellWirelessCtl.cpp:
153 Class 17, select 11 is radio control. It returns an array of 32-bit values. 228 Class 17, select 11 is radio control. It returns an array of 32-bit values.
154 229
230 Input byte 0 = 0: Wireless information
231
155 result[0]: return code 232 result[0]: return code
156 result[1]: 233 result[1]:
157 Bit 0: Hardware switch supported 234 Bit 0: Hardware switch supported
@@ -172,33 +249,62 @@ dell_send_request(struct calling_interface_buffer *buffer, int class,
172 Bits 20-31: Reserved 249 Bits 20-31: Reserved
173 result[2]: NVRAM size in bytes 250 result[2]: NVRAM size in bytes
174 result[3]: NVRAM format version number 251 result[3]: NVRAM format version number
252
253 Input byte 0 = 2: Wireless switch configuration
254 result[0]: return code
255 result[1]:
256 Bit 0: Wifi controlled by switch
257 Bit 1: Bluetooth controlled by switch
258 Bit 2: WWAN controlled by switch
259 Bits 3-6: Reserved
260 Bit 7: Wireless switch config locked
261 Bit 8: Wifi locator enabled
262 Bits 9-14: Reserved
263 Bit 15: Wifi locator setting locked
264 Bits 16-31: Reserved
175*/ 265*/
176 266
177static int dell_rfkill_set(void *data, bool blocked) 267static int dell_rfkill_set(void *data, bool blocked)
178{ 268{
179 struct calling_interface_buffer buffer;
180 int disable = blocked ? 1 : 0; 269 int disable = blocked ? 1 : 0;
181 unsigned long radio = (unsigned long)data; 270 unsigned long radio = (unsigned long)data;
271 int hwswitch_bit = (unsigned long)data - 1;
272 int ret = 0;
182 273
183 memset(&buffer, 0, sizeof(struct calling_interface_buffer)); 274 get_buffer();
184 buffer.input[0] = (1 | (radio<<8) | (disable << 16)); 275 dell_send_request(buffer, 17, 11);
185 dell_send_request(&buffer, 17, 11);
186 276
187 return 0; 277 /* If the hardware switch controls this radio, and the hardware
278 switch is disabled, don't allow changing the software state */
279 if ((hwswitch_state & BIT(hwswitch_bit)) &&
280 !(buffer->output[1] & BIT(16))) {
281 ret = -EINVAL;
282 goto out;
283 }
284
285 buffer->input[0] = (1 | (radio<<8) | (disable << 16));
286 dell_send_request(buffer, 17, 11);
287
288out:
289 release_buffer();
290 return ret;
188} 291}
189 292
190static void dell_rfkill_query(struct rfkill *rfkill, void *data) 293static void dell_rfkill_query(struct rfkill *rfkill, void *data)
191{ 294{
192 struct calling_interface_buffer buffer;
193 int status; 295 int status;
194 int bit = (unsigned long)data + 16; 296 int bit = (unsigned long)data + 16;
297 int hwswitch_bit = (unsigned long)data - 1;
195 298
196 memset(&buffer, 0, sizeof(struct calling_interface_buffer)); 299 get_buffer();
197 dell_send_request(&buffer, 17, 11); 300 dell_send_request(buffer, 17, 11);
198 status = buffer.output[1]; 301 status = buffer->output[1];
302 release_buffer();
199 303
200 if (status & BIT(bit)) 304 rfkill_set_sw_state(rfkill, !!(status & BIT(bit)));
201 rfkill_set_hw_state(rfkill, !!(status & BIT(16))); 305
306 if (hwswitch_state & (BIT(hwswitch_bit)))
307 rfkill_set_hw_state(rfkill, !(status & BIT(16)));
202} 308}
203 309
204static const struct rfkill_ops dell_rfkill_ops = { 310static const struct rfkill_ops dell_rfkill_ops = {
@@ -206,18 +312,40 @@ static const struct rfkill_ops dell_rfkill_ops = {
206 .query = dell_rfkill_query, 312 .query = dell_rfkill_query,
207}; 313};
208 314
209static int dell_setup_rfkill(void) 315static void dell_update_rfkill(struct work_struct *ignored)
316{
317 if (wifi_rfkill)
318 dell_rfkill_query(wifi_rfkill, (void *)1);
319 if (bluetooth_rfkill)
320 dell_rfkill_query(bluetooth_rfkill, (void *)2);
321 if (wwan_rfkill)
322 dell_rfkill_query(wwan_rfkill, (void *)3);
323}
324static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill);
325
326
327static int __init dell_setup_rfkill(void)
210{ 328{
211 struct calling_interface_buffer buffer;
212 int status; 329 int status;
213 int ret; 330 int ret;
214 331
215 memset(&buffer, 0, sizeof(struct calling_interface_buffer)); 332 if (dmi_check_system(dell_blacklist)) {
216 dell_send_request(&buffer, 17, 11); 333 printk(KERN_INFO "dell-laptop: Blacklisted hardware detected - "
217 status = buffer.output[1]; 334 "not enabling rfkill\n");
335 return 0;
336 }
337
338 get_buffer();
339 dell_send_request(buffer, 17, 11);
340 status = buffer->output[1];
341 buffer->input[0] = 0x2;
342 dell_send_request(buffer, 17, 11);
343 hwswitch_state = buffer->output[1];
344 release_buffer();
218 345
219 if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) { 346 if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) {
220 wifi_rfkill = rfkill_alloc("dell-wifi", NULL, RFKILL_TYPE_WLAN, 347 wifi_rfkill = rfkill_alloc("dell-wifi", &platform_device->dev,
348 RFKILL_TYPE_WLAN,
221 &dell_rfkill_ops, (void *) 1); 349 &dell_rfkill_ops, (void *) 1);
222 if (!wifi_rfkill) { 350 if (!wifi_rfkill) {
223 ret = -ENOMEM; 351 ret = -ENOMEM;
@@ -229,7 +357,8 @@ static int dell_setup_rfkill(void)
229 } 357 }
230 358
231 if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) { 359 if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) {
232 bluetooth_rfkill = rfkill_alloc("dell-bluetooth", NULL, 360 bluetooth_rfkill = rfkill_alloc("dell-bluetooth",
361 &platform_device->dev,
233 RFKILL_TYPE_BLUETOOTH, 362 RFKILL_TYPE_BLUETOOTH,
234 &dell_rfkill_ops, (void *) 2); 363 &dell_rfkill_ops, (void *) 2);
235 if (!bluetooth_rfkill) { 364 if (!bluetooth_rfkill) {
@@ -242,7 +371,9 @@ static int dell_setup_rfkill(void)
242 } 371 }
243 372
244 if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) { 373 if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) {
245 wwan_rfkill = rfkill_alloc("dell-wwan", NULL, RFKILL_TYPE_WWAN, 374 wwan_rfkill = rfkill_alloc("dell-wwan",
375 &platform_device->dev,
376 RFKILL_TYPE_WWAN,
246 &dell_rfkill_ops, (void *) 3); 377 &dell_rfkill_ops, (void *) 3);
247 if (!wwan_rfkill) { 378 if (!wwan_rfkill) {
248 ret = -ENOMEM; 379 ret = -ENOMEM;
@@ -268,41 +399,67 @@ err_wifi:
268 return ret; 399 return ret;
269} 400}
270 401
402static void dell_cleanup_rfkill(void)
403{
404 if (wifi_rfkill) {
405 rfkill_unregister(wifi_rfkill);
406 rfkill_destroy(wifi_rfkill);
407 }
408 if (bluetooth_rfkill) {
409 rfkill_unregister(bluetooth_rfkill);
410 rfkill_destroy(bluetooth_rfkill);
411 }
412 if (wwan_rfkill) {
413 rfkill_unregister(wwan_rfkill);
414 rfkill_destroy(wwan_rfkill);
415 }
416}
417
271static int dell_send_intensity(struct backlight_device *bd) 418static int dell_send_intensity(struct backlight_device *bd)
272{ 419{
273 struct calling_interface_buffer buffer; 420 int ret = 0;
274 421
275 memset(&buffer, 0, sizeof(struct calling_interface_buffer)); 422 get_buffer();
276 buffer.input[0] = find_token_location(BRIGHTNESS_TOKEN); 423 buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN);
277 buffer.input[1] = bd->props.brightness; 424 buffer->input[1] = bd->props.brightness;
278 425
279 if (buffer.input[0] == -1) 426 if (buffer->input[0] == -1) {
280 return -ENODEV; 427 ret = -ENODEV;
428 goto out;
429 }
281 430
282 if (power_supply_is_system_supplied() > 0) 431 if (power_supply_is_system_supplied() > 0)
283 dell_send_request(&buffer, 1, 2); 432 dell_send_request(buffer, 1, 2);
284 else 433 else
285 dell_send_request(&buffer, 1, 1); 434 dell_send_request(buffer, 1, 1);
286 435
436out:
437 release_buffer();
287 return 0; 438 return 0;
288} 439}
289 440
290static int dell_get_intensity(struct backlight_device *bd) 441static int dell_get_intensity(struct backlight_device *bd)
291{ 442{
292 struct calling_interface_buffer buffer; 443 int ret = 0;
293 444
294 memset(&buffer, 0, sizeof(struct calling_interface_buffer)); 445 get_buffer();
295 buffer.input[0] = find_token_location(BRIGHTNESS_TOKEN); 446 buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN);
296 447
297 if (buffer.input[0] == -1) 448 if (buffer->input[0] == -1) {
298 return -ENODEV; 449 ret = -ENODEV;
450 goto out;
451 }
299 452
300 if (power_supply_is_system_supplied() > 0) 453 if (power_supply_is_system_supplied() > 0)
301 dell_send_request(&buffer, 0, 2); 454 dell_send_request(buffer, 0, 2);
302 else 455 else
303 dell_send_request(&buffer, 0, 1); 456 dell_send_request(buffer, 0, 1);
304 457
305 return buffer.output[1]; 458out:
459 release_buffer();
460 if (ret)
461 return ret;
462 return buffer->output[1];
306} 463}
307 464
308static struct backlight_ops dell_ops = { 465static struct backlight_ops dell_ops = {
@@ -310,9 +467,32 @@ static struct backlight_ops dell_ops = {
310 .update_status = dell_send_intensity, 467 .update_status = dell_send_intensity,
311}; 468};
312 469
470bool dell_laptop_i8042_filter(unsigned char data, unsigned char str,
471 struct serio *port)
472{
473 static bool extended;
474
475 if (str & 0x20)
476 return false;
477
478 if (unlikely(data == 0xe0)) {
479 extended = true;
480 return false;
481 } else if (unlikely(extended)) {
482 switch (data) {
483 case 0x8:
484 schedule_delayed_work(&dell_rfkill_work,
485 round_jiffies_relative(HZ));
486 break;
487 }
488 extended = false;
489 }
490
491 return false;
492}
493
313static int __init dell_init(void) 494static int __init dell_init(void)
314{ 495{
315 struct calling_interface_buffer buffer;
316 int max_intensity = 0; 496 int max_intensity = 0;
317 int ret; 497 int ret;
318 498
@@ -326,11 +506,41 @@ static int __init dell_init(void)
326 return -ENODEV; 506 return -ENODEV;
327 } 507 }
328 508
509 ret = platform_driver_register(&platform_driver);
510 if (ret)
511 goto fail_platform_driver;
512 platform_device = platform_device_alloc("dell-laptop", -1);
513 if (!platform_device) {
514 ret = -ENOMEM;
515 goto fail_platform_device1;
516 }
517 ret = platform_device_add(platform_device);
518 if (ret)
519 goto fail_platform_device2;
520
521 /*
522 * Allocate buffer below 4GB for SMI data--only 32-bit physical addr
523 * is passed to SMI handler.
524 */
525 bufferpage = alloc_page(GFP_KERNEL | GFP_DMA32);
526
527 if (!bufferpage)
528 goto fail_buffer;
529 buffer = page_address(bufferpage);
530 mutex_init(&buffer_mutex);
531
329 ret = dell_setup_rfkill(); 532 ret = dell_setup_rfkill();
330 533
331 if (ret) { 534 if (ret) {
332 printk(KERN_WARNING "dell-laptop: Unable to setup rfkill\n"); 535 printk(KERN_WARNING "dell-laptop: Unable to setup rfkill\n");
333 goto out; 536 goto fail_rfkill;
537 }
538
539 ret = i8042_install_filter(dell_laptop_i8042_filter);
540 if (ret) {
541 printk(KERN_WARNING
542 "dell-laptop: Unable to install key filter\n");
543 goto fail_filter;
334 } 544 }
335 545
336#ifdef CONFIG_ACPI 546#ifdef CONFIG_ACPI
@@ -341,53 +551,67 @@ static int __init dell_init(void)
341 return 0; 551 return 0;
342#endif 552#endif
343 553
344 memset(&buffer, 0, sizeof(struct calling_interface_buffer)); 554 get_buffer();
345 buffer.input[0] = find_token_location(BRIGHTNESS_TOKEN); 555 buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN);
346 556 if (buffer->input[0] != -1) {
347 if (buffer.input[0] != -1) { 557 dell_send_request(buffer, 0, 2);
348 dell_send_request(&buffer, 0, 2); 558 max_intensity = buffer->output[3];
349 max_intensity = buffer.output[3];
350 } 559 }
560 release_buffer();
351 561
352 if (max_intensity) { 562 if (max_intensity) {
353 dell_backlight_device = backlight_device_register( 563 struct backlight_properties props;
354 "dell_backlight", 564 memset(&props, 0, sizeof(struct backlight_properties));
355 NULL, NULL, 565 props.max_brightness = max_intensity;
356 &dell_ops); 566 dell_backlight_device = backlight_device_register("dell_backlight",
567 &platform_device->dev,
568 NULL,
569 &dell_ops,
570 &props);
357 571
358 if (IS_ERR(dell_backlight_device)) { 572 if (IS_ERR(dell_backlight_device)) {
359 ret = PTR_ERR(dell_backlight_device); 573 ret = PTR_ERR(dell_backlight_device);
360 dell_backlight_device = NULL; 574 dell_backlight_device = NULL;
361 goto out; 575 goto fail_backlight;
362 } 576 }
363 577
364 dell_backlight_device->props.max_brightness = max_intensity;
365 dell_backlight_device->props.brightness = 578 dell_backlight_device->props.brightness =
366 dell_get_intensity(dell_backlight_device); 579 dell_get_intensity(dell_backlight_device);
367 backlight_update_status(dell_backlight_device); 580 backlight_update_status(dell_backlight_device);
368 } 581 }
369 582
370 return 0; 583 return 0;
371out: 584
372 if (wifi_rfkill) 585fail_backlight:
373 rfkill_unregister(wifi_rfkill); 586 i8042_remove_filter(dell_laptop_i8042_filter);
374 if (bluetooth_rfkill) 587 cancel_delayed_work_sync(&dell_rfkill_work);
375 rfkill_unregister(bluetooth_rfkill); 588fail_filter:
376 if (wwan_rfkill) 589 dell_cleanup_rfkill();
377 rfkill_unregister(wwan_rfkill); 590fail_rfkill:
591 free_page((unsigned long)bufferpage);
592fail_buffer:
593 platform_device_del(platform_device);
594fail_platform_device2:
595 platform_device_put(platform_device);
596fail_platform_device1:
597 platform_driver_unregister(&platform_driver);
598fail_platform_driver:
378 kfree(da_tokens); 599 kfree(da_tokens);
379 return ret; 600 return ret;
380} 601}
381 602
382static void __exit dell_exit(void) 603static void __exit dell_exit(void)
383{ 604{
605 i8042_remove_filter(dell_laptop_i8042_filter);
606 cancel_delayed_work_sync(&dell_rfkill_work);
384 backlight_device_unregister(dell_backlight_device); 607 backlight_device_unregister(dell_backlight_device);
385 if (wifi_rfkill) 608 dell_cleanup_rfkill();
386 rfkill_unregister(wifi_rfkill); 609 if (platform_device) {
387 if (bluetooth_rfkill) 610 platform_device_unregister(platform_device);
388 rfkill_unregister(bluetooth_rfkill); 611 platform_driver_unregister(&platform_driver);
389 if (wwan_rfkill) 612 }
390 rfkill_unregister(wwan_rfkill); 613 kfree(da_tokens);
614 free_page((unsigned long)buffer);
391} 615}
392 616
393module_init(dell_init); 617module_init(dell_init);
@@ -397,3 +621,4 @@ MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
397MODULE_DESCRIPTION("Dell laptop driver"); 621MODULE_DESCRIPTION("Dell laptop driver");
398MODULE_LICENSE("GPL"); 622MODULE_LICENSE("GPL");
399MODULE_ALIAS("dmi:*svnDellInc.:*:ct8:*"); 623MODULE_ALIAS("dmi:*svnDellInc.:*:ct8:*");
624MODULE_ALIAS("dmi:*svnDellComputerCorporation.:*:ct8:*");