diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-05-07 12:56:19 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-05-07 12:56:19 -0700 |
commit | 01e5d1830cf54ac45768ef9ceb3e79cea2e1198c (patch) | |
tree | 139cab6f6ea7e927d07e75028a74e7a98d554055 /drivers/mmc/host/alcor.c | |
parent | b4b52b881cf08e13d110eac811d4becc0775abbf (diff) | |
parent | 0a49a619e7e1aeb3f5f5511ca59314423c83dae2 (diff) | |
download | lwn-01e5d1830cf54ac45768ef9ceb3e79cea2e1198c.tar.gz lwn-01e5d1830cf54ac45768ef9ceb3e79cea2e1198c.zip |
Merge tag 'mmc-v5.2' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc
Pull MMC updates from Ulf Hansson:
"MMC core:
- Fix a few memoryleaks
- Minor improvements to the card initialization sequence
- Partially support sleepy GPIO controllers for pwrseq eMMC
MMC host:
- alcor: Work with multiple-entry sglists
- alcor: Enable DMA for writes
- meson-gx: Improve tuning support
- meson-gx: Avoid clock glitch when switching to DDR modes
- meson-gx: Disable unreliable HS400 mode
- mmci: Minor updates for support of HW busy detection
- mmci: Support data transfers for the stm32_sdmmc variant
- mmci: Restructure code to better support different variants
- mtk-sd: Add support for version found on MT7620 family SOCs
- mtk-sd: Add support for the MT8516 version
- mtk-sd: Add Chaotian Jing as the maintainer
- sdhci: Reorganize request-code to convert from tasklet to workqueue
- sdhci_am654: Stabilize support for lower speed modes
- sdhci-esdhc-imx: Add HS400 support for iMX7ULP
- sdhci-esdhc-imx: Add support for iMX7ULP version
- sdhci-of-arasan: Allow to disable DCMDs via DT for CQE
- sdhci-of-esdhc: Add support for the ls1028a version
- sdhci-of-esdhc: Several fixups for errata
- sdhci-pci: Fix BYT OCP setting
- sdhci-pci: Add support for Intel CML
- sdhci-tegra: Add support for system suspend/resume
- sdhci-tegra: Add CQE support for Tegra186 WAR
- sdhci-tegra: Add support for Tegra194
- sdhci-tegra: Update HW tuning process
MEMSTICK:
- I volunteered to help as a maintainer for the memstick subsystem,
which is reflected by an update to the MAINTAINERS file. Changes
are funneled through my MMC git and we will use the linux-mmc
mailing list.
MEMSTICK host:
- A few minor cleanups"
* tag 'mmc-v5.2' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (87 commits)
mmc: sdhci-pci: Fix BYT OCP setting
dt-bindings: mmc: add DT bindings for ls1028a eSDHC host controller
mmc: alcor: Drop pointer to mmc_host from alcor_sdmmc_host
mmc: mtk-sd: select REGULATOR
mmc: mtk-sd: enable internal card-detect logic.
mmc: mtk-sd: add support for config found in mt7620 family SOCs.
mmc: mtk-sd: don't hard-code interrupt trigger type
mmc: core: Fix tag set memory leak
dt-bindings: mmc: Add support for MT8516 to mtk-sd
mmc: mmci: Prevent polling for busy detection in IRQ context
mmc: mmci: Cleanup mmci_cmd_irq() for busy detect
mmc: usdhi6rol0: mark expected switch fall-throughs
mmc: core: Verify SD bus width
mmc: sdhci-esdhc-imx: Add HS400 support for iMX7ULP
mmc: sdhci-esdhc-imx: add pm_qos to interact with cpuidle
dt-bindings: mmc: fsl-imx-esdhc: add imx7ulp compatible string
mmc: meson-gx: add signal resampling tuning
mmc: meson-gx: remove Rx phase tuning
mmc: meson-gx: avoid clock glitch when switching to DDR modes
mmc: meson-gx: disable HS400
...
Diffstat (limited to 'drivers/mmc/host/alcor.c')
-rw-r--r-- | drivers/mmc/host/alcor.c | 72 |
1 files changed, 45 insertions, 27 deletions
diff --git a/drivers/mmc/host/alcor.c b/drivers/mmc/host/alcor.c index 6e6b7ade4b60..e481535cba2b 100644 --- a/drivers/mmc/host/alcor.c +++ b/drivers/mmc/host/alcor.c @@ -43,7 +43,6 @@ struct alcor_sdmmc_host { struct device *dev; struct alcor_pci_priv *alcor_pci; - struct mmc_host *mmc; struct mmc_request *mrq; struct mmc_command *cmd; struct mmc_data *data; @@ -117,6 +116,9 @@ static void alcor_reset(struct alcor_sdmmc_host *host, u8 val) dev_err(host->dev, "%s: timeout\n", __func__); } +/* + * Perform DMA I/O of a single page. + */ static void alcor_data_set_dma(struct alcor_sdmmc_host *host) { struct alcor_pci_priv *priv = host->alcor_pci; @@ -153,12 +155,26 @@ static void alcor_trigger_data_transfer(struct alcor_sdmmc_host *host) ctrl |= AU6601_DATA_WRITE; if (data->host_cookie == COOKIE_MAPPED) { + /* + * For DMA transfers, this function is called just once, + * at the start of the operation. The hardware can only + * perform DMA I/O on a single page at a time, so here + * we kick off the transfer with the first page, and expect + * subsequent pages to be transferred upon IRQ events + * indicating that the single-page DMA was completed. + */ alcor_data_set_dma(host); ctrl |= AU6601_DATA_DMA_MODE; host->dma_on = 1; alcor_write32(priv, data->sg_count * 0x1000, AU6601_REG_BLOCK_SIZE); } else { + /* + * For PIO transfers, we break down each operation + * into several sector-sized transfers. When one sector has + * complete, the IRQ handler will call this function again + * to kick off the transfer of the next sector. + */ alcor_write32(priv, data->blksz, AU6601_REG_BLOCK_SIZE); } @@ -276,7 +292,7 @@ static void alcor_send_cmd(struct alcor_sdmmc_host *host, break; default: dev_err(host->dev, "%s: cmd->flag (0x%02x) is not valid\n", - mmc_hostname(host->mmc), mmc_resp_type(cmd)); + mmc_hostname(mmc_from_priv(host)), mmc_resp_type(cmd)); break; } @@ -317,7 +333,7 @@ static void alcor_request_complete(struct alcor_sdmmc_host *host, host->data = NULL; host->dma_on = 0; - mmc_request_done(host->mmc, mrq); + mmc_request_done(mmc_from_priv(host), mrq); } static void alcor_finish_data(struct alcor_sdmmc_host *host) @@ -547,7 +563,7 @@ static void alcor_cd_irq(struct alcor_sdmmc_host *host, u32 intmask) alcor_request_complete(host, 1); } - mmc_detect_change(host->mmc, msecs_to_jiffies(1)); + mmc_detect_change(mmc_from_priv(host), msecs_to_jiffies(1)); } static irqreturn_t alcor_irq_thread(int irq, void *d) @@ -771,12 +787,17 @@ static void alcor_pre_req(struct mmc_host *mmc, data->host_cookie = COOKIE_UNMAPPED; /* FIXME: looks like the DMA engine works only with CMD18 */ - if (cmd->opcode != 18) + if (cmd->opcode != MMC_READ_MULTIPLE_BLOCK + && cmd->opcode != MMC_WRITE_MULTIPLE_BLOCK) return; /* * We don't do DMA on "complex" transfers, i.e. with - * non-word-aligned buffers or lengths. Also, we don't bother - * with all the DMA setup overhead for short transfers. + * non-word-aligned buffers or lengths. A future improvement + * could be made to use temporary DMA bounce-buffers when these + * requirements are not met. + * + * Also, we don't bother with all the DMA setup overhead for + * short transfers. */ if (data->blocks * data->blksz < AU6601_MAX_DMA_BLOCK_SIZE) return; @@ -787,6 +808,8 @@ static void alcor_pre_req(struct mmc_host *mmc, for_each_sg(data->sg, sg, data->sg_len, i) { if (sg->length != AU6601_MAX_DMA_BLOCK_SIZE) return; + if (sg->offset != 0) + return; } /* This data might be unmapped at this time */ @@ -1024,7 +1047,7 @@ static void alcor_hw_uninit(struct alcor_sdmmc_host *host) static void alcor_init_mmc(struct alcor_sdmmc_host *host) { - struct mmc_host *mmc = host->mmc; + struct mmc_host *mmc = mmc_from_priv(host); mmc->f_min = AU6601_MIN_CLOCK; mmc->f_max = AU6601_MAX_CLOCK; @@ -1036,26 +1059,21 @@ static void alcor_init_mmc(struct alcor_sdmmc_host *host) mmc->ops = &alcor_sdc_ops; /* The hardware does DMA data transfer of 4096 bytes to/from a single - * buffer address. Scatterlists are not supported, but upon DMA - * completion (signalled via IRQ), the original vendor driver does - * then immediately set up another DMA transfer of the next 4096 - * bytes. - * - * This means that we need to handle the I/O in 4096 byte chunks. - * Lacking a way to limit the sglist entries to 4096 bytes, we instead - * impose that only one segment is provided, with maximum size 4096, - * which also happens to be the minimum size. This means that the - * single-entry sglist handled by this driver can be handed directly - * to the hardware, nice and simple. + * buffer address. Scatterlists are not supported at the hardware + * level, however we can work with them at the driver level, + * provided that each segment is exactly 4096 bytes in size. + * Upon DMA completion of a single segment (signalled via IRQ), we + * immediately proceed to transfer the next segment from the + * scatterlist. * - * Unfortunately though, that means we only do 4096 bytes I/O per - * MMC command. A future improvement would be to make the driver - * accept sg lists and entries of any size, and simply iterate - * through them 4096 bytes at a time. + * The overall request is limited to 240 sectors, matching the + * original vendor driver. */ mmc->max_segs = AU6601_MAX_DMA_SEGMENTS; mmc->max_seg_size = AU6601_MAX_DMA_BLOCK_SIZE; - mmc->max_req_size = mmc->max_seg_size; + mmc->max_blk_count = 240; + mmc->max_req_size = mmc->max_blk_count * mmc->max_blk_size; + dma_set_max_seg_size(host->dev, mmc->max_seg_size); } static int alcor_pci_sdmmc_drv_probe(struct platform_device *pdev) @@ -1072,7 +1090,6 @@ static int alcor_pci_sdmmc_drv_probe(struct platform_device *pdev) } host = mmc_priv(mmc); - host->mmc = mmc; host->dev = &pdev->dev; host->cur_power_mode = MMC_POWER_UNDEFINED; host->alcor_pci = priv; @@ -1104,13 +1121,14 @@ static int alcor_pci_sdmmc_drv_probe(struct platform_device *pdev) static int alcor_pci_sdmmc_drv_remove(struct platform_device *pdev) { struct alcor_sdmmc_host *host = dev_get_drvdata(&pdev->dev); + struct mmc_host *mmc = mmc_from_priv(host); if (cancel_delayed_work_sync(&host->timeout_work)) alcor_request_complete(host, 0); alcor_hw_uninit(host); - mmc_remove_host(host->mmc); - mmc_free_host(host->mmc); + mmc_remove_host(mmc); + mmc_free_host(mmc); return 0; } |