Skip to contentSkip to conversation

OMAP: HSMMC: Patches

PATCH In review Adrian Hunter 3 participants 17 messages Nov 21, 2008

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

cover OMAP: HSMMC: Patches

Hi

OMAP HSMMC is not ready for mainline, but development is ongoing.

In the meantime these patches are best posted and applied via omap mailing list and tree.

Regards
Adrian

#1 lore raw
Quoted 14 lines · Adrian Hunter

Hi

OMAP HSMMC is not ready for mainline, but development is ongoing.

In the meantime these patches are best posted and applied via omap mailing list and tree.

Regards
Adrian

Signature · 3 lines
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Just realised I forgot the from lines in all those patches - will resend.

#2 lore raw

PATCH 1/9 OMAP: HSMMC: Make driver support dynamic idle

From: Amit Kucheria <[email protected]>
Date: Mon, 22 Sep 2008 10:23:09 +0300

Add a timer that is kept active by MMC requests. FCLK is disabled on timeout

---
drivers/mmc/host/omap_hsmmc.c | 73 +++++++++++++++++++++++++++++++++-------

1 files changed, 60 insertions(+), 13 deletions(-)
drivers/mmc/host/omap_hsmmc.c +0 -0
index 0df6841..ee21a64 100644
@@ -121,6 +121,9 @@
#define OMAP_HSMMC_WRITE(base, reg, val) \
	__raw_writel((val), (base) + OMAP_HSMMC_##reg)

+enum {OFF = 0, ON};
+#define IDLE_TIMEOUT (5*HZ)
+

struct mmc_omap_host {
	struct	device		*dev;
	struct	mmc_host	*mmc;
@@ -148,9 +151,50 @@ struct mmc_omap_host {
	int			initstr;
	int			slot_id;
	int			dbclk_enabled;

+
+ struct timer_list idle_timer;
+ spinlock_t clk_lock; /* for changing enabled state */
+ int fclk_enabled:1;
+

	struct	omap_mmc_platform_data	*pdata;
};

+int mmc_omap_fclk_state(struct mmc_omap_host *host, unsigned int state)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&host->clk_lock, flags);
+ if (host->fclk_enabled != state) {
+ if (state == ON) {
+ if (host->fclk_enabled == OFF) {
+ ret = clk_enable(host->fclk);
+ if (ret != 0)
+ return ret;
+ dev_dbg(mmc_dev(host->mmc),
+ "mmc_fclk: enabled\n");
+ }
+ /* Revisit: Change the timer bump based on real
+ MMC usage characteristics */
+ mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
+ } else {
+ clk_disable(host->fclk);
+ dev_dbg(mmc_dev(host->mmc), "mmc_fclk: disabled\n");
+ del_timer(&host->idle_timer);
+ }
+ host->fclk_enabled = state;
+ }
+ spin_unlock_irqrestore(&host->clk_lock, flags);
+ return 0;
+}
+
+static void mmc_omap_idle_timer(unsigned long data)
+{
+ struct mmc_omap_host *host = (struct mmc_omap_host *) data;
+
+ mmc_omap_fclk_state(host, OFF);
+}
+
/*
* Stop clock to the card
*/
@@ -457,7 +501,7 @@ static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd)

	int ret;

	/* Disable the clocks */
-	clk_disable(host->fclk);
+	mmc_omap_fclk_state(host, OFF);
	clk_disable(host->iclk);
	clk_disable(host->dbclk);

@@ -471,7 +515,7 @@ static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd)

	if (ret != 0)
		goto err;

- clk_enable(host->fclk);

+	mmc_omap_fclk_state(host, ON);
	clk_enable(host->iclk);
	clk_enable(host->dbclk);

@@ -742,10 +786,10 @@ static void omap_mmc_request(struct mmc_host *mmc, struct mmc_request *req)

	WARN_ON(host->mrq != NULL);
	host->mrq = req;
	mmc_omap_prepare_data(host, req);
+	mmc_omap_fclk_state(host, ON);
	mmc_omap_start_command(host, req->cmd, req->data);
}

-
/* Routine to configure clock values. Exposed API to core */
static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
@@ -824,6 +868,7 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)

	if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
		OMAP_HSMMC_WRITE(host->base, CON,
				OMAP_HSMMC_READ(host->base, CON) | OD);

+
}

static int omap_hsmmc_get_cd(struct mmc_host *mmc)
@@ -860,7 +905,7 @@ static int __init omap_mmc_probe(struct platform_device *pdev)

	struct mmc_host *mmc;
	struct mmc_omap_host *host = NULL;
	struct resource *res;
-	int ret = 0, irq;
+	int ret = 0, irq, reg;
	u32 hctl, capa;

	if (pdata == NULL) {
@@ -925,14 +970,17 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
		goto err1;
	}

- if (clk_enable(host->fclk) != 0) {
+ spin_lock_init(&host->clk_lock);
+ setup_timer(&host->idle_timer, mmc_omap_idle_timer,
+ (unsigned long) host);
+

+	if (mmc_omap_fclk_state(host, ON) != 0) {
		clk_put(host->iclk);
		clk_put(host->fclk);
		goto err1;
	}
-
	if (clk_enable(host->iclk) != 0) {
-		clk_disable(host->fclk);
+		mmc_omap_fclk_state(host, OFF);
		clk_put(host->iclk);
		clk_put(host->fclk);
		goto err1;
@@ -1047,7 +1095,7 @@ err_irq_cd:
err_irq_cd_init:
	free_irq(host->irq, host);
err_irq:
-	clk_disable(host->fclk);
+	mmc_omap_fclk_state(host, OFF);
	clk_disable(host->iclk);
	clk_put(host->fclk);
	clk_put(host->iclk);
@@ -1096,7 +1144,7 @@ static int omap_mmc_remove(struct platform_device *pdev)
			free_irq(mmc_slot(host).card_detect_irq, host);
		flush_scheduled_work();

- clk_disable(host->fclk);

+		mmc_omap_fclk_state(host, OFF);
		clk_disable(host->iclk);
		clk_put(host->fclk);
		clk_put(host->iclk);
@@ -1149,7 +1197,7 @@ static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state)
					| SDBP);
			}

