diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2011-03-17 01:09:38 -0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2011-03-17 02:28:55 -0400 |
commit | 99bb892d8a3f4f384d61e5d20499247a7cdd3d74 (patch) | |
tree | 9bf03301750c8c21a43dc15a0f4682d2e0de2b41 | |
parent | f8a67139c68eb8a58907906622c9aa02cd6a1dd1 (diff) |
Input: tsc2005 - rework driver initialization code
We need to make sure we have time/work initialized before requesting and
enabling interrupts, otherwise we might start using them way too early.
Tested-by: Aaro Koskinen <aaro.koskinen@nokia.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
-rw-r--r-- | drivers/input/touchscreen/tsc2005.c | 215 |
1 files changed, 105 insertions, 110 deletions
diff --git a/drivers/input/touchscreen/tsc2005.c b/drivers/input/touchscreen/tsc2005.c index 09cbcb0b19fe..de170e9dc54a 100644 --- a/drivers/input/touchscreen/tsc2005.c +++ b/drivers/input/touchscreen/tsc2005.c | |||
@@ -495,6 +495,16 @@ out: | |||
495 | mutex_unlock(&ts->mutex); | 495 | mutex_unlock(&ts->mutex); |
496 | } | 496 | } |
497 | 497 | ||
498 | static struct attribute *tsc2005_attrs[] = { | ||
499 | &dev_attr_disable.attr, | ||
500 | &dev_attr_selftest.attr, | ||
501 | NULL | ||
502 | }; | ||
503 | |||
504 | static struct attribute_group tsc2005_attr_group = { | ||
505 | .attrs = tsc2005_attrs, | ||
506 | }; | ||
507 | |||
498 | static void __devinit tsc2005_setup_spi_xfer(struct tsc2005 *ts) | 508 | static void __devinit tsc2005_setup_spi_xfer(struct tsc2005 *ts) |
499 | { | 509 | { |
500 | tsc2005_setup_read(&ts->spi_x, TSC2005_REG_X, 0); | 510 | tsc2005_setup_read(&ts->spi_x, TSC2005_REG_X, 0); |
@@ -509,144 +519,130 @@ static void __devinit tsc2005_setup_spi_xfer(struct tsc2005 *ts) | |||
509 | spi_message_add_tail(&ts->spi_z2.spi_xfer, &ts->spi_read_msg); | 519 | spi_message_add_tail(&ts->spi_z2.spi_xfer, &ts->spi_read_msg); |
510 | } | 520 | } |
511 | 521 | ||
512 | static struct attribute *tsc2005_attrs[] = { | 522 | static int __devinit tsc2005_probe(struct spi_device *spi) |
513 | &dev_attr_disable.attr, | ||
514 | &dev_attr_selftest.attr, | ||
515 | NULL | ||
516 | }; | ||
517 | |||
518 | static struct attribute_group tsc2005_attr_group = { | ||
519 | .attrs = tsc2005_attrs, | ||
520 | }; | ||
521 | |||
522 | static int __devinit tsc2005_setup(struct tsc2005 *ts, | ||
523 | struct tsc2005_platform_data *pdata) | ||
524 | { | 523 | { |
525 | int r; | 524 | const struct tsc2005_platform_data *pdata = spi->dev.platform_data; |
526 | int fudge_x; | 525 | struct tsc2005 *ts; |
527 | int fudge_y; | 526 | struct input_dev *input_dev; |
528 | int fudge_p; | 527 | unsigned int max_x, max_y, max_p; |
529 | int p_max; | 528 | unsigned int fudge_x, fudge_y, fudge_p; |
530 | int x_max; | 529 | int error; |
531 | int y_max; | ||
532 | 530 | ||
533 | mutex_init(&ts->mutex); | 531 | if (!pdata) { |
532 | dev_dbg(&spi->dev, "no platform data\n"); | ||
533 | return -ENODEV; | ||
534 | } | ||
534 | 535 | ||
535 | tsc2005_setup_spi_xfer(ts); | 536 | fudge_x = pdata->ts_x_fudge ? : 4; |
537 | fudge_y = pdata->ts_y_fudge ? : 8; | ||
538 | fudge_p = pdata->ts_pressure_fudge ? : 2; | ||
539 | max_x = pdata->ts_x_max ? : MAX_12BIT; | ||
540 | max_y = pdata->ts_y_max ? : MAX_12BIT; | ||
541 | max_p = pdata->ts_pressure_max ? : MAX_12BIT; | ||
536 | 542 | ||
537 | init_timer(&ts->penup_timer); | 543 | if (spi->irq <= 0) { |
538 | setup_timer(&ts->penup_timer, tsc2005_penup_timer, (unsigned long)ts); | 544 | dev_dbg(&spi->dev, "no irq\n"); |
539 | INIT_WORK(&ts->penup_work, tsc2005_penup_work); | 545 | return -ENODEV; |
546 | } | ||
540 | 547 | ||
541 | fudge_x = pdata->ts_x_fudge ? : 4; | 548 | spi->mode = SPI_MODE_0; |
542 | fudge_y = pdata->ts_y_fudge ? : 8; | 549 | spi->bits_per_word = 8; |
543 | fudge_p = pdata->ts_pressure_fudge ? : 2; | 550 | if (!spi->max_speed_hz) |
544 | x_max = pdata->ts_x_max ? : MAX_12BIT; | 551 | spi->max_speed_hz = TSC2005_SPI_MAX_SPEED_HZ; |
545 | y_max = pdata->ts_y_max ? : MAX_12BIT; | ||
546 | p_max = pdata->ts_pressure_max ? : MAX_12BIT; | ||
547 | ts->x_plate_ohm = pdata->ts_x_plate_ohm ? : 280; | ||
548 | ts->esd_timeout = pdata->esd_timeout_ms; | ||
549 | ts->set_reset = pdata->set_reset; | ||
550 | 552 | ||
551 | ts->idev = input_allocate_device(); | 553 | error = spi_setup(spi); |
552 | if (ts->idev == NULL) | 554 | if (error) |
553 | return -ENOMEM; | 555 | return error; |
554 | ts->idev->name = "TSC2005 touchscreen"; | ||
555 | snprintf(ts->phys, sizeof(ts->phys), "%s/input-ts", | ||
556 | dev_name(&ts->spi->dev)); | ||
557 | ts->idev->phys = ts->phys; | ||
558 | ts->idev->id.bustype = BUS_SPI; | ||
559 | ts->idev->dev.parent = &ts->spi->dev; | ||
560 | ts->idev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY); | ||
561 | ts->idev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE); | ||
562 | ts->idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); | ||
563 | |||
564 | input_set_abs_params(ts->idev, ABS_X, 0, x_max, fudge_x, 0); | ||
565 | input_set_abs_params(ts->idev, ABS_Y, 0, y_max, fudge_y, 0); | ||
566 | input_set_abs_params(ts->idev, ABS_PRESSURE, 0, p_max, fudge_p, 0); | ||
567 | |||
568 | r = request_threaded_irq(ts->spi->irq, tsc2005_irq_handler, | ||
569 | tsc2005_irq_thread, IRQF_TRIGGER_RISING, | ||
570 | "tsc2005", ts); | ||
571 | if (r) { | ||
572 | dev_err(&ts->spi->dev, "request_threaded_irq(): %d\n", r); | ||
573 | goto err1; | ||
574 | } | ||
575 | set_irq_wake(ts->spi->irq, 1); | ||
576 | 556 | ||
577 | r = input_register_device(ts->idev); | 557 | ts = kzalloc(sizeof(*ts), GFP_KERNEL); |
578 | if (r) { | 558 | input_dev = input_allocate_device(); |
579 | dev_err(&ts->spi->dev, "input_register_device(): %d\n", r); | 559 | if (!ts || !input_dev) { |
580 | goto err2; | 560 | error = -ENOMEM; |
561 | goto err_free_mem; | ||
581 | } | 562 | } |
582 | 563 | ||
583 | r = sysfs_create_group(&ts->spi->dev.kobj, &tsc2005_attr_group); | 564 | ts->spi = spi; |
584 | if (r) | 565 | ts->idev = input_dev; |
585 | dev_warn(&ts->spi->dev, "sysfs entry creation failed: %d\n", r); | 566 | |
567 | ts->x_plate_ohm = pdata->ts_x_plate_ohm ? : 280; | ||
568 | ts->esd_timeout = pdata->esd_timeout_ms; | ||
569 | ts->set_reset = pdata->set_reset; | ||
586 | 570 | ||
587 | tsc2005_start_scan(ts); | 571 | mutex_init(&ts->mutex); |
588 | 572 | ||
589 | if (!ts->esd_timeout || !ts->set_reset) | 573 | setup_timer(&ts->penup_timer, tsc2005_penup_timer, (unsigned long)ts); |
590 | goto done; | 574 | INIT_WORK(&ts->penup_work, tsc2005_penup_work); |
591 | 575 | ||
592 | /* start the optional ESD watchdog */ | ||
593 | setup_timer(&ts->esd_timer, tsc2005_esd_timer, (unsigned long)ts); | 576 | setup_timer(&ts->esd_timer, tsc2005_esd_timer, (unsigned long)ts); |
594 | INIT_WORK(&ts->esd_work, tsc2005_esd_work); | 577 | INIT_WORK(&ts->esd_work, tsc2005_esd_work); |
595 | mod_timer(&ts->esd_timer, | ||
596 | round_jiffies(jiffies + msecs_to_jiffies(ts->esd_timeout))); | ||
597 | |||
598 | done: | ||
599 | return 0; | ||
600 | 578 | ||
601 | err2: | 579 | tsc2005_setup_spi_xfer(ts); |
602 | free_irq(ts->spi->irq, ts); | ||
603 | |||
604 | err1: | ||
605 | input_free_device(ts->idev); | ||
606 | return r; | ||
607 | } | ||
608 | |||
609 | static int __devinit tsc2005_probe(struct spi_device *spi) | ||
610 | { | ||
611 | struct tsc2005_platform_data *pdata = spi->dev.platform_data; | ||
612 | struct tsc2005 *ts; | ||
613 | int r; | ||
614 | 580 | ||
615 | if (spi->irq < 0) { | 581 | snprintf(ts->phys, sizeof(ts->phys), |
616 | dev_dbg(&spi->dev, "no irq\n"); | 582 | "%s/input-ts", dev_name(&spi->dev)); |
617 | return -ENODEV; | 583 | |
584 | input_dev->name = "TSC2005 touchscreen"; | ||
585 | input_dev->phys = ts->phys; | ||
586 | input_dev->id.bustype = BUS_SPI; | ||
587 | input_dev->dev.parent = &spi->dev; | ||
588 | input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY); | ||
589 | input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); | ||
590 | |||
591 | input_set_abs_params(input_dev, ABS_X, 0, max_x, fudge_x, 0); | ||
592 | input_set_abs_params(input_dev, ABS_Y, 0, max_y, fudge_y, 0); | ||
593 | input_set_abs_params(input_dev, ABS_PRESSURE, 0, max_p, fudge_p, 0); | ||
594 | |||
595 | error = request_threaded_irq(spi->irq, | ||
596 | tsc2005_irq_handler, tsc2005_irq_thread, | ||
597 | IRQF_TRIGGER_RISING, "tsc2005", ts); | ||
598 | if (error) { | ||
599 | dev_err(&spi->dev, "Failed to request irq, err: %d\n", error); | ||
600 | goto err_free_mem; | ||
618 | } | 601 | } |
619 | 602 | ||
620 | if (!pdata) { | 603 | spi_set_drvdata(spi, ts); |
621 | dev_dbg(&spi->dev, "no platform data\n"); | 604 | error = sysfs_create_group(&spi->dev.kobj, &tsc2005_attr_group); |
622 | return -ENODEV; | 605 | if (error) { |
606 | dev_err(&spi->dev, | ||
607 | "Failed to create sysfs attributes, err: %d\n", error); | ||
608 | goto err_clear_drvdata; | ||
623 | } | 609 | } |
624 | 610 | ||
625 | ts = kzalloc(sizeof(*ts), GFP_KERNEL); | 611 | error = input_register_device(ts->idev); |
626 | if (ts == NULL) | 612 | if (error) { |
627 | return -ENOMEM; | 613 | dev_err(&spi->dev, |
614 | "Failed to register input device, err: %d\n", error); | ||
615 | goto err_remove_sysfs; | ||
616 | } | ||
628 | 617 | ||
629 | spi_set_drvdata(spi, ts); | 618 | tsc2005_start_scan(ts); |
630 | ts->spi = spi; | ||
631 | spi->dev.power.power_state = PMSG_ON; | ||
632 | spi->mode = SPI_MODE_0; | ||
633 | spi->bits_per_word = 8; | ||
634 | if (!spi->max_speed_hz) | ||
635 | spi->max_speed_hz = TSC2005_SPI_MAX_SPEED_HZ; | ||
636 | spi_setup(spi); | ||
637 | 619 | ||
638 | r = tsc2005_setup(ts, pdata); | 620 | if (ts->esd_timeout && ts->set_reset) { |
639 | if (r) { | 621 | /* start the optional ESD watchdog */ |
640 | kfree(ts); | 622 | mod_timer(&ts->esd_timer, round_jiffies(jiffies + |
641 | spi_set_drvdata(spi, NULL); | 623 | msecs_to_jiffies(ts->esd_timeout))); |
642 | } | 624 | } |
643 | return r; | 625 | |
626 | set_irq_wake(spi->irq, 1); | ||
627 | return 0; | ||
628 | |||
629 | err_remove_sysfs: | ||
630 | sysfs_remove_group(&spi->dev.kobj, &tsc2005_attr_group); | ||
631 | err_clear_drvdata: | ||
632 | spi_set_drvdata(spi, NULL); | ||
633 | free_irq(spi->irq, ts); | ||
634 | err_free_mem: | ||
635 | input_free_device(input_dev); | ||
636 | kfree(ts); | ||
637 | return error; | ||
644 | } | 638 | } |
645 | 639 | ||
646 | static int __devexit tsc2005_remove(struct spi_device *spi) | 640 | static int __devexit tsc2005_remove(struct spi_device *spi) |
647 | { | 641 | { |
648 | struct tsc2005 *ts = spi_get_drvdata(spi); | 642 | struct tsc2005 *ts = spi_get_drvdata(spi); |
649 | 643 | ||
644 | sysfs_remove_group(&ts->spi->dev.kobj, &tsc2005_attr_group); | ||
645 | |||
650 | mutex_lock(&ts->mutex); | 646 | mutex_lock(&ts->mutex); |
651 | tsc2005_disable(ts); | 647 | tsc2005_disable(ts); |
652 | mutex_unlock(&ts->mutex); | 648 | mutex_unlock(&ts->mutex); |
@@ -658,7 +654,6 @@ static int __devexit tsc2005_remove(struct spi_device *spi) | |||
658 | flush_work(&ts->esd_work); | 654 | flush_work(&ts->esd_work); |
659 | flush_work(&ts->penup_work); | 655 | flush_work(&ts->penup_work); |
660 | 656 | ||
661 | sysfs_remove_group(&ts->spi->dev.kobj, &tsc2005_attr_group); | ||
662 | free_irq(ts->spi->irq, ts); | 657 | free_irq(ts->spi->irq, ts); |
663 | input_unregister_device(ts->idev); | 658 | input_unregister_device(ts->idev); |
664 | kfree(ts); | 659 | kfree(ts); |