Skip to contentSkip to conversation

omap_hsmmc patches

PATCH In review Adrian Hunter 8 participants 31 messages May 6, 2011 – Oct 14, 2011

Subsystems (5)
lore
Share & tools
lore
#0 lore raw

v2 cover omap_hsmmc patches

Hi

Here is V2 of some patches for omap_hsmmc.

Changes in V2:

OMAP: sDMA: descriptor autoloading feature
- removed the feature entirely as per Tony Lindgren

mmc: omap_hsmmc: fix few bugs when set the clock divisor
- added cpu_relax() as per Grazvydas Ignotas

mmc: omap_hsmmc: fix missing mmc_release_host() in no_off case
- removed {} as per Sergei Shtylyov

OMAP: hsmmc: implement clock switcher
- changed to >= threshold as per Grazvydas Ignotas

Adrian Hunter (6):
mmc: omap_hsmmc: correct debug report error status mnemonics
OMAP: board-rm680: set MMC nomux flag
mmc: omap_hsmmc: ensure pbias configuration is always done
mmc: omap_hsmmc: fix oops in omap_hsmmc_dma_cb
OMAP: hsmmc: add platform data for eMMC hardware reset gpio
mmc: omap_hsmmc: add a hardware reset before initialization

Andy Shevchenko (8):
mmc: omap_hsmmc: move hardcoded frequency constants to definition block
mmc: omap_hsmmc: reduce a bit the error handlers in probe()
mmc: omap_hsmmc: split duplicate code to calc_divisor() function
mmc: omap_hsmmc: introduce start_clock and re-use stop_clock
mmc: omap_hsmmc: fix few bugs when set the clock divisor
mmc: omap_hsmmc: split same pieces of code to separate functions
OMAP: hsmmc: implement clock switcher
mmc: omap_hsmmc: adjust host controller clock in regard to current OPP

Jarkko Lavinen (1):
OMAP: hsmmc: Do not mux the slot if non default muxing is already done

Sudhir Bera (1):
mmc: omap_hsmmc: fix missing mmc_release_host() in no_off case

 arch/arm/mach-omap2/board-rm680.c     |    1 +
 arch/arm/mach-omap2/hsmmc.c           |  188 +++++++++++++++++-
 arch/arm/mach-omap2/hsmmc.h           |    2 +
 arch/arm/plat-omap/include/plat/mmc.h |    9 +
 drivers/mmc/host/omap_hsmmc.c         |  353 ++++++++++++++++++++-------------
 5 files changed, 414 insertions(+), 139 deletions(-)

Regards
Adrian

#1 lore raw

PATCH v2 1/16 mmc: omap_hsmmc: fix missing mmc_release_host() in no_off case

From: Sudhir Bera <[email protected]>

In fact the no_off check here will not be hit because 'omap_hsmmc_disabled_to_sleep()' won't schedule a deeper disable in the no_off case.

---
 drivers/mmc/host/omap_hsmmc.c |   10 ++++------
 1 files changed, 4 insertions(+), 6 deletions(-)
drivers/mmc/host/omap_hsmmc.c +4 -6
index 259ece0..73faaec 100644
@@ -1770,15 +1770,13 @@ static int omap_hsmmc_sleep_to_off(struct omap_hsmmc_host *host)
 		return 0;
 
 	if (mmc_slot(host).no_off)
-		return 0;
+		goto out;
 
 	if (!((host->mmc->caps & MMC_CAP_NONREMOVABLE) ||
 	      mmc_slot(host).card_detect ||
 	      (mmc_slot(host).get_cover_state &&
-	       mmc_slot(host).get_cover_state(host->dev, host->slot_id)))) {
-		mmc_release_host(host->mmc);
-		return 0;
-	}
+	       mmc_slot(host).get_cover_state(host->dev, host->slot_id))))
+		goto out;
 
 	mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
 	host->vdd = 0;
@@ -1788,7 +1786,7 @@ static int omap_hsmmc_sleep_to_off(struct omap_hsmmc_host *host)
 		host->dpm_state == CARDSLEEP ? "CARDSLEEP" : "REGSLEEP");
 
 	host->dpm_state = OFF;
-
+out:
 	mmc_release_host(host->mmc);
 
 	return 0;
Signature · 1 line
1.7.0.4
#2 lore raw

PATCH v2 2/16 mmc: omap_hsmmc: correct debug report error status mnemonics

In reply to Adrian Hunter

CERR and BADA were in the wrong place and there are only 32 not 35.

---
 drivers/mmc/host/omap_hsmmc.c |   19 +++++++++++--------
 1 files changed, 11 insertions(+), 8 deletions(-)
drivers/mmc/host/omap_hsmmc.c +11 -8
index 73faaec..0fed8eb 100644
@@ -970,14 +970,14 @@ static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno)
  * Readable error output
  */
 #ifdef CONFIG_MMC_DEBUG
-static void omap_hsmmc_report_irq(struct omap_hsmmc_host *host, u32 status)
+static void omap_hsmmc_dbg_report_irq(struct omap_hsmmc_host *host, u32 status)
 {
 	/* --- means reserved bit without definition at documentation */
 	static const char *omap_hsmmc_status_bits[] = {
-		"CC", "TC", "BGE", "---", "BWR", "BRR", "---", "---", "CIRQ",
-		"OBI", "---", "---", "---", "---", "---", "ERRI", "CTO", "CCRC",
-		"CEB", "CIE", "DTO", "DCRC", "DEB", "---", "ACE", "---",
-		"---", "---", "---", "CERR", "CERR", "BADA", "---", "---", "---"
+		"CC"  , "TC"  , "BGE", "---", "BWR" , "BRR" , "---" , "---" ,
+		"CIRQ",	"OBI" , "---", "---", "---" , "---" , "---" , "ERRI",
+		"CTO" , "CCRC", "CEB", "CIE", "DTO" , "DCRC", "DEB" , "---" ,
+		"ACE" , "---" , "---", "---", "CERR", "BADA", "---" , "---"
 	};
 	char res[256];
 	char *buf = res;
@@ -994,6 +994,11 @@ static void omap_hsmmc_report_irq(struct omap_hsmmc_host *host, u32 status)
 
 	dev_dbg(mmc_dev(host->mmc), "%s\n", res);
 }
+#else
+static inline void omap_hsmmc_dbg_report_irq(struct omap_hsmmc_host *host,
+					     u32 status)
+{
+}
 #endif  /* CONFIG_MMC_DEBUG */
 
 /*
@@ -1052,9 +1057,7 @@ static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
 	dev_dbg(mmc_dev(host->mmc), "IRQ Status is %x\n", status);
 
 	if (status & ERR) {
-#ifdef CONFIG_MMC_DEBUG
-		omap_hsmmc_report_irq(host, status);
-#endif
+		omap_hsmmc_dbg_report_irq(host, status);
 		if ((status & CMD_TIMEOUT) ||
 			(status & CMD_CRC)) {
 			if (host->cmd) {
Signature · 1 line
1.7.0.4
#3 lore raw

PATCH v2 3/16 mmc: omap_hsmmc: move hardcoded frequency constants to definition block

In reply to Adrian Hunter

From: Andy Shevchenko <[email protected]>

Move the min and max frequency constants to the definition block in the source file.

---
 drivers/mmc/host/omap_hsmmc.c |    6 ++++--
 1 files changed, 4 insertions(+), 2 deletions(-)
drivers/mmc/host/omap_hsmmc.c +4 -2
index 0fed8eb..c8aab47 100644
@@ -118,6 +118,8 @@
 
 #define MMC_TIMEOUT_MS		20
 #define OMAP_MMC_MASTER_CLOCK	96000000
+#define OMAP_MMC_MIN_CLOCK	400000
+#define OMAP_MMC_MAX_CLOCK	52000000
 #define DRIVER_NAME		"omap_hsmmc"
 
 /* Timeouts for entering power saving states on inactivity, msec */
@@ -2091,8 +2093,8 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
 	if (mmc_slot(host).vcc_aux_disable_is_sleep)
 		mmc_slot(host).no_off = 1;
 
-	mmc->f_min	= 400000;
-	mmc->f_max	= 52000000;
+	mmc->f_min	= OMAP_MMC_MIN_CLOCK;
+	mmc->f_max	= OMAP_MMC_MAX_CLOCK;
 
 	spin_lock_init(&host->irq_lock);
 
Signature · 1 line
1.7.0.4
#4 lore raw

PATCH v2 4/16 mmc: omap_hsmmc: reduce a bit the error handlers in probe()

In reply to Adrian Hunter

From: Andy Shevchenko <[email protected]>

---
 drivers/mmc/host/omap_hsmmc.c |   17 ++++++-----------
 1 files changed, 6 insertions(+), 11 deletions(-)
drivers/mmc/host/omap_hsmmc.c +6 -11
index c8aab47..3121293 100644
@@ -2119,18 +2119,11 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
 	/* we start off in DISABLED state */
 	host->dpm_state = DISABLED;
 
-	if (clk_enable(host->iclk) != 0) {
-		clk_put(host->iclk);
-		clk_put(host->fclk);
-		goto err1;
-	}
+	if (clk_enable(host->iclk) != 0)
+		goto err2;
 