- clk_disable(host->fclk);

+			mmc_omap_fclk_state(host, OFF);
			clk_disable(host->iclk);
			clk_disable(host->dbclk);
		}

@@ -1169,13 +1217,12 @@ static int omap_mmc_resume(struct platform_device *pdev)

	if (host) {

- ret = clk_enable(host->fclk);
- if (ret)
+ if (mmc_omap_fclk_state(host, ON) != 0)

			goto clk_en_err;

		ret = clk_enable(host->iclk);
		if (ret) {
-			clk_disable(host->fclk);
+			mmc_omap_fclk_state(host, OFF);
			clk_put(host->fclk);
			goto clk_en_err;
		}
Signature · 1 line
1.5.4.3
#3 lore raw

PATCH 2/9 OMAP: HSMMC: Ensure fclk is running before accessing HSMMC registers

In reply to Adrian Hunter

From: Jarkko Lavinen <[email protected]>
Date: Fri, 24 Oct 2008 16:03:23 +0300

---
drivers/mmc/host/omap_hsmmc.c | 32 ++++++++++++++++++++------------

1 files changed, 20 insertions(+), 12 deletions(-)
drivers/mmc/host/omap_hsmmc.c +0 -0
index ee21a64..2eed691 100644
@@ -154,26 +154,24 @@ struct mmc_omap_host {
 
	struct timer_list       idle_timer;
	spinlock_t		clk_lock;     /* for changing enabled state */

- int fclk_enabled:1;
+ unsigned int fclk_enabled:1;

	struct	omap_mmc_platform_data	*pdata;
};

-int mmc_omap_fclk_state(struct mmc_omap_host *host, unsigned int state) +static int mmc_omap_fclk_state(struct mmc_omap_host *host, unsigned int state)

{
	unsigned long flags;

- int ret;
+ int ret = 0;

	spin_lock_irqsave(&host->clk_lock, flags);
	if (host->fclk_enabled != state) {
		if (state == ON) {

- if (host->fclk_enabled == OFF) {
- ret = clk_enable(host->fclk);
- if (ret != 0)
- return ret;
- dev_dbg(mmc_dev(host->mmc),
- "mmc_fclk: enabled\n");
- }
+ ret = clk_enable(host->fclk);
+ if (ret != 0)
+ goto err_out;
+

+			dev_dbg(mmc_dev(host->mmc), "mmc_fclk: enabled\n");
			/* Revisit: Change the timer bump based on real
			   MMC usage characteristics */
			mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
@@ -184,8 +182,10 @@ int mmc_omap_fclk_state(struct mmc_omap_host *host, unsigned int state)
		}
		host->fclk_enabled = state;
	}
+
+err_out:
	spin_unlock_irqrestore(&host->clk_lock, flags);

- return 0;
+ return ret;
}

static void mmc_omap_idle_timer(unsigned long data)
@@ -557,6 +557,7 @@ static void mmc_omap_detect(struct work_struct *work)

						mmc_carddetect_work);

	sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch");
+	mmc_omap_fclk_state(host, ON);
	if (host->carddetect) {
		if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
			/*

@@ -785,8 +786,8 @@ static void omap_mmc_request(struct mmc_host *mmc, struct mmc_request *req)

	WARN_ON(host->mrq != NULL);
	host->mrq = req;
-	mmc_omap_prepare_data(host, req);
	mmc_omap_fclk_state(host, ON);
+	mmc_omap_prepare_data(host, req);
	mmc_omap_start_command(host, req->cmd, req->data);
}

@@ -798,6 +799,8 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)

	unsigned long regval;
	unsigned long timeout;

+ mmc_omap_fclk_state(host, ON);
+

	switch (ios->power_mode) {
	case MMC_POWER_OFF:
		mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
@@ -869,6 +872,8 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
		OMAP_HSMMC_WRITE(host->base, CON,
				OMAP_HSMMC_READ(host->base, CON) | OD);

+ if (ios->power_mode == MMC_POWER_OFF)
+ mmc_omap_fclk_state(host, OFF);
}

static int omap_hsmmc_get_cd(struct mmc_host *mmc)
@@ -1120,6 +1125,7 @@ static int omap_mmc_remove(struct platform_device *pdev)

	struct resource *res;
	u16 vdd = 0;
+	mmc_omap_fclk_state(host, ON);
	if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
	/*
	 * Set the vdd back to 3V,
@@ -1170,6 +1176,8 @@ static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state)
		return 0;

	if (host) {
+		mmc_omap_fclk_state(host, ON);
+
		ret = mmc_suspend_host(host->mmc, state);
		if (ret == 0) {
			host->suspended = 1;
Signature · 1 line
1.5.4.3
#4 lore raw

PATCH 3/9 OMAP: HSMMC: Make fclk disable request driven.

In reply to Adrian Hunter

From: Jarkko Lavinen <[email protected]>
Date: Fri, 24 Oct 2008 16:30:26 +0300

Start idle timer only at the end of request or request like operation such card detect, set_ios, probe, or resume. This will ensure the fclk is disable quickly after the operation is done, but never too early.

---
drivers/mmc/host/omap_hsmmc.c | 15 ++++++++++-----

1 files changed, 10 insertions(+), 5 deletions(-)
drivers/mmc/host/omap_hsmmc.c +0 -0
index 2eed691..a134f76 100644
@@ -122,7 +122,7 @@
	__raw_writel((val), (base) + OMAP_HSMMC_##reg)

enum {OFF = 0, ON};
-#define IDLE_TIMEOUT (5*HZ)
+#define IDLE_TIMEOUT (jiffies_to_msecs(10))

struct mmc_omap_host {
	struct	device		*dev;
@@ -165,6 +165,7 @@ static int mmc_omap_fclk_state(struct mmc_omap_host *host, unsigned int state)
	int ret = 0;

	spin_lock_irqsave(&host->clk_lock, flags);
+	del_timer(&host->idle_timer);
	if (host->fclk_enabled != state) {
		if (state == ON) {
			ret = clk_enable(host->fclk);
@@ -172,13 +173,9 @@ static int mmc_omap_fclk_state(struct mmc_omap_host *host, unsigned int state)
				goto err_out;

			dev_dbg(mmc_dev(host->mmc), "mmc_fclk: enabled\n");
-			/* Revisit: Change the timer bump based on real
-			   MMC usage characteristics */
-			mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
		} else {
			clk_disable(host->fclk);
			dev_dbg(mmc_dev(host->mmc), "mmc_fclk: disabled\n");
-			del_timer(&host->idle_timer);
		}
		host->fclk_enabled = state;
	}

