diff options
Diffstat (limited to 'drivers/mmc/host/dw_mmc.c')
| -rw-r--r-- | drivers/mmc/host/dw_mmc.c | 1487 |
1 files changed, 981 insertions, 506 deletions
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 8bec1c36b15..1ac227c603b 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -29,15 +29,19 @@ #include <linux/irq.h> #include <linux/mmc/host.h> #include <linux/mmc/mmc.h> +#include <linux/mmc/sdio.h> #include <linux/mmc/dw_mmc.h> #include <linux/bitops.h> #include <linux/regulator/consumer.h> #include <linux/workqueue.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/mmc/slot-gpio.h> #include "dw_mmc.h" /* Common flag combinations */ -#define DW_MCI_DATA_ERROR_FLAGS (SDMMC_INT_DTO | SDMMC_INT_DCRC | \ +#define DW_MCI_DATA_ERROR_FLAGS (SDMMC_INT_DRTO | SDMMC_INT_DCRC | \ SDMMC_INT_HTO | SDMMC_INT_SBE | \ SDMMC_INT_EBE) #define DW_MCI_CMD_ERROR_FLAGS (SDMMC_INT_RTO | SDMMC_INT_RCRC | \ @@ -48,7 +52,15 @@ #define DW_MCI_RECV_STATUS 2 #define DW_MCI_DMA_THRESHOLD 16 +#define DW_MCI_FREQ_MAX 200000000 /* unit: HZ */ +#define DW_MCI_FREQ_MIN 400000 /* unit: HZ */ + #ifdef CONFIG_MMC_DW_IDMAC +#define IDMAC_INT_CLR (SDMMC_IDMAC_INT_AI | SDMMC_IDMAC_INT_NI | \ + SDMMC_IDMAC_INT_CES | SDMMC_IDMAC_INT_DU | \ + SDMMC_IDMAC_INT_FBE | SDMMC_IDMAC_INT_RI | \ + SDMMC_IDMAC_INT_TI) + struct idmac_desc { u32 des0; /* Control Descriptor */ #define IDMAC_DES0_DIC BIT(1) @@ -69,38 +81,38 @@ struct idmac_desc { }; #endif /* CONFIG_MMC_DW_IDMAC */ -/** - * struct dw_mci_slot - MMC slot state - * @mmc: The mmc_host representing this slot. - * @host: The MMC controller this slot is using. - * @ctype: Card type for this slot. - * @mrq: mmc_request currently being processed or waiting to be - * processed, or NULL when the slot is idle. - * @queue_node: List node for placing this node in the @queue list of - * &struct dw_mci. - * @clock: Clock rate configured by set_ios(). Protected by host->lock. - * @flags: Random state bits associated with the slot. - * @id: Number of this slot. - * @last_detect_state: Most recently observed card detect state. - */ -struct dw_mci_slot { - struct mmc_host *mmc; - struct dw_mci *host; - - u32 ctype; - - struct mmc_request *mrq; - struct list_head queue_node; +static const u8 tuning_blk_pattern_4bit[] = { + 0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc, + 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef, + 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb, + 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef, + 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c, + 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee, + 0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff, + 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde, +}; - unsigned int clock; - unsigned long flags; -#define DW_MMC_CARD_PRESENT 0 -#define DW_MMC_CARD_NEED_INIT 1 - int id; - int last_detect_state; +static const u8 tuning_blk_pattern_8bit[] = { + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc, + 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff, + 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff, + 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd, + 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, + 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, + 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, + 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, + 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, + 0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, + 0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, + 0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, + 0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, }; -static struct workqueue_struct *dw_mci_card_workqueue; +static inline bool dw_mci_fifo_reset(struct dw_mci *host); +static inline bool dw_mci_ctrl_all_reset(struct dw_mci *host); #if defined(CONFIG_DEBUG_FS) static int dw_mci_req_show(struct seq_file *s, void *v) @@ -223,23 +235,23 @@ err: } #endif /* defined(CONFIG_DEBUG_FS) */ -static void dw_mci_set_timeout(struct dw_mci *host) -{ - /* timeout (maximum) */ - mci_writel(host, TMOUT, 0xffffffff); -} - static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) { struct mmc_data *data; + struct dw_mci_slot *slot = mmc_priv(mmc); + const struct dw_mci_drv_data *drv_data = slot->host->drv_data; u32 cmdr; cmd->error = -EINPROGRESS; cmdr = cmd->opcode; - if (cmdr == MMC_STOP_TRANSMISSION) + if (cmd->opcode == MMC_STOP_TRANSMISSION || + cmd->opcode == MMC_GO_IDLE_STATE || + cmd->opcode == MMC_GO_INACTIVE_STATE || + (cmd->opcode == SD_IO_RW_DIRECT && + ((cmd->arg >> 9) & 0x1FFFF) == SDIO_CCCR_ABORT)) cmdr |= SDMMC_CMD_STOP; - else + else if (cmd->opcode != MMC_SEND_STATUS && cmd->data) cmdr |= SDMMC_CMD_PRV_DAT_WAIT; if (cmd->flags & MMC_RSP_PRESENT) { @@ -261,6 +273,43 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) cmdr |= SDMMC_CMD_DAT_WR; } + if (drv_data && drv_data->prepare_command) + drv_data->prepare_command(slot->host, &cmdr); + + return cmdr; +} + +static u32 dw_mci_prep_stop_abort(struct dw_mci *host, struct mmc_command *cmd) +{ + struct mmc_command *stop; + u32 cmdr; + + if (!cmd->data) + return 0; + + stop = &host->stop_abort; + cmdr = cmd->opcode; + memset(stop, 0, sizeof(struct mmc_command)); + + if (cmdr == MMC_READ_SINGLE_BLOCK || + cmdr == MMC_READ_MULTIPLE_BLOCK || + cmdr == MMC_WRITE_BLOCK || + cmdr == MMC_WRITE_MULTIPLE_BLOCK) { + stop->opcode = MMC_STOP_TRANSMISSION; + stop->arg = 0; + stop->flags = MMC_RSP_R1B | MMC_CMD_AC; + } else if (cmdr == SD_IO_RW_EXTENDED) { + stop->opcode = SD_IO_RW_DIRECT; + stop->arg |= (1 << 31) | (0 << 28) | (SDIO_CCCR_ABORT << 9) | + ((cmd->arg >> 28) & 0x7); + stop->flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_AC; + } else { + return 0; + } + + cmdr = stop->opcode | SDMMC_CMD_STOP | + SDMMC_CMD_RESP_CRC | SDMMC_CMD_RESP_EXP; + return cmdr; } @@ -268,7 +317,7 @@ static void dw_mci_start_command(struct dw_mci *host, struct mmc_command *cmd, u32 cmd_flags) { host->cmd = cmd; - dev_vdbg(&host->pdev->dev, + dev_vdbg(host->dev, "start command: ARGR=0x%08x CMDR=0x%08x\n", cmd->arg, cmd_flags); @@ -278,9 +327,10 @@ static void dw_mci_start_command(struct dw_mci *host, mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START); } -static void send_stop_cmd(struct dw_mci *host, struct mmc_data *data) +static inline void send_stop_abort(struct dw_mci *host, struct mmc_data *data) { - dw_mci_start_command(host, data->stop, host->stop_cmdr); + struct mmc_command *stop = data->stop ? data->stop : &host->stop_abort; + dw_mci_start_command(host, stop, host->stop_cmdr); } /* DMA interface functions */ @@ -289,10 +339,18 @@ static void dw_mci_stop_dma(struct dw_mci *host) if (host->using_dma) { host->dma_ops->stop(host); host->dma_ops->cleanup(host); - } else { - /* Data transfer was stopped by the interrupt handler */ - set_bit(EVENT_XFER_COMPLETE, &host->pending_events); } + + /* Data transfer was stopped by the interrupt handler */ + set_bit(EVENT_XFER_COMPLETE, &host->pending_events); +} + +static int dw_mci_get_dma_dir(struct mmc_data *data) +{ + if (data->flags & MMC_DATA_WRITE) + return DMA_TO_DEVICE; + else + return DMA_FROM_DEVICE; } #ifdef CONFIG_MMC_DW_IDMAC @@ -301,9 +359,19 @@ static void dw_mci_dma_cleanup(struct dw_mci *host) struct mmc_data *data = host->data; if (data) - dma_unmap_sg(&host->pdev->dev, data->sg, data->sg_len, - ((data->flags & MMC_DATA_WRITE) - ? DMA_TO_DEVICE : DMA_FROM_DEVICE)); + if (!data->host_cookie) + dma_unmap_sg(host->dev, + data->sg, + data->sg_len, + dw_mci_get_dma_dir(data)); +} + +static void dw_mci_idmac_reset(struct dw_mci *host) +{ + u32 bmod = mci_readl(host, BMOD); + /* Software reset of DMA */ + bmod |= SDMMC_IDMAC_SWRESET; + mci_writel(host, BMOD, bmod); } static void dw_mci_idmac_stop_dma(struct dw_mci *host) @@ -319,6 +387,7 @@ static void dw_mci_idmac_stop_dma(struct dw_mci *host) /* Stop the IDMAC running */ temp = mci_readl(host, BMOD); temp &= ~(SDMMC_IDMAC_ENABLE | SDMMC_IDMAC_FB); + temp |= SDMMC_IDMAC_SWRESET; mci_writel(host, BMOD, temp); } @@ -326,7 +395,7 @@ static void dw_mci_idmac_complete_dma(struct dw_mci *host) { struct mmc_data *data = host->data; - dev_vdbg(&host->pdev->dev, "DMA complete\n"); + dev_vdbg(host->dev, "DMA complete\n"); host->dma_ops->cleanup(host); @@ -410,7 +479,10 @@ static int dw_mci_idmac_init(struct dw_mci *host) p->des3 = host->sg_dma; p->des0 = IDMAC_DES0_ER; + dw_mci_idmac_reset(host); + /* Mask out interrupts - get Tx & Rx complete only */ + mci_writel(host, IDSTS, IDMAC_INT_CLR); mci_writel(host, IDINTEN, SDMMC_IDMAC_INT_NI | SDMMC_IDMAC_INT_RI | SDMMC_IDMAC_INT_TI); @@ -419,7 +491,7 @@ static int dw_mci_idmac_init(struct dw_mci *host) return 0; } -static struct dw_mci_dma_ops dw_mci_idmac_ops = { +static const struct dw_mci_dma_ops dw_mci_idmac_ops = { .init = dw_mci_idmac_init, .start = dw_mci_idmac_start_dma, .stop = dw_mci_idmac_stop_dma, @@ -428,17 +500,15 @@ static struct dw_mci_dma_ops dw_mci_idmac_ops = { }; #endif /* CONFIG_MMC_DW_IDMAC */ -static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) +static int dw_mci_pre_dma_transfer(struct dw_mci *host, + struct mmc_data *data, + bool next) { struct scatterlist *sg; - unsigned int i, direction, sg_len; - u32 temp; + unsigned int i, sg_len; - host->using_dma = 0; - - /* If we don't have a channel, we can't do DMA */ - if (!host->use_dma) - return -ENODEV; + if (!next && data->host_cookie) + return data->host_cookie; /* * We don't do DMA on "complex" transfers, i.e. with @@ -447,6 +517,7 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) */ if (data->blocks * data->blksz < DW_MCI_DMA_THRESHOLD) return -EINVAL; + if (data->blksz & 3) return -EINVAL; @@ -455,21 +526,160 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) return -EINVAL; } - host->using_dma = 1; + sg_len = dma_map_sg(host->dev, + data->sg, + data->sg_len, + dw_mci_get_dma_dir(data)); + if (sg_len == 0) + return -EINVAL; - if (data->flags & MMC_DATA_READ) - direction = DMA_FROM_DEVICE; - else - direction = DMA_TO_DEVICE; + if (next) + data->host_cookie = sg_len; + + return sg_len; +} + +static void dw_mci_pre_req(struct mmc_host *mmc, + struct mmc_request *mrq, + bool is_first_req) +{ + struct dw_mci_slot *slot = mmc_priv(mmc); + struct mmc_data *data = mrq->data; + + if (!slot->host->use_dma || !data) + return; - sg_len = dma_map_sg(&host->pdev->dev, data->sg, data->sg_len, - direction); + if (data->host_cookie) { + data->host_cookie = 0; + return; + } + + if (dw_mci_pre_dma_transfer(slot->host, mrq->data, 1) < 0) + data->host_cookie = 0; +} + +static void dw_mci_post_req(struct mmc_host *mmc, + struct mmc_request *mrq, + int err) +{ + struct dw_mci_slot *slot = mmc_priv(mmc); + struct mmc_data *data = mrq->data; - dev_vdbg(&host->pdev->dev, + if (!slot->host->use_dma || !data) + return; + + if (data->host_cookie) + dma_unmap_sg(slot->host->dev, + data->sg, + data->sg_len, + dw_mci_get_dma_dir(data)); + data->host_cookie = 0; +} + +static void dw_mci_adjust_fifoth(struct dw_mci *host, struct mmc_data *data) +{ +#ifdef CONFIG_MMC_DW_IDMAC + unsigned int blksz = data->blksz; + const u32 mszs[] = {1, 4, 8, 16, 32, 64, 128, 256}; + u32 fifo_width = 1 << host->data_shift; + u32 blksz_depth = blksz / fifo_width, fifoth_val; + u32 msize = 0, rx_wmark = 1, tx_wmark, tx_wmark_invers; + int idx = (sizeof(mszs) / sizeof(mszs[0])) - 1; + + tx_wmark = (host->fifo_depth) / 2; + tx_wmark_invers = host->fifo_depth - tx_wmark; + + /* + * MSIZE is '1', + * if blksz is not a multiple of the FIFO width + */ + if (blksz % fifo_width) { + msize = 0; + rx_wmark = 1; + goto done; + } + + do { + if (!((blksz_depth % mszs[idx]) || + (tx_wmark_invers % mszs[idx]))) { + msize = idx; + rx_wmark = mszs[idx] - 1; + break; + } + } while (--idx > 0); + /* + * If idx is '0', it won't be tried + * Thus, initial values are uesed + */ +done: + fifoth_val = SDMMC_SET_FIFOTH(msize, rx_wmark, tx_wmark); + mci_writel(host, FIFOTH, fifoth_val); +#endif +} + +static void dw_mci_ctrl_rd_thld(struct dw_mci *host, struct mmc_data *data) +{ + unsigned int blksz = data->blksz; + u32 blksz_depth, fifo_depth; + u16 thld_size; + + WARN_ON(!(data->flags & MMC_DATA_READ)); + + if (host->timing != MMC_TIMING_MMC_HS200 && + host->timing != MMC_TIMING_UHS_SDR104) + goto disable; + + blksz_depth = blksz / (1 << host->data_shift); + fifo_depth = host->fifo_depth; + + if (blksz_depth > fifo_depth) + goto disable; + + /* + * If (blksz_depth) >= (fifo_depth >> 1), should be 'thld_size <= blksz' + * If (blksz_depth) < (fifo_depth >> 1), should be thld_size = blksz + * Currently just choose blksz. + */ + thld_size = blksz; + mci_writel(host, CDTHRCTL, SDMMC_SET_RD_THLD(thld_size, 1)); + return; + +disable: + mci_writel(host, CDTHRCTL, SDMMC_SET_RD_THLD(0, 0)); +} + +static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) +{ + int sg_len; + u32 temp; + + host->using_dma = 0; + + /* If we don't have a channel, we can't do DMA */ + if (!host->use_dma) + return -ENODEV; + + sg_len = dw_mci_pre_dma_transfer(host, data, 0); + if (sg_len < 0) { + host->dma_ops->stop(host); + return sg_len; + } + + host->using_dma = 1; + + dev_vdbg(host->dev, "sd sg_cpu: %#lx sg_dma: %#lx sg_len: %d\n", (unsigned long)host->sg_cpu, (unsigned long)host->sg_dma, sg_len); + /* + * Decide the MSIZE and RX/TX Watermark. + * If current block size is same with previous size, + * no need to update fifoth. + */ + if (host->prev_blksz != data->blksz) + dw_mci_adjust_fifoth(host, data); + /* Enable the DMA interface */ temp = mci_readl(host, CTRL); temp |= SDMMC_CTRL_DMA_ENABLE; @@ -495,10 +705,12 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data) host->sg = NULL; host->data = data; - if (data->flags & MMC_DATA_READ) + if (data->flags & MMC_DATA_READ) { host->dir_status = DW_MCI_RECV_STATUS; - else + dw_mci_ctrl_rd_thld(host, data); + } else { host->dir_status = DW_MCI_SEND_STATUS; + } if (dw_mci_submit_data_dma(host, data)) { int flags = SG_MITER_ATOMIC; @@ -520,6 +732,21 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data) temp = mci_readl(host, CTRL); temp &= ~SDMMC_CTRL_DMA_ENABLE; mci_writel(host, CTRL, temp); + + /* + * Use the initial fifoth_val for PIO mode. + * If next issued data may be transfered by DMA mode, + * prev_blksz should be invalidated. + */ + mci_writel(host, FIFOTH, host->fifoth_val); + host->prev_blksz = 0; + } else { + /* + * Keep the current block size. + * It will be used to decide whether to update + * fifoth register next time. + */ + host->prev_blksz = data->blksz; } } @@ -543,25 +770,34 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) cmd, arg, cmd_status); } -static void dw_mci_setup_bus(struct dw_mci_slot *slot) +static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) { struct dw_mci *host = slot->host; + unsigned int clock = slot->clock; u32 div; + u32 clk_en_a; - if (slot->clock != host->current_speed) { - if (host->bus_hz % slot->clock) + if (!clock) { + mci_writel(host, CLKENA, 0); + mci_send_cmd(slot, + SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); + } else if (clock != host->current_speed || force_clkinit) { + div = host->bus_hz / clock; + if (host->bus_hz % clock && host->bus_hz > clock) /* * move the + 1 after the divide to prevent * over-clocking the card. */ - div = ((host->bus_hz / slot->clock) >> 1) + 1; - else - div = (host->bus_hz / slot->clock) >> 1; + div += 1; + + div = (host->bus_hz != clock) ? DIV_ROUND_UP(div, 2) : 0; - dev_info(&slot->mmc->class_dev, - "Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ" - " div = %d)\n", slot->id, host->bus_hz, slot->clock, - div ? ((host->bus_hz / div) >> 1) : host->bus_hz, div); + if ((clock << div) != slot->__clk_old || force_clkinit) + dev_info(&slot->mmc->class_dev, + "Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ div = %d)\n", + slot->id, host->bus_hz, clock, + div ? ((host->bus_hz / div) >> 1) : + host->bus_hz, div); /* disable clock */ mci_writel(host, CLKENA, 0); @@ -578,17 +814,22 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot) mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); - /* enable clock */ - mci_writel(host, CLKENA, SDMMC_CLKEN_ENABLE | - SDMMC_CLKEN_LOW_PWR); + /* enable clock; only low power if no SDIO */ + clk_en_a = SDMMC_CLKEN_ENABLE << slot->id; + if (!(mci_readl(host, INTMASK) & SDMMC_INT_SDIO(slot->id))) + clk_en_a |= SDMMC_CLKEN_LOW_PWR << slot->id; + mci_writel(host, CLKENA, clk_en_a); /* inform CIU */ mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); - host->current_speed = slot->clock; + /* keep the clock with reflecting clock dividor */ + slot->__clk_old = clock << div; } + host->current_speed = clock; + /* Set the current slot bus width */ mci_writel(host, CTYPE, (slot->ctype << slot->id)); } @@ -602,22 +843,19 @@ static void __dw_mci_start_request(struct dw_mci *host, u32 cmdflags; mrq = slot->mrq; - if (host->pdata->select_slot) - host->pdata->select_slot(slot->id); - - /* Slot specific timing and width adjustment */ - dw_mci_setup_bus(slot); host->cur_slot = slot; host->mrq = mrq; host->pending_events = 0; host->completed_events = 0; + host->cmd_status = 0; host->data_status = 0; + host->dir_status = 0; data = cmd->data; if (data) { - dw_mci_set_timeout(host); + mci_writel(host, TMOUT, 0xFFFFFFFF); mci_writel(host, BYTCNT, data->blksz*data->blocks); mci_writel(host, BLKSIZ, data->blksz); } @@ -637,6 +875,8 @@ static void __dw_mci_start_request(struct dw_mci *host, if (mrq->stop) host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop); + else + host->stop_cmdr = dw_mci_prep_stop_abort(host, cmd); } static void dw_mci_start_request(struct dw_mci *host, @@ -695,44 +935,55 @@ static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq) static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct dw_mci_slot *slot = mmc_priv(mmc); + const struct dw_mci_drv_data *drv_data = slot->host->drv_data; u32 regs; - /* set default 1 bit mode */ - slot->ctype = SDMMC_CTYPE_1BIT; - switch (ios->bus_width) { - case MMC_BUS_WIDTH_1: - slot->ctype = SDMMC_CTYPE_1BIT; - break; case MMC_BUS_WIDTH_4: slot->ctype = SDMMC_CTYPE_4BIT; break; case MMC_BUS_WIDTH_8: slot->ctype = SDMMC_CTYPE_8BIT; break; + default: + /* set default 1 bit mode */ + slot->ctype = SDMMC_CTYPE_1BIT; } regs = mci_readl(slot->host, UHS_REG); /* DDR mode set */ - if (ios->timing == MMC_TIMING_UHS_DDR50) - regs |= (0x1 << slot->id) << 16; + if (ios->timing == MMC_TIMING_MMC_DDR52) + regs |= ((0x1 << slot->id) << 16); else - regs &= ~(0x1 << slot->id) << 16; + regs &= ~((0x1 << slot->id) << 16); mci_writel(slot->host, UHS_REG, regs); + slot->host->timing = ios->timing; - if (ios->clock) { - /* - * Use mirror of ios->clock to prevent race with mmc - * core ios update when finding the minimum. - */ - slot->clock = ios->clock; - } + /* + * Use mirror of ios->clock to prevent race with mmc + * core ios update when finding the minimum. + */ + slot->clock = ios->clock; + + if (drv_data && drv_data->set_ios) + drv_data->set_ios(slot->host, ios); + + /* Slot specific timing and width adjustment */ + dw_mci_setup_bus(slot, false); switch (ios->power_mode) { case MMC_POWER_UP: set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags); + regs = mci_readl(slot->host, PWREN); + regs |= (1 << slot->id); + mci_writel(slot->host, PWREN, regs); + break; + case MMC_POWER_OFF: + regs = mci_readl(slot->host, PWREN); + regs &= ~(1 << slot->id); + mci_writel(slot->host, PWREN, regs); break; default: break; @@ -743,11 +994,13 @@ static int dw_mci_get_ro(struct mmc_host *mmc) { int read_only; struct dw_mci_slot *slot = mmc_priv(mmc); - struct dw_mci_board *brd = slot->host->pdata; + int gpio_ro = mmc_gpio_get_ro(mmc); /* Use platform get_ro function, else try on board write protect */ - if (brd->get_ro) - read_only = brd->get_ro(slot->id); + if (slot->quirks & DW_MCI_SLOT_QUIRK_NO_WRITE_PROTECT) + read_only = 0; + else if (!IS_ERR_VALUE(gpio_ro)) + read_only = gpio_ro; else read_only = mci_readl(slot->host, WRTPRT) & (1 << slot->id) ? 1 : 0; @@ -763,24 +1016,55 @@ static int dw_mci_get_cd(struct mmc_host *mmc) int present; struct dw_mci_slot *slot = mmc_priv(mmc); struct dw_mci_board *brd = slot->host->pdata; + struct dw_mci *host = slot->host; + int gpio_cd = mmc_gpio_get_cd(mmc); /* Use platform get_cd function, else try onboard card detect */ if (brd->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION) present = 1; - else if (brd->get_cd) - present = !brd->get_cd(slot->id); + else if (!IS_ERR_VALUE(gpio_cd)) + present = gpio_cd; else present = (mci_readl(slot->host, CDETECT) & (1 << slot->id)) == 0 ? 1 : 0; - if (present) + spin_lock_bh(&host->lock); + if (present) { + set_bit(DW_MMC_CARD_PRESENT, &slot->flags); dev_dbg(&mmc->class_dev, "card is present\n"); - else + } else { + clear_bit(DW_MMC_CARD_PRESENT, &slot->flags); dev_dbg(&mmc->class_dev, "card is not present\n"); + } + spin_unlock_bh(&host->lock); return present; } +/* + * Disable lower power mode. + * + * Low power mode will stop the card clock when idle. According to the + * description of the CLKENA register we should disable low power mode + * for SDIO cards if we need SDIO interrupts to work. + * + * This function is fast if low power mode is already disabled. + */ +static void dw_mci_disable_low_power(struct dw_mci_slot *slot) +{ + struct dw_mci *host = slot->host; + u32 clk_en_a; + const u32 clken_low_pwr = SDMMC_CLKEN_LOW_PWR << slot->id; + + clk_en_a = mci_readl(host, CLKENA); + + if (clk_en_a & clken_low_pwr) { + mci_writel(host, CLKENA, clk_en_a & ~clken_low_pwr); + mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | + SDMMC_CMD_PRV_DAT_WAIT, 0); + } +} + static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) { struct dw_mci_slot *slot = mmc_priv(mmc); @@ -790,20 +1074,63 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) /* Enable/disable Slot Specific SDIO interrupt */ int_mask = mci_readl(host, INTMASK); if (enb) { + /* + * Turn off low power mode if it was enabled. This is a bit of + * a heavy operation and we disable / enable IRQs a lot, so + * we'll leave low power mode disabled and it will get + * re-enabled again in dw_mci_setup_bus(). + */ + dw_mci_disable_low_power(slot); + 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))); } } +static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode) +{ + struct dw_mci_slot *slot = mmc_priv(mmc); + struct dw_mci *host = slot->host; + const struct dw_mci_drv_data *drv_data = host->drv_data; + struct dw_mci_tuning_data tuning_data; + int err = -ENOSYS; + + if (opcode == MMC_SEND_TUNING_BLOCK_HS200) { + if (mmc->ios.bus_width == MMC_BUS_WIDTH_8) { + tuning_data.blk_pattern = tuning_blk_pattern_8bit; + tuning_data.blksz = sizeof(tuning_blk_pattern_8bit); + } else if (mmc->ios.bus_width == MMC_BUS_WIDTH_4) { + tuning_data.blk_pattern = tuning_blk_pattern_4bit; + tuning_data.blksz = sizeof(tuning_blk_pattern_4bit); + } else { + return -EINVAL; + } + } else if (opcode == MMC_SEND_TUNING_BLOCK) { + tuning_data.blk_pattern = tuning_blk_pattern_4bit; + tuning_data.blksz = sizeof(tuning_blk_pattern_4bit); + } else { + dev_err(host->dev, + "Undefined command(%d) for tuning\n", opcode); + return -EINVAL; + } + + if (drv_data && drv_data->execute_tuning) + err = drv_data->execute_tuning(slot, opcode, &tuning_data); + return err; +} + static const struct mmc_host_ops dw_mci_ops = { .request = dw_mci_request, + .pre_req = dw_mci_pre_req, + .post_req = dw_mci_post_req, .set_ios = dw_mci_set_ios, .get_ro = dw_mci_get_ro, .get_cd = dw_mci_get_cd, .enable_sdio_irq = dw_mci_enable_sdio_irq, + .execute_tuning = dw_mci_execute_tuning, }; static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) @@ -821,12 +1148,12 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) slot = list_entry(host->queue.next, struct dw_mci_slot, queue_node); list_del(&slot->queue_node); - dev_vdbg(&host->pdev->dev, "list not empty: %s is next\n", + dev_vdbg(host->dev, "list not empty: %s is next\n", mmc_hostname(slot->mmc)); host->state = STATE_SENDING_CMD; dw_mci_start_request(host, slot); } else { - dev_vdbg(&host->pdev->dev, "list empty\n"); + dev_vdbg(host->dev, "list empty\n"); host->state = STATE_IDLE; } @@ -835,7 +1162,7 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) spin_lock(&host->lock); } -static void dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd) +static int dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd) { u32 status = host->cmd_status; @@ -869,12 +1196,52 @@ static void dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd /* newer ip versions need a delay between retries */ if (host->quirks & DW_MCI_QUIRK_RETRY_DELAY) mdelay(20); + } - if (cmd->data) { - host->data = NULL; - dw_mci_stop_dma(host); + return cmd->error; +} + +static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data) +{ + u32 status = host->data_status; + + if (status & DW_MCI_DATA_ERROR_FLAGS) { + if (status & SDMMC_INT_DRTO) { + data->error = -ETIMEDOUT; + } else if (status & SDMMC_INT_DCRC) { + data->error = -EILSEQ; + } else if (status & SDMMC_INT_EBE) { + if (host->dir_status == + DW_MCI_SEND_STATUS) { + /* + * No data CRC status was returned. + * The number of bytes transferred + * will be exaggerated in PIO mode. + */ + data->bytes_xfered = 0; + data->error = -ETIMEDOUT; + } else if (host->dir_status == + DW_MCI_RECV_STATUS) { + data->error = -EIO; + } + } else { + /* SDMMC_INT_SBE is included */ + data->error = -EIO; } + + dev_dbg(host->dev, "data error, status 0x%08x\n", status); + + /* + * After an error, there may be data lingering + * in the FIFO + */ + dw_mci_fifo_reset(host); + } else { + data->bytes_xfered = data->blocks * data->blksz; + data->error = 0; } + + return data->error; } static void dw_mci_tasklet_func(unsigned long priv) @@ -882,14 +1249,16 @@ static void dw_mci_tasklet_func(unsigned long priv) struct dw_mci *host = (struct dw_mci *)priv; struct mmc_data *data; struct mmc_command *cmd; + struct mmc_request *mrq; enum dw_mci_state state; enum dw_mci_state prev_state; - u32 status, ctrl; + unsigned int err; spin_lock(&host->lock); state = host->state; data = host->data; + mrq = host->mrq; do { prev_state = state; @@ -906,16 +1275,23 @@ static void dw_mci_tasklet_func(unsigned long priv) cmd = host->cmd; host->cmd = NULL; set_bit(EVENT_CMD_COMPLETE, &host->completed_events); - dw_mci_command_complete(host, cmd); - if (cmd == host->mrq->sbc && !cmd->error) { + err = dw_mci_command_complete(host, cmd); + if (cmd == mrq->sbc && !err) { prev_state = state = STATE_SENDING_CMD; __dw_mci_start_request(host, host->cur_slot, - host->mrq->cmd); + mrq->cmd); goto unlock; } - if (!host->mrq->data || cmd->error) { - dw_mci_request_end(host, host->mrq); + if (cmd->data && err) { + dw_mci_stop_dma(host); + send_stop_abort(host, data); + state = STATE_SENDING_STOP; + break; + } + + if (!cmd->data || err) { + dw_mci_request_end(host, mrq); goto unlock; } @@ -926,8 +1302,7 @@ static void dw_mci_tasklet_func(unsigned long priv) if (test_and_clear_bit(EVENT_DATA_ERROR, &host->pending_events)) { dw_mci_stop_dma(host); - if (data->stop) - send_stop_cmd(host, data); + send_stop_abort(host, data); state = STATE_DATA_ERROR; break; } @@ -947,60 +1322,27 @@ static void dw_mci_tasklet_func(unsigned long priv) host->data = NULL; set_bit(EVENT_DATA_COMPLETE, &host->completed_events); - status = host->data_status; - - if (status & DW_MCI_DATA_ERROR_FLAGS) { - if (status & SDMMC_INT_DTO) { - data->error = -ETIMEDOUT; - } else if (status & SDMMC_INT_DCRC) { - data->error = -EILSEQ; - } else if (status & SDMMC_INT_EBE && - host->dir_status == - DW_MCI_SEND_STATUS) { - /* - * No data CRC status was returned. - * The number of bytes transferred will - * be exaggerated in PIO mode. - */ - data->bytes_xfered = 0; - data->error = -ETIMEDOUT; - } else { - dev_err(&host->pdev->dev, - "data FIFO error " - "(status=%08x)\n", - status); - data->error = -EIO; - } - /* - * After an error, there may be data lingering - * in the FIFO, so reset it - doing so - * generates a block interrupt, hence setting - * the scatter-gather pointer to NULL. - */ - sg_miter_stop(&host->sg_miter); - host->sg = NULL; - ctrl = mci_readl(host, CTRL); - ctrl |= SDMMC_CTRL_FIFO_RESET; - mci_writel(host, CTRL, ctrl); - } else { - data->bytes_xfered = data->blocks * data->blksz; - data->error = 0; - } + err = dw_mci_data_complete(host, data); - if (!data->stop) { - dw_mci_request_end(host, host->mrq); - goto unlock; - } + if (!err) { + if (!data->stop || mrq->sbc) { + if (mrq->sbc && data->stop) + data->stop->error = 0; + dw_mci_request_end(host, mrq); + goto unlock; + } - if (host->mrq->sbc && !data->error) { - data->stop->error = 0; - dw_mci_request_end(host, host->mrq); - goto unlock; + /* stop command for open-ended transfer*/ + if (data->stop) + send_stop_abort(host, data); } + /* + * If err has non-zero, + * stop-abort command has been already issued. + */ prev_state = state = STATE_SENDING_STOP; - if (!data->error) - send_stop_cmd(host, data); + /* fall through */ case STATE_SENDING_STOP: @@ -1008,9 +1350,19 @@ static void dw_mci_tasklet_func(unsigned long priv) &host->pending_events)) break; + /* CMD error in data command */ + if (mrq->cmd->error && mrq->data) + dw_mci_fifo_reset(host); + host->cmd = NULL; - dw_mci_command_complete(host, host->mrq->stop); - dw_mci_request_end(host, host->mrq); + host->data = NULL; + + if (mrq->stop) + dw_mci_command_complete(host, mrq->stop); + else + host->cmd_status = 0; + + dw_mci_request_end(host, mrq); goto unlock; case STATE_DATA_ERROR: @@ -1068,12 +1420,15 @@ static void dw_mci_pull_final_bytes(struct dw_mci *host, void *buf, int cnt) static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt) { + struct mmc_data *data = host->data; + int init_cnt = cnt; + /* try and push anything in the part_buf */ if (unlikely(host->part_buf_count)) { int len = dw_mci_push_part_bytes(host, buf, cnt); buf += len; cnt -= len; - if (!sg_next(host->sg) || host->part_buf_count == 2) { + if (host->part_buf_count == 2) { mci_writew(host, DATA(host->data_offset), host->part_buf16); host->part_buf_count = 0; @@ -1106,9 +1461,11 @@ static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt) /* put anything remaining in the part_buf */ if (cnt) { dw_mci_set_part_bytes(host, buf, cnt); - if (!sg_next(host->sg)) + /* Push data if we have reached the expected data length */ + if ((data->bytes_xfered + init_cnt) == + (data->blksz * data->blocks)) mci_writew(host, DATA(host->data_offset), - host->part_buf16); + host->part_buf16); } } @@ -1146,12 +1503,15 @@ static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt) static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt) { + struct mmc_data *data = host->data; + int init_cnt = cnt; + /* try and push anything in the part_buf */ if (unlikely(host->part_buf_count)) { int len = dw_mci_push_part_bytes(host, buf, cnt); buf += len; cnt -= len; - if (!sg_next(host->sg) || host->part_buf_count == 4) { + if (host->part_buf_count == 4) { mci_writel(host, DATA(host->data_offset), host->part_buf32); host->part_buf_count = 0; @@ -1184,9 +1544,11 @@ static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt) /* put anything remaining in the part_buf */ if (cnt) { dw_mci_set_part_bytes(host, buf, cnt); - if (!sg_next(host->sg)) + /* Push data if we have reached the expected data length */ + if ((data->bytes_xfered + init_cnt) == + (data->blksz * data->blocks)) mci_writel(host, DATA(host->data_offset), - host->part_buf32); + host->part_buf32); } } @@ -1224,13 +1586,17 @@ static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt) static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt) { + struct mmc_data *data = host->data; + int init_cnt = cnt; + /* try and push anything in the part_buf */ if (unlikely(host->part_buf_count)) { int len = dw_mci_push_part_bytes(host, buf, cnt); buf += len; cnt -= len; - if (!sg_next(host->sg) || host->part_buf_count == 8) { - mci_writew(host, DATA(host->data_offset), + + if (host->part_buf_count == 8) { + mci_writeq(host, DATA(host->data_offset), host->part_buf); host->part_buf_count = 0; } @@ -1262,9 +1628,11 @@ static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt) /* put anything remaining in the part_buf */ if (cnt) { dw_mci_set_part_bytes(host, buf, cnt); - if (!sg_next(host->sg)) + /* Push data if we have reached the expected data length */ + if ((data->bytes_xfered + init_cnt) == + (data->blksz * data->blocks)) mci_writeq(host, DATA(host->data_offset), - host->part_buf); + host->part_buf); } } @@ -1315,7 +1683,7 @@ static void dw_mci_pull_data(struct dw_mci *host, void *buf, int cnt) host->pull_data(host, buf, cnt); } -static void dw_mci_read_data_pio(struct dw_mci *host) +static void dw_mci_read_data_pio(struct dw_mci *host, bool dto) { struct sg_mapping_iter *sg_miter = &host->sg_miter; void *buf; @@ -1323,14 +1691,14 @@ static void dw_mci_read_data_pio(struct dw_mci *host) struct mmc_data *data = host->data; int shift = host->data_shift; u32 status; - unsigned int nbytes = 0, len; + unsigned int len; unsigned int remain, fcnt; do { if (!sg_miter_next(sg_miter)) goto done; - host->sg = sg_miter->__sg; + host->sg = sg_miter->piter.sg; buf = sg_miter->addr; remain = sg_miter->length; offset = 0; @@ -1342,28 +1710,17 @@ static void dw_mci_read_data_pio(struct dw_mci *host) if (!len) break; dw_mci_pull_data(host, (void *)(buf + offset), len); + data->bytes_xfered += len; offset += len; - nbytes += len; remain -= len; } while (remain); - sg_miter->consumed = offset; + sg_miter->consumed = offset; status = mci_readl(host, MINTSTS); mci_writel(host, RINTSTS, SDMMC_INT_RXDR); - if (status & DW_MCI_DATA_ERROR_FLAGS) { - host->data_status = status; - data->bytes_xfered += nbytes; - sg_miter_stop(sg_miter); - host->sg = NULL; - smp_wmb(); - - set_bit(EVENT_DATA_ERROR, &host->pending_events); - - tasklet_schedule(&host->tasklet); - return; - } - } while (status & SDMMC_INT_RXDR); /*if the RXDR is ready read again*/ - data->bytes_xfered += nbytes; + /* if the RXDR is ready read again */ + } while ((status & SDMMC_INT_RXDR) || + (dto && SDMMC_GET_FCNT(mci_readl(host, STATUS)))); if (!remain) { if (!sg_miter_next(sg_miter)) @@ -1374,7 +1731,6 @@ static void dw_mci_read_data_pio(struct dw_mci *host) return; done: - data->bytes_xfered += nbytes; sg_miter_stop(sg_miter); host->sg = NULL; smp_wmb(); @@ -1389,7 +1745,7 @@ static void dw_mci_write_data_pio(struct dw_mci *host) struct mmc_data *data = host->data; int shift = host->data_shift; u32 status; - unsigned int nbytes = 0, len; + unsigned int len; unsigned int fifo_depth = host->fifo_depth; unsigned int remain, fcnt; @@ -1397,7 +1753,7 @@ static void dw_mci_write_data_pio(struct dw_mci *host) if (!sg_miter_next(sg_miter)) goto done; - host->sg = sg_miter->__sg; + host->sg = sg_miter->piter.sg; buf = sg_miter->addr; remain = sg_miter->length; offset = 0; @@ -1410,29 +1766,15 @@ static void dw_mci_write_data_pio(struct dw_mci *host) if (!len) break; host->push_data(host, (void *)(buf + offset), len); + data->bytes_xfered += len; offset += len; - nbytes += len; remain -= len; } while (remain); - sg_miter->consumed = offset; + sg_miter->consumed = offset; status = mci_readl(host, MINTSTS); mci_writel(host, RINTSTS, SDMMC_INT_TXDR); - if (status & DW_MCI_DATA_ERROR_FLAGS) { - host->data_status = status; - data->bytes_xfered += nbytes; - sg_miter_stop(sg_miter); - host->sg = NULL; - - smp_wmb(); - - set_bit(EVENT_DATA_ERROR, &host->pending_events); - - tasklet_schedule(&host->tasklet); - return; - } } while (status & SDMMC_INT_TXDR); /* if TXDR write again */ - data->bytes_xfered += nbytes; if (!remain) { if (!sg_miter_next(sg_miter)) @@ -1443,7 +1785,6 @@ static void dw_mci_write_data_pio(struct dw_mci *host) return; done: - data->bytes_xfered += nbytes; sg_miter_stop(sg_miter); host->sg = NULL; smp_wmb(); @@ -1464,30 +1805,25 @@ static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status) static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) { struct dw_mci *host = dev_id; - u32 status, pending; - unsigned int pass_count = 0; + u32 pending; int i; - do { - status = mci_readl(host, RINTSTS); - pending = mci_readl(host, MINTSTS); /* read-only mask reg */ - - /* - * DTO fix - version 2.10a and below, and only if internal DMA - * is configured. - */ - if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) { - if (!pending && - ((mci_readl(host, STATUS) >> 17) & 0x1fff)) - pending |= SDMMC_INT_DATA_OVER; - } + pending = mci_readl(host, MINTSTS); /* read-only mask reg */ - if (!pending) - break; + /* + * DTO fix - version 2.10a and below, and only if internal DMA + * is configured. + */ + if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) { + if (!pending && + ((mci_readl(host, STATUS) >> 17) & 0x1fff)) + pending |= SDMMC_INT_DATA_OVER; + } + if (pending) { if (pending & DW_MCI_CMD_ERROR_FLAGS) { mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS); - host->cmd_status = status; + host->cmd_status = pending; smp_wmb(); set_bit(EVENT_CMD_COMPLETE, &host->pending_events); } @@ -1495,22 +1831,20 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) if (pending & DW_MCI_DATA_ERROR_FLAGS) { /* if there is an error report DATA_ERROR */ mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS); - host->data_status = status; + host->data_status = pending; smp_wmb(); set_bit(EVENT_DATA_ERROR, &host->pending_events); - if (!(pending & (SDMMC_INT_DTO | SDMMC_INT_DCRC | - SDMMC_INT_SBE | SDMMC_INT_EBE))) - tasklet_schedule(&host->tasklet); + tasklet_schedule(&host->tasklet); } if (pending & SDMMC_INT_DATA_OVER) { mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER); if (!host->data_status) - host->data_status = status; + host->data_status = pending; smp_wmb(); if (host->dir_status == DW_MCI_RECV_STATUS) { if (host->sg != NULL) - dw_mci_read_data_pio(host); + dw_mci_read_data_pio(host, true); } set_bit(EVENT_DATA_COMPLETE, &host->pending_events); tasklet_schedule(&host->tasklet); @@ -1519,7 +1853,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) if (pending & SDMMC_INT_RXDR) { mci_writel(host, RINTSTS, SDMMC_INT_RXDR); if (host->dir_status == DW_MCI_RECV_STATUS && host->sg) - dw_mci_read_data_pio(host); + dw_mci_read_data_pio(host, false); } if (pending & SDMMC_INT_TXDR) { @@ -1530,12 +1864,12 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) if (pending & SDMMC_INT_CMD_DONE) { mci_writel(host, RINTSTS, SDMMC_INT_CMD_DONE); - dw_mci_cmd_interrupt(host, status); + dw_mci_cmd_interrupt(host, pending); } 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 */ @@ -1547,7 +1881,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) } } - } while (pass_count++ < 5); + } #ifdef CONFIG_MMC_DW_IDMAC /* Handle DMA interrupts */ @@ -1555,7 +1889,6 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) { mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI); mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI); - set_bit(EVENT_DATA_COMPLETE, &host->pending_events); host->dma_ops->complete(host); } #endif @@ -1573,26 +1906,17 @@ static void dw_mci_work_routine_card(struct work_struct *work) struct mmc_host *mmc = slot->mmc; struct mmc_request *mrq; int present; - u32 ctrl; present = dw_mci_get_cd(mmc); while (present != slot->last_detect_state) { dev_dbg(&slot->mmc->class_dev, "card %s\n", present ? "inserted" : "removed"); - /* Power up slot (before spin_lock, may sleep) */ - if (present != 0 && host->pdata->setpower) - host->pdata->setpower(slot->id, mmc->ocr_avail); - spin_lock_bh(&host->lock); /* Card change detected */ slot->last_detect_state = present; - /* Mark card as present if applicable */ - if (present != 0) - set_bit(DW_MMC_CARD_PRESENT, &slot->flags); - /* Clean up queue if present */ mrq = slot->mrq; if (mrq) { @@ -1616,11 +1940,10 @@ static void dw_mci_work_routine_card(struct work_struct *work) case STATE_DATA_ERROR: if (mrq->data->error == -EINPROGRESS) mrq->data->error = -ENOMEDIUM; - if (!mrq->stop) - break; /* fall through */ case STATE_SENDING_STOP: - mrq->stop->error = -ENOMEDIUM; + if (mrq->stop) + mrq->stop->error = -ENOMEDIUM; break; } @@ -1641,34 +1964,16 @@ static void dw_mci_work_routine_card(struct work_struct *work) /* Power down slot */ if (present == 0) { - clear_bit(DW_MMC_CARD_PRESENT, &slot->flags); - - /* - * Clear down the FIFO - doing so generates a - * block interrupt, hence setting the - * scatter-gather pointer to NULL. - */ - sg_miter_stop(&host->sg_miter); - host->sg = NULL; - - ctrl = mci_readl(host, CTRL); - ctrl |= SDMMC_CTRL_FIFO_RESET; - mci_writel(host, CTRL, ctrl); - + /* Clear down the FIFO */ + dw_mci_fifo_reset(host); #ifdef CONFIG_MMC_DW_IDMAC - ctrl = mci_readl(host, BMOD); - ctrl |= 0x01; /* Software reset of DMA */ - mci_writel(host, BMOD, ctrl); + dw_mci_idmac_reset(host); #endif } spin_unlock_bh(&host->lock); - /* Power down slot (after spin_unlock, may sleep) */ - if (present == 0 && host->pdata->setpower) - host->pdata->setpower(slot->id, 0); - present = dw_mci_get_cd(mmc); } @@ -1677,12 +1982,70 @@ static void dw_mci_work_routine_card(struct work_struct *work) } } -static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id) +#ifdef CONFIG_OF +/* given a slot id, find out the device node representing that slot */ +static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot) +{ + struct device_node *np; + const __be32 *addr; + int len; + + if (!dev || !dev->of_node) + return NULL; + + for_each_child_of_node(dev->of_node, np) { + addr = of_get_property(np, "reg", &len); + if (!addr || (len < sizeof(int))) + continue; + if (be32_to_cpup(addr) == slot) + return np; + } + return NULL; +} + +static struct dw_mci_of_slot_quirks { + char *quirk; + int id; +} of_slot_quirks[] = { + { + .quirk = "disable-wp", + .id = DW_MCI_SLOT_QUIRK_NO_WRITE_PROTECT, + }, +}; + +static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot) +{ + struct device_node *np = dw_mci_of_find_slot_node(dev, slot); + int quirks = 0; + int idx; + + /* get quirks */ + for (idx = 0; idx < ARRAY_SIZE(of_slot_quirks); idx++) + if (of_get_property(np, of_slot_quirks[idx].quirk, NULL)) + quirks |= of_slot_quirks[idx].id; + + return quirks; +} +#else /* CONFIG_OF */ +static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot) +{ + return 0; +} +static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot) +{ + return NULL; +} +#endif /* CONFIG_OF */ + +static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) { struct mmc_host *mmc; struct dw_mci_slot *slot; + const struct dw_mci_drv_data *drv_data = host->drv_data; + int ctrl_id, ret; + u32 freq[2]; - mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), &host->pdev->dev); + mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev); if (!mmc) return -ENOMEM; @@ -1690,43 +2053,43 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id) slot->id = id; slot->mmc = mmc; slot->host = host; + host->slot[id] = slot; - mmc->ops = &dw_mci_ops; - mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510); - mmc->f_max = host->bus_hz; + slot->quirks = dw_mci_of_get_slot_quirks(host->dev, slot->id); - if (host->pdata->get_ocr) - mmc->ocr_avail = host->pdata->get_ocr(id); - else - mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; + mmc->ops = &dw_mci_ops; + if (of_property_read_u32_array(host->dev->of_node, + "clock-freq-min-max", freq, 2)) { + mmc->f_min = DW_MCI_FREQ_MIN; + mmc->f_max = DW_MCI_FREQ_MAX; + } else { + mmc->f_min = freq[0]; + mmc->f_max = freq[1]; + } - /* - * Start with slot power disabled, it will be enabled when a card - * is detected. - */ - if (host->pdata->setpower) - host->pdata->setpower(id, 0); + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; if (host->pdata->caps) mmc->caps = host->pdata->caps; + if (host->pdata->pm_caps) + mmc->pm_caps = host->pdata->pm_caps; + + if (host->dev->of_node) { + ctrl_id = of_alias_get_id(host->dev->of_node, "mshc"); + if (ctrl_id < 0) + ctrl_id = 0; + } else { + ctrl_id = to_platform_device(host->dev)->id; + } + if (drv_data && drv_data->caps) + mmc->caps |= drv_data->caps[ctrl_id]; + if (host->pdata->caps2) mmc->caps2 = host->pdata->caps2; - if (host->pdata->get_bus_wd) - if (host->pdata->get_bus_wd(slot->id) >= 4) - mmc->caps |= MMC_CAP_4_BIT_DATA; + mmc_of_parse(mmc); - if (host->pdata->quirks & DW_MCI_QUIRK_HIGHSPEED) - mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; - -#ifdef CONFIG_MMC_DW_IDMAC - mmc->max_segs = host->ring_size; - mmc->max_blk_size = 65536; - mmc->max_blk_count = host->ring_size; - mmc->max_seg_size = 0x1000; - mmc->max_req_size = mmc->max_seg_size * mmc->max_blk_count; -#else if (host->pdata->blk_settings) { mmc->max_segs = host->pdata->blk_settings->max_segs; mmc->max_blk_size = host->pdata->blk_settings->max_blk_size; @@ -1735,28 +2098,29 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id) mmc->max_seg_size = host->pdata->blk_settings->max_seg_size; } else { /* Useful defaults if platform data is unset. */ +#ifdef CONFIG_MMC_DW_IDMAC + mmc->max_segs = host->ring_size; + mmc->max_blk_size = 65536; + mmc->max_blk_count = host->ring_size; + mmc->max_seg_size = 0x1000; + mmc->max_req_size = mmc->max_seg_size * mmc->max_blk_count; +#else mmc->max_segs = 64; mmc->max_blk_size = 65536; /* BLKSIZ is 16 bits */ mmc->max_blk_count = 512; mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; mmc->max_seg_size = mmc->max_req_size; - } #endif /* CONFIG_MMC_DW_IDMAC */ - - host->vmmc = regulator_get(mmc_dev(mmc), "vmmc"); - if (IS_ERR(host->vmmc)) { - pr_info("%s: no vmmc regulator found\n", mmc_hostname(mmc)); - host->vmmc = NULL; - } else - regulator_enable(host->vmmc); + } if (dw_mci_get_cd(mmc)) set_bit(DW_MMC_CARD_PRESENT, &slot->flags); else clear_bit(DW_MMC_CARD_PRESENT, &slot->flags); - host->slot[id] = slot; - mmc_add_host(mmc); + ret = mmc_add_host(mmc); + if (ret) + goto err_setup_bus; #if defined(CONFIG_DEBUG_FS) dw_mci_init_debugfs(slot); @@ -1765,21 +2129,15 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id) /* Card initially undetected */ slot->last_detect_state = 0; - /* - * 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); - return 0; + +err_setup_bus: + mmc_free_host(mmc); + return -EINVAL; } static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id) { - /* Shutdown detect IRQ */ - if (slot->host->pdata->exit) - slot->host->pdata->exit(id); - /* Debugfs stuff is cleaned up by mmc core */ mmc_remove_host(slot->mmc); slot->host->slot[id] = NULL; @@ -1789,10 +2147,10 @@ static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id) static void dw_mci_init_dma(struct dw_mci *host) { /* Alloc memory for sg translation */ - host->sg_cpu = dma_alloc_coherent(&host->pdev->dev, PAGE_SIZE, + host->sg_cpu = dmam_alloc_coherent(host->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL); if (!host->sg_cpu) { - dev_err(&host->pdev->dev, "%s: could not alloc DMA memory\n", + dev_err(host->dev, "%s: could not alloc DMA memory\n", __func__); goto no_dma; } @@ -1800,20 +2158,21 @@ static void dw_mci_init_dma(struct dw_mci *host) /* Determine which DMA interface to use */ #ifdef CONFIG_MMC_DW_IDMAC host->dma_ops = &dw_mci_idmac_ops; - dev_info(&host->pdev->dev, "Using internal DMA controller.\n"); + dev_info(host->dev, "Using internal DMA controller.\n"); #endif if (!host->dma_ops) goto no_dma; - if (host->dma_ops->init) { + if (host->dma_ops->init && host->dma_ops->start && + host->dma_ops->stop && host->dma_ops->cleanup) { if (host->dma_ops->init(host)) { - dev_err(&host->pdev->dev, "%s: Unable to initialize " + dev_err(host->dev, "%s: Unable to initialize " "DMA Controller.\n", __func__); goto no_dma; } } else { - dev_err(&host->pdev->dev, "DMA initialization not found.\n"); + dev_err(host->dev, "DMA initialization not found.\n"); goto no_dma; } @@ -1821,88 +2180,223 @@ static void dw_mci_init_dma(struct dw_mci *host) return; no_dma: - dev_info(&host->pdev->dev, "Using PIO mode.\n"); + dev_info(host->dev, "Using PIO mode.\n"); host->use_dma = 0; return; } -static bool mci_wait_reset(struct device *dev, struct dw_mci *host) +static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset) { unsigned long timeout = jiffies + msecs_to_jiffies(500); - unsigned int ctrl; + u32 ctrl; - mci_writel(host, CTRL, (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | - SDMMC_CTRL_DMA_RESET)); + ctrl = mci_readl(host, CTRL); + ctrl |= reset; + mci_writel(host, CTRL, ctrl); /* wait till resets clear */ do { ctrl = mci_readl(host, CTRL); - if (!(ctrl & (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | - SDMMC_CTRL_DMA_RESET))) + if (!(ctrl & reset)) return true; } while (time_before(jiffies, timeout)); - dev_err(dev, "Timeout resetting block (ctrl %#x)\n", ctrl); + dev_err(host->dev, + "Timeout resetting block (ctrl reset %#x)\n", + ctrl & reset); return false; } -static int dw_mci_probe(struct platform_device *pdev) +static inline bool dw_mci_fifo_reset(struct dw_mci *host) +{ + /* + * Reseting generates a block interrupt, hence setting + * the scatter-gather pointer to NULL. + */ + if (host->sg) { + sg_miter_stop(&host->sg_miter); + host->sg = NULL; + } + + return dw_mci_ctrl_reset(host, SDMMC_CTRL_FIFO_RESET); +} + +static inline bool dw_mci_ctrl_all_reset(struct dw_mci *host) +{ + return dw_mci_ctrl_reset(host, + SDMMC_CTRL_FIFO_RESET | + SDMMC_CTRL_RESET | + SDMMC_CTRL_DMA_RESET); +} + +#ifdef CONFIG_OF +static struct dw_mci_of_quirks { + char *quirk; + int id; +} of_quirks[] = { + { + .quirk = "broken-cd", + .id = DW_MCI_QUIRK_BROKEN_CARD_DETECTION, + }, +}; + +static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) { - struct dw_mci *host; - struct resource *regs; struct dw_mci_board *pdata; - int irq, ret, i, width; - u32 fifo_size; + struct device *dev = host->dev; + struct device_node *np = dev->of_node; + const struct dw_mci_drv_data *drv_data = host->drv_data; + int idx, ret; + u32 clock_frequency; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(dev, "could not allocate memory for pdata\n"); + return ERR_PTR(-ENOMEM); + } - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!regs) - return -ENXIO; + /* find out number of slots supported */ + if (of_property_read_u32(dev->of_node, "num-slots", + &pdata->num_slots)) { + dev_info(dev, "num-slots property not found, " + "assuming 1 slot is available\n"); + pdata->num_slots = 1; + } - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; + /* get quirks */ + for (idx = 0; idx < ARRAY_SIZE(of_quirks); idx++) + if (of_get_property(np, of_quirks[idx].quirk, NULL)) + pdata->quirks |= of_quirks[idx].id; - host = kzalloc(sizeof(struct dw_mci), GFP_KERNEL); - if (!host) - return -ENOMEM; + if (of_property_read_u32(np, "fifo-depth", &pdata->fifo_depth)) + dev_info(dev, "fifo-depth property not found, using " + "value of FIFOTH register as default\n"); - host->pdev = pdev; - host->pdata = pdata = pdev->dev.platform_data; - if (!pdata || !pdata->init) { - dev_err(&pdev->dev, - "Platform data must supply init function\n"); - ret = -ENODEV; - goto err_freehost; + of_property_read_u32(np, "card-detect-delay", &pdata->detect_delay_ms); + + if (!of_property_read_u32(np, "clock-frequency", &clock_frequency)) + pdata->bus_hz = clock_frequency; + + if (drv_data && drv_data->parse_dt) { + ret = drv_data->parse_dt(host); + if (ret) + return ERR_PTR(ret); } - if (!pdata->select_slot && pdata->num_slots > 1) { - dev_err(&pdev->dev, - "Platform data must supply select_slot function\n"); - ret = -ENODEV; - goto err_freehost; + if (of_find_property(np, "supports-highspeed", NULL)) + pdata->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; + + return pdata; +} + +#else /* CONFIG_OF */ +static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) +{ + return ERR_PTR(-EINVAL); +} +#endif /* CONFIG_OF */ + +int dw_mci_probe(struct dw_mci *host) +{ + const struct dw_mci_drv_data *drv_data = host->drv_data; + int width, i, ret = 0; + u32 fifo_size; + int init_slots = 0; + + if (!host->pdata) { + host->pdata = dw_mci_parse_dt(host); + if (IS_ERR(host->pdata)) { + dev_err(host->dev, "platform data not available\n"); + return -EINVAL; + } + } + + if (host->pdata->num_slots > 1) { + dev_err(host->dev, + "Platform data must supply num_slots.\n"); + return -ENODEV; } - if (!pdata->bus_hz) { - dev_err(&pdev->dev, + host->biu_clk = devm_clk_get(host->dev, "biu"); + if (IS_ERR(host->biu_clk)) { + dev_dbg(host->dev, "biu clock not available\n"); + } else { + ret = clk_prepare_enable(host->biu_clk); + if (ret) { + dev_err(host->dev, "failed to enable biu clock\n"); + return ret; + } + } + + host->ciu_clk = devm_clk_get(host->dev, "ciu"); + if (IS_ERR(host->ciu_clk)) { + dev_dbg(host->dev, "ciu clock not available\n"); + host->bus_hz = host->pdata->bus_hz; + } else { + ret = clk_prepare_enable(host->ciu_clk); + if (ret) { + dev_err(host->dev, "failed to enable ciu clock\n"); + goto err_clk_biu; + } + + if (host->pdata->bus_hz) { + ret = clk_set_rate(host->ciu_clk, host->pdata->bus_hz); + if (ret) + dev_warn(host->dev, + "Unable to set bus rate to %uHz\n", + host->pdata->bus_hz); + } + host->bus_hz = clk_get_rate(host->ciu_clk); + } + + if (!host->bus_hz) { + dev_err(host->dev, "Platform data must supply bus speed\n"); ret = -ENODEV; - goto err_freehost; + goto err_clk_ciu; } - host->bus_hz = pdata->bus_hz; - host->quirks = pdata->quirks; + if (drv_data && drv_data->init) { + ret = drv_data->init(host); + if (ret) { + dev_err(host->dev, + "implementation specific init failed\n"); + goto err_clk_ciu; + } + } - spin_lock_init(&host->lock); - INIT_LIST_HEAD(&host->queue); + if (drv_data && drv_data->setup_clock) { + ret = drv_data->setup_clock(host); + if (ret) { + dev_err(host->dev, + "implementation specific clock setup failed\n"); + goto err_clk_ciu; + } + } - ret = -ENOMEM; - host->regs = ioremap(regs->start, resource_size(regs)); - if (!host->regs) - goto err_freehost; + host->vmmc = devm_regulator_get_optional(host->dev, "vmmc"); + if (IS_ERR(host->vmmc)) { + ret = PTR_ERR(host->vmmc); + if (ret == -EPROBE_DEFER) + goto err_clk_ciu; - host->dma_ops = pdata->dma_ops; - dw_mci_init_dma(host); + dev_info(host->dev, "no vmmc regulator found: %d\n", ret); + host->vmmc = NULL; + } else { + ret = regulator_enable(host->vmmc); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(host->dev, + "regulator_enable fail: %d\n", ret); + goto err_clk_ciu; + } + } + + host->quirks = host->pdata->quirks; + + spin_lock_init(&host->lock); + INIT_LIST_HEAD(&host->queue); /* * Get the host data width - this assumes that HCON has been set with @@ -1931,10 +2425,11 @@ static int dw_mci_probe(struct platform_device *pdev) } /* Reset all blocks */ - if (!mci_wait_reset(&pdev->dev, host)) { - ret = -ENODEV; - goto err_dmaunmap; - } + if (!dw_mci_ctrl_all_reset(host)) + return -ENODEV; + + host->dma_ops = host->pdata->dma_ops; + dw_mci_init_dma(host); /* Clear the interrupts for the host controller */ mci_writel(host, RINTSTS, 0xFFFFFFFF); @@ -1960,53 +2455,44 @@ static int dw_mci_probe(struct platform_device *pdev) fifo_size = host->pdata->fifo_depth; } host->fifo_depth = fifo_size; - host->fifoth_val = ((0x2 << 28) | ((fifo_size/2 - 1) << 16) | - ((fifo_size/2) << 0)); + host->fifoth_val = + SDMMC_SET_FIFOTH(0x2, fifo_size / 2 - 1, fifo_size / 2); mci_writel(host, FIFOTH, host->fifoth_val); /* disable clock to CIU */ mci_writel(host, CLKENA, 0); 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", - WQ_MEM_RECLAIM | WQ_NON_REENTRANT, 1); - if (!dw_mci_card_workqueue) - goto err_dmaunmap; - INIT_WORK(&host->card_work, dw_mci_work_routine_card); - - ret = request_irq(irq, dw_mci_interrupt, 0, "dw-mci", host); - if (ret) - goto err_workqueue; - - platform_set_drvdata(pdev, host); - - if (host->pdata->num_slots) - host->num_slots = host->pdata->num_slots; - else - host->num_slots = ((mci_readl(host, HCON) >> 1) & 0x1F) + 1; - - /* We need at least one slot to succeed */ - for (i = 0; i < host->num_slots; i++) { - ret = dw_mci_init_slot(host, i); - if (ret) { - ret = -ENODEV; - goto err_init_slot; - } - } - /* * In 2.40a spec, Data offset is changed. * Need to check the version-id and set data-offset for DATA register. */ host->verid = SDMMC_GET_VERID(mci_readl(host, VERID)); - dev_info(&pdev->dev, "Version ID is %04x\n", host->verid); + dev_info(host->dev, "Version ID is %04x\n", host->verid); if (host->verid < DW_MMC_240A) host->data_offset = DATA_OFFSET; else host->data_offset = DATA_240A_OFFSET; + tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host); + host->card_workqueue = alloc_workqueue("dw-mci-card", + WQ_MEM_RECLAIM, 1); + if (!host->card_workqueue) { + ret = -ENOMEM; + goto err_dmaunmap; + } + INIT_WORK(&host->card_work, dw_mci_work_routine_card); + ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt, + host->irq_flags, "dw-mci", host); + if (ret) + goto err_workqueue; + + if (host->pdata->num_slots) + host->num_slots = host->pdata->num_slots; + else + host->num_slots = ((mci_readl(host, HCON) >> 1) & 0x1F) + 1; + /* * Enable interrupts for command done, data over, data empty, card det, * receive ready and error such as transmit, receive timeout, crc error @@ -2017,57 +2503,63 @@ static int dw_mci_probe(struct platform_device *pdev) DW_MCI_ERROR_FLAGS | SDMMC_INT_CD); mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); /* Enable mci interrupt */ - dev_info(&pdev->dev, "DW MMC controller at irq %d, " + dev_info(host->dev, "DW MMC controller at irq %d, " "%d bit host data width, " "%u deep fifo\n", - irq, width, fifo_size); - if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) - dev_info(&pdev->dev, "Internal DMAC interrupt fix enabled.\n"); + host->irq, width, fifo_size); - return 0; + /* We need at least one slot to succeed */ + for (i = 0; i < host->num_slots; i++) { + ret = dw_mci_init_slot(host, i); + if (ret) + dev_dbg(host->dev, "slot %d init failed\n", i); + else + init_slots++; + } -err_init_slot: - /* De-init any initialized slots */ - while (i > 0) { - if (host->slot[i]) - dw_mci_cleanup_slot(host->slot[i], i); - i--; + if (init_slots) { + dev_info(host->dev, "%d slots initialized\n", init_slots); + } else { + dev_dbg(host->dev, "attempted to initialize %d slots, " + "but failed on all\n", host->num_slots); + goto err_workqueue; } - free_irq(irq, host); + + if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) + dev_info(host->dev, "Internal DMAC interrupt fix enabled.\n"); + + return 0; err_workqueue: - destroy_workqueue(dw_mci_card_workqueue); + destroy_workqueue(host->card_workqueue); err_dmaunmap: if (host->use_dma && host->dma_ops->exit) host->dma_ops->exit(host); - dma_free_coherent(&host->pdev->dev, PAGE_SIZE, - host->sg_cpu, host->sg_dma); - iounmap(host->regs); - - if (host->vmmc) { + if (host->vmmc) regulator_disable(host->vmmc); - regulator_put(host->vmmc); - } +err_clk_ciu: + if (!IS_ERR(host->ciu_clk)) + clk_disable_unprepare(host->ciu_clk); + +err_clk_biu: + if (!IS_ERR(host->biu_clk)) + clk_disable_unprepare(host->biu_clk); -err_freehost: - kfree(host); return ret; } +EXPORT_SYMBOL(dw_mci_probe); -static int __exit dw_mci_remove(struct platform_device *pdev) +void dw_mci_remove(struct dw_mci *host) { - struct dw_mci *host = platform_get_drvdata(pdev); int i; mci_writel(host, RINTSTS, 0xFFFFFFFF); mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */ - platform_set_drvdata(pdev, NULL); - for (i = 0; i < host->num_slots; i++) { - dev_dbg(&pdev->dev, "remove slot %d\n", i); + dev_dbg(host->dev, "remove slot %d\n", i); if (host->slot[i]) dw_mci_cleanup_slot(host->slot[i], i); } @@ -2076,72 +2568,67 @@ static int __exit dw_mci_remove(struct platform_device *pdev) mci_writel(host, CLKENA, 0); mci_writel(host, CLKSRC, 0); - free_irq(platform_get_irq(pdev, 0), host); - destroy_workqueue(dw_mci_card_workqueue); - dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); + destroy_workqueue(host->card_workqueue); if (host->use_dma && host->dma_ops->exit) host->dma_ops->exit(host); - if (host->vmmc) { + if (host->vmmc) regulator_disable(host->vmmc); - regulator_put(host->vmmc); - } - iounmap(host->regs); + if (!IS_ERR(host->ciu_clk)) + clk_disable_unprepare(host->ciu_clk); - kfree(host); - return 0; + if (!IS_ERR(host->biu_clk)) + clk_disable_unprepare(host->biu_clk); } +EXPORT_SYMBOL(dw_mci_remove); + + #ifdef CONFIG_PM_SLEEP /* * TODO: we should probably disable the clock to the card in the suspend path. */ -static int dw_mci_suspend(struct device *dev) +int dw_mci_suspend(struct dw_mci *host) { - int i, ret; - struct dw_mci *host = dev_get_drvdata(dev); - - for (i = 0; i < host->num_slots; i++) { - struct dw_mci_slot *slot = host->slot[i]; - if (!slot) - continue; - ret = mmc_suspend_host(slot->mmc); - if (ret < 0) { - while (--i >= 0) { - slot = host->slot[i]; - if (slot) - mmc_resume_host(host->slot[i]->mmc); - } - return ret; - } - } - if (host->vmmc) regulator_disable(host->vmmc); return 0; } +EXPORT_SYMBOL(dw_mci_suspend); -static int dw_mci_resume(struct device *dev) +int dw_mci_resume(struct dw_mci *host) { int i, ret; - struct dw_mci *host = dev_get_drvdata(dev); - - if (host->vmmc) - regulator_enable(host->vmmc); - if (host->dma_ops->init) - host->dma_ops->init(host); + if (host->vmmc) { + ret = regulator_enable(host->vmmc); + if (ret) { + dev_err(host->dev, + "failed to enable regulator: %d\n", ret); + return ret; + } + } - if (!mci_wait_reset(dev, host)) { + if (!dw_mci_ctrl_all_reset(host)) { ret = -ENODEV; return ret; } - /* Restore the old value at FIFOTH register */ + if (host->use_dma && host->dma_ops->init) + host->dma_ops->init(host); + + /* + * Restore the initial value at FIFOTH register + * And Invalidate the prev_blksz with zero + */ mci_writel(host, FIFOTH, host->fifoth_val); + host->prev_blksz = 0; + + /* Put in max timeout */ + mci_writel(host, TMOUT, 0xFFFFFFFF); mci_writel(host, RINTSTS, 0xFFFFFFFF); mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER | @@ -2153,36 +2640,24 @@ static int dw_mci_resume(struct device *dev) struct dw_mci_slot *slot = host->slot[i]; if (!slot) continue; - ret = mmc_resume_host(host->slot[i]->mmc); - if (ret < 0) - return ret; + if (slot->mmc->pm_flags & MMC_PM_KEEP_POWER) { + dw_mci_set_ios(slot->mmc, &slot->mmc->ios); + dw_mci_setup_bus(slot, true); + } } - return 0; } -#else -#define dw_mci_suspend NULL -#define dw_mci_resume NULL +EXPORT_SYMBOL(dw_mci_resume); #endif /* CONFIG_PM_SLEEP */ -static SIMPLE_DEV_PM_OPS(dw_mci_pmops, dw_mci_suspend, dw_mci_resume); - -static struct platform_driver dw_mci_driver = { - .remove = __exit_p(dw_mci_remove), - .driver = { - .name = "dw_mmc", - .pm = &dw_mci_pmops, - }, -}; - static int __init dw_mci_init(void) { - return platform_driver_probe(&dw_mci_driver, dw_mci_probe); + pr_info("Synopsys Designware Multimedia Card Interface Driver\n"); + return 0; } static void __exit dw_mci_exit(void) { - platform_driver_unregister(&dw_mci_driver); } module_init(dw_mci_init); |