-	if (mmc_host_enable(host->mmc) != 0) {
-		clk_disable(host->iclk);
-		clk_put(host->iclk);
-		clk_put(host->fclk);
-		goto err1;
-	}
+	if (mmc_host_enable(host->mmc) != 0)
+		goto err3;
 
 	if (cpu_is_omap2430()) {
 		host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck");
@@ -2275,7 +2268,9 @@ err_irq_cd_init:
 	free_irq(host->irq, host);
 err_irq:
 	mmc_host_disable(host->mmc);
+err3:
 	clk_disable(host->iclk);
+err2:
 	clk_put(host->fclk);
 	clk_put(host->iclk);
 	if (host->got_dbclk) {
Signature · 1 line
1.7.0.4
#6 lore raw

PATCH v2 4/16 mmc: omap_hsmmc: reduce a bit the error handlers in probe()

Quoted 5 lines · Charulatha
Quoted 2 lines · Adrian Hunter

From: Andy Shevchenko <[email protected]>

Add patch description here.

You mean something like following:
"The code contains similarities in the error path of probe function. Let's combine them at one place."
?

Quoted 14 lines
Quoted 7 lines

---
?drivers/mmc/host/omap_hsmmc.c | ? 17 ++++++----------- ?1 files changed, 6 insertions(+), 11 deletions(-)

<<snip>>
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo at vger.kernel.org
More majordomo info at ?http://vger.kernel.org/majordomo-info.html

Signature · 2 lines
With Best Regards,
Andy Shevchenko
#7 lore raw

[PATCHv2.1] mmc: omap_hsmmc: reduce a bit the error handlers in probe()

In reply to Varadarajan, Charulatha

The code contains the similarities in the error path of probe function. Let's combine them at one place.

---
 drivers/mmc/host/omap_hsmmc.c |   17 ++++++-----------
 1 files changed, 6 insertions(+), 11 deletions(-)
drivers/mmc/host/omap_hsmmc.c +6 -11
index aeaf0cf..b9200ef 100644
@@ -2118,18 +2118,11 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
 	/* we start off in DISABLED state */
 	host->dpm_state = DISABLED;
 
-	if (clk_enable(host->iclk) != 0) {
-		clk_put(host->iclk);
-		clk_put(host->fclk);
-		goto err1;
-	}
+	if (clk_enable(host->iclk) != 0)
+		goto err2;
 
-	if (mmc_host_enable(host->mmc) != 0) {
-		clk_disable(host->iclk);
-		clk_put(host->iclk);
-		clk_put(host->fclk);
-		goto err1;
-	}
+	if (mmc_host_enable(host->mmc) != 0)
+		goto err3;
 
 	if (cpu_is_omap2430()) {
 		host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck");
@@ -2274,7 +2267,9 @@ err_irq_cd_init:
 	free_irq(host->irq, host);
 err_irq:
 	mmc_host_disable(host->mmc);
+err3:
 	clk_disable(host->iclk);
+err2:
 	clk_put(host->fclk);
 	clk_put(host->iclk);
 	if (host->got_dbclk) {
Signature · 1 line
1.6.3.3
#8 lore raw

PATCH v2 5/16 mmc: omap_hsmmc: split duplicate code to calc_divisor() function

In reply to Adrian Hunter

From: Andy Shevchenko <[email protected]>

There are two places where the same calculations are done. Let's split them to separate function.

In addition the new function is simplified by usage DIV_ROUND_UP kernel macro.

---
 drivers/mmc/host/omap_hsmmc.c |   45 ++++++++++++++++------------------------
 1 files changed, 18 insertions(+), 27 deletions(-)
drivers/mmc/host/omap_hsmmc.c +18 -27
index 3121293..af2dbd3 100644
@@ -584,6 +584,20 @@ static void omap_hsmmc_disable_irq(struct omap_hsmmc_host *host)
 	OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
 }
 
+/* Calculate divisor for the given clock frequency */
+static u16 calc_divisor(struct mmc_ios *ios)
+{
+	u16 dsor = 0;
+
+	if (ios->clock) {
+		dsor = DIV_ROUND_UP(OMAP_MMC_MASTER_CLOCK, ios->clock);
+		if (dsor > 250)
+			dsor = 250;
+	}
+
+	return dsor;
+}
+
 #ifdef CONFIG_PM
 
 /*
@@ -596,7 +610,6 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
 	struct omap_mmc_platform_data *pdata = host->pdata;
 	int context_loss = 0;
 	u32 hctl, capa, con;
-	u16 dsor = 0;
 	unsigned long timeout;
 
 	if (pdata->get_context_loss_count) {
@@ -675,21 +688,10 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
 		break;
 	}
 
-	if (ios->clock) {
-		dsor = OMAP_MMC_MASTER_CLOCK / ios->clock;
-		if (dsor < 1)
-			dsor = 1;
-
-		if (OMAP_MMC_MASTER_CLOCK / dsor > ios->clock)
-			dsor++;
-
-		if (dsor > 250)
-			dsor = 250;
-	}
-
 	OMAP_HSMMC_WRITE(host->base, SYSCTL,
 		OMAP_HSMMC_READ(host->base, SYSCTL) & ~CEN);
-	OMAP_HSMMC_WRITE(host->base, SYSCTL, (dsor << 6) | (DTO << 16));
+	OMAP_HSMMC_WRITE(host->base, SYSCTL,
+		(calc_divisor(ios) << 6) | (DTO << 16));
 	OMAP_HSMMC_WRITE(host->base, SYSCTL,
 		OMAP_HSMMC_READ(host->base, SYSCTL) | ICE);
 
@@ -1530,7 +1532,6 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
 static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 {
 	struct omap_hsmmc_host *host = mmc_priv(mmc);
-	u16 dsor = 0;
 	unsigned long regval;
 	unsigned long timeout;
 	u32 con;
@@ -1594,21 +1595,11 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 		}
 	}
 
-	if (ios->clock) {
-		dsor = OMAP_MMC_MASTER_CLOCK / ios->clock;
-		if (dsor < 1)
-			dsor = 1;
-
-		if (OMAP_MMC_MASTER_CLOCK / dsor > ios->clock)
-			dsor++;
-
-		if (dsor > 250)
-			dsor = 250;
-	}
 	omap_hsmmc_stop_clock(host);
+
 	regval = OMAP_HSMMC_READ(host->base, SYSCTL);
 	regval = regval & ~(CLKD_MASK);
-	regval = regval | (dsor << 6) | (DTO << 16);
+	regval = regval | (calc_divisor(ios) << 6) | (DTO << 16);
 	OMAP_HSMMC_WRITE(host->base, SYSCTL, regval);
 	OMAP_HSMMC_WRITE(host->base, SYSCTL,
 		OMAP_HSMMC_READ(host->base, SYSCTL) | ICE);
Signature · 1 line
1.7.0.4
#9 lore raw

[PATCHv2.1] mmc: omap_hsmmc: split duplicate code to calc_divisor() function

There are two places where the same calculations are done. Let's split them to separate function.

In addition the new function is simplified by usage DIV_ROUND_UP kernel macro.

---
 drivers/mmc/host/omap_hsmmc.c |   46 +++++++++++++++++------------------------
 1 files changed, 19 insertions(+), 27 deletions(-)
drivers/mmc/host/omap_hsmmc.c +19 -27
index b9200ef..8787ba8 100644
@@ -17,6 +17,7 @@
 
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/kernel.h>
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
 #include <linux/interrupt.h>
@@ -584,6 +585,20 @@ static void omap_hsmmc_disable_irq(struct omap_hsmmc_host *host)
 	OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
 }
 
+/* Calculate divisor for the given clock frequency */
+static u16 calc_divisor(struct mmc_ios *ios)
+{
+	u16 dsor = 0;
+
+	if (ios->clock) {
+		dsor = DIV_ROUND_UP(OMAP_MMC_MASTER_CLOCK, ios->clock);
+		if (dsor > 250)
+			dsor = 250;
+	}
+
+	return dsor;
+}
+
 #ifdef CONFIG_PM
 
 /*
@@ -596,7 +611,6 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
 	struct omap_mmc_platform_data *pdata = host->pdata;
 	int context_loss = 0;
 	u32 hctl, capa, con;
-	u16 dsor = 0;
 	unsigned long timeout;
 
 	if (pdata->get_context_loss_count) {
@@ -675,21 +689,10 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
 		break;
 	}
 
-	if (ios->clock) {
-		dsor = OMAP_MMC_MASTER_CLOCK / ios->clock;
-		if (dsor < 1)
-			dsor = 1;
-
-		if (OMAP_MMC_MASTER_CLOCK / dsor > ios->clock)
-			dsor++;
-
-		if (dsor > 250)
-			dsor = 250;
-	}
-
 	OMAP_HSMMC_WRITE(host->base, SYSCTL,
 		OMAP_HSMMC_READ(host->base, SYSCTL) & ~CEN);
-	OMAP_HSMMC_WRITE(host->base, SYSCTL, (dsor << 6) | (DTO << 16));
+	OMAP_HSMMC_WRITE(host->base, SYSCTL,
+		(calc_divisor(ios) << 6) | (DTO << 16));
 	OMAP_HSMMC_WRITE(host->base, SYSCTL,
 		OMAP_HSMMC_READ(host->base, SYSCTL) | ICE);
 
@@ -1527,7 +1530,6 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
 static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 {
 	struct omap_hsmmc_host *host = mmc_priv(mmc);
-	u16 dsor = 0;
 	unsigned long regval;
 	unsigned long timeout;
 	u32 con;
@@ -1591,21 +1593,11 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 		}
 	}
 
-	if (ios->clock) {
-		dsor = OMAP_MMC_MASTER_CLOCK / ios->clock;
-		if (dsor < 1)
-			dsor = 1;
-
-		if (OMAP_MMC_MASTER_CLOCK / dsor > ios->clock)
-			dsor++;
-
-		if (dsor > 250)
-			dsor = 250;
-	}
 	omap_hsmmc_stop_clock(host);
+
 	regval = OMAP_HSMMC_READ(host->base, SYSCTL);
 	regval = regval & ~(CLKD_MASK);
-	regval = regval | (dsor << 6) | (DTO << 16);
+	regval = regval | (calc_divisor(ios) << 6) | (DTO << 16);
 	OMAP_HSMMC_WRITE(host->base, SYSCTL, regval);
 	OMAP_HSMMC_WRITE(host->base, SYSCTL,
 		OMAP_HSMMC_READ(host->base, SYSCTL) | ICE);
Signature · 1 line
1.6.3.3
#10 lore raw

PATCH v2 6/16 mmc: omap_hsmmc: introduce start_clock and re-use stop_clock

In reply to Adrian Hunter

From: Andy Shevchenko <[email protected]>

There is similar piece of code in two functions which enables clock. Split this code to omap_hsmmc_start_clock(). Re-use omap_hsmmc_stop_clock() in omap_hsmmc_context_restore() as well.

---
 drivers/mmc/host/omap_hsmmc.c |   19 +++++++++++++------
 1 files changed, 13 insertions(+), 6 deletions(-)
drivers/mmc/host/omap_hsmmc.c +13 -6
index af2dbd3..915d08b 100644
@@ -548,6 +548,15 @@ static void omap_hsmmc_gpio_free(struct omap_mmc_platform_data *pdata)
 }
 
 /*
+ * Start clock to the card
+ */
+static void omap_hsmmc_start_clock(struct omap_hsmmc_host *host)
+{
+	OMAP_HSMMC_WRITE(host->base, SYSCTL,
+		OMAP_HSMMC_READ(host->base, SYSCTL) | CEN);
+}
+
+/*
  * Stop clock to the card
  */
 static void omap_hsmmc_stop_clock(struct omap_hsmmc_host *host)
@@ -688,8 +697,8 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
 		break;
 	}
 
-	OMAP_HSMMC_WRITE(host->base, SYSCTL,
-		OMAP_HSMMC_READ(host->base, SYSCTL) & ~CEN);
+	omap_hsmmc_stop_clock(host);
+
 	OMAP_HSMMC_WRITE(host->base, SYSCTL,
 		(calc_divisor(ios) << 6) | (DTO << 16));
 	OMAP_HSMMC_WRITE(host->base, SYSCTL,
@@ -700,8 +709,7 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
 		&& time_before(jiffies, timeout))
 		;
 
-	OMAP_HSMMC_WRITE(host->base, SYSCTL,
-		OMAP_HSMMC_READ(host->base, SYSCTL) | CEN);
+	omap_hsmmc_start_clock(host);
 
 	con = OMAP_HSMMC_READ(host->base, CON);
 	if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
@@ -1610,8 +1618,7 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 		&& time_before(jiffies, timeout))
 		msleep(1);
 
-	OMAP_HSMMC_WRITE(host->base, SYSCTL,
-		OMAP_HSMMC_READ(host->base, SYSCTL) | CEN);
+	omap_hsmmc_start_clock(host);
 
 	if (do_send_init_stream)
 		send_init_stream(host);
Signature · 1 line
1.7.0.4
#11 lore raw

PATCH v2 7/16 mmc: omap_hsmmc: fix few bugs when set the clock divisor

In reply to Adrian Hunter

From: Andy Shevchenko <[email protected]>

There are two pieces of code which similar, but not the same. Each of them contains a bug.

The SYSCTL register should be read before write in the omap_hsmmc_context_restore() to remain the state of the reserved bits.

Before set the clock divisor and DTO bits the value from the SYSCTL register should be masked properly. We were lucky to have no problems with DTO bits. So, make sure we have clear DTO bits properly in the omap_hsmmc_set_ios().

Additionally get rid of msleep(1). The actual time rare higher than 30us on OMAP 3630.

The result pieces of code are split to omap_hsmmc_set_clock() function.

---
 drivers/mmc/host/omap_hsmmc.c |   59 +++++++++++++++++++---------------------
 1 files changed, 28 insertions(+), 31 deletions(-)
drivers/mmc/host/omap_hsmmc.c +28 -31
index 915d08b..dd0d9d5 100644
@@ -607,6 +607,32 @@ static u16 calc_divisor(struct mmc_ios *ios)
 	return dsor;
 }
 
