#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 | \
#define DW_MCI_DMA_THRESHOLD 16
#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)
mci_writel(host, BMOD, SDMMC_IDMAC_SWRESET);
/* 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);
/* DDR mode set */
if (ios->timing == MMC_TIMING_UHS_DDR50)
- regs |= (0x1 << slot->id) << 16;
+ regs |= ((0x1 << slot->id) << 16);
else
- regs &= ~(0x1 << slot->id) << 16;
+ regs &= ~((0x1 << slot->id) << 16);
mci_writel(slot->host, UHS_REG, regs);
switch (ios->power_mode) {
case MMC_POWER_UP:
set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags);
+ /* Power up slot */
+ if (slot->host->pdata->setpower)
+ slot->host->pdata->setpower(slot->id, mmc->ocr_avail);
+ regs = mci_readl(slot->host, PWREN);
+ regs |= (1 << slot->id);
+ mci_writel(slot->host, PWREN, regs);
+ break;
+ case MMC_POWER_OFF:
+ /* Power down slot */
+ if (slot->host->pdata->setpower)
+ slot->host->pdata->setpower(slot->id, 0);
+ regs = mci_readl(slot->host, PWREN);
+ regs &= ~(1 << slot->id);
+ mci_writel(slot->host, PWREN, regs);
break;
default:
break;
status = host->data_status;
if (status & DW_MCI_DATA_ERROR_FLAGS) {
- if (status & SDMMC_INT_DTO) {
+ if (status & SDMMC_INT_DRTO) {
data->error = -ETIMEDOUT;
} else if (status & SDMMC_INT_DCRC) {
data->error = -EILSEQ;
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;
/* 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);
}
}
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;
/* 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);
}
}
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;
}
/* 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);
}
}
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;
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 (!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;
status = mci_readl(host, MINTSTS);
mci_writel(host, RINTSTS, SDMMC_INT_RXDR);
- } 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))
return;
done:
- data->bytes_xfered += nbytes;
sg_miter_stop(sg_miter);
host->sg = NULL;
smp_wmb();
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;
if (!len)
break;
host->push_data(host, (void *)(buf + offset), len);
+ data->bytes_xfered += len;
offset += len;
- nbytes += len;
remain -= len;
} while (remain);
status = mci_readl(host, MINTSTS);
mci_writel(host, RINTSTS, SDMMC_INT_TXDR);
} while (status & SDMMC_INT_TXDR); /* if TXDR write again */
- data->bytes_xfered += nbytes;
if (!remain) {
if (!sg_miter_next(sg_miter))
return;
done:
- data->bytes_xfered += nbytes;
sg_miter_stop(sg_miter);
host->sg = NULL;
smp_wmb();
{
struct dw_mci *host = dev_id;
u32 pending;
- unsigned int pass_count = 0;
int i;
- do {
- pending = mci_readl(host, MINTSTS); /* read-only mask reg */
+ pending = mci_readl(host, MINTSTS); /* read-only mask reg */
+
+ if (pending) {
/*
* DTO fix - version 2.10a and below, and only if internal DMA
pending |= SDMMC_INT_DATA_OVER;
}
- if (!pending)
- break;
-
if (pending & DW_MCI_CMD_ERROR_FLAGS) {
mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS);
host->cmd_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);
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) {
}
}
- } while (pass_count++ < 5);
+ }
#ifdef CONFIG_MMC_DW_IDMAC
/* Handle DMA interrupts */
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 */
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);
}
else
bus_width = 1;
- if (drv_data && drv_data->setup_bus) {
- struct device_node *slot_np;
- slot_np = dw_mci_of_find_slot_node(host->dev, slot->id);
- ret = drv_data->setup_bus(host, slot_np, bus_width);
- if (ret)
- goto err_setup_bus;
- }
-
switch (bus_width) {
case 8:
mmc->caps |= MMC_CAP_8_BIT_DATA;
#endif /* CONFIG_MMC_DW_IDMAC */
}
- host->vmmc = devm_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
slot->wp_gpio = dw_mci_of_get_wp_gpio(host->dev, slot->id);
- 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);
}
}
+ host->vmmc = devm_regulator_get(host->dev, "vmmc");
+ if (IS_ERR(host->vmmc)) {
+ ret = PTR_ERR(host->vmmc);
+ if (ret == -EPROBE_DEFER)
+ goto err_clk_ciu;
+
+ 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;
+ }
+ }
+
if (!host->bus_hz) {
dev_err(host->dev,
"Platform data must supply bus speed\n");
ret = -ENODEV;
- goto err_clk_ciu;
+ goto err_regulator;
}
host->quirks = host->pdata->quirks;
mci_writel(host, CLKENA, 0);
mci_writel(host, CLKSRC, 0);
+ /*
+ * 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(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 | WQ_NON_REENTRANT, 1);
- if (!host->card_workqueue)
+ 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);
goto err_workqueue;
}
- /*
- * 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(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;
-
if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO)
dev_info(host->dev, "Internal DMAC interrupt fix enabled.\n");
if (host->use_dma && host->dma_ops->exit)
host->dma_ops->exit(host);
+err_regulator:
if (host->vmmc)
regulator_disable(host->vmmc);
{
int i, ret;
- if (host->vmmc)
- regulator_enable(host->vmmc);
+ 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(host->dev, host)) {
ret = -ENODEV;
static int __init dw_mci_init(void)
{
- printk(KERN_INFO "Synopsys Designware Multimedia Card Interface Driver");
+ pr_info("Synopsys Designware Multimedia Card Interface Driver\n");
return 0;
}