Linux-mmc Archive on lore.kernel.org
 help / color / Atom feed
* [RFC PATCH V3 12/21] mmc: sdhci: UHS-II support, add hooks for additional operations
@ 2020-07-10 11:10 Ben Chuang
  0 siblings, 0 replies; only message in thread
From: Ben Chuang @ 2020-07-10 11:10 UTC (permalink / raw)
  To: adrian.hunter, ulf.hansson
  Cc: linux-mmc, linux-kernel, ben.chuang, takahiro.akashi, greg.tu,
	Ben Chuang

From: Ben Chuang <ben.chuang@genesyslogic.com.tw>

In this commit, UHS-II related operations will be called via a function
pointer array, sdhci_uhs2_ops, in order to make UHS-II support as
a kernel module.
This array will be initialized only if CONFIG_MMC_SDHCI_UHS2 is enabled
and when the UHS-II module is loaded. Otherwise, all the functions
stay void.

Signed-off-by: Ben Chuang <ben.chuang@genesyslogic.com.tw>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 drivers/mmc/host/sdhci.c | 152 ++++++++++++++++++++++++++++++++++-----
 1 file changed, 136 insertions(+), 16 deletions(-)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index aaf41954511a..5511649946b9 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -32,8 +32,12 @@
 #include <linux/mmc/card.h>
 #include <linux/mmc/sdio.h>
 #include <linux/mmc/slot-gpio.h>
+#include <linux/mmc/uhs2.h>
+#include <linux/pci.h>
 
 #include "sdhci.h"
+#include "sdhci-uhs2.h"
+#include "sdhci-pci.h"
 
 #define DRIVER_NAME "sdhci"
 
@@ -45,6 +49,11 @@
 
 #define MAX_TUNING_LOOP 40
 
+#if IS_ENABLED(CONFIG_MMC_SDHCI_UHS2)
+struct sdhci_uhs2_ops sdhci_uhs2_ops;
+EXPORT_SYMBOL_GPL(sdhci_uhs2_ops);
+#endif
+
 static unsigned int debug_quirks = 0;
 static unsigned int debug_quirks2;
 
@@ -1041,8 +1050,11 @@ EXPORT_SYMBOL_GPL(sdhci_set_data_timeout_irq);
 
 void __sdhci_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
 {
+	u8 count;
+
 	bool too_big = false;
-	u8 count = sdhci_calc_timeout(host, cmd, &too_big);
+
+	count = sdhci_calc_timeout(host, cmd, &too_big);
 
 	if (too_big &&
 	    host->quirks2 & SDHCI_QUIRK2_DISABLE_HW_TIMEOUT) {
@@ -1053,6 +1065,11 @@ void __sdhci_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
 	}
 
 	sdhci_writeb(host, count, SDHCI_TIMEOUT_CONTROL);
+
+	if (IS_ENABLED(CONFIG_MMC_SDHCI_UHS2) &&
+	    host->mmc->flags & MMC_UHS2_SUPPORT &&
+	    sdhci_uhs2_ops.set_timeout)
+		sdhci_uhs2_ops.set_timeout(host);
 }
 EXPORT_SYMBOL_GPL(__sdhci_set_timeout);
 
@@ -1191,7 +1208,14 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
 
 	sdhci_set_transfer_irqs(host);
 
-	sdhci_set_block_info(host, data);
+	if (IS_ENABLED(CONFIG_MMC_SDHCI_UHS2) &&
+	    host->mmc->flags & MMC_UHS2_SUPPORT &&
+	    host->mmc->flags & MMC_UHS2_INITIALIZED) {
+		sdhci_writew(host, data->blksz, SDHCI_UHS2_BLOCK_SIZE);
+		sdhci_writew(host, data->blocks, SDHCI_UHS2_BLOCK_COUNT);
+	} else {
+		sdhci_set_block_info(host, data);
+	}
 }
 
 #if IS_ENABLED(CONFIG_MMC_SDHCI_EXTERNAL_DMA)
@@ -1439,6 +1463,13 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
 	u16 mode = 0;
 	struct mmc_data *data = cmd->data;
 
