[PATCH] ALCHEMY: Add SD support to AU1200 MMC/SD driver
From: Jordan Crouse <hidden>
Date: 2005-12-02 18:50:21
Subsystem:
multimedia card (mmc), secure digital (sd) and sdio subsystem, the rest · Maintainers:
Ulf Hansson, Linus Torvalds
Add SD support to the AU1200 MMC driver. This can be added post 2.6.15, I'm just sending them out today so the various maintainers can get them queued up. Signed-off-by: Jordan Crouse <redacted> --- drivers/mmc/au1xmmc.c | 124 ++++++++++++++++++++++++++++--------------------- 1 files changed, 71 insertions(+), 53 deletions(-)
diff --git a/drivers/mmc/au1xmmc.c b/drivers/mmc/au1xmmc.c
index cb32a08..c8c8f29 100644
--- a/drivers/mmc/au1xmmc.c
+++ b/drivers/mmc/au1xmmc.c@@ -99,16 +99,24 @@ static inline void IRQ_ON(struct au1xmmc au_sync(); } -static inline void FLUSH_FIFO(struct au1xmmc_host *host) +/* Turn on the FIFO flush - the fifo will be returned to active right + * before data transfer + */ + +static inline void FLUSH_FIFO_ON(struct au1xmmc_host *host) { u32 val = au_readl(HOST_CONFIG2(host)); + val |= SD_CONFIG2_FF; + au_writel(val, HOST_CONFIG2(host)); + au_sync(); +} - au_writel(val | SD_CONFIG2_FF, HOST_CONFIG2(host)); - au_sync_delay(1); - - /* SEND_STOP will turn off clock control - this re-enables it */ - val &= ~SD_CONFIG2_DF; +static inline void FIFO_ACTIVE(struct au1xmmc_host *host) +{ + u32 val = au_readl(HOST_CONFIG2(host)); + /* SEND_STOP will turn off clock control - this re-enables it */ + val &= ~(SD_CONFIG2_DF | SD_CONFIG2_FF); au_writel(val, HOST_CONFIG2(host)); au_sync(); }
@@ -124,8 +132,8 @@ static inline void IRQ_OFF(struct au1xmm static inline void SEND_STOP(struct au1xmmc_host *host) { - /* We know the value of CONFIG2, so avoid a read we don't need */ - u32 mask = SD_CONFIG2_EN; + /* Penalty box for Jordan - NEVER ASSUME! */ + u32 mask = au_readl(HOST_CONFIG2(host)); WARN_ON(host->status != HOST_S_DATA); host->status = HOST_S_STOP;
@@ -169,7 +177,7 @@ static void au1xmmc_finish_request(struc host->flags &= HOST_F_ACTIVE; host->dma.len = 0; - host->dma.dir = 0; + host->dma.dir = DMA_BIDIRECTIONAL; host->pio.index = 0; host->pio.offset = 0;
@@ -179,6 +187,9 @@ static void au1xmmc_finish_request(struc bcsr->disk_leds |= (1 << 8); + /* Flush the FIFO until our next request */ + FLUSH_FIFO_ON(host); + mmc_request_done(host->mmc, mrq); }
@@ -196,7 +207,11 @@ static int au1xmmc_send_command(struct a switch(cmd->flags) { case MMC_RSP_R1: - mmccmd |= SD_CMD_RT_1; + if (cmd->opcode == 0x03 && host->mmc->mode == MMC_MODE_SD) + mmccmd |= SD_CMD_RT_6; + else + mmccmd |= SD_CMD_RT_1; + break; case MMC_RSP_R1B: mmccmd |= SD_CMD_RT_1B;
@@ -504,8 +519,8 @@ static void au1xmmc_cmd_complete(struct r[3] = au_readl(host->iobase + SD_RESP0); /* The CRC is omitted from the response, so really we only got - * 120 bytes, but the engine expects 128 bits, so we have to shift - * things up + * 120 bytes, but the engine expects 128 bits, so we have to + * shift things up */ for(i = 0; i < 4; i++) {
@@ -576,9 +591,8 @@ au1xmmc_prepare_data(struct au1xmmc_host { int datalen = data->blocks * (1 << data->blksz_bits); - - if (dma != 0) - host->flags |= HOST_F_DMA; + int i = 0; + u32 channel; if (data->flags & MMC_DATA_READ) host->flags |= HOST_F_RECV;
@@ -588,8 +602,6 @@ au1xmmc_prepare_data(struct au1xmmc_host if (host->mrq->stop) host->flags |= HOST_F_STOP; - host->dma.dir = DMA_BIDIRECTIONAL; - host->dma.len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, host->dma.dir);
@@ -598,9 +610,21 @@ au1xmmc_prepare_data(struct au1xmmc_host au_writel((1 << data->blksz_bits) - 1, HOST_BLKSIZE(host)); - if (host->flags & HOST_F_DMA) { - int i; - u32 channel = DMA_CHANNEL(host); + if (dma == 0) { + host->pio.index = 0; + host->pio.offset = 0; + host->pio.len = datalen; + + if (host->flags & HOST_F_XMIT) + IRQ_ON(host, SD_CONFIG_TH); + else + IRQ_ON(host, SD_CONFIG_NE); + + return MMC_ERR_NONE; + } + + host->flags |= HOST_F_DMA; + channel = DMA_CHANNEL(host); au1xxx_dbdma_stop(channel);
@@ -611,7 +635,7 @@ au1xmmc_prepare_data(struct au1xmmc_host int len = (datalen > sg_len) ? sg_len : datalen; - if (i == host->dma.len - 1) + if (i == (host->dma.len - 1)) flags = DDMA_FLAGS_IE; if (host->flags & HOST_F_XMIT){
@@ -627,23 +651,11 @@ au1xmmc_prepare_data(struct au1xmmc_host len, flags); } - if (!ret) + if (ret == 0) goto dataerr; datalen -= len; } - } - else { - host->pio.index = 0; - host->pio.offset = 0; - host->pio.len = datalen; - - if (host->flags & HOST_F_XMIT) - IRQ_ON(host, SD_CONFIG_TH); - else - IRQ_ON(host, SD_CONFIG_NE); - //IRQ_ON(host, SD_CONFIG_RA|SD_CONFIG_RF); - } return MMC_ERR_NONE;
@@ -671,7 +683,7 @@ static void au1xmmc_request(struct mmc_h bcsr->disk_leds &= ~(1 << 8); if (mrq->data) { - FLUSH_FIFO(host); + FIFO_ACTIVE(host); ret = au1xmmc_prepare_data(host, mrq->data); }
@@ -734,6 +746,20 @@ static void au1xmmc_set_ios(struct mmc_h au1xmmc_set_clock(host, ios->clock); host->clock = ios->clock; } + + /* Set the bus width for SD */ + + if (ios->bus_width != host->bus_width) { + u32 val; + val = au_readl(HOST_CONFIG2(host)); + val &= ~(SD_CONFIG2_WB); + val |= (ios->bus_width == MMC_BUS_WIDTH_4) ? SD_CONFIG2_WB : 0; + + au_writel(val, HOST_CONFIG2(host)); + au_sync(); + + host->bus_width = ios->bus_width; + } } static void au1xmmc_dma_callback(int irq, void *dev_id, struct pt_regs *regs)
@@ -778,24 +804,8 @@ static irqreturn_t au1xmmc_irq(int irq, /* In PIO mode, interrupts might still be enabled */ IRQ_OFF(host, SD_CONFIG_NE | SD_CONFIG_TH); - - //IRQ_OFF(host, SD_CONFIG_TH|SD_CONFIG_RA|SD_CONFIG_RF); tasklet_schedule(&host->finish_task); } -#if 0 - else if (status & SD_STATUS_DD) { - - /* Sometimes we get a DD before a NE in PIO mode */ - - if (!(host->flags & HOST_F_DMA) && - (status & SD_STATUS_NE)) - au1xmmc_receive_pio(host); - else { - au1xmmc_data_complete(host, status); - //tasklet_schedule(&host->data_task); - } - } -#endif else if (status & (SD_STATUS_CR)) { if (host->status == HOST_S_CMD) au1xmmc_cmd_complete(host,status);
@@ -875,9 +885,15 @@ static void au1xmmc_init_dma(struct au1x host->rx_chan = rxchan; } +static int au1xmmc_get_ro(struct mmc_host *mmc) { + struct au1xmmc_host *host = mmc_priv(mmc); + return au1xmmc_card_readonly(host); +} + struct mmc_host_ops au1xmmc_ops = { .request = au1xmmc_request, .set_ios = au1xmmc_set_ios, + .get_ro = au1xmmc_get_ro, }; static int au1xmmc_probe(struct device *dev)
@@ -914,6 +930,7 @@ static int au1xmmc_probe(struct device * mmc->max_seg_size = AU1XMMC_DESCRIPTOR_SIZE; mmc->max_phys_segs = AU1XMMC_DESCRIPTOR_COUNT; + mmc->caps = MMC_CAP_4_BIT_DATA; mmc->ocr_avail = AU1XMMC_OCR; host = mmc_priv(mmc);
@@ -923,7 +940,9 @@ static int au1xmmc_probe(struct device * host->iobase = au1xmmc_card_table[host->id].iobase; host->clock = 0; host->power_mode = MMC_POWER_OFF; - + + host->bus_width = MMC_BUS_WIDTH_1; + host->flags = au1xmmc_card_inserted(host) ? HOST_F_ACTIVE : 0; host->status = HOST_S_IDLE;
@@ -1017,4 +1036,3 @@ MODULE_AUTHOR("Advanced Micro Devices, I MODULE_DESCRIPTION("MMC/SD driver for the Alchemy Au1XXX"); MODULE_LICENSE("GPL"); #endif -