Commit e57a5f61 authored by Adrian Hunter's avatar Adrian Hunter Committed by Ulf Hansson
Browse files

mmc: sdhci: Add 64-bit ADMA support



Add 64-bit ADMA support including:
	- add 64-bit ADMA descriptor
	- add SDHCI_USE_64_BIT_DMA flag
	- set upper 32-bits of DMA addresses
	- ability to select 64-bit ADMA
	- ability to use 64-bit ADMA sizes and alignment
	- display "ADMA 64-bit" when host is added

It is assumed that a 64-bit capable device has set a 64-bit DMA mask
and *must* do 64-bit DMA.  A driver has the opportunity to change
that during the first call to ->enable_dma().  Similarly
SDHCI_QUIRK2_BROKEN_64_BIT_DMA must be left to the drivers to
implement.

Signed-off-by: default avatarAdrian Hunter <adrian.hunter@intel.com>
Signed-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
parent 0545230f
Loading
Loading
Loading
Loading
+81 −28
Original line number Diff line number Diff line
@@ -117,10 +117,17 @@ static void sdhci_dumpregs(struct sdhci_host *host)
	pr_debug(DRIVER_NAME ": Host ctl2: 0x%08x\n",
		sdhci_readw(host, SDHCI_HOST_CONTROL2));

	if (host->flags & SDHCI_USE_ADMA)
	if (host->flags & SDHCI_USE_ADMA) {
		if (host->flags & SDHCI_USE_64_BIT_DMA)
			pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x%08x\n",
				 readl(host->ioaddr + SDHCI_ADMA_ERROR),
				 readl(host->ioaddr + SDHCI_ADMA_ADDRESS_HI),
				 readl(host->ioaddr + SDHCI_ADMA_ADDRESS));
		else
			pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n",
				 readl(host->ioaddr + SDHCI_ADMA_ERROR),
				 readl(host->ioaddr + SDHCI_ADMA_ADDRESS));
	}

	pr_debug(DRIVER_NAME ": ===========================================\n");
}
@@ -446,19 +453,25 @@ static void sdhci_kunmap_atomic(void *buffer, unsigned long *flags)
	local_irq_restore(*flags);
}

static void sdhci_adma_write_desc(void *desc, u32 addr, int len, unsigned cmd)
static void sdhci_adma_write_desc(struct sdhci_host *host, void *desc,
				  dma_addr_t addr, int len, unsigned cmd)
{
	struct sdhci_adma2_32_desc *dma_desc = desc;
	struct sdhci_adma2_64_desc *dma_desc = desc;

	/* 32-bit and 64-bit descriptors have these members in same position */
	dma_desc->cmd = cpu_to_le16(cmd);
	dma_desc->len = cpu_to_le16(len);
	dma_desc->addr = cpu_to_le32(addr);
	dma_desc->addr_lo = cpu_to_le32((u32)addr);

	if (host->flags & SDHCI_USE_64_BIT_DMA)
		dma_desc->addr_hi = cpu_to_le32((u64)addr >> 32);
}

static void sdhci_adma_mark_end(void *desc)
{
	struct sdhci_adma2_32_desc *dma_desc = desc;
	struct sdhci_adma2_64_desc *dma_desc = desc;

	/* 32-bit and 64-bit descriptors have 'cmd' in same position */
	dma_desc->cmd |= cpu_to_le16(ADMA2_END);
}