+static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host)
+{
+	struct mmc_ios *ios = &host->mmc->ios;
+	unsigned long regval;
+	unsigned long timeout;
+
+	dev_dbg(mmc_dev(host->mmc), "Set clock to %uHz\n", ios->clock);
+
+	omap_hsmmc_stop_clock(host);
+
+	regval = OMAP_HSMMC_READ(host->base, SYSCTL);
+	regval = regval & ~(CLKD_MASK | DTO_MASK);
+	regval = regval | (calc_divisor(ios) << 6) | (DTO << 16);
+	OMAP_HSMMC_WRITE(host->base, SYSCTL, regval);
+	OMAP_HSMMC_WRITE(host->base, SYSCTL,
+		OMAP_HSMMC_READ(host->base, SYSCTL) | ICE);
+
+	/* Wait till the ICS bit is set */
+	timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
+	while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != ICS
+		&& time_before(jiffies, timeout))
+		cpu_relax();
+
+	omap_hsmmc_start_clock(host);
+}
+
 #ifdef CONFIG_PM
 
 /*
@@ -697,19 +723,7 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
 		break;
 	}
 
-	omap_hsmmc_stop_clock(host);
-
-	OMAP_HSMMC_WRITE(host->base, SYSCTL,
-		(calc_divisor(ios) << 6) | (DTO << 16));
-	OMAP_HSMMC_WRITE(host->base, SYSCTL,
-		OMAP_HSMMC_READ(host->base, SYSCTL) | ICE);
-
-	timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
-	while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != ICS
-		&& time_before(jiffies, timeout))
-		;
-
-	omap_hsmmc_start_clock(host);
+	omap_hsmmc_set_clock(host);
 
 	con = OMAP_HSMMC_READ(host->base, CON);
 	if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
@@ -1540,8 +1554,6 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
 static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 {
 	struct omap_hsmmc_host *host = mmc_priv(mmc);
-	unsigned long regval;
-	unsigned long timeout;
 	u32 con;
 	int do_send_init_stream = 0;
 
@@ -1603,22 +1615,7 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 		}
 	}
 
-	omap_hsmmc_stop_clock(host);
-
-	regval = OMAP_HSMMC_READ(host->base, SYSCTL);
-	regval = regval & ~(CLKD_MASK);
-	regval = regval | (calc_divisor(ios) << 6) | (DTO << 16);
-	OMAP_HSMMC_WRITE(host->base, SYSCTL, regval);
-	OMAP_HSMMC_WRITE(host->base, SYSCTL,
-		OMAP_HSMMC_READ(host->base, SYSCTL) | ICE);
-
-	/* Wait till the ICS bit is set */
-	timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
-	while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != ICS
-		&& time_before(jiffies, timeout))
-		msleep(1);
-
-	omap_hsmmc_start_clock(host);
+	omap_hsmmc_set_clock(host);
 
 	if (do_send_init_stream)
 		send_init_stream(host);
Signature · 1 line
1.7.0.4
#12 lore raw

PATCH v2 8/16 mmc: omap_hsmmc: split same pieces of code to separate functions

In reply to Adrian Hunter

From: Andy Shevchenko <[email protected]>

There are few places with the same functionality. This patch creates two functions omap_hsmmc_set_bus_width() and omap_hsmmc_set_bus_mode() to do the job.

---
 drivers/mmc/host/omap_hsmmc.c |   85 ++++++++++++++++++++---------------------
 1 files changed, 41 insertions(+), 44 deletions(-)
drivers/mmc/host/omap_hsmmc.c +41 -44
index dd0d9d5..4f6e552 100644
@@ -633,6 +633,41 @@ static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host)
 	omap_hsmmc_start_clock(host);
 }
 
+static void omap_hsmmc_set_bus_width(struct omap_hsmmc_host *host)
+{
+	struct mmc_ios *ios = &host->mmc->ios;
+	u32 con;
+
+	con = OMAP_HSMMC_READ(host->base, CON);
+	switch (ios->bus_width) {
+	case MMC_BUS_WIDTH_8:
+		OMAP_HSMMC_WRITE(host->base, CON, con | DW8);
+		break;
+	case MMC_BUS_WIDTH_4:
+		OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
+		OMAP_HSMMC_WRITE(host->base, HCTL,
+			OMAP_HSMMC_READ(host->base, HCTL) | FOUR_BIT);
+		break;
+	case MMC_BUS_WIDTH_1:
+		OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
+		OMAP_HSMMC_WRITE(host->base, HCTL,
+			OMAP_HSMMC_READ(host->base, HCTL) & ~FOUR_BIT);
+		break;
+	}
+}
+
+static void omap_hsmmc_set_bus_mode(struct omap_hsmmc_host *host)
+{
+	struct mmc_ios *ios = &host->mmc->ios;
+	u32 con;
+
+	con = OMAP_HSMMC_READ(host->base, CON);
+	if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
+		OMAP_HSMMC_WRITE(host->base, CON, con | OD);
+	else
+		OMAP_HSMMC_WRITE(host->base, CON, con & ~OD);
+}
+
 #ifdef CONFIG_PM
 
 /*
@@ -644,7 +679,7 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
 	struct mmc_ios *ios = &host->mmc->ios;
 	struct omap_mmc_platform_data *pdata = host->pdata;
 	int context_loss = 0;
-	u32 hctl, capa, con;
+	u32 hctl, capa;
 	unsigned long timeout;
 
 	if (pdata->get_context_loss_count) {
@@ -706,30 +741,12 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
 	if (host->power_mode == MMC_POWER_OFF)
 		goto out;
 
-	con = OMAP_HSMMC_READ(host->base, CON);
-	switch (ios->bus_width) {
-	case MMC_BUS_WIDTH_8:
-		OMAP_HSMMC_WRITE(host->base, CON, con | DW8);
-		break;
-	case MMC_BUS_WIDTH_4:
-		OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
-		OMAP_HSMMC_WRITE(host->base, HCTL,
-			OMAP_HSMMC_READ(host->base, HCTL) | FOUR_BIT);
-		break;
-	case MMC_BUS_WIDTH_1:
-		OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
-		OMAP_HSMMC_WRITE(host->base, HCTL,
-			OMAP_HSMMC_READ(host->base, HCTL) & ~FOUR_BIT);
-		break;
-	}
+	omap_hsmmc_set_bus_width(host);
 
 	omap_hsmmc_set_clock(host);
 
-	con = OMAP_HSMMC_READ(host->base, CON);
-	if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
-		OMAP_HSMMC_WRITE(host->base, CON, con | OD);
-	else
-		OMAP_HSMMC_WRITE(host->base, CON, con & ~OD);
+	omap_hsmmc_set_bus_mode(host);
+
 out:
 	host->context_loss = context_loss;
 
@@ -1554,7 +1571,6 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
 static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 {
 	struct omap_hsmmc_host *host = mmc_priv(mmc);
-	u32 con;
 	int do_send_init_stream = 0;
 
 	mmc_host_enable(host->mmc);
@@ -1580,22 +1596,7 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 
 	/* FIXME: set registers based only on changes to ios */
 
