aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/hwmon/lis3lv02d.c87
-rw-r--r--include/linux/lis3lv02d.h1
2 files changed, 65 insertions, 23 deletions
diff --git a/drivers/hwmon/lis3lv02d.c b/drivers/hwmon/lis3lv02d.c
index 50f312339608..3c06350bae3a 100644
--- a/drivers/hwmon/lis3lv02d.c
+++ b/drivers/hwmon/lis3lv02d.c
@@ -251,6 +251,9 @@ EXPORT_SYMBOL_GPL(lis3lv02d_poweron);
251 251
252static irqreturn_t lis302dl_interrupt(int irq, void *dummy) 252static 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);
266out:
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
266static int lis3lv02d_misc_open(struct inode *inode, struct file *file) 273static irqreturn_t lis302dl_interrupt_thread1_8b(int irq, void *data)
267{ 274{
268 int ret; 275 return IRQ_HANDLED;
276}
269 277
278static irqreturn_t lis302dl_interrupt_thread2_8b(int irq, void *data)
279{
280 return IRQ_HANDLED;
281}
282
283static 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
297static int lis3lv02d_misc_release(struct inode *inode, struct file *file) 292static 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
435void lis3lv02d_joystick_disable(void) 429void 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);
524static void lis3lv02d_8b_configure(struct lis3lv02d *dev, 523static 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 */
563int lis3lv02d_init_device(struct lis3lv02d *dev) 575int 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");
621out: 662out:
diff --git a/include/linux/lis3lv02d.h b/include/linux/lis3lv02d.h
index d6251995c042..fd289b1e1ec1 100644
--- a/include/linux/lis3lv02d.h
+++ b/include/linux/lis3lv02d.h
@@ -67,6 +67,7 @@ struct lis3lv02d_platform_data {
67 /* Limits for selftest are specified in chip data sheet */ 67 /* Limits for selftest are specified in chip data sheet */
68 s16 st_min_limits[3]; /* min pass limit x, y, z */ 68 s16 st_min_limits[3]; /* min pass limit x, y, z */
69 s16 st_max_limits[3]; /* max pass limit x, y, z */ 69 s16 st_max_limits[3]; /* max pass limit x, y, z */
70 int irq2;
70}; 71};
71 72
72#endif /* __LIS3LV02D_H_ */ 73#endif /* __LIS3LV02D_H_ */