+	if (IS_ENABLED(CONFIG_MMC_SDHCI_UHS2) &&
+	    host->mmc->flags & MMC_UHS2_SUPPORT) {
+		if (sdhci_uhs2_ops.set_transfer_mode)
+			sdhci_uhs2_ops.set_transfer_mode(host, cmd);
+		return;
+	}
+
 	if (data == NULL) {
 		if (host->quirks2 &
 			SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD) {
@@ -1570,6 +1601,12 @@ static void __sdhci_finish_data(struct sdhci_host *host, bool sw_data_timeout)
 	else
 		data->bytes_xfered = data->blksz * data->blocks;
 
+	if (IS_ENABLED(CONFIG_MMC_SDHCI_UHS2) &&
+	    host->mmc->flags & MMC_UHS2_INITIALIZED) {
+		__sdhci_finish_mrq(host, data->mrq);
+		return;
+	}
+
 	/*
 	 * Need to send CMD12 if -
 	 * a) open-ended multiblock transfer not using auto CMD12 (no CMD23)
@@ -1654,7 +1691,8 @@ static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
 			sdhci_prepare_data(host, cmd);
 	}
 
-	sdhci_writel(host, cmd->arg, SDHCI_ARGUMENT);
+	if (!IS_ENABLED(CONFIG_MMC_SDHCI_UHS2))
+		sdhci_writel(host, cmd->arg, SDHCI_ARGUMENT);
 
 	sdhci_set_transfer_mode(host, cmd);
 
@@ -1699,6 +1737,17 @@ static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
 	if (host->use_external_dma)
 		sdhci_external_dma_pre_transfer(host, cmd);
 
+	if (IS_ENABLED(CONFIG_MMC_SDHCI_UHS2) &&
+	    (host->mmc->flags & MMC_UHS2_SUPPORT)) {
+		if (sdhci_uhs2_ops.send_command)
+			sdhci_uhs2_ops.send_command(host, cmd);
+
+		return true;
+	}
+
+	if (IS_ENABLED(CONFIG_MMC_SDHCI_UHS2))
+		sdhci_writel(host, cmd->arg, SDHCI_ARGUMENT);
+
 	sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
 
 	return true;
@@ -1780,13 +1829,20 @@ static void sdhci_finish_command(struct sdhci_host *host)
 {
 	struct mmc_command *cmd = host->cmd;
 
-	host->cmd = NULL;
+	if (IS_ENABLED(CONFIG_MMC_SDHCI_UHS2) &&
+	    host->mmc->flags & MMC_UHS2_SUPPORT) {
+		if (sdhci_uhs2_ops.finish_command)
+			sdhci_uhs2_ops.finish_command(host);
+	} else {
+		host->cmd = NULL;
 
-	if (cmd->flags & MMC_RSP_PRESENT) {
-		if (cmd->flags & MMC_RSP_136) {
-			sdhci_read_rsp_136(host, cmd);
-		} else {
-			cmd->resp[0] = sdhci_readl(host, SDHCI_RESPONSE);
+		if (cmd->flags & MMC_RSP_PRESENT) {
+			if (cmd->flags & MMC_RSP_136) {
+				sdhci_read_rsp_136(host, cmd);
+			} else {
+				cmd->resp[0] = sdhci_readl(host,
+							   SDHCI_RESPONSE);
+			}
 		}
 	}
 
@@ -1809,6 +1865,7 @@ static void sdhci_finish_command(struct sdhci_host *host)
 		} else if (!(host->quirks & SDHCI_QUIRK_NO_BUSY_IRQ) &&
 			   cmd == host->data_cmd) {
 			/* Command complete before busy is ended */
+			host->cmd = NULL;
 			return;
 		}
 	}
@@ -1828,6 +1885,8 @@ static void sdhci_finish_command(struct sdhci_host *host)
 		if (!cmd->data)
 			__sdhci_finish_mrq(host, cmd->mrq);
 	}
+
+	host->cmd = NULL;
 }
 
 static u16 sdhci_get_preset_value(struct sdhci_host *host)
@@ -1855,6 +1914,11 @@ static u16 sdhci_get_preset_value(struct sdhci_host *host)
 	case MMC_TIMING_MMC_HS400:
 		preset = sdhci_readw(host, SDHCI_PRESET_FOR_HS400);
 		break;
+#if IS_ENABLED(CONFIG_MMC_SDHCI_UHS2)
+	case MMC_TIMING_UHS2:
+		preset = sdhci_readw(host, SDHCI_PRESET_FOR_UHS2);
+		break;
+#endif
 	default:
 		pr_warn("%s: Invalid UHS-I mode selected\n",
 			mmc_hostname(host->mmc));
@@ -2095,7 +2159,6 @@ void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode,
 			sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
 
 		pwr |= SDHCI_POWER_ON;
-
 		sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
 
 		if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)