@@ -335,6 +332,7 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)

	if (!data->stop) {
		host->mrq = NULL;
+		mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
		mmc_request_done(host->mmc, data->mrq);
		return;
	}
@@ -363,6 +361,7 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
	}
	if (host->data == NULL || cmd->error) {
		host->mrq = NULL;
+		mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
		mmc_request_done(host->mmc, cmd->mrq);
	}
}
@@ -575,6 +574,7 @@ static void mmc_omap_detect(struct work_struct *work)
		while (OMAP_HSMMC_READ(host->base, SYSCTL) & SRD) ;
		mmc_detect_change(host->mmc, (HZ * 50) / 1000);
	}

+ mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
}

/*
@@ -874,6 +874,8 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)

	if (ios->power_mode == MMC_POWER_OFF)
		mmc_omap_fclk_state(host, OFF);

+ else
+ mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
}

static int omap_hsmmc_get_cd(struct mmc_host *mmc)
@@ -1088,6 +1090,7 @@ static int __init omap_mmc_probe(struct platform_device *pdev)

		if (ret < 0)
			goto err_cover_switch;
	}

+ mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);

	return 0;

@@ -1250,6 +1253,8 @@ static int omap_mmc_resume(struct platform_device *pdev)

		ret = mmc_resume_host(host->mmc);
		if (ret == 0)
			host->suspended = 0;
+
+		mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
	}

	return ret;
Signature · 1 line
1.5.4.3
#5 lore raw

PATCH 4/9 OMAP: HSMMC: Fix suspend/resume for MMCHS2

In reply to Adrian Hunter

From: Jarkko Lavinen <[email protected]>
Date: Tue, 28 Oct 2008 11:12:28 +0200

For HSMMC2 and HSMMC3 set SDVSS to 1.8V in HCTL before enabling the SDBP bit.

Fixes bug 84194.

---
drivers/mmc/host/omap_hsmmc.c | 19 ++++++++++---------

1 files changed, 10 insertions(+), 9 deletions(-)
drivers/mmc/host/omap_hsmmc.c +0 -0
index a134f76..4bc25c0 100644
@@ -1197,15 +1197,16 @@ static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state)
			}

			if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {

- OMAP_HSMMC_WRITE(host->base, HCTL,
- OMAP_HSMMC_READ(host->base, HCTL)
- & SDVSCLR);
- OMAP_HSMMC_WRITE(host->base, HCTL,
- OMAP_HSMMC_READ(host->base, HCTL)
- | SDVS30);
- OMAP_HSMMC_WRITE(host->base, HCTL,
- OMAP_HSMMC_READ(host->base, HCTL)
- | SDBP);
+ u32 hctl = OMAP_HSMMC_READ(host->base, HCTL) &
+ SDVSCLR;
+
+ if (host->id == OMAP_MMC1_DEVID)
+ hctl |= SDVS30;
+ else
+ hctl |= SDVS18;
+
+ OMAP_HSMMC_WRITE(host->base, HCTL, hctl);

+				OMAP_HSMMC_WRITE(host->base, HCTL, hctl | SDBP);
			}

			mmc_omap_fclk_state(host, OFF);
Signature · 1 line
1.5.4.3
#6 lore raw

PATCH 5/9 OMAP: HSMMC: Fix oops in omap_mmc_remove

In reply to Adrian Hunter

From: Jarkko Lavinen <[email protected]>
Date: Thu, 6 Nov 2008 16:02:29 +0200

Do not access host in omap_mmc_remove() if it is null. This occurs when module is removed before its platform data is set.

This fixes the oops part in bug 86677.

---
drivers/mmc/host/omap_hsmmc.c | 20 +++++++++++---------

1 files changed, 11 insertions(+), 9 deletions(-)
drivers/mmc/host/omap_hsmmc.c +0 -0
index 4bc25c0..f4b7c30 100644
@@ -1128,15 +1128,17 @@ static int omap_mmc_remove(struct platform_device *pdev)
	struct resource *res;
	u16 vdd = 0;

- mmc_omap_fclk_state(host, ON);
- if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
- /*
- * Set the vdd back to 3V,
- * applicable for dual volt support.
- */
- vdd = fls(host->mmc->ocr_avail) - 1;
- if (omap_mmc_switch_opcond(host, vdd) != 0)
- host->mmc->ios.vdd = vdd;
+ if (host) {
+ mmc_omap_fclk_state(host, ON);
+ if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
+ /*
+ * Set the vdd back to 3V,
+ * applicable for dual volt support.
+ */
+ vdd = fls(host->mmc->ocr_avail) - 1;
+ if (omap_mmc_switch_opcond(host, vdd) != 0)
+ host->mmc->ios.vdd = vdd;
+ }

	}

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Signature · 1 line
1.5.4.3
#7 lore raw

