summaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/host')
-rw-r--r--drivers/mmc/host/Kconfig17
-rw-r--r--drivers/mmc/host/Makefile1
-rw-r--r--drivers/mmc/host/atmel-mci.c469
-rw-r--r--drivers/mmc/host/davinci_mmc.c1
-rw-r--r--drivers/mmc/host/dw_mmc.c18
-rw-r--r--drivers/mmc/host/imxmmc.c1169
-rw-r--r--drivers/mmc/host/imxmmc.h64
-rw-r--r--drivers/mmc/host/mmci.c65
-rw-r--r--drivers/mmc/host/mvsdio.c14
-rw-r--r--drivers/mmc/host/mxcmmc.c39
-rw-r--r--drivers/mmc/host/mxs-mmc.c197
-rw-r--r--drivers/mmc/host/omap.c48
-rw-r--r--drivers/mmc/host/omap_hsmmc.c86
-rw-r--r--drivers/mmc/host/sdhci-esdhc-imx.c44
-rw-r--r--drivers/mmc/host/sdhci-pltfm.c8
-rw-r--r--drivers/mmc/host/sdhci-spear.c82
-rw-r--r--drivers/mmc/host/sdhci-tegra.c26
-rw-r--r--drivers/mmc/host/sdhci.c4
18 files changed, 721 insertions, 1631 deletions
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 2bc06e7344db..aa131b32e3b2 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -278,10 +278,13 @@ choice
Choose which driver to use for the Atmel MCI Silicon
config MMC_AT91
- tristate "AT91 SD/MMC Card Interface support"
+ tristate "AT91 SD/MMC Card Interface support (DEPRECATED)"
depends on ARCH_AT91
help
- This selects the AT91 MCI controller.
+ This selects the AT91 MCI controller. This driver will
+ be removed soon (for more information have a look to
+ Documentation/feature-removal-schedule.txt). Please use
+ MMC_ATMEL_MCI.
If unsure, say N.
@@ -307,16 +310,6 @@ config MMC_ATMELMCI_DMA
If unsure, say N.
-config MMC_IMX
- tristate "Motorola i.MX Multimedia Card Interface support"
- depends on ARCH_MX1
- help
- This selects the Motorola i.MX Multimedia card Interface.
- If you have a i.MX platform with a Multimedia Card slot,
- say Y or M here.
-
- If unsure, say N.
-
config MMC_MSM
tristate "Qualcomm SDCC Controller Support"
depends on MMC && ARCH_MSM
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 3e7e26d08073..8922b06be925 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -4,7 +4,6 @@
obj-$(CONFIG_MMC_ARMMMCI) += mmci.o
obj-$(CONFIG_MMC_PXA) += pxamci.o
-obj-$(CONFIG_MMC_IMX) += imxmmc.o
obj-$(CONFIG_MMC_MXC) += mxcmmc.o
obj-$(CONFIG_MMC_MXS) += mxs-mmc.o
obj-$(CONFIG_MMC_SDHCI) += sdhci.o
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
index e94476beca18..420aca642b14 100644
--- a/drivers/mmc/host/atmel-mci.c
+++ b/drivers/mmc/host/atmel-mci.c
@@ -45,19 +45,19 @@
#define ATMCI_DMA_THRESHOLD 16
enum {
- EVENT_CMD_COMPLETE = 0,
+ EVENT_CMD_RDY = 0,
EVENT_XFER_COMPLETE,
- EVENT_DATA_COMPLETE,
+ EVENT_NOTBUSY,
EVENT_DATA_ERROR,
};
enum atmel_mci_state {
STATE_IDLE = 0,
STATE_SENDING_CMD,
- STATE_SENDING_DATA,
- STATE_DATA_BUSY,
+ STATE_DATA_XFER,
+ STATE_WAITING_NOTBUSY,
STATE_SENDING_STOP,
- STATE_DATA_ERROR,
+ STATE_END_REQUEST,
};
enum atmci_xfer_dir {
@@ -78,6 +78,9 @@ struct atmel_mci_caps {
bool has_highspeed;
bool has_rwproof;
bool has_odd_clk_div;
+ bool has_bad_data_ordering;
+ bool need_reset_after_xfer;
+ bool need_blksz_mul_4;
};
struct atmel_mci_dma {
@@ -91,6 +94,11 @@ struct atmel_mci_dma {
* @regs: Pointer to MMIO registers.
* @sg: Scatterlist entry currently being processed by PIO or PDC code.
* @pio_offset: Offset into the current scatterlist entry.
+ * @buffer: Buffer used if we don't have the r/w proof capability. We
+ * don't have the time to switch pdc buffers so we have to use only
+ * one buffer for the full transaction.
+ * @buf_size: size of the buffer.
+ * @phys_buf_addr: buffer address needed for pdc.
* @cur_slot: The slot which is currently using the controller.
* @mrq: The request currently being processed on @cur_slot,
* or NULL if the controller is idle.
@@ -116,6 +124,7 @@ struct atmel_mci_dma {
* @queue: List of slots waiting for access to the controller.
* @need_clock_update: Update the clock rate before the next request.
* @need_reset: Reset controller before next request.
+ * @timer: Timer to balance the data timeout error flag which cannot rise.
* @mode_reg: Value of the MR register.
* @cfg_reg: Value of the CFG register.
* @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus
@@ -166,6 +175,9 @@ struct atmel_mci {
struct scatterlist *sg;
unsigned int pio_offset;
+ unsigned int *buffer;
+ unsigned int buf_size;
+ dma_addr_t buf_phys_addr;
struct atmel_mci_slot *cur_slot;
struct mmc_request *mrq;
@@ -189,6 +201,7 @@ struct atmel_mci {
bool need_clock_update;
bool need_reset;
+ struct timer_list timer;
u32 mode_reg;
u32 cfg_reg;
unsigned long bus_hz;
@@ -480,6 +493,32 @@ err:
dev_err(&mmc->class_dev, "failed to initialize debugfs for slot\n");
}
+static inline unsigned int atmci_get_version(struct atmel_mci *host)
+{
+ return atmci_readl(host, ATMCI_VERSION) & 0x00000fff;
+}
+
+static void atmci_timeout_timer(unsigned long data)
+{
+ struct atmel_mci *host;
+
+ host = (struct atmel_mci *)data;
+
+ dev_dbg(&host->pdev->dev, "software timeout\n");
+
+ if (host->mrq->cmd->data) {
+ host->mrq->cmd->data->error = -ETIMEDOUT;
+ host->data = NULL;
+ } else {
+ host->mrq->cmd->error = -ETIMEDOUT;
+ host->cmd = NULL;
+ }
+ host->need_reset = 1;
+ host->state = STATE_END_REQUEST;
+ smp_wmb();
+ tasklet_schedule(&host->tasklet);
+}
+
static inline unsigned int atmci_ns_to_clocks(struct atmel_mci *host,
unsigned int ns)
{
@@ -591,6 +630,7 @@ static void atmci_send_command(struct atmel_mci *host,
static void atmci_send_stop_cmd(struct atmel_mci *host, struct mmc_data *data)
{
+ dev_dbg(&host->pdev->dev, "send stop command\n");
atmci_send_command(host, data->stop, host->stop_cmdr);
atmci_writel(host, ATMCI_IER, ATMCI_CMDRDY);
}
@@ -603,6 +643,7 @@ 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;
+ unsigned int buf_size;
if (dir == XFER_RECEIVE) {
pointer_reg = ATMEL_PDC_RPR;
@@ -617,8 +658,15 @@ static void atmci_pdc_set_single_buf(struct atmel_mci *host,
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->caps.has_rwproof) {
+ buf_size = host->buf_size;
+ atmci_writel(host, pointer_reg, host->buf_phys_addr);
+ } else {
+ buf_size = sg_dma_len(host->sg);
+ atmci_writel(host, pointer_reg, sg_dma_address(host->sg));
+ }
+
+ if (host->data_size <= buf_size) {
if (host->data_size & 0x3) {
/* If size is different from modulo 4, transfer bytes */
atmci_writel(host, counter_reg, host->data_size);
@@ -670,7 +718,20 @@ static void atmci_pdc_cleanup(struct atmel_mci *host)
*/
static void atmci_pdc_complete(struct atmel_mci *host)
{
+ int transfer_size = host->data->blocks * host->data->blksz;
+ int i;
+
atmci_writel(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
+
+ if ((!host->caps.has_rwproof)
+ && (host->data->flags & MMC_DATA_READ)) {
+ if (host->caps.has_bad_data_ordering)
+ for (i = 0; i < transfer_size; i++)
+ host->buffer[i] = swab32(host->buffer[i]);
+ sg_copy_from_buffer(host->data->sg, host->data->sg_len,
+ host->buffer, transfer_size);
+ }
+
atmci_pdc_cleanup(host);
/*
@@ -678,9 +739,10 @@ static void atmci_pdc_complete(struct atmel_mci *host)
* to send the stop command or waiting for NBUSY in this case.
*/
if (host->data) {
+ dev_dbg(&host->pdev->dev,
+ "(%s) set pending xfer complete\n", __func__);
atmci_set_pending(host, EVENT_XFER_COMPLETE);
tasklet_schedule(&host->tasklet);
- atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
}
}
@@ -716,6 +778,8 @@ static void atmci_dma_complete(void *arg)
* to send the stop command or waiting for NBUSY in this case.
*/
if (data) {
+ dev_dbg(&host->pdev->dev,
+ "(%s) set pending xfer complete\n", __func__);
atmci_set_pending(host, EVENT_XFER_COMPLETE);
tasklet_schedule(&host->tasklet);
@@ -791,6 +855,7 @@ atmci_prepare_data_pdc(struct atmel_mci *host, struct mmc_data *data)
u32 iflags, tmp;
unsigned int sg_len;
enum dma_data_direction dir;
+ int i;
data->error = -EINPROGRESS;
@@ -806,7 +871,7 @@ atmci_prepare_data_pdc(struct atmel_mci *host, struct mmc_data *data)
iflags |= ATMCI_ENDRX | ATMCI_RXBUFF;
} else {
dir = DMA_TO_DEVICE;
- iflags |= ATMCI_ENDTX | ATMCI_TXBUFE;
+ iflags |= ATMCI_ENDTX | ATMCI_TXBUFE | ATMCI_BLKE;
}
/* Set BLKLEN */
@@ -818,6 +883,16 @@ atmci_prepare_data_pdc(struct atmel_mci *host, struct mmc_data *data)
/* 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->caps.has_rwproof)
+ && (host->data->flags & MMC_DATA_WRITE)) {
+ sg_copy_to_buffer(host->data->sg, host->data->sg_len,
+ host->buffer, host->data_size);
+ if (host->caps.has_bad_data_ordering)
+ for (i = 0; i < host->data_size; i++)
+ host->buffer[i] = swab32(host->buffer[i]);
+ }
+
if (host->data_size)
atmci_pdc_set_both_buf(host,
((dir == DMA_FROM_DEVICE) ? XFER_RECEIVE : XFER_TRANSMIT));
@@ -931,6 +1006,8 @@ atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data)
static void atmci_stop_transfer(struct atmel_mci *host)
{
+ dev_dbg(&host->pdev->dev,
+ "(%s) set pending xfer complete\n", __func__);
atmci_set_pending(host, EVENT_XFER_COMPLETE);
atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
}
@@ -940,8 +1017,7 @@ static void atmci_stop_transfer(struct atmel_mci *host)
*/
static void atmci_stop_transfer_pdc(struct atmel_mci *host)
{
- atmci_set_pending(host, EVENT_XFER_COMPLETE);
- atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
+ atmci_writel(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
}
static void atmci_stop_transfer_dma(struct atmel_mci *host)
@@ -953,6 +1029,8 @@ static void atmci_stop_transfer_dma(struct atmel_mci *host)
atmci_dma_cleanup(host);
} else {
/* Data transfer was stopped by the interrupt handler */
+ dev_dbg(&host->pdev->dev,
+ "(%s) set pending xfer complete\n", __func__);
atmci_set_pending(host, EVENT_XFER_COMPLETE);
atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
}
@@ -977,9 +1055,12 @@ static void atmci_start_request(struct atmel_mci *host,
host->pending_events = 0;
host->completed_events = 0;
+ host->cmd_status = 0;
host->data_status = 0;
- if (host->need_reset) {
+ dev_dbg(&host->pdev->dev, "start request: cmd %u\n", mrq->cmd->opcode);
+
+ if (host->need_reset || host->caps.need_reset_after_xfer) {
iflags = atmci_readl(host, ATMCI_IMR);
iflags &= (ATMCI_SDIOIRQA | ATMCI_SDIOIRQB);
atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST);
@@ -994,7 +1075,7 @@ static void atmci_start_request(struct atmel_mci *host,
iflags = atmci_readl(host, ATMCI_IMR);
if (iflags & ~(ATMCI_SDIOIRQA | ATMCI_SDIOIRQB))
- dev_warn(&slot->mmc->class_dev, "WARNING: IMR=0x%08x\n",
+ dev_dbg(&slot->mmc->class_dev, "WARNING: IMR=0x%08x\n",
iflags);
if (unlikely(test_and_clear_bit(ATMCI_CARD_NEED_INIT, &slot->flags))) {
@@ -1043,6 +1124,8 @@ static void atmci_start_request(struct atmel_mci *host,
* prepared yet.)
*/
atmci_writel(host, ATMCI_IER, iflags);
+
+ mod_timer(&host->timer, jiffies + msecs_to_jiffies(2000));
}
static void atmci_queue_request(struct atmel_mci *host,
@@ -1057,6 +1140,7 @@ static void atmci_queue_request(struct atmel_mci *host,
host->state = STATE_SENDING_CMD;
atmci_start_request(host, slot);
} else {
+ dev_dbg(&host->pdev->dev, "queue request\n");
list_add_tail(&slot->queue_node, &host->queue);
}
spin_unlock_bh(&host->lock);
@@ -1069,6 +1153,7 @@ static void atmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
struct mmc_data *data;
WARN_ON(slot->mrq);
+ dev_dbg(&host->pdev->dev, "MRQ: cmd %u\n", mrq->cmd->opcode);
/*
* We may "know" the card is gone even though there's still an
@@ -1308,6 +1393,8 @@ static void atmci_request_end(struct atmel_mci *host, struct mmc_request *mrq)
host->state = STATE_IDLE;
}
+ del_timer(&host->timer);
+
spin_unlock(&host->lock);
mmc_request_done(prev_mmc, mrq);
spin_lock(&host->lock);
@@ -1330,21 +1417,13 @@ static void atmci_command_complete(struct atmel_mci *host,
cmd->error = -EILSEQ;
else if (status & (ATMCI_RINDE | ATMCI_RDIRE | ATMCI_RENDE))
cmd->error = -EIO;
- else
- cmd->error = 0;
-
- if (cmd->error) {
- dev_dbg(&host->pdev->dev,
- "command error: status=0x%08x\n", status);
-
- if (cmd->data) {
- host->stop_transfer(host);
- host->data = NULL;
- atmci_writel(host, ATMCI_IDR, ATMCI_NOTBUSY
- | ATMCI_TXRDY | ATMCI_RXRDY
- | ATMCI_DATA_ERROR_FLAGS);
+ else if (host->mrq->data && (host->mrq->data->blksz & 3)) {
+ if (host->caps.need_blksz_mul_4) {
+ cmd->error = -EINVAL;
+ host->need_reset = 1;
}
- }
+ } else
+ cmd->error = 0;
}
static void atmci_detect_change(unsigned long data)
@@ -1407,23 +1486,21 @@ static void atmci_detect_change(unsigned long data)
break;
case STATE_SENDING_CMD:
mrq->cmd->error = -ENOMEDIUM;
- if (!mrq->data)
- break;
- /* fall through */
- case STATE_SENDING_DATA:
+ if (mrq->data)
+ host->stop_transfer(host);
+ break;
+ case STATE_DATA_XFER:
mrq->data->error = -ENOMEDIUM;
host->stop_transfer(host);
break;
- case STATE_DATA_BUSY:
- case STATE_DATA_ERROR:
- if (mrq->data->error == -EINPROGRESS)
- mrq->data->error = -ENOMEDIUM;
- if (!mrq->stop)
- break;
- /* fall through */
+ case STATE_WAITING_NOTBUSY:
+ mrq->data->error = -ENOMEDIUM;
+ break;
case STATE_SENDING_STOP:
mrq->stop->error = -ENOMEDIUM;
break;
+ case STATE_END_REQUEST:
+ break;
}
atmci_request_end(host, mrq);
@@ -1451,7 +1528,6 @@ static void atmci_tasklet_func(unsigned long priv)
struct atmel_mci *host = (struct atmel_mci *)priv;
struct mmc_request *mrq = host->mrq;
struct mmc_data *data = host->data;
- struct mmc_command *cmd = host->cmd;
enum atmel_mci_state state = host->state;
enum atmel_mci_state prev_state;
u32 status;
@@ -1467,107 +1543,186 @@ static void atmci_tasklet_func(unsigned long priv)
do {
prev_state = state;
+ dev_dbg(&host->pdev->dev, "FSM: state=%d\n", state);
switch (state) {
case STATE_IDLE:
break;
case STATE_SENDING_CMD:
+ /*
+ * Command has been sent, we are waiting for command
+ * ready. Then we have three next states possible:
+ * END_REQUEST by default, WAITING_NOTBUSY if it's a
+ * command needing it or DATA_XFER if there is data.
+ */
+ dev_dbg(&host->pdev->dev, "FSM: cmd ready?\n");
if (!atmci_test_and_clear_pending(host,
- EVENT_CMD_COMPLETE))
+ EVENT_CMD_RDY))
break;
+ dev_dbg(&host->pdev->dev, "set completed cmd ready\n");
host->cmd = NULL;
- atmci_set_completed(host, EVENT_CMD_COMPLETE);
+ atmci_set_completed(host, EVENT_CMD_RDY);
atmci_command_complete(host, mrq->cmd);
- if (!mrq->data || cmd->error) {
- atmci_request_end(host, host->mrq);
- goto unlock;
- }
+ if (mrq->data) {
+ dev_dbg(&host->pdev->dev,
+ "command with data transfer");
+ /*
+ * If there is a command error don't start
+ * data transfer.
+ */
+ if (mrq->cmd->error) {
+ host->stop_transfer(host);
+ host->data = NULL;
+ atmci_writel(host, ATMCI_IDR,
+ ATMCI_TXRDY | ATMCI_RXRDY
+ | ATMCI_DATA_ERROR_FLAGS);
+ state = STATE_END_REQUEST;
+ } else
+ state = STATE_DATA_XFER;
+ } else if ((!mrq->data) && (mrq->cmd->flags & MMC_RSP_BUSY)) {
+ dev_dbg(&host->pdev->dev,
+ "command response need waiting notbusy");
+ atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
+ state = STATE_WAITING_NOTBUSY;
+ } else
+ state = STATE_END_REQUEST;
- prev_state = state = STATE_SENDING_DATA;
- /* fall through */
+ break;
- case STATE_SENDING_DATA:
+ case STATE_DATA_XFER:
if (atmci_test_and_clear_pending(host,
EVENT_DATA_ERROR)) {
- host->stop_transfer(host);
- if (data->stop)
- atmci_send_stop_cmd(host, data);
- state = STATE_DATA_ERROR;
+ dev_dbg(&host->pdev->dev, "set completed data error\n");
+ atmci_set_completed(host, EVENT_DATA_ERROR);
+ state = STATE_END_REQUEST;
break;
}
+ /*
+ * A data transfer is in progress. The event expected
+ * to move to the next state depends of data transfer
+ * type (PDC or DMA). Once transfer done we can move
+ * to the next step which is WAITING_NOTBUSY in write
+ * case and directly SENDING_STOP in read case.
+ */
+ dev_dbg(&host->pdev->dev, "FSM: xfer complete?\n");
if (!atmci_test_and_clear_pending(host,
EVENT_XFER_COMPLETE))
break;
+ dev_dbg(&host->pdev->dev,
+ "(%s) set completed xfer complete\n",
+ __func__);
atmci_set_completed(host, EVENT_XFER_COMPLETE);
- prev_state = state = STATE_DATA_BUSY;
- /* fall through */
- case STATE_DATA_BUSY:
- if (!atmci_test_and_clear_pending(host,
- EVENT_DATA_COMPLETE))
- break;
-
- host->data = NULL;
- atmci_set_completed(host, EVENT_DATA_COMPLETE);
- status = host->data_status;
- if (unlikely(status & ATMCI_DATA_ERROR_FLAGS)) {
- if (status & ATMCI_DTOE) {
- dev_dbg(&host->pdev->dev,
- "data timeout error\n");
- data->error = -ETIMEDOUT;
- } else if (status & ATMCI_DCRCE) {
- dev_dbg(&host->pdev->dev,
- "data CRC error\n");
- data->error = -EILSEQ;
- } else {
- dev_dbg(&host->pdev->dev,
- "data FIFO error (status=%08x)\n",
- status);
- data->error = -EIO;
- }
+ if (host->data->flags & MMC_DATA_WRITE) {
+ atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
+ state = STATE_WAITING_NOTBUSY;
+ } else if (host->mrq->stop) {
+ atmci_writel(host, ATMCI_IER, ATMCI_CMDRDY);
+ atmci_send_stop_cmd(host, data);
+ state = STATE_SENDING_STOP;
} else {
+ host->data = NULL;
data->bytes_xfered = data->blocks * data->blksz;
data->error = 0;
- atmci_writel(host, ATMCI_IDR, ATMCI_DATA_ERROR_FLAGS);
+ state = STATE_END_REQUEST;
}
+ break;
- if (!data->stop) {
- atmci_request_end(host, host->mrq);
- goto unlock;
- }
+ case STATE_WAITING_NOTBUSY:
+ /*
+ * We can be in the state for two reasons: a command
+ * requiring waiting not busy signal (stop command
+ * included) or a write operation. In the latest case,
+ * we need to send a stop command.
+ */
+ dev_dbg(&host->pdev->dev, "FSM: not busy?\n");
+ if (!atmci_test_and_clear_pending(host,
+ EVENT_NOTBUSY))
+ break;
- prev_state = state = STATE_SENDING_STOP;
- if (!data->error)
- atmci_send_stop_cmd(host, data);
- /* fall through */
+ dev_dbg(&host->pdev->dev, "set completed not busy\n");
+ atmci_set_completed(host, EVENT_NOTBUSY);
+
+ if (host->data) {
+ /*
+ * For some commands such as CMD53, even if
+ * there is data transfer, there is no stop
+ * command to send.
+ */
+ if (host->mrq->stop) {
+ atmci_writel(host, ATMCI_IER,
+ ATMCI_CMDRDY);
+ atmci_send_stop_cmd(host, data);
+ state = STATE_SENDING_STOP;
+ } else {
+ host->data = NULL;
+ data->bytes_xfered = data->blocks
+ * data->blksz;
+ data->error = 0;
+ state = STATE_END_REQUEST;
+ }
+ } else
+ state = STATE_END_REQUEST;
+ break;
case STATE_SENDING_STOP:
+ /*
+ * In this state, it is important to set host->data to
+ * NULL (which is tested in the waiting notbusy state)
+ * in order to go to the end request state instead of
+ * sending stop again.
+ */
+ dev_dbg(&host->pdev->dev, "FSM: cmd ready?\n");
if (!atmci_test_and_clear_pending(host,
- EVENT_CMD_COMPLETE))
+ EVENT_CMD_RDY))
break;
+ dev_dbg(&host->pdev->dev, "FSM: cmd ready\n");
host->cmd = NULL;
+ host->data = NULL;
+ data->bytes_xfered = data->blocks * data->blksz;
+ data->error = 0;
atmci_command_complete(host, mrq->stop);
- atmci_request_end(host, host->mrq);
- goto unlock;
+ if (mrq->stop->error) {
+ host->stop_transfer(host);
+ atmci_writel(host, ATMCI_IDR,
+ ATMCI_TXRDY | ATMCI_RXRDY
+ | ATMCI_DATA_ERROR_FLAGS);
+ state = STATE_END_REQUEST;
+ } else {
+ atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
+ state = STATE_WAITING_NOTBUSY;
+ }
+ break;
- case STATE_DATA_ERROR:
- if (!atmci_test_and_clear_pending(host,
- EVENT_XFER_COMPLETE))
- break;
+ case STATE_END_REQUEST:
+ atmci_writel(host, ATMCI_IDR, ATMCI_TXRDY | ATMCI_RXRDY
+ | ATMCI_DATA_ERROR_FLAGS);
+ status = host->data_status;
+ if (unlikely(status)) {
+ host->stop_transfer(host);
+ host->data = NULL;
+ if (status & ATMCI_DTOE) {
+ data->error = -ETIMEDOUT;
+ } else if (status & ATMCI_DCRCE) {
+ data->error = -EILSEQ;
+ } else {
+ data->error = -EIO;
+ }
+ }
- state = STATE_DATA_BUSY;
+ atmci_request_end(host, host->mrq);
+ state = STATE_IDLE;
break;
}
} while (state != prev_state);
host->state = state;
-unlock:
spin_unlock(&host->lock);
}
@@ -1620,9 +1775,6 @@ static void atmci_read_data_pio(struct atmel_mci *host)
| ATMCI_DATA_ERROR_FLAGS));
host->data_status = status;
data->bytes_xfered += nbytes;
- smp_wmb();
- atmci_set_pending(host, EVENT_DATA_ERROR);
- tasklet_schedule(&host->tasklet);
return;
}
} while (status & ATMCI_RXRDY);
@@ -1691,9 +1843,6 @@ static void atmci_write_data_pio(struct atmel_mci *host)
| ATMCI_DATA_ERROR_FLAGS));
host->data_status = status;
data->bytes_xfered += nbytes;
- smp_wmb();
- atmci_set_pending(host, EVENT_DATA_ERROR);
- tasklet_schedule(&host->tasklet);
return;
}
} while (status & ATMCI_TXRDY);
@@ -1711,16 +1860,6 @@ done:
atmci_set_pending(host, EVENT_XFER_COMPLETE);
}
-static void atmci_cmd_interrupt(struct atmel_mci *host, u32 status)
-{
- atmci_writel(host, ATMCI_IDR, ATMCI_CMDRDY);
-
- host->cmd_status = status;
- smp_wmb();
- atmci_set_pending(host, EVENT_CMD_COMPLETE);
- tasklet_schedule(&host->tasklet);
-}
-
static void atmci_sdio_interrupt(struct atmel_mci *host, u32 status)
{
int i;
@@ -1748,17 +1887,21 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
break;
if (pending & ATMCI_DATA_ERROR_FLAGS) {
+ dev_dbg(&host->pdev->dev, "IRQ: data error\n");
atmci_writel(host, ATMCI_IDR, ATMCI_DATA_ERROR_FLAGS
- | ATMCI_RXRDY | ATMCI_TXRDY);
- pending &= atmci_readl(host, ATMCI_IMR);
+ | ATMCI_RXRDY | ATMCI_TXRDY
+ | ATMCI_ENDRX | ATMCI_ENDTX
+ | ATMCI_RXBUFF | ATMCI_TXBUFE);
host->data_status = status;
+ dev_dbg(&host->pdev->dev, "set pending data error\n");
smp_wmb();
atmci_set_pending(host, EVENT_DATA_ERROR);
tasklet_schedule(&host->tasklet);
}
if (pending & ATMCI_TXBUFE) {
+ dev_dbg(&host->pdev->dev, "IRQ: tx buffer empty\n");
atmci_writel(host, ATMCI_IDR, ATMCI_TXBUFE);
atmci_writel(host, ATMCI_IDR, ATMCI_ENDTX);
/*
@@ -1774,6 +1917,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
atmci_pdc_complete(host);
}
} else if (pending & ATMCI_ENDTX) {
+ dev_dbg(&host->pdev->dev, "IRQ: end of tx buffer\n");
atmci_writel(host, ATMCI_IDR, ATMCI_ENDTX);
if (host->data_size) {
@@ -1784,6 +1928,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
}
if (pending & ATMCI_RXBUFF) {
+ dev_dbg(&host->pdev->dev, "IRQ: rx buffer full\n");
atmci_writel(host, ATMCI_IDR, ATMCI_RXBUFF);
atmci_writel(host, ATMCI_IDR, ATMCI_ENDRX);
/*
@@ -1799,6 +1944,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
atmci_pdc_complete(host);
}
} else if (pending & ATMCI_ENDRX) {
+ dev_dbg(&host->pdev->dev, "IRQ: end of rx buffer\n");
atmci_writel(host, ATMCI_IDR, ATMCI_ENDRX);
if (host->data_size) {
@@ -1808,23 +1954,44 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
}
}
+ /*
+ * First mci IPs, so mainly the ones having pdc, have some
+ * issues with the notbusy signal. You can't get it after
+ * data transmission if you have not sent a stop command.
+ * The appropriate workaround is to use the BLKE signal.
+ */
+ if (pending & ATMCI_BLKE) {
+ dev_dbg(&host->pdev->dev, "IRQ: blke\n");
+ atmci_writel(host, ATMCI_IDR, ATMCI_BLKE);
+ smp_wmb();
+ dev_dbg(&host->pdev->dev, "set pending notbusy\n");
+ atmci_set_pending(host, EVENT_NOTBUSY);
+ tasklet_schedule(&host->tasklet);
+ }
if (pending & ATMCI_NOTBUSY) {
- atmci_writel(host, ATMCI_IDR,
- ATMCI_DATA_ERROR_FLAGS | ATMCI_NOTBUSY);
- if (!host->data_status)
- host->data_status = status;
+ dev_dbg(&host->pdev->dev, "IRQ: not_busy\n");
+ atmci_writel(host, ATMCI_IDR, ATMCI_NOTBUSY);
smp_wmb();
- atmci_set_pending(host, EVENT_DATA_COMPLETE);
+ dev_dbg(&host->pdev->dev, "set pending notbusy\n");
+ atmci_set_pending(host, EVENT_NOTBUSY);
tasklet_schedule(&host->tasklet);
}
+
if (pending & ATMCI_RXRDY)
atmci_read_data_pio(host);
if (pending & ATMCI_TXRDY)
atmci_write_data_pio(host);
- if (pending & ATMCI_CMDRDY)
- atmci_cmd_interrupt(host, status);
+ if (pending & ATMCI_CMDRDY) {
+ dev_dbg(&host->pdev->dev, "IRQ: cmd ready\n");
+ atmci_writel(host, ATMCI_IDR, ATMCI_CMDRDY);
+ host->cmd_status = status;
+ smp_wmb();
+ dev_dbg(&host->pdev->dev, "set pending cmd rdy\n");
+ atmci_set_pending(host, EVENT_CMD_RDY);
+ tasklet_schedule(&host->tasklet);
+ }
if (pending & (ATMCI_SDIOIRQA | ATMCI_SDIOIRQB))
atmci_sdio_interrupt(host, status);
@@ -1877,13 +2044,26 @@ static int __init atmci_init_slot(struct atmel_mci *host,
mmc->caps |= MMC_CAP_SDIO_IRQ;
if (host->caps.has_highspeed)
mmc->caps |= MMC_CAP_SD_HIGHSPEED;
- if (slot_data->bus_width >= 4)
+ /*
+ * Without the read/write proof capability, it is strongly suggested to
+ * use only one bit for data to prevent fifo underruns and overruns
+ * which will corrupt data.
+ */
+ if ((slot_data->bus_width >= 4) && host->caps.has_rwproof)
mmc->caps |= MMC_CAP_4_BIT_DATA;
- mmc->max_segs = 64;
- mmc->max_req_size = 32768 * 512;
- mmc->max_blk_size = 32768;
- mmc->max_blk_count = 512;
+ if (atmci_get_version(host) < 0x200) {
+ mmc->max_segs = 256;
+ mmc->max_blk_size = 4095;
+ mmc->max_blk_count = 256;
+ mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
+ mmc->max_seg_size = mmc->max_blk_size * mmc->max_segs;
+ } else {
+ mmc->max_segs = 64;
+ mmc->max_req_size = 32768 * 512;
+ mmc->max_blk_size = 32768;
+ mmc->max_blk_count = 512;
+ }
/* Assume card is present initially */
set_bit(ATMCI_CARD_PRESENT, &slot->flags);
@@ -2007,11 +2187,6 @@ static bool atmci_configure_dma(struct atmel_mci *host)
}
}
-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
@@ -2032,6 +2207,9 @@ static void __init atmci_get_cap(struct atmel_mci *host)
host->caps.has_highspeed = 0;
host->caps.has_rwproof = 0;
host->caps.has_odd_clk_div = 0;
+ host->caps.has_bad_data_ordering = 1;
+ host->caps.need_reset_after_xfer = 1;
+ host->caps.need_blksz_mul_4 = 1;
/* keep only major version number */
switch (version & 0xf00) {
@@ -2051,7 +2229,11 @@ static void __init atmci_get_cap(struct atmel_mci *host)
host->caps.has_highspeed = 1;
case 0x200:
host->caps.has_rwproof = 1;
+ host->caps.need_blksz_mul_4 = 0;
case 0x100:
+ host->caps.has_bad_data_ordering = 0;
+ host->caps.need_reset_after_xfer = 0;
+ case 0x0:
break;
default:
host->caps.has_pdc = 0;
@@ -2138,14 +2320,20 @@ static int __init atmci_probe(struct platform_device *pdev)
if (pdata->slot[0].bus_width) {
ret = atmci_init_slot(host, &pdata->slot[0],
0, ATMCI_SDCSEL_SLOT_A, ATMCI_SDIOIRQA);
- if (!ret)
+ if (!ret) {
nr_slots++;
+ host->buf_size = host->slot[0]->mmc->max_req_size;
+ }
}
if (pdata->slot[1].bus_width) {
ret = atmci_init_slot(host, &pdata->slot[1],
1, ATMCI_SDCSEL_SLOT_B, ATMCI_SDIOIRQB);
- if (!ret)
+ if (!ret) {
nr_slots++;
+ if (host->slot[1]->mmc->max_req_size > host->buf_size)
+ host->buf_size =
+ host->slot[1]->mmc->max_req_size;
+ }
}
if (!nr_slots) {
@@ -2153,6 +2341,19 @@ static int __init atmci_probe(struct platform_device *pdev)
goto err_init_slot;
}
+ if (!host->caps.has_rwproof) {
+ host->buffer = dma_alloc_coherent(&pdev->dev, host->buf_size,
+ &host->buf_phys_addr,
+ GFP_KERNEL);
+ if (!host->buffer) {
+ ret = -ENOMEM;
+ dev_err(&pdev->dev, "buffer allocation failed\n");
+ goto err_init_slot;
+ }
+ }
+
+ setup_timer(&host->timer, atmci_timeout_timer, (unsigned long)host);
+
dev_info(&pdev->dev,
"Atmel MCI controller at 0x%08lx irq %d, %u slots\n",
host->mapbase, irq, nr_slots);
@@ -2179,6 +2380,10 @@ static int __exit atmci_remove(struct platform_device *pdev)
platform_set_drvdata(pdev, NULL);
+ if (host->buffer)
+ dma_free_coherent(&pdev->dev, host->buf_size,
+ host->buffer, host->buf_phys_addr);
+
for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) {
if (host->slot[i])
atmci_cleanup_slot(host->slot[i], i);
diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c
index c1f3673ae1ef..7cf6c624bf73 100644
--- a/drivers/mmc/host/davinci_mmc.c
+++ b/drivers/mmc/host/davinci_mmc.c
@@ -1533,4 +1533,5 @@ module_exit(davinci_mmcsd_exit);
MODULE_AUTHOR("Texas Instruments India");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MMC/SD driver for Davinci MMC controller");
+MODULE_ALIAS("platform:davinci_mmc");
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index ab3fc4617107..9bbf45f8c538 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -100,8 +100,6 @@ struct dw_mci_slot {
int last_detect_state;
};
-static struct workqueue_struct *dw_mci_card_workqueue;
-
#if defined(CONFIG_DEBUG_FS)
static int dw_mci_req_show(struct seq_file *s, void *v)
{
@@ -859,10 +857,10 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb)
int_mask = mci_readl(host, INTMASK);
if (enb) {
mci_writel(host, INTMASK,
- (int_mask | (1 << SDMMC_INT_SDIO(slot->id))));
+ (int_mask | SDMMC_INT_SDIO(slot->id)));
} else {
mci_writel(host, INTMASK,
- (int_mask & ~(1 << SDMMC_INT_SDIO(slot->id))));
+ (int_mask & ~SDMMC_INT_SDIO(slot->id)));
}
}
@@ -1605,7 +1603,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
if (pending & SDMMC_INT_CD) {
mci_writel(host, RINTSTS, SDMMC_INT_CD);
- queue_work(dw_mci_card_workqueue, &host->card_work);
+ queue_work(host->card_workqueue, &host->card_work);
}
/* Handle SDIO Interrupts */
@@ -1844,7 +1842,7 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
* Card may have been plugged in prior to boot so we
* need to run the detect tasklet
*/
- queue_work(dw_mci_card_workqueue, &host->card_work);
+ queue_work(host->card_workqueue, &host->card_work);
return 0;
}
@@ -2021,9 +2019,9 @@ int dw_mci_probe(struct dw_mci *host)
mci_writel(host, CLKSRC, 0);
tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host);
- dw_mci_card_workqueue = alloc_workqueue("dw-mci-card",
+ host->card_workqueue = alloc_workqueue("dw-mci-card",
WQ_MEM_RECLAIM | WQ_NON_REENTRANT, 1);
- if (!dw_mci_card_workqueue)
+ if (!host->card_workqueue)
goto err_dmaunmap;
INIT_WORK(&host->card_work, dw_mci_work_routine_card);
ret = request_irq(host->irq, dw_mci_interrupt, host->irq_flags, "dw-mci", host);
@@ -2085,7 +2083,7 @@ err_init_slot:
free_irq(host->irq, host);
err_workqueue:
- destroy_workqueue(dw_mci_card_workqueue);
+ destroy_workqueue(host->card_workqueue);
err_dmaunmap:
if (host->use_dma && host->dma_ops->exit)
@@ -2119,7 +2117,7 @@ void dw_mci_remove(struct dw_mci *host)
mci_writel(host, CLKSRC, 0);
free_irq(host->irq, host);
- destroy_workqueue(dw_mci_card_workqueue);
+ destroy_workqueue(host->card_workqueue);
dma_free_coherent(&host->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
if (host->use_dma && host->dma_ops->exit)
diff --git a/drivers/mmc/host/imxmmc.c b/drivers/mmc/host/imxmmc.c
deleted file mode 100644
index ea0f3cedef21..000000000000
--- a/drivers/mmc/host/imxmmc.c
+++ /dev/null
@@ -1,1169 +0,0 @@
-/*
- * linux/drivers/mmc/host/imxmmc.c - Motorola i.MX MMCI driver
- *
- * Copyright (C) 2004 Sascha Hauer, Pengutronix <sascha@saschahauer.de>
- * Copyright (C) 2006 Pavel Pisa, PiKRON <ppisa@pikron.com>
- *
- * derived from pxamci.c by Russell King
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/ioport.h>
-#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/blkdev.h>
-#include <linux/dma-mapping.h>
-#include <linux/mmc/host.h>
-#include <linux/mmc/card.h>
-#include <linux/delay.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-
-#include <asm/dma.h>
-#include <asm/irq.h>
-#include <asm/sizes.h>
-#include <mach/mmc.h>
-#include <mach/imx-dma.h>
-
-#include "imxmmc.h"
-
-#define DRIVER_NAME "imx-mmc"
-
-#define IMXMCI_INT_MASK_DEFAULT (INT_MASK_BUF_READY | INT_MASK_DATA_TRAN | \
- INT_MASK_WRITE_OP_DONE | INT_MASK_END_CMD_RES | \
- INT_MASK_AUTO_CARD_DETECT | INT_MASK_DAT0_EN | INT_MASK_SDIO)
-
-struct imxmci_host {
- struct mmc_host *mmc;
- spinlock_t lock;
- struct resource *res;
- void __iomem *base;
- int irq;
- imx_dmach_t dma;
- volatile unsigned int imask;
- unsigned int power_mode;
- unsigned int present;
- struct imxmmc_platform_data *pdata;
-
- struct mmc_request *req;
- struct mmc_command *cmd;
- struct mmc_data *data;
-
- struct timer_list timer;
- struct tasklet_struct tasklet;
- unsigned int status_reg;
- unsigned long pending_events;
- /* Next two fields are there for CPU driven transfers to overcome SDHC deficiencies */
- u16 *data_ptr;
- unsigned int data_cnt;
- atomic_t stuck_timeout;
-
- unsigned int dma_nents;
- unsigned int dma_size;
- unsigned int dma_dir;
- int dma_allocated;
-
- unsigned char actual_bus_width;
-
- int prev_cmd_code;
-
- struct clk *clk;
-};
-
-#define IMXMCI_PEND_IRQ_b 0
-#define IMXMCI_PEND_DMA_END_b 1
-#define IMXMCI_PEND_DMA_ERR_b 2
-#define IMXMCI_PEND_WAIT_RESP_b 3
-#define IMXMCI_PEND_DMA_DATA_b 4
-#define IMXMCI_PEND_CPU_DATA_b 5
-#define IMXMCI_PEND_CARD_XCHG_b 6
-#define IMXMCI_PEND_SET_INIT_b 7
-#define IMXMCI_PEND_STARTED_b 8
-
-#define IMXMCI_PEND_IRQ_m (1 << IMXMCI_PEND_IRQ_b)
-#define IMXMCI_PEND_DMA_END_m (1 << IMXMCI_PEND_DMA_END_b)
-#define IMXMCI_PEND_DMA_ERR_m (1 << IMXMCI_PEND_DMA_ERR_b)
-#define IMXMCI_PEND_WAIT_RESP_m (1 << IMXMCI_PEND_WAIT_RESP_b)
-#define IMXMCI_PEND_DMA_DATA_m (1 << IMXMCI_PEND_DMA_DATA_b)
-#define IMXMCI_PEND_CPU_DATA_m (1 << IMXMCI_PEND_CPU_DATA_b)
-#define IMXMCI_PEND_CARD_XCHG_m (1 << IMXMCI_PEND_CARD_XCHG_b)
-#define IMXMCI_PEND_SET_INIT_m (1 << IMXMCI_PEND_SET_INIT_b)
-#define IMXMCI_PEND_STARTED_m (1 << IMXMCI_PEND_STARTED_b)
-
-static void imxmci_stop_clock(struct imxmci_host *host)
-{
- int i = 0;
- u16 reg;
-
- reg = readw(host->base + MMC_REG_STR_STP_CLK);
- writew(reg & ~STR_STP_CLK_START_CLK, host->base + MMC_REG_STR_STP_CLK);
- while (i < 0x1000) {
- if (!(i & 0x7f)) {
- reg = readw(host->base + MMC_REG_STR_STP_CLK);
- writew(reg | STR_STP_CLK_STOP_CLK,
- host->base + MMC_REG_STR_STP_CLK);
- }
-
- reg = readw(host->base + MMC_REG_STATUS);
- if (!(reg & STATUS_CARD_BUS_CLK_RUN)) {
- /* Check twice before cut */
- reg = readw(host->base + MMC_REG_STATUS);
- if (!(reg & STATUS_CARD_BUS_CLK_RUN))
- return;
- }
-
- i++;
- }
- dev_dbg(mmc_dev(host->mmc), "imxmci_stop_clock blocked, no luck\n");
-}
-
-static int imxmci_start_clock(struct imxmci_host *host)
-{
- unsigned int trials = 0;
- unsigned int delay_limit = 128;
- unsigned long flags;
- u16 reg;
-
- reg = readw(host->base + MMC_REG_STR_STP_CLK);
- writew(reg & ~STR_STP_CLK_STOP_CLK, host->base + MMC_REG_STR_STP_CLK);
-
- clear_bit(IMXMCI_PEND_STARTED_b, &host->pending_events);
-
- /*
- * Command start of the clock, this usually succeeds in less
- * then 6 delay loops, but during card detection (low clockrate)
- * it takes up to 5000 delay loops and sometimes fails for the first time
- */
- reg = readw(host->base + MMC_REG_STR_STP_CLK);
- writew(reg | STR_STP_CLK_START_CLK, host->base + MMC_REG_STR_STP_CLK);
-
- do {
- unsigned int delay = delay_limit;
-
- while (delay--) {
- reg = readw(host->base + MMC_REG_STATUS);
- if (reg & STATUS_CARD_BUS_CLK_RUN) {
- /* Check twice before cut */
- reg = readw(host->base + MMC_REG_STATUS);
- if (reg & STATUS_CARD_BUS_CLK_RUN)
- return 0;
- }
-
- if (test_bit(IMXMCI_PEND_STARTED_b, &host->pending_events))
- return 0;
- }
-
- local_irq_save(flags);
- /*
- * Ensure, that request is not doubled under all possible circumstances.
- * It is possible, that cock running state is missed, because some other
- * IRQ or schedule delays this function execution and the clocks has
- * been already stopped by other means (response processing, SDHC HW)
- */
- if (!test_bit(IMXMCI_PEND_STARTED_b, &host->pending_events)) {
- reg = readw(host->base + MMC_REG_STR_STP_CLK);
- writew(reg | STR_STP_CLK_START_CLK,
- host->base + MMC_REG_STR_STP_CLK);
- }
- local_irq_restore(flags);
-
- } while (++trials < 256);
-
- dev_err(mmc_dev(host->mmc), "imxmci_start_clock blocked, no luck\n");
-
- return -1;
-}
-
-static void imxmci_softreset(struct imxmci_host *host)
-{
- int i;
-
- /* reset sequence */
- writew(0x08, host->base + MMC_REG_STR_STP_CLK);
- writew(0x0D, host->base + MMC_REG_STR_STP_CLK);
-
- for (i = 0; i < 8; i++)
- writew(0x05, host->base + MMC_REG_STR_STP_CLK);
-
- writew(0xff, host->base + MMC_REG_RES_TO);
- writew(512, host->base + MMC_REG_BLK_LEN);
- writew(1, host->base + MMC_REG_NOB);
-}
-
-static int imxmci_busy_wait_for_status(struct imxmci_host *host,
- unsigned int *pstat, unsigned int stat_mask,
- int timeout, const char *where)
-{
- int loops = 0;
-
- while (!(*pstat & stat_mask)) {
- loops += 2;
- if (loops >= timeout) {
- dev_dbg(mmc_dev(host->mmc), "busy wait timeout in %s, STATUS = 0x%x (0x%x)\n",
- where, *pstat, stat_mask);
- return -1;
- }
- udelay(2);
- *pstat |= readw(host->base + MMC_REG_STATUS);
- }
- if (!loops)
- return 0;
-
- /* The busy-wait is expected there for clock <8MHz due to SDHC hardware flaws */
- if (!(stat_mask & STATUS_END_CMD_RESP) || (host->mmc->ios.clock >= 8000000))
- dev_info(mmc_dev(host->mmc), "busy wait for %d usec in %s, STATUS = 0x%x (0x%x)\n",
- loops, where, *pstat, stat_mask);
- return loops;
-}
-
-static void imxmci_setup_data(struct imxmci_host *host, struct mmc_data *data)
-{
- unsigned int nob = data->blocks;
- unsigned int blksz = data->blksz;
- unsigned int datasz = nob * blksz;
- int i;
-
- if (data->flags & MMC_DATA_STREAM)
- nob = 0xffff;
-
- host->data = data;
- data->bytes_xfered = 0;
-
- writew(nob, host->base + MMC_REG_NOB);
- writew(blksz, host->base + MMC_REG_BLK_LEN);
-
- /*
- * DMA cannot be used for small block sizes, we have to use CPU driven transfers otherwise.
- * We are in big troubles for non-512 byte transfers according to note in the paragraph
- * 20.6.7 of User Manual anyway, but we need to be able to transfer SCR at least.
- * The situation is even more complex in reality. The SDHC in not able to handle wll
- * partial FIFO fills and reads. The length has to be rounded up to burst size multiple.
- * This is required for SCR read at least.
- */
- if (datasz < 512) {
- host->dma_size = datasz;
- if (data->flags & MMC_DATA_READ) {
- host->dma_dir = DMA_FROM_DEVICE;
-
- /* Hack to enable read SCR */
- writew(1, host->base + MMC_REG_NOB);
- writew(512, host->base + MMC_REG_BLK_LEN);
- } else {
- host->dma_dir = DMA_TO_DEVICE;
- }
-
- /* Convert back to virtual address */
- host->data_ptr = (u16 *)sg_virt(data->sg);
- host->data_cnt = 0;
-
- clear_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events);
- set_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events);
-
- return;
- }
-
- if (data->flags & MMC_DATA_READ) {
- host->dma_dir = DMA_FROM_DEVICE;
- host->dma_nents = dma_map_sg(mmc_dev(host->mmc), data->sg,
- data->sg_len, host->dma_dir);
-
- imx_dma_setup_sg(host->dma, data->sg, data->sg_len, datasz,
- host->res->start + MMC_REG_BUFFER_ACCESS,
- DMA_MODE_READ);
-
- /*imx_dma_setup_mem2dev_ccr(host->dma, DMA_MODE_READ, IMX_DMA_WIDTH_16, CCR_REN);*/
- CCR(host->dma) = CCR_DMOD_LINEAR | CCR_DSIZ_32 | CCR_SMOD_FIFO | CCR_SSIZ_16 | CCR_REN;
- } else {
- host->dma_dir = DMA_TO_DEVICE;
-
- host->dma_nents = dma_map_sg(mmc_dev(host->mmc), data->sg,
- data->sg_len, host->dma_dir);
-
- imx_dma_setup_sg(host->dma, data->sg, data->sg_len, datasz,
- host->res->start + MMC_REG_BUFFER_ACCESS,
- DMA_MODE_WRITE);
-
- /*imx_dma_setup_mem2dev_ccr(host->dma, DMA_MODE_WRITE, IMX_DMA_WIDTH_16, CCR_REN);*/
- CCR(host->dma) = CCR_SMOD_LINEAR | CCR_SSIZ_32 | CCR_DMOD_FIFO | CCR_DSIZ_16 | CCR_REN;
- }
-
-#if 1 /* This code is there only for consistency checking and can be disabled in future */
- host->dma_size = 0;
- for (i = 0; i < host->dma_nents; i++)
- host->dma_size += data->sg[i].length;
-
- if (datasz > host->dma_size) {
- dev_err(mmc_dev(host->mmc), "imxmci_setup_data datasz 0x%x > 0x%x dm_size\n",
- datasz, host->dma_size);
- }
-#endif
-
- host->dma_size = datasz;
-
- wmb();
-
- set_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events);
- clear_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events);
-
- /* start DMA engine for read, write is delayed after initial response */
- if (host->dma_dir == DMA_FROM_DEVICE)
- imx_dma_enable(host->dma);
-}
-
-static void imxmci_start_cmd(struct imxmci_host *host, struct mmc_command *cmd, unsigned int cmdat)
-{
- unsigned long flags;
- u32 imask;
-
- WARN_ON(host->cmd != NULL);
- host->cmd = cmd;
-
- /* Ensure, that clock are stopped else command programming and start fails */
- imxmci_stop_clock(host);
-
- if (cmd->flags & MMC_RSP_BUSY)
- cmdat |= CMD_DAT_CONT_BUSY;
-
- switch (mmc_resp_type(cmd)) {
- case MMC_RSP_R1: /* short CRC, OPCODE */
- case MMC_RSP_R1B:/* short CRC, OPCODE, BUSY */
- cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R1;
- break;
- case MMC_RSP_R2: /* long 136 bit + CRC */
- cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R2;
- break;
- case MMC_RSP_R3: /* short */
- cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R3;
- break;
- default:
- break;
- }
-
- if (test_and_clear_bit(IMXMCI_PEND_SET_INIT_b, &host->pending_events))
- cmdat |= CMD_DAT_CONT_INIT; /* This command needs init */
-
- if (host->actual_bus_width == MMC_BUS_WIDTH_4)
- cmdat |= CMD_DAT_CONT_BUS_WIDTH_4;
-
- writew(cmd->opcode, host->base + MMC_REG_CMD);
- writew(cmd->arg >> 16, host->base + MMC_REG_ARGH);
- writew(cmd->arg & 0xffff, host->base + MMC_REG_ARGL);
- writew(cmdat, host->base + MMC_REG_CMD_DAT_CONT);
-
- atomic_set(&host->stuck_timeout, 0);
- set_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events);
-
-
- imask = IMXMCI_INT_MASK_DEFAULT;
- imask &= ~INT_MASK_END_CMD_RES;
- if (cmdat & CMD_DAT_CONT_DATA_ENABLE) {
- /* imask &= ~INT_MASK_BUF_READY; */
- imask &= ~INT_MASK_DATA_TRAN;
- if (cmdat & CMD_DAT_CONT_WRITE)
- imask &= ~INT_MASK_WRITE_OP_DONE;
- if (test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events))
- imask &= ~INT_MASK_BUF_READY;
- }
-
- spin_lock_irqsave(&host->lock, flags);
- host->imask = imask;
- writew(host->imask, host->base + MMC_REG_INT_MASK);
- spin_unlock_irqrestore(&host->lock, flags);
-
- dev_dbg(mmc_dev(host->mmc), "CMD%02d (0x%02x) mask set to 0x%04x\n",
- cmd->opcode, cmd->opcode, imask);
-
- imxmci_start_clock(host);
-}
-
-static void imxmci_finish_request(struct imxmci_host *host, struct mmc_request *req)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&host->lock, flags);
-
- host->pending_events &= ~(IMXMCI_PEND_WAIT_RESP_m | IMXMCI_PEND_DMA_END_m |
- IMXMCI_PEND_DMA_DATA_m | IMXMCI_PEND_CPU_DATA_m);
-
- host->imask = IMXMCI_INT_MASK_DEFAULT;
- writew(host->imask, host->base + MMC_REG_INT_MASK);
-
- spin_unlock_irqrestore(&host->lock, flags);
-
- if (req && req->cmd)
- host->prev_cmd_code = req->cmd->opcode;
-
- host->req = NULL;
- host->cmd = NULL;
- host->data = NULL;
- mmc_request_done(host->mmc, req);
-}
-
-static int imxmci_finish_data(struct imxmci_host *host, unsigned int stat)
-{
- struct mmc_data *data = host->data;
- int data_error;
-
- if (test_and_clear_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)) {
- imx_dma_disable(host->dma);
- dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_nents,
- host->dma_dir);
- }
-
- if (stat & STATUS_ERR_MASK) {
- dev_dbg(mmc_dev(host->mmc), "request failed. status: 0x%08x\n", stat);
- if (stat & (STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR))
- data->error = -EILSEQ;
- else if (stat & STATUS_TIME_OUT_READ)
- data->error = -ETIMEDOUT;
- else
- data->error = -EIO;
- } else {
- data->bytes_xfered = host->dma_size;
- }
-
- data_error = data->error;
-
- host->data = NULL;
-
- return data_error;
-}
-
-static int imxmci_cmd_done(struct imxmci_host *host, unsigned int stat)
-{
- struct mmc_command *cmd = host->cmd;
- int i;
- u32 a, b, c;
- struct mmc_data *data = host->data;
-
- if (!cmd)
- return 0;
-
- host->cmd = NULL;
-
- if (stat & STATUS_TIME_OUT_RESP) {
- dev_dbg(mmc_dev(host->mmc), "CMD TIMEOUT\n");
- cmd->error = -ETIMEDOUT;
- } else if (stat & STATUS_RESP_CRC_ERR && cmd->flags & MMC_RSP_CRC) {
- dev_dbg(mmc_dev(host->mmc), "cmd crc error\n");
- cmd->error = -EILSEQ;
- }
-
- if (cmd->flags & MMC_RSP_PRESENT) {
- if (cmd->flags & MMC_RSP_136) {
- for (i = 0; i < 4; i++) {
- a = readw(host->base + MMC_REG_RES_FIFO);
- b = readw(host->base + MMC_REG_RES_FIFO);
- cmd->resp[i] = a << 16 | b;
- }
- } else {
- a = readw(host->base + MMC_REG_RES_FIFO);
- b = readw(host->base + MMC_REG_RES_FIFO);
- c = readw(host->base + MMC_REG_RES_FIFO);
- cmd->resp[0] = a << 24 | b << 8 | c >> 8;
- }
- }
-
- dev_dbg(mmc_dev(host->mmc), "RESP 0x%08x, 0x%08x, 0x%08x, 0x%08x, error %d\n",
- cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3], cmd->error);
-
- if (data && !cmd->error && !(stat & STATUS_ERR_MASK)) {
- if (host->req->data->flags & MMC_DATA_WRITE) {
-
- /* Wait for FIFO to be empty before starting DMA write */
-
- stat = readw(host->base + MMC_REG_STATUS);
- if (imxmci_busy_wait_for_status(host, &stat,
- STATUS_APPL_BUFF_FE,
- 40, "imxmci_cmd_done DMA WR") < 0) {
- cmd->error = -EIO;
- imxmci_finish_data(host, stat);
- if (host->req)
- imxmci_finish_request(host, host->req);
- dev_warn(mmc_dev(host->mmc), "STATUS = 0x%04x\n",
- stat);
- return 0;
- }
-
- if (test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events))
- imx_dma_enable(host->dma);
- }
- } else {
- struct mmc_request *req;
- imxmci_stop_clock(host);
- req = host->req;
-
- if (data)
- imxmci_finish_data(host, stat);
-
- if (req)
- imxmci_finish_request(host, req);
- else
- dev_warn(mmc_dev(host->mmc), "imxmci_cmd_done: no request to finish\n");
- }
-
- return 1;
-}
-
-static int imxmci_data_done(struct imxmci_host *host, unsigned int stat)
-{
- struct mmc_data *data = host->data;
- int data_error;
-
- if (!data)
- return 0;
-
- data_error = imxmci_finish_data(host, stat);
-
- if (host->req->stop) {
- imxmci_stop_clock(host);
- imxmci_start_cmd(host, host->req->stop, 0);
- } else {
- struct mmc_request *req;
- req = host->req;
- if (req)
- imxmci_finish_request(host, req);
- else
- dev_warn(mmc_dev(host->mmc), "imxmci_data_done: no request to finish\n");
- }
-
- return 1;
-}
-
-static int imxmci_cpu_driven_data(struct imxmci_host *host, unsigned int *pstat)
-{
- int i;
- int burst_len;
- int trans_done = 0;
- unsigned int stat = *pstat;
-
- if (host->actual_bus_width != MMC_BUS_WIDTH_4)
- burst_len = 16;
- else
- burst_len = 64;
-
- /* This is unfortunately required */
- dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data running STATUS = 0x%x\n",
- stat);
-
- udelay(20); /* required for clocks < 8MHz*/
-
- if (host->dma_dir == DMA_FROM_DEVICE) {
- imxmci_busy_wait_for_status(host, &stat,
- STATUS_APPL_BUFF_FF | STATUS_DATA_TRANS_DONE |
- STATUS_TIME_OUT_READ,
- 50, "imxmci_cpu_driven_data read");
-
- while ((stat & (STATUS_APPL_BUFF_FF | STATUS_DATA_TRANS_DONE)) &&
- !(stat & STATUS_TIME_OUT_READ) &&
- (host->data_cnt < 512)) {
-
- udelay(20); /* required for clocks < 8MHz*/
-
- for (i = burst_len; i >= 2 ; i -= 2) {
- u16 data;
- data = readw(host->base + MMC_REG_BUFFER_ACCESS);
- udelay(10); /* required for clocks < 8MHz*/
- if (host->data_cnt+2 <= host->dma_size) {
- *(host->data_ptr++) = data;
- } else {
- if (host->data_cnt < host->dma_size)
- *(u8 *)(host->data_ptr) = data;
- }
- host->data_cnt += 2;
- }
-
- stat = readw(host->base + MMC_REG_STATUS);
-
- dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data read %d burst %d STATUS = 0x%x\n",
- host->data_cnt, burst_len, stat);
- }
-
- if ((stat & STATUS_DATA_TRANS_DONE) && (host->data_cnt >= 512))
- trans_done = 1;
-
- if (host->dma_size & 0x1ff)
- stat &= ~STATUS_CRC_READ_ERR;
-
- if (stat & STATUS_TIME_OUT_READ) {
- dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data read timeout STATUS = 0x%x\n",
- stat);
- trans_done = -1;
- }
-
- } else {
- imxmci_busy_wait_for_status(host, &stat,
- STATUS_APPL_BUFF_FE,
- 20, "imxmci_cpu_driven_data write");
-
- while ((stat & STATUS_APPL_BUFF_FE) &&
- (host->data_cnt < host->dma_size)) {
- if (burst_len >= host->dma_size - host->data_cnt) {
- burst_len = host->dma_size - host->data_cnt;
- host->data_cnt = host->dma_size;
- trans_done = 1;
- } else {
- host->data_cnt += burst_len;
- }
-
- for (i = burst_len; i > 0 ; i -= 2)
- writew(*(host->data_ptr++), host->base + MMC_REG_BUFFER_ACCESS);
-
- stat = readw(host->base + MMC_REG_STATUS);
-
- dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data write burst %d STATUS = 0x%x\n",
- burst_len, stat);
- }
- }
-
- *pstat = stat;
-
- return trans_done;
-}
-
-static void imxmci_dma_irq(int dma, void *devid)
-{
- struct imxmci_host *host = devid;
- u32 stat = readw(host->base + MMC_REG_STATUS);
-
- atomic_set(&host->stuck_timeout, 0);
- host->status_reg = stat;
- set_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events);
- tasklet_schedule(&host->tasklet);
-}
-
-static irqreturn_t imxmci_irq(int irq, void *devid)
-{
- struct imxmci_host *host = devid;
- u32 stat = readw(host->base + MMC_REG_STATUS);
- int handled = 1;
-
- writew(host->imask | INT_MASK_SDIO | INT_MASK_AUTO_CARD_DETECT,
- host->base + MMC_REG_INT_MASK);
-
- atomic_set(&host->stuck_timeout, 0);
- host->status_reg = stat;
- set_bit(IMXMCI_PEND_IRQ_b, &host->pending_events);
- set_bit(IMXMCI_PEND_STARTED_b, &host->pending_events);
- tasklet_schedule(&host->tasklet);
-
- return IRQ_RETVAL(handled);
-}
-
-static void imxmci_tasklet_fnc(unsigned long data)
-{
- struct imxmci_host *host = (struct imxmci_host *)data;
- u32 stat;
- unsigned int data_dir_mask = 0; /* STATUS_WR_CRC_ERROR_CODE_MASK */
- int timeout = 0;
-
- if (atomic_read(&host->stuck_timeout) > 4) {
- char *what;
- timeout = 1;
- stat = readw(host->base + MMC_REG_STATUS);
- host->status_reg = stat;
- if (test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events))
- if (test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events))
- what = "RESP+DMA";
- else
- what = "RESP";
- else
- if (test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events))
- if (test_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events))
- what = "DATA";
- else
- what = "DMA";
- else
- what = "???";
-
- dev_err(mmc_dev(host->mmc),
- "%s TIMEOUT, hardware stucked STATUS = 0x%04x IMASK = 0x%04x\n",
- what, stat,
- readw(host->base + MMC_REG_INT_MASK));
- dev_err(mmc_dev(host->mmc),
- "CMD_DAT_CONT = 0x%04x, MMC_BLK_LEN = 0x%04x, MMC_NOB = 0x%04x, DMA_CCR = 0x%08x\n",
- readw(host->base + MMC_REG_CMD_DAT_CONT),
- readw(host->base + MMC_REG_BLK_LEN),
- readw(host->base + MMC_REG_NOB),
- CCR(host->dma));
- dev_err(mmc_dev(host->mmc), "CMD%d, prevCMD%d, bus %d-bit, dma_size = 0x%x\n",
- host->cmd ? host->cmd->opcode : 0,
- host->prev_cmd_code,
- 1 << host->actual_bus_width, host->dma_size);
- }
-
- if (!host->present || timeout)
- host->status_reg = STATUS_TIME_OUT_RESP | STATUS_TIME_OUT_READ |
- STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR;
-
- if (test_bit(IMXMCI_PEND_IRQ_b, &host->pending_events) || timeout) {
- clear_bit(IMXMCI_PEND_IRQ_b, &host->pending_events);
-
- stat = readw(host->base + MMC_REG_STATUS);
- /*
- * This is not required in theory, but there is chance to miss some flag
- * which clears automatically by mask write, FreeScale original code keeps
- * stat from IRQ time so do I
- */
- stat |= host->status_reg;
-
- if (test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events))
- stat &= ~STATUS_CRC_READ_ERR;
-
- if (test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) {
- imxmci_busy_wait_for_status(host, &stat,
- STATUS_END_CMD_RESP | STATUS_ERR_MASK,
- 20, "imxmci_tasklet_fnc resp (ERRATUM #4)");
- }
-
- if (stat & (STATUS_END_CMD_RESP | STATUS_ERR_MASK)) {
- if (test_and_clear_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events))
- imxmci_cmd_done(host, stat);
- if (host->data && (stat & STATUS_ERR_MASK))
- imxmci_data_done(host, stat);
- }
-
- if (test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events)) {
- stat |= readw(host->base + MMC_REG_STATUS);
- if (imxmci_cpu_driven_data(host, &stat)) {
- if (test_and_clear_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events))
- imxmci_cmd_done(host, stat);
- atomic_clear_mask(IMXMCI_PEND_IRQ_m|IMXMCI_PEND_CPU_DATA_m,
- &host->pending_events);
- imxmci_data_done(host, stat);
- }
- }
- }
-
- if (test_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events) &&
- !test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) {
-
- stat = readw(host->base + MMC_REG_STATUS);
- /* Same as above */
- stat |= host->status_reg;
-
- if (host->dma_dir == DMA_TO_DEVICE)
- data_dir_mask = STATUS_WRITE_OP_DONE;
- else
- data_dir_mask = STATUS_DATA_TRANS_DONE;
-
- if (stat & data_dir_mask) {
- clear_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events);
- imxmci_data_done(host, stat);
- }
- }
-
- if (test_and_clear_bit(IMXMCI_PEND_CARD_XCHG_b, &host->pending_events)) {
-
- if (host->cmd)
- imxmci_cmd_done(host, STATUS_TIME_OUT_RESP);
-
- if (host->data)
- imxmci_data_done(host, STATUS_TIME_OUT_READ |
- STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR);
-
- if (host->req)
- imxmci_finish_request(host, host->req);
-
- mmc_detect_change(host->mmc, msecs_to_jiffies(100));
-
- }
-}
-
-static void imxmci_request(struct mmc_host *mmc, struct mmc_request *req)
-{
- struct imxmci_host *host = mmc_priv(mmc);
- unsigned int cmdat;
-
- WARN_ON(host->req != NULL);
-
- host->req = req;
-
- cmdat = 0;
-
- if (req->data) {
- imxmci_setup_data(host, req->data);
-
- cmdat |= CMD_DAT_CONT_DATA_ENABLE;
-
- if (req->data->flags & MMC_DATA_WRITE)
- cmdat |= CMD_DAT_CONT_WRITE;
-
- if (req->data->flags & MMC_DATA_STREAM)
- cmdat |= CMD_DAT_CONT_STREAM_BLOCK;
- }
-
- imxmci_start_cmd(host, req->cmd, cmdat);
-}
-
-#define CLK_RATE 19200000
-
-static void imxmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
-{
- struct imxmci_host *host = mmc_priv(mmc);
- int prescaler;
-
- if (ios->bus_width == MMC_BUS_WIDTH_4) {
- host->actual_bus_width = MMC_BUS_WIDTH_4;
- imx_gpio_mode(PB11_PF_SD_DAT3);
- BLR(host->dma) = 0; /* burst 64 byte read/write */
- } else {
- host->actual_bus_width = MMC_BUS_WIDTH_1;
- imx_gpio_mode(GPIO_PORTB | GPIO_IN | GPIO_PUEN | 11);
- BLR(host->dma) = 16; /* burst 16 byte read/write */
- }
-
- if (host->power_mode != ios->power_mode) {
- switch (ios->power_mode) {
- case MMC_POWER_OFF:
- break;
- case MMC_POWER_UP:
- set_bit(IMXMCI_PEND_SET_INIT_b, &host->pending_events);
- break;
- case MMC_POWER_ON:
- break;
- }
- host->power_mode = ios->power_mode;
- }
-
- if (ios->clock) {
- unsigned int clk;
- u16 reg;
-
- /* The prescaler is 5 for PERCLK2 equal to 96MHz
- * then 96MHz / 5 = 19.2 MHz
- */
- clk = clk_get_rate(host->clk);
- prescaler = (clk + (CLK_RATE * 7) / 8) / CLK_RATE;
- switch (prescaler) {
- case 0:
- case 1: prescaler = 0;
- break;
- case 2: prescaler = 1;
- break;
- case 3: prescaler = 2;
- break;
- case 4: prescaler = 4;
- break;
- default:
- case 5: prescaler = 5;
- break;
- }
-
- dev_dbg(mmc_dev(host->mmc), "PERCLK2 %d MHz -> prescaler %d\n",
- clk, prescaler);
-
- for (clk = 0; clk < 8; clk++) {
- int x;
- x = CLK_RATE / (1 << clk);
- if (x <= ios->clock)
- break;
- }
-
- /* enable controller */
- reg = readw(host->base + MMC_REG_STR_STP_CLK);
- writew(reg | STR_STP_CLK_ENABLE,
- host->base + MMC_REG_STR_STP_CLK);
-
- imxmci_stop_clock(host);
- writew((prescaler << 3) | clk, host->base + MMC_REG_CLK_RATE);
- /*
- * Under my understanding, clock should not be started there, because it would
- * initiate SDHC sequencer and send last or random command into card
- */
- /* imxmci_start_clock(host); */
-
- dev_dbg(mmc_dev(host->mmc),
- "MMC_CLK_RATE: 0x%08x\n",
- readw(host->base + MMC_REG_CLK_RATE));
- } else {
- imxmci_stop_clock(host);
- }
-}
-
-static int imxmci_get_ro(struct mmc_host *mmc)
-{
- struct imxmci_host *host = mmc_priv(mmc);
-
- if (host->pdata && host->pdata->get_ro)
- return !!host->pdata->get_ro(mmc_dev(mmc));
- /*
- * Board doesn't support read only detection; let the mmc core
- * decide what to do.
- */
- return -ENOSYS;
-}
-
-
-static const struct mmc_host_ops imxmci_ops = {
- .request = imxmci_request,
- .set_ios = imxmci_set_ios,
- .get_ro = imxmci_get_ro,
-};
-
-static void imxmci_check_status(unsigned long data)
-{
- struct imxmci_host *host = (struct imxmci_host *)data;
-
- if (host->pdata && host->pdata->card_present &&
- host->pdata->card_present(mmc_dev(host->mmc)) != host->present) {
- host->present ^= 1;
- dev_info(mmc_dev(host->mmc), "card %s\n",
- host->present ? "inserted" : "removed");
-
- set_bit(IMXMCI_PEND_CARD_XCHG_b, &host->pending_events);
- tasklet_schedule(&host->tasklet);
- }
-
- if (test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events) ||
- test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)) {
- atomic_inc(&host->stuck_timeout);
- if (atomic_read(&host->stuck_timeout) > 4)
- tasklet_schedule(&host->tasklet);
- } else {
- atomic_set(&host->stuck_timeout, 0);
-
- }
-
- mod_timer(&host->timer, jiffies + (HZ>>1));
-}
-
-static int __init imxmci_probe(struct platform_device *pdev)
-{
- struct mmc_host *mmc;
- struct imxmci_host *host = NULL;
- struct resource *r;
- int ret = 0, irq;
- u16 rev_no;
-
- pr_info("i.MX mmc driver\n");
-
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- irq = platform_get_irq(pdev, 0);
- if (!r || irq < 0)
- return -ENXIO;
-
- r = request_mem_region(r->start, resource_size(r), pdev->name);
- if (!r)
- return -EBUSY;
-
- mmc = mmc_alloc_host(sizeof(struct imxmci_host), &pdev->dev);
- if (!mmc) {
- ret = -ENOMEM;
- goto out;
- }
-
- mmc->ops = &imxmci_ops;
- mmc->f_min = 150000;
- mmc->f_max = CLK_RATE/2;
- mmc->ocr_avail = MMC_VDD_32_33;
- mmc->caps = MMC_CAP_4_BIT_DATA;
-
- /* MMC core transfer sizes tunable parameters */
- mmc->max_segs = 64;
- mmc->max_seg_size = 64*512; /* default PAGE_CACHE_SIZE */
- mmc->max_req_size = 64*512; /* default PAGE_CACHE_SIZE */
- mmc->max_blk_size = 2048;
- mmc->max_blk_count = 65535;
-
- host = mmc_priv(mmc);
- host->base = ioremap(r->start, resource_size(r));
- if (!host->base) {
- ret = -ENOMEM;
- goto out;
- }
-
- host->mmc = mmc;
- host->dma_allocated = 0;
- host->pdata = pdev->dev.platform_data;
- if (!host->pdata)
- dev_warn(&pdev->dev, "No platform data provided!\n");
-
- spin_lock_init(&host->lock);
- host->res = r;
- host->irq = irq;
-
- host->clk = clk_get(&pdev->dev, "perclk2");
- if (IS_ERR(host->clk)) {
- ret = PTR_ERR(host->clk);
- goto out;
- }
- clk_enable(host->clk);
-
- imx_gpio_mode(PB8_PF_SD_DAT0);
- imx_gpio_mode(PB9_PF_SD_DAT1);
- imx_gpio_mode(PB10_PF_SD_DAT2);
- /* Configured as GPIO with pull-up to ensure right MCC card mode */
- /* Switched to PB11_PF_SD_DAT3 if 4 bit bus is configured */
- imx_gpio_mode(GPIO_PORTB | GPIO_IN | GPIO_PUEN | 11);
- /* imx_gpio_mode(PB11_PF_SD_DAT3); */
- imx_gpio_mode(PB12_PF_SD_CLK);
- imx_gpio_mode(PB13_PF_SD_CMD);
-
- imxmci_softreset(host);
-
- rev_no = readw(host->base + MMC_REG_REV_NO);
- if (rev_no != 0x390) {
- dev_err(mmc_dev(host->mmc), "wrong rev.no. 0x%08x. aborting.\n",
- readw(host->base + MMC_REG_REV_NO));
- goto out;
- }
-
- /* recommended in data sheet */
- writew(0x2db4, host->base + MMC_REG_READ_TO);
-
- host->imask = IMXMCI_INT_MASK_DEFAULT;
- writew(host->imask, host->base + MMC_REG_INT_MASK);
-
- host->dma = imx_dma_request_by_prio(DRIVER_NAME, DMA_PRIO_LOW);
- if(host->dma < 0) {
- dev_err(mmc_dev(host->mmc), "imx_dma_request_by_prio failed\n");
- ret = -EBUSY;
- goto out;
- }
- host->dma_allocated = 1;
- imx_dma_setup_handlers(host->dma, imxmci_dma_irq, NULL, host);
- RSSR(host->dma) = DMA_REQ_SDHC;
-
- tasklet_init(&host->tasklet, imxmci_tasklet_fnc, (unsigned long)host);
- host->status_reg=0;
- host->pending_events=0;
-
- ret = request_irq(host->irq, imxmci_irq, 0, DRIVER_NAME, host);
- if (ret)
- goto out;
-
- if (host->pdata && host->pdata->card_present)
- host->present = host->pdata->card_present(mmc_dev(mmc));
- else /* if there is no way to detect assume that card is present */
- host->present = 1;
-
- init_timer(&host->timer);
- host->timer.data = (unsigned long)host;
- host->timer.function = imxmci_check_status;
- add_timer(&host->timer);
- mod_timer(&host->timer, jiffies + (HZ >> 1));
-
- platform_set_drvdata(pdev, mmc);
-
- mmc_add_host(mmc);
-
- return 0;
-
-out:
- if (host) {
- if (host->dma_allocated) {
- imx_dma_free(host->dma);
- host->dma_allocated = 0;
- }
- if (host->clk) {
- clk_disable(host->clk);
- clk_put(host->clk);
- }
- if (host->base)
- iounmap(host->base);
- }
- if (mmc)
- mmc_free_host(mmc);
- release_mem_region(r->start, resource_size(r));
- return ret;
-}
-
-static int __exit imxmci_remove(struct platform_device *pdev)
-{
- struct mmc_host *mmc = platform_get_drvdata(pdev);
-
- platform_set_drvdata(pdev, NULL);
-
- if (mmc) {
- struct imxmci_host *host = mmc_priv(mmc);
-
- tasklet_disable(&host->tasklet);
-
- del_timer_sync(&host->timer);
- mmc_remove_host(mmc);
-
- free_irq(host->irq, host);
- iounmap(host->base);
- if (host->dma_allocated) {
- imx_dma_free(host->dma);
- host->dma_allocated = 0;
- }
-
- tasklet_kill(&host->tasklet);
-
- clk_disable(host->clk);
- clk_put(host->clk);
-
- release_mem_region(host->res->start, resource_size(host->res));
-
- mmc_free_host(mmc);
- }
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int imxmci_suspend(struct platform_device *dev, pm_message_t state)
-{
- struct mmc_host *mmc = platform_get_drvdata(dev);
- int ret = 0;
-
- if (mmc)
- ret = mmc_suspend_host(mmc);
-
- return ret;
-}
-
-static int imxmci_resume(struct platform_device *dev)
-{
- struct mmc_host *mmc = platform_get_drvdata(dev);
- struct imxmci_host *host;
- int ret = 0;
-
- if (mmc) {
- host = mmc_priv(mmc);
- if (host)
- set_bit(IMXMCI_PEND_SET_INIT_b, &host->pending_events);
- ret = mmc_resume_host(mmc);
- }
-
- return ret;
-}
-#else
-#define imxmci_suspend NULL
-#define imxmci_resume NULL
-#endif /* CONFIG_PM */
-
-static struct platform_driver imxmci_driver = {
- .remove = __exit_p(imxmci_remove),
- .suspend = imxmci_suspend,
- .resume = imxmci_resume,
- .driver = {
- .name = DRIVER_NAME,
- .owner = THIS_MODULE,
- }
-};
-
-static int __init imxmci_init(void)
-{
- return platform_driver_probe(&imxmci_driver, imxmci_probe);
-}
-
-static void __exit imxmci_exit(void)
-{
- platform_driver_unregister(&imxmci_driver);
-}
-
-module_init(imxmci_init);
-module_exit(imxmci_exit);
-
-MODULE_DESCRIPTION("i.MX Multimedia Card Interface Driver");
-MODULE_AUTHOR("Sascha Hauer, Pengutronix");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:imx-mmc");
diff --git a/drivers/mmc/host/imxmmc.h b/drivers/mmc/host/imxmmc.h
deleted file mode 100644
index 09d5d4ee3a77..000000000000
--- a/drivers/mmc/host/imxmmc.h
+++ /dev/null
@@ -1,64 +0,0 @@
-#define MMC_REG_STR_STP_CLK 0x00
-#define MMC_REG_STATUS 0x04
-#define MMC_REG_CLK_RATE 0x08
-#define MMC_REG_CMD_DAT_CONT 0x0C
-#define MMC_REG_RES_TO 0x10
-#define MMC_REG_READ_TO 0x14
-#define MMC_REG_BLK_LEN 0x18
-#define MMC_REG_NOB 0x1C
-#define MMC_REG_REV_NO 0x20
-#define MMC_REG_INT_MASK 0x24
-#define MMC_REG_CMD 0x28
-#define MMC_REG_ARGH 0x2C
-#define MMC_REG_ARGL 0x30
-#define MMC_REG_RES_FIFO 0x34
-#define MMC_REG_BUFFER_ACCESS 0x38
-
-#define STR_STP_CLK_IPG_CLK_GATE_DIS (1<<15)
-#define STR_STP_CLK_IPG_PERCLK_GATE_DIS (1<<14)
-#define STR_STP_CLK_ENDIAN (1<<5)
-#define STR_STP_CLK_RESET (1<<3)
-#define STR_STP_CLK_ENABLE (1<<2)
-#define STR_STP_CLK_START_CLK (1<<1)
-#define STR_STP_CLK_STOP_CLK (1<<0)
-#define STATUS_CARD_PRESENCE (1<<15)
-#define STATUS_SDIO_INT_ACTIVE (1<<14)
-#define STATUS_END_CMD_RESP (1<<13)
-#define STATUS_WRITE_OP_DONE (1<<12)
-#define STATUS_DATA_TRANS_DONE (1<<11)
-#define STATUS_WR_CRC_ERROR_CODE_MASK (3<<10)
-#define STATUS_CARD_BUS_CLK_RUN (1<<8)
-#define STATUS_APPL_BUFF_FF (1<<7)
-#define STATUS_APPL_BUFF_FE (1<<6)
-#define STATUS_RESP_CRC_ERR (1<<5)
-#define STATUS_CRC_READ_ERR (1<<3)
-#define STATUS_CRC_WRITE_ERR (1<<2)
-#define STATUS_TIME_OUT_RESP (1<<1)
-#define STATUS_TIME_OUT_READ (1<<0)
-#define STATUS_ERR_MASK 0x2f
-#define CLK_RATE_PRESCALER(x) ((x) & 0x7)
-#define CLK_RATE_CLK_RATE(x) (((x) & 0x7) << 3)
-#define CMD_DAT_CONT_CMD_RESP_LONG_OFF (1<<12)
-#define CMD_DAT_CONT_STOP_READWAIT (1<<11)
-#define CMD_DAT_CONT_START_READWAIT (1<<10)
-#define CMD_DAT_CONT_BUS_WIDTH_1 (0<<8)
-#define CMD_DAT_CONT_BUS_WIDTH_4 (2<<8)
-#define CMD_DAT_CONT_INIT (1<<7)
-#define CMD_DAT_CONT_BUSY (1<<6)
-#define CMD_DAT_CONT_STREAM_BLOCK (1<<5)
-#define CMD_DAT_CONT_WRITE (1<<4)
-#define CMD_DAT_CONT_DATA_ENABLE (1<<3)
-#define CMD_DAT_CONT_RESPONSE_FORMAT_R1 (1)
-#define CMD_DAT_CONT_RESPONSE_FORMAT_R2 (2)
-#define CMD_DAT_CONT_RESPONSE_FORMAT_R3 (3)
-#define CMD_DAT_CONT_RESPONSE_FORMAT_R4 (4)
-#define CMD_DAT_CONT_RESPONSE_FORMAT_R5 (5)
-#define CMD_DAT_CONT_RESPONSE_FORMAT_R6 (6)
-#define INT_MASK_AUTO_CARD_DETECT (1<<6)
-#define INT_MASK_DAT0_EN (1<<5)
-#define INT_MASK_SDIO (1<<4)
-#define INT_MASK_BUF_READY (1<<3)
-#define INT_MASK_END_CMD_RES (1<<2)
-#define INT_MASK_WRITE_OP_DONE (1<<1)
-#define INT_MASK_DATA_TRAN (1<<0)
-#define INT_ALL (0x7f)
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index b6f38421d541..f0fcce40cd8d 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -15,6 +15,7 @@
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
+#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/highmem.h>
@@ -25,6 +26,7 @@
#include <linux/clk.h>
#include <linux/scatterlist.h>
#include <linux/gpio.h>
+#include <linux/of_gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
@@ -1207,21 +1209,76 @@ static const struct mmc_host_ops mmci_ops = {
.get_cd = mmci_get_cd,
};
+#ifdef CONFIG_OF
+static void mmci_dt_populate_generic_pdata(struct device_node *np,
+ struct mmci_platform_data *pdata)
+{
+ int bus_width = 0;
+
+ pdata->gpio_wp = of_get_named_gpio(np, "wp-gpios", 0);
+ if (!pdata->gpio_wp)
+ pdata->gpio_wp = -1;
+
+ pdata->gpio_cd = of_get_named_gpio(np, "cd-gpios", 0);
+ if (!pdata->gpio_cd)
+ pdata->gpio_cd = -1;
+
+ if (of_get_property(np, "cd-inverted", NULL))
+ pdata->cd_invert = true;
+ else
+ pdata->cd_invert = false;
+
+ of_property_read_u32(np, "max-frequency", &pdata->f_max);
+ if (!pdata->f_max)
+ pr_warn("%s has no 'max-frequency' property\n", np->full_name);
+
+ if (of_get_property(np, "mmc-cap-mmc-highspeed", NULL))
+ pdata->capabilities |= MMC_CAP_MMC_HIGHSPEED;
+ if (of_get_property(np, "mmc-cap-sd-highspeed", NULL))
+ pdata->capabilities |= MMC_CAP_SD_HIGHSPEED;
+
+ of_property_read_u32(np, "bus-width", &bus_width);
+ switch (bus_width) {
+ case 0 :
+ /* No bus-width supplied. */
+ break;
+ case 4 :
+ pdata->capabilities |= MMC_CAP_4_BIT_DATA;
+ break;
+ case 8 :
+ pdata->capabilities |= MMC_CAP_8_BIT_DATA;
+ break;
+ default :
+ pr_warn("%s: Unsupported bus width\n", np->full_name);
+ }
+}
+#else
+static void mmci_dt_populate_generic_pdata(struct device_node *np,
+ struct mmci_platform_data *pdata)
+{
+ return;
+}
+#endif
+
static int __devinit mmci_probe(struct amba_device *dev,
const struct amba_id *id)
{
struct mmci_platform_data *plat = dev->dev.platform_data;
+ struct device_node *np = dev->dev.of_node;
struct variant_data *variant = id->data;
struct mmci_host *host;
struct mmc_host *mmc;
int ret;
- /* must have platform data */
- if (!plat) {
- ret = -EINVAL;
- goto out;
+ /* Must have platform data or Device Tree. */
+ if (!plat && !np) {
+ dev_err(&dev->dev, "No plat data or DT found\n");
+ return -EINVAL;
}
+ if (np)
+ mmci_dt_populate_generic_pdata(np, plat);
+
ret = amba_request_regions(dev, DRIVER_NAME);
if (ret)
goto out;
diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c
index eeb8cd125b0c..3b9136c1a475 100644
--- a/drivers/mmc/host/mvsdio.c
+++ b/drivers/mmc/host/mvsdio.c
@@ -19,6 +19,7 @@
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
#include <linux/irq.h>
+#include <linux/clk.h>
#include <linux/gpio.h>
#include <linux/mmc/host.h>
@@ -51,6 +52,7 @@ struct mvsd_host {
struct device *dev;
struct resource *res;
int irq;
+ struct clk *clk;
int gpio_card_detect;
int gpio_write_protect;
};
@@ -770,6 +772,13 @@ static int __init mvsd_probe(struct platform_device *pdev)
} else
host->irq = irq;
+ /* Not all platforms can gate the clock, so it is not
+ an error if the clock does not exists. */
+ host->clk = clk_get(&pdev->dev, NULL);
+ if (!IS_ERR(host->clk)) {
+ clk_prepare_enable(host->clk);
+ }
+
if (mvsd_data->gpio_card_detect) {
ret = gpio_request(mvsd_data->gpio_card_detect,
DRIVER_NAME " cd");
@@ -854,6 +863,11 @@ static int __exit mvsd_remove(struct platform_device *pdev)
mvsd_power_down(host);
iounmap(host->base);
release_resource(host->res);
+
+ if (!IS_ERR(host->clk)) {
+ clk_disable_unprepare(host->clk);
+ clk_put(host->clk);
+ }
mmc_free_host(mmc);
}
platform_set_drvdata(pdev, NULL);
diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c
index b2058b432320..28ed52d58f7f 100644
--- a/drivers/mmc/host/mxcmmc.c
+++ b/drivers/mmc/host/mxcmmc.c
@@ -136,7 +136,8 @@ struct mxcmci_host {
u16 rev_no;
unsigned int cmdat;
- struct clk *clk;
+ struct clk *clk_ipg;
+ struct clk *clk_per;
int clock;
@@ -672,7 +673,7 @@ static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios)
{
unsigned int divider;
int prescaler = 0;
- unsigned int clk_in = clk_get_rate(host->clk);
+ unsigned int clk_in = clk_get_rate(host->clk_per);
while (prescaler <= 0x800) {
for (divider = 1; divider <= 0xF; divider++) {
@@ -900,12 +901,20 @@ static int mxcmci_probe(struct platform_device *pdev)
host->res = r;
host->irq = irq;
- host->clk = clk_get(&pdev->dev, NULL);
- if (IS_ERR(host->clk)) {
- ret = PTR_ERR(host->clk);
+ host->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
+ if (IS_ERR(host->clk_ipg)) {
+ ret = PTR_ERR(host->clk_ipg);
goto out_iounmap;
}
- clk_enable(host->clk);
+
+ host->clk_per = devm_clk_get(&pdev->dev, "per");
+ if (IS_ERR(host->clk_per)) {
+ ret = PTR_ERR(host->clk_per);
+ goto out_iounmap;
+ }
+
+ clk_prepare_enable(host->clk_per);
+ clk_prepare_enable(host->clk_ipg);
mxcmci_softreset(host);
@@ -917,8 +926,8 @@ static int mxcmci_probe(struct platform_device *pdev)
goto out_clk_put;
}
- mmc->f_min = clk_get_rate(host->clk) >> 16;
- mmc->f_max = clk_get_rate(host->clk) >> 1;
+ mmc->f_min = clk_get_rate(host->clk_per) >> 16;
+ mmc->f_max = clk_get_rate(host->clk_per) >> 1;
/* recommended in data sheet */
writew(0x2db4, host->base + MMC_REG_READ_TO);
@@ -967,8 +976,8 @@ out_free_dma:
if (host->dma)
dma_release_channel(host->dma);
out_clk_put:
- clk_disable(host->clk);
- clk_put(host->clk);
+ clk_disable_unprepare(host->clk_per);
+ clk_disable_unprepare(host->clk_ipg);
out_iounmap:
iounmap(host->base);
out_free:
@@ -999,8 +1008,8 @@ static int mxcmci_remove(struct platform_device *pdev)
if (host->dma)
dma_release_channel(host->dma);
- clk_disable(host->clk);
- clk_put(host->clk);
+ clk_disable_unprepare(host->clk_per);
+ clk_disable_unprepare(host->clk_ipg);
release_mem_region(host->res->start, resource_size(host->res));
@@ -1018,7 +1027,8 @@ static int mxcmci_suspend(struct device *dev)
if (mmc)
ret = mmc_suspend_host(mmc);
- clk_disable(host->clk);
+ clk_disable_unprepare(host->clk_per);
+ clk_disable_unprepare(host->clk_ipg);
return ret;
}
@@ -1029,7 +1039,8 @@ static int mxcmci_resume(struct device *dev)
struct mxcmci_host *host = mmc_priv(mmc);
int ret = 0;
- clk_enable(host->clk);
+ clk_prepare_enable(host->clk_per);
+ clk_prepare_enable(host->clk_ipg);
if (mmc)
ret = mmc_resume_host(mmc);
diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c
index bb03ddda481d..34a90266ab11 100644
--- a/drivers/mmc/host/mxs-mmc.c
+++ b/drivers/mmc/host/mxs-mmc.c
@@ -23,6 +23,9 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/ioport.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -40,18 +43,15 @@
#include <linux/module.h>
#include <linux/fsl/mxs-dma.h>
#include <linux/pinctrl/consumer.h>
-
-#include <mach/mxs.h>
-#include <mach/common.h>
-#include <mach/mmc.h>
+#include <linux/stmp_device.h>
+#include <linux/mmc/mxs-mmc.h>
#define DRIVER_NAME "mxs-mmc"
/* card detect polling timeout */
#define MXS_MMC_DETECT_TIMEOUT (HZ/2)
-#define SSP_VERSION_LATEST 4
-#define ssp_is_old() (host->version < SSP_VERSION_LATEST)
+#define ssp_is_old(host) ((host)->devid == IMX23_MMC)
/* SSP registers */
#define HW_SSP_CTRL0 0x000
@@ -86,14 +86,14 @@
#define BM_SSP_BLOCK_SIZE_BLOCK_COUNT (0xffffff << 4)
#define BP_SSP_BLOCK_SIZE_BLOCK_SIZE (0)
#define BM_SSP_BLOCK_SIZE_BLOCK_SIZE (0xf)
-#define HW_SSP_TIMING (ssp_is_old() ? 0x050 : 0x070)
+#define HW_SSP_TIMING(h) (ssp_is_old(h) ? 0x050 : 0x070)
#define BP_SSP_TIMING_TIMEOUT (16)
#define BM_SSP_TIMING_TIMEOUT (0xffff << 16)
#define BP_SSP_TIMING_CLOCK_DIVIDE (8)
#define BM_SSP_TIMING_CLOCK_DIVIDE (0xff << 8)
#define BP_SSP_TIMING_CLOCK_RATE (0)
#define BM_SSP_TIMING_CLOCK_RATE (0xff)
-#define HW_SSP_CTRL1 (ssp_is_old() ? 0x060 : 0x080)
+#define HW_SSP_CTRL1(h) (ssp_is_old(h) ? 0x060 : 0x080)
#define BM_SSP_CTRL1_SDIO_IRQ (1 << 31)
#define BM_SSP_CTRL1_SDIO_IRQ_EN (1 << 30)
#define BM_SSP_CTRL1_RESP_ERR_IRQ (1 << 29)
@@ -116,15 +116,13 @@
#define BM_SSP_CTRL1_WORD_LENGTH (0xf << 4)
#define BP_SSP_CTRL1_SSP_MODE (0)
#define BM_SSP_CTRL1_SSP_MODE (0xf)
-#define HW_SSP_SDRESP0 (ssp_is_old() ? 0x080 : 0x0a0)
-#define HW_SSP_SDRESP1 (ssp_is_old() ? 0x090 : 0x0b0)
-#define HW_SSP_SDRESP2 (ssp_is_old() ? 0x0a0 : 0x0c0)
-#define HW_SSP_SDRESP3 (ssp_is_old() ? 0x0b0 : 0x0d0)
-#define HW_SSP_STATUS (ssp_is_old() ? 0x0c0 : 0x100)
+#define HW_SSP_SDRESP0(h) (ssp_is_old(h) ? 0x080 : 0x0a0)
+#define HW_SSP_SDRESP1(h) (ssp_is_old(h) ? 0x090 : 0x0b0)
+#define HW_SSP_SDRESP2(h) (ssp_is_old(h) ? 0x0a0 : 0x0c0)
+#define HW_SSP_SDRESP3(h) (ssp_is_old(h) ? 0x0b0 : 0x0d0)
+#define HW_SSP_STATUS(h) (ssp_is_old(h) ? 0x0c0 : 0x100)
#define BM_SSP_STATUS_CARD_DETECT (1 << 28)
#define BM_SSP_STATUS_SDIO_IRQ (1 << 17)
-#define HW_SSP_VERSION (cpu_is_mx23() ? 0x110 : 0x130)
-#define BP_SSP_VERSION_MAJOR (24)
#define BF_SSP(value, field) (((value) << BP_SSP_##field) & BM_SSP_##field)
@@ -139,6 +137,11 @@
#define SSP_PIO_NUM 3
+enum mxs_mmc_id {
+ IMX23_MMC,
+ IMX28_MMC,
+};
+
struct mxs_mmc_host {
struct mmc_host *mmc;
struct mmc_request *mrq;
@@ -146,9 +149,7 @@ struct mxs_mmc_host {
struct mmc_data *data;
void __iomem *base;
- int irq;
- struct resource *res;
- struct resource *dma_res;
+ int dma_channel;
struct clk *clk;
unsigned int clk_rate;
@@ -158,32 +159,28 @@ struct mxs_mmc_host {
enum dma_transfer_direction slave_dirn;
u32 ssp_pio_words[SSP_PIO_NUM];
- unsigned int version;
+ enum mxs_mmc_id devid;
unsigned char bus_width;
spinlock_t lock;
int sdio_irq_en;
+ int wp_gpio;
};
static int mxs_mmc_get_ro(struct mmc_host *mmc)
{
struct mxs_mmc_host *host = mmc_priv(mmc);
- struct mxs_mmc_platform_data *pdata =
- mmc_dev(host->mmc)->platform_data;
-
- if (!pdata)
- return -EFAULT;
- if (!gpio_is_valid(pdata->wp_gpio))
+ if (!gpio_is_valid(host->wp_gpio))
return -EINVAL;
- return gpio_get_value(pdata->wp_gpio);
+ return gpio_get_value(host->wp_gpio);
}
static int mxs_mmc_get_cd(struct mmc_host *mmc)
{
struct mxs_mmc_host *host = mmc_priv(mmc);
- return !(readl(host->base + HW_SSP_STATUS) &
+ return !(readl(host->base + HW_SSP_STATUS(host)) &
BM_SSP_STATUS_CARD_DETECT);
}
@@ -191,7 +188,7 @@ static void mxs_mmc_reset(struct mxs_mmc_host *host)
{
u32 ctrl0, ctrl1;
- mxs_reset_block(host->base);
+ stmp_reset_block(host->base);
ctrl0 = BM_SSP_CTRL0_IGNORE_CRC;
ctrl1 = BF_SSP(0x3, CTRL1_SSP_MODE) |
@@ -207,7 +204,7 @@ static void mxs_mmc_reset(struct mxs_mmc_host *host)
writel(BF_SSP(0xffff, TIMING_TIMEOUT) |
BF_SSP(2, TIMING_CLOCK_DIVIDE) |
BF_SSP(0, TIMING_CLOCK_RATE),
- host->base + HW_SSP_TIMING);
+ host->base + HW_SSP_TIMING(host));
if (host->sdio_irq_en) {
ctrl0 |= BM_SSP_CTRL0_SDIO_IRQ_CHECK;
@@ -215,7 +212,7 @@ static void mxs_mmc_reset(struct mxs_mmc_host *host)
}
writel(ctrl0, host->base + HW_SSP_CTRL0);
- writel(ctrl1, host->base + HW_SSP_CTRL1);
+ writel(ctrl1, host->base + HW_SSP_CTRL1(host));
}
static void mxs_mmc_start_cmd(struct mxs_mmc_host *host,
@@ -229,12 +226,12 @@ static void mxs_mmc_request_done(struct mxs_mmc_host *host)
if (mmc_resp_type(cmd) & MMC_RSP_PRESENT) {
if (mmc_resp_type(cmd) & MMC_RSP_136) {
- cmd->resp[3] = readl(host->base + HW_SSP_SDRESP0);
- cmd->resp[2] = readl(host->base + HW_SSP_SDRESP1);
- cmd->resp[1] = readl(host->base + HW_SSP_SDRESP2);
- cmd->resp[0] = readl(host->base + HW_SSP_SDRESP3);
+ cmd->resp[3] = readl(host->base + HW_SSP_SDRESP0(host));
+ cmd->resp[2] = readl(host->base + HW_SSP_SDRESP1(host));
+ cmd->resp[1] = readl(host->base + HW_SSP_SDRESP2(host));
+ cmd->resp[0] = readl(host->base + HW_SSP_SDRESP3(host));
} else {
- cmd->resp[0] = readl(host->base + HW_SSP_SDRESP0);
+ cmd->resp[0] = readl(host->base + HW_SSP_SDRESP0(host));
}
}
@@ -277,9 +274,9 @@ static irqreturn_t mxs_mmc_irq_handler(int irq, void *dev_id)
spin_lock(&host->lock);
- stat = readl(host->base + HW_SSP_CTRL1);
+ stat = readl(host->base + HW_SSP_CTRL1(host));
writel(stat & MXS_MMC_IRQ_BITS,
- host->base + HW_SSP_CTRL1 + MXS_CLR_ADDR);
+ host->base + HW_SSP_CTRL1(host) + STMP_OFFSET_REG_CLR);
if ((stat & BM_SSP_CTRL1_SDIO_IRQ) && (stat & BM_SSP_CTRL1_SDIO_IRQ_EN))
mmc_signal_sdio_irq(host->mmc);
@@ -485,7 +482,7 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host)
blocks = 1;
/* xfer count, block size and count need to be set differently */
- if (ssp_is_old()) {
+ if (ssp_is_old(host)) {
ctrl0 |= BF_SSP(data_size, CTRL0_XFER_COUNT);
cmd0 |= BF_SSP(log2_blksz, CMD0_BLOCK_SIZE) |
BF_SSP(blocks - 1, CMD0_BLOCK_COUNT);
@@ -509,10 +506,10 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host)
/* set the timeout count */
timeout = mxs_ns_to_ssp_ticks(host->clk_rate, data->timeout_ns);
- val = readl(host->base + HW_SSP_TIMING);
+ val = readl(host->base + HW_SSP_TIMING(host));
val &= ~(BM_SSP_TIMING_TIMEOUT);
val |= BF_SSP(timeout, TIMING_TIMEOUT);
- writel(val, host->base + HW_SSP_TIMING);
+ writel(val, host->base + HW_SSP_TIMING(host));
/* pio */
host->ssp_pio_words[0] = ctrl0;
@@ -598,11 +595,11 @@ static void mxs_mmc_set_clk_rate(struct mxs_mmc_host *host, unsigned int rate)
ssp_sck = ssp_clk / clock_divide / (1 + clock_rate);
- val = readl(host->base + HW_SSP_TIMING);
+ val = readl(host->base + HW_SSP_TIMING(host));
val &= ~(BM_SSP_TIMING_CLOCK_DIVIDE | BM_SSP_TIMING_CLOCK_RATE);
val |= BF_SSP(clock_divide, TIMING_CLOCK_DIVIDE);
val |= BF_SSP(clock_rate, TIMING_CLOCK_RATE);
- writel(val, host->base + HW_SSP_TIMING);
+ writel(val, host->base + HW_SSP_TIMING(host));
host->clk_rate = ssp_sck;
@@ -637,18 +634,19 @@ static void mxs_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
if (enable) {
writel(BM_SSP_CTRL0_SDIO_IRQ_CHECK,
- host->base + HW_SSP_CTRL0 + MXS_SET_ADDR);
+ host->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
writel(BM_SSP_CTRL1_SDIO_IRQ_EN,
- host->base + HW_SSP_CTRL1 + MXS_SET_ADDR);
+ host->base + HW_SSP_CTRL1(host) + STMP_OFFSET_REG_SET);
- if (readl(host->base + HW_SSP_STATUS) & BM_SSP_STATUS_SDIO_IRQ)
+ if (readl(host->base + HW_SSP_STATUS(host)) &
+ BM_SSP_STATUS_SDIO_IRQ)
mmc_signal_sdio_irq(host->mmc);
} else {
writel(BM_SSP_CTRL0_SDIO_IRQ_CHECK,
- host->base + HW_SSP_CTRL0 + MXS_CLR_ADDR);
+ host->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR);
writel(BM_SSP_CTRL1_SDIO_IRQ_EN,
- host->base + HW_SSP_CTRL1 + MXS_CLR_ADDR);
+ host->base + HW_SSP_CTRL1(host) + STMP_OFFSET_REG_CLR);
}
spin_unlock_irqrestore(&host->lock, flags);
@@ -669,7 +667,7 @@ static bool mxs_mmc_dma_filter(struct dma_chan *chan, void *param)
if (!mxs_dma_is_apbh(chan))
return false;
- if (chan->chan_id != host->dma_res->start)
+ if (chan->chan_id != host->dma_channel)
return false;
chan->private = &host->dma_data;
@@ -677,11 +675,34 @@ static bool mxs_mmc_dma_filter(struct dma_chan *chan, void *param)
return true;
}
+static struct platform_device_id mxs_mmc_ids[] = {
+ {
+ .name = "imx23-mmc",
+ .driver_data = IMX23_MMC,
+ }, {
+ .name = "imx28-mmc",
+ .driver_data = IMX28_MMC,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(platform, mxs_mmc_ids);
+
+static const struct of_device_id mxs_mmc_dt_ids[] = {
+ { .compatible = "fsl,imx23-mmc", .data = (void *) IMX23_MMC, },
+ { .compatible = "fsl,imx28-mmc", .data = (void *) IMX28_MMC, },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mxs_mmc_dt_ids);
+
static int mxs_mmc_probe(struct platform_device *pdev)
{
+ const struct of_device_id *of_id =
+ of_match_device(mxs_mmc_dt_ids, &pdev->dev);
+ struct device_node *np = pdev->dev.of_node;
struct mxs_mmc_host *host;
struct mmc_host *mmc;
- struct resource *iores, *dmares, *r;
+ struct resource *iores, *dmares;
struct mxs_mmc_platform_data *pdata;
struct pinctrl *pinctrl;
int ret = 0, irq_err, irq_dma;
@@ -691,46 +712,51 @@ static int mxs_mmc_probe(struct platform_device *pdev)
dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
irq_err = platform_get_irq(pdev, 0);
irq_dma = platform_get_irq(pdev, 1);
- if (!iores || !dmares || irq_err < 0 || irq_dma < 0)
+ if (!iores || irq_err < 0 || irq_dma < 0)
return -EINVAL;
- r = request_mem_region(iores->start, resource_size(iores), pdev->name);
- if (!r)
- return -EBUSY;
-
mmc = mmc_alloc_host(sizeof(struct mxs_mmc_host), &pdev->dev);
- if (!mmc) {
- ret = -ENOMEM;
- goto out_release_mem;
- }
+ if (!mmc)
+ return -ENOMEM;
host = mmc_priv(mmc);
- host->base = ioremap(r->start, resource_size(r));
+ host->base = devm_request_and_ioremap(&pdev->dev, iores);
if (!host->base) {
- ret = -ENOMEM;
+ ret = -EADDRNOTAVAIL;
goto out_mmc_free;
}
- /* only major verion does matter */
- host->version = readl(host->base + HW_SSP_VERSION) >>
- BP_SSP_VERSION_MAJOR;
+ if (np) {
+ host->devid = (enum mxs_mmc_id) of_id->data;
+ /*
+ * TODO: This is a temporary solution and should be changed
+ * to use generic DMA binding later when the helpers get in.
+ */
+ ret = of_property_read_u32(np, "fsl,ssp-dma-channel",
+ &host->dma_channel);
+ if (ret) {
+ dev_err(mmc_dev(host->mmc),
+ "failed to get dma channel\n");
+ goto out_mmc_free;
+ }
+ } else {
+ host->devid = pdev->id_entry->driver_data;
+ host->dma_channel = dmares->start;
+ }
host->mmc = mmc;
- host->res = r;
- host->dma_res = dmares;
- host->irq = irq_err;
host->sdio_irq_en = 0;
pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
if (IS_ERR(pinctrl)) {
ret = PTR_ERR(pinctrl);
- goto out_iounmap;
+ goto out_mmc_free;
}
host->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(host->clk)) {
ret = PTR_ERR(host->clk);
- goto out_iounmap;
+ goto out_mmc_free;
}
clk_prepare_enable(host->clk);
@@ -752,11 +778,20 @@ static int mxs_mmc_probe(struct platform_device *pdev)
MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL;
pdata = mmc_dev(host->mmc)->platform_data;
- if (pdata) {
+ if (!pdata) {
+ u32 bus_width = 0;
+ of_property_read_u32(np, "bus-width", &bus_width);
+ if (bus_width == 4)
+ mmc->caps |= MMC_CAP_4_BIT_DATA;
+ else if (bus_width == 8)
+ mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA;
+ host->wp_gpio = of_get_named_gpio(np, "wp-gpios", 0);
+ } else {
if (pdata->flags & SLOTF_8_BIT_CAPABLE)
mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA;
if (pdata->flags & SLOTF_4_BIT_CAPABLE)
mmc->caps |= MMC_CAP_4_BIT_DATA;
+ host->wp_gpio = pdata->wp_gpio;
}
mmc->f_min = 400000;
@@ -765,13 +800,14 @@ static int mxs_mmc_probe(struct platform_device *pdev)
mmc->max_segs = 52;
mmc->max_blk_size = 1 << 0xf;
- mmc->max_blk_count = (ssp_is_old()) ? 0xff : 0xffffff;
- mmc->max_req_size = (ssp_is_old()) ? 0xffff : 0xffffffff;
+ mmc->max_blk_count = (ssp_is_old(host)) ? 0xff : 0xffffff;
+ mmc->max_req_size = (ssp_is_old(host)) ? 0xffff : 0xffffffff;
mmc->max_seg_size = dma_get_max_seg_size(host->dmach->device->dev);
platform_set_drvdata(pdev, mmc);
- ret = request_irq(host->irq, mxs_mmc_irq_handler, 0, DRIVER_NAME, host);
+ ret = devm_request_irq(&pdev->dev, irq_err, mxs_mmc_irq_handler, 0,
+ DRIVER_NAME, host);
if (ret)
goto out_free_dma;
@@ -779,26 +815,20 @@ static int mxs_mmc_probe(struct platform_device *pdev)
ret = mmc_add_host(mmc);
if (ret)
- goto out_free_irq;
+ goto out_free_dma;
dev_info(mmc_dev(host->mmc), "initialized\n");
return 0;
-out_free_irq:
- free_irq(host->irq, host);
out_free_dma:
if (host->dmach)
dma_release_channel(host->dmach);
out_clk_put:
clk_disable_unprepare(host->clk);
clk_put(host->clk);
-out_iounmap:
- iounmap(host->base);
out_mmc_free:
mmc_free_host(mmc);
-out_release_mem:
- release_mem_region(iores->start, resource_size(iores));
return ret;
}
@@ -806,12 +836,9 @@ static int mxs_mmc_remove(struct platform_device *pdev)
{
struct mmc_host *mmc = platform_get_drvdata(pdev);
struct mxs_mmc_host *host = mmc_priv(mmc);
- struct resource *res = host->res;
mmc_remove_host(mmc);
- free_irq(host->irq, host);
-
platform_set_drvdata(pdev, NULL);
if (host->dmach)
@@ -820,12 +847,8 @@ static int mxs_mmc_remove(struct platform_device *pdev)
clk_disable_unprepare(host->clk);
clk_put(host->clk);
- iounmap(host->base);
-
mmc_free_host(mmc);
- release_mem_region(res->start, resource_size(res));
-
return 0;
}
@@ -865,11 +888,13 @@ static const struct dev_pm_ops mxs_mmc_pm_ops = {
static struct platform_driver mxs_mmc_driver = {
.probe = mxs_mmc_probe,
.remove = mxs_mmc_remove,
+ .id_table = mxs_mmc_ids,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
#ifdef CONFIG_PM
.pm = &mxs_mmc_pm_ops,
+ .of_match_table = mxs_mmc_dt_ids,
#endif
},
};
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c
index 887c0e598cf3..552196c764d4 100644
--- a/drivers/mmc/host/omap.c
+++ b/drivers/mmc/host/omap.c
@@ -169,11 +169,11 @@ struct mmc_omap_host {
struct timer_list clk_timer;
spinlock_t clk_lock; /* for changing enabled state */
unsigned int fclk_enabled:1;
+ struct workqueue_struct *mmc_omap_wq;
struct omap_mmc_platform_data *pdata;
};
-static struct workqueue_struct *mmc_omap_wq;
static void mmc_omap_fclk_offdelay(struct mmc_omap_slot *slot)
{
@@ -291,7 +291,7 @@ static void mmc_omap_release_slot(struct mmc_omap_slot *slot, int clk_enabled)
host->next_slot = new_slot;
host->mmc = new_slot->mmc;
spin_unlock_irqrestore(&host->slot_lock, flags);
- queue_work(mmc_omap_wq, &host->slot_release_work);
+ queue_work(host->mmc_omap_wq, &host->slot_release_work);
return;
}
@@ -459,7 +459,7 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
}
host->stop_data = data;
- queue_work(mmc_omap_wq, &host->send_stop_work);
+ queue_work(host->mmc_omap_wq, &host->send_stop_work);
}
static void
@@ -639,7 +639,7 @@ mmc_omap_cmd_timer(unsigned long data)
OMAP_MMC_WRITE(host, IE, 0);
disable_irq(host->irq);
host->abort = 1;
- queue_work(mmc_omap_wq, &host->cmd_abort_work);
+ queue_work(host->mmc_omap_wq, &host->cmd_abort_work);
}
spin_unlock_irqrestore(&host->slot_lock, flags);
}
@@ -828,7 +828,7 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
host->abort = 1;
OMAP_MMC_WRITE(host, IE, 0);
disable_irq_nosync(host->irq);
- queue_work(mmc_omap_wq, &host->cmd_abort_work);
+ queue_work(host->mmc_omap_wq, &host->cmd_abort_work);
return IRQ_HANDLED;
}
@@ -1389,13 +1389,13 @@ static void mmc_omap_remove_slot(struct mmc_omap_slot *slot)
tasklet_kill(&slot->cover_tasklet);
del_timer_sync(&slot->cover_timer);
- flush_workqueue(mmc_omap_wq);
+ flush_workqueue(slot->host->mmc_omap_wq);
mmc_remove_host(mmc);
mmc_free_host(mmc);
}
-static int __init mmc_omap_probe(struct platform_device *pdev)
+static int __devinit mmc_omap_probe(struct platform_device *pdev)
{
struct omap_mmc_platform_data *pdata = pdev->dev.platform_data;
struct mmc_omap_host *host = NULL;
@@ -1497,6 +1497,10 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
host->reg_shift = (cpu_is_omap7xx() ? 1 : 2);
+ host->mmc_omap_wq = alloc_workqueue("mmc_omap", 0, 0);
+ if (!host->mmc_omap_wq)
+ goto err_plat_cleanup;
+
return 0;
err_plat_cleanup:
@@ -1518,7 +1522,7 @@ err_free_mem_region:
return ret;
}
-static int mmc_omap_remove(struct platform_device *pdev)
+static int __devexit mmc_omap_remove(struct platform_device *pdev)
{
struct mmc_omap_host *host = platform_get_drvdata(pdev);
int i;
@@ -1542,6 +1546,7 @@ static int mmc_omap_remove(struct platform_device *pdev)
iounmap(host->virt_base);
release_mem_region(pdev->resource[0].start,
pdev->resource[0].end - pdev->resource[0].start + 1);
+ destroy_workqueue(host->mmc_omap_wq);
kfree(host);
@@ -1599,7 +1604,8 @@ static int mmc_omap_resume(struct platform_device *pdev)
#endif
static struct platform_driver mmc_omap_driver = {
- .remove = mmc_omap_remove,
+ .probe = mmc_omap_probe,
+ .remove = __devexit_p(mmc_omap_remove),
.suspend = mmc_omap_suspend,
.resume = mmc_omap_resume,
.driver = {
@@ -1608,29 +1614,7 @@ static struct platform_driver mmc_omap_driver = {
},
};
-static int __init mmc_omap_init(void)
-{
- int ret;
-
- mmc_omap_wq = alloc_workqueue("mmc_omap", 0, 0);
- if (!mmc_omap_wq)
- return -ENOMEM;
-
- ret = platform_driver_probe(&mmc_omap_driver, mmc_omap_probe);
- if (ret)
- destroy_workqueue(mmc_omap_wq);
- return ret;
-}
-
-static void __exit mmc_omap_exit(void)
-{
- platform_driver_unregister(&mmc_omap_driver);
- destroy_workqueue(mmc_omap_wq);
-}
-
-module_init(mmc_omap_init);
-module_exit(mmc_omap_exit);
-
+module_platform_driver(mmc_omap_driver);
MODULE_DESCRIPTION("OMAP Multimedia Card driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 56d4499d4388..9a7a60aeb19e 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -85,12 +85,14 @@
#define BRR_ENABLE (1 << 5)
#define DTO_ENABLE (1 << 20)
#define INIT_STREAM (1 << 1)
+#define ACEN_ACMD12 (1 << 2)
#define DP_SELECT (1 << 21)
#define DDIR (1 << 4)
#define DMA_EN 0x1
#define MSBS (1 << 5)
#define BCE (1 << 1)
#define FOUR_BIT (1 << 1)
+#define DDR (1 << 19)
#define DW8 (1 << 5)
#define CC 0x1
#define TC 0x02
@@ -115,6 +117,7 @@
#define OMAP_MMC_MAX_CLOCK 52000000
#define DRIVER_NAME "omap_hsmmc"
+#define AUTO_CMD12 (1 << 0) /* Auto CMD12 support */
/*
* One controller can have multiple slots, like on some omap boards using
* omap.c controller driver. Luckily this is not currently done on any known
@@ -167,7 +170,6 @@ struct omap_hsmmc_host {
int use_dma, dma_ch;
int dma_line_tx, dma_line_rx;
int slot_id;
- int got_dbclk;
int response_busy;
int context_loss;
int vdd;
@@ -175,6 +177,7 @@ struct omap_hsmmc_host {
int reqs_blocked;
int use_reg;
int req_in_progress;
+ unsigned int flags;
struct omap_hsmmc_next next_data;
struct omap_mmc_platform_data *pdata;
@@ -520,6 +523,10 @@ static void omap_hsmmc_set_bus_width(struct omap_hsmmc_host *host)
u32 con;
con = OMAP_HSMMC_READ(host->base, CON);
+ if (ios->timing == MMC_TIMING_UHS_DDR50)
+ con |= DDR; /* configure in DDR mode */
+ else
+ con &= ~DDR;
switch (ios->bus_width) {
case MMC_BUS_WIDTH_8:
OMAP_HSMMC_WRITE(host->base, CON, con | DW8);
@@ -766,6 +773,8 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,
cmdtype = 0x3;
cmdreg = (cmd->opcode << 24) | (resptype << 16) | (cmdtype << 22);
+ if ((host->flags & AUTO_CMD12) && mmc_op_multi(cmd->opcode))
+ cmdreg |= ACEN_ACMD12;
if (data) {
cmdreg |= DP_SELECT | MSBS | BCE;
@@ -796,11 +805,12 @@ omap_hsmmc_get_dma_dir(struct omap_hsmmc_host *host, struct mmc_data *data)
static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_request *mrq)
{
int dma_ch;
+ unsigned long flags;
- spin_lock(&host->irq_lock);
+ spin_lock_irqsave(&host->irq_lock, flags);
host->req_in_progress = 0;
dma_ch = host->dma_ch;
- spin_unlock(&host->irq_lock);
+ spin_unlock_irqrestore(&host->irq_lock, flags);
omap_hsmmc_disable_irq(host);
/* Do not complete the request if DMA is still in progress */
@@ -837,11 +847,14 @@ omap_hsmmc_xfer_done(struct omap_hsmmc_host *host, struct mmc_data *data)
else
data->bytes_xfered = 0;
- if (!data->stop) {
+ if (data->stop && ((!(host->flags & AUTO_CMD12)) || data->error)) {
+ omap_hsmmc_start_command(host, data->stop, NULL);
+ } else {
+ if (data->stop)
+ data->stop->resp[0] = OMAP_HSMMC_READ(host->base,
+ RSP76);
omap_hsmmc_request_done(host, data->mrq);
- return;
}
- omap_hsmmc_start_command(host, data->stop, NULL);
}
/*
@@ -874,13 +887,14 @@ omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)
static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno)
{
int dma_ch;
+ unsigned long flags;
host->data->error = errno;
- spin_lock(&host->irq_lock);
+ spin_lock_irqsave(&host->irq_lock, flags);
dma_ch = host->dma_ch;
host->dma_ch = -1;
- spin_unlock(&host->irq_lock);
+ spin_unlock_irqrestore(&host->irq_lock, flags);
if (host->use_dma && dma_ch != -1) {
dma_unmap_sg(mmc_dev(host->mmc), host->data->sg,
@@ -1082,7 +1096,7 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd)
/* Disable the clocks */
pm_runtime_put_sync(host->dev);
- if (host->got_dbclk)
+ if (host->dbclk)
clk_disable(host->dbclk);
/* Turn the power off */
@@ -1093,7 +1107,7 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd)
ret = mmc_slot(host).set_power(host->dev, host->slot_id, 1,
vdd);
pm_runtime_get_sync(host->dev);
- if (host->got_dbclk)
+ if (host->dbclk)
clk_enable(host->dbclk);
if (ret != 0)
@@ -1234,6 +1248,7 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
struct omap_hsmmc_host *host = cb_data;
struct mmc_data *data;
int dma_ch, req_in_progress;
+ unsigned long flags;
if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) {
dev_warn(mmc_dev(host->mmc), "unexpected dma status %x\n",
@@ -1241,9 +1256,9 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
return;
}
- spin_lock(&host->irq_lock);
+ spin_lock_irqsave(&host->irq_lock, flags);
if (host->dma_ch < 0) {
- spin_unlock(&host->irq_lock);
+ spin_unlock_irqrestore(&host->irq_lock, flags);
return;
}
@@ -1253,7 +1268,7 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
/* Fire up the next transfer. */
omap_hsmmc_config_dma_params(host, data,
data->sg + host->dma_sg_idx);
- spin_unlock(&host->irq_lock);
+ spin_unlock_irqrestore(&host->irq_lock, flags);
return;
}
@@ -1264,7 +1279,7 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
req_in_progress = host->req_in_progress;
dma_ch = host->dma_ch;
host->dma_ch = -1;
- spin_unlock(&host->irq_lock);
+ spin_unlock_irqrestore(&host->irq_lock, flags);
omap_free_dma(dma_ch);
@@ -1766,7 +1781,7 @@ static struct omap_mmc_platform_data *of_get_hsmmc_pdata(struct device *dev)
pdata->slots[0].nonremovable = true;
pdata->slots[0].no_regulator_off_init = true;
}
- of_property_read_u32(np, "ti,bus-width", &bus_width);
+ of_property_read_u32(np, "bus-width", &bus_width);
if (bus_width == 4)
pdata->slots[0].caps |= MMC_CAP_4_BIT_DATA;
else if (bus_width == 8)
@@ -1844,6 +1859,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
host->mapbase = res->start + pdata->reg_offset;
host->base = ioremap(host->mapbase, SZ_4K);
host->power_mode = MMC_POWER_OFF;
+ host->flags = AUTO_CMD12;
host->next_data.cookie = 1;
platform_set_drvdata(pdev, host);
@@ -1885,21 +1901,17 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
omap_hsmmc_context_save(host);
- if (cpu_is_omap2430()) {
- host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck");
- /*
- * MMC can still work without debounce clock.
- */
- if (IS_ERR(host->dbclk))
- dev_warn(mmc_dev(host->mmc),
- "Failed to get debounce clock\n");
- else
- host->got_dbclk = 1;
-
- if (host->got_dbclk)
- if (clk_enable(host->dbclk) != 0)
- dev_dbg(mmc_dev(host->mmc), "Enabling debounce"
- " clk failed\n");
+ host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck");
+ /*
+ * MMC can still work without debounce clock.
+ */
+ if (IS_ERR(host->dbclk)) {
+ dev_warn(mmc_dev(host->mmc), "Failed to get debounce clk\n");
+ host->dbclk = NULL;
+ } else if (clk_enable(host->dbclk) != 0) {
+ dev_warn(mmc_dev(host->mmc), "Failed to enable debounce clk\n");
+ clk_put(host->dbclk);
+ host->dbclk = NULL;
}
/* Since we do only SG emulation, we can have as many segs
@@ -1969,7 +1981,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
ret = request_threaded_irq(mmc_slot(host).card_detect_irq,
NULL,
omap_hsmmc_detect,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
mmc_hostname(mmc), host);
if (ret) {
dev_dbg(mmc_dev(host->mmc),
@@ -2019,7 +2031,7 @@ err_irq:
pm_runtime_put_sync(host->dev);
pm_runtime_disable(host->dev);
clk_put(host->fclk);
- if (host->got_dbclk) {
+ if (host->dbclk) {
clk_disable(host->dbclk);
clk_put(host->dbclk);
}
@@ -2030,7 +2042,9 @@ err1:
err_alloc:
omap_hsmmc_gpio_free(pdata);
err:
- release_mem_region(res->start, resource_size(res));
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res)
+ release_mem_region(res->start, resource_size(res));
return ret;
}
@@ -2052,7 +2066,7 @@ static int __devexit omap_hsmmc_remove(struct platform_device *pdev)
pm_runtime_put_sync(host->dev);
pm_runtime_disable(host->dev);
clk_put(host->fclk);
- if (host->got_dbclk) {
+ if (host->dbclk) {
clk_disable(host->dbclk);
clk_put(host->dbclk);
}
@@ -2110,7 +2124,7 @@ static int omap_hsmmc_suspend(struct device *dev)
OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
}
- if (host->got_dbclk)
+ if (host->dbclk)
clk_disable(host->dbclk);
err:
pm_runtime_put_sync(host->dev);
@@ -2131,7 +2145,7 @@ static int omap_hsmmc_resume(struct device *dev)
pm_runtime_get_sync(host->dev);
- if (host->got_dbclk)
+ if (host->dbclk)
clk_enable(host->dbclk);
if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER))
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index d190d04636a7..ebbe984e5d00 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -71,6 +71,9 @@ struct pltfm_imx_data {
enum imx_esdhc_type devtype;
struct pinctrl *pinctrl;
struct esdhc_platform_data boarddata;
+ struct clk *clk_ipg;
+ struct clk *clk_ahb;
+ struct clk *clk_per;
};
static struct platform_device_id imx_esdhc_devtype[] = {
@@ -404,7 +407,7 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
if (!np)
return -ENODEV;
- if (of_get_property(np, "fsl,card-wired", NULL))
+ if (of_get_property(np, "non-removable", NULL))
boarddata->cd_type = ESDHC_CD_PERMANENT;
if (of_get_property(np, "fsl,cd-controller", NULL))
@@ -439,7 +442,6 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev)
struct sdhci_pltfm_host *pltfm_host;
struct sdhci_host *host;
struct esdhc_platform_data *boarddata;
- struct clk *clk;
int err;
struct pltfm_imx_data *imx_data;
@@ -460,14 +462,29 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev)
imx_data->devtype = pdev->id_entry->driver_data;
pltfm_host->priv = imx_data;
- clk = clk_get(mmc_dev(host->mmc), NULL);
- if (IS_ERR(clk)) {
- dev_err(mmc_dev(host->mmc), "clk err\n");
- err = PTR_ERR(clk);
+ imx_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
+ if (IS_ERR(imx_data->clk_ipg)) {
+ err = PTR_ERR(imx_data->clk_ipg);
goto err_clk_get;
}
- clk_prepare_enable(clk);
- pltfm_host->clk = clk;
+
+ imx_data->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
+ if (IS_ERR(imx_data->clk_ahb)) {
+ err = PTR_ERR(imx_data->clk_ahb);
+ goto err_clk_get;
+ }
+
+ imx_data->clk_per = devm_clk_get(&pdev->dev, "per");
+ if (IS_ERR(imx_data->clk_per)) {
+ err = PTR_ERR(imx_data->clk_per);
+ goto err_clk_get;
+ }
+
+ pltfm_host->clk = imx_data->clk_per;
+
+ clk_prepare_enable(imx_data->clk_per);
+ clk_prepare_enable(imx_data->clk_ipg);
+ clk_prepare_enable(imx_data->clk_ahb);
imx_data->pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
if (IS_ERR(imx_data->pinctrl)) {
@@ -567,8 +584,9 @@ no_card_detect_irq:
no_card_detect_pin:
no_board_data:
pin_err:
- clk_disable_unprepare(pltfm_host->clk);
- clk_put(pltfm_host->clk);
+ clk_disable_unprepare(imx_data->clk_per);
+ clk_disable_unprepare(imx_data->clk_ipg);
+ clk_disable_unprepare(imx_data->clk_ahb);
err_clk_get:
kfree(imx_data);
err_imx_data:
@@ -594,8 +612,10 @@ static int __devexit sdhci_esdhc_imx_remove(struct platform_device *pdev)
gpio_free(boarddata->cd_gpio);
}
- clk_disable_unprepare(pltfm_host->clk);
- clk_put(pltfm_host->clk);
+ clk_disable_unprepare(imx_data->clk_per);
+ clk_disable_unprepare(imx_data->clk_ipg);
+ clk_disable_unprepare(imx_data->clk_ahb);
+
kfree(imx_data);
sdhci_pltfm_free(pdev);
diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c
index c5c2a48bdd94..d9a4ef4f1ed0 100644
--- a/drivers/mmc/host/sdhci-pltfm.c
+++ b/drivers/mmc/host/sdhci-pltfm.c
@@ -42,7 +42,8 @@ static struct sdhci_ops sdhci_pltfm_ops = {
#ifdef CONFIG_OF
static bool sdhci_of_wp_inverted(struct device_node *np)
{
- if (of_get_property(np, "sdhci,wp-inverted", NULL))
+ if (of_get_property(np, "sdhci,wp-inverted", NULL) ||
+ of_get_property(np, "wp-inverted", NULL))
return true;
/* Old device trees don't have the wp-inverted property. */
@@ -59,13 +60,16 @@ void sdhci_get_of_property(struct platform_device *pdev)
struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
const __be32 *clk;
+ u32 bus_width;
int size;
if (of_device_is_available(np)) {
if (of_get_property(np, "sdhci,auto-cmd12", NULL))
host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
- if (of_get_property(np, "sdhci,1-bit-only", NULL))
+ if (of_get_property(np, "sdhci,1-bit-only", NULL) ||
+ (of_property_read_u32(np, "bus-width", &bus_width) == 0 &&
+ bus_width == 1))
host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA;
if (sdhci_of_wp_inverted(np))
diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c
index 6dfa82e03c7e..1fe32dfa7cd4 100644
--- a/drivers/mmc/host/sdhci-spear.c
+++ b/drivers/mmc/host/sdhci-spear.c
@@ -75,8 +75,6 @@ static int __devinit sdhci_probe(struct platform_device *pdev)
struct spear_sdhci *sdhci;
int ret;
- BUG_ON(pdev == NULL);
-
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!iomem) {
ret = -ENOMEM;
@@ -84,18 +82,18 @@ static int __devinit sdhci_probe(struct platform_device *pdev)
goto err;
}
- if (!request_mem_region(iomem->start, resource_size(iomem),
- "spear-sdhci")) {
+ if (!devm_request_mem_region(&pdev->dev, iomem->start,
+ resource_size(iomem), "spear-sdhci")) {
ret = -EBUSY;
dev_dbg(&pdev->dev, "cannot request region\n");
goto err;
}
- sdhci = kzalloc(sizeof(*sdhci), GFP_KERNEL);
+ sdhci = devm_kzalloc(&pdev->dev, sizeof(*sdhci), GFP_KERNEL);
if (!sdhci) {
ret = -ENOMEM;
dev_dbg(&pdev->dev, "cannot allocate memory for sdhci\n");
- goto err_kzalloc;
+ goto err;
}
/* clk enable */
@@ -103,13 +101,13 @@ static int __devinit sdhci_probe(struct platform_device *pdev)
if (IS_ERR(sdhci->clk)) {
ret = PTR_ERR(sdhci->clk);
dev_dbg(&pdev->dev, "Error getting clock\n");
- goto err_clk_get;
+ goto err;
}
ret = clk_enable(sdhci->clk);
if (ret) {
dev_dbg(&pdev->dev, "Error enabling clock\n");
- goto err_clk_enb;
+ goto put_clk;
}
/* overwrite platform_data */
@@ -124,7 +122,7 @@ static int __devinit sdhci_probe(struct platform_device *pdev)
if (IS_ERR(host)) {
ret = PTR_ERR(host);
dev_dbg(&pdev->dev, "error allocating host\n");
- goto err_alloc_host;
+ goto disable_clk;
}
host->hw_name = "sdhci";
@@ -132,17 +130,18 @@ static int __devinit sdhci_probe(struct platform_device *pdev)
host->irq = platform_get_irq(pdev, 0);
host->quirks = SDHCI_QUIRK_BROKEN_ADMA;
- host->ioaddr = ioremap(iomem->start, resource_size(iomem));
+ host->ioaddr = devm_ioremap(&pdev->dev, iomem->start,
+ resource_size(iomem));
if (!host->ioaddr) {
ret = -ENOMEM;
dev_dbg(&pdev->dev, "failed to remap registers\n");
- goto err_ioremap;
+ goto free_host;
}
ret = sdhci_add_host(host);
if (ret) {
dev_dbg(&pdev->dev, "error adding host\n");
- goto err_add_host;
+ goto free_host;
}
platform_set_drvdata(pdev, host);
@@ -161,11 +160,12 @@ static int __devinit sdhci_probe(struct platform_device *pdev)
if (sdhci->data->card_power_gpio >= 0) {
int val = 0;
- ret = gpio_request(sdhci->data->card_power_gpio, "sdhci");
+ ret = devm_gpio_request(&pdev->dev,
+ sdhci->data->card_power_gpio, "sdhci");
if (ret < 0) {
dev_dbg(&pdev->dev, "gpio request fail: %d\n",
sdhci->data->card_power_gpio);
- goto err_pgpio_request;
+ goto set_drvdata;
}
if (sdhci->data->power_always_enb)
@@ -177,60 +177,48 @@ static int __devinit sdhci_probe(struct platform_device *pdev)
if (ret) {
dev_dbg(&pdev->dev, "gpio set direction fail: %d\n",
sdhci->data->card_power_gpio);
- goto err_pgpio_direction;
+ goto set_drvdata;
}
}
if (sdhci->data->card_int_gpio >= 0) {
- ret = gpio_request(sdhci->data->card_int_gpio, "sdhci");
+ ret = devm_gpio_request(&pdev->dev, sdhci->data->card_int_gpio,
+ "sdhci");
if (ret < 0) {
dev_dbg(&pdev->dev, "gpio request fail: %d\n",
sdhci->data->card_int_gpio);
- goto err_igpio_request;
+ goto set_drvdata;
}
ret = gpio_direction_input(sdhci->data->card_int_gpio);
if (ret) {
dev_dbg(&pdev->dev, "gpio set direction fail: %d\n",
sdhci->data->card_int_gpio);
- goto err_igpio_direction;
+ goto set_drvdata;
}
- ret = request_irq(gpio_to_irq(sdhci->data->card_int_gpio),
+ ret = devm_request_irq(&pdev->dev,
+ gpio_to_irq(sdhci->data->card_int_gpio),
sdhci_gpio_irq, IRQF_TRIGGER_LOW,
mmc_hostname(host->mmc), pdev);
if (ret) {
dev_dbg(&pdev->dev, "gpio request irq fail: %d\n",
sdhci->data->card_int_gpio);
- goto err_igpio_request_irq;
+ goto set_drvdata;
}
}
return 0;
-err_igpio_request_irq:
-err_igpio_direction:
- if (sdhci->data->card_int_gpio >= 0)
- gpio_free(sdhci->data->card_int_gpio);
-err_igpio_request:
-err_pgpio_direction:
- if (sdhci->data->card_power_gpio >= 0)
- gpio_free(sdhci->data->card_power_gpio);
-err_pgpio_request:
+set_drvdata:
platform_set_drvdata(pdev, NULL);
sdhci_remove_host(host, 1);
-err_add_host:
- iounmap(host->ioaddr);
-err_ioremap:
+free_host:
sdhci_free_host(host);
-err_alloc_host:
+disable_clk:
clk_disable(sdhci->clk);
-err_clk_enb:
+put_clk:
clk_put(sdhci->clk);
-err_clk_get:
- kfree(sdhci);
-err_kzalloc:
- release_mem_region(iomem->start, resource_size(iomem));
err:
dev_err(&pdev->dev, "spear-sdhci probe failed: %d\n", ret);
return ret;
@@ -239,35 +227,19 @@ err:
static int __devexit sdhci_remove(struct platform_device *pdev)
{
struct sdhci_host *host = platform_get_drvdata(pdev);
- struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
struct spear_sdhci *sdhci = dev_get_platdata(&pdev->dev);
- int dead;
+ int dead = 0;
u32 scratch;
- if (sdhci->data) {
- if (sdhci->data->card_int_gpio >= 0) {
- free_irq(gpio_to_irq(sdhci->data->card_int_gpio), pdev);
- gpio_free(sdhci->data->card_int_gpio);
- }
-
- if (sdhci->data->card_power_gpio >= 0)
- gpio_free(sdhci->data->card_power_gpio);
- }
-
platform_set_drvdata(pdev, NULL);
- dead = 0;
scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
if (scratch == (u32)-1)
dead = 1;
sdhci_remove_host(host, dead);
- iounmap(host->ioaddr);
sdhci_free_host(host);
clk_disable(sdhci->clk);
clk_put(sdhci->clk);
- kfree(sdhci);
- if (iomem)
- release_mem_region(iomem->start, resource_size(iomem));
return 0;
}
diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index ff5a16991939..b38d8a78f6a0 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -32,8 +32,13 @@
#include "sdhci-pltfm.h"
+/* Tegra SDHOST controller vendor register definitions */
+#define SDHCI_TEGRA_VENDOR_MISC_CTRL 0x120
+#define SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 0x20
+
#define NVQUIRK_FORCE_SDHCI_SPEC_200 BIT(0)
#define NVQUIRK_ENABLE_BLOCK_GAP_DET BIT(1)
+#define NVQUIRK_ENABLE_SDHCI_SPEC_300 BIT(2)
struct sdhci_tegra_soc_data {
struct sdhci_pltfm_data *pdata;
@@ -120,6 +125,25 @@ static irqreturn_t carddetect_irq(int irq, void *data)
return IRQ_HANDLED;
};
+static void tegra_sdhci_reset_exit(struct sdhci_host *host, u8 mask)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_tegra *tegra_host = pltfm_host->priv;
+ const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
+
+ if (!(mask & SDHCI_RESET_ALL))
+ return;
+
+ /* Erratum: Enable SDHCI spec v3.00 support */
+ if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300) {
+ u32 misc_ctrl;
+
+ misc_ctrl = sdhci_readb(host, SDHCI_TEGRA_VENDOR_MISC_CTRL);
+ misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300;
+ sdhci_writeb(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL);
+ }
+}
+
static int tegra_sdhci_8bit(struct sdhci_host *host, int bus_width)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -148,6 +172,7 @@ static struct sdhci_ops tegra_sdhci_ops = {
.read_w = tegra_sdhci_readw,
.write_l = tegra_sdhci_writel,
.platform_8bit_width = tegra_sdhci_8bit,
+ .platform_reset_exit = tegra_sdhci_reset_exit,
};
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
@@ -178,6 +203,7 @@ static struct sdhci_pltfm_data sdhci_tegra30_pdata = {
static struct sdhci_tegra_soc_data soc_data_tegra30 = {
.pdata = &sdhci_tegra30_pdata,
+ .nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300,
};
#endif
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index ccefdebeff14..e626732aff77 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -680,8 +680,8 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
}
if (count >= 0xF) {
- pr_warning("%s: Too large timeout requested for CMD%d!\n",
- mmc_hostname(host->mmc), cmd->opcode);
+ pr_warning("%s: Too large timeout 0x%x requested for CMD%d!\n",
+ mmc_hostname(host->mmc), count, cmd->opcode);
count = 0xE;
}
OpenPOWER on IntegriCloud