diff options
Diffstat (limited to 'drivers/hwmon')
-rw-r--r-- | drivers/hwmon/lis3lv02d.c | 87 |
1 files changed, 64 insertions, 23 deletions
diff --git a/drivers/hwmon/lis3lv02d.c b/drivers/hwmon/lis3lv02d.c index 50f31233960..3c06350bae3 100644 --- a/drivers/hwmon/lis3lv02d.c +++ b/drivers/hwmon/lis3lv02d.c | |||
@@ -251,6 +251,9 @@ EXPORT_SYMBOL_GPL(lis3lv02d_poweron); | |||
251 | 251 | ||
252 | static irqreturn_t lis302dl_interrupt(int irq, void *dummy) | 252 | static irqreturn_t lis302dl_interrupt(int irq, void *dummy) |
253 | { | 253 | { |
254 | if (!test_bit(0, &lis3_dev.misc_opened)) | ||
255 | goto out; | ||
256 | |||
254 | /* | 257 | /* |
255 | * Be careful: on some HP laptops the bios force DD when on battery and | 258 | * Be careful: on some HP laptops the bios force DD when on battery and |
256 | * the lid is closed. This leads to interrupts as soon as a little move | 259 | * the lid is closed. This leads to interrupts as soon as a little move |
@@ -260,44 +263,35 @@ static irqreturn_t lis302dl_interrupt(int irq, void *dummy) | |||
260 | 263 | ||
261 | wake_up_interruptible(&lis3_dev.misc_wait); | 264 | wake_up_interruptible(&lis3_dev.misc_wait); |
262 | kill_fasync(&lis3_dev.async_queue, SIGIO, POLL_IN); | 265 | kill_fasync(&lis3_dev.async_queue, SIGIO, POLL_IN); |
266 | out: | ||
267 | if (lis3_dev.whoami == WAI_8B && lis3_dev.idev && | ||
268 | lis3_dev.idev->input->users) | ||
269 | return IRQ_WAKE_THREAD; | ||
263 | return IRQ_HANDLED; | 270 | return IRQ_HANDLED; |
264 | } | 271 | } |
265 | 272 | ||
266 | static int lis3lv02d_misc_open(struct inode *inode, struct file *file) | 273 | static irqreturn_t lis302dl_interrupt_thread1_8b(int irq, void *data) |
267 | { | 274 | { |
268 | int ret; | 275 | return IRQ_HANDLED; |
276 | } | ||
269 | 277 | ||
278 | static irqreturn_t lis302dl_interrupt_thread2_8b(int irq, void *data) | ||
279 | { | ||
280 | return IRQ_HANDLED; | ||
281 | } | ||
282 | |||
283 | static int lis3lv02d_misc_open(struct inode *inode, struct file *file) | ||
284 | { | ||
270 | if (test_and_set_bit(0, &lis3_dev.misc_opened)) | 285 | if (test_and_set_bit(0, &lis3_dev.misc_opened)) |
271 | return -EBUSY; /* already open */ | 286 | return -EBUSY; /* already open */ |
272 | 287 | ||
273 | atomic_set(&lis3_dev.count, 0); | 288 | atomic_set(&lis3_dev.count, 0); |
274 | |||
275 | /* | ||
276 | * The sensor can generate interrupts for free-fall and direction | ||
277 | * detection (distinguishable with FF_WU_SRC and DD_SRC) but to keep | ||
278 | * the things simple and _fast_ we activate it only for free-fall, so | ||
279 | * no need to read register (very slow with ACPI). For the same reason, | ||
280 | * we forbid shared interrupts. | ||
281 | * | ||
282 | * IRQF_TRIGGER_RISING seems pointless on HP laptops because the | ||
283 | * io-apic is not configurable (and generates a warning) but I keep it | ||
284 | * in case of support for other hardware. | ||
285 | */ | ||
286 | ret = request_irq(lis3_dev.irq, lis302dl_interrupt, IRQF_TRIGGER_RISING, | ||
287 | DRIVER_NAME, &lis3_dev); | ||
288 | |||
289 | if (ret) { | ||
290 | clear_bit(0, &lis3_dev.misc_opened); | ||
291 | printk(KERN_ERR DRIVER_NAME ": IRQ%d allocation failed\n", lis3_dev.irq); | ||
292 | return -EBUSY; | ||
293 | } | ||
294 | return 0; | 289 | return 0; |
295 | } | 290 | } |
296 | 291 | ||
297 | static int lis3lv02d_misc_release(struct inode *inode, struct file *file) | 292 | static int lis3lv02d_misc_release(struct inode *inode, struct file *file) |
298 | { | 293 | { |
299 | fasync_helper(-1, file, 0, &lis3_dev.async_queue); | 294 | fasync_helper(-1, file, 0, &lis3_dev.async_queue); |
300 | free_irq(lis3_dev.irq, &lis3_dev); | ||
301 | clear_bit(0, &lis3_dev.misc_opened); /* release the device */ | 295 | clear_bit(0, &lis3_dev.misc_opened); /* release the device */ |
302 | return 0; | 296 | return 0; |
303 | } | 297 | } |
@@ -434,6 +428,11 @@ EXPORT_SYMBOL_GPL(lis3lv02d_joystick_enable); | |||
434 | 428 | ||
435 | void lis3lv02d_joystick_disable(void) | 429 | void lis3lv02d_joystick_disable(void) |
436 | { | 430 | { |
431 | if (lis3_dev.irq) | ||
432 | free_irq(lis3_dev.irq, &lis3_dev); | ||
433 | if (lis3_dev.pdata && lis3_dev.pdata->irq2) | ||
434 | free_irq(lis3_dev.pdata->irq2, &lis3_dev); | ||
435 | |||
437 | if (!lis3_dev.idev) | 436 | if (!lis3_dev.idev) |
438 | return; | 437 | return; |
439 | 438 | ||
@@ -524,6 +523,7 @@ EXPORT_SYMBOL_GPL(lis3lv02d_remove_fs); | |||
524 | static void lis3lv02d_8b_configure(struct lis3lv02d *dev, | 523 | static void lis3lv02d_8b_configure(struct lis3lv02d *dev, |
525 | struct lis3lv02d_platform_data *p) | 524 | struct lis3lv02d_platform_data *p) |
526 | { | 525 | { |
526 | int err; | ||
527 | int ctrl2 = p->hipass_ctrl; | 527 | int ctrl2 = p->hipass_ctrl; |
528 | 528 | ||
529 | if (p->click_flags) { | 529 | if (p->click_flags) { |
@@ -554,6 +554,18 @@ static void lis3lv02d_8b_configure(struct lis3lv02d *dev, | |||
554 | } | 554 | } |
555 | /* Configure hipass filters */ | 555 | /* Configure hipass filters */ |
556 | dev->write(dev, CTRL_REG2, ctrl2); | 556 | dev->write(dev, CTRL_REG2, ctrl2); |
557 | |||
558 | if (p->irq2) { | ||
559 | err = request_threaded_irq(p->irq2, | ||
560 | NULL, | ||
561 | lis302dl_interrupt_thread2_8b, | ||
562 | IRQF_TRIGGER_RISING | | ||
563 | IRQF_ONESHOT, | ||
564 | DRIVER_NAME, &lis3_dev); | ||
565 | if (err < 0) | ||
566 | printk(KERN_ERR DRIVER_NAME | ||
567 | "No second IRQ. Limited functionality\n"); | ||
568 | } | ||
557 | } | 569 | } |
558 | 570 | ||
559 | /* | 571 | /* |
@@ -562,6 +574,9 @@ static void lis3lv02d_8b_configure(struct lis3lv02d *dev, | |||
562 | */ | 574 | */ |
563 | int lis3lv02d_init_device(struct lis3lv02d *dev) | 575 | int lis3lv02d_init_device(struct lis3lv02d *dev) |
564 | { | 576 | { |
577 | int err; | ||
578 | irq_handler_t thread_fn; | ||
579 | |||
565 | dev->whoami = lis3lv02d_read_8(dev, WHO_AM_I); | 580 | dev->whoami = lis3lv02d_read_8(dev, WHO_AM_I); |
566 | 581 | ||
567 | switch (dev->whoami) { | 582 | switch (dev->whoami) { |
@@ -616,6 +631,32 @@ int lis3lv02d_init_device(struct lis3lv02d *dev) | |||
616 | goto out; | 631 | goto out; |
617 | } | 632 | } |
618 | 633 | ||
634 | /* | ||
635 | * The sensor can generate interrupts for free-fall and direction | ||
636 | * detection (distinguishable with FF_WU_SRC and DD_SRC) but to keep | ||
637 | * the things simple and _fast_ we activate it only for free-fall, so | ||
638 | * no need to read register (very slow with ACPI). For the same reason, | ||
639 | * we forbid shared interrupts. | ||
640 | * | ||
641 | * IRQF_TRIGGER_RISING seems pointless on HP laptops because the | ||
642 | * io-apic is not configurable (and generates a warning) but I keep it | ||
643 | * in case of support for other hardware. | ||
644 | */ | ||
645 | if (dev->whoami == WAI_8B) | ||
646 | thread_fn = lis302dl_interrupt_thread1_8b; | ||
647 | else | ||
648 | thread_fn = NULL; | ||
649 | |||
650 | err = request_threaded_irq(dev->irq, lis302dl_interrupt, | ||
651 | thread_fn, | ||
652 | IRQF_TRIGGER_RISING | IRQF_ONESHOT, | ||
653 | DRIVER_NAME, &lis3_dev); | ||
654 | |||
655 | if (err < 0) { | ||
656 | printk(KERN_ERR DRIVER_NAME "Cannot get IRQ\n"); | ||
657 | goto out; | ||
658 | } | ||
659 | |||
619 | if (misc_register(&lis3lv02d_misc_device)) | 660 | if (misc_register(&lis3lv02d_misc_device)) |
620 | printk(KERN_ERR DRIVER_NAME ": misc_register failed\n"); | 661 | printk(KERN_ERR DRIVER_NAME ": misc_register failed\n"); |
621 | out: | 662 | out: |