@@ -527,7 +540,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
			}

			/* tran, valid */
			sdhci_adma_write_desc(desc, align_addr, offset,
			sdhci_adma_write_desc(host, desc, align_addr, offset,
					      ADMA2_TRAN_VALID);

			BUG_ON(offset > 65536);
@@ -544,7 +557,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
		BUG_ON(len > 65536);

		/* tran, valid */
		sdhci_adma_write_desc(desc, addr, len, ADMA2_TRAN_VALID);
		sdhci_adma_write_desc(host, desc, addr, len, ADMA2_TRAN_VALID);
		desc += host->desc_sz;

		/*
@@ -568,7 +581,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
		*/

		/* nop, end, valid */
		sdhci_adma_write_desc(desc, 0, 0, ADMA2_NOP_END_VALID);
		sdhci_adma_write_desc(host, desc, 0, 0, ADMA2_NOP_END_VALID);
	}

	/*
@@ -827,6 +840,10 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
			} else {
				sdhci_writel(host, host->adma_addr,
					SDHCI_ADMA_ADDRESS);
				if (host->flags & SDHCI_USE_64_BIT_DMA)
					sdhci_writel(host,
						     (u64)host->adma_addr >> 32,
						     SDHCI_ADMA_ADDRESS_HI);
			}
		} else {
			int sg_cnt;
@@ -860,10 +877,14 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
		ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
		ctrl &= ~SDHCI_CTRL_DMA_MASK;
		if ((host->flags & SDHCI_REQ_USE_DMA) &&
			(host->flags & SDHCI_USE_ADMA))
			ctrl |= SDHCI_CTRL_ADMA32;
			(host->flags & SDHCI_USE_ADMA)) {
			if (host->flags & SDHCI_USE_64_BIT_DMA)
				ctrl |= SDHCI_CTRL_ADMA64;
			else
				ctrl |= SDHCI_CTRL_ADMA32;
		} else {
			ctrl |= SDHCI_CTRL_SDMA;
		}
		sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
	}

@@ -2296,10 +2317,17 @@ static void sdhci_adma_show_error(struct sdhci_host *host)
	sdhci_dumpregs(host);

	while (true) {
		struct sdhci_adma2_32_desc *dma_desc = desc;
		struct sdhci_adma2_64_desc *dma_desc = desc;

		if (host->flags & SDHCI_USE_64_BIT_DMA)
			DBG("%s: %p: DMA 0x%08x%08x, LEN 0x%04x, Attr=0x%02x\n",
			    name, desc, le32_to_cpu(dma_desc->addr_hi),
			    le32_to_cpu(dma_desc->addr_lo),
			    le16_to_cpu(dma_desc->len),
			    le16_to_cpu(dma_desc->cmd));
		else
			DBG("%s: %p: DMA 0x%08x, LEN 0x%04x, Attr=0x%02x\n",
		    name, desc, le32_to_cpu(dma_desc->addr),
			    name, desc, le32_to_cpu(dma_desc->addr_lo),
			    le16_to_cpu(dma_desc->len),
			    le16_to_cpu(dma_desc->cmd));

@@ -2852,6 +2880,16 @@ int sdhci_add_host(struct sdhci_host *host)
		host->flags &= ~SDHCI_USE_ADMA;
	}

	/*
	 * It is assumed that a 64-bit capable device has set a 64-bit DMA mask
	 * and *must* do 64-bit DMA.  A driver has the opportunity to change
	 * that during the first call to ->enable_dma().  Similarly
	 * SDHCI_QUIRK2_BROKEN_64_BIT_DMA must be left to the drivers to
	 * implement.
	 */
	if (sdhci_readl(host, SDHCI_CAPABILITIES) & SDHCI_CAN_64BIT)
		host->flags |= SDHCI_USE_64_BIT_DMA;

	if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
		if (host->ops->enable_dma) {
			if (host->ops->enable_dma(host)) {
@@ -2863,6 +2901,10 @@ int sdhci_add_host(struct sdhci_host *host)
		}
	}

	/* SDMA does not support 64-bit DMA */
	if (host->flags & SDHCI_USE_64_BIT_DMA)
		host->flags &= ~SDHCI_USE_SDMA;

	if (host->flags & SDHCI_USE_ADMA) {
		/*
		 * The DMA descriptor table size is calculated as the maximum
@@ -2870,6 +2912,15 @@ int sdhci_add_host(struct sdhci_host *host)
		 * descriptor for each segment, plus 1 for a nop end descriptor,
		 * all multipled by the descriptor size.
		 */
		if (host->flags & SDHCI_USE_64_BIT_DMA) {
			host->adma_table_sz = (SDHCI_MAX_SEGS * 2 + 1) *
					      SDHCI_ADMA2_64_DESC_SZ;
			host->align_buffer_sz = SDHCI_MAX_SEGS *
						SDHCI_ADMA2_64_ALIGN;
			host->desc_sz = SDHCI_ADMA2_64_DESC_SZ;
			host->align_sz = SDHCI_ADMA2_64_ALIGN;
			host->align_mask = SDHCI_ADMA2_64_ALIGN - 1;
		} else {
			host->adma_table_sz = (SDHCI_MAX_SEGS * 2 + 1) *
					      SDHCI_ADMA2_32_DESC_SZ;
			host->align_buffer_sz = SDHCI_MAX_SEGS *
@@ -2877,6 +2928,7 @@ int sdhci_add_host(struct sdhci_host *host)
			host->desc_sz = SDHCI_ADMA2_32_DESC_SZ;
			host->align_sz = SDHCI_ADMA2_32_ALIGN;
			host->align_mask = SDHCI_ADMA2_32_ALIGN - 1;
		}
		host->adma_table = dma_alloc_coherent(mmc_dev(mmc),
						      host->adma_table_sz,
						      &host->adma_addr,
@@ -3289,7 +3341,8 @@ int sdhci_add_host(struct sdhci_host *host)

	pr_info("%s: SDHCI controller on %s [%s] using %s\n",
		mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)),
		(host->flags & SDHCI_USE_ADMA) ? "ADMA" :
		(host->flags & SDHCI_USE_ADMA) ?
		(host->flags & SDHCI_USE_64_BIT_DMA) ? "ADMA 64-bit" : "ADMA" :
		(host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO");

	sdhci_enable_card_detection(host);
+18 −0
Original line number Diff line number Diff line
@@ -227,6 +227,7 @@
/* 55-57 reserved */

#define SDHCI_ADMA_ADDRESS	0x58
#define SDHCI_ADMA_ADDRESS_HI	0x5C

/* 60-FB reserved */

@@ -279,6 +280,23 @@ struct sdhci_adma2_32_desc {
	__le32	addr;
}  __packed __aligned(SDHCI_ADMA2_32_ALIGN);

/* ADMA2 64-bit DMA descriptor size */
#define SDHCI_ADMA2_64_DESC_SZ	12

/* ADMA2 64-bit DMA alignment */
#define SDHCI_ADMA2_64_ALIGN	8

/*
 * ADMA2 64-bit descriptor. Note 12-byte descriptor can't always be 8-byte
 * aligned.
 */
struct sdhci_adma2_64_desc {
	__le16	cmd;
	__le16	len;
	__le32	addr_lo;
	__le32	addr_hi;
}  __packed __aligned(4);

#define ADMA2_TRAN_VALID	0x21
#define ADMA2_NOP_END_VALID	0x3
#define ADMA2_END		0x2
+3 −0
Original line number Diff line number Diff line
@@ -100,6 +100,8 @@ struct sdhci_host {
#define SDHCI_QUIRK2_BROKEN_DDR50			(1<<7)
/* Stop command (CMD12) can set Transfer Complete when not using MMC_RSP_BUSY */
#define SDHCI_QUIRK2_STOP_WITH_TC			(1<<8)
/* Controller does not support 64-bit DMA */
#define SDHCI_QUIRK2_BROKEN_64_BIT_DMA			(1<<9)

	int irq;		/* Device IRQ */
	void __iomem *ioaddr;	/* Mapped address */
@@ -130,6 +132,7 @@ struct sdhci_host {
#define SDHCI_SDIO_IRQ_ENABLED	(1<<9)	/* SDIO irq enabled */
#define SDHCI_SDR104_NEEDS_TUNING (1<<10)	/* SDR104/HS200 needs tuning */
#define SDHCI_USING_RETUNING_TIMER (1<<11)	/* Host is using a retuning timer for the card */
#define SDHCI_USE_64_BIT_DMA	(1<<12)	/* Use 64-bit DMA */

	unsigned int version;	/* SDHCI spec. version */