-	con = OMAP_HSMMC_READ(host->base, CON);
-	switch (mmc->ios.bus_width) {
-	case MMC_BUS_WIDTH_8:
-		OMAP_HSMMC_WRITE(host->base, CON, con | DW8);
-		break;
-	case MMC_BUS_WIDTH_4:
-		OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
-		OMAP_HSMMC_WRITE(host->base, HCTL,
-			OMAP_HSMMC_READ(host->base, HCTL) | FOUR_BIT);
-		break;
-	case MMC_BUS_WIDTH_1:
-		OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
-		OMAP_HSMMC_WRITE(host->base, HCTL,
-			OMAP_HSMMC_READ(host->base, HCTL) & ~FOUR_BIT);
-		break;
-	}
+	omap_hsmmc_set_bus_width(host);
 
 	if (host->pdata->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT) {
 		/* Only MMC1 can interface at 3V without some flavor
@@ -1620,11 +1621,7 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 	if (do_send_init_stream)
 		send_init_stream(host);
 
-	con = OMAP_HSMMC_READ(host->base, CON);
-	if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
-		OMAP_HSMMC_WRITE(host->base, CON, con | OD);
-	else
-		OMAP_HSMMC_WRITE(host->base, CON, con & ~OD);
+	omap_hsmmc_set_bus_mode(host);
 
 	if (host->power_mode == MMC_POWER_OFF)
 		mmc_host_disable(host->mmc);
Signature · 1 line
1.7.0.4
#13 lore raw

PATCH v2 9/16 OMAP: hsmmc: Do not mux the slot if non default muxing is already done

In reply to Adrian Hunter

From: Jarkko Lavinen <[email protected]>

Allow the bootloader do all the muxing.

---
 arch/arm/mach-omap2/hsmmc.c |    3 ++-
 arch/arm/mach-omap2/hsmmc.h |    1 +
 2 files changed, 3 insertions(+), 1 deletions(-)
arch/arm/mach-omap2/hsmmc.c +2 -1
index b2f30be..6b97fae 100644
@@ -440,7 +440,8 @@ void __init omap_init_hsmmc(struct omap2_hsmmc_info *hsmmcinfo, int ctrl_nr)
 		pr_err("%s fails!\n", __func__);
 		goto done;
 	}
-	omap_hsmmc_mux(mmc_data, (ctrl_nr - 1));
+	if (!hsmmcinfo->nomux)
+		omap_hsmmc_mux(mmc_data, (ctrl_nr - 1));
 
 	name = "omap_hsmmc";
 	ohl = omap_hsmmc_latency;
arch/arm/mach-omap2/hsmmc.h +1 -0
index f119348..0f2a87e 100644
@@ -19,6 +19,7 @@ struct omap2_hsmmc_info {
 	bool	power_saving;	/* Try to sleep or power off when possible */
 	bool	no_off;		/* power_saving and power is not to go off */
 	bool	vcc_aux_disable_is_sleep; /* Regulator off remapped to sleep */
+	bool    nomux;          /* No default muxing for this slot */
 	int	gpio_cd;	/* or -EINVAL */
 	int	gpio_wp;	/* or -EINVAL */
 	char	*name;		/* or NULL for default */
Signature · 1 line
1.7.0.4
#14 lore raw

PATCH v2 10/16 OMAP: board-rm680: set MMC nomux flag

In reply to Adrian Hunter

Let the bootloader do all the pad configuration.

---
 arch/arm/mach-omap2/board-rm680.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)
arch/arm/mach-omap2/board-rm680.c +1 -0
index 42d10b1..c4e821f 100644
@@ -116,6 +116,7 @@ static struct omap2_hsmmc_info mmc[] __initdata = {
 		.caps		= MMC_CAP_4_BIT_DATA | MMC_CAP_MMC_HIGHSPEED,
 		.gpio_cd	= -EINVAL,
 		.gpio_wp	= -EINVAL,
+		.nomux		= true,
 	},
 	{ /* Terminator */ }
 };
Signature · 1 line
1.7.0.4
#15 lore raw

PATCH v2 11/16 mmc: omap_hsmmc: ensure pbias configuration is always done

In reply to Adrian Hunter

Go through the driver's set_power() functions rather than calling regulator_enable/disable() directly because otherwise pbias configuration for MMC1 is not done.

---
 drivers/mmc/host/omap_hsmmc.c |   17 ++++++++---------
 1 files changed, 8 insertions(+), 9 deletions(-)
drivers/mmc/host/omap_hsmmc.c +8 -9
index 4f6e552..8aa9440 100644
@@ -445,15 +445,14 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
 		* framework is fixed, we need a workaround like this
 		* (which is safe for MMC, but not in general).
 		*/
-		if (regulator_is_enabled(host->vcc) > 0) {
-			regulator_enable(host->vcc);
-			regulator_disable(host->vcc);
-		}
-		if (host->vcc_aux) {
-			if (regulator_is_enabled(reg) > 0) {
-				regulator_enable(reg);
-				regulator_disable(reg);
-			}
+		if (regulator_is_enabled(host->vcc) > 0 ||
+		    (host->vcc_aux && regulator_is_enabled(host->vcc_aux))) {
+			int vdd = ffs(mmc_slot(host).ocr_mask) - 1;
+
+			mmc_slot(host).set_power(host->dev, host->slot_id,
+						 1, vdd);
+			mmc_slot(host).set_power(host->dev, host->slot_id,
+						 0, 0);
 		}
 	}
 
Signature · 1 line
1.7.0.4
#16 lore raw

PATCH v2 11/16 mmc: omap_hsmmc: ensure pbias configuration is always done

Quoted 3 lines · Adrian Hunter

Go through the driver's set_power() functions rather than calling regulator_enable/disable() directly because otherwise pbias configuration for MMC1 is not done.

Hi Chris,

Are you OK to queue this patch as bug fix. Rest of the patches of this series is either
merged or not needed. Should I rebase and repost this alone ?

FWIW:

Quoted 41 lines

---
?drivers/mmc/host/omap_hsmmc.c | ? 17 ++++++++--------- ?1 files changed, 8 insertions(+), 9 deletions(-)

drivers/mmc/host/omap_hsmmc.c +0 -0
index 4f6e552..8aa9440 100644
@@ -445,15 +445,14 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)

? ? ? ? ? ? ? ?* framework is fixed, we need a workaround like this ? ? ? ? ? ? ? ?* (which is safe for MMC, but not in general).
? ? ? ? ? ? ? ?*/
- ? ? ? ? ? ? ? if (regulator_is_enabled(host->vcc) > 0) {
- ? ? ? ? ? ? ? ? ? ? ? regulator_enable(host->vcc);
- ? ? ? ? ? ? ? ? ? ? ? regulator_disable(host->vcc);
- ? ? ? ? ? ? ? }
- ? ? ? ? ? ? ? if (host->vcc_aux) {
- ? ? ? ? ? ? ? ? ? ? ? if (regulator_is_enabled(reg) > 0) {
- ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? regulator_enable(reg);
- ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? regulator_disable(reg);
- ? ? ? ? ? ? ? ? ? ? ? }
+ ? ? ? ? ? ? ? if (regulator_is_enabled(host->vcc) > 0 ||
+ ? ? ? ? ? ? ? ? ? (host->vcc_aux && regulator_is_enabled(host->vcc_aux))) {
+ ? ? ? ? ? ? ? ? ? ? ? int vdd = ffs(mmc_slot(host).ocr_mask) - 1; +
+ ? ? ? ? ? ? ? ? ? ? ? mmc_slot(host).set_power(host->dev, host->slot_id,
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?1, vdd);
+ ? ? ? ? ? ? ? ? ? ? ? mmc_slot(host).set_power(host->dev, host->slot_id,
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?0, 0);
? ? ? ? ? ? ? ?}
? ? ? ?}

--
1.7.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo at vger.kernel.org
More majordomo info at ?http://vger.kernel.org/majordomo-info.html

#17 lore raw

PATCH v2 11/16 mmc: omap_hsmmc: ensure pbias configuration is always done

Hi,

Quoted 4 lines · Balaji
Quoted 3 lines · Adrian Hunter

Go through the driver's set_power() functions rather than calling regulator_enable/disable() directly because otherwise pbias configuration for MMC1 is not done.

Quoted 8 lines

Hi Chris,

Are you OK to queue this patch as bug fix. Rest of the patches of this series is either
merged or not needed. Should I rebase and repost this alone ?

FWIW:

Thanks, I've pushed this to mmc-next for 3.2 now.

- Chris.

Signature · 2 lines
Chris Ball   <[email protected]>   <http://printf.net/>
One Laptop Per Child
#18 lore raw

PATCH v2 12/16 mmc: omap_hsmmc: fix oops in omap_hsmmc_dma_cb

In reply to Adrian Hunter

In the case of an I/O error, the DMA will have been cleaned up in the MMC interrupt and the request structure pointer will be null.

In that case, it is essential to check if the DMA
DMA is over before dereferencing host->mrq->data.

Oops as follows:

