aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hwmon/lis3lv02d.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hwmon/lis3lv02d.c')
-rw-r--r--drivers/hwmon/lis3lv02d.c245
1 files changed, 190 insertions, 55 deletions
diff --git a/drivers/hwmon/lis3lv02d.c b/drivers/hwmon/lis3lv02d.c
index b2f2277cad3c..6138f036b159 100644
--- a/drivers/hwmon/lis3lv02d.c
+++ b/drivers/hwmon/lis3lv02d.c
@@ -41,6 +41,8 @@
41 41
42/* joystick device poll interval in milliseconds */ 42/* joystick device poll interval in milliseconds */
43#define MDPS_POLL_INTERVAL 50 43#define MDPS_POLL_INTERVAL 50
44#define MDPS_POLL_MIN 0
45#define MDPS_POLL_MAX 2000
44/* 46/*
45 * The sensor can also generate interrupts (DRDY) but it's pretty pointless 47 * The sensor can also generate interrupts (DRDY) but it's pretty pointless
46 * because they are generated even if the data do not change. So it's better 48 * because they are generated even if the data do not change. So it's better
@@ -121,11 +123,9 @@ static void lis3lv02d_get_xyz(struct lis3lv02d *lis3, int *x, int *y, int *z)
121 int position[3]; 123 int position[3];
122 int i; 124 int i;
123 125
124 mutex_lock(&lis3->mutex);
125 position[0] = lis3->read_data(lis3, OUTX); 126 position[0] = lis3->read_data(lis3, OUTX);
126 position[1] = lis3->read_data(lis3, OUTY); 127 position[1] = lis3->read_data(lis3, OUTY);
127 position[2] = lis3->read_data(lis3, OUTZ); 128 position[2] = lis3->read_data(lis3, OUTZ);
128 mutex_unlock(&lis3->mutex);
129 129
130 for (i = 0; i < 3; i++) 130 for (i = 0; i < 3; i++)
131 position[i] = (position[i] * lis3->scale) / LIS3_ACCURACY; 131 position[i] = (position[i] * lis3->scale) / LIS3_ACCURACY;
@@ -249,8 +249,24 @@ void lis3lv02d_poweron(struct lis3lv02d *lis3)
249EXPORT_SYMBOL_GPL(lis3lv02d_poweron); 249EXPORT_SYMBOL_GPL(lis3lv02d_poweron);
250 250
251 251
252static void lis3lv02d_joystick_poll(struct input_polled_dev *pidev)
253{
254 int x, y, z;
255
256 mutex_lock(&lis3_dev.mutex);
257 lis3lv02d_get_xyz(&lis3_dev, &x, &y, &z);
258 input_report_abs(pidev->input, ABS_X, x);
259 input_report_abs(pidev->input, ABS_Y, y);
260 input_report_abs(pidev->input, ABS_Z, z);
261 input_sync(pidev->input);
262 mutex_unlock(&lis3_dev.mutex);
263}
264
252static irqreturn_t lis302dl_interrupt(int irq, void *dummy) 265static irqreturn_t lis302dl_interrupt(int irq, void *dummy)
253{ 266{
267 if (!test_bit(0, &lis3_dev.misc_opened))
268 goto out;
269
254 /* 270 /*
255 * Be careful: on some HP laptops the bios force DD when on battery and 271 * 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 272 * the lid is closed. This leads to interrupts as soon as a little move
@@ -260,44 +276,93 @@ static irqreturn_t lis302dl_interrupt(int irq, void *dummy)
260 276
261 wake_up_interruptible(&lis3_dev.misc_wait); 277 wake_up_interruptible(&lis3_dev.misc_wait);
262 kill_fasync(&lis3_dev.async_queue, SIGIO, POLL_IN); 278 kill_fasync(&lis3_dev.async_queue, SIGIO, POLL_IN);
279out:
280 if (lis3_dev.whoami == WAI_8B && lis3_dev.idev &&
281 lis3_dev.idev->input->users)
282 return IRQ_WAKE_THREAD;
263 return IRQ_HANDLED; 283 return IRQ_HANDLED;
264} 284}
265 285
266static int lis3lv02d_misc_open(struct inode *inode, struct file *file) 286static void lis302dl_interrupt_handle_click(struct lis3lv02d *lis3)
267{ 287{
268 int ret; 288 struct input_dev *dev = lis3->idev->input;
289 u8 click_src;
269 290
270 if (test_and_set_bit(0, &lis3_dev.misc_opened)) 291 mutex_lock(&lis3->mutex);
271 return -EBUSY; /* already open */ 292 lis3->read(lis3, CLICK_SRC, &click_src);
272 293
273 atomic_set(&lis3_dev.count, 0); 294 if (click_src & CLICK_SINGLE_X) {
295 input_report_key(dev, lis3->mapped_btns[0], 1);
296 input_report_key(dev, lis3->mapped_btns[0], 0);
297 }
274 298
275 /* 299 if (click_src & CLICK_SINGLE_Y) {
276 * The sensor can generate interrupts for free-fall and direction 300 input_report_key(dev, lis3->mapped_btns[1], 1);
277 * detection (distinguishable with FF_WU_SRC and DD_SRC) but to keep 301 input_report_key(dev, lis3->mapped_btns[1], 0);
278 * the things simple and _fast_ we activate it only for free-fall, so 302 }
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 303
289 if (ret) { 304 if (click_src & CLICK_SINGLE_Z) {
290 clear_bit(0, &lis3_dev.misc_opened); 305 input_report_key(dev, lis3->mapped_btns[2], 1);
291 printk(KERN_ERR DRIVER_NAME ": IRQ%d allocation failed\n", lis3_dev.irq); 306 input_report_key(dev, lis3->mapped_btns[2], 0);
292 return -EBUSY;
293 } 307 }
308 input_sync(dev);
309 mutex_unlock(&lis3->mutex);
310}
311
312static void lis302dl_interrupt_handle_ff_wu(struct lis3lv02d *lis3)
313{
314 u8 wu1_src;
315 u8 wu2_src;
316
317 lis3->read(lis3, FF_WU_SRC_1, &wu1_src);
318 lis3->read(lis3, FF_WU_SRC_2, &wu2_src);
319
320 wu1_src = wu1_src & FF_WU_SRC_IA ? wu1_src : 0;
321 wu2_src = wu2_src & FF_WU_SRC_IA ? wu2_src : 0;
322
323 /* joystick poll is internally protected by the lis3->mutex. */
324 if (wu1_src || wu2_src)
325 lis3lv02d_joystick_poll(lis3_dev.idev);
326}
327
328static irqreturn_t lis302dl_interrupt_thread1_8b(int irq, void *data)
329{
330
331 struct lis3lv02d *lis3 = data;
332
333 if ((lis3->pdata->irq_cfg & LIS3_IRQ1_MASK) == LIS3_IRQ1_CLICK)
334 lis302dl_interrupt_handle_click(lis3);
335 else
336 lis302dl_interrupt_handle_ff_wu(lis3);
337
338 return IRQ_HANDLED;
339}
340
341static irqreturn_t lis302dl_interrupt_thread2_8b(int irq, void *data)
342{
343
344 struct lis3lv02d *lis3 = data;
345
346 if ((lis3->pdata->irq_cfg & LIS3_IRQ2_MASK) == LIS3_IRQ2_CLICK)
347 lis302dl_interrupt_handle_click(lis3);
348 else
349 lis302dl_interrupt_handle_ff_wu(lis3);
350
351 return IRQ_HANDLED;
352}
353
354static int lis3lv02d_misc_open(struct inode *inode, struct file *file)
355{
356 if (test_and_set_bit(0, &lis3_dev.misc_opened))
357 return -EBUSY; /* already open */
358
359 atomic_set(&lis3_dev.count, 0);
294 return 0; 360 return 0;
295} 361}
296 362
297static int lis3lv02d_misc_release(struct inode *inode, struct file *file) 363static int lis3lv02d_misc_release(struct inode *inode, struct file *file)
298{ 364{
299 fasync_helper(-1, file, 0, &lis3_dev.async_queue); 365 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 */ 366 clear_bit(0, &lis3_dev.misc_opened); /* release the device */
302 return 0; 367 return 0;
303} 368}
@@ -380,22 +445,12 @@ static struct miscdevice lis3lv02d_misc_device = {
380 .fops = &lis3lv02d_misc_fops, 445 .fops = &lis3lv02d_misc_fops,
381}; 446};
382 447
383static void lis3lv02d_joystick_poll(struct input_polled_dev *pidev)
384{
385 int x, y, z;
386
387 lis3lv02d_get_xyz(&lis3_dev, &x, &y, &z);
388 input_report_abs(pidev->input, ABS_X, x);
389 input_report_abs(pidev->input, ABS_Y, y);
390 input_report_abs(pidev->input, ABS_Z, z);
391 input_sync(pidev->input);
392}
393
394int lis3lv02d_joystick_enable(void) 448int lis3lv02d_joystick_enable(void)
395{ 449{
396 struct input_dev *input_dev; 450 struct input_dev *input_dev;
397 int err; 451 int err;
398 int max_val, fuzz, flat; 452 int max_val, fuzz, flat;
453 int btns[] = {BTN_X, BTN_Y, BTN_Z};
399 454
400 if (lis3_dev.idev) 455 if (lis3_dev.idev)
401 return -EINVAL; 456 return -EINVAL;
@@ -406,6 +461,8 @@ int lis3lv02d_joystick_enable(void)
406 461
407 lis3_dev.idev->poll = lis3lv02d_joystick_poll; 462 lis3_dev.idev->poll = lis3lv02d_joystick_poll;
408 lis3_dev.idev->poll_interval = MDPS_POLL_INTERVAL; 463 lis3_dev.idev->poll_interval = MDPS_POLL_INTERVAL;
464 lis3_dev.idev->poll_interval_min = MDPS_POLL_MIN;
465 lis3_dev.idev->poll_interval_max = MDPS_POLL_MAX;
409 input_dev = lis3_dev.idev->input; 466 input_dev = lis3_dev.idev->input;
410 467
411 input_dev->name = "ST LIS3LV02DL Accelerometer"; 468 input_dev->name = "ST LIS3LV02DL Accelerometer";
@@ -422,6 +479,10 @@ int lis3lv02d_joystick_enable(void)
422 input_set_abs_params(input_dev, ABS_Y, -max_val, max_val, fuzz, flat); 479 input_set_abs_params(input_dev, ABS_Y, -max_val, max_val, fuzz, flat);
423 input_set_abs_params(input_dev, ABS_Z, -max_val, max_val, fuzz, flat); 480 input_set_abs_params(input_dev, ABS_Z, -max_val, max_val, fuzz, flat);
424 481
482 lis3_dev.mapped_btns[0] = lis3lv02d_get_axis(abs(lis3_dev.ac.x), btns);
483 lis3_dev.mapped_btns[1] = lis3lv02d_get_axis(abs(lis3_dev.ac.y), btns);
484 lis3_dev.mapped_btns[2] = lis3lv02d_get_axis(abs(lis3_dev.ac.z), btns);
485
425 err = input_register_polled_device(lis3_dev.idev); 486 err = input_register_polled_device(lis3_dev.idev);
426 if (err) { 487 if (err) {
427 input_free_polled_device(lis3_dev.idev); 488 input_free_polled_device(lis3_dev.idev);
@@ -434,6 +495,11 @@ EXPORT_SYMBOL_GPL(lis3lv02d_joystick_enable);
434 495
435void lis3lv02d_joystick_disable(void) 496void lis3lv02d_joystick_disable(void)
436{ 497{
498 if (lis3_dev.irq)
499 free_irq(lis3_dev.irq, &lis3_dev);
500 if (lis3_dev.pdata && lis3_dev.pdata->irq2)
501 free_irq(lis3_dev.pdata->irq2, &lis3_dev);
502
437 if (!lis3_dev.idev) 503 if (!lis3_dev.idev)
438 return; 504 return;
439 505
@@ -462,7 +528,9 @@ static ssize_t lis3lv02d_position_show(struct device *dev,
462{ 528{
463 int x, y, z; 529 int x, y, z;
464 530
531 mutex_lock(&lis3_dev.mutex);
465 lis3lv02d_get_xyz(&lis3_dev, &x, &y, &z); 532 lis3lv02d_get_xyz(&lis3_dev, &x, &y, &z);
533 mutex_unlock(&lis3_dev.mutex);
466 return sprintf(buf, "(%d,%d,%d)\n", x, y, z); 534 return sprintf(buf, "(%d,%d,%d)\n", x, y, z);
467} 535}
468 536
@@ -521,12 +589,70 @@ int lis3lv02d_remove_fs(struct lis3lv02d *lis3)
521} 589}
522EXPORT_SYMBOL_GPL(lis3lv02d_remove_fs); 590EXPORT_SYMBOL_GPL(lis3lv02d_remove_fs);
523 591
592static void lis3lv02d_8b_configure(struct lis3lv02d *dev,
593 struct lis3lv02d_platform_data *p)
594{
595 int err;
596 int ctrl2 = p->hipass_ctrl;
597
598 if (p->click_flags) {
599 dev->write(dev, CLICK_CFG, p->click_flags);
600 dev->write(dev, CLICK_TIMELIMIT, p->click_time_limit);
601 dev->write(dev, CLICK_LATENCY, p->click_latency);
602 dev->write(dev, CLICK_WINDOW, p->click_window);
603 dev->write(dev, CLICK_THSZ, p->click_thresh_z & 0xf);
604 dev->write(dev, CLICK_THSY_X,
605 (p->click_thresh_x & 0xf) |
606 (p->click_thresh_y << 4));
607
608 if (dev->idev) {
609 struct input_dev *input_dev = lis3_dev.idev->input;
610 input_set_capability(input_dev, EV_KEY, BTN_X);
611 input_set_capability(input_dev, EV_KEY, BTN_Y);
612 input_set_capability(input_dev, EV_KEY, BTN_Z);
613 }
614 }
615
616 if (p->wakeup_flags) {
617 dev->write(dev, FF_WU_CFG_1, p->wakeup_flags);
618 dev->write(dev, FF_WU_THS_1, p->wakeup_thresh & 0x7f);
619 /* default to 2.5ms for now */
620 dev->write(dev, FF_WU_DURATION_1, 1);
621 ctrl2 ^= HP_FF_WU1; /* Xor to keep compatible with old pdata*/
622 }
623
624 if (p->wakeup_flags2) {
625 dev->write(dev, FF_WU_CFG_2, p->wakeup_flags2);
626 dev->write(dev, FF_WU_THS_2, p->wakeup_thresh2 & 0x7f);
627 /* default to 2.5ms for now */
628 dev->write(dev, FF_WU_DURATION_2, 1);
629 ctrl2 ^= HP_FF_WU2; /* Xor to keep compatible with old pdata*/
630 }
631 /* Configure hipass filters */
632 dev->write(dev, CTRL_REG2, ctrl2);
633
634 if (p->irq2) {
635 err = request_threaded_irq(p->irq2,
636 NULL,
637 lis302dl_interrupt_thread2_8b,
638 IRQF_TRIGGER_RISING |
639 IRQF_ONESHOT,
640 DRIVER_NAME, &lis3_dev);
641 if (err < 0)
642 printk(KERN_ERR DRIVER_NAME
643 "No second IRQ. Limited functionality\n");
644 }
645}
646
524/* 647/*
525 * Initialise the accelerometer and the various subsystems. 648 * Initialise the accelerometer and the various subsystems.
526 * Should be rather independent of the bus system. 649 * Should be rather independent of the bus system.
527 */ 650 */
528int lis3lv02d_init_device(struct lis3lv02d *dev) 651int lis3lv02d_init_device(struct lis3lv02d *dev)
529{ 652{
653 int err;
654 irq_handler_t thread_fn;
655
530 dev->whoami = lis3lv02d_read_8(dev, WHO_AM_I); 656 dev->whoami = lis3lv02d_read_8(dev, WHO_AM_I);
531 657
532 switch (dev->whoami) { 658 switch (dev->whoami) {
@@ -567,25 +693,8 @@ int lis3lv02d_init_device(struct lis3lv02d *dev)
567 if (dev->pdata) { 693 if (dev->pdata) {
568 struct lis3lv02d_platform_data *p = dev->pdata; 694 struct lis3lv02d_platform_data *p = dev->pdata;
569 695
570 if (p->click_flags && (dev->whoami == WAI_8B)) { 696 if (dev->whoami == WAI_8B)
571 dev->write(dev, CLICK_CFG, p->click_flags); 697 lis3lv02d_8b_configure(dev, p);
572 dev->write(dev, CLICK_TIMELIMIT, p->click_time_limit);
573 dev->write(dev, CLICK_LATENCY, p->click_latency);
574 dev->write(dev, CLICK_WINDOW, p->click_window);
575 dev->write(dev, CLICK_THSZ, p->click_thresh_z & 0xf);
576 dev->write(dev, CLICK_THSY_X,
577 (p->click_thresh_x & 0xf) |
578 (p->click_thresh_y << 4));
579 }
580
581 if (p->wakeup_flags && (dev->whoami == WAI_8B)) {
582 dev->write(dev, FF_WU_CFG_1, p->wakeup_flags);
583 dev->write(dev, FF_WU_THS_1, p->wakeup_thresh & 0x7f);
584 /* default to 2.5ms for now */
585 dev->write(dev, FF_WU_DURATION_1, 1);
586 /* enable high pass filter for both free-fall units */
587 dev->write(dev, CTRL_REG2, HP_FF_WU1 | HP_FF_WU2);
588 }
589 698
590 if (p->irq_cfg) 699 if (p->irq_cfg)
591 dev->write(dev, CTRL_REG3, p->irq_cfg); 700 dev->write(dev, CTRL_REG3, p->irq_cfg);
@@ -598,6 +707,32 @@ int lis3lv02d_init_device(struct lis3lv02d *dev)
598 goto out; 707 goto out;
599 } 708 }
600 709
710 /*
711 * The sensor can generate interrupts for free-fall and direction
712 * detection (distinguishable with FF_WU_SRC and DD_SRC) but to keep
713 * the things simple and _fast_ we activate it only for free-fall, so
714 * no need to read register (very slow with ACPI). For the same reason,
715 * we forbid shared interrupts.
716 *
717 * IRQF_TRIGGER_RISING seems pointless on HP laptops because the
718 * io-apic is not configurable (and generates a warning) but I keep it
719 * in case of support for other hardware.
720 */
721 if (dev->whoami == WAI_8B)
722 thread_fn = lis302dl_interrupt_thread1_8b;
723 else
724 thread_fn = NULL;
725
726 err = request_threaded_irq(dev->irq, lis302dl_interrupt,
727 thread_fn,
728 IRQF_TRIGGER_RISING | IRQF_ONESHOT,
729 DRIVER_NAME, &lis3_dev);
730
731 if (err < 0) {
732 printk(KERN_ERR DRIVER_NAME "Cannot get IRQ\n");
733 goto out;
734 }
735
601 if (misc_register(&lis3lv02d_misc_device)) 736 if (misc_register(&lis3lv02d_misc_device))
602 printk(KERN_ERR DRIVER_NAME ": misc_register failed\n"); 737 printk(KERN_ERR DRIVER_NAME ": misc_register failed\n");
603out: 738out: