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

Commit bb7505b1 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "mmc: sdhci: Add LPAE (Large Physical Address Extension) support"

parents 8737f036 01079142
Loading
Loading
Loading
Loading
+128 −43
Original line number Diff line number Diff line
@@ -138,10 +138,17 @@ static void sdhci_dumpregs(struct sdhci_host *host)
	pr_info(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_64BIT) {
		pr_info(DRIVER_NAME ": ADMA Err: 0x%08x\n",
		       readl(host->ioaddr + SDHCI_ADMA_ERROR));
		pr_info(DRIVER_NAME ": ADMA Addr(0:31): 0x%08x | ADMA Addr(32:63): 0x%08x\n",
		       readl(host->ioaddr + SDHCI_ADMA_ADDRESS_LOW),
		       readl(host->ioaddr + SDHCI_ADMA_ADDRESS_HIGH));
	} else if (host->flags & SDHCI_USE_ADMA) {
		pr_info(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n",
		       readl(host->ioaddr + SDHCI_ADMA_ERROR),
		       readl(host->ioaddr + SDHCI_ADMA_ADDRESS));
		       readl(host->ioaddr + SDHCI_ADMA_ADDRESS_LOW));
	}

	if (host->ops->dump_vendor_regs)
		host->ops->dump_vendor_regs(host);
@@ -514,19 +521,28 @@ static void sdhci_kunmap_atomic(void *buffer, unsigned long *flags)
	local_irq_restore(*flags);
}

static void sdhci_set_adma_desc(u8 *desc, u32 addr, int len, unsigned cmd)
static void sdhci_set_adma_desc(struct sdhci_host *host, u8 *desc,
				dma_addr_t addr, int len, unsigned cmd)
{
	__le32 *dataddr = (__le32 __force *)(desc + 4);
	__le16 *cmdlen = (__le16 __force *)desc;

	/* SDHCI specification says ADMA descriptors should be 4 byte
	 * aligned, so using 16 or 32bit operations should be safe. */
	/*
	 * SDHCI specification says ADMA descriptors should be 4 byte
	 * or 8 byte aligned, so using 16 or 32 or 64bit operations
	 * should be safe.
	 */

	cmdlen[0] = cpu_to_le16(cmd);
	cmdlen[1] = cpu_to_le16(len);

	if (host->flags & SDHCI_USE_ADMA_64BIT) {
		__le64 *dataddr = (__le64 __force *)(desc + 4);
		dataddr[0] = cpu_to_le64(addr);
	} else {
		__le32 *dataddr = (__le32 __force *)(desc + 4);
		dataddr[0] = cpu_to_le32(addr);
	}
}

static int sdhci_pre_dma_transfer(struct sdhci_host *host,
				  struct mmc_data *data,
@@ -602,7 +618,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
					  direction);
	if (dma_mapping_error(mmc_dev(host->mmc), host->align_addr))
		goto fail;
	BUG_ON(host->align_addr & 0x3);
	BUG_ON(host->align_addr & (host->align_bytes - 1));

	host->sg_count = sdhci_pre_dma_transfer(host, data, NULL);
	if (host->sg_count < 0)