PATCH 1/1 OMAP: HSMMC: Fix SDVS values in omap_mmc_switch_opcond

In reply to Adrian Hunter

From: Jarkko Lavinen <[email protected]>
Date: Thu, 6 Nov 2008 16:02:28 +0200

Set only 1.8V as SDVS value in HCTL register for HSMMC2 and HSMMC3.

---
drivers/mmc/host/omap_hsmmc.c | 11 +++++++----

1 files changed, 7 insertions(+), 4 deletions(-)
drivers/mmc/host/omap_hsmmc.c +0 -0
index f4b7c30..e329115 100644
@@ -529,10 +529,13 @@ static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd)

* Only MMC1 supports 3.0V. MMC2 will not function if SDVS30 is
* set in HCTL.
*/
- if (host->id == OMAP_MMC1_DEVID && (((1 << vdd) == MMC_VDD_32_33) ||
- ((1 << vdd) == MMC_VDD_33_34)))
- reg_val |= SDVS30;
- if ((1 << vdd) == MMC_VDD_165_195)
+ if (host->id == OMAP_MMC1_DEVID) {
+ if (((1 << vdd) == MMC_VDD_32_33) ||
+ ((1 << vdd) == MMC_VDD_33_34))
+ reg_val |= SDVS30;
+ else if ((1 << vdd) == MMC_VDD_165_195)
+ reg_val |= SDVS18;
+ } else

		reg_val |= SDVS18;

	OMAP_HSMMC_WRITE(host->base, HCTL, reg_val);
Signature · 1 line
1.5.4.3
#8 lore raw

PATCH 7/9 OMAP: HSMMC: Use mod_timer from a single point for readability.

In reply to Adrian Hunter

From: Jarkko Lavinen <[email protected]>
Date: Thu, 6 Nov 2008 16:02:30 +0200

---
drivers/mmc/host/omap_hsmmc.c | 18 +++++++++++-------

1 files changed, 11 insertions(+), 7 deletions(-)
drivers/mmc/host/omap_hsmmc.c +0 -0
index e329115..d81f9af 100644
@@ -192,6 +192,11 @@ static void mmc_omap_idle_timer(unsigned long data)
	mmc_omap_fclk_state(host, OFF);
}

+static void mmc_omap_fclk_lazy_disable(struct mmc_omap_host *host)
+{
+ mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
+}
+
/*
* Stop clock to the card
*/
@@ -332,7 +337,7 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)

	if (!data->stop) {
		host->mrq = NULL;
-		mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
+		mmc_omap_fclk_lazy_disable(host);
		mmc_request_done(host->mmc, data->mrq);
		return;
	}
@@ -361,7 +366,7 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
	}
	if (host->data == NULL || cmd->error) {
		host->mrq = NULL;
-		mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
+		mmc_omap_fclk_lazy_disable(host);
		mmc_request_done(host->mmc, cmd->mrq);
	}
}
@@ -577,7 +582,7 @@ static void mmc_omap_detect(struct work_struct *work)
		while (OMAP_HSMMC_READ(host->base, SYSCTL) & SRD) ;
		mmc_detect_change(host->mmc, (HZ * 50) / 1000);
	}

- mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
+ mmc_omap_fclk_lazy_disable(host);
}

