diff options
Diffstat (limited to 'drivers/mmc/host/dw_mmc.c')
-rw-r--r-- | drivers/mmc/host/dw_mmc.c | 346 |
1 files changed, 201 insertions, 145 deletions
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 69f0cc68d5b..67c04518ec4 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -34,7 +34,6 @@ #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> @@ -62,6 +61,24 @@ SDMMC_IDMAC_INT_FBE | SDMMC_IDMAC_INT_RI | \ SDMMC_IDMAC_INT_TI) +struct idmac_desc_64addr { + u32 des0; /* Control Descriptor */ + + u32 des1; /* Reserved */ + + u32 des2; /*Buffer sizes */ +#define IDMAC_64ADDR_SET_BUFFER1_SIZE(d, s) \ + ((d)->des2 = ((d)->des2 & 0x03ffe000) | ((s) & 0x1fff)) + + u32 des3; /* Reserved */ + + u32 des4; /* Lower 32-bits of Buffer Address Pointer 1*/ + u32 des5; /* Upper 32-bits of Buffer Address Pointer 1*/ + + u32 des6; /* Lower 32-bits of Next Descriptor Address */ + u32 des7; /* Upper 32-bits of Next Descriptor Address */ +}; + struct idmac_desc { u32 des0; /* Control Descriptor */ #define IDMAC_DES0_DIC BIT(1) @@ -83,6 +100,7 @@ struct idmac_desc { #endif /* CONFIG_MMC_DW_IDMAC */ static bool dw_mci_reset(struct dw_mci *host); +static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset); #if defined(CONFIG_DEBUG_FS) static int dw_mci_req_show(struct seq_file *s, void *v) @@ -414,30 +432,66 @@ static void dw_mci_translate_sglist(struct dw_mci *host, struct mmc_data *data, unsigned int sg_len) { int i; - struct idmac_desc *desc = host->sg_cpu; + if (host->dma_64bit_address == 1) { + struct idmac_desc_64addr *desc = host->sg_cpu; - for (i = 0; i < sg_len; i++, desc++) { - unsigned int length = sg_dma_len(&data->sg[i]); - u32 mem_addr = sg_dma_address(&data->sg[i]); + for (i = 0; i < sg_len; i++, desc++) { + unsigned int length = sg_dma_len(&data->sg[i]); + u64 mem_addr = sg_dma_address(&data->sg[i]); - /* Set the OWN bit and disable interrupts for this descriptor */ - desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | IDMAC_DES0_CH; + /* + * Set the OWN bit and disable interrupts for this + * descriptor + */ + desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | + IDMAC_DES0_CH; + /* Buffer length */ + IDMAC_64ADDR_SET_BUFFER1_SIZE(desc, length); + + /* Physical address to DMA to/from */ + desc->des4 = mem_addr & 0xffffffff; + desc->des5 = mem_addr >> 32; + } - /* Buffer length */ - IDMAC_SET_BUFFER1_SIZE(desc, length); + /* Set first descriptor */ + desc = host->sg_cpu; + desc->des0 |= IDMAC_DES0_FD; - /* Physical address to DMA to/from */ - desc->des2 = mem_addr; - } + /* Set last descriptor */ + desc = host->sg_cpu + (i - 1) * + sizeof(struct idmac_desc_64addr); + desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC); + desc->des0 |= IDMAC_DES0_LD; - /* Set first descriptor */ - desc = host->sg_cpu; - desc->des0 |= IDMAC_DES0_FD; + } else { + struct idmac_desc *desc = host->sg_cpu; + + for (i = 0; i < sg_len; i++, desc++) { + unsigned int length = sg_dma_len(&data->sg[i]); + u32 mem_addr = sg_dma_address(&data->sg[i]); + + /* + * Set the OWN bit and disable interrupts for this + * descriptor + */ + desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | + IDMAC_DES0_CH; + /* Buffer length */ + IDMAC_SET_BUFFER1_SIZE(desc, length); - /* Set last descriptor */ - desc = host->sg_cpu + (i - 1) * sizeof(struct idmac_desc); - desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC); - desc->des0 |= IDMAC_DES0_LD; + /* Physical address to DMA to/from */ + desc->des2 = mem_addr; + } + + /* Set first descriptor */ + desc = host->sg_cpu; + desc->des0 |= IDMAC_DES0_FD; + + /* Set last descriptor */ + desc = host->sg_cpu + (i - 1) * sizeof(struct idmac_desc); + desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC); + desc->des0 |= IDMAC_DES0_LD; + } wmb(); } @@ -448,6 +502,10 @@ static void dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len) dw_mci_translate_sglist(host, host->data, sg_len); + /* Make sure to reset DMA in case we did PIO before this */ + dw_mci_ctrl_reset(host, SDMMC_CTRL_DMA_RESET); + dw_mci_idmac_reset(host); + /* Select IDMAC interface */ temp = mci_readl(host, CTRL); temp |= SDMMC_CTRL_USE_IDMAC; @@ -466,29 +524,71 @@ static void dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len) static int dw_mci_idmac_init(struct dw_mci *host) { - struct idmac_desc *p; int i; - /* Number of descriptors in the ring buffer */ - host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc); + if (host->dma_64bit_address == 1) { + struct idmac_desc_64addr *p; + /* Number of descriptors in the ring buffer */ + host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc_64addr); + + /* Forward link the descriptor list */ + for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; + i++, p++) { + p->des6 = (host->sg_dma + + (sizeof(struct idmac_desc_64addr) * + (i + 1))) & 0xffffffff; + + p->des7 = (u64)(host->sg_dma + + (sizeof(struct idmac_desc_64addr) * + (i + 1))) >> 32; + /* Initialize reserved and buffer size fields to "0" */ + p->des1 = 0; + p->des2 = 0; + p->des3 = 0; + } + + /* Set the last descriptor as the end-of-ring descriptor */ + p->des6 = host->sg_dma & 0xffffffff; + p->des7 = (u64)host->sg_dma >> 32; + p->des0 = IDMAC_DES0_ER; + + } else { + struct idmac_desc *p; + /* Number of descriptors in the ring buffer */ + host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc); - /* Forward link the descriptor list */ - for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++) - p->des3 = host->sg_dma + (sizeof(struct idmac_desc) * (i + 1)); + /* Forward link the descriptor list */ + for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++) + p->des3 = host->sg_dma + (sizeof(struct idmac_desc) * + (i + 1)); - /* Set the last descriptor as the end-of-ring descriptor */ - p->des3 = host->sg_dma; - p->des0 = IDMAC_DES0_ER; + /* Set the last descriptor as the end-of-ring descriptor */ + 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); + if (host->dma_64bit_address == 1) { + /* Mask out interrupts - get Tx & Rx complete only */ + mci_writel(host, IDSTS64, IDMAC_INT_CLR); + mci_writel(host, IDINTEN64, SDMMC_IDMAC_INT_NI | + SDMMC_IDMAC_INT_RI | SDMMC_IDMAC_INT_TI); + + /* Set the descriptor base address */ + mci_writel(host, DBADDRL, host->sg_dma & 0xffffffff); + mci_writel(host, DBADDRU, (u64)host->sg_dma >> 32); + + } else { + /* 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); + + /* Set the descriptor base address */ + mci_writel(host, DBADDR, host->sg_dma); + } - /* Set the descriptor base address */ - mci_writel(host, DBADDR, host->sg_dma); return 0; } @@ -626,6 +726,13 @@ static void dw_mci_ctrl_rd_thld(struct dw_mci *host, struct mmc_data *data) WARN_ON(!(data->flags & MMC_DATA_READ)); + /* + * CDTHRCTL doesn't exist prior to 240A (in fact that register offset is + * in the FIFO region, so we really shouldn't access it). + */ + if (host->verid < DW_MMC_240A) + return; + if (host->timing != MMC_TIMING_MMC_HS200 && host->timing != MMC_TIMING_UHS_SDR104) goto disable; @@ -819,7 +926,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) /* 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))) + if (!(mci_readl(host, INTMASK) & SDMMC_INT_SDIO(slot->sdio_id))) clk_en_a |= SDMMC_CLKEN_LOW_PWR << slot->id; mci_writel(host, CLKENA, clk_en_a); @@ -1075,7 +1182,7 @@ static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) ret = regulator_set_voltage(mmc->supply.vqmmc, min_uv, max_uv); if (ret) { - dev_err(&mmc->class_dev, + dev_dbg(&mmc->class_dev, "Regulator set error %d: %d - %d\n", ret, min_uv, max_uv); return ret; @@ -1180,10 +1287,10 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) dw_mci_disable_low_power(slot); mci_writel(host, INTMASK, - (int_mask | SDMMC_INT_SDIO(slot->id))); + (int_mask | SDMMC_INT_SDIO(slot->sdio_id))); } else { mci_writel(host, INTMASK, - (int_mask & ~SDMMC_INT_SDIO(slot->id))); + (int_mask & ~SDMMC_INT_SDIO(slot->sdio_id))); } } @@ -1954,6 +2061,23 @@ static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status) tasklet_schedule(&host->tasklet); } +static void dw_mci_handle_cd(struct dw_mci *host) +{ + int i; + + for (i = 0; i < host->num_slots; i++) { + struct dw_mci_slot *slot = host->slot[i]; + + if (!slot) + continue; + + if (slot->mmc->ops->card_event) + slot->mmc->ops->card_event(slot->mmc); + mmc_detect_change(slot->mmc, + msecs_to_jiffies(host->pdata->detect_delay_ms)); + } +} + static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) { struct dw_mci *host = dev_id; @@ -2029,14 +2153,15 @@ 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(host->card_workqueue, &host->card_work); + dw_mci_handle_cd(host); } /* Handle SDIO Interrupts */ for (i = 0; i < host->num_slots; i++) { struct dw_mci_slot *slot = host->slot[i]; - if (pending & SDMMC_INT_SDIO(i)) { - mci_writel(host, RINTSTS, SDMMC_INT_SDIO(i)); + if (pending & SDMMC_INT_SDIO(slot->sdio_id)) { + mci_writel(host, RINTSTS, + SDMMC_INT_SDIO(slot->sdio_id)); mmc_signal_sdio_irq(slot->mmc); } } @@ -2045,99 +2170,28 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) #ifdef CONFIG_MMC_DW_IDMAC /* Handle DMA interrupts */ - pending = mci_readl(host, IDSTS); - 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); - host->dma_ops->complete(host); + if (host->dma_64bit_address == 1) { + pending = mci_readl(host, IDSTS64); + if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) { + mci_writel(host, IDSTS64, SDMMC_IDMAC_INT_TI | + SDMMC_IDMAC_INT_RI); + mci_writel(host, IDSTS64, SDMMC_IDMAC_INT_NI); + host->dma_ops->complete(host); + } + } else { + pending = mci_readl(host, IDSTS); + 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); + host->dma_ops->complete(host); + } } #endif return IRQ_HANDLED; } -static void dw_mci_work_routine_card(struct work_struct *work) -{ - struct dw_mci *host = container_of(work, struct dw_mci, card_work); - int i; - - for (i = 0; i < host->num_slots; i++) { - struct dw_mci_slot *slot = host->slot[i]; - struct mmc_host *mmc = slot->mmc; - struct mmc_request *mrq; - int present; - - present = dw_mci_get_cd(mmc); - while (present != slot->last_detect_state) { - dev_dbg(&slot->mmc->class_dev, "card %s\n", - present ? "inserted" : "removed"); - - spin_lock_bh(&host->lock); - - /* Card change detected */ - slot->last_detect_state = present; - - /* Clean up queue if present */ - mrq = slot->mrq; - if (mrq) { - if (mrq == host->mrq) { - host->data = NULL; - host->cmd = NULL; - - switch (host->state) { - case STATE_IDLE: - case STATE_WAITING_CMD11_DONE: - break; - case STATE_SENDING_CMD11: - case STATE_SENDING_CMD: - mrq->cmd->error = -ENOMEDIUM; - if (!mrq->data) - break; - /* fall through */ - case STATE_SENDING_DATA: - mrq->data->error = -ENOMEDIUM; - dw_mci_stop_dma(host); - break; - case STATE_DATA_BUSY: - case STATE_DATA_ERROR: - if (mrq->data->error == -EINPROGRESS) - mrq->data->error = -ENOMEDIUM; - /* fall through */ - case STATE_SENDING_STOP: - if (mrq->stop) - mrq->stop->error = -ENOMEDIUM; - break; - } - - dw_mci_request_end(host, mrq); - } else { - list_del(&slot->queue_node); - mrq->cmd->error = -ENOMEDIUM; - if (mrq->data) - mrq->data->error = -ENOMEDIUM; - if (mrq->stop) - mrq->stop->error = -ENOMEDIUM; - - spin_unlock(&host->lock); - mmc_request_done(slot->mmc, mrq); - spin_lock(&host->lock); - } - } - - /* Power down slot */ - if (present == 0) - dw_mci_reset(host); - - spin_unlock_bh(&host->lock); - - present = dw_mci_get_cd(mmc); - } - - mmc_detect_change(slot->mmc, - msecs_to_jiffies(host->pdata->detect_delay_ms)); - } -} - #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) @@ -2206,6 +2260,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) slot = mmc_priv(mmc); slot->id = id; + slot->sdio_id = host->sdio_id0 + id; slot->mmc = mmc; slot->host = host; host->slot[id] = slot; @@ -2289,9 +2344,6 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) dw_mci_init_debugfs(slot); #endif - /* Card initially undetected */ - slot->last_detect_state = 0; - return 0; err_host_allocated: @@ -2309,6 +2361,22 @@ static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id) static void dw_mci_init_dma(struct dw_mci *host) { + int addr_config; + /* Check ADDR_CONFIG bit in HCON to find IDMAC address bus width */ + addr_config = (mci_readl(host, HCON) >> 27) & 0x01; + + if (addr_config == 1) { + /* host supports IDMAC in 64-bit address mode */ + host->dma_64bit_address = 1; + dev_info(host->dev, "IDMAC supports 64-bit address mode.\n"); + if (!dma_set_mask(host->dev, DMA_BIT_MASK(64))) + dma_set_coherent_mask(host->dev, DMA_BIT_MASK(64)); + } else { + /* host supports IDMAC in 32-bit address mode */ + host->dma_64bit_address = 0; + dev_info(host->dev, "IDMAC supports 32-bit address mode.\n"); + } + /* Alloc memory for sg translation */ host->sg_cpu = dmam_alloc_coherent(host->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL); @@ -2672,17 +2740,10 @@ int dw_mci_probe(struct dw_mci *host) 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; + goto err_dmaunmap; if (host->pdata->num_slots) host->num_slots = host->pdata->num_slots; @@ -2718,7 +2779,7 @@ int dw_mci_probe(struct dw_mci *host) } else { dev_dbg(host->dev, "attempted to initialize %d slots, " "but failed on all\n", host->num_slots); - goto err_workqueue; + goto err_dmaunmap; } if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) @@ -2726,9 +2787,6 @@ int dw_mci_probe(struct dw_mci *host) return 0; -err_workqueue: - destroy_workqueue(host->card_workqueue); - err_dmaunmap: if (host->use_dma && host->dma_ops->exit) host->dma_ops->exit(host); @@ -2762,8 +2820,6 @@ void dw_mci_remove(struct dw_mci *host) mci_writel(host, CLKENA, 0); mci_writel(host, CLKSRC, 0); - destroy_workqueue(host->card_workqueue); - if (host->use_dma && host->dma_ops->exit) host->dma_ops->exit(host); |