|
@@ -30,6 +30,7 @@
|
|
|
|
|
|
#include <mach/atmel-mci.h>
|
|
|
#include <linux/atmel-mci.h>
|
|
|
+#include <linux/atmel_pdc.h>
|
|
|
|
|
|
#include <asm/io.h>
|
|
|
#include <asm/unaligned.h>
|
|
@@ -39,7 +40,7 @@
|
|
|
|
|
|
#include "atmel-mci-regs.h"
|
|
|
|
|
|
-#define ATMCI_DATA_ERROR_FLAGS (MCI_DCRCE | MCI_DTOE | MCI_OVRE | MCI_UNRE)
|
|
|
+#define ATMCI_DATA_ERROR_FLAGS (ATMCI_DCRCE | ATMCI_DTOE | ATMCI_OVRE | ATMCI_UNRE)
|
|
|
#define ATMCI_DMA_THRESHOLD 16
|
|
|
|
|
|
enum {
|
|
@@ -58,18 +59,35 @@ enum atmel_mci_state {
|
|
|
STATE_DATA_ERROR,
|
|
|
};
|
|
|
|
|
|
+enum atmci_xfer_dir {
|
|
|
+ XFER_RECEIVE = 0,
|
|
|
+ XFER_TRANSMIT,
|
|
|
+};
|
|
|
+
|
|
|
+enum atmci_pdc_buf {
|
|
|
+ PDC_FIRST_BUF = 0,
|
|
|
+ PDC_SECOND_BUF,
|
|
|
+};
|
|
|
+
|
|
|
+struct atmel_mci_caps {
|
|
|
+ bool has_dma;
|
|
|
+ bool has_pdc;
|
|
|
+ bool has_cfg_reg;
|
|
|
+ bool has_cstor_reg;
|
|
|
+ bool has_highspeed;
|
|
|
+ bool has_rwproof;
|
|
|
+};
|
|
|
+
|
|
|
struct atmel_mci_dma {
|
|
|
-#ifdef CONFIG_MMC_ATMELMCI_DMA
|
|
|
struct dma_chan *chan;
|
|
|
struct dma_async_tx_descriptor *data_desc;
|
|
|
-#endif
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
* struct atmel_mci - MMC controller state shared between all slots
|
|
|
* @lock: Spinlock protecting the queue and associated data.
|
|
|
* @regs: Pointer to MMIO registers.
|
|
|
- * @sg: Scatterlist entry currently being processed by PIO code, if any.
|
|
|
+ * @sg: Scatterlist entry currently being processed by PIO or PDC code.
|
|
|
* @pio_offset: Offset into the current scatterlist entry.
|
|
|
* @cur_slot: The slot which is currently using the controller.
|
|
|
* @mrq: The request currently being processed on @cur_slot,
|
|
@@ -77,6 +95,7 @@ struct atmel_mci_dma {
|
|
|
* @cmd: The command currently being sent to the card, or NULL.
|
|
|
* @data: The data currently being transferred, or NULL if no data
|
|
|
* transfer is in progress.
|
|
|
+ * @data_size: just data->blocks * data->blksz.
|
|
|
* @dma: DMA client state.
|
|
|
* @data_chan: DMA channel being used for the current data transfer.
|
|
|
* @cmd_status: Snapshot of SR taken upon completion of the current
|
|
@@ -103,6 +122,13 @@ struct atmel_mci_dma {
|
|
|
* @mck: The peripheral bus clock hooked up to the MMC controller.
|
|
|
* @pdev: Platform device associated with the MMC controller.
|
|
|
* @slot: Slots sharing this MMC controller.
|
|
|
+ * @caps: MCI capabilities depending on MCI version.
|
|
|
+ * @prepare_data: function to setup MCI before data transfer which
|
|
|
+ * depends on MCI capabilities.
|
|
|
+ * @submit_data: function to start data transfer which depends on MCI
|
|
|
+ * capabilities.
|
|
|
+ * @stop_transfer: function to stop data transfer which depends on MCI
|
|
|
+ * capabilities.
|
|
|
*
|
|
|
* Locking
|
|
|
* =======
|
|
@@ -143,6 +169,7 @@ struct atmel_mci {
|
|
|
struct mmc_request *mrq;
|
|
|
struct mmc_command *cmd;
|
|
|
struct mmc_data *data;
|
|
|
+ unsigned int data_size;
|
|
|
|
|
|
struct atmel_mci_dma dma;
|
|
|
struct dma_chan *data_chan;
|
|
@@ -166,7 +193,13 @@ struct atmel_mci {
|
|
|
struct clk *mck;
|
|
|
struct platform_device *pdev;
|
|
|
|
|
|
- struct atmel_mci_slot *slot[ATMEL_MCI_MAX_NR_SLOTS];
|
|
|
+ struct atmel_mci_slot *slot[ATMCI_MAX_NR_SLOTS];
|
|
|
+
|
|
|
+ struct atmel_mci_caps caps;
|
|
|
+
|
|
|
+ u32 (*prepare_data)(struct atmel_mci *host, struct mmc_data *data);
|
|
|
+ void (*submit_data)(struct atmel_mci *host, struct mmc_data *data);
|
|
|
+ void (*stop_transfer)(struct atmel_mci *host);
|
|
|
};
|
|
|
|
|
|
/**
|
|
@@ -219,31 +252,6 @@ struct atmel_mci_slot {
|
|
|
#define atmci_set_pending(host, event) \
|
|
|
set_bit(event, &host->pending_events)
|
|
|
|
|
|
-/*
|
|
|
- * Enable or disable features/registers based on
|
|
|
- * whether the processor supports them
|
|
|
- */
|
|
|
-static bool mci_has_rwproof(void)
|
|
|
-{
|
|
|
- if (cpu_is_at91sam9261() || cpu_is_at91rm9200())
|
|
|
- return false;
|
|
|
- else
|
|
|
- return true;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * The new MCI2 module isn't 100% compatible with the old MCI module,
|
|
|
- * and it has a few nice features which we want to use...
|
|
|
- */
|
|
|
-static inline bool atmci_is_mci2(void)
|
|
|
-{
|
|
|
- if (cpu_is_at91sam9g45())
|
|
|
- return true;
|
|
|
-
|
|
|
- return false;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
/*
|
|
|
* The debugfs stuff below is mostly optimized away when
|
|
|
* CONFIG_DEBUG_FS is not set.
|
|
@@ -352,7 +360,7 @@ static int atmci_regs_show(struct seq_file *s, void *v)
|
|
|
struct atmel_mci *host = s->private;
|
|
|
u32 *buf;
|
|
|
|
|
|
- buf = kmalloc(MCI_REGS_SIZE, GFP_KERNEL);
|
|
|
+ buf = kmalloc(ATMCI_REGS_SIZE, GFP_KERNEL);
|
|
|
if (!buf)
|
|
|
return -ENOMEM;
|
|
|
|
|
@@ -363,47 +371,50 @@ static int atmci_regs_show(struct seq_file *s, void *v)
|
|
|
*/
|
|
|
spin_lock_bh(&host->lock);
|
|
|
clk_enable(host->mck);
|
|
|
- memcpy_fromio(buf, host->regs, MCI_REGS_SIZE);
|
|
|
+ memcpy_fromio(buf, host->regs, ATMCI_REGS_SIZE);
|
|
|
clk_disable(host->mck);
|
|
|
spin_unlock_bh(&host->lock);
|
|
|
|
|
|
seq_printf(s, "MR:\t0x%08x%s%s CLKDIV=%u\n",
|
|
|
- buf[MCI_MR / 4],
|
|
|
- buf[MCI_MR / 4] & MCI_MR_RDPROOF ? " RDPROOF" : "",
|
|
|
- buf[MCI_MR / 4] & MCI_MR_WRPROOF ? " WRPROOF" : "",
|
|
|
- buf[MCI_MR / 4] & 0xff);
|
|
|
- seq_printf(s, "DTOR:\t0x%08x\n", buf[MCI_DTOR / 4]);
|
|
|
- seq_printf(s, "SDCR:\t0x%08x\n", buf[MCI_SDCR / 4]);
|
|
|
- seq_printf(s, "ARGR:\t0x%08x\n", buf[MCI_ARGR / 4]);
|
|
|
+ buf[ATMCI_MR / 4],
|
|
|
+ buf[ATMCI_MR / 4] & ATMCI_MR_RDPROOF ? " RDPROOF" : "",
|
|
|
+ buf[ATMCI_MR / 4] & ATMCI_MR_WRPROOF ? " WRPROOF" : "",
|
|
|
+ buf[ATMCI_MR / 4] & 0xff);
|
|
|
+ seq_printf(s, "DTOR:\t0x%08x\n", buf[ATMCI_DTOR / 4]);
|
|
|
+ seq_printf(s, "SDCR:\t0x%08x\n", buf[ATMCI_SDCR / 4]);
|
|
|
+ seq_printf(s, "ARGR:\t0x%08x\n", buf[ATMCI_ARGR / 4]);
|
|
|
seq_printf(s, "BLKR:\t0x%08x BCNT=%u BLKLEN=%u\n",
|
|
|
- buf[MCI_BLKR / 4],
|
|
|
- buf[MCI_BLKR / 4] & 0xffff,
|
|
|
- (buf[MCI_BLKR / 4] >> 16) & 0xffff);
|
|
|
- if (atmci_is_mci2())
|
|
|
- seq_printf(s, "CSTOR:\t0x%08x\n", buf[MCI_CSTOR / 4]);
|
|
|
+ buf[ATMCI_BLKR / 4],
|
|
|
+ buf[ATMCI_BLKR / 4] & 0xffff,
|
|
|
+ (buf[ATMCI_BLKR / 4] >> 16) & 0xffff);
|
|
|
+ if (host->caps.has_cstor_reg)
|
|
|
+ seq_printf(s, "CSTOR:\t0x%08x\n", buf[ATMCI_CSTOR / 4]);
|
|
|
|
|
|
/* Don't read RSPR and RDR; it will consume the data there */
|
|
|
|
|
|
- atmci_show_status_reg(s, "SR", buf[MCI_SR / 4]);
|
|
|
- atmci_show_status_reg(s, "IMR", buf[MCI_IMR / 4]);
|
|
|
+ atmci_show_status_reg(s, "SR", buf[ATMCI_SR / 4]);
|
|
|
+ atmci_show_status_reg(s, "IMR", buf[ATMCI_IMR / 4]);
|
|
|
|
|
|
- if (atmci_is_mci2()) {
|
|
|
+ if (host->caps.has_dma) {
|
|
|
u32 val;
|
|
|
|
|
|
- val = buf[MCI_DMA / 4];
|
|
|
+ val = buf[ATMCI_DMA / 4];
|
|
|
seq_printf(s, "DMA:\t0x%08x OFFSET=%u CHKSIZE=%u%s\n",
|
|
|
val, val & 3,
|
|
|
((val >> 4) & 3) ?
|
|
|
1 << (((val >> 4) & 3) + 1) : 1,
|
|
|
- val & MCI_DMAEN ? " DMAEN" : "");
|
|
|
+ val & ATMCI_DMAEN ? " DMAEN" : "");
|
|
|
+ }
|
|
|
+ if (host->caps.has_cfg_reg) {
|
|
|
+ u32 val;
|
|
|
|
|
|
- val = buf[MCI_CFG / 4];
|
|
|
+ val = buf[ATMCI_CFG / 4];
|
|
|
seq_printf(s, "CFG:\t0x%08x%s%s%s%s\n",
|
|
|
val,
|
|
|
- val & MCI_CFG_FIFOMODE_1DATA ? " FIFOMODE_ONE_DATA" : "",
|
|
|
- val & MCI_CFG_FERRCTRL_COR ? " FERRCTRL_CLEAR_ON_READ" : "",
|
|
|
- val & MCI_CFG_HSMODE ? " HSMODE" : "",
|
|
|
- val & MCI_CFG_LSYNC ? " LSYNC" : "");
|
|
|
+ val & ATMCI_CFG_FIFOMODE_1DATA ? " FIFOMODE_ONE_DATA" : "",
|
|
|
+ val & ATMCI_CFG_FERRCTRL_COR ? " FERRCTRL_CLEAR_ON_READ" : "",
|
|
|
+ val & ATMCI_CFG_HSMODE ? " HSMODE" : "",
|
|
|
+ val & ATMCI_CFG_LSYNC ? " LSYNC" : "");
|
|
|
}
|
|
|
|
|
|
kfree(buf);
|
|
@@ -466,7 +477,7 @@ err:
|
|
|
dev_err(&mmc->class_dev, "failed to initialize debugfs for slot\n");
|
|
|
}
|
|
|
|
|
|
-static inline unsigned int ns_to_clocks(struct atmel_mci *host,
|
|
|
+static inline unsigned int atmci_ns_to_clocks(struct atmel_mci *host,
|
|
|
unsigned int ns)
|
|
|
{
|
|
|
return (ns * (host->bus_hz / 1000000) + 999) / 1000;
|
|
@@ -482,7 +493,8 @@ static void atmci_set_timeout(struct atmel_mci *host,
|
|
|
unsigned dtocyc;
|
|
|
unsigned dtomul;
|
|
|
|
|
|
- timeout = ns_to_clocks(host, data->timeout_ns) + data->timeout_clks;
|
|
|
+ timeout = atmci_ns_to_clocks(host, data->timeout_ns)
|
|
|
+ + data->timeout_clks;
|
|
|
|
|
|
for (dtomul = 0; dtomul < 8; dtomul++) {
|
|
|
unsigned shift = dtomul_to_shift[dtomul];
|
|
@@ -498,7 +510,7 @@ static void atmci_set_timeout(struct atmel_mci *host,
|
|
|
|
|
|
dev_vdbg(&slot->mmc->class_dev, "setting timeout to %u cycles\n",
|
|
|
dtocyc << dtomul_to_shift[dtomul]);
|
|
|
- mci_writel(host, DTOR, (MCI_DTOMUL(dtomul) | MCI_DTOCYC(dtocyc)));
|
|
|
+ atmci_writel(host, ATMCI_DTOR, (ATMCI_DTOMUL(dtomul) | ATMCI_DTOCYC(dtocyc)));
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -512,13 +524,13 @@ static u32 atmci_prepare_command(struct mmc_host *mmc,
|
|
|
|
|
|
cmd->error = -EINPROGRESS;
|
|
|
|
|
|
- cmdr = MCI_CMDR_CMDNB(cmd->opcode);
|
|
|
+ cmdr = ATMCI_CMDR_CMDNB(cmd->opcode);
|
|
|
|
|
|
if (cmd->flags & MMC_RSP_PRESENT) {
|
|
|
if (cmd->flags & MMC_RSP_136)
|
|
|
- cmdr |= MCI_CMDR_RSPTYP_136BIT;
|
|
|
+ cmdr |= ATMCI_CMDR_RSPTYP_136BIT;
|
|
|
else
|
|
|
- cmdr |= MCI_CMDR_RSPTYP_48BIT;
|
|
|
+ cmdr |= ATMCI_CMDR_RSPTYP_48BIT;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -526,34 +538,34 @@ static u32 atmci_prepare_command(struct mmc_host *mmc,
|
|
|
* it's too difficult to determine whether this is an ACMD or
|
|
|
* not. Better make it 64.
|
|
|
*/
|
|
|
- cmdr |= MCI_CMDR_MAXLAT_64CYC;
|
|
|
+ cmdr |= ATMCI_CMDR_MAXLAT_64CYC;
|
|
|
|
|
|
if (mmc->ios.bus_mode == MMC_BUSMODE_OPENDRAIN)
|
|
|
- cmdr |= MCI_CMDR_OPDCMD;
|
|
|
+ cmdr |= ATMCI_CMDR_OPDCMD;
|
|
|
|
|
|
data = cmd->data;
|
|
|
if (data) {
|
|
|
- cmdr |= MCI_CMDR_START_XFER;
|
|
|
+ cmdr |= ATMCI_CMDR_START_XFER;
|
|
|
|
|
|
if (cmd->opcode == SD_IO_RW_EXTENDED) {
|
|
|
- cmdr |= MCI_CMDR_SDIO_BLOCK;
|
|
|
+ cmdr |= ATMCI_CMDR_SDIO_BLOCK;
|
|
|
} else {
|
|
|
if (data->flags & MMC_DATA_STREAM)
|
|
|
- cmdr |= MCI_CMDR_STREAM;
|
|
|
+ cmdr |= ATMCI_CMDR_STREAM;
|
|
|
else if (data->blocks > 1)
|
|
|
- cmdr |= MCI_CMDR_MULTI_BLOCK;
|
|
|
+ cmdr |= ATMCI_CMDR_MULTI_BLOCK;
|
|
|
else
|
|
|
- cmdr |= MCI_CMDR_BLOCK;
|
|
|
+ cmdr |= ATMCI_CMDR_BLOCK;
|
|
|
}
|
|
|
|
|
|
if (data->flags & MMC_DATA_READ)
|
|
|
- cmdr |= MCI_CMDR_TRDIR_READ;
|
|
|
+ cmdr |= ATMCI_CMDR_TRDIR_READ;
|
|
|
}
|
|
|
|
|
|
return cmdr;
|
|
|
}
|
|
|
|
|
|
-static void atmci_start_command(struct atmel_mci *host,
|
|
|
+static void atmci_send_command(struct atmel_mci *host,
|
|
|
struct mmc_command *cmd, u32 cmd_flags)
|
|
|
{
|
|
|
WARN_ON(host->cmd);
|
|
@@ -563,43 +575,119 @@ static void atmci_start_command(struct atmel_mci *host,
|
|
|
"start command: ARGR=0x%08x CMDR=0x%08x\n",
|
|
|
cmd->arg, cmd_flags);
|
|
|
|
|
|
- mci_writel(host, ARGR, cmd->arg);
|
|
|
- mci_writel(host, CMDR, cmd_flags);
|
|
|
+ atmci_writel(host, ATMCI_ARGR, cmd->arg);
|
|
|
+ atmci_writel(host, ATMCI_CMDR, cmd_flags);
|
|
|
}
|
|
|
|
|
|
-static void send_stop_cmd(struct atmel_mci *host, struct mmc_data *data)
|
|
|
+static void atmci_send_stop_cmd(struct atmel_mci *host, struct mmc_data *data)
|
|
|
{
|
|
|
- atmci_start_command(host, data->stop, host->stop_cmdr);
|
|
|
- mci_writel(host, IER, MCI_CMDRDY);
|
|
|
+ atmci_send_command(host, data->stop, host->stop_cmdr);
|
|
|
+ atmci_writel(host, ATMCI_IER, ATMCI_CMDRDY);
|
|
|
}
|
|
|
|
|
|
-#ifdef CONFIG_MMC_ATMELMCI_DMA
|
|
|
-static void atmci_dma_cleanup(struct atmel_mci *host)
|
|
|
+/*
|
|
|
+ * Configure given PDC buffer taking care of alignement issues.
|
|
|
+ * Update host->data_size and host->sg.
|
|
|
+ */
|
|
|
+static void atmci_pdc_set_single_buf(struct atmel_mci *host,
|
|
|
+ enum atmci_xfer_dir dir, enum atmci_pdc_buf buf_nb)
|
|
|
+{
|
|
|
+ u32 pointer_reg, counter_reg;
|
|
|
+
|
|
|
+ if (dir == XFER_RECEIVE) {
|
|
|
+ pointer_reg = ATMEL_PDC_RPR;
|
|
|
+ counter_reg = ATMEL_PDC_RCR;
|
|
|
+ } else {
|
|
|
+ pointer_reg = ATMEL_PDC_TPR;
|
|
|
+ counter_reg = ATMEL_PDC_TCR;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (buf_nb == PDC_SECOND_BUF) {
|
|
|
+ pointer_reg += ATMEL_PDC_SCND_BUF_OFF;
|
|
|
+ counter_reg += ATMEL_PDC_SCND_BUF_OFF;
|
|
|
+ }
|
|
|
+
|
|
|
+ atmci_writel(host, pointer_reg, sg_dma_address(host->sg));
|
|
|
+ if (host->data_size <= sg_dma_len(host->sg)) {
|
|
|
+ if (host->data_size & 0x3) {
|
|
|
+ /* If size is different from modulo 4, transfer bytes */
|
|
|
+ atmci_writel(host, counter_reg, host->data_size);
|
|
|
+ atmci_writel(host, ATMCI_MR, host->mode_reg | ATMCI_MR_PDCFBYTE);
|
|
|
+ } else {
|
|
|
+ /* Else transfer 32-bits words */
|
|
|
+ atmci_writel(host, counter_reg, host->data_size / 4);
|
|
|
+ }
|
|
|
+ host->data_size = 0;
|
|
|
+ } else {
|
|
|
+ /* We assume the size of a page is 32-bits aligned */
|
|
|
+ atmci_writel(host, counter_reg, sg_dma_len(host->sg) / 4);
|
|
|
+ host->data_size -= sg_dma_len(host->sg);
|
|
|
+ if (host->data_size)
|
|
|
+ host->sg = sg_next(host->sg);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Configure PDC buffer according to the data size ie configuring one or two
|
|
|
+ * buffers. Don't use this function if you want to configure only the second
|
|
|
+ * buffer. In this case, use atmci_pdc_set_single_buf.
|
|
|
+ */
|
|
|
+static void atmci_pdc_set_both_buf(struct atmel_mci *host, int dir)
|
|
|
{
|
|
|
- struct mmc_data *data = host->data;
|
|
|
+ atmci_pdc_set_single_buf(host, dir, PDC_FIRST_BUF);
|
|
|
+ if (host->data_size)
|
|
|
+ atmci_pdc_set_single_buf(host, dir, PDC_SECOND_BUF);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Unmap sg lists, called when transfer is finished.
|
|
|
+ */
|
|
|
+static void atmci_pdc_cleanup(struct atmel_mci *host)
|
|
|
+{
|
|
|
+ struct mmc_data *data = host->data;
|
|
|
|
|
|
if (data)
|
|
|
- dma_unmap_sg(host->dma.chan->device->dev,
|
|
|
- data->sg, data->sg_len,
|
|
|
- ((data->flags & MMC_DATA_WRITE)
|
|
|
- ? DMA_TO_DEVICE : DMA_FROM_DEVICE));
|
|
|
+ dma_unmap_sg(&host->pdev->dev,
|
|
|
+ data->sg, data->sg_len,
|
|
|
+ ((data->flags & MMC_DATA_WRITE)
|
|
|
+ ? DMA_TO_DEVICE : DMA_FROM_DEVICE));
|
|
|
}
|
|
|
|
|
|
-static void atmci_stop_dma(struct atmel_mci *host)
|
|
|
+/*
|
|
|
+ * Disable PDC transfers. Update pending flags to EVENT_XFER_COMPLETE after
|
|
|
+ * having received ATMCI_TXBUFE or ATMCI_RXBUFF interrupt. Enable ATMCI_NOTBUSY
|
|
|
+ * interrupt needed for both transfer directions.
|
|
|
+ */
|
|
|
+static void atmci_pdc_complete(struct atmel_mci *host)
|
|
|
{
|
|
|
- struct dma_chan *chan = host->data_chan;
|
|
|
+ atmci_writel(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
|
|
|
+ atmci_pdc_cleanup(host);
|
|
|
|
|
|
- if (chan) {
|
|
|
- dmaengine_terminate_all(chan);
|
|
|
- atmci_dma_cleanup(host);
|
|
|
- } else {
|
|
|
- /* Data transfer was stopped by the interrupt handler */
|
|
|
+ /*
|
|
|
+ * If the card was removed, data will be NULL. No point trying
|
|
|
+ * to send the stop command or waiting for NBUSY in this case.
|
|
|
+ */
|
|
|
+ if (host->data) {
|
|
|
atmci_set_pending(host, EVENT_XFER_COMPLETE);
|
|
|
- mci_writel(host, IER, MCI_NOTBUSY);
|
|
|
+ tasklet_schedule(&host->tasklet);
|
|
|
+ atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/* This function is called by the DMA driver from tasklet context. */
|
|
|
+static void atmci_dma_cleanup(struct atmel_mci *host)
|
|
|
+{
|
|
|
+ struct mmc_data *data = host->data;
|
|
|
+
|
|
|
+ if (data)
|
|
|
+ dma_unmap_sg(host->dma.chan->device->dev,
|
|
|
+ data->sg, data->sg_len,
|
|
|
+ ((data->flags & MMC_DATA_WRITE)
|
|
|
+ ? DMA_TO_DEVICE : DMA_FROM_DEVICE));
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * This function is called by the DMA driver from tasklet context.
|
|
|
+ */
|
|
|
static void atmci_dma_complete(void *arg)
|
|
|
{
|
|
|
struct atmel_mci *host = arg;
|
|
@@ -607,9 +695,9 @@ static void atmci_dma_complete(void *arg)
|
|
|
|
|
|
dev_vdbg(&host->pdev->dev, "DMA complete\n");
|
|
|
|
|
|
- if (atmci_is_mci2())
|
|
|
+ if (host->caps.has_dma)
|
|
|
/* Disable DMA hardware handshaking on MCI */
|
|
|
- mci_writel(host, DMA, mci_readl(host, DMA) & ~MCI_DMAEN);
|
|
|
+ atmci_writel(host, ATMCI_DMA, atmci_readl(host, ATMCI_DMA) & ~ATMCI_DMAEN);
|
|
|
|
|
|
atmci_dma_cleanup(host);
|
|
|
|
|
@@ -641,11 +729,93 @@ static void atmci_dma_complete(void *arg)
|
|
|
* completion callback" rule of the dma engine
|
|
|
* framework.
|
|
|
*/
|
|
|
- mci_writel(host, IER, MCI_NOTBUSY);
|
|
|
+ atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static int
|
|
|
+/*
|
|
|
+ * Returns a mask of interrupt flags to be enabled after the whole
|
|
|
+ * request has been prepared.
|
|
|
+ */
|
|
|
+static u32 atmci_prepare_data(struct atmel_mci *host, struct mmc_data *data)
|
|
|
+{
|
|
|
+ u32 iflags;
|
|
|
+
|
|
|
+ data->error = -EINPROGRESS;
|
|
|
+
|
|
|
+ host->sg = data->sg;
|
|
|
+ host->data = data;
|
|
|
+ host->data_chan = NULL;
|
|
|
+
|
|
|
+ iflags = ATMCI_DATA_ERROR_FLAGS;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Errata: MMC data write operation with less than 12
|
|
|
+ * bytes is impossible.
|
|
|
+ *
|
|
|
+ * Errata: MCI Transmit Data Register (TDR) FIFO
|
|
|
+ * corruption when length is not multiple of 4.
|
|
|
+ */
|
|
|
+ if (data->blocks * data->blksz < 12
|
|
|
+ || (data->blocks * data->blksz) & 3)
|
|
|
+ host->need_reset = true;
|
|
|
+
|
|
|
+ host->pio_offset = 0;
|
|
|
+ if (data->flags & MMC_DATA_READ)
|
|
|
+ iflags |= ATMCI_RXRDY;
|
|
|
+ else
|
|
|
+ iflags |= ATMCI_TXRDY;
|
|
|
+
|
|
|
+ return iflags;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Set interrupt flags and set block length into the MCI mode register even
|
|
|
+ * if this value is also accessible in the MCI block register. It seems to be
|
|
|
+ * necessary before the High Speed MCI version. It also map sg and configure
|
|
|
+ * PDC registers.
|
|
|
+ */
|
|
|
+static u32
|
|
|
+atmci_prepare_data_pdc(struct atmel_mci *host, struct mmc_data *data)
|
|
|
+{
|
|
|
+ u32 iflags, tmp;
|
|
|
+ unsigned int sg_len;
|
|
|
+ enum dma_data_direction dir;
|
|
|
+
|
|
|
+ data->error = -EINPROGRESS;
|
|
|
+
|
|
|
+ host->data = data;
|
|
|
+ host->sg = data->sg;
|
|
|
+ iflags = ATMCI_DATA_ERROR_FLAGS;
|
|
|
+
|
|
|
+ /* Enable pdc mode */
|
|
|
+ atmci_writel(host, ATMCI_MR, host->mode_reg | ATMCI_MR_PDCMODE);
|
|
|
+
|
|
|
+ if (data->flags & MMC_DATA_READ) {
|
|
|
+ dir = DMA_FROM_DEVICE;
|
|
|
+ iflags |= ATMCI_ENDRX | ATMCI_RXBUFF;
|
|
|
+ } else {
|
|
|
+ dir = DMA_TO_DEVICE;
|
|
|
+ iflags |= ATMCI_ENDTX | ATMCI_TXBUFE;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Set BLKLEN */
|
|
|
+ tmp = atmci_readl(host, ATMCI_MR);
|
|
|
+ tmp &= 0x0000ffff;
|
|
|
+ tmp |= ATMCI_BLKLEN(data->blksz);
|
|
|
+ atmci_writel(host, ATMCI_MR, tmp);
|
|
|
+
|
|
|
+ /* Configure PDC */
|
|
|
+ host->data_size = data->blocks * data->blksz;
|
|
|
+ sg_len = dma_map_sg(&host->pdev->dev, data->sg, data->sg_len, dir);
|
|
|
+ if (host->data_size)
|
|
|
+ atmci_pdc_set_both_buf(host,
|
|
|
+ ((dir == DMA_FROM_DEVICE) ? XFER_RECEIVE : XFER_TRANSMIT));
|
|
|
+
|
|
|
+ return iflags;
|
|
|
+}
|
|
|
+
|
|
|
+static u32
|
|
|
atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data)
|
|
|
{
|
|
|
struct dma_chan *chan;
|
|
@@ -654,6 +824,15 @@ atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data)
|
|
|
unsigned int i;
|
|
|
enum dma_data_direction direction;
|
|
|
unsigned int sglen;
|
|
|
+ u32 iflags;
|
|
|
+
|
|
|
+ data->error = -EINPROGRESS;
|
|
|
+
|
|
|
+ WARN_ON(host->data);
|
|
|
+ host->sg = NULL;
|
|
|
+ host->data = data;
|
|
|
+
|
|
|
+ iflags = ATMCI_DATA_ERROR_FLAGS;
|
|
|
|
|
|
/*
|
|
|
* We don't do DMA on "complex" transfers, i.e. with
|
|
@@ -661,13 +840,13 @@ atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data)
|
|
|
* with all the DMA setup overhead for short transfers.
|
|
|
*/
|
|
|
if (data->blocks * data->blksz < ATMCI_DMA_THRESHOLD)
|
|
|
- return -EINVAL;
|
|
|
+ return atmci_prepare_data(host, data);
|
|
|
if (data->blksz & 3)
|
|
|
- return -EINVAL;
|
|
|
+ return atmci_prepare_data(host, data);
|
|
|
|
|
|
for_each_sg(data->sg, sg, data->sg_len, i) {
|
|
|
if (sg->offset & 3 || sg->length & 3)
|
|
|
- return -EINVAL;
|
|
|
+ return atmci_prepare_data(host, data);
|
|
|
}
|
|
|
|
|
|
/* If we don't have a channel, we can't do DMA */
|
|
@@ -678,8 +857,8 @@ atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data)
|
|
|
if (!chan)
|
|
|
return -ENODEV;
|
|
|
|
|
|
- if (atmci_is_mci2())
|
|
|
- mci_writel(host, DMA, MCI_DMA_CHKSIZE(3) | MCI_DMAEN);
|
|
|
+ if (host->caps.has_dma)
|
|
|
+ atmci_writel(host, ATMCI_DMA, ATMCI_DMA_CHKSIZE(3) | ATMCI_DMAEN);
|
|
|
|
|
|
if (data->flags & MMC_DATA_READ)
|
|
|
direction = DMA_FROM_DEVICE;
|
|
@@ -687,7 +866,7 @@ atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data)
|
|
|
direction = DMA_TO_DEVICE;
|
|
|
|
|
|
sglen = dma_map_sg(chan->device->dev, data->sg,
|
|
|
- data->sg_len, direction);
|
|
|
+ data->sg_len, direction);
|
|
|
|
|
|
desc = chan->device->device_prep_slave_sg(chan,
|
|
|
data->sg, sglen, direction,
|
|
@@ -699,13 +878,32 @@ atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data)
|
|
|
desc->callback = atmci_dma_complete;
|
|
|
desc->callback_param = host;
|
|
|
|
|
|
- return 0;
|
|
|
+ return iflags;
|
|
|
unmap_exit:
|
|
|
dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, direction);
|
|
|
return -ENOMEM;
|
|
|
}
|
|
|
|
|
|
-static void atmci_submit_data(struct atmel_mci *host)
|
|
|
+static void
|
|
|
+atmci_submit_data(struct atmel_mci *host, struct mmc_data *data)
|
|
|
+{
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Start PDC according to transfer direction.
|
|
|
+ */
|
|
|
+static void
|
|
|
+atmci_submit_data_pdc(struct atmel_mci *host, struct mmc_data *data)
|
|
|
+{
|
|
|
+ if (data->flags & MMC_DATA_READ)
|
|
|
+ atmci_writel(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTEN);
|
|
|
+ else
|
|
|
+ atmci_writel(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN);
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data)
|
|
|
{
|
|
|
struct dma_chan *chan = host->data_chan;
|
|
|
struct dma_async_tx_descriptor *desc = host->dma.data_desc;
|
|
@@ -716,64 +914,39 @@ static void atmci_submit_data(struct atmel_mci *host)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-#else /* CONFIG_MMC_ATMELMCI_DMA */
|
|
|
-
|
|
|
-static int atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data)
|
|
|
-{
|
|
|
- return -ENOSYS;
|
|
|
-}
|
|
|
-
|
|
|
-static void atmci_submit_data(struct atmel_mci *host) {}
|
|
|
-
|
|
|
-static void atmci_stop_dma(struct atmel_mci *host)
|
|
|
+static void atmci_stop_transfer(struct atmel_mci *host)
|
|
|
{
|
|
|
- /* Data transfer was stopped by the interrupt handler */
|
|
|
atmci_set_pending(host, EVENT_XFER_COMPLETE);
|
|
|
- mci_writel(host, IER, MCI_NOTBUSY);
|
|
|
+ atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
|
|
|
}
|
|
|
|
|
|
-#endif /* CONFIG_MMC_ATMELMCI_DMA */
|
|
|
-
|
|
|
/*
|
|
|
- * Returns a mask of interrupt flags to be enabled after the whole
|
|
|
- * request has been prepared.
|
|
|
+ * Stop data transfer because error(s) occured.
|
|
|
*/
|
|
|
-static u32 atmci_prepare_data(struct atmel_mci *host, struct mmc_data *data)
|
|
|
+static void atmci_stop_transfer_pdc(struct atmel_mci *host)
|
|
|
{
|
|
|
- u32 iflags;
|
|
|
-
|
|
|
- data->error = -EINPROGRESS;
|
|
|
-
|
|
|
- WARN_ON(host->data);
|
|
|
- host->sg = NULL;
|
|
|
- host->data = data;
|
|
|
-
|
|
|
- iflags = ATMCI_DATA_ERROR_FLAGS;
|
|
|
- if (atmci_prepare_data_dma(host, data)) {
|
|
|
- host->data_chan = NULL;
|
|
|
+ atmci_set_pending(host, EVENT_XFER_COMPLETE);
|
|
|
+ atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
|
|
|
+}
|
|
|
|
|
|
- /*
|
|
|
- * Errata: MMC data write operation with less than 12
|
|
|
- * bytes is impossible.
|
|
|
- *
|
|
|
- * Errata: MCI Transmit Data Register (TDR) FIFO
|
|
|
- * corruption when length is not multiple of 4.
|
|
|
- */
|
|
|
- if (data->blocks * data->blksz < 12
|
|
|
- || (data->blocks * data->blksz) & 3)
|
|
|
- host->need_reset = true;
|
|
|
+static void atmci_stop_transfer_dma(struct atmel_mci *host)
|
|
|
+{
|
|
|
+ struct dma_chan *chan = host->data_chan;
|
|
|
|
|
|
- host->sg = data->sg;
|
|
|
- host->pio_offset = 0;
|
|
|
- if (data->flags & MMC_DATA_READ)
|
|
|
- iflags |= MCI_RXRDY;
|
|
|
- else
|
|
|
- iflags |= MCI_TXRDY;
|
|
|
+ if (chan) {
|
|
|
+ dmaengine_terminate_all(chan);
|
|
|
+ atmci_dma_cleanup(host);
|
|
|
+ } else {
|
|
|
+ /* Data transfer was stopped by the interrupt handler */
|
|
|
+ atmci_set_pending(host, EVENT_XFER_COMPLETE);
|
|
|
+ atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
|
|
|
}
|
|
|
-
|
|
|
- return iflags;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Start a request: prepare data if needed, prepare the command and activate
|
|
|
+ * interrupts.
|
|
|
+ */
|
|
|
static void atmci_start_request(struct atmel_mci *host,
|
|
|
struct atmel_mci_slot *slot)
|
|
|
{
|
|
@@ -792,24 +965,24 @@ static void atmci_start_request(struct atmel_mci *host,
|
|
|
host->data_status = 0;
|
|
|
|
|
|
if (host->need_reset) {
|
|
|
- mci_writel(host, CR, MCI_CR_SWRST);
|
|
|
- mci_writel(host, CR, MCI_CR_MCIEN);
|
|
|
- mci_writel(host, MR, host->mode_reg);
|
|
|
- if (atmci_is_mci2())
|
|
|
- mci_writel(host, CFG, host->cfg_reg);
|
|
|
+ atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST);
|
|
|
+ atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIEN);
|
|
|
+ atmci_writel(host, ATMCI_MR, host->mode_reg);
|
|
|
+ if (host->caps.has_cfg_reg)
|
|
|
+ atmci_writel(host, ATMCI_CFG, host->cfg_reg);
|
|
|
host->need_reset = false;
|
|
|
}
|
|
|
- mci_writel(host, SDCR, slot->sdc_reg);
|
|
|
+ atmci_writel(host, ATMCI_SDCR, slot->sdc_reg);
|
|
|
|
|
|
- iflags = mci_readl(host, IMR);
|
|
|
- if (iflags & ~(MCI_SDIOIRQA | MCI_SDIOIRQB))
|
|
|
+ iflags = atmci_readl(host, ATMCI_IMR);
|
|
|
+ if (iflags & ~(ATMCI_SDIOIRQA | ATMCI_SDIOIRQB))
|
|
|
dev_warn(&slot->mmc->class_dev, "WARNING: IMR=0x%08x\n",
|
|
|
iflags);
|
|
|
|
|
|
if (unlikely(test_and_clear_bit(ATMCI_CARD_NEED_INIT, &slot->flags))) {
|
|
|
/* Send init sequence (74 clock cycles) */
|
|
|
- mci_writel(host, CMDR, MCI_CMDR_SPCMD_INIT);
|
|
|
- while (!(mci_readl(host, SR) & MCI_CMDRDY))
|
|
|
+ atmci_writel(host, ATMCI_CMDR, ATMCI_CMDR_SPCMD_INIT);
|
|
|
+ while (!(atmci_readl(host, ATMCI_SR) & ATMCI_CMDRDY))
|
|
|
cpu_relax();
|
|
|
}
|
|
|
iflags = 0;
|
|
@@ -818,31 +991,31 @@ static void atmci_start_request(struct atmel_mci *host,
|
|
|
atmci_set_timeout(host, slot, data);
|
|
|
|
|
|
/* Must set block count/size before sending command */
|
|
|
- mci_writel(host, BLKR, MCI_BCNT(data->blocks)
|
|
|
- | MCI_BLKLEN(data->blksz));
|
|
|
+ atmci_writel(host, ATMCI_BLKR, ATMCI_BCNT(data->blocks)
|
|
|
+ | ATMCI_BLKLEN(data->blksz));
|
|
|
dev_vdbg(&slot->mmc->class_dev, "BLKR=0x%08x\n",
|
|
|
- MCI_BCNT(data->blocks) | MCI_BLKLEN(data->blksz));
|
|
|
+ ATMCI_BCNT(data->blocks) | ATMCI_BLKLEN(data->blksz));
|
|
|
|
|
|
- iflags |= atmci_prepare_data(host, data);
|
|
|
+ iflags |= host->prepare_data(host, data);
|
|
|
}
|
|
|
|
|
|
- iflags |= MCI_CMDRDY;
|
|
|
+ iflags |= ATMCI_CMDRDY;
|
|
|
cmd = mrq->cmd;
|
|
|
cmdflags = atmci_prepare_command(slot->mmc, cmd);
|
|
|
- atmci_start_command(host, cmd, cmdflags);
|
|
|
+ atmci_send_command(host, cmd, cmdflags);
|
|
|
|
|
|
if (data)
|
|
|
- atmci_submit_data(host);
|
|
|
+ host->submit_data(host, data);
|
|
|
|
|
|
if (mrq->stop) {
|
|
|
host->stop_cmdr = atmci_prepare_command(slot->mmc, mrq->stop);
|
|
|
- host->stop_cmdr |= MCI_CMDR_STOP_XFER;
|
|
|
+ host->stop_cmdr |= ATMCI_CMDR_STOP_XFER;
|
|
|
if (!(data->flags & MMC_DATA_WRITE))
|
|
|
- host->stop_cmdr |= MCI_CMDR_TRDIR_READ;
|
|
|
+ host->stop_cmdr |= ATMCI_CMDR_TRDIR_READ;
|
|
|
if (data->flags & MMC_DATA_STREAM)
|
|
|
- host->stop_cmdr |= MCI_CMDR_STREAM;
|
|
|
+ host->stop_cmdr |= ATMCI_CMDR_STREAM;
|
|
|
else
|
|
|
- host->stop_cmdr |= MCI_CMDR_MULTI_BLOCK;
|
|
|
+ host->stop_cmdr |= ATMCI_CMDR_MULTI_BLOCK;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -851,7 +1024,7 @@ static void atmci_start_request(struct atmel_mci *host,
|
|
|
* conditions (e.g. command and data complete, but stop not
|
|
|
* prepared yet.)
|
|
|
*/
|
|
|
- mci_writel(host, IER, iflags);
|
|
|
+ atmci_writel(host, ATMCI_IER, iflags);
|
|
|
}
|
|
|
|
|
|
static void atmci_queue_request(struct atmel_mci *host,
|
|
@@ -909,13 +1082,13 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|
|
struct atmel_mci *host = slot->host;
|
|
|
unsigned int i;
|
|
|
|
|
|
- slot->sdc_reg &= ~MCI_SDCBUS_MASK;
|
|
|
+ slot->sdc_reg &= ~ATMCI_SDCBUS_MASK;
|
|
|
switch (ios->bus_width) {
|
|
|
case MMC_BUS_WIDTH_1:
|
|
|
- slot->sdc_reg |= MCI_SDCBUS_1BIT;
|
|
|
+ slot->sdc_reg |= ATMCI_SDCBUS_1BIT;
|
|
|
break;
|
|
|
case MMC_BUS_WIDTH_4:
|
|
|
- slot->sdc_reg |= MCI_SDCBUS_4BIT;
|
|
|
+ slot->sdc_reg |= ATMCI_SDCBUS_4BIT;
|
|
|
break;
|
|
|
}
|
|
|
|
|
@@ -926,10 +1099,10 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|
|
spin_lock_bh(&host->lock);
|
|
|
if (!host->mode_reg) {
|
|
|
clk_enable(host->mck);
|
|
|
- mci_writel(host, CR, MCI_CR_SWRST);
|
|
|
- mci_writel(host, CR, MCI_CR_MCIEN);
|
|
|
- if (atmci_is_mci2())
|
|
|
- mci_writel(host, CFG, host->cfg_reg);
|
|
|
+ atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST);
|
|
|
+ atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIEN);
|
|
|
+ if (host->caps.has_cfg_reg)
|
|
|
+ atmci_writel(host, ATMCI_CFG, host->cfg_reg);
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -937,7 +1110,7 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|
|
* core ios update when finding the minimum.
|
|
|
*/
|
|
|
slot->clock = ios->clock;
|
|
|
- for (i = 0; i < ATMEL_MCI_MAX_NR_SLOTS; i++) {
|
|
|
+ for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) {
|
|
|
if (host->slot[i] && host->slot[i]->clock
|
|
|
&& host->slot[i]->clock < clock_min)
|
|
|
clock_min = host->slot[i]->clock;
|
|
@@ -952,28 +1125,28 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|
|
clkdiv = 255;
|
|
|
}
|
|
|
|
|
|
- host->mode_reg = MCI_MR_CLKDIV(clkdiv);
|
|
|
+ host->mode_reg = ATMCI_MR_CLKDIV(clkdiv);
|
|
|
|
|
|
/*
|
|
|
* WRPROOF and RDPROOF prevent overruns/underruns by
|
|
|
* stopping the clock when the FIFO is full/empty.
|
|
|
* This state is not expected to last for long.
|
|
|
*/
|
|
|
- if (mci_has_rwproof())
|
|
|
- host->mode_reg |= (MCI_MR_WRPROOF | MCI_MR_RDPROOF);
|
|
|
+ if (host->caps.has_rwproof)
|
|
|
+ host->mode_reg |= (ATMCI_MR_WRPROOF | ATMCI_MR_RDPROOF);
|
|
|
|
|
|
- if (atmci_is_mci2()) {
|
|
|
+ if (host->caps.has_cfg_reg) {
|
|
|
/* setup High Speed mode in relation with card capacity */
|
|
|
if (ios->timing == MMC_TIMING_SD_HS)
|
|
|
- host->cfg_reg |= MCI_CFG_HSMODE;
|
|
|
+ host->cfg_reg |= ATMCI_CFG_HSMODE;
|
|
|
else
|
|
|
- host->cfg_reg &= ~MCI_CFG_HSMODE;
|
|
|
+ host->cfg_reg &= ~ATMCI_CFG_HSMODE;
|
|
|
}
|
|
|
|
|
|
if (list_empty(&host->queue)) {
|
|
|
- mci_writel(host, MR, host->mode_reg);
|
|
|
- if (atmci_is_mci2())
|
|
|
- mci_writel(host, CFG, host->cfg_reg);
|
|
|
+ atmci_writel(host, ATMCI_MR, host->mode_reg);
|
|
|
+ if (host->caps.has_cfg_reg)
|
|
|
+ atmci_writel(host, ATMCI_CFG, host->cfg_reg);
|
|
|
} else {
|
|
|
host->need_clock_update = true;
|
|
|
}
|
|
@@ -984,16 +1157,16 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|
|
|
|
|
spin_lock_bh(&host->lock);
|
|
|
slot->clock = 0;
|
|
|
- for (i = 0; i < ATMEL_MCI_MAX_NR_SLOTS; i++) {
|
|
|
+ for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) {
|
|
|
if (host->slot[i] && host->slot[i]->clock) {
|
|
|
any_slot_active = true;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
if (!any_slot_active) {
|
|
|
- mci_writel(host, CR, MCI_CR_MCIDIS);
|
|
|
+ atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIDIS);
|
|
|
if (host->mode_reg) {
|
|
|
- mci_readl(host, MR);
|
|
|
+ atmci_readl(host, ATMCI_MR);
|
|
|
clk_disable(host->mck);
|
|
|
}
|
|
|
host->mode_reg = 0;
|
|
@@ -1057,9 +1230,9 @@ static void atmci_enable_sdio_irq(struct mmc_host *mmc, int enable)
|
|
|
struct atmel_mci *host = slot->host;
|
|
|
|
|
|
if (enable)
|
|
|
- mci_writel(host, IER, slot->sdio_irq);
|
|
|
+ atmci_writel(host, ATMCI_IER, slot->sdio_irq);
|
|
|
else
|
|
|
- mci_writel(host, IDR, slot->sdio_irq);
|
|
|
+ atmci_writel(host, ATMCI_IDR, slot->sdio_irq);
|
|
|
}
|
|
|
|
|
|
static const struct mmc_host_ops atmci_ops = {
|
|
@@ -1086,9 +1259,9 @@ static void atmci_request_end(struct atmel_mci *host, struct mmc_request *mrq)
|
|
|
* busy transferring data.
|
|
|
*/
|
|
|
if (host->need_clock_update) {
|
|
|
- mci_writel(host, MR, host->mode_reg);
|
|
|
- if (atmci_is_mci2())
|
|
|
- mci_writel(host, CFG, host->cfg_reg);
|
|
|
+ atmci_writel(host, ATMCI_MR, host->mode_reg);
|
|
|
+ if (host->caps.has_cfg_reg)
|
|
|
+ atmci_writel(host, ATMCI_CFG, host->cfg_reg);
|
|
|
}
|
|
|
|
|
|
host->cur_slot->mrq = NULL;
|
|
@@ -1117,16 +1290,16 @@ static void atmci_command_complete(struct atmel_mci *host,
|
|
|
u32 status = host->cmd_status;
|
|
|
|
|
|
/* Read the response from the card (up to 16 bytes) */
|
|
|
- cmd->resp[0] = mci_readl(host, RSPR);
|
|
|
- cmd->resp[1] = mci_readl(host, RSPR);
|
|
|
- cmd->resp[2] = mci_readl(host, RSPR);
|
|
|
- cmd->resp[3] = mci_readl(host, RSPR);
|
|
|
+ cmd->resp[0] = atmci_readl(host, ATMCI_RSPR);
|
|
|
+ cmd->resp[1] = atmci_readl(host, ATMCI_RSPR);
|
|
|
+ cmd->resp[2] = atmci_readl(host, ATMCI_RSPR);
|
|
|
+ cmd->resp[3] = atmci_readl(host, ATMCI_RSPR);
|
|
|
|
|
|
- if (status & MCI_RTOE)
|
|
|
+ if (status & ATMCI_RTOE)
|
|
|
cmd->error = -ETIMEDOUT;
|
|
|
- else if ((cmd->flags & MMC_RSP_CRC) && (status & MCI_RCRCE))
|
|
|
+ else if ((cmd->flags & MMC_RSP_CRC) && (status & ATMCI_RCRCE))
|
|
|
cmd->error = -EILSEQ;
|
|
|
- else if (status & (MCI_RINDE | MCI_RDIRE | MCI_RENDE))
|
|
|
+ else if (status & (ATMCI_RINDE | ATMCI_RDIRE | ATMCI_RENDE))
|
|
|
cmd->error = -EIO;
|
|
|
else
|
|
|
cmd->error = 0;
|
|
@@ -1136,10 +1309,10 @@ static void atmci_command_complete(struct atmel_mci *host,
|
|
|
"command error: status=0x%08x\n", status);
|
|
|
|
|
|
if (cmd->data) {
|
|
|
- atmci_stop_dma(host);
|
|
|
+ host->stop_transfer(host);
|
|
|
host->data = NULL;
|
|
|
- mci_writel(host, IDR, MCI_NOTBUSY
|
|
|
- | MCI_TXRDY | MCI_RXRDY
|
|
|
+ atmci_writel(host, ATMCI_IDR, ATMCI_NOTBUSY
|
|
|
+ | ATMCI_TXRDY | ATMCI_RXRDY
|
|
|
| ATMCI_DATA_ERROR_FLAGS);
|
|
|
}
|
|
|
}
|
|
@@ -1191,11 +1364,11 @@ static void atmci_detect_change(unsigned long data)
|
|
|
* Reset controller to terminate any ongoing
|
|
|
* commands or data transfers.
|
|
|
*/
|
|
|
- mci_writel(host, CR, MCI_CR_SWRST);
|
|
|
- mci_writel(host, CR, MCI_CR_MCIEN);
|
|
|
- mci_writel(host, MR, host->mode_reg);
|
|
|
- if (atmci_is_mci2())
|
|
|
- mci_writel(host, CFG, host->cfg_reg);
|
|
|
+ atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST);
|
|
|
+ atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIEN);
|
|
|
+ atmci_writel(host, ATMCI_MR, host->mode_reg);
|
|
|
+ if (host->caps.has_cfg_reg)
|
|
|
+ atmci_writel(host, ATMCI_CFG, host->cfg_reg);
|
|
|
|
|
|
host->data = NULL;
|
|
|
host->cmd = NULL;
|
|
@@ -1210,7 +1383,7 @@ static void atmci_detect_change(unsigned long data)
|
|
|
/* fall through */
|
|
|
case STATE_SENDING_DATA:
|
|
|
mrq->data->error = -ENOMEDIUM;
|
|
|
- atmci_stop_dma(host);
|
|
|
+ host->stop_transfer(host);
|
|
|
break;
|
|
|
case STATE_DATA_BUSY:
|
|
|
case STATE_DATA_ERROR:
|
|
@@ -1261,7 +1434,7 @@ static void atmci_tasklet_func(unsigned long priv)
|
|
|
dev_vdbg(&host->pdev->dev,
|
|
|
"tasklet: state %u pending/completed/mask %lx/%lx/%x\n",
|
|
|
state, host->pending_events, host->completed_events,
|
|
|
- mci_readl(host, IMR));
|
|
|
+ atmci_readl(host, ATMCI_IMR));
|
|
|
|
|
|
do {
|
|
|
prev_state = state;
|
|
@@ -1289,9 +1462,9 @@ static void atmci_tasklet_func(unsigned long priv)
|
|
|
case STATE_SENDING_DATA:
|
|
|
if (atmci_test_and_clear_pending(host,
|
|
|
EVENT_DATA_ERROR)) {
|
|
|
- atmci_stop_dma(host);
|
|
|
+ host->stop_transfer(host);
|
|
|
if (data->stop)
|
|
|
- send_stop_cmd(host, data);
|
|
|
+ atmci_send_stop_cmd(host, data);
|
|
|
state = STATE_DATA_ERROR;
|
|
|
break;
|
|
|
}
|
|
@@ -1313,11 +1486,11 @@ static void atmci_tasklet_func(unsigned long priv)
|
|
|
atmci_set_completed(host, EVENT_DATA_COMPLETE);
|
|
|
status = host->data_status;
|
|
|
if (unlikely(status & ATMCI_DATA_ERROR_FLAGS)) {
|
|
|
- if (status & MCI_DTOE) {
|
|
|
+ if (status & ATMCI_DTOE) {
|
|
|
dev_dbg(&host->pdev->dev,
|
|
|
"data timeout error\n");
|
|
|
data->error = -ETIMEDOUT;
|
|
|
- } else if (status & MCI_DCRCE) {
|
|
|
+ } else if (status & ATMCI_DCRCE) {
|
|
|
dev_dbg(&host->pdev->dev,
|
|
|
"data CRC error\n");
|
|
|
data->error = -EILSEQ;
|
|
@@ -1330,7 +1503,7 @@ static void atmci_tasklet_func(unsigned long priv)
|
|
|
} else {
|
|
|
data->bytes_xfered = data->blocks * data->blksz;
|
|
|
data->error = 0;
|
|
|
- mci_writel(host, IDR, ATMCI_DATA_ERROR_FLAGS);
|
|
|
+ atmci_writel(host, ATMCI_IDR, ATMCI_DATA_ERROR_FLAGS);
|
|
|
}
|
|
|
|
|
|
if (!data->stop) {
|
|
@@ -1340,7 +1513,7 @@ static void atmci_tasklet_func(unsigned long priv)
|
|
|
|
|
|
prev_state = state = STATE_SENDING_STOP;
|
|
|
if (!data->error)
|
|
|
- send_stop_cmd(host, data);
|
|
|
+ atmci_send_stop_cmd(host, data);
|
|
|
/* fall through */
|
|
|
|
|
|
case STATE_SENDING_STOP:
|
|
@@ -1380,7 +1553,7 @@ static void atmci_read_data_pio(struct atmel_mci *host)
|
|
|
unsigned int nbytes = 0;
|
|
|
|
|
|
do {
|
|
|
- value = mci_readl(host, RDR);
|
|
|
+ value = atmci_readl(host, ATMCI_RDR);
|
|
|
if (likely(offset + 4 <= sg->length)) {
|
|
|
put_unaligned(value, (u32 *)(buf + offset));
|
|
|
|
|
@@ -1412,9 +1585,9 @@ static void atmci_read_data_pio(struct atmel_mci *host)
|
|
|
nbytes += offset;
|
|
|
}
|
|
|
|
|
|
- status = mci_readl(host, SR);
|
|
|
+ status = atmci_readl(host, ATMCI_SR);
|
|
|
if (status & ATMCI_DATA_ERROR_FLAGS) {
|
|
|
- mci_writel(host, IDR, (MCI_NOTBUSY | MCI_RXRDY
|
|
|
+ atmci_writel(host, ATMCI_IDR, (ATMCI_NOTBUSY | ATMCI_RXRDY
|
|
|
| ATMCI_DATA_ERROR_FLAGS));
|
|
|
host->data_status = status;
|
|
|
data->bytes_xfered += nbytes;
|
|
@@ -1423,7 +1596,7 @@ static void atmci_read_data_pio(struct atmel_mci *host)
|
|
|
tasklet_schedule(&host->tasklet);
|
|
|
return;
|
|
|
}
|
|
|
- } while (status & MCI_RXRDY);
|
|
|
+ } while (status & ATMCI_RXRDY);
|
|
|
|
|
|
host->pio_offset = offset;
|
|
|
data->bytes_xfered += nbytes;
|
|
@@ -1431,8 +1604,8 @@ static void atmci_read_data_pio(struct atmel_mci *host)
|
|
|
return;
|
|
|
|
|
|
done:
|
|
|
- mci_writel(host, IDR, MCI_RXRDY);
|
|
|
- mci_writel(host, IER, MCI_NOTBUSY);
|
|
|
+ atmci_writel(host, ATMCI_IDR, ATMCI_RXRDY);
|
|
|
+ atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
|
|
|
data->bytes_xfered += nbytes;
|
|
|
smp_wmb();
|
|
|
atmci_set_pending(host, EVENT_XFER_COMPLETE);
|
|
@@ -1451,7 +1624,7 @@ static void atmci_write_data_pio(struct atmel_mci *host)
|
|
|
do {
|
|
|
if (likely(offset + 4 <= sg->length)) {
|
|
|
value = get_unaligned((u32 *)(buf + offset));
|
|
|
- mci_writel(host, TDR, value);
|
|
|
+ atmci_writel(host, ATMCI_TDR, value);
|
|
|
|
|
|
offset += 4;
|
|
|
nbytes += 4;
|
|
@@ -1472,20 +1645,20 @@ static void atmci_write_data_pio(struct atmel_mci *host)
|
|
|
|
|
|
host->sg = sg = sg_next(sg);
|
|
|
if (!sg) {
|
|
|
- mci_writel(host, TDR, value);
|
|
|
+ atmci_writel(host, ATMCI_TDR, value);
|
|
|
goto done;
|
|
|
}
|
|
|
|
|
|
offset = 4 - remaining;
|
|
|
buf = sg_virt(sg);
|
|
|
memcpy((u8 *)&value + remaining, buf, offset);
|
|
|
- mci_writel(host, TDR, value);
|
|
|
+ atmci_writel(host, ATMCI_TDR, value);
|
|
|
nbytes += offset;
|
|
|
}
|
|
|
|
|
|
- status = mci_readl(host, SR);
|
|
|
+ status = atmci_readl(host, ATMCI_SR);
|
|
|
if (status & ATMCI_DATA_ERROR_FLAGS) {
|
|
|
- mci_writel(host, IDR, (MCI_NOTBUSY | MCI_TXRDY
|
|
|
+ atmci_writel(host, ATMCI_IDR, (ATMCI_NOTBUSY | ATMCI_TXRDY
|
|
|
| ATMCI_DATA_ERROR_FLAGS));
|
|
|
host->data_status = status;
|
|
|
data->bytes_xfered += nbytes;
|
|
@@ -1494,7 +1667,7 @@ static void atmci_write_data_pio(struct atmel_mci *host)
|
|
|
tasklet_schedule(&host->tasklet);
|
|
|
return;
|
|
|
}
|
|
|
- } while (status & MCI_TXRDY);
|
|
|
+ } while (status & ATMCI_TXRDY);
|
|
|
|
|
|
host->pio_offset = offset;
|
|
|
data->bytes_xfered += nbytes;
|
|
@@ -1502,8 +1675,8 @@ static void atmci_write_data_pio(struct atmel_mci *host)
|
|
|
return;
|
|
|
|
|
|
done:
|
|
|
- mci_writel(host, IDR, MCI_TXRDY);
|
|
|
- mci_writel(host, IER, MCI_NOTBUSY);
|
|
|
+ atmci_writel(host, ATMCI_IDR, ATMCI_TXRDY);
|
|
|
+ atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
|
|
|
data->bytes_xfered += nbytes;
|
|
|
smp_wmb();
|
|
|
atmci_set_pending(host, EVENT_XFER_COMPLETE);
|
|
@@ -1511,7 +1684,7 @@ done:
|
|
|
|
|
|
static void atmci_cmd_interrupt(struct atmel_mci *host, u32 status)
|
|
|
{
|
|
|
- mci_writel(host, IDR, MCI_CMDRDY);
|
|
|
+ atmci_writel(host, ATMCI_IDR, ATMCI_CMDRDY);
|
|
|
|
|
|
host->cmd_status = status;
|
|
|
smp_wmb();
|
|
@@ -1523,7 +1696,7 @@ static void atmci_sdio_interrupt(struct atmel_mci *host, u32 status)
|
|
|
{
|
|
|
int i;
|
|
|
|
|
|
- for (i = 0; i < ATMEL_MCI_MAX_NR_SLOTS; i++) {
|
|
|
+ for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) {
|
|
|
struct atmel_mci_slot *slot = host->slot[i];
|
|
|
if (slot && (status & slot->sdio_irq)) {
|
|
|
mmc_signal_sdio_irq(slot->mmc);
|
|
@@ -1539,40 +1712,92 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
|
|
|
unsigned int pass_count = 0;
|
|
|
|
|
|
do {
|
|
|
- status = mci_readl(host, SR);
|
|
|
- mask = mci_readl(host, IMR);
|
|
|
+ status = atmci_readl(host, ATMCI_SR);
|
|
|
+ mask = atmci_readl(host, ATMCI_IMR);
|
|
|
pending = status & mask;
|
|
|
if (!pending)
|
|
|
break;
|
|
|
|
|
|
if (pending & ATMCI_DATA_ERROR_FLAGS) {
|
|
|
- mci_writel(host, IDR, ATMCI_DATA_ERROR_FLAGS
|
|
|
- | MCI_RXRDY | MCI_TXRDY);
|
|
|
- pending &= mci_readl(host, IMR);
|
|
|
+ atmci_writel(host, ATMCI_IDR, ATMCI_DATA_ERROR_FLAGS
|
|
|
+ | ATMCI_RXRDY | ATMCI_TXRDY);
|
|
|
+ pending &= atmci_readl(host, ATMCI_IMR);
|
|
|
|
|
|
host->data_status = status;
|
|
|
smp_wmb();
|
|
|
atmci_set_pending(host, EVENT_DATA_ERROR);
|
|
|
tasklet_schedule(&host->tasklet);
|
|
|
}
|
|
|
- if (pending & MCI_NOTBUSY) {
|
|
|
- mci_writel(host, IDR,
|
|
|
- ATMCI_DATA_ERROR_FLAGS | MCI_NOTBUSY);
|
|
|
+
|
|
|
+ if (pending & ATMCI_TXBUFE) {
|
|
|
+ atmci_writel(host, ATMCI_IDR, ATMCI_TXBUFE);
|
|
|
+ atmci_writel(host, ATMCI_IDR, ATMCI_ENDTX);
|
|
|
+ /*
|
|
|
+ * We can receive this interruption before having configured
|
|
|
+ * the second pdc buffer, so we need to reconfigure first and
|
|
|
+ * second buffers again
|
|
|
+ */
|
|
|
+ if (host->data_size) {
|
|
|
+ atmci_pdc_set_both_buf(host, XFER_TRANSMIT);
|
|
|
+ atmci_writel(host, ATMCI_IER, ATMCI_ENDTX);
|
|
|
+ atmci_writel(host, ATMCI_IER, ATMCI_TXBUFE);
|
|
|
+ } else {
|
|
|
+ atmci_pdc_complete(host);
|
|
|
+ }
|
|
|
+ } else if (pending & ATMCI_ENDTX) {
|
|
|
+ atmci_writel(host, ATMCI_IDR, ATMCI_ENDTX);
|
|
|
+
|
|
|
+ if (host->data_size) {
|
|
|
+ atmci_pdc_set_single_buf(host,
|
|
|
+ XFER_TRANSMIT, PDC_SECOND_BUF);
|
|
|
+ atmci_writel(host, ATMCI_IER, ATMCI_ENDTX);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (pending & ATMCI_RXBUFF) {
|
|
|
+ atmci_writel(host, ATMCI_IDR, ATMCI_RXBUFF);
|
|
|
+ atmci_writel(host, ATMCI_IDR, ATMCI_ENDRX);
|
|
|
+ /*
|
|
|
+ * We can receive this interruption before having configured
|
|
|
+ * the second pdc buffer, so we need to reconfigure first and
|
|
|
+ * second buffers again
|
|
|
+ */
|
|
|
+ if (host->data_size) {
|
|
|
+ atmci_pdc_set_both_buf(host, XFER_RECEIVE);
|
|
|
+ atmci_writel(host, ATMCI_IER, ATMCI_ENDRX);
|
|
|
+ atmci_writel(host, ATMCI_IER, ATMCI_RXBUFF);
|
|
|
+ } else {
|
|
|
+ atmci_pdc_complete(host);
|
|
|
+ }
|
|
|
+ } else if (pending & ATMCI_ENDRX) {
|
|
|
+ atmci_writel(host, ATMCI_IDR, ATMCI_ENDRX);
|
|
|
+
|
|
|
+ if (host->data_size) {
|
|
|
+ atmci_pdc_set_single_buf(host,
|
|
|
+ XFER_RECEIVE, PDC_SECOND_BUF);
|
|
|
+ atmci_writel(host, ATMCI_IER, ATMCI_ENDRX);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ if (pending & ATMCI_NOTBUSY) {
|
|
|
+ atmci_writel(host, ATMCI_IDR,
|
|
|
+ ATMCI_DATA_ERROR_FLAGS | ATMCI_NOTBUSY);
|
|
|
if (!host->data_status)
|
|
|
host->data_status = status;
|
|
|
smp_wmb();
|
|
|
atmci_set_pending(host, EVENT_DATA_COMPLETE);
|
|
|
tasklet_schedule(&host->tasklet);
|
|
|
}
|
|
|
- if (pending & MCI_RXRDY)
|
|
|
+ if (pending & ATMCI_RXRDY)
|
|
|
atmci_read_data_pio(host);
|
|
|
- if (pending & MCI_TXRDY)
|
|
|
+ if (pending & ATMCI_TXRDY)
|
|
|
atmci_write_data_pio(host);
|
|
|
|
|
|
- if (pending & MCI_CMDRDY)
|
|
|
+ if (pending & ATMCI_CMDRDY)
|
|
|
atmci_cmd_interrupt(host, status);
|
|
|
|
|
|
- if (pending & (MCI_SDIOIRQA | MCI_SDIOIRQB))
|
|
|
+ if (pending & (ATMCI_SDIOIRQA | ATMCI_SDIOIRQB))
|
|
|
atmci_sdio_interrupt(host, status);
|
|
|
|
|
|
} while (pass_count++ < 5);
|
|
@@ -1621,7 +1846,7 @@ static int __init atmci_init_slot(struct atmel_mci *host,
|
|
|
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
|
|
|
if (sdio_irq)
|
|
|
mmc->caps |= MMC_CAP_SDIO_IRQ;
|
|
|
- if (atmci_is_mci2())
|
|
|
+ if (host->caps.has_highspeed)
|
|
|
mmc->caps |= MMC_CAP_SD_HIGHSPEED;
|
|
|
if (slot_data->bus_width >= 4)
|
|
|
mmc->caps |= MMC_CAP_4_BIT_DATA;
|
|
@@ -1704,8 +1929,7 @@ static void __exit atmci_cleanup_slot(struct atmel_mci_slot *slot,
|
|
|
mmc_free_host(slot->mmc);
|
|
|
}
|
|
|
|
|
|
-#ifdef CONFIG_MMC_ATMELMCI_DMA
|
|
|
-static bool filter(struct dma_chan *chan, void *slave)
|
|
|
+static bool atmci_filter(struct dma_chan *chan, void *slave)
|
|
|
{
|
|
|
struct mci_dma_data *sl = slave;
|
|
|
|
|
@@ -1730,14 +1954,14 @@ static void atmci_configure_dma(struct atmel_mci *host)
|
|
|
dma_cap_mask_t mask;
|
|
|
|
|
|
setup_dma_addr(pdata->dma_slave,
|
|
|
- host->mapbase + MCI_TDR,
|
|
|
- host->mapbase + MCI_RDR);
|
|
|
+ host->mapbase + ATMCI_TDR,
|
|
|
+ host->mapbase + ATMCI_RDR);
|
|
|
|
|
|
/* Try to grab a DMA channel */
|
|
|
dma_cap_zero(mask);
|
|
|
dma_cap_set(DMA_SLAVE, mask);
|
|
|
host->dma.chan =
|
|
|
- dma_request_channel(mask, filter, pdata->dma_slave);
|
|
|
+ dma_request_channel(mask, atmci_filter, pdata->dma_slave);
|
|
|
}
|
|
|
if (!host->dma.chan)
|
|
|
dev_notice(&host->pdev->dev, "DMA not available, using PIO\n");
|
|
@@ -1746,9 +1970,60 @@ static void atmci_configure_dma(struct atmel_mci *host)
|
|
|
"Using %s for DMA transfers\n",
|
|
|
dma_chan_name(host->dma.chan));
|
|
|
}
|
|
|
+
|
|
|
+static inline unsigned int atmci_get_version(struct atmel_mci *host)
|
|
|
+{
|
|
|
+ return atmci_readl(host, ATMCI_VERSION) & 0x00000fff;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * HSMCI (High Speed MCI) module is not fully compatible with MCI module.
|
|
|
+ * HSMCI provides DMA support and a new config register but no more supports
|
|
|
+ * PDC.
|
|
|
+ */
|
|
|
+static void __init atmci_get_cap(struct atmel_mci *host)
|
|
|
+{
|
|
|
+ unsigned int version;
|
|
|
+
|
|
|
+ version = atmci_get_version(host);
|
|
|
+ dev_info(&host->pdev->dev,
|
|
|
+ "version: 0x%x\n", version);
|
|
|
+
|
|
|
+ host->caps.has_dma = 0;
|
|
|
+ host->caps.has_pdc = 0;
|
|
|
+ host->caps.has_cfg_reg = 0;
|
|
|
+ host->caps.has_cstor_reg = 0;
|
|
|
+ host->caps.has_highspeed = 0;
|
|
|
+ host->caps.has_rwproof = 0;
|
|
|
+
|
|
|
+ /* keep only major version number */
|
|
|
+ switch (version & 0xf00) {
|
|
|
+ case 0x100:
|
|
|
+ case 0x200:
|
|
|
+ host->caps.has_pdc = 1;
|
|
|
+ host->caps.has_rwproof = 1;
|
|
|
+ break;
|
|
|
+ case 0x300:
|
|
|
+ case 0x400:
|
|
|
+ case 0x500:
|
|
|
+#ifdef CONFIG_AT_HDMAC
|
|
|
+ host->caps.has_dma = 1;
|
|
|
#else
|
|
|
-static void atmci_configure_dma(struct atmel_mci *host) {}
|
|
|
+ host->caps.has_dma = 0;
|
|
|
+ dev_info(&host->pdev->dev,
|
|
|
+ "has dma capability but dma engine is not selected, then use pio\n");
|
|
|
#endif
|
|
|
+ host->caps.has_cfg_reg = 1;
|
|
|
+ host->caps.has_cstor_reg = 1;
|
|
|
+ host->caps.has_highspeed = 1;
|
|
|
+ host->caps.has_rwproof = 1;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ dev_warn(&host->pdev->dev,
|
|
|
+ "Unmanaged mci version, set minimum capabilities\n");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
static int __init atmci_probe(struct platform_device *pdev)
|
|
|
{
|
|
@@ -1789,7 +2064,7 @@ static int __init atmci_probe(struct platform_device *pdev)
|
|
|
goto err_ioremap;
|
|
|
|
|
|
clk_enable(host->mck);
|
|
|
- mci_writel(host, CR, MCI_CR_SWRST);
|
|
|
+ atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST);
|
|
|
host->bus_hz = clk_get_rate(host->mck);
|
|
|
clk_disable(host->mck);
|
|
|
|
|
@@ -1801,7 +2076,27 @@ static int __init atmci_probe(struct platform_device *pdev)
|
|
|
if (ret)
|
|
|
goto err_request_irq;
|
|
|
|
|
|
- atmci_configure_dma(host);
|
|
|
+ /* Get MCI capabilities and set operations according to it */
|
|
|
+ atmci_get_cap(host);
|
|
|
+ if (host->caps.has_dma) {
|
|
|
+ dev_info(&pdev->dev, "using DMA\n");
|
|
|
+ host->prepare_data = &atmci_prepare_data_dma;
|
|
|
+ host->submit_data = &atmci_submit_data_dma;
|
|
|
+ host->stop_transfer = &atmci_stop_transfer_dma;
|
|
|
+ } else if (host->caps.has_pdc) {
|
|
|
+ dev_info(&pdev->dev, "using PDC\n");
|
|
|
+ host->prepare_data = &atmci_prepare_data_pdc;
|
|
|
+ host->submit_data = &atmci_submit_data_pdc;
|
|
|
+ host->stop_transfer = &atmci_stop_transfer_pdc;
|
|
|
+ } else {
|
|
|
+ dev_info(&pdev->dev, "no DMA, no PDC\n");
|
|
|
+ host->prepare_data = &atmci_prepare_data;
|
|
|
+ host->submit_data = &atmci_submit_data;
|
|
|
+ host->stop_transfer = &atmci_stop_transfer;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (host->caps.has_dma)
|
|
|
+ atmci_configure_dma(host);
|
|
|
|
|
|
platform_set_drvdata(pdev, host);
|
|
|
|
|
@@ -1810,13 +2105,13 @@ static int __init atmci_probe(struct platform_device *pdev)
|
|
|
ret = -ENODEV;
|
|
|
if (pdata->slot[0].bus_width) {
|
|
|
ret = atmci_init_slot(host, &pdata->slot[0],
|
|
|
- 0, MCI_SDCSEL_SLOT_A, MCI_SDIOIRQA);
|
|
|
+ 0, ATMCI_SDCSEL_SLOT_A, ATMCI_SDIOIRQA);
|
|
|
if (!ret)
|
|
|
nr_slots++;
|
|
|
}
|
|
|
if (pdata->slot[1].bus_width) {
|
|
|
ret = atmci_init_slot(host, &pdata->slot[1],
|
|
|
- 1, MCI_SDCSEL_SLOT_B, MCI_SDIOIRQB);
|
|
|
+ 1, ATMCI_SDCSEL_SLOT_B, ATMCI_SDIOIRQB);
|
|
|
if (!ret)
|
|
|
nr_slots++;
|
|
|
}
|
|
@@ -1833,10 +2128,8 @@ static int __init atmci_probe(struct platform_device *pdev)
|
|
|
return 0;
|
|
|
|
|
|
err_init_slot:
|
|
|
-#ifdef CONFIG_MMC_ATMELMCI_DMA
|
|
|
if (host->dma.chan)
|
|
|
dma_release_channel(host->dma.chan);
|
|
|
-#endif
|
|
|
free_irq(irq, host);
|
|
|
err_request_irq:
|
|
|
iounmap(host->regs);
|
|
@@ -1854,15 +2147,15 @@ static int __exit atmci_remove(struct platform_device *pdev)
|
|
|
|
|
|
platform_set_drvdata(pdev, NULL);
|
|
|
|
|
|
- for (i = 0; i < ATMEL_MCI_MAX_NR_SLOTS; i++) {
|
|
|
+ for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) {
|
|
|
if (host->slot[i])
|
|
|
atmci_cleanup_slot(host->slot[i], i);
|
|
|
}
|
|
|
|
|
|
clk_enable(host->mck);
|
|
|
- mci_writel(host, IDR, ~0UL);
|
|
|
- mci_writel(host, CR, MCI_CR_MCIDIS);
|
|
|
- mci_readl(host, SR);
|
|
|
+ atmci_writel(host, ATMCI_IDR, ~0UL);
|
|
|
+ atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIDIS);
|
|
|
+ atmci_readl(host, ATMCI_SR);
|
|
|
clk_disable(host->mck);
|
|
|
|
|
|
#ifdef CONFIG_MMC_ATMELMCI_DMA
|
|
@@ -1885,7 +2178,7 @@ static int atmci_suspend(struct device *dev)
|
|
|
struct atmel_mci *host = dev_get_drvdata(dev);
|
|
|
int i;
|
|
|
|
|
|
- for (i = 0; i < ATMEL_MCI_MAX_NR_SLOTS; i++) {
|
|
|
+ for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) {
|
|
|
struct atmel_mci_slot *slot = host->slot[i];
|
|
|
int ret;
|
|
|
|
|
@@ -1916,7 +2209,7 @@ static int atmci_resume(struct device *dev)
|
|
|
int i;
|
|
|
int ret = 0;
|
|
|
|
|
|
- for (i = 0; i < ATMEL_MCI_MAX_NR_SLOTS; i++) {
|
|
|
+ for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) {
|
|
|
struct atmel_mci_slot *slot = host->slot[i];
|
|
|
int err;
|
|
|
|