/*
@@ -878,7 +883,7 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)

	if (ios->power_mode == MMC_POWER_OFF)
		mmc_omap_fclk_state(host, OFF);
	else

- mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
+ mmc_omap_fclk_lazy_disable(host);
}

static int omap_hsmmc_get_cd(struct mmc_host *mmc)
@@ -1093,7 +1098,7 @@ static int __init omap_mmc_probe(struct platform_device *pdev)

		if (ret < 0)
			goto err_cover_switch;
	}

- mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);
+ mmc_omap_fclk_lazy_disable(host);

	return 0;

@@ -1233,7 +1238,6 @@ static int omap_mmc_resume(struct platform_device *pdev)

		return 0;

	if (host) {
-
		if (mmc_omap_fclk_state(host, ON) != 0)
			goto clk_en_err;

@@ -1260,7 +1264,7 @@ static int omap_mmc_resume(struct platform_device *pdev)

		if (ret == 0)
			host->suspended = 0;

- mod_timer(&host->idle_timer, jiffies + IDLE_TIMEOUT);

+		mmc_omap_fclk_lazy_disable(host);
	}

	return ret;
Signature · 1 line
1.5.4.3
#9 lore raw

PATCH 8/9 OMAP: HSMMC: Implement scatter-gather emulation

In reply to Adrian Hunter

From: Juha Yrjola <[email protected]>
Date: Fri, 14 Nov 2008 15:22:00 +0200

Instead of using the bounce buffer, using scatter-gather emulation (as in the OMAP1/2 MMC driver) removes the need of one extra memory copy and improves performance.

---
drivers/mmc/host/omap_hsmmc.c | 174 ++++++++++++++++++++++------------------

1 files changed, 96 insertions(+), 78 deletions(-)
drivers/mmc/host/omap_hsmmc.c +0 -0
index d81f9af..6058a70 100644
@@ -98,9 +98,6 @@

#define OMAP_MMC1_DEVID 0
#define OMAP_MMC2_DEVID 1

-#define OMAP_MMC_DATADIR_NONE 0
-#define OMAP_MMC_DATADIR_READ 1
-#define OMAP_MMC_DATADIR_WRITE 2
#define MMC_TIMEOUT_MS 20
#define OMAP_MMC_MASTER_CLOCK 96000000
#define DRIVER_NAME "mmci-omap"

@@ -139,16 +136,14 @@ struct mmc_omap_host {
	resource_size_t		mapbase;
	unsigned int		id;
	unsigned int		dma_len;
-	unsigned int		dma_dir;
+	unsigned int		dma_sg_idx;
	unsigned char		bus_mode;
-	unsigned char		datadir;
	u32			*buffer;
	u32			bytesleft;
	int			suspended;
	int			irq;
	int			carddetect;
	int			use_dma, dma_ch;
-	int			initstr;
	int			slot_id;
	int			dbclk_enabled;

@@ -316,6 +311,15 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd,

	OMAP_HSMMC_WRITE(host->base, CMD, cmdreg);
}

+static int
+mmc_omap_get_dma_dir(struct mmc_omap_host *host, struct mmc_data *data)
+{
+ if (data->flags & MMC_DATA_WRITE)
+ return DMA_TO_DEVICE;
+ else
+ return DMA_FROM_DEVICE;
+}
+
/*
* Notify the transfer complete to MMC core
*/
@@ -326,9 +330,7 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)

	if (host->use_dma && host->dma_ch != -1)
		dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len,

- host->dma_dir);
-
- host->datadir = OMAP_MMC_DATADIR_NONE;
+ mmc_omap_get_dma_dir(host, data));

	if (!data->error)
		data->bytes_xfered += data->blocks * (data->blksz);

@@ -380,13 +382,12 @@ static void mmc_dma_cleanup(struct mmc_omap_host *host)

	if (host->use_dma && host->dma_ch != -1) {
		dma_unmap_sg(mmc_dev(host->mmc), host->data->sg, host->dma_len,
-			host->dma_dir);
+			mmc_omap_get_dma_dir(host, host->data));
		omap_free_dma(host->dma_ch);
		host->dma_ch = -1;
		up(&host->sem);
	}
	host->data = NULL;

- host->datadir = OMAP_MMC_DATADIR_NONE;
}

/*
@@ -598,6 +599,55 @@ static irqreturn_t omap_mmc_cd_handler(int irq, void *dev_id)

	return IRQ_HANDLED;
}

+static int mmc_omap_get_dma_sync_dev(struct mmc_omap_host *host,
+ struct mmc_data *data)
+{
+ int sync_dev;
+
+ if (data->flags & MMC_DATA_WRITE) {
+ if (host->id == OMAP_MMC1_DEVID)
+ sync_dev = OMAP24XX_DMA_MMC1_TX;
+ else
+ sync_dev = OMAP24XX_DMA_MMC2_TX;
+ } else {
+ if (host->id == OMAP_MMC1_DEVID)
+ sync_dev = OMAP24XX_DMA_MMC1_RX;
+ else
+ sync_dev = OMAP24XX_DMA_MMC2_RX;
+ }
+ return sync_dev;
+}
+
+static void mmc_omap_config_dma_params(struct mmc_omap_host *host,
+ struct mmc_data *data,
+ struct scatterlist *sgl)
+{
+ int blksz, nblk, dma_ch;
+
+ dma_ch = host->dma_ch;
+ if (data->flags & MMC_DATA_WRITE) {
+ omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
+ (host->mapbase + OMAP_HSMMC_DATA), 0, 0);
+ omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
+ sg_dma_address(sgl), 0, 0);
+ } else {
+ omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
+ (host->mapbase + OMAP_HSMMC_DATA), 0, 0);
+ omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
+ sg_dma_address(sgl), 0, 0);
+ }
+
+ blksz = host->data->blksz;
+ nblk = sg_dma_len(sgl) / blksz;
+
+ omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32,
+ blksz / 4, nblk, OMAP_DMA_SYNC_FRAME,
+ mmc_omap_get_dma_sync_dev(host, data),
+ !(data->flags & MMC_DATA_WRITE));
+
+ omap_start_dma(dma_ch);
+}
+
/*
* DMA call back function
*/
@@ -611,6 +661,14 @@ static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data)

	if (host->dma_ch < 0)
		return;

