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); |