@@ -618,30 +634,39 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
		len = sg_dma_len(sg);

		/*
		 * The SDHCI specification states that ADMA
		 * addresses must be 32-bit aligned. If they
		 * aren't, then we use a bounce buffer for
		 * the (up to three) bytes that screw up the
		 * alignment.
		 * The SDHCI specification states that ADMA addresses must be
		 * 32-bit aligned for 32-bit ADMA or 64-bit aligned for 64-bit
		 * ADMA. If they aren't, then we use a bounce buffer for the
		 * (up to three for 32-bit and up to seven for 64-bit) bytes
		 * that screw up the alignment.
		 *
		 */
		offset = (4 - (addr & 0x3)) & 0x3;
		offset = (host->align_bytes - (addr & (host->align_bytes - 1)))
				& (host->align_bytes - 1);
		if (offset) {
			if (data->flags & MMC_DATA_WRITE) {
				buffer = sdhci_kmap_atomic(sg, &flags);
				WARN_ON(((long)buffer & PAGE_MASK) > (PAGE_SIZE - 3));
				/*
				 * This check is intended here to verify if the
				 * page offset plus alignment bytes is indeed
				 * within the same page.
				 */
				WARN_ON(((long)buffer & (PAGE_SIZE - 1)) >
					(PAGE_SIZE - (host->align_bytes - 1)));
				memcpy(align, buffer, offset);
				sdhci_kunmap_atomic(buffer, &flags);
			}

			/* tran, valid */
			sdhci_set_adma_desc(desc, align_addr, offset, 0x21);
			sdhci_set_adma_desc(host, desc, align_addr,
						offset, 0x21);

			BUG_ON(offset > 65536);

			align += 4;
			align_addr += 4;
			align += host->align_bytes;
			align_addr += host->align_bytes;

			desc += 8;
			desc += host->adma_desc_line_sz;

			addr += offset;
			len -= offset;
@@ -650,8 +675,8 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
		BUG_ON(len > 65536);

		/* tran, valid */
		sdhci_set_adma_desc(desc, addr, len, 0x21);
		desc += 8;
		sdhci_set_adma_desc(host, desc, addr, len, 0x21);
		desc += host->adma_desc_line_sz;

		/*
		 * If this triggers then we have a calculation bug
@@ -666,7 +691,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
		* Mark the last descriptor as the terminating descriptor
		*/
		if (desc != host->adma_desc) {
			desc -= 8;
			desc -= host->adma_desc_line_sz;
			desc[0] |= 0x2; /* end */
		}
	} else {
@@ -675,7 +700,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
		*/

		/* nop, end, valid */
		sdhci_set_adma_desc(desc, 0, 0, 0x3);
		sdhci_set_adma_desc(host, desc, 0, 0, 0x3);
	}

	/*
@@ -694,7 +719,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
					 DMA_TO_DEVICE);
	if (dma_mapping_error(mmc_dev(host->mmc), host->adma_addr))
		goto unmap_entries;
	BUG_ON(host->adma_addr & 0x3);
	BUG_ON(host->adma_addr & (host->align_bytes - 1));

	return 0;

@@ -737,15 +762,22 @@ static void sdhci_adma_table_post(struct sdhci_host *host,
		align = host->align_buffer;

		for_each_sg(data->sg, sg, host->sg_count, i) {
			if (sg_dma_address(sg) & 0x3) {
				size = 4 - (sg_dma_address(sg) & 0x3);
			if (sg_dma_address(sg) & (host->align_bytes - 1)) {
				size = host->align_bytes - (sg_dma_address(sg)
					& (host->align_bytes - 1));

				buffer = sdhci_kmap_atomic(sg, &flags);
				WARN_ON(((long)buffer & PAGE_MASK) > (PAGE_SIZE - 3));
				/*
				 * This check is intended here to verify if the
				 * page offset plus alignment bytes is indeed
				 * within the same page.
				 */
				WARN_ON(((long)buffer & (PAGE_SIZE - 1)) >
					(PAGE_SIZE - (host->align_bytes - 1)));
				memcpy(buffer, align, size);
				sdhci_kunmap_atomic(buffer, &flags);

				align += 4;
				align += host->align_bytes;
			}
		}
	}
@@ -935,9 +967,19 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
				 */
				WARN_ON(1);
				host->flags &= ~SDHCI_REQ_USE_DMA;
			} else {
				if (host->flags & SDHCI_USE_ADMA_64BIT) {
					sdhci_writel(host,
						(u32)host->adma_addr,
						SDHCI_ADMA_ADDRESS_LOW);
					sdhci_writel(host,
					  (u32)((u64)host->adma_addr
						  >> SDHCI_HI_SHIFT),
					  SDHCI_ADMA_ADDRESS_HIGH);
				} else {
					sdhci_writel(host, host->adma_addr,
					SDHCI_ADMA_ADDRESS);
						SDHCI_ADMA_ADDRESS_LOW);
				}
			}
		} else {
			int sg_cnt;
@@ -967,10 +1009,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_ADMA_64BIT)
				ctrl |= SDHCI_CTRL_ADMA64;
			else
				ctrl |= SDHCI_CTRL_ADMA32;
		} else {
			ctrl |= SDHCI_CTRL_SDMA;
		}
		sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
	}