<3>[ 2293.695281] wl1271: ERROR sdio read failed (-110)
<1>[ 2293.695739] Unable to handle kernel NULL pointer dereference at virtual
address 00000004
<1>[ 2293.703094] pgd = b0004000
<1>[ 2293.705780] [00000004] *pgd=00000000
<0>[ 2293.709381] Internal error: Oops: 17 [#1] PREEMPT
<0>[ 2293.714080] last sysfs file:
/sys/devices/platform/omapdss/manager0/cpr_enable
<4>[ 2293.721313] Modules linked in: ext2 dm_crypt xt_NFLOG xt_rateest

xt_RATEEST xt_condition iptable_filter ip_tables dm_mod xt_IDLETIMER nfnetlink_log nfnetlink as3645a ad58xx smiapp smiapp_power omap3_isp iovmm omap3_iommu iommu2 iommu wl12xx_spi bnep omaplfb bridgedriver g_file_storage cmt_speech ssi_protocol phonet hsi_char wl12xx_sdio wl12xx pvrsrvkm omap_ssi mailbox_mach vibra_spi radio_wl1273 mailbox bcm4751_gps lis3lv02d_i2c ak8975 lis3lv02d leds_lp5521 apds990x pn544 rtc_twl4030 twl4030_keypad twl4030_pwrbutton cmt twl5031_aci hci_h4p

<4>[ 2293.769287] CPU: 0    Not tainted  (2.6.32.36-dfl61-20111603 #1)
<4>[ 2293.775329] PC is at omap_hsmmc_dma_cb+0x18/0x140
<4>[ 2293.780029] LR is at omap2_dma_irq_handler+0x244/0x28c
<4>[ 2293.785156] pc : [<b024e180>]    lr : [<b0049ed0>]    psr: a00001d3
<4>[ 2293.785186] sp : edfb3c48  ip : edfb3bf8  fp : edfb3d3c
<4>[ 2293.796661] r10: fa05632c  r9 : fa056000  r8 : ee4889c0
<4>[ 2293.801910] r7 : 00000020  r6 : 00000001  r5 : edfb2000  r4 : ee53e1c0
<4>[ 2293.808441] r3 : 00000000  r2 : edfb3c48  r1 : 00000020  r0 : 00000007
<4>[ 2293.814971] Flags: NzCv  IRQs off  FIQs off  Mode SVC_32  ISA ARM
Segment kernel
<4>[ 2293.822479] Control: 10c5387d  Table: bc9f4019  DAC: 00000017
<0>[ 2293.828247] Process phy0 (pid: 313, stack limit = 0xedfb22e8)
<0>[ 2293.833984] Stack: (0xedfb3c48 to 0xedfb4000)
...
<4>[ 2294.084320] [<b024e180>] (omap_hsmmc_dma_cb+0x18/0x140) from [<b0049ed0>]
(omap2_dma_irq_handler+0x244/0x28c)
<4>[ 2294.094268] [<b0049ed0>] (omap2_dma_irq_handler+0x244/0x28c) from
[<b0093d44>] (handle_IRQ_event+0x34/0xf0)
<4>[ 2294.104034] [<b0093d44>] (handle_IRQ_event+0x34/0xf0) from [<b0095bec>]
(handle_level_irq+0xcc/0x170)
<4>[ 2294.113250] [<b0095bec>] (handle_level_irq+0xcc/0x170) from [<b002c068>]
(asm_do_IRQ+0x68/0x84)
<4>[ 2294.121978] [<b002c068>] (asm_do_IRQ+0x68/0x84) from [<b002ca84>]
(__irq_svc+0x44/0xa8)
<4>[ 2294.129974] Exception stack(0xedfb3cc0 to 0xedfb3d08)
<4>[ 2294.135040] 3cc0: 0021d5cd 00000000 3d20cc2d 00000002 edf7dd40 edfb2000
edf7dee8 edf7ddd8
<4>[ 2294.143249] 3ce0: 00000001 00000000 ee516080 edfb3d3c b04880b8 edfb3d08
b0059e5c b0344184
<4>[ 2294.151428] 3d00: 00000053 ffffffff
<4>[ 2294.154968] [<b002ca84>] (__irq_svc+0x44/0xa8) from [<b0344184>]
(schedule+0x248/0x3a8)
<4>[ 2294.162963] [<b0344184>] (schedule+0x248/0x3a8) from [<b03448ac>]
(schedule_timeout+0x1c/0x224)
<4>[ 2294.171691] [<b03448ac>] (schedule_timeout+0x1c/0x224) from [<b0344738>]
(wait_for_common+0xe8/0x1a8)
<4>[ 2294.180938] [<b0344738>] (wait_for_common+0xe8/0x1a8) from [<b02459f8>]
(mmc_wait_for_req+0x110/0x120)
<4>[ 2294.190277] [<b02459f8>] (mmc_wait_for_req+0x110/0x120) from [<b0249bac>]
(mmc_io_rw_extended+0x178/0x1e0)
<4>[ 2294.199951] [<b0249bac>] (mmc_io_rw_extended+0x178/0x1e0) from
[<b024ab54>] (sdio_io_rw_ext_helper+0x164/0x190)
<4>[ 2294.210052] [<b024ab54>] (sdio_io_rw_ext_helper+0x164/0x190) from
[<b024aba0>] (sdio_writesb+0x20/0x24)
<4>[ 2294.219512] [<b024aba0>] (sdio_writesb+0x20/0x24) from [<af0d50e0>]
(wl1271_sdio_raw_write+0x64/0xa4 [wl12xx_sdio])
<4>[ 2294.230041] [<af0d50e0>] (wl1271_sdio_raw_write+0x64/0xa4 [wl12xx_sdio])
from [<af0bd4bc>] (wl1271_tx_work_locked+0x548/0x5fc [wl12xx])
<4>[ 2294.242218] [<af0bd4bc>] (wl1271_tx_work_locked+0x548/0x5fc [wl12xx])
from [<af0ba944>] (wl1271_irq_work+0x230/0x314 [wl12xx])
<4>[ 2294.253631] [<af0ba944>] (wl1271_irq_work+0x230/0x314 [wl12xx]) from
[<b00725ac>] (worker_thread+0x174/0x224)
<4>[ 2294.263519] [<b00725ac>] (worker_thread+0x174/0x224) from [<b0075bac>]
(kthread+0x7c/0x84)
<4>[ 2294.271820] [<b0075bac>] (kthread+0x7c/0x84) from [<b002d950>]
(kernel_thread_exit+0x0/0x8)
<0>[ 2294.280181] Code: e5923008 e1a0200d e3c25d7f e3c5503f (e5936004)
<4>[ 2294.286529] ---[ end trace e8fb05c679bd87ff ]---
---
 drivers/mmc/host/omap_hsmmc.c |    3 ++-
 1 files changed, 2 insertions(+), 1 deletions(-)
drivers/mmc/host/omap_hsmmc.c +2 -1
index 8aa9440..ee57b9b 100644
@@ -1364,7 +1364,7 @@ static void omap_hsmmc_config_dma_params(struct omap_hsmmc_host *host,
 static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
 {
 	struct omap_hsmmc_host *host = cb_data;
-	struct mmc_data *data = host->mrq->data;
+	struct mmc_data *data;
 	int dma_ch, req_in_progress;
 
 	if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) {
@@ -1379,6 +1379,7 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
 		return;
 	}
 
+	data = host->mrq->data;
 	host->dma_sg_idx++;
 	if (host->dma_sg_idx < host->dma_len) {
 		/* Fire up the next transfer. */
Signature · 1 line
1.7.0.4
#19 lore raw

PATCH v2 13/16 OMAP: hsmmc: implement clock switcher

In reply to Adrian Hunter

From: Andy Shevchenko <[email protected]>

There are 3 new platform data methods which should help us to do a clock switching when notification is happened or request is started.

The purpose of the patch is to avoid high frequency of MMC controller on low OPPs due to an HW bug in OMAP 3630.

The algorithm:
- the PM governor switches the clock of L3 (and therefore L4) bus on demand
- the MMC controller clock should follow the change

We have considered two OPPs for L3/L4 bus. Thus, we have corresponding flow:
- OPP1 -> OPP2 (in case of abort - OPP1)
- OPP2 -> OPP1 (in case of abort - OPP2)

During idle the MMC gates functional clock and we don't care about the frequency. Most important to get proper solution when notification comes during request. Then:
- in case of OPP1 -> OPP2 we update frequency limit and it will be used for new coming requests (it assumes the current frequency of the controller is lower then new one)
- otherwise we wait until no device has used higher frequency then upcoming one

Known issues and limitations:
- originally a clock notifier was used for the core L4 iclk but for upstream Adrian changed to a cpufreq notifier where we assume OPP1 below 400MHz and OPP2 above 400MHz

---
 arch/arm/mach-omap2/hsmmc.c           |  180 ++++++++++++++++++++++++++++++++-
 arch/arm/plat-omap/include/plat/mmc.h |    8 ++
 2 files changed, 187 insertions(+), 1 deletions(-)
arch/arm/mach-omap2/hsmmc.c +179 -1
index 6b97fae..c37ba4f 100644
@@ -10,10 +10,15 @@
  * published by the Free Software Foundation.
  */
 #include <linux/kernel.h>
+#include <linux/err.h>
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/mmc/card.h>
 #include <mach/hardware.h>
+#include <plat/clock.h>
 #include <plat/mmc.h>
 #include <plat/omap-pm.h>
 #include <plat/mux.h>
@@ -23,6 +28,8 @@
 #include "hsmmc.h"
 #include "control.h"
 
+#define HSMMC_MAX_FREQ	48000000
+
 #if defined(CONFIG_MMC_OMAP_HS) || defined(CONFIG_MMC_OMAP_HS_MODULE)
 
 static u16 control_pbias_offset;
@@ -203,6 +210,155 @@ static int nop_mmc_set_power(struct device *dev, int slot, int power_on,
 	return 0;
 }
 
+#ifdef CONFIG_ARCH_OMAP3
+static struct hsmmc_max_freq_info {
+	struct device *dev;
+	int freq;
+	int high_speed;
+} hsmmc_max_freq_info[OMAP34XX_NR_MMC];
+
+static unsigned int hsmmc_max_freq = HSMMC_MAX_FREQ;
+static DEFINE_SPINLOCK(hsmmc_max_freq_lock);
+
+static DECLARE_WAIT_QUEUE_HEAD(hsmmc_max_freq_wq);
+
+static int hsmmc_high_speed(struct device *dev)
+{
+	void *drvdata = platform_get_drvdata(to_platform_device(dev));
+	struct mmc_host *mmc = container_of(drvdata, struct mmc_host, private);
+
+	return mmc->card ? mmc_card_highspeed(mmc->card) : 0;
+}
+
+static unsigned int hsmmc_get_max_freq_hs(struct device *dev, int high_speed)
+{
+	return high_speed ? hsmmc_max_freq : hsmmc_max_freq >> 1;
+}
+
+static unsigned int hsmmc_get_max_freq(struct device *dev)
+{
+	return hsmmc_get_max_freq_hs(dev, hsmmc_high_speed(dev));
+}
+
+static unsigned int hsmmc_active(struct device *dev, unsigned int target_freq)
+{
+	int high_speed = hsmmc_high_speed(dev);
+	int i;
+	unsigned int max_freq, freq;
+	unsigned long flags;
+
+	spin_lock_irqsave(&hsmmc_max_freq_lock, flags);
+	max_freq = hsmmc_get_max_freq_hs(dev, high_speed);
+	freq = min(target_freq, max_freq);
+	for (i = 0; i < ARRAY_SIZE(hsmmc_max_freq_info); i++) {
+		if (!hsmmc_max_freq_info[i].dev) {
+			hsmmc_max_freq_info[i].dev = dev;
+			hsmmc_max_freq_info[i].freq = freq;
+			hsmmc_max_freq_info[i].high_speed = high_speed;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&hsmmc_max_freq_lock, flags);
+	return freq;
+}
+
+static void hsmmc_inactive(struct device *dev)
+{
+	int i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&hsmmc_max_freq_lock, flags);
+	for (i = 0; i < ARRAY_SIZE(hsmmc_max_freq_info); i++) {
+		if (hsmmc_max_freq_info[i].dev == dev) {
+			hsmmc_max_freq_info[i].dev = NULL;
+			spin_unlock_irqrestore(&hsmmc_max_freq_lock, flags);
+			/*
+			 * Wake up the queue only in case we deactivated a
+			 * device.
+			 */
+			wake_up(&hsmmc_max_freq_wq);
+			return;
+		}
+	}
+	spin_unlock_irqrestore(&hsmmc_max_freq_lock, flags);
+}
+
+static bool hsmmc_max_freq_ok(void)
+{
+	int i;
+	bool ret = true;
+	unsigned long flags;
+
+	spin_lock_irqsave(&hsmmc_max_freq_lock, flags);
+	for (i = 0; i < ARRAY_SIZE(hsmmc_max_freq_info); i++) {
+		if (hsmmc_max_freq_info[i].dev) {
+			unsigned int max_freq;
+
+			if (hsmmc_max_freq_info[i].high_speed)
+				max_freq = HSMMC_MAX_FREQ >> 1;
+			else
+				max_freq = HSMMC_MAX_FREQ >> 2;
+
+			if (hsmmc_max_freq_info[i].freq > max_freq) {
+				ret = false;
+				break;
+			}
+		}
+	}
+	spin_unlock_irqrestore(&hsmmc_max_freq_lock, flags);
+	return ret;
+}
+
+static int hsmmc_clk_notifier(struct notifier_block *nb, unsigned long event,
+			      void *data)
+{
+	struct cpufreq_freqs *freqs = data;
+	unsigned int threshold = 400000; /* between opp1 and opp2 */
+
+	switch (event) {
+	case CPUFREQ_PRECHANGE:
+		if (freqs->new < threshold && freqs->old >= threshold) {
+			/* opp2 -> opp1 */
+			hsmmc_max_freq = HSMMC_MAX_FREQ >> 1;
+
+			/* Timeout is 1 sec */
+			if (!wait_event_timeout(hsmmc_max_freq_wq,
+						hsmmc_max_freq_ok(),
+						msecs_to_jiffies(1000)))
+				pr_err("MMC violates maximum frequency "
+				       "constraint\n");
+		}
+		break;
+	case CPUFREQ_POSTCHANGE:
+		if (freqs->old < threshold && freqs->new >= threshold) {
+			/* opp1 -> opp2 */
+			hsmmc_max_freq = HSMMC_MAX_FREQ;
+		}
+		break;
+	default:
+		break;
+	}
+
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block hsmmc_notifier_block = {
+	.notifier_call	= hsmmc_clk_notifier,
+	.priority	= INT_MAX,
+};
+
+static int __init hsmmc_init_notifier(void)
+{
+	return cpufreq_register_notifier(&hsmmc_notifier_block,
+					 CPUFREQ_TRANSITION_NOTIFIER);
+}
+#else
+static inline int hsmmc_init_notifier(void)
+{
+	return 0;
+}
+#endif
+
 static inline void omap_hsmmc_mux(struct omap_mmc_platform_data *mmc_controller,
 			int controller_nr)
 {
@@ -401,6 +557,17 @@ static int __init omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c,
 		kfree(hc_name);
 		return -ENODEV;
 	}
+
+	/*
+	 * The 3630's host controller cannot guarantee setup times at all
+	 * frequencies, so notification and control of frequency changes
+	 * is necessary.
+	 */
+	if (cpu_is_omap3630()) {
+		mmc->get_max_freq = hsmmc_get_max_freq;
+		mmc->active = hsmmc_active;
+		mmc->inactive = hsmmc_inactive;
+	}
 	return 0;
 }
 
@@ -507,9 +674,20 @@ void __init omap2_hsmmc_init(struct omap2_hsmmc_info *controllers)
 		omap4_ctrl_pad_writel(reg, control_mmc1);
 	}
 
+	/*
+	 * The 3630's host controller cannot guarantee setup times at all
+	 * frequencies, so notification and control of frequency changes
+	 * is necessary.
+	 */
+	if (cpu_is_omap3630()) {
+		if (hsmmc_init_notifier()) {
+			pr_err("Can't setup clock notifier for mmc driver!\n");
+			return;
+		}
+	}
+
 	for (; controllers->mmc; controllers++)
 		omap_init_hsmmc(controllers, controllers->mmc);
-
 }
 
 #endif
arch/arm/plat-omap/include/plat/mmc.h +8 -0
index f38fef9..e3c9b20 100644
@@ -27,6 +27,8 @@
 #define OMAP2420_MMC_SIZE	OMAP1_MMC_SIZE
 #define OMAP2_MMC1_BASE		0x4809c000
 
+#define OMAP34XX_NR_MMC		3
+
 #define OMAP4_MMC_REG_OFFSET	0x100
 
 #define OMAP_MMC_MAX_SLOTS	2
@@ -63,6 +65,12 @@ struct omap_mmc_platform_data {
 	/* Return context loss count due to PM states changing */
 	int (*get_context_loss_count)(struct device *dev);
 
+	/* Return max controller frequency for current OPP */
+	unsigned int (*get_max_freq)(struct device *dev);
+
+	unsigned int (*active)(struct device *dev, unsigned int target_freq);
+	void (*inactive)(struct device *dev);
+
 	u64 dma_mask;
 
 	/* Integrating attributes from the omap_hwmod layer */
Signature · 1 line
1.7.0.4
maintainer
#20 lore raw

PATCH v2 13/16 OMAP: hsmmc: implement clock switcher

Quoted 7 lines · Adrian Hunter

From: Andy Shevchenko <[email protected]>

There are 3 new platform data methods which should help us to do a clock switching when notification is happened or request is started.

The purpose of the patch is to avoid high frequency of MMC controller on low OPPs due to an HW bug in OMAP 3630.

...

Quoted 22 lines
arch/arm/mach-omap2/hsmmc.c +22 -0
+static int hsmmc_clk_notifier(struct notifier_block *nb, unsigned long event,
+			      void *data)
+{
+	struct cpufreq_freqs *freqs = data;
+	unsigned int threshold = 400000; /* between opp1 and opp2 */
+
+	switch (event) {
+	case CPUFREQ_PRECHANGE:
+		if (freqs->new < threshold && freqs->old >= threshold) {
+			/* opp2 -> opp1 */
+			hsmmc_max_freq = HSMMC_MAX_FREQ >> 1;
+
+			/* Timeout is 1 sec */
+			if (!wait_event_timeout(hsmmc_max_freq_wq,
+						hsmmc_max_freq_ok(),
+						msecs_to_jiffies(1000)))
+				pr_err("MMC violates maximum frequency "
+				       "constraint\n");
+		}
+		break;
+	case CPUFREQ_POSTCHANGE:
+		if (freqs->old < threshold && freqs->new >= threshold) {

arch/arm/mach-omap2/hsmmc.c static int nop_mmc_set_power(struct device *dev, int slot, int power_on, · line 343

Quoted 10 lines
arch/arm/mach-omap2/hsmmc.c +10 -0
+			/* opp1 -> opp2 */
+			hsmmc_max_freq = HSMMC_MAX_FREQ;
+		}
+		break;
+	default:
+		break;
+	}
+
+	return NOTIFY_DONE;
+}

I think the cpufreq notifier code should be in the driver, the platform init code should just prepare things for the driver.

Regards,

Tony

#21 lore raw

PATCH v2 14/16 mmc: omap_hsmmc: adjust host controller clock in regard to current OPP

In reply to Adrian Hunter

From: Andy Shevchenko <[email protected]>

We should like to adjust MMC host controller clock whenever the OPP is changed. OPP affects to L3/L4 bus frequency. Due to this we update the maximum frequency limits before each upcoming request and when the divisor is calculated.

---
 drivers/mmc/host/omap_hsmmc.c |   65 +++++++++++++++++++++++++++++++++++++----
 1 files changed, 59 insertions(+), 6 deletions(-)
drivers/mmc/host/omap_hsmmc.c +59 -6
index ee57b9b..e1c9017 100644
@@ -187,6 +187,9 @@ struct omap_hsmmc_host {
 	int			use_reg;
 	int			req_in_progress;
 
+	/* Actual output frequency of host controller */
+	unsigned int		freq;
+
 	struct	omap_mmc_platform_data	*pdata;
 };
 
@@ -592,13 +595,31 @@ static void omap_hsmmc_disable_irq(struct omap_hsmmc_host *host)
 	OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
 }
 
+/*
+ * Recalculate desired clock frequency with regard to maximum possible
+ * frequency at current OPP.
+ *
+ * Choose either target frequency (ios->clock) or maximum possible frequency at
+ * current OPP (get_max_freq() returns this limit)
+ */
+static void omap_hsmmc_recalc_freq(struct omap_hsmmc_host *host,
+				   struct mmc_ios *ios)
+{
+	struct omap_mmc_platform_data *pdata = host->pdata;
+
+	if (pdata->get_max_freq)
+		host->freq = min(ios->clock, pdata->get_max_freq(host->dev));
+	else
+		host->freq = ios->clock;
+}
+
 /* Calculate divisor for the given clock frequency */
-static u16 calc_divisor(struct mmc_ios *ios)
+static u16 calc_divisor(struct omap_hsmmc_host *host)
 {
 	u16 dsor = 0;
 
-	if (ios->clock) {
-		dsor = DIV_ROUND_UP(OMAP_MMC_MASTER_CLOCK, ios->clock);
+	if (host->freq) {
+		dsor = DIV_ROUND_UP(OMAP_MMC_MASTER_CLOCK, host->freq);
 		if (dsor > 250)
 			dsor = 250;
 	}
@@ -608,17 +629,16 @@ static u16 calc_divisor(struct mmc_ios *ios)
 
 static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host)
 {
-	struct mmc_ios *ios = &host->mmc->ios;
 	unsigned long regval;
 	unsigned long timeout;
 
-	dev_dbg(mmc_dev(host->mmc), "Set clock to %uHz\n", ios->clock);
+	dev_dbg(mmc_dev(host->mmc), "Set clock to %uHz\n", host->freq);
 
 	omap_hsmmc_stop_clock(host);
 
 	regval = OMAP_HSMMC_READ(host->base, SYSCTL);
 	regval = regval & ~(CLKD_MASK | DTO_MASK);
-	regval = regval | (calc_divisor(ios) << 6) | (DTO << 16);
+	regval = regval | (calc_divisor(host) << 6) | (DTO << 16);
 	OMAP_HSMMC_WRITE(host->base, SYSCTL, regval);
 	OMAP_HSMMC_WRITE(host->base, SYSCTL,
 		OMAP_HSMMC_READ(host->base, SYSCTL) | ICE);
@@ -742,6 +762,7 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
 
 	omap_hsmmc_set_bus_width(host);
 
+	omap_hsmmc_recalc_freq(host, ios);
 	omap_hsmmc_set_clock(host);
 
 	omap_hsmmc_set_bus_mode(host);
@@ -925,6 +946,8 @@ static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_req
 	if (mrq->data && host->use_dma && dma_ch != -1)
 		return;
 	host->mrq = NULL;
+	if (host->pdata->inactive)
+		host->pdata->inactive(host->dev);
 	mmc_request_done(host->mmc, mrq);
 }
 
@@ -984,6 +1007,9 @@ omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)
 	}
 	if ((host->data == NULL && !host->response_busy) || cmd->error)
 		omap_hsmmc_request_done(host, cmd->mrq);
+	else if (host->data == NULL && host->response_busy &&
+		 host->pdata->inactive)
+		host->pdata->inactive(host->dev);
 }
 
 /*
@@ -1404,6 +1430,8 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
 		struct mmc_request *mrq = host->mrq;
 
 		host->mrq = NULL;
+		if (host->pdata->inactive)
+			host->pdata->inactive(host->dev);
 		mmc_request_done(host->mmc, mrq);
 	}
 }
@@ -1533,6 +1561,26 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
 
 	BUG_ON(host->req_in_progress);
 	BUG_ON(host->dma_ch != -1);
+
+	if (host->pdata->active) {
+		unsigned int new_freq;
+
+		/*
+		 * active() returns minimum of two (target, maximum)
+		 * frequencies.
+		 */
+		new_freq = host->pdata->active(host->dev, mmc->ios.clock);
+
+		/*
+		 * We need to update actual frequency if it is not equal to the
+		 * minimum of two (target and maximum) frequencies
+		 */
+		if (host->freq != new_freq) {
+			host->freq = new_freq;
+			omap_hsmmc_set_clock(host);
+		}
+	}
+
 	if (host->protect_card) {
 		if (host->reqs_blocked < 3) {
 			/*
@@ -1548,6 +1596,8 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
 		if (req->data)
 			req->data->error = -EBADF;
 		req->cmd->retries = 0;
+		if (host->pdata->inactive)
+			host->pdata->inactive(host->dev);
 		mmc_request_done(mmc, req);
 		return;
 	} else if (host->reqs_blocked)
@@ -1560,6 +1610,8 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
 		if (req->data)
 			req->data->error = err;
 		host->mrq = NULL;
+		if (host->pdata->inactive)
+			host->pdata->inactive(host->dev);
 		mmc_request_done(mmc, req);
 		return;
 	}
@@ -1616,6 +1668,7 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 		}
 	}
 
+	omap_hsmmc_recalc_freq(host, ios);
 	omap_hsmmc_set_clock(host);
 
 	if (do_send_init_stream)
Signature · 1 line
1.7.0.4
#22 lore raw

PATCH v2 15/16 OMAP: hsmmc: add platform data for eMMC hardware reset gpio

In reply to Adrian Hunter

eMMC may have a hardware reset line connected to a gpio, so pass it to the driver.

---
 arch/arm/mach-omap2/hsmmc.c           |    5 +++++
 arch/arm/mach-omap2/hsmmc.h           |    1 +
 arch/arm/plat-omap/include/plat/mmc.h |    1 +
 3 files changed, 7 insertions(+), 0 deletions(-)
arch/arm/mach-omap2/hsmmc.c +5 -0
index c37ba4f..a8078c1 100644
@@ -472,6 +472,11 @@ static int __init omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c,
 	mmc->slots[0].switch_pin = c->gpio_cd;
 	mmc->slots[0].gpio_wp = c->gpio_wp;
 
+	if (c->gpio_hw_reset)
+		mmc->slots[0].gpio_hw_reset = c->gpio_hw_reset;
+	else
+		mmc->slots[0].gpio_hw_reset = -EINVAL;
+
 	mmc->slots[0].remux = c->remux;
 	mmc->slots[0].init_card = c->init_card;
 
arch/arm/mach-omap2/hsmmc.h +1 -0
index 0f2a87e..b78ed41 100644
@@ -22,6 +22,7 @@ struct omap2_hsmmc_info {
 	bool    nomux;          /* No default muxing for this slot */
 	int	gpio_cd;	/* or -EINVAL */
 	int	gpio_wp;	/* or -EINVAL */
+	int     gpio_hw_reset;  /* hardware reset */
 	char	*name;		/* or NULL for default */
 	struct device *dev;	/* returned: pointer to mmc adapter */
 	int	ocr_mask;	/* temporary HACK */
arch/arm/plat-omap/include/plat/mmc.h +1 -0
index e3c9b20..9b69b7e 100644
@@ -119,6 +119,7 @@ struct omap_mmc_platform_data {
 
 		int switch_pin;			/* gpio (card detect) */
 		int gpio_wp;			/* gpio (write protect) */
+		int gpio_hw_reset;              /* gpio (hardware reset) */
 
 		int (*set_bus_mode)(struct device *dev, int slot, int bus_mode);
 		int (*set_power)(struct device *dev, int slot,
Signature · 1 line
1.7.0.4
maintainer
#23 lore raw

PATCH v2 15/16 OMAP: hsmmc: add platform data for eMMC hardware reset gpio

Quoted 4 lines · Adrian Hunter

eMMC may have a hardware reset line connected to a gpio, so pass it to the driver.

This looks safe to merge via MMC list:

Quoted 49 lines
---
 arch/arm/mach-omap2/hsmmc.c           |    5 +++++
 arch/arm/mach-omap2/hsmmc.h           |    1 +
 arch/arm/plat-omap/include/plat/mmc.h |    1 +
 3 files changed, 7 insertions(+), 0 deletions(-)
arch/arm/mach-omap2/hsmmc.c +5 -0
index c37ba4f..a8078c1 100644
@@ -472,6 +472,11 @@ static int __init omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c,
 	mmc->slots[0].switch_pin = c->gpio_cd;
 	mmc->slots[0].gpio_wp = c->gpio_wp;
 
+	if (c->gpio_hw_reset)
+		mmc->slots[0].gpio_hw_reset = c->gpio_hw_reset;
+	else
+		mmc->slots[0].gpio_hw_reset = -EINVAL;
+
 	mmc->slots[0].remux = c->remux;
 	mmc->slots[0].init_card = c->init_card;
 
arch/arm/mach-omap2/hsmmc.h +1 -0
index 0f2a87e..b78ed41 100644
@@ -22,6 +22,7 @@ struct omap2_hsmmc_info {
 	bool    nomux;          /* No default muxing for this slot */
 	int	gpio_cd;	/* or -EINVAL */
 	int	gpio_wp;	/* or -EINVAL */
+	int     gpio_hw_reset;  /* hardware reset */
 	char	*name;		/* or NULL for default */
 	struct device *dev;	/* returned: pointer to mmc adapter */
 	int	ocr_mask;	/* temporary HACK */
arch/arm/plat-omap/include/plat/mmc.h +1 -0
index e3c9b20..9b69b7e 100644
@@ -119,6 +119,7 @@ struct omap_mmc_platform_data {
 
 		int switch_pin;			/* gpio (card detect) */
 		int gpio_wp;			/* gpio (write protect) */
+		int gpio_hw_reset;              /* gpio (hardware reset) */
 
 		int (*set_bus_mode)(struct device *dev, int slot, int bus_mode);
 		int (*set_power)(struct device *dev, int slot,
Signature · 1 line
1.7.0.4
#24 lore raw

PATCH v2 16/16 mmc: omap_hsmmc: add a hardware reset before initialization

In reply to Adrian Hunter

After a warm restart, an eMMC which cannot be powered off is in an unknown state, so reset it to be sure it will initialize.

---
 drivers/mmc/host/omap_hsmmc.c |   38 +++++++++++++++++++++++++++++++++++++-
 1 files changed, 37 insertions(+), 1 deletions(-)
drivers/mmc/host/omap_hsmmc.c +37 -1
index e1c9017..70b8ef8 100644
@@ -530,10 +530,25 @@ static int omap_hsmmc_gpio_init(struct omap_mmc_platform_data *pdata)
 	} else
 		pdata->slots[0].gpio_wp = -EINVAL;
 
+	if (gpio_is_valid(pdata->slots[0].gpio_hw_reset)) {
+		ret = gpio_request(pdata->slots[0].gpio_hw_reset,
+				   "mmc_hw_reset");
+		if (ret)
+			goto err_free_wp;
+		ret = gpio_direction_output(pdata->slots[0].gpio_hw_reset, 1);
+		if (ret)
+			goto err_free_hw_reset;
+	} else
+		pdata->slots[0].gpio_hw_reset = -EINVAL;
+
 	return 0;
 
+
+err_free_hw_reset:
+	gpio_free(pdata->slots[0].gpio_hw_reset);
 err_free_wp:
-	gpio_free(pdata->slots[0].gpio_wp);
+	if (gpio_is_valid(pdata->slots[0].gpio_wp))
+		gpio_free(pdata->slots[0].gpio_wp);
 err_free_cd:
 	if (gpio_is_valid(pdata->slots[0].switch_pin))
 err_free_sp:
@@ -543,6 +558,8 @@ err_free_sp:
 
 static void omap_hsmmc_gpio_free(struct omap_mmc_platform_data *pdata)
 {
+	if (gpio_is_valid(pdata->slots[0].gpio_hw_reset))
+		gpio_free(pdata->slots[0].gpio_hw_reset);
 	if (gpio_is_valid(pdata->slots[0].gpio_wp))
 		gpio_free(pdata->slots[0].gpio_wp);
 	if (gpio_is_valid(pdata->slots[0].switch_pin))
@@ -803,6 +820,18 @@ static void omap_hsmmc_context_save(struct omap_hsmmc_host *host)
 
 #endif
 
+static void omap_hsmmc_do_hw_reset(struct omap_hsmmc_host *host)
+{
+	if (!gpio_is_valid(mmc_slot(host).gpio_hw_reset))
+		return;
+
+	gpio_set_value_cansleep(mmc_slot(host).gpio_hw_reset, 0);
+	udelay(9);
+	gpio_set_value_cansleep(mmc_slot(host).gpio_hw_reset, 1);
+	usleep_range(1000, 2000);
+	printk(KERN_INFO "%s: hardware reset done\n", mmc_hostname(host->mmc));
+}
+
 /*
  * Send init stream sequence to card
  * before sending IDLE command
@@ -815,6 +844,13 @@ static void send_init_stream(struct omap_hsmmc_host *host)
 	if (host->protect_card)
 		return;
 
+	/*
+	 * After a warm restart, an eMMC which cannot be powered off is in an
+	 * unknown state so reset it to be sure it will initialize.
+	 */
+	if (mmc_slot(host).no_off)
+		omap_hsmmc_do_hw_reset(host);
+
 	disable_irq(host->irq);
 
 	OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK);
Signature · 1 line
1.7.0.4
#25 lore raw

PATCH v2 16/16 mmc: omap_hsmmc: add a hardware reset before initialization

Hi,

Quoted 16 lines · Adrian Hunter

After a warm restart, an eMMC which cannot be powered off is in an unknown state, so reset it to be sure it will initialize.

---
?drivers/mmc/host/omap_hsmmc.c | ? 38 +++++++++++++++++++++++++++++++++++++- ?1 files changed, 37 insertions(+), 1 deletions(-)

drivers/mmc/host/omap_hsmmc.c +0 -0
index e1c9017..70b8ef8 100644
@@ -530,10 +530,25 @@ static int omap_hsmmc_gpio_init(struct omap_mmc_platform_data *pdata)

? ? ? ?} else
? ? ? ? ? ? ? ?pdata->slots[0].gpio_wp = -EINVAL;

Quoted 1 line

+ ? ? ? if (gpio_is_valid(pdata->slots[0].gpio_hw_reset)) {

Not required as gpio_request takes care of it.

Quoted 5 lines

+ ? ? ? ? ? ? ? ret = gpio_request(pdata->slots[0].gpio_hw_reset,
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?"mmc_hw_reset");
+ ? ? ? ? ? ? ? if (ret)
+ ? ? ? ? ? ? ? ? ? ? ? goto err_free_wp;
+ ? ? ? ? ? ? ? ret = gpio_direction_output(pdata->slots[0].gpio_hw_reset, 1);

Use gpio_request_one instead.

Quoted 10 lines

+ ? ? ? ? ? ? ? if (ret)
+ ? ? ? ? ? ? ? ? ? ? ? goto err_free_hw_reset;
+ ? ? ? } else
+ ? ? ? ? ? ? ? pdata->slots[0].gpio_hw_reset = -EINVAL; +
? ? ? ?return 0;

drivers/mmc/host/omap_hsmmc.c +3 -0
+
+err_free_hw_reset:
+ ? ? ? gpio_free(pdata->slots[0].gpio_hw_reset);
Quoted 3 lines

?err_free_wp:
- ? ? ? gpio_free(pdata->slots[0].gpio_wp);
+ ? ? ? if (gpio_is_valid(pdata->slots[0].gpio_wp))

Not required as gpio_free takes care of it.

drivers/mmc/host/omap_hsmmc.c err_free_sp: · line 561

Quoted 9 lines

+ ? ? ? ? ? ? ? gpio_free(pdata->slots[0].gpio_wp); ?err_free_cd:
? ? ? ?if (gpio_is_valid(pdata->slots[0].switch_pin)) ?err_free_sp:

drivers/mmc/host/omap_hsmmc.c +1 -0
@@ -543,6 +558,8 @@ err_free_sp:
 
 ?static void omap_hsmmc_gpio_free(struct omap_mmc_platform_data *pdata)
 ?{
+ ? ? ? if (gpio_is_valid(pdata->slots[0].gpio_hw_reset))

ditto

drivers/mmc/host/omap_hsmmc.c err_free_sp: · line 565

Quoted 4 lines
drivers/mmc/host/omap_hsmmc.c +1 -0
+ ? ? ? ? ? ? ? gpio_free(pdata->slots[0].gpio_hw_reset);
 ? ? ? ?if (gpio_is_valid(pdata->slots[0].gpio_wp))
 ? ? ? ? ? ? ? ?gpio_free(pdata->slots[0].gpio_wp);
 ? ? ? ?if (gpio_is_valid(pdata->slots[0].switch_pin))

<<snip>>

#26 lore raw

v2 cover omap_hsmmc patches

In reply to Adrian Hunter

Hello,

it seems this series got lost in time, at least patches 01, 02, 03, 04, 05, 06, 07, 08, 12 look like valid standalone fixes and improvements to me, they don't touch other trees and still apply cleanly, so would be good to have.

Chris, can you queue these for upcoming merge window? These patches are also archived in patchwork here:
https://patchwork.kernel.org/project/linux-omap/list/?archive=both&page=8

Quoted 58 lines · Adrian Hunter

Hi

Here is V2 of some patches for omap_hsmmc.

Changes in V2:

? ? ? ?OMAP: sDMA: descriptor autoloading feature
? ? ? ? ? ? ? ?- removed the feature entirely as per Tony Lindgren

? ? ? ?mmc: omap_hsmmc: fix few bugs when set the clock divisor ? ? ? ? ? ? ? ?- added cpu_relax() as per Grazvydas Ignotas

? ? ? ?mmc: omap_hsmmc: fix missing mmc_release_host() in no_off case ? ? ? ? ? ? ? ?- removed {} as per Sergei Shtylyov

? ? ? ?OMAP: hsmmc: implement clock switcher
? ? ? ? ? ? ? ?- changed to >= threshold as per Grazvydas Ignotas

Adrian Hunter (6):
? ? ?mmc: omap_hsmmc: correct debug report error status mnemonics ? ? ?OMAP: board-rm680: set MMC nomux flag
? ? ?mmc: omap_hsmmc: ensure pbias configuration is always done ? ? ?mmc: omap_hsmmc: fix oops in omap_hsmmc_dma_cb
? ? ?OMAP: hsmmc: add platform data for eMMC hardware reset gpio ? ? ?mmc: omap_hsmmc: add a hardware reset before initialization

Andy Shevchenko (8):
? ? ?mmc: omap_hsmmc: move hardcoded frequency constants to definition block ? ? ?mmc: omap_hsmmc: reduce a bit the error handlers in probe() ? ? ?mmc: omap_hsmmc: split duplicate code to calc_divisor() function ? ? ?mmc: omap_hsmmc: introduce start_clock and re-use stop_clock ? ? ?mmc: omap_hsmmc: fix few bugs when set the clock divisor
? ? ?mmc: omap_hsmmc: split same pieces of code to separate functions ? ? ?OMAP: hsmmc: implement clock switcher
? ? ?mmc: omap_hsmmc: adjust host controller clock in regard to current OPP

Jarkko Lavinen (1):
? ? ?OMAP: hsmmc: Do not mux the slot if non default muxing is already done

Sudhir Bera (1):
? ? ?mmc: omap_hsmmc: fix missing mmc_release_host() in no_off case

?arch/arm/mach-omap2/board-rm680.c ? ? | ? ?1 +
?arch/arm/mach-omap2/hsmmc.c ? ? ? ? ? | ?188 +++++++++++++++++-
?arch/arm/mach-omap2/hsmmc.h ? ? ? ? ? | ? ?2 +
?arch/arm/plat-omap/include/plat/mmc.h | ? ?9 +
?drivers/mmc/host/omap_hsmmc.c ? ? ? ? | ?353 ++++++++++++++++++++-------------

?5 files changed, 414 insertions(+), 139 deletions(-)

Regards
Adrian

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo at vger.kernel.org
More majordomo info at ?http://vger.kernel.org/majordomo-info.html

Signature · 1 line
Gra?vydas
#27 lore raw

v2 cover omap_hsmmc patches

Hi,

Quoted 4 lines · Grazvydas Ignotas

it seems this series got lost in time, at least patches 01, 02, 03, 04, 05, 06, 07, 08, 12 look like valid standalone fixes and improvements to me, they don't touch other trees and still apply cleanly, so would be good to have.

Yeah. I didn't merge them because Tony still has unanswered comments on the OMAP side of the patches, and they're submitted as a single patchset.

I've applied {2, 3, 5, 6, 7, 8, 12} to mmc-next for 3.1 now. 1 doesn't apply because the function doesn't exist anymore. 4 doesn't apply because iclk doesn't exist anymore.

If someone wants to look at rebasing the rest of these against mmc-next and resubmitting, I think that'd be useful.

Thanks,

- Chris.

Signature · 2 lines
Chris Ball   <[email protected]>   <http://printf.net/>
One Laptop Per Child
#28 lore raw

v2 cover omap_hsmmc patches

Quoted 11 lines · Chris Ball

Hi,

Quoted 4 lines · Grazvydas Ignotas

it seems this series got lost in time, at least patches 01, 02, 03, 04, 05, 06, 07, 08, 12 look like valid standalone fixes and improvements to me, they don't touch other trees and still apply cleanly, so would be good to have.

Yeah. ?I didn't merge them because Tony still has unanswered comments on the OMAP side of the patches, and they're submitted as a single patchset.

Quoted 6 lines

I've applied {2, 3, 5, 6, 7, 8, 12} to mmc-next for 3.1 now. ?1 doesn't apply because the function doesn't exist anymore. ?4 doesn't apply because iclk doesn't exist anymore.

If someone wants to look at rebasing the rest of these against mmc-next and resubmitting, I think that'd be useful.

Thanks! 1 and 4 seem to be unneeded any more (I was trying them on linux-next, not noticing it was not updated for a while), and others have comments pending, so it's all done.

Quoted 8 lines

Thanks,

- Chris.
--
Chris Ball ? <[email protected]> ? <http://printf.net/>
One Laptop Per Child

Signature · 1 line
Gra?vydas
maintainer
#29 lore raw

v2 cover omap_hsmmc patches

Quoted 16 lines · Grazvydas Ignotas
Quoted 15 lines · Chris Ball

Hi,

Quoted 4 lines · Grazvydas Ignotas

it seems this series got lost in time, at least patches 01, 02, 03, 04, 05, 06, 07, 08, 12 look like valid standalone fixes and improvements to me, they don't touch other trees and still apply cleanly, so would be good to have.

Yeah. ?I didn't merge them because Tony still has unanswered comments on the OMAP side of the patches, and they're submitted as a single patchset.

I've applied {2, 3, 5, 6, 7, 8, 12} to mmc-next for 3.1 now. ?1 doesn't apply because the function doesn't exist anymore. ?4 doesn't apply because iclk doesn't exist anymore.

Quoted 6 lines
Quoted 2 lines

If someone wants to look at rebasing the rest of these against mmc-next and resubmitting, I think that'd be useful.

Thanks! 1 and 4 seem to be unneeded any more (I was trying them on linux-next, not noticing it was not updated for a while), and others have comments pending, so it's all done.

So I guess that means I don't need to look at anything, right?

Tony

#30 lore raw
Quoted 10 lines · Tony Lindgren
Quoted 7 lines · Grazvydas Ignotas
Quoted 2 lines · Chris Ball

If someone wants to look at rebasing the rest of these against mmc-next and resubmitting, I think that'd be useful.

Thanks! 1 and 4 seem to be unneeded any more (I was trying them on linux-next, not noticing it was not updated for a while), and others have comments pending, so it's all done.

So I guess that means I don't need to look at anything, right?

Yeah, these patches hit mainline already.