diff options
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 | |||