+ host->dma_sg_idx++;
+ if (host->dma_sg_idx < host->dma_len) {
+ /* Fire up the next transfer. */
+ mmc_omap_config_dma_params(host, host->data,
+ host->data->sg + host->dma_sg_idx);
+ return;
+ }
+

	omap_free_dma(host->dma_ch);
	host->dma_ch = -1;
	/*

@@ -621,38 +679,28 @@ static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data) }

/*
- * Configure dma src and destination parameters
- */
-static int mmc_omap_config_dma_param(int sync_dir, struct mmc_omap_host *host,
- struct mmc_data *data)
-{
- if (sync_dir == 0) {
- omap_set_dma_dest_params(host->dma_ch, 0,
- OMAP_DMA_AMODE_CONSTANT,
- (host->mapbase + OMAP_HSMMC_DATA), 0, 0);
- omap_set_dma_src_params(host->dma_ch, 0,
- OMAP_DMA_AMODE_POST_INC,
- sg_dma_address(&data->sg[0]), 0, 0);
- } else {
- omap_set_dma_src_params(host->dma_ch, 0,
- OMAP_DMA_AMODE_CONSTANT,
- (host->mapbase + OMAP_HSMMC_DATA), 0, 0);
- omap_set_dma_dest_params(host->dma_ch, 0,
- OMAP_DMA_AMODE_POST_INC,
- sg_dma_address(&data->sg[0]), 0, 0);
- }
- return 0;
-}
-/*
* Routine to configure and start DMA for the MMC card
*/
static int
mmc_omap_start_dma_transfer(struct mmc_omap_host *host, struct mmc_request *req) {
- int sync_dev, sync_dir = 0;
- int dma_ch = 0, ret = 0, err = 1;

+	int dma_ch = 0, ret = 0, err = 1, i;
	struct mmc_data *data = req->data;

+ /* Sanity check: all the SG entries must be aligned by block size. */
+ for (i = 0; i < host->dma_len; i++) {
+ struct scatterlist *sgl;
+
+ sgl = data->sg + i;
+ if (sgl->length % data->blksz)
+ return -EINVAL;
+ }
+ if ((data->blksz % 4) != 0)
+ /* REVISIT: The MMC buffer increments only when MSB is written.
+ * Return error for blksz which is non multiple of four.
+ */
+ return -EINVAL;
+
/*
* If for some reason the DMA transfer is still active,
* we wait for timeout period and free the dma
@@ -671,49 +719,22 @@ mmc_omap_start_dma_transfer(struct mmc_omap_host *host, struct mmc_request *req)

			return err;
	}

- if (!(data->flags & MMC_DATA_WRITE)) {
- host->dma_dir = DMA_FROM_DEVICE;
- if (host->id == OMAP_MMC1_DEVID)
- sync_dev = OMAP24XX_DMA_MMC1_RX;
- else
- sync_dev = OMAP24XX_DMA_MMC2_RX;
- } else {
- host->dma_dir = DMA_TO_DEVICE;
- if (host->id == OMAP_MMC1_DEVID)
- sync_dev = OMAP24XX_DMA_MMC1_TX;
- else
- sync_dev = OMAP24XX_DMA_MMC2_TX;
- }
-
- ret = omap_request_dma(sync_dev, "MMC/SD", mmc_omap_dma_cb,
- host, &dma_ch);
+ ret = omap_request_dma(mmc_omap_get_dma_sync_dev(host, data), "MMC/SD",

+			       mmc_omap_dma_cb,host, &dma_ch);
	if (ret != 0) {
-		dev_dbg(mmc_dev(host->mmc),
+		dev_err(mmc_dev(host->mmc),
			"%s: omap_request_dma() failed with %d\n",
			mmc_hostname(host->mmc), ret);
		return ret;
	}

	host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
-			data->sg_len, host->dma_dir);
+			data->sg_len, mmc_omap_get_dma_dir(host, data));
	host->dma_ch = dma_ch;

+ host->dma_sg_idx = 0;

- if (!(data->flags & MMC_DATA_WRITE))
- mmc_omap_config_dma_param(1, host, data);
- else
- mmc_omap_config_dma_param(0, host, data);
-
- if ((data->blksz % 4) == 0)
- omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32,
- (data->blksz / 4), data->blocks, OMAP_DMA_SYNC_FRAME,
- sync_dev, sync_dir);
- else
- /* REVISIT: The MMC buffer increments only when MSB is written.
- * Return error for blksz which is non multiple of four.
- */
- return -EINVAL;
+ mmc_omap_config_dma_params(host, data, data->sg);

-	omap_start_dma(dma_ch);
	return 0;
}

@@ -763,7 +784,6 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)

	host->data = req->data;

	if (req->data == NULL) {
-		host->datadir = OMAP_MMC_DATADIR_NONE;
		OMAP_HSMMC_WRITE(host->base, BLK, 0);
		return 0;
	}
@@ -772,9 +792,6 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
					| (req->data->blocks << 16));
	set_data_timeout(host, req);

