Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7e3cb169 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull MMC fixes from Ulf Hansson:
 "A couple of MMC host fixes:

   - sdhci-pci: Fixup tuning for AMD for eMMC HS200 mode

   - renesas_sdhi_internal_dmac: Avoid data corruption by limiting
     DMA RX"

* tag 'mmc-v4.17-3' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc:
  mmc: renesas_sdhi_internal_dmac: limit DMA RX for old SoCs
  mmc: sdhci-pci: Only do AMD tuning for HS200
parents 7768ee3f 0cbc94da
Loading
Loading
Loading
Loading
+33 −6
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@
 * published by the Free Software Foundation.
 */

#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/io-64-nonatomic-hi-lo.h>
@@ -62,6 +63,17 @@
 *   need a custom accessor.
 */

static unsigned long global_flags;
/*
 * Workaround for avoiding to use RX DMAC by multiple channels.
 * On R-Car H3 ES1.* and M3-W ES1.0, when multiple SDHI channels use
 * RX DMAC simultaneously, sometimes hundreds of bytes data are not
 * stored into the system memory even if the DMAC interrupt happened.
 * So, this driver then uses one RX DMAC channel only.
 */
#define SDHI_INTERNAL_DMAC_ONE_RX_ONLY	0
#define SDHI_INTERNAL_DMAC_RX_IN_USE	1

/* Definitions for sampling clocks */
static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = {
	{
@@ -126,6 +138,9 @@ renesas_sdhi_internal_dmac_abort_dma(struct tmio_mmc_host *host) {
	renesas_sdhi_internal_dmac_dm_write(host, DM_CM_RST,
					    RST_RESERVED_BITS | val);

	if (host->data && host->data->flags & MMC_DATA_READ)
		clear_bit(SDHI_INTERNAL_DMAC_RX_IN_USE, &global_flags);

	renesas_sdhi_internal_dmac_enable_dma(host, true);
}

@@ -155,6 +170,9 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host,
	if (data->flags & MMC_DATA_READ) {
		dtran_mode |= DTRAN_MODE_CH_NUM_CH1;
		dir = DMA_FROM_DEVICE;
		if (test_bit(SDHI_INTERNAL_DMAC_ONE_RX_ONLY, &global_flags) &&
		    test_and_set_bit(SDHI_INTERNAL_DMAC_RX_IN_USE, &global_flags))
			goto force_pio;
	} else {
		dtran_mode |= DTRAN_MODE_CH_NUM_CH0;
		dir = DMA_TO_DEVICE;
@@ -208,6 +226,9 @@ static void renesas_sdhi_internal_dmac_complete_tasklet_fn(unsigned long arg)
	renesas_sdhi_internal_dmac_enable_dma(host, false);
	dma_unmap_sg(&host->pdev->dev, host->sg_ptr, host->sg_len, dir);

	if (dir == DMA_FROM_DEVICE)
		clear_bit(SDHI_INTERNAL_DMAC_RX_IN_USE, &global_flags);

	tmio_mmc_do_data_irq(host);
out:
	spin_unlock_irq(&host->lock);
@@ -251,18 +272,24 @@ static const struct tmio_mmc_dma_ops renesas_sdhi_internal_dmac_dma_ops = {
 * implementation as others may use a different implementation.
 */
static const struct soc_device_attribute gen3_soc_whitelist[] = {
        { .soc_id = "r8a7795", .revision = "ES1.*" },
	{ .soc_id = "r8a7795", .revision = "ES1.*",
	  .data = (void *)BIT(SDHI_INTERNAL_DMAC_ONE_RX_ONLY) },
	{ .soc_id = "r8a7795", .revision = "ES2.0" },
        { .soc_id = "r8a7796", .revision = "ES1.0" },
	{ .soc_id = "r8a7796", .revision = "ES1.0",
	  .data = (void *)BIT(SDHI_INTERNAL_DMAC_ONE_RX_ONLY) },
	{ .soc_id = "r8a77995", .revision = "ES1.0" },
	{ /* sentinel */ }
};

static int renesas_sdhi_internal_dmac_probe(struct platform_device *pdev)
{
	if (!soc_device_match(gen3_soc_whitelist))
	const struct soc_device_attribute *soc = soc_device_match(gen3_soc_whitelist);

	if (!soc)
		return -ENODEV;

	global_flags |= (unsigned long)soc->data;

	return renesas_sdhi_probe(pdev, &renesas_sdhi_internal_dmac_dma_ops);
}

+23 −2
Original line number Diff line number Diff line
@@ -1312,7 +1312,7 @@ static void amd_enable_manual_tuning(struct pci_dev *pdev)
	pci_write_config_dword(pdev, AMD_SD_MISC_CONTROL, val);
}

static int amd_execute_tuning(struct sdhci_host *host, u32 opcode)
static int amd_execute_tuning_hs200(struct sdhci_host *host, u32 opcode)
{
	struct sdhci_pci_slot *slot = sdhci_priv(host);
	struct pci_dev *pdev = slot->chip->pdev;
@@ -1351,6 +1351,27 @@ static int amd_execute_tuning(struct sdhci_host *host, u32 opcode)
	return 0;
}

static int amd_execute_tuning(struct mmc_host *mmc, u32 opcode)
{
	struct sdhci_host *host = mmc_priv(mmc);

	/* AMD requires custom HS200 tuning */
	if (host->timing == MMC_TIMING_MMC_HS200)
		return amd_execute_tuning_hs200(host, opcode);

	/* Otherwise perform standard SDHCI tuning */
	return sdhci_execute_tuning(mmc, opcode);
}

static int amd_probe_slot(struct sdhci_pci_slot *slot)
{
	struct mmc_host_ops *ops = &slot->host->mmc_host_ops;

	ops->execute_tuning = amd_execute_tuning;

	return 0;
}

static int amd_probe(struct sdhci_pci_chip *chip)
{
	struct pci_dev	*smbus_dev;
@@ -1385,12 +1406,12 @@ static const struct sdhci_ops amd_sdhci_pci_ops = {
	.set_bus_width			= sdhci_set_bus_width,
	.reset				= sdhci_reset,
	.set_uhs_signaling		= sdhci_set_uhs_signaling,
	.platform_execute_tuning	= amd_execute_tuning,
};

static const struct sdhci_pci_fixes sdhci_amd = {
	.probe		= amd_probe,
	.ops		= &amd_sdhci_pci_ops,
	.probe_slot	= amd_probe_slot,
};

static const struct pci_device_id pci_ids[] = {