Skip to contentSkip to conversation

OMAP: HSMMC: Make driver support dynamic idle

PATCH In review Adrian Hunter 2 participants 4 messages Nov 21, 2008 – Dec 9, 2008

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

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

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
#1 lore raw
Quoted 1 line · Adrian Hunter

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

Let's keep these patches on hold until we have the hsmmc.c driver in mainline, then get them integrated via LKML.

Meanwhile, maybe you want to set up a git branch for hsmmc to queue these? And then I could mirror your hsmmc branch.

Regards,

Tony

Quoted 183 lines

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

1 files changed, 60 insertions(+), 13 deletions(-)
drivers/mmc/host/omap_hsmmc.c +55 -7
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:

drivers/mmc/host/omap_hsmmc.c +1 -1
-	clk_disable(host->fclk);
+	mmc_omap_fclk_state(host, OFF);
	clk_disable(host->iclk);
	clk_put(host->fclk);
	clk_put(host->iclk);
drivers/mmc/host/omap_hsmmc.c +4 -5
@@ -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
#2 lore raw
Quoted 8 lines · Tony Lindgren
Quoted 1 line · Adrian Hunter

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

Let's keep these patches on hold until we have the hsmmc.c driver in mainline, then get them integrated via LKML.

Meanwhile, maybe you want to set up a git branch for hsmmc to queue these? And then I could mirror your hsmmc branch.

As you wish.

tree is:

git://git.infradead.org/users/ahunter/omap_hsmmc.git

branch is:

omap_hsmmc

#3 lore raw
Quoted 12 lines · Adrian Hunter
Quoted 8 lines · Tony Lindgren
Quoted 1 line · Adrian Hunter

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

Let's keep these patches on hold until we have the hsmmc.c driver in mainline, then get them integrated via LKML.

Meanwhile, maybe you want to set up a git branch for hsmmc to queue these? And then I could mirror your hsmmc branch.

As you wish.

Quoted 7 lines

tree is:

git://git.infradead.org/users/ahunter/omap_hsmmc.git

branch is:

omap_hsmmc

Thanks, I'm now mirroring that branch in my git tree, and I've merged it to linux-omap tree.

Please rebase your omap_hsmmc branch against the mainline tree once we have the current mmc patches in my hsmmc branch integrated to mainline. That way the omap_hsmmc queue will be ready for next merge window.

Regards,

Tony