- host->datadir = (req->data->flags & MMC_DATA_WRITE) ?
- OMAP_MMC_DATADIR_WRITE : OMAP_MMC_DATADIR_READ;
-

	if (host->use_dma) {
		ret = mmc_omap_start_dma_transfer(host, req);
		if (ret != 0) {
@@ -920,7 +937,7 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
	struct mmc_host *mmc;
	struct mmc_omap_host *host = NULL;
	struct resource *res;
-	int ret = 0, irq, reg;
+	int ret = 0, irq;
	u32 hctl, capa;

	if (pdata == NULL) {
@@ -1014,10 +1031,11 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
		else
			host->dbclk_enabled = 1;

-#ifdef CONFIG_MMC_BLOCK_BOUNCE
- mmc->max_phys_segs = 1;
- mmc->max_hw_segs = 1;
-#endif
+ /* Since we do only SG emulation, we can have as many segs
+ * as we want. */
+ mmc->max_phys_segs = 1024;
+ mmc->max_hw_segs = 1024;
+

	mmc->max_blk_size = 512;       /* Block Length at max can be 1024 */
	mmc->max_blk_count = 0xFFFF;    /* No. of Blocks is 16 bits */
	mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
Signature · 1 line
1.5.4.3
#10 lore raw

PATCH 9/9 OMAP: HSMMC: SDBP and missing clock enabled fixes

In reply to Adrian Hunter

From: Jarkko Lavinen <[email protected]>
Date: Wed, 19 Nov 2008 15:49:52 +0200

Disable SDBP at suspend and enable it resume. Make sure fclk is running at suspend before touching HCTL.

---
drivers/mmc/host/omap_hsmmc.c | 31 ++++++++++++-------------------

1 files changed, 12 insertions(+), 19 deletions(-)
drivers/mmc/host/omap_hsmmc.c +0 -0
index 6058a70..78c5c18 100644
@@ -897,10 +897,7 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
		OMAP_HSMMC_WRITE(host->base, CON,
				OMAP_HSMMC_READ(host->base, CON) | OD);

- if (ios->power_mode == MMC_POWER_OFF)
- mmc_omap_fclk_state(host, OFF);
- else
- mmc_omap_fclk_lazy_disable(host);
+ mmc_omap_fclk_lazy_disable(host);
}

static int omap_hsmmc_get_cd(struct mmc_host *mmc)
@@ -1207,12 +1204,11 @@ static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state)

		return 0;

	if (host) {
-		mmc_omap_fclk_state(host, ON);
-
		ret = mmc_suspend_host(host->mmc, state);
		if (ret == 0) {
			host->suspended = 1;
+			mmc_omap_fclk_state(host, ON);
			OMAP_HSMMC_WRITE(host->base, ISE, 0);
			OMAP_HSMMC_WRITE(host->base, IE, 0);

@@ -1224,19 +1220,8 @@ static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state)

						" level suspend\n");
			}

- if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
- u32 hctl = OMAP_HSMMC_READ(host->base, HCTL) &
- SDVSCLR;
-
- if (host->id == OMAP_MMC1_DEVID)
- hctl |= SDVS30;
- else
- hctl |= SDVS18;
-
- OMAP_HSMMC_WRITE(host->base, HCTL, hctl);
- OMAP_HSMMC_WRITE(host->base, HCTL, hctl | SDBP);
- }
-
+ OMAP_HSMMC_WRITE(host->base, HCTL,

+					 OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
			mmc_omap_fclk_state(host, OFF);
			clk_disable(host->iclk);
			clk_disable(host->dbclk);
@@ -1256,6 +1241,7 @@ static int omap_mmc_resume(struct platform_device *pdev)
		return 0;

	if (host) {
+		int i;
		if (mmc_omap_fclk_state(host, ON) != 0)
			goto clk_en_err;

@@ -1270,6 +1256,13 @@ static int omap_mmc_resume(struct platform_device *pdev)

			dev_dbg(mmc_dev(host->mmc),
					"Enabling debounce clk failed\n");

+ OMAP_HSMMC_WRITE(host->base, HCTL,
+ OMAP_HSMMC_READ(host->base, HCTL) | SDBP);
+
+ for (i = 0; i < 100; i++)
+ if (OMAP_HSMMC_READ(host->base, HCTL) & SDBP)
+ break;
+

		if (host->pdata->resume) {
			ret = host->pdata->resume(&pdev->dev, host->slot_id);
			if (ret)
Signature · 1 line
1.5.4.3
#11 lore raw
In reply to Adrian Hunter
Quoted 8 lines · Adrian Hunter
Quoted 4 lines · Adrian Hunter

OMAP HSMMC is not ready for mainline, but development is ongoing.

In the meantime these patches are best posted and applied via omap mailing list and tree.

Just realised I forgot the from lines in all those patches - will resend.

And now 4 and 5 again without the spurious bug references...

#12 lore raw

PATCH 4/9 OMAP: HSMMC: Fix suspend/resume for MMCHS2

From: Jarkko Lavinen <[email protected]>
Date: Tue, 28 Oct 2008 11:12:28 +0200

For HSMMC2 and HSMMC3 set SDVSS to 1.8V in HCTL before enabling the SDBP bit.

---
drivers/mmc/host/omap_hsmmc.c | 19 ++++++++++---------

1 files changed, 10 insertions(+), 9 deletions(-)
drivers/mmc/host/omap_hsmmc.c +0 -0
index a134f76..4bc25c0 100644
@@ -1197,15 +1197,16 @@ static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state)
			}

			if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {

- OMAP_HSMMC_WRITE(host->base, HCTL,
- OMAP_HSMMC_READ(host->base, HCTL)
- & SDVSCLR);
- OMAP_HSMMC_WRITE(host->base, HCTL,
- OMAP_HSMMC_READ(host->base, HCTL)
- | SDVS30);
- OMAP_HSMMC_WRITE(host->base, HCTL,
- OMAP_HSMMC_READ(host->base, HCTL)
- | SDBP);
+ u32 hctl = OMAP_HSMMC_READ(host->base, HCTL) &
+ SDVSCLR;
+
+ if (host->id == OMAP_MMC1_DEVID)
+ hctl |= SDVS30;
+ else
+ hctl |= SDVS18;
+
+ OMAP_HSMMC_WRITE(host->base, HCTL, hctl);

+				OMAP_HSMMC_WRITE(host->base, HCTL, hctl | SDBP);
			}

			mmc_omap_fclk_state(host, OFF);
Signature · 1 line
1.5.4.3
#13 lore raw

PATCH 5/9 OMAP: HSMMC: Fix oops in omap_mmc_remove

In reply to Adrian Hunter

From: Jarkko Lavinen <[email protected]>
Date: Thu, 6 Nov 2008 16:02:29 +0200

Do not access host in omap_mmc_remove() if it is null. This occurs when module is removed before its platform data is set.

---
drivers/mmc/host/omap_hsmmc.c | 20 +++++++++++---------

1 files changed, 11 insertions(+), 9 deletions(-)
drivers/mmc/host/omap_hsmmc.c +0 -0
index 4bc25c0..f4b7c30 100644
@@ -1128,15 +1128,17 @@ static int omap_mmc_remove(struct platform_device *pdev)
	struct resource *res;
	u16 vdd = 0;

- mmc_omap_fclk_state(host, ON);
- if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
- /*
- * Set the vdd back to 3V,
- * applicable for dual volt support.
- */
- vdd = fls(host->mmc->ocr_avail) - 1;
- if (omap_mmc_switch_opcond(host, vdd) != 0)
- host->mmc->ios.vdd = vdd;
+ if (host) {
+ mmc_omap_fclk_state(host, ON);
+ if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
+ /*
+ * Set the vdd back to 3V,
+ * applicable for dual volt support.
+ */
+ vdd = fls(host->mmc->ocr_avail) - 1;
+ if (omap_mmc_switch_opcond(host, vdd) != 0)
+ host->mmc->ios.vdd = vdd;
+ }

	}

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Signature · 1 line
1.5.4.3
#14 lore raw