@@ -2261,6 +2324,7 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 {
 	struct sdhci_host *host = mmc_priv(mmc);
 	u8 ctrl;
+	u16 ctrl_2;
 
 	if (ios->power_mode == MMC_POWER_UNDEFINED)
 		return;
@@ -2287,6 +2351,10 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 		sdhci_enable_preset_value(host, false);
 
 	if (!ios->clock || ios->clock != host->clock) {
+		if (IS_ENABLED(CONFIG_MMC_SDHCI_UHS2) &&
+		    ios->timing == MMC_TIMING_UHS2)
+			host->timing = ios->timing;
+
 		host->ops->set_clock(host, ios->clock);
 		host->clock = ios->clock;
 
@@ -2308,6 +2376,18 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 	else
 		sdhci_set_power(host, ios->power_mode, ios->vdd);
 
+	/* 4.0 host support */
+	if (host->version >= SDHCI_SPEC_400) {
+		/* UHS2 Support */
+		if (IS_ENABLED(CONFIG_MMC_SDHCI_UHS2) &&
+		    host->mmc->flags & MMC_UHS2_SUPPORT &&
+		    host->mmc->caps & MMC_CAP_UHS2) {
+			if (sdhci_uhs2_ops.do_set_ios)
+				sdhci_uhs2_ops.do_set_ios(host, ios);
+			return;
+		}
+	}
+
 	if (host->ops->platform_send_init_74_clocks)
 		host->ops->platform_send_init_74_clocks(host, ios->power_mode);
 
@@ -2331,7 +2411,7 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 	}
 
 	if (host->version >= SDHCI_SPEC_300) {
-		u16 clk, ctrl_2;
+		u16 clk;
 
 		if (!host->preset_enabled) {
 			sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
@@ -3173,11 +3253,19 @@ static bool sdhci_request_done(struct sdhci_host *host)
 			/* This is to force an update */
 			host->ops->set_clock(host, host->clock);
 
-		/* Spec says we should do both at the same time, but Ricoh
-		   controllers do not like that. */
-		sdhci_do_reset(host, SDHCI_RESET_CMD);
-		sdhci_do_reset(host, SDHCI_RESET_DATA);
-
+		if (IS_ENABLED(CONFIG_MMC_SDHCI_UHS2) &&
+		    host->mmc->flags & MMC_UHS2_INITIALIZED) {
+			if (sdhci_uhs2_ops.reset)
+				sdhci_uhs2_ops.reset(host,
+						     SDHCI_UHS2_SW_RESET_SD);
+		} else {
+			/*
+			 * Spec says we should do both at the same time, but
+			 * Ricoh controllers do not like that.
+			 */
+			sdhci_do_reset(host, SDHCI_RESET_CMD);
+			sdhci_do_reset(host, SDHCI_RESET_DATA);
+		}
 		host->pending_reset = false;
 	}
 
@@ -3532,6 +3620,13 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
 				  SDHCI_INT_BUS_POWER);
 		sdhci_writel(host, mask, SDHCI_INT_STATUS);
 
+		if (IS_ENABLED(CONFIG_MMC_SDHCI_UHS2) &&
+		    intmask & SDHCI_INT_ERROR &&
+		    host->mmc->flags & MMC_UHS2_SUPPORT) {
+			if (sdhci_uhs2_ops.irq)
+				sdhci_uhs2_ops.irq(host);
+		}
+
 		if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
 			u32 present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
 				      SDHCI_CARD_PRESENT;
@@ -4717,6 +4812,14 @@ int sdhci_setup_host(struct sdhci_host *host)
 		/* This may alter mmc->*_blk_* parameters */
 		sdhci_allocate_bounce_buffer(host);
 
+	if (IS_ENABLED(CONFIG_MMC_SDHCI_UHS2) &&
+	    host->version >= SDHCI_SPEC_400 &&
+	    sdhci_uhs2_ops.add_host) {
+		ret = sdhci_uhs2_ops.add_host(host, host->caps1);
+		if (ret)
+			goto unreg;
+	}
+
 	return 0;
 
 unreg:
@@ -4738,6 +4841,8 @@ void sdhci_cleanup_host(struct sdhci_host *host)
 {
 	struct mmc_host *mmc = host->mmc;
 
+	/* FIXME: Do we have to do some cleanup for UHS2 here? */
+
 	if (!IS_ERR(mmc->supply.vqmmc))
 		regulator_disable(mmc->supply.vqmmc);
 
@@ -4766,6 +4871,14 @@ int __sdhci_add_host(struct sdhci_host *host)
 		mmc->cqe_ops = NULL;
 	}
 
+	if ((mmc->caps & MMC_CAP_UHS2) && !host->v4_mode) {
+		/* host doesn't want to enable UHS2 support */
+		mmc->caps &= ~MMC_CAP_UHS2;
+		mmc->flags &= ~MMC_UHS2_SUPPORT;
+
+		/* FIXME: Do we have to do some cleanup here? */
+	}
+
 	host->complete_wq = alloc_workqueue("sdhci", flags, 0);
 	if (!host->complete_wq)
 		return -ENOMEM;
@@ -4812,6 +4925,9 @@ int __sdhci_add_host(struct sdhci_host *host)
 unled:
 	sdhci_led_unregister(host);
 unirq:
+	if (IS_ENABLED(CONFIG_MMC_SDHCI_UHS2) &&
+	    sdhci_uhs2_ops.remove_host)
+		sdhci_uhs2_ops.remove_host(host, 0);
 	sdhci_do_reset(host, SDHCI_RESET_ALL);
 	sdhci_writel(host, 0, SDHCI_INT_ENABLE);
 	sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
@@ -4869,6 +4985,10 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
 
 	sdhci_led_unregister(host);
 
+	if (IS_ENABLED(CONFIG_MMC_SDHCI_UHS2) &&
+	    sdhci_uhs2_ops.remove_host)
+		sdhci_uhs2_ops.remove_host(host, dead);
+
 	if (!dead)
 		sdhci_do_reset(host, SDHCI_RESET_ALL);
 
-- 
2.27.0


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, back to index

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-07-10 11:10 [RFC PATCH V3 12/21] mmc: sdhci: UHS-II support, add hooks for additional operations Ben Chuang

Linux-mmc Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-mmc/0 linux-mmc/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-mmc linux-mmc/ https://lore.kernel.org/linux-mmc \
		linux-mmc@vger.kernel.org
	public-inbox-index linux-mmc

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-mmc


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git