diff options
Diffstat (limited to 'drivers/memstick/host/jmb38x_ms.c')
-rw-r--r-- | drivers/memstick/host/jmb38x_ms.c | 102 |
1 files changed, 74 insertions, 28 deletions
diff --git a/drivers/memstick/host/jmb38x_ms.c b/drivers/memstick/host/jmb38x_ms.c index 4e3bfbcdf155..3485c63d20b0 100644 --- a/drivers/memstick/host/jmb38x_ms.c +++ b/drivers/memstick/host/jmb38x_ms.c | |||
@@ -50,6 +50,7 @@ struct jmb38x_ms_host { | |||
50 | struct jmb38x_ms *chip; | 50 | struct jmb38x_ms *chip; |
51 | void __iomem *addr; | 51 | void __iomem *addr; |
52 | spinlock_t lock; | 52 | spinlock_t lock; |
53 | struct tasklet_struct notify; | ||
53 | int id; | 54 | int id; |
54 | char host_id[32]; | 55 | char host_id[32]; |
55 | int irq; | 56 | int irq; |
@@ -590,55 +591,97 @@ static void jmb38x_ms_abort(unsigned long data) | |||
590 | spin_unlock_irqrestore(&host->lock, flags); | 591 | spin_unlock_irqrestore(&host->lock, flags); |
591 | } | 592 | } |
592 | 593 | ||
593 | static void jmb38x_ms_request(struct memstick_host *msh) | 594 | static void jmb38x_ms_req_tasklet(unsigned long data) |
594 | { | 595 | { |
596 | struct memstick_host *msh = (struct memstick_host *)data; | ||
595 | struct jmb38x_ms_host *host = memstick_priv(msh); | 597 | struct jmb38x_ms_host *host = memstick_priv(msh); |
596 | unsigned long flags; | 598 | unsigned long flags; |
597 | int rc; | 599 | int rc; |
598 | 600 | ||
599 | spin_lock_irqsave(&host->lock, flags); | 601 | spin_lock_irqsave(&host->lock, flags); |
600 | if (host->req) { | 602 | if (!host->req) { |
601 | spin_unlock_irqrestore(&host->lock, flags); | 603 | do { |
602 | BUG(); | 604 | rc = memstick_next_req(msh, &host->req); |
603 | return; | 605 | dev_dbg(&host->chip->pdev->dev, "tasklet req %d\n", rc); |
606 | } while (!rc && jmb38x_ms_issue_cmd(msh)); | ||
604 | } | 607 | } |
605 | |||
606 | do { | ||
607 | rc = memstick_next_req(msh, &host->req); | ||
608 | } while (!rc && jmb38x_ms_issue_cmd(msh)); | ||
609 | spin_unlock_irqrestore(&host->lock, flags); | 608 | spin_unlock_irqrestore(&host->lock, flags); |
610 | } | 609 | } |
611 | 610 | ||
612 | static void jmb38x_ms_reset(struct jmb38x_ms_host *host) | 611 | static void jmb38x_ms_dummy_submit(struct memstick_host *msh) |
613 | { | 612 | { |
614 | unsigned int host_ctl = readl(host->addr + HOST_CONTROL); | 613 | return; |
614 | } | ||
615 | |||
616 | static void jmb38x_ms_submit_req(struct memstick_host *msh) | ||
617 | { | ||
618 | struct jmb38x_ms_host *host = memstick_priv(msh); | ||
619 | |||
620 | tasklet_schedule(&host->notify); | ||
621 | } | ||
622 | |||
623 | static int jmb38x_ms_reset(struct jmb38x_ms_host *host) | ||
624 | { | ||
625 | int cnt; | ||
626 | |||
627 | writel(HOST_CONTROL_RESET_REQ | HOST_CONTROL_CLOCK_EN | ||
628 | | readl(host->addr + HOST_CONTROL), | ||
629 | host->addr + HOST_CONTROL); | ||
630 | mmiowb(); | ||
631 | |||
632 | for (cnt = 0; cnt < 20; ++cnt) { | ||
633 | if (!(HOST_CONTROL_RESET_REQ | ||
634 | & readl(host->addr + HOST_CONTROL))) | ||
635 | goto reset_next; | ||
615 | 636 | ||
616 | writel(HOST_CONTROL_RESET_REQ, host->addr + HOST_CONTROL); | 637 | ndelay(20); |
638 | } | ||
639 | dev_dbg(&host->chip->pdev->dev, "reset_req timeout\n"); | ||
640 | return -EIO; | ||
641 | |||
642 | reset_next: | ||
643 | writel(HOST_CONTROL_RESET | HOST_CONTROL_CLOCK_EN | ||
644 | | readl(host->addr + HOST_CONTROL), | ||
645 | host->addr + HOST_CONTROL); | ||
646 | mmiowb(); | ||
647 | |||
648 | for (cnt = 0; cnt < 20; ++cnt) { | ||
649 | if (!(HOST_CONTROL_RESET | ||
650 | & readl(host->addr + HOST_CONTROL))) | ||
651 | goto reset_ok; | ||
617 | 652 | ||
618 | while (HOST_CONTROL_RESET_REQ | ||
619 | & (host_ctl = readl(host->addr + HOST_CONTROL))) { | ||
620 | ndelay(20); | 653 | ndelay(20); |
621 | dev_dbg(&host->chip->pdev->dev, "reset %08x\n", host_ctl); | ||
622 | } | 654 | } |
655 | dev_dbg(&host->chip->pdev->dev, "reset timeout\n"); | ||
656 | return -EIO; | ||
623 | 657 | ||
624 | writel(HOST_CONTROL_RESET, host->addr + HOST_CONTROL); | 658 | reset_ok: |
625 | mmiowb(); | 659 | mmiowb(); |
626 | writel(INT_STATUS_ALL, host->addr + INT_SIGNAL_ENABLE); | 660 | writel(INT_STATUS_ALL, host->addr + INT_SIGNAL_ENABLE); |
627 | writel(INT_STATUS_ALL, host->addr + INT_STATUS_ENABLE); | 661 | writel(INT_STATUS_ALL, host->addr + INT_STATUS_ENABLE); |
662 | return 0; | ||
628 | } | 663 | } |
629 | 664 | ||
630 | static void jmb38x_ms_set_param(struct memstick_host *msh, | 665 | static int jmb38x_ms_set_param(struct memstick_host *msh, |
631 | enum memstick_param param, | 666 | enum memstick_param param, |
632 | int value) | 667 | int value) |
633 | { | 668 | { |
634 | struct jmb38x_ms_host *host = memstick_priv(msh); | 669 | struct jmb38x_ms_host *host = memstick_priv(msh); |
635 | unsigned int host_ctl = readl(host->addr + HOST_CONTROL); | 670 | unsigned int host_ctl = readl(host->addr + HOST_CONTROL); |
636 | unsigned int clock_ctl = CLOCK_CONTROL_40MHZ, clock_delay = 0; | 671 | unsigned int clock_ctl = CLOCK_CONTROL_40MHZ, clock_delay = 0; |
672 | int rc = 0; | ||
637 | 673 | ||
638 | switch (param) { | 674 | switch (param) { |
639 | case MEMSTICK_POWER: | 675 | case MEMSTICK_POWER: |
640 | if (value == MEMSTICK_POWER_ON) { | 676 | if (value == MEMSTICK_POWER_ON) { |
641 | jmb38x_ms_reset(host); | 677 | rc = jmb38x_ms_reset(host); |
678 | if (rc) | ||
679 | return rc; | ||
680 | |||
681 | host_ctl = 7; | ||
682 | host_ctl |= HOST_CONTROL_POWER_EN | ||
683 | | HOST_CONTROL_CLOCK_EN; | ||
684 | writel(host_ctl, host->addr + HOST_CONTROL); | ||
642 | 685 | ||
643 | writel(host->id ? PAD_PU_PD_ON_MS_SOCK1 | 686 | writel(host->id ? PAD_PU_PD_ON_MS_SOCK1 |
644 | : PAD_PU_PD_ON_MS_SOCK0, | 687 | : PAD_PU_PD_ON_MS_SOCK0, |
@@ -647,11 +690,7 @@ static void jmb38x_ms_set_param(struct memstick_host *msh, | |||
647 | writel(PAD_OUTPUT_ENABLE_MS, | 690 | writel(PAD_OUTPUT_ENABLE_MS, |
648 | host->addr + PAD_OUTPUT_ENABLE); | 691 | host->addr + PAD_OUTPUT_ENABLE); |
649 | 692 | ||
650 | host_ctl = 7; | 693 | msleep(10); |
651 | host_ctl |= HOST_CONTROL_POWER_EN | ||
652 | | HOST_CONTROL_CLOCK_EN; | ||
653 | writel(host_ctl, host->addr + HOST_CONTROL); | ||
654 | |||
655 | dev_dbg(&host->chip->pdev->dev, "power on\n"); | 694 | dev_dbg(&host->chip->pdev->dev, "power on\n"); |
656 | } else if (value == MEMSTICK_POWER_OFF) { | 695 | } else if (value == MEMSTICK_POWER_OFF) { |
657 | host_ctl &= ~(HOST_CONTROL_POWER_EN | 696 | host_ctl &= ~(HOST_CONTROL_POWER_EN |
@@ -660,7 +699,8 @@ static void jmb38x_ms_set_param(struct memstick_host *msh, | |||
660 | writel(0, host->addr + PAD_OUTPUT_ENABLE); | 699 | writel(0, host->addr + PAD_OUTPUT_ENABLE); |
661 | writel(PAD_PU_PD_OFF, host->addr + PAD_PU_PD); | 700 | writel(PAD_PU_PD_OFF, host->addr + PAD_PU_PD); |
662 | dev_dbg(&host->chip->pdev->dev, "power off\n"); | 701 | dev_dbg(&host->chip->pdev->dev, "power off\n"); |
663 | } | 702 | } else |
703 | return -EINVAL; | ||
664 | break; | 704 | break; |
665 | case MEMSTICK_INTERFACE: | 705 | case MEMSTICK_INTERFACE: |
666 | host_ctl &= ~(3 << HOST_CONTROL_IF_SHIFT); | 706 | host_ctl &= ~(3 << HOST_CONTROL_IF_SHIFT); |
@@ -686,12 +726,14 @@ static void jmb38x_ms_set_param(struct memstick_host *msh, | |||
686 | host_ctl &= ~HOST_CONTROL_REI; | 726 | host_ctl &= ~HOST_CONTROL_REI; |
687 | clock_ctl = CLOCK_CONTROL_60MHZ; | 727 | clock_ctl = CLOCK_CONTROL_60MHZ; |
688 | clock_delay = 0; | 728 | clock_delay = 0; |
689 | } | 729 | } else |
730 | return -EINVAL; | ||
690 | writel(host_ctl, host->addr + HOST_CONTROL); | 731 | writel(host_ctl, host->addr + HOST_CONTROL); |
691 | writel(clock_ctl, host->addr + CLOCK_CONTROL); | 732 | writel(clock_ctl, host->addr + CLOCK_CONTROL); |
692 | writel(clock_delay, host->addr + CLOCK_DELAY); | 733 | writel(clock_delay, host->addr + CLOCK_DELAY); |
693 | break; | 734 | break; |
694 | }; | 735 | }; |
736 | return 0; | ||
695 | } | 737 | } |
696 | 738 | ||
697 | #ifdef CONFIG_PM | 739 | #ifdef CONFIG_PM |
@@ -785,7 +827,9 @@ static struct memstick_host *jmb38x_ms_alloc_host(struct jmb38x_ms *jm, int cnt) | |||
785 | host->id); | 827 | host->id); |
786 | host->irq = jm->pdev->irq; | 828 | host->irq = jm->pdev->irq; |
787 | host->timeout_jiffies = msecs_to_jiffies(1000); | 829 | host->timeout_jiffies = msecs_to_jiffies(1000); |
788 | msh->request = jmb38x_ms_request; | 830 | |
831 | tasklet_init(&host->notify, jmb38x_ms_req_tasklet, (unsigned long)msh); | ||
832 | msh->request = jmb38x_ms_submit_req; | ||
789 | msh->set_param = jmb38x_ms_set_param; | 833 | msh->set_param = jmb38x_ms_set_param; |
790 | 834 | ||
791 | msh->caps = MEMSTICK_CAP_PAR4 | MEMSTICK_CAP_PAR8; | 835 | msh->caps = MEMSTICK_CAP_PAR4 | MEMSTICK_CAP_PAR8; |
@@ -897,6 +941,8 @@ static void jmb38x_ms_remove(struct pci_dev *dev) | |||
897 | 941 | ||
898 | host = memstick_priv(jm->hosts[cnt]); | 942 | host = memstick_priv(jm->hosts[cnt]); |
899 | 943 | ||
944 | jm->hosts[cnt]->request = jmb38x_ms_dummy_submit; | ||
945 | tasklet_kill(&host->notify); | ||
900 | writel(0, host->addr + INT_SIGNAL_ENABLE); | 946 | writel(0, host->addr + INT_SIGNAL_ENABLE); |
901 | writel(0, host->addr + INT_STATUS_ENABLE); | 947 | writel(0, host->addr + INT_STATUS_ENABLE); |
902 | mmiowb(); | 948 | mmiowb(); |