Re: [PATCH 5/9] OMAP: HSMMC: Fix oops in omap_mmc_remove

On Fri, Nov 21, 2008 at 11:58 AM, Adrian Hunter
<[email protected]> wrote:
<snip>

Quoted 1 line
(quoted diff) +0 -0
@@ -1128,15 +1128,17 @@ static int omap_mmc_remove(struct platform_device

<snip>

Review

Quoted 10 lines
(quoted diff) +10 -0
+               mmc_omap_fclk_state(host, ON);
+               if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
+                       /*
+                        * Set the vdd back to 3V,
+                        * applicable for dual volt support.
+                        */
+                       vdd = fls(host->mmc->ocr_avail) - 1;
+                       if (omap_mmc_switch_opcond(host, vdd) != 0)
+                               host->mmc->ios.vdd = vdd;
+               }

I wonder what this block is for? Wouldn't simple
host->mmc->ios.vdd = vdd;
be enough? It calls omap_mmc_switch_opcond() which toggles clocks and power without good reason (I think). Clocks will be turned off later in omap_mmc_remove() anyway, and .set_ios will turn off power. Also, HCTL SDVSDET bit will never be set for MMC2 and MMC3, causing mentioned omap_mmc_switch_opcond() to be called unconditionally here.

The same block can be found in mmc_omap_detect() function, where it is executed on card removal. There it causes MMC2 controller to stop working completely on pandora board :(

#15 lore raw

Re: [PATCH 5/9] OMAP: HSMMC: Fix oops in omap_mmc_remove

On Fri, Nov 21, 2008 at 04:03:25PM +0200, ext Grazvydas Ignotas wrote:
...

Quoted 6 lines

Also, HCTL SDVSDET bit will never be set for MMC2 and MMC3, causing mentioned omap_mmc_switch_opcond() to be called unconditionally here.

The same block can be found in mmc_omap_detect() function, where it is executed on card removal. There it causes MMC2 controller to stop working completely on pandora board :(

TI TRM saus HSMMC2 and HSMMC3 works only with SDVS set to 1.8V in HCTL.

If the voltage is set to 3.0V and then SDBP (bus power bit) is set, it is rejected and SDBP remains zero, the controller does nothing and is seemingly frozen.

The wrong voltage for MMC2 SDVS is set in omap_mmc_switch_opcond() if vdd different from 1.8V.

Also omap_mmc_suspend() set SDVS to 3.0V even for MMC2 and MMC3. When suspending, everything seemed to work and at resume MMC2 was hung and failed to detect the card since not even CMD0 was sent.

Regards
Jarkko Lavinen

#16 lore raw

Re: [PATCH 5/9] OMAP: HSMMC: Fix oops in omap_mmc_remove

Quoted 15 lines · Jarkko Lavinen

On Fri, Nov 21, 2008 at 04:03:25PM +0200, ext Grazvydas Ignotas wrote:
...

Quoted 6 lines

Also, HCTL SDVSDET bit will never be set for MMC2 and MMC3, causing mentioned omap_mmc_switch_opcond() to be called unconditionally here.

The same block can be found in mmc_omap_detect() function, where it is executed on card removal. There it causes MMC2 controller to stop working completely on pandora board :(

TI TRM saus HSMMC2 and HSMMC3 works only with SDVS set to 1.8V in HCTL.

If the voltage is set to 3.0V and then SDBP (bus power bit) is set, it is rejected and SDBP remains zero, the controller does nothing and is seemingly frozen.

Quoted 6 lines

The wrong voltage for MMC2 SDVS is set in omap_mmc_switch_opcond() if vdd different from 1.8V.

Also omap_mmc_suspend() set SDVS to 3.0V even for MMC2 and MMC3. When suspending, everything seemed to work and at resume MMC2 was hung and failed to detect the card since not even CMD0 was sent.

ok, but why there are calls to omap_mmc_switch_opcond() from both omap_mmc_remove() and mmc_omap_detect() at all? This basically causes power and clocks to be turned off, then on, then again off whenever card is or host driver removed. This is probably no big deal, but still.