diff options
author | Manuel Lauss <mano@roarinelk.homelinux.net> | 2008-06-09 02:36:13 -0400 |
---|---|---|
committer | Pierre Ossman <drzeus@drzeus.cx> | 2008-07-15 08:14:43 -0400 |
commit | c4223c2c91fa9e5addd6eadd804e57a925ac5e5e (patch) | |
tree | d64af7e2b6322d59f1081ff8562cfc5f51f7f974 /drivers/mmc/host/au1xmmc.c | |
parent | 12bd257532708a4d5be4b8548ff121a45ff88f5d (diff) |
au1xmmc: remove db1200 board code, rewrite probe.
Remove the DB1200 board-specific functions (card present, read-only,
activity LED methods) and instead add platform data which is passed
to the driver. This also allows for platforms to implement other
carddetect schemes (e.g. dedicated irq) without having to pollute the
driver code. The poll timer (used for pb1200) is kept for compatibility.
With the board-specific stuff gone, the driver's ->probe() code can be
cleaned up considerably.
Signed-off-by: Manuel Lauss <mano@roarinelk.homelinux.net>
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
Diffstat (limited to 'drivers/mmc/host/au1xmmc.c')
-rw-r--r-- | drivers/mmc/host/au1xmmc.c | 573 |
1 files changed, 340 insertions, 233 deletions
diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c index cc5f7bc546af..d8776d61b3cc 100644 --- a/drivers/mmc/host/au1xmmc.c +++ b/drivers/mmc/host/au1xmmc.c | |||
@@ -41,8 +41,9 @@ | |||
41 | #include <linux/interrupt.h> | 41 | #include <linux/interrupt.h> |
42 | #include <linux/dma-mapping.h> | 42 | #include <linux/dma-mapping.h> |
43 | #include <linux/scatterlist.h> | 43 | #include <linux/scatterlist.h> |
44 | 44 | #include <linux/leds.h> | |
45 | #include <linux/mmc/host.h> | 45 | #include <linux/mmc/host.h> |
46 | |||
46 | #include <asm/io.h> | 47 | #include <asm/io.h> |
47 | #include <asm/mach-au1x00/au1000.h> | 48 | #include <asm/mach-au1x00/au1000.h> |
48 | #include <asm/mach-au1x00/au1xxx_dbdma.h> | 49 | #include <asm/mach-au1x00/au1xxx_dbdma.h> |
@@ -54,6 +55,7 @@ | |||
54 | #define DRIVER_NAME "au1xxx-mmc" | 55 | #define DRIVER_NAME "au1xxx-mmc" |
55 | 56 | ||
56 | /* Set this to enable special debugging macros */ | 57 | /* Set this to enable special debugging macros */ |
58 | /* #define DEBUG */ | ||
57 | 59 | ||
58 | #ifdef DEBUG | 60 | #ifdef DEBUG |
59 | #define DBG(fmt, idx, args...) printk("au1xx(%d): DEBUG: " fmt, idx, ##args) | 61 | #define DBG(fmt, idx, args...) printk("au1xx(%d): DEBUG: " fmt, idx, ##args) |
@@ -61,32 +63,6 @@ | |||
61 | #define DBG(fmt, idx, args...) | 63 | #define DBG(fmt, idx, args...) |
62 | #endif | 64 | #endif |
63 | 65 | ||
64 | const struct { | ||
65 | u32 iobase; | ||
66 | u32 tx_devid, rx_devid; | ||
67 | u16 bcsrpwr; | ||
68 | u16 bcsrstatus; | ||
69 | u16 wpstatus; | ||
70 | } au1xmmc_card_table[] = { | ||
71 | { SD0_BASE, DSCR_CMD0_SDMS_TX0, DSCR_CMD0_SDMS_RX0, | ||
72 | BCSR_BOARD_SD0PWR, BCSR_INT_SD0INSERT, BCSR_STATUS_SD0WP }, | ||
73 | #ifndef CONFIG_MIPS_DB1200 | ||
74 | { SD1_BASE, DSCR_CMD0_SDMS_TX1, DSCR_CMD0_SDMS_RX1, | ||
75 | BCSR_BOARD_DS1PWR, BCSR_INT_SD1INSERT, BCSR_STATUS_SD1WP } | ||
76 | #endif | ||
77 | }; | ||
78 | |||
79 | #define AU1XMMC_CONTROLLER_COUNT (ARRAY_SIZE(au1xmmc_card_table)) | ||
80 | |||
81 | /* This array stores pointers for the hosts (used by the IRQ handler) */ | ||
82 | struct au1xmmc_host *au1xmmc_hosts[AU1XMMC_CONTROLLER_COUNT]; | ||
83 | static int dma = 1; | ||
84 | |||
85 | #ifdef MODULE | ||
86 | module_param(dma, bool, 0); | ||
87 | MODULE_PARM_DESC(dma, "Use DMA engine for data transfers (0 = disabled)"); | ||
88 | #endif | ||
89 | |||
90 | static inline void IRQ_ON(struct au1xmmc_host *host, u32 mask) | 66 | static inline void IRQ_ON(struct au1xmmc_host *host, u32 mask) |
91 | { | 67 | { |
92 | u32 val = au_readl(HOST_CONFIG(host)); | 68 | u32 val = au_readl(HOST_CONFIG(host)); |
@@ -135,26 +111,33 @@ static inline void SEND_STOP(struct au1xmmc_host *host) | |||
135 | 111 | ||
136 | static void au1xmmc_set_power(struct au1xmmc_host *host, int state) | 112 | static void au1xmmc_set_power(struct au1xmmc_host *host, int state) |
137 | { | 113 | { |
138 | 114 | if (host->platdata && host->platdata->set_power) | |
139 | u32 val = au1xmmc_card_table[host->id].bcsrpwr; | 115 | host->platdata->set_power(host->mmc, state); |
140 | |||
141 | bcsr->board &= ~val; | ||
142 | if (state) bcsr->board |= val; | ||
143 | |||
144 | au_sync_delay(1); | ||
145 | } | 116 | } |
146 | 117 | ||
147 | static inline int au1xmmc_card_inserted(struct au1xmmc_host *host) | 118 | static int au1xmmc_card_inserted(struct au1xmmc_host *host) |
148 | { | 119 | { |
149 | return (bcsr->sig_status & au1xmmc_card_table[host->id].bcsrstatus) | 120 | int ret; |
150 | ? 1 : 0; | 121 | |
122 | if (host->platdata && host->platdata->card_inserted) | ||
123 | ret = host->platdata->card_inserted(host->mmc); | ||
124 | else | ||
125 | ret = 1; /* assume there is a card */ | ||
126 | |||
127 | return ret; | ||
151 | } | 128 | } |
152 | 129 | ||
153 | static int au1xmmc_card_readonly(struct mmc_host *mmc) | 130 | static int au1xmmc_card_readonly(struct mmc_host *mmc) |
154 | { | 131 | { |
155 | struct au1xmmc_host *host = mmc_priv(mmc); | 132 | struct au1xmmc_host *host = mmc_priv(mmc); |
156 | return (bcsr->status & au1xmmc_card_table[host->id].wpstatus) | 133 | int ret; |
157 | ? 1 : 0; | 134 | |
135 | if (host->platdata && host->platdata->card_readonly) | ||
136 | ret = host->platdata->card_readonly(mmc); | ||
137 | else | ||
138 | ret = 0; /* assume card is read-write */ | ||
139 | |||
140 | return ret; | ||
158 | } | 141 | } |
159 | 142 | ||
160 | static void au1xmmc_finish_request(struct au1xmmc_host *host) | 143 | static void au1xmmc_finish_request(struct au1xmmc_host *host) |
@@ -163,7 +146,7 @@ static void au1xmmc_finish_request(struct au1xmmc_host *host) | |||
163 | struct mmc_request *mrq = host->mrq; | 146 | struct mmc_request *mrq = host->mrq; |
164 | 147 | ||
165 | host->mrq = NULL; | 148 | host->mrq = NULL; |
166 | host->flags &= HOST_F_ACTIVE; | 149 | host->flags &= HOST_F_ACTIVE | HOST_F_DMA; |
167 | 150 | ||
168 | host->dma.len = 0; | 151 | host->dma.len = 0; |
169 | host->dma.dir = 0; | 152 | host->dma.dir = 0; |
@@ -174,8 +157,6 @@ static void au1xmmc_finish_request(struct au1xmmc_host *host) | |||
174 | 157 | ||
175 | host->status = HOST_S_IDLE; | 158 | host->status = HOST_S_IDLE; |
176 | 159 | ||
177 | bcsr->disk_leds |= (1 << 8); | ||
178 | |||
179 | mmc_request_done(host->mmc, mrq); | 160 | mmc_request_done(host->mmc, mrq); |
180 | } | 161 | } |
181 | 162 | ||
@@ -299,11 +280,13 @@ static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status) | |||
299 | 280 | ||
300 | if (!data->error) { | 281 | if (!data->error) { |
301 | if (host->flags & HOST_F_DMA) { | 282 | if (host->flags & HOST_F_DMA) { |
283 | #ifdef CONFIG_SOC_AU1200 /* DBDMA */ | ||
302 | u32 chan = DMA_CHANNEL(host); | 284 | u32 chan = DMA_CHANNEL(host); |
303 | 285 | ||
304 | chan_tab_t *c = *((chan_tab_t **) chan); | 286 | chan_tab_t *c = *((chan_tab_t **) chan); |
305 | au1x_dma_chan_t *cp = c->chan_ptr; | 287 | au1x_dma_chan_t *cp = c->chan_ptr; |
306 | data->bytes_xfered = cp->ddma_bytecnt; | 288 | data->bytes_xfered = cp->ddma_bytecnt; |
289 | #endif | ||
307 | } | 290 | } |
308 | else | 291 | else |
309 | data->bytes_xfered = | 292 | data->bytes_xfered = |
@@ -420,18 +403,18 @@ static void au1xmmc_receive_pio(struct au1xmmc_host *host) | |||
420 | break; | 403 | break; |
421 | 404 | ||
422 | if (status & SD_STATUS_RC) { | 405 | if (status & SD_STATUS_RC) { |
423 | DBG("RX CRC Error [%d + %d].\n", host->id, | 406 | DBG("RX CRC Error [%d + %d].\n", host->pdev->id, |
424 | host->pio.len, count); | 407 | host->pio.len, count); |
425 | break; | 408 | break; |
426 | } | 409 | } |
427 | 410 | ||
428 | if (status & SD_STATUS_RO) { | 411 | if (status & SD_STATUS_RO) { |
429 | DBG("RX Overrun [%d + %d]\n", host->id, | 412 | DBG("RX Overrun [%d + %d]\n", host->pdev->id, |
430 | host->pio.len, count); | 413 | host->pio.len, count); |
431 | break; | 414 | break; |
432 | } | 415 | } |
433 | else if (status & SD_STATUS_RU) { | 416 | else if (status & SD_STATUS_RU) { |
434 | DBG("RX Underrun [%d + %d]\n", host->id, | 417 | DBG("RX Underrun [%d + %d]\n", host->pdev->id, |
435 | host->pio.len, count); | 418 | host->pio.len, count); |
436 | break; | 419 | break; |
437 | } | 420 | } |
@@ -528,6 +511,7 @@ static void au1xmmc_cmd_complete(struct au1xmmc_host *host, u32 status) | |||
528 | host->status = HOST_S_DATA; | 511 | host->status = HOST_S_DATA; |
529 | 512 | ||
530 | if (host->flags & HOST_F_DMA) { | 513 | if (host->flags & HOST_F_DMA) { |
514 | #ifdef CONFIG_SOC_AU1200 /* DBDMA */ | ||
531 | u32 channel = DMA_CHANNEL(host); | 515 | u32 channel = DMA_CHANNEL(host); |
532 | 516 | ||
533 | /* Start the DMA as soon as the buffer gets something in it */ | 517 | /* Start the DMA as soon as the buffer gets something in it */ |
@@ -540,6 +524,7 @@ static void au1xmmc_cmd_complete(struct au1xmmc_host *host, u32 status) | |||
540 | } | 524 | } |
541 | 525 | ||
542 | au1xxx_dbdma_start(channel); | 526 | au1xxx_dbdma_start(channel); |
527 | #endif | ||
543 | } | 528 | } |
544 | } | 529 | } |
545 | 530 | ||
@@ -571,12 +556,8 @@ static void au1xmmc_set_clock(struct au1xmmc_host *host, int rate) | |||
571 | static int | 556 | static int |
572 | au1xmmc_prepare_data(struct au1xmmc_host *host, struct mmc_data *data) | 557 | au1xmmc_prepare_data(struct au1xmmc_host *host, struct mmc_data *data) |
573 | { | 558 | { |
574 | |||
575 | int datalen = data->blocks * data->blksz; | 559 | int datalen = data->blocks * data->blksz; |
576 | 560 | ||
577 | if (dma != 0) | ||
578 | host->flags |= HOST_F_DMA; | ||
579 | |||
580 | if (data->flags & MMC_DATA_READ) | 561 | if (data->flags & MMC_DATA_READ) |
581 | host->flags |= HOST_F_RECV; | 562 | host->flags |= HOST_F_RECV; |
582 | else | 563 | else |
@@ -596,6 +577,7 @@ au1xmmc_prepare_data(struct au1xmmc_host *host, struct mmc_data *data) | |||
596 | au_writel(data->blksz - 1, HOST_BLKSIZE(host)); | 577 | au_writel(data->blksz - 1, HOST_BLKSIZE(host)); |
597 | 578 | ||
598 | if (host->flags & HOST_F_DMA) { | 579 | if (host->flags & HOST_F_DMA) { |
580 | #ifdef CONFIG_SOC_AU1200 /* DBDMA */ | ||
599 | int i; | 581 | int i; |
600 | u32 channel = DMA_CHANNEL(host); | 582 | u32 channel = DMA_CHANNEL(host); |
601 | 583 | ||
@@ -621,11 +603,12 @@ au1xmmc_prepare_data(struct au1xmmc_host *host, struct mmc_data *data) | |||
621 | len, flags); | 603 | len, flags); |
622 | } | 604 | } |
623 | 605 | ||
624 | if (!ret) | 606 | if (!ret) |
625 | goto dataerr; | 607 | goto dataerr; |
626 | 608 | ||
627 | datalen -= len; | 609 | datalen -= len; |
628 | } | 610 | } |
611 | #endif | ||
629 | } | 612 | } |
630 | else { | 613 | else { |
631 | host->pio.index = 0; | 614 | host->pio.index = 0; |
@@ -641,8 +624,9 @@ au1xmmc_prepare_data(struct au1xmmc_host *host, struct mmc_data *data) | |||
641 | 624 | ||
642 | return 0; | 625 | return 0; |
643 | 626 | ||
644 | dataerr: | 627 | dataerr: |
645 | dma_unmap_sg(mmc_dev(host->mmc),data->sg,data->sg_len,host->dma.dir); | 628 | dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, |
629 | host->dma.dir); | ||
646 | return -ETIMEDOUT; | 630 | return -ETIMEDOUT; |
647 | } | 631 | } |
648 | 632 | ||
@@ -663,8 +647,6 @@ static void au1xmmc_request(struct mmc_host* mmc, struct mmc_request* mrq) | |||
663 | host->mrq = mrq; | 647 | host->mrq = mrq; |
664 | host->status = HOST_S_CMD; | 648 | host->status = HOST_S_CMD; |
665 | 649 | ||
666 | bcsr->disk_leds &= ~(1 << 8); | ||
667 | |||
668 | if (mrq->data) { | 650 | if (mrq->data) { |
669 | FLUSH_FIFO(host); | 651 | FLUSH_FIFO(host); |
670 | flags = mrq->data->flags; | 652 | flags = mrq->data->flags; |
@@ -728,149 +710,145 @@ static void au1xmmc_set_ios(struct mmc_host* mmc, struct mmc_ios* ios) | |||
728 | } | 710 | } |
729 | } | 711 | } |
730 | 712 | ||
731 | static void au1xmmc_dma_callback(int irq, void *dev_id) | ||
732 | { | ||
733 | struct au1xmmc_host *host = (struct au1xmmc_host *) dev_id; | ||
734 | |||
735 | /* Avoid spurious interrupts */ | ||
736 | |||
737 | if (!host->mrq) | ||
738 | return; | ||
739 | |||
740 | if (host->flags & HOST_F_STOP) | ||
741 | SEND_STOP(host); | ||
742 | |||
743 | tasklet_schedule(&host->data_task); | ||
744 | } | ||
745 | |||
746 | #define STATUS_TIMEOUT (SD_STATUS_RAT | SD_STATUS_DT) | 713 | #define STATUS_TIMEOUT (SD_STATUS_RAT | SD_STATUS_DT) |
747 | #define STATUS_DATA_IN (SD_STATUS_NE) | 714 | #define STATUS_DATA_IN (SD_STATUS_NE) |
748 | #define STATUS_DATA_OUT (SD_STATUS_TH) | 715 | #define STATUS_DATA_OUT (SD_STATUS_TH) |
749 | 716 | ||
750 | static irqreturn_t au1xmmc_irq(int irq, void *dev_id) | 717 | static irqreturn_t au1xmmc_irq(int irq, void *dev_id) |
751 | { | 718 | { |
752 | 719 | struct au1xmmc_host *host = dev_id; | |
753 | u32 status; | 720 | u32 status; |
754 | int i, ret = 0; | ||
755 | |||
756 | disable_irq(AU1100_SD_IRQ); | ||
757 | 721 | ||
758 | for(i = 0; i < AU1XMMC_CONTROLLER_COUNT; i++) { | 722 | status = au_readl(HOST_STATUS(host)); |
759 | struct au1xmmc_host * host = au1xmmc_hosts[i]; | ||
760 | u32 handled = 1; | ||
761 | |||
762 | status = au_readl(HOST_STATUS(host)); | ||
763 | 723 | ||
764 | if (host->mrq && (status & STATUS_TIMEOUT)) { | 724 | if (!(status & SD_STATUS_I)) |
765 | if (status & SD_STATUS_RAT) | 725 | return IRQ_NONE; /* not ours */ |
766 | host->mrq->cmd->error = -ETIMEDOUT; | ||
767 | 726 | ||
768 | else if (status & SD_STATUS_DT) | 727 | if (host->mrq && (status & STATUS_TIMEOUT)) { |
769 | host->mrq->data->error = -ETIMEDOUT; | 728 | if (status & SD_STATUS_RAT) |
729 | host->mrq->cmd->error = -ETIMEDOUT; | ||
730 | else if (status & SD_STATUS_DT) | ||
731 | host->mrq->data->error = -ETIMEDOUT; | ||
770 | 732 | ||
771 | /* In PIO mode, interrupts might still be enabled */ | 733 | /* In PIO mode, interrupts might still be enabled */ |
772 | IRQ_OFF(host, SD_CONFIG_NE | SD_CONFIG_TH); | 734 | IRQ_OFF(host, SD_CONFIG_NE | SD_CONFIG_TH); |
773 | 735 | ||
774 | //IRQ_OFF(host, SD_CONFIG_TH|SD_CONFIG_RA|SD_CONFIG_RF); | 736 | /* IRQ_OFF(host, SD_CONFIG_TH | SD_CONFIG_RA | SD_CONFIG_RF); */ |
775 | tasklet_schedule(&host->finish_task); | 737 | tasklet_schedule(&host->finish_task); |
776 | } | 738 | } |
777 | #if 0 | 739 | #if 0 |
778 | else if (status & SD_STATUS_DD) { | 740 | else if (status & SD_STATUS_DD) { |
779 | 741 | /* Sometimes we get a DD before a NE in PIO mode */ | |
780 | /* Sometimes we get a DD before a NE in PIO mode */ | 742 | if (!(host->flags & HOST_F_DMA) && (status & SD_STATUS_NE)) |
781 | 743 | au1xmmc_receive_pio(host); | |
782 | if (!(host->flags & HOST_F_DMA) && | 744 | else { |
783 | (status & SD_STATUS_NE)) | 745 | au1xmmc_data_complete(host, status); |
784 | au1xmmc_receive_pio(host); | 746 | /* tasklet_schedule(&host->data_task); */ |
785 | else { | ||
786 | au1xmmc_data_complete(host, status); | ||
787 | //tasklet_schedule(&host->data_task); | ||
788 | } | ||
789 | } | ||
790 | #endif | ||
791 | else if (status & (SD_STATUS_CR)) { | ||
792 | if (host->status == HOST_S_CMD) | ||
793 | au1xmmc_cmd_complete(host,status); | ||
794 | } | ||
795 | else if (!(host->flags & HOST_F_DMA)) { | ||
796 | if ((host->flags & HOST_F_XMIT) && | ||
797 | (status & STATUS_DATA_OUT)) | ||
798 | au1xmmc_send_pio(host); | ||
799 | else if ((host->flags & HOST_F_RECV) && | ||
800 | (status & STATUS_DATA_IN)) | ||
801 | au1xmmc_receive_pio(host); | ||
802 | } | ||
803 | else if (status & 0x203FBC70) { | ||
804 | DBG("Unhandled status %8.8x\n", host->id, status); | ||
805 | handled = 0; | ||
806 | } | 747 | } |
807 | |||
808 | au_writel(status, HOST_STATUS(host)); | ||
809 | au_sync(); | ||
810 | |||
811 | ret |= handled; | ||
812 | } | 748 | } |
813 | 749 | #endif | |
814 | enable_irq(AU1100_SD_IRQ); | 750 | else if (status & SD_STATUS_CR) { |
815 | return ret; | 751 | if (host->status == HOST_S_CMD) |
816 | } | 752 | au1xmmc_cmd_complete(host, status); |
817 | 753 | ||
818 | static void au1xmmc_poll_event(unsigned long arg) | 754 | } else if (!(host->flags & HOST_F_DMA)) { |
819 | { | 755 | if ((host->flags & HOST_F_XMIT) && (status & STATUS_DATA_OUT)) |
820 | struct au1xmmc_host *host = (struct au1xmmc_host *) arg; | 756 | au1xmmc_send_pio(host); |
821 | 757 | else if ((host->flags & HOST_F_RECV) && (status & STATUS_DATA_IN)) | |
822 | int card = au1xmmc_card_inserted(host); | 758 | au1xmmc_receive_pio(host); |
823 | int controller = (host->flags & HOST_F_ACTIVE) ? 1 : 0; | 759 | |
824 | 760 | } else if (status & 0x203F3C70) { | |
825 | if (card != controller) { | 761 | DBG("Unhandled status %8.8x\n", host->pdev->id, |
826 | host->flags &= ~HOST_F_ACTIVE; | 762 | status); |
827 | if (card) host->flags |= HOST_F_ACTIVE; | ||
828 | mmc_detect_change(host->mmc, 0); | ||
829 | } | 763 | } |
830 | 764 | ||
831 | if (host->mrq != NULL) { | 765 | au_writel(status, HOST_STATUS(host)); |
832 | u32 status = au_readl(HOST_STATUS(host)); | 766 | au_sync(); |
833 | DBG("PENDING - %8.8x\n", host->id, status); | ||
834 | } | ||
835 | 767 | ||
836 | mod_timer(&host->timer, jiffies + AU1XMMC_DETECT_TIMEOUT); | 768 | return IRQ_HANDLED; |
837 | } | 769 | } |
838 | 770 | ||
839 | static dbdev_tab_t au1xmmc_mem_dbdev = | 771 | #ifdef CONFIG_SOC_AU1200 |
840 | { | 772 | /* 8bit memory DMA device */ |
841 | DSCR_CMD0_ALWAYS, DEV_FLAGS_ANYUSE, 0, 8, 0x00000000, 0, 0 | 773 | static dbdev_tab_t au1xmmc_mem_dbdev = { |
774 | .dev_id = DSCR_CMD0_ALWAYS, | ||
775 | .dev_flags = DEV_FLAGS_ANYUSE, | ||
776 | .dev_tsize = 0, | ||
777 | .dev_devwidth = 8, | ||
778 | .dev_physaddr = 0x00000000, | ||
779 | .dev_intlevel = 0, | ||
780 | .dev_intpolarity = 0, | ||
842 | }; | 781 | }; |
782 | static int memid; | ||
843 | 783 | ||
844 | static void au1xmmc_init_dma(struct au1xmmc_host *host) | 784 | static void au1xmmc_dbdma_callback(int irq, void *dev_id) |
845 | { | 785 | { |
786 | struct au1xmmc_host *host = (struct au1xmmc_host *)dev_id; | ||
846 | 787 | ||
847 | u32 rxchan, txchan; | 788 | /* Avoid spurious interrupts */ |
789 | if (!host->mrq) | ||
790 | return; | ||
848 | 791 | ||
849 | int txid = au1xmmc_card_table[host->id].tx_devid; | 792 | if (host->flags & HOST_F_STOP) |
850 | int rxid = au1xmmc_card_table[host->id].rx_devid; | 793 | SEND_STOP(host); |
851 | 794 | ||
852 | /* DSCR_CMD0_ALWAYS has a stride of 32 bits, we need a stride | 795 | tasklet_schedule(&host->data_task); |
853 | of 8 bits. And since devices are shared, we need to create | 796 | } |
854 | our own to avoid freaking out other devices | ||
855 | */ | ||
856 | 797 | ||
857 | int memid = au1xxx_ddma_add_device(&au1xmmc_mem_dbdev); | 798 | static int au1xmmc_dbdma_init(struct au1xmmc_host *host) |
799 | { | ||
800 | struct resource *res; | ||
801 | int txid, rxid; | ||
802 | |||
803 | res = platform_get_resource(host->pdev, IORESOURCE_DMA, 0); | ||
804 | if (!res) | ||
805 | return -ENODEV; | ||
806 | txid = res->start; | ||
807 | |||
808 | res = platform_get_resource(host->pdev, IORESOURCE_DMA, 1); | ||
809 | if (!res) | ||
810 | return -ENODEV; | ||
811 | rxid = res->start; | ||
812 | |||
813 | if (!memid) | ||
814 | return -ENODEV; | ||
815 | |||
816 | host->tx_chan = au1xxx_dbdma_chan_alloc(memid, txid, | ||
817 | au1xmmc_dbdma_callback, (void *)host); | ||
818 | if (!host->tx_chan) { | ||
819 | dev_err(&host->pdev->dev, "cannot allocate TX DMA\n"); | ||
820 | return -ENODEV; | ||
821 | } | ||
822 | |||
823 | host->rx_chan = au1xxx_dbdma_chan_alloc(rxid, memid, | ||
824 | au1xmmc_dbdma_callback, (void *)host); | ||
825 | if (!host->rx_chan) { | ||
826 | dev_err(&host->pdev->dev, "cannot allocate RX DMA\n"); | ||
827 | au1xxx_dbdma_chan_free(host->tx_chan); | ||
828 | return -ENODEV; | ||
829 | } | ||
858 | 830 | ||
859 | txchan = au1xxx_dbdma_chan_alloc(memid, txid, | 831 | au1xxx_dbdma_set_devwidth(host->tx_chan, 8); |
860 | au1xmmc_dma_callback, (void *) host); | 832 | au1xxx_dbdma_set_devwidth(host->rx_chan, 8); |
861 | 833 | ||
862 | rxchan = au1xxx_dbdma_chan_alloc(rxid, memid, | 834 | au1xxx_dbdma_ring_alloc(host->tx_chan, AU1XMMC_DESCRIPTOR_COUNT); |
863 | au1xmmc_dma_callback, (void *) host); | 835 | au1xxx_dbdma_ring_alloc(host->rx_chan, AU1XMMC_DESCRIPTOR_COUNT); |
864 | 836 | ||
865 | au1xxx_dbdma_set_devwidth(txchan, 8); | 837 | /* DBDMA is good to go */ |
866 | au1xxx_dbdma_set_devwidth(rxchan, 8); | 838 | host->flags |= HOST_F_DMA; |
867 | 839 | ||
868 | au1xxx_dbdma_ring_alloc(txchan, AU1XMMC_DESCRIPTOR_COUNT); | 840 | return 0; |
869 | au1xxx_dbdma_ring_alloc(rxchan, AU1XMMC_DESCRIPTOR_COUNT); | 841 | } |
870 | 842 | ||
871 | host->tx_chan = txchan; | 843 | static void au1xmmc_dbdma_shutdown(struct au1xmmc_host *host) |
872 | host->rx_chan = rxchan; | 844 | { |
845 | if (host->flags & HOST_F_DMA) { | ||
846 | host->flags &= ~HOST_F_DMA; | ||
847 | au1xxx_dbdma_chan_free(host->tx_chan); | ||
848 | au1xxx_dbdma_chan_free(host->rx_chan); | ||
849 | } | ||
873 | } | 850 | } |
851 | #endif | ||
874 | 852 | ||
875 | static const struct mmc_host_ops au1xmmc_ops = { | 853 | static const struct mmc_host_ops au1xmmc_ops = { |
876 | .request = au1xmmc_request, | 854 | .request = au1xmmc_request, |
@@ -878,116 +856,235 @@ static const struct mmc_host_ops au1xmmc_ops = { | |||
878 | .get_ro = au1xmmc_card_readonly, | 856 | .get_ro = au1xmmc_card_readonly, |
879 | }; | 857 | }; |
880 | 858 | ||
881 | static int __devinit au1xmmc_probe(struct platform_device *pdev) | 859 | static void au1xmmc_poll_event(unsigned long arg) |
882 | { | 860 | { |
861 | struct au1xmmc_host *host = (struct au1xmmc_host *)arg; | ||
862 | int card = au1xmmc_card_inserted(host); | ||
863 | int controller = (host->flags & HOST_F_ACTIVE) ? 1 : 0; | ||
864 | |||
865 | if (card != controller) { | ||
866 | host->flags &= ~HOST_F_ACTIVE; | ||
867 | if (card) | ||
868 | host->flags |= HOST_F_ACTIVE; | ||
869 | mmc_detect_change(host->mmc, 0); | ||
870 | } | ||
883 | 871 | ||
884 | int i, ret = 0; | 872 | #ifdef DEBUG |
873 | if (host->mrq != NULL) { | ||
874 | u32 status = au_readl(HOST_STATUS(host)); | ||
875 | DBG("PENDING - %8.8x\n", host->pdev->id, status); | ||
876 | } | ||
877 | #endif | ||
878 | mod_timer(&host->timer, jiffies + AU1XMMC_DETECT_TIMEOUT); | ||
879 | } | ||
885 | 880 | ||
886 | /* THe interrupt is shared among all controllers */ | 881 | static void au1xmmc_init_cd_poll_timer(struct au1xmmc_host *host) |
887 | ret = request_irq(AU1100_SD_IRQ, au1xmmc_irq, IRQF_DISABLED, "MMC", 0); | 882 | { |
883 | init_timer(&host->timer); | ||
884 | host->timer.function = au1xmmc_poll_event; | ||
885 | host->timer.data = (unsigned long)host; | ||
886 | host->timer.expires = jiffies + AU1XMMC_DETECT_TIMEOUT; | ||
887 | } | ||
888 | 888 | ||
889 | if (ret) { | 889 | static int __devinit au1xmmc_probe(struct platform_device *pdev) |
890 | printk(DRIVER_NAME "ERROR: Couldn't get int %d: %d\n", | 890 | { |
891 | AU1100_SD_IRQ, ret); | 891 | struct mmc_host *mmc; |
892 | return -ENXIO; | 892 | struct au1xmmc_host *host; |
893 | struct resource *r; | ||
894 | int ret; | ||
895 | |||
896 | mmc = mmc_alloc_host(sizeof(struct au1xmmc_host), &pdev->dev); | ||
897 | if (!mmc) { | ||
898 | dev_err(&pdev->dev, "no memory for mmc_host\n"); | ||
899 | ret = -ENOMEM; | ||
900 | goto out0; | ||
893 | } | 901 | } |
894 | 902 | ||
895 | disable_irq(AU1100_SD_IRQ); | 903 | host = mmc_priv(mmc); |
904 | host->mmc = mmc; | ||
905 | host->platdata = pdev->dev.platform_data; | ||
906 | host->pdev = pdev; | ||
896 | 907 | ||
897 | for(i = 0; i < AU1XMMC_CONTROLLER_COUNT; i++) { | 908 | ret = -ENODEV; |
898 | struct mmc_host *mmc = mmc_alloc_host(sizeof(struct au1xmmc_host), &pdev->dev); | 909 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
899 | struct au1xmmc_host *host = 0; | 910 | if (!r) { |
911 | dev_err(&pdev->dev, "no mmio defined\n"); | ||
912 | goto out1; | ||
913 | } | ||
900 | 914 | ||
901 | if (!mmc) { | 915 | host->ioarea = request_mem_region(r->start, r->end - r->start + 1, |
902 | printk(DRIVER_NAME "ERROR: no mem for host %d\n", i); | 916 | pdev->name); |
903 | au1xmmc_hosts[i] = 0; | 917 | if (!host->ioarea) { |
904 | continue; | 918 | dev_err(&pdev->dev, "mmio already in use\n"); |
905 | } | 919 | goto out1; |
920 | } | ||
906 | 921 | ||
907 | mmc->ops = &au1xmmc_ops; | 922 | host->iobase = (unsigned long)ioremap(r->start, 0x3c); |
923 | if (!host->iobase) { | ||
924 | dev_err(&pdev->dev, "cannot remap mmio\n"); | ||
925 | goto out2; | ||
926 | } | ||
927 | |||
928 | r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | ||
929 | if (!r) { | ||
930 | dev_err(&pdev->dev, "no IRQ defined\n"); | ||
931 | goto out3; | ||
932 | } | ||
908 | 933 | ||
909 | mmc->f_min = 450000; | 934 | host->irq = r->start; |
910 | mmc->f_max = 24000000; | 935 | /* IRQ is shared among both SD controllers */ |
936 | ret = request_irq(host->irq, au1xmmc_irq, IRQF_SHARED, | ||
937 | DRIVER_NAME, host); | ||
938 | if (ret) { | ||
939 | dev_err(&pdev->dev, "cannot grab IRQ\n"); | ||
940 | goto out3; | ||
941 | } | ||
911 | 942 | ||
912 | mmc->max_seg_size = AU1XMMC_DESCRIPTOR_SIZE; | 943 | mmc->ops = &au1xmmc_ops; |
913 | mmc->max_phys_segs = AU1XMMC_DESCRIPTOR_COUNT; | ||
914 | 944 | ||
915 | mmc->max_blk_size = 2048; | 945 | mmc->f_min = 450000; |
916 | mmc->max_blk_count = 512; | 946 | mmc->f_max = 24000000; |
917 | 947 | ||
918 | mmc->ocr_avail = AU1XMMC_OCR; | 948 | mmc->max_seg_size = AU1XMMC_DESCRIPTOR_SIZE; |
949 | mmc->max_phys_segs = AU1XMMC_DESCRIPTOR_COUNT; | ||
919 | 950 | ||
920 | host = mmc_priv(mmc); | 951 | mmc->max_blk_size = 2048; |
921 | host->mmc = mmc; | 952 | mmc->max_blk_count = 512; |
922 | 953 | ||
923 | host->id = i; | 954 | mmc->ocr_avail = AU1XMMC_OCR; |
924 | host->iobase = au1xmmc_card_table[host->id].iobase; | 955 | mmc->caps = 0; |
925 | host->clock = 0; | ||
926 | host->power_mode = MMC_POWER_OFF; | ||
927 | 956 | ||
928 | host->flags = au1xmmc_card_inserted(host) ? HOST_F_ACTIVE : 0; | 957 | host->status = HOST_S_IDLE; |
929 | host->status = HOST_S_IDLE; | ||
930 | 958 | ||
931 | init_timer(&host->timer); | 959 | /* board-specific carddetect setup, if any */ |
960 | if (host->platdata && host->platdata->cd_setup) { | ||
961 | ret = host->platdata->cd_setup(mmc, 1); | ||
962 | if (ret) { | ||
963 | dev_err(&pdev->dev, "board CD setup failed\n"); | ||
964 | goto out4; | ||
965 | } | ||
966 | } else { | ||
967 | /* poll the board-specific is-card-in-socket-? method */ | ||
968 | au1xmmc_init_cd_poll_timer(host); | ||
969 | } | ||
932 | 970 | ||
933 | host->timer.function = au1xmmc_poll_event; | 971 | tasklet_init(&host->data_task, au1xmmc_tasklet_data, |
934 | host->timer.data = (unsigned long) host; | 972 | (unsigned long)host); |
935 | host->timer.expires = jiffies + AU1XMMC_DETECT_TIMEOUT; | ||
936 | 973 | ||
937 | tasklet_init(&host->data_task, au1xmmc_tasklet_data, | 974 | tasklet_init(&host->finish_task, au1xmmc_tasklet_finish, |
938 | (unsigned long) host); | 975 | (unsigned long)host); |
939 | 976 | ||
940 | tasklet_init(&host->finish_task, au1xmmc_tasklet_finish, | 977 | #ifdef CONFIG_SOC_AU1200 |
941 | (unsigned long) host); | 978 | ret = au1xmmc_dbdma_init(host); |
979 | if (ret) | ||
980 | printk(KERN_INFO DRIVER_NAME ": DBDMA init failed; using PIO\n"); | ||
981 | #endif | ||
942 | 982 | ||
943 | spin_lock_init(&host->lock); | 983 | #ifdef CONFIG_LEDS_CLASS |
984 | if (host->platdata && host->platdata->led) { | ||
985 | struct led_classdev *led = host->platdata->led; | ||
986 | led->name = mmc_hostname(mmc); | ||
987 | led->brightness = LED_OFF; | ||
988 | led->default_trigger = mmc_hostname(mmc); | ||
989 | ret = led_classdev_register(mmc_dev(mmc), led); | ||
990 | if (ret) | ||
991 | goto out5; | ||
992 | } | ||
993 | #endif | ||
944 | 994 | ||
945 | if (dma != 0) | 995 | au1xmmc_reset_controller(host); |
946 | au1xmmc_init_dma(host); | ||
947 | 996 | ||
948 | au1xmmc_reset_controller(host); | 997 | ret = mmc_add_host(mmc); |
998 | if (ret) { | ||
999 | dev_err(&pdev->dev, "cannot add mmc host\n"); | ||
1000 | goto out6; | ||
1001 | } | ||
949 | 1002 | ||
950 | mmc_add_host(mmc); | 1003 | platform_set_drvdata(pdev, mmc); |
951 | au1xmmc_hosts[i] = host; | ||
952 | 1004 | ||
1005 | /* start the carddetect poll timer if necessary */ | ||
1006 | if (!(host->platdata && host->platdata->cd_setup)) | ||
953 | add_timer(&host->timer); | 1007 | add_timer(&host->timer); |
954 | 1008 | ||
955 | printk(KERN_INFO DRIVER_NAME ": MMC Controller %d set up at %8.8X (mode=%s)\n", | 1009 | printk(KERN_INFO DRIVER_NAME ": MMC Controller %d set up at %8.8X" |
956 | host->id, host->iobase, dma ? "dma" : "pio"); | 1010 | " (mode=%s)\n", pdev->id, host->iobase, |
957 | } | 1011 | host->flags & HOST_F_DMA ? "dma" : "pio"); |
958 | 1012 | ||
959 | enable_irq(AU1100_SD_IRQ); | 1013 | return 0; /* all ok */ |
960 | 1014 | ||
961 | return 0; | 1015 | out6: |
1016 | #ifdef CONFIG_LEDS_CLASS | ||
1017 | if (host->platdata && host->platdata->led) | ||
1018 | led_classdev_unregister(host->platdata->led); | ||
1019 | out5: | ||
1020 | #endif | ||
1021 | au_writel(0, HOST_ENABLE(host)); | ||
1022 | au_writel(0, HOST_CONFIG(host)); | ||
1023 | au_writel(0, HOST_CONFIG2(host)); | ||
1024 | au_sync(); | ||
1025 | |||
1026 | #ifdef CONFIG_SOC_AU1200 | ||
1027 | au1xmmc_dbdma_shutdown(host); | ||
1028 | #endif | ||
1029 | |||
1030 | tasklet_kill(&host->data_task); | ||
1031 | tasklet_kill(&host->finish_task); | ||
1032 | |||
1033 | if (host->platdata && host->platdata->cd_setup) | ||
1034 | host->platdata->cd_setup(mmc, 0); | ||
1035 | out4: | ||
1036 | free_irq(host->irq, host); | ||
1037 | out3: | ||
1038 | iounmap((void *)host->iobase); | ||
1039 | out2: | ||
1040 | release_resource(host->ioarea); | ||
1041 | kfree(host->ioarea); | ||
1042 | out1: | ||
1043 | mmc_free_host(mmc); | ||
1044 | out0: | ||
1045 | return ret; | ||
962 | } | 1046 | } |
963 | 1047 | ||
964 | static int __devexit au1xmmc_remove(struct platform_device *pdev) | 1048 | static int __devexit au1xmmc_remove(struct platform_device *pdev) |
965 | { | 1049 | { |
1050 | struct mmc_host *mmc = platform_get_drvdata(pdev); | ||
1051 | struct au1xmmc_host *host; | ||
1052 | |||
1053 | if (mmc) { | ||
1054 | host = mmc_priv(mmc); | ||
1055 | |||
1056 | mmc_remove_host(mmc); | ||
966 | 1057 | ||
967 | int i; | 1058 | #ifdef CONFIG_LEDS_CLASS |
1059 | if (host->platdata && host->platdata->led) | ||
1060 | led_classdev_unregister(host->platdata->led); | ||
1061 | #endif | ||
968 | 1062 | ||
969 | disable_irq(AU1100_SD_IRQ); | 1063 | if (host->platdata && host->platdata->cd_setup) |
1064 | host->platdata->cd_setup(mmc, 0); | ||
1065 | else | ||
1066 | del_timer_sync(&host->timer); | ||
970 | 1067 | ||
971 | for(i = 0; i < AU1XMMC_CONTROLLER_COUNT; i++) { | 1068 | au_writel(0, HOST_ENABLE(host)); |
972 | struct au1xmmc_host *host = au1xmmc_hosts[i]; | 1069 | au_writel(0, HOST_CONFIG(host)); |
973 | if (!host) continue; | 1070 | au_writel(0, HOST_CONFIG2(host)); |
1071 | au_sync(); | ||
974 | 1072 | ||
975 | tasklet_kill(&host->data_task); | 1073 | tasklet_kill(&host->data_task); |
976 | tasklet_kill(&host->finish_task); | 1074 | tasklet_kill(&host->finish_task); |
977 | 1075 | ||
978 | del_timer_sync(&host->timer); | 1076 | #ifdef CONFIG_SOC_AU1200 |
1077 | au1xmmc_dbdma_shutdown(host); | ||
1078 | #endif | ||
979 | au1xmmc_set_power(host, 0); | 1079 | au1xmmc_set_power(host, 0); |
980 | 1080 | ||
981 | mmc_remove_host(host->mmc); | 1081 | free_irq(host->irq, host); |
1082 | iounmap((void *)host->iobase); | ||
1083 | release_resource(host->ioarea); | ||
1084 | kfree(host->ioarea); | ||
982 | 1085 | ||
983 | au1xxx_dbdma_chan_free(host->tx_chan); | 1086 | mmc_free_host(mmc); |
984 | au1xxx_dbdma_chan_free(host->rx_chan); | ||
985 | |||
986 | au_writel(0x0, HOST_ENABLE(host)); | ||
987 | au_sync(); | ||
988 | } | 1087 | } |
989 | |||
990 | free_irq(AU1100_SD_IRQ, 0); | ||
991 | return 0; | 1088 | return 0; |
992 | } | 1089 | } |
993 | 1090 | ||
@@ -1004,21 +1101,31 @@ static struct platform_driver au1xmmc_driver = { | |||
1004 | 1101 | ||
1005 | static int __init au1xmmc_init(void) | 1102 | static int __init au1xmmc_init(void) |
1006 | { | 1103 | { |
1104 | #ifdef CONFIG_SOC_AU1200 | ||
1105 | /* DSCR_CMD0_ALWAYS has a stride of 32 bits, we need a stride | ||
1106 | * of 8 bits. And since devices are shared, we need to create | ||
1107 | * our own to avoid freaking out other devices. | ||
1108 | */ | ||
1109 | memid = au1xxx_ddma_add_device(&au1xmmc_mem_dbdev); | ||
1110 | if (!memid) | ||
1111 | printk(KERN_ERR "au1xmmc: cannot add memory dbdma dev\n"); | ||
1112 | #endif | ||
1007 | return platform_driver_register(&au1xmmc_driver); | 1113 | return platform_driver_register(&au1xmmc_driver); |
1008 | } | 1114 | } |
1009 | 1115 | ||
1010 | static void __exit au1xmmc_exit(void) | 1116 | static void __exit au1xmmc_exit(void) |
1011 | { | 1117 | { |
1118 | #ifdef CONFIG_SOC_AU1200 | ||
1119 | if (memid) | ||
1120 | au1xxx_ddma_del_device(memid); | ||
1121 | #endif | ||
1012 | platform_driver_unregister(&au1xmmc_driver); | 1122 | platform_driver_unregister(&au1xmmc_driver); |
1013 | } | 1123 | } |
1014 | 1124 | ||
1015 | module_init(au1xmmc_init); | 1125 | module_init(au1xmmc_init); |
1016 | module_exit(au1xmmc_exit); | 1126 | module_exit(au1xmmc_exit); |
1017 | 1127 | ||
1018 | #ifdef MODULE | ||
1019 | MODULE_AUTHOR("Advanced Micro Devices, Inc"); | 1128 | MODULE_AUTHOR("Advanced Micro Devices, Inc"); |
1020 | MODULE_DESCRIPTION("MMC/SD driver for the Alchemy Au1XXX"); | 1129 | MODULE_DESCRIPTION("MMC/SD driver for the Alchemy Au1XXX"); |
1021 | MODULE_LICENSE("GPL"); | 1130 | MODULE_LICENSE("GPL"); |
1022 | MODULE_ALIAS("platform:au1xxx-mmc"); | 1131 | MODULE_ALIAS("platform:au1xxx-mmc"); |
1023 | #endif | ||
1024 | |||