@@ -2757,21 +2803,28 @@ static void sdhci_show_adma_error(struct sdhci_host *host)
{
	const char *name = mmc_hostname(host->mmc);
	u8 *desc = host->adma_desc;
	__le32 *dma;
	__le16 *len;
	u8 attr;

	sdhci_dumpregs(host);

	while (true) {
		dma = (__le32 *)(desc + 4);
		len = (__le16 *)(desc + 2);
		attr = *desc;

		if (host->flags & SDHCI_USE_ADMA_64BIT) {
			__le64 *dma = (__le64 *)(desc + 4);
			pr_info("%s: %p: DMA %llx, LEN 0x%04x, Attr=0x%02x\n",
			    name, desc, (long long)le64_to_cpu(*dma),
			    le16_to_cpu(*len), attr);
		} else {
			__le32 *dma = (__le32 *)(desc + 4);
			pr_info("%s: %p: DMA 0x%08x, LEN 0x%04x, Attr=0x%02x\n",
		    name, desc, le32_to_cpu(*dma), le16_to_cpu(*len), attr);
			    name, desc, le32_to_cpu(*dma), le16_to_cpu(*len),
			    attr);
		}

		desc += 8;
		desc += host->adma_desc_line_sz;

		if (attr & 2)
			break;
@@ -3282,6 +3335,25 @@ struct sdhci_host *sdhci_alloc_host(struct device *dev,

EXPORT_SYMBOL_GPL(sdhci_alloc_host);

#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
static int sdhci_is_adma2_64bit(struct sdhci_host *host)
{
	u32 caps;

	caps = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? host->caps :
		sdhci_readl(host, SDHCI_CAPABILITIES);

	if (caps & SDHCI_CAN_64BIT)
		return 1;
	return 0;
}
#else
static int sdhci_is_adma2_64bit(struct sdhci_host *host)
{
	return 0;
}
#endif

int sdhci_add_host(struct sdhci_host *host)
{
	struct mmc_host *mmc;
@@ -3334,8 +3406,11 @@ int sdhci_add_host(struct sdhci_host *host)
	}

	if ((host->version >= SDHCI_SPEC_200) &&
		(caps[0] & SDHCI_CAN_DO_ADMA2))
		(caps[0] & SDHCI_CAN_DO_ADMA2)) {
		host->flags |= SDHCI_USE_ADMA;
		if (sdhci_is_adma2_64bit(host))
			host->flags |= SDHCI_USE_ADMA_64BIT;
	}

	if ((host->quirks & SDHCI_QUIRK_BROKEN_ADMA) &&
		(host->flags & SDHCI_USE_ADMA)) {
@@ -3366,8 +3441,16 @@ int sdhci_add_host(struct sdhci_host *host)
		else
			host->adma_max_desc = 128;

		host->adma_desc_sz = (host->adma_max_desc * 2 + 1) * 4;
		host->align_buf_sz = host->adma_max_desc * 4;
		if (host->flags & SDHCI_USE_ADMA_64BIT) {
			host->adma_desc_line_sz = 12;
			host->align_bytes = 8;
		} else {
			host->adma_desc_line_sz = 8;
			host->align_bytes = 4;
		}
		host->adma_desc_sz = (host->adma_max_desc * 2 + 1) *
						host->adma_desc_line_sz;
		host->align_buf_sz = host->adma_max_desc * host->align_bytes;

		pr_debug("%s: %s: dma_desc_size: %d\n",
			mmc_hostname(host->mmc), __func__, host->adma_desc_sz);
@@ -3811,8 +3894,10 @@ int sdhci_add_host(struct sdhci_host *host)
		sdhci_clear_set_irqs(host, SDHCI_INT_DATA_END_BIT, 0);
	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_SDMA) ? "DMA" : "PIO");
		(host->flags & SDHCI_USE_ADMA) ?
		((host->flags & SDHCI_USE_ADMA_64BIT) ?
		"64-bit ADMA" : "32-bit ADMA") :
		((host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO"));

	sdhci_enable_card_detection(host);

+3 −1
Original line number Diff line number Diff line
@@ -235,7 +235,9 @@

/* 55-57 reserved */

#define SDHCI_ADMA_ADDRESS	0x58
#define SDHCI_HI_SHIFT 32
#define SDHCI_ADMA_ADDRESS_LOW	0x58 /* addr[0:31] */
#define SDHCI_ADMA_ADDRESS_HIGH	0x5C /* addr[32:63] */

/* 60-FB reserved */

+3 −0
Original line number Diff line number Diff line
@@ -202,6 +202,7 @@ struct sdhci_host {
#define SDHCI_HS200_NEEDS_TUNING (1<<10)	/* HS200 needs tuning */
#define SDHCI_USING_RETUNING_TIMER (1<<11)	/* Host is using a retuning timer for the card */
#define SDHCI_HS400_NEEDS_TUNING (1<<12)	/* HS400 needs tuning */
#define SDHCI_USE_ADMA_64BIT	 (1<<13)/* Host is 64-bit ADMA capable */

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

@@ -228,7 +229,9 @@ struct sdhci_host {
	u8 *align_buffer;	/* Bounce buffer */

	unsigned int adma_desc_sz; /* ADMA descriptor table size */
	unsigned int adma_desc_line_sz; /* ADMA descriptor line size */
	unsigned int align_buf_sz; /* Bounce buffer size */
	unsigned int align_bytes; /* Alignment bytes (4/8 for 32-bit/64-bit) */
	unsigned int adma_max_desc; /* Max ADMA descriptos (max sg segments) */

	dma_addr_t adma_addr;	/* Mapped ADMA descr. table */