All of lore.kernel.org
 help / color / mirror / Atom feed
From: Ben Dooks <ben-linux@fluff.org>
To: sdhci-devel@list.drzeus.cx, linux-kernel@vger.kernel.org,
	drzeus-sdhci@drzeus.cx
Cc: Ben Dooks <ben-linux@fluff.org>
Subject: [patch 7/8] SDHCI: Add change_clock callback for glue drivers
Date: Tue, 02 Dec 2008 15:40:25 +0000	[thread overview]
Message-ID: <20081202154112.967292876@fluff.org.uk> (raw)
In-Reply-To: 20081202154018.906091477@fluff.org.uk

[-- Attachment #1: simtec/s3c64xx/sdhci-select-clock.patch --]
[-- Type: text/plain, Size: 6954 bytes --]

Add a change_clock callback to allow drivers to update
device specific clock selections and control registers
when there is a change in clock.

Move the main part of sdhci_set_clock() to a new routine
which can be called by the glue drivers to do the sdhci
standard clock management.

Update the sdhci-s3c driver to use this to select the
appropriate clock source when clocks change.

Signed-off-by: Ben Dooks <ben-linux@fluff.org>

Index: linux.git/drivers/mmc/host/sdhci-pci.c
===================================================================
--- linux.git.orig/drivers/mmc/host/sdhci-pci.c	2008-12-02 14:36:39.000000000 +0000
+++ linux.git/drivers/mmc/host/sdhci-pci.c	2008-12-02 14:36:52.000000000 +0000
@@ -391,6 +391,7 @@ static int sdhci_pci_enable_dma(struct s
 
 static struct sdhci_ops sdhci_pci_ops = {
 	.enable_dma	= sdhci_pci_enable_dma,
+	.change_clock	= sdhci_change_clock,
 };
 
 /*****************************************************************************\
Index: linux.git/drivers/mmc/host/sdhci-s3c.c
===================================================================
--- linux.git.orig/drivers/mmc/host/sdhci-s3c.c	2008-12-02 14:36:52.000000000 +0000
+++ linux.git/drivers/mmc/host/sdhci-s3c.c	2008-12-02 14:47:04.000000000 +0000
@@ -20,6 +20,7 @@
 
 #include <linux/mmc/host.h>
 
+#include <plat/regs-sdhci.h>
 #include <plat/sdhci.h>
 
 #include "sdhci.h"
@@ -31,6 +32,7 @@ struct sdhci_s3c {
 	struct platform_device	*pdev;
 	struct resource		*ioarea;
 	struct s3c_sdhci_platdata *pdata;
+	unsigned int		cur_clk;
 
 	struct clk		*clk_io;	/* clock for io bus */
 	struct clk		*clk_bus[MAX_BUS_CLK];
@@ -41,15 +43,46 @@ static inline struct sdhci_s3c *to_s3c(s
 	return sdhci_priv(host);
 }
 
+static u32 get_curclk(u32 ctrl2)
+{
+	ctrl2 &= S3C_SDHCI_CTRL2_SELBASECLK_MASK;
+	ctrl2 >>= S3C_SDHCI_CTRL2_SELBASECLK_SHIFT;
+
+	return ctrl2;
+}
+
+/**
+ * sdhci_s3c_check_sclk() - check the clock selection against what we set
+ * @host: The host to check
+ *
+ * Certain controller resets clear the extra configuration register(s) and
+ * thus we need to check that the controller still has the clock setting
+ * we set for it.
+ */
+static void sdhci_s3c_check_sclk(struct sdhci_host *host)
+{
+	struct sdhci_s3c *ourhost = to_s3c(host);
+	u32 tmp = readl(host->ioaddr + S3C_SDHCI_CONTROL2);
+
+	if (get_curclk(tmp) != ourhost->cur_clk) {
+		dev_dbg(&ourhost->pdev->dev, "restored ctrl2 clock setting\n");
+
+		tmp &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK;
+		tmp |= ourhost->cur_clk << S3C_SDHCI_CTRL2_SELBASECLK_SHIFT;
+		writel(tmp, host->ioaddr + 0x80);
+	}
+}
+
 static unsigned int sdhci_s3c_get_max_clk(struct sdhci_host *host)
 {
 	struct sdhci_s3c *ourhost = to_s3c(host);
-	unsigned int rate;
 	u32 control2;
 	int clk;
 
 	/* note, a reset will reset the clock source */
 
+	sdhci_s3c_check_sclk(host);
+
 	control2 = readl(host->ioaddr + 0x80);
 	clk = clk_get_rate(ourhost->clk_bus[(control2 >> 4) & 3]);
 
@@ -67,13 +100,82 @@ static void sdhci_s3c_set_ios(struct sdh
 	struct sdhci_s3c *ourhost = to_s3c(host);
 	struct s3c_sdhci_platdata *pdata = ourhost->pdata;
 
+	sdhci_s3c_check_sclk(host);
+
 	if (pdata->cfg_card)
 		pdata->cfg_card(ourhost->pdev, host->ioaddr, ios);
 }
 
+static unsigned int sdhci_s3c_consider_clock(struct sdhci_s3c *ourhost,
+					     unsigned int src,
+					     unsigned int wanted)
+{
+	unsigned long rate;
+	struct clk *clksrc = ourhost->clk_bus[src];
+	int div;
+
+	if (!clksrc)
+		return UINT_MAX;
+
+	rate = clk_get_rate(clksrc);
+
+	for (div = 1; div < 256; div *= 2) {
+		if ((rate / div) <= wanted)
+			break;
+	}
+
+	dev_dbg(&ourhost->pdev->dev, "clk %d: rate %ld, want %d, got %ld\n",
+		src, rate, wanted, rate / div);
+
+	return (wanted - (rate / div));
+}
+
+static void sdhci_s3c_change_clock(struct sdhci_host *host, unsigned int clock)
+{
+	struct sdhci_s3c *ourhost = to_s3c(host);
+	unsigned int best = UINT_MAX;
+	unsigned int delta;
+	int best_src = 0;
+	int src;
+	u32 ctrl;
+
+	for (src = 0; src < MAX_BUS_CLK; src++) {
+		delta = sdhci_s3c_consider_clock(ourhost, src, clock);
+		if (delta < best) {
+			best = delta;
+			best_src = src;
+		}
+	}
+
+	dev_dbg(&ourhost->pdev->dev,
+		"selected source %d, clock %d, delta %d\n",
+		 best_src, clock, best);
+
+	/* turn clock off to card before changing clock source */
+	writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL);
+
+	/* select the new clock source */
+
+	if (ourhost->cur_clk != best_src) {
+		struct clk *clk = ourhost->clk_bus[best_src];
+
+		ourhost->cur_clk = best_src;
+		host->max_clk = clk_get_rate(clk);
+		host->timeout_clk = host->max_clk / 1000000;
+
+		ctrl = readl(host->ioaddr + S3C_SDHCI_CONTROL2);
+		ctrl &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK;
+		ctrl |= best_src << S3C_SDHCI_CTRL2_SELBASECLK_SHIFT;
+		writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL2);
+	}
+
+	sdhci_change_clock(host, clock);
+}
+
 static struct sdhci_ops sdhci_s3c_ops = {
 	.get_max_clock		= sdhci_s3c_get_max_clk,
 	.get_timeout_clock	= sdhci_s3c_get_timeout_clk,
+	.change_clock		= sdhci_s3c_change_clock,
 	.set_ios		= sdhci_s3c_set_ios,
 };
 
Index: linux.git/drivers/mmc/host/sdhci.c
===================================================================
--- linux.git.orig/drivers/mmc/host/sdhci.c	2008-12-02 14:36:52.000000000 +0000
+++ linux.git/drivers/mmc/host/sdhci.c	2008-12-02 14:36:52.000000000 +0000
@@ -907,13 +907,18 @@ static void sdhci_finish_command(struct 
 
 static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
 {
+	if (clock == host->clock)
+		return;
+
+	host->ops->change_clock(host, clock);
+}
+
+void sdhci_change_clock(struct sdhci_host *host, unsigned int clock)
+{
 	int div;
 	u16 clk;
 	unsigned long timeout;
 
-	if (clock == host->clock)
-		return;
-
 	writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL);
 
 	if (clock == 0)
@@ -950,6 +955,8 @@ out:
 	host->clock = clock;
 }
 
+EXPORT_SYMBOL_GPL(sdhci_set_clock);
+
 static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
 {
 	u8 pwr;
Index: linux.git/drivers/mmc/host/sdhci.h
===================================================================
--- linux.git.orig/drivers/mmc/host/sdhci.h	2008-12-02 14:36:52.000000000 +0000
+++ linux.git/drivers/mmc/host/sdhci.h	2008-12-02 14:36:52.000000000 +0000
@@ -272,6 +272,9 @@ struct sdhci_ops {
 	unsigned int	(*get_max_clock)(struct sdhci_host *host);
 	unsigned int	(*get_timeout_clock)(struct sdhci_host *host);
 
+	void		(*change_clock)(struct sdhci_host *host,
+					unsigned int clock);
+
 	void		(*set_ios)(struct sdhci_host *host,
 				   struct mmc_ios *ios);
 };
@@ -281,6 +284,8 @@ extern struct sdhci_host *sdhci_alloc_ho
 	size_t priv_size);
 extern void sdhci_free_host(struct sdhci_host *host);
 
+extern void sdhci_change_clock(struct sdhci_host *host, unsigned int clock);
+
 static inline void *sdhci_priv(struct sdhci_host *host)
 {
 	return (void *)host->private;

-- 
Ben (ben@fluff.org, http://www.fluff.org/)

  'a smiley only costs 4 bytes'

  parent reply	other threads:[~2008-12-02 15:43 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-12-02 15:40 [patch 0/8] Update Samsung SDHCI Ben Dooks
2008-12-02 15:40 ` [patch 1/8] SDHCI: Add timeout hooks Ben Dooks
2008-12-02 16:46   ` smohideen
2008-12-21 13:16     ` Pierre Ossman
2008-12-02 15:40 ` [patch 2/8] SDHCI: Print ADMA status and pointer on debug Ben Dooks
2008-12-02 15:40 ` [patch 3/8] SDHCI: Add set_ios hook Ben Dooks
2008-12-21 13:22   ` Pierre Ossman
2008-12-02 15:40 ` [patch 4/8] SDHCI: Add quirk for controller with no end-of-busy IRQ Ben Dooks
2008-12-02 15:40 ` [patch 5/8] SDHCI: Samsung SDHCI (HSMMC) driver Ben Dooks
2008-12-21 13:32   ` Pierre Ossman
2008-12-02 15:40 ` [patch 6/8] SDHCI: Check DMA for overruns at end of transfer Ben Dooks
2008-12-21 13:49   ` Pierre Ossman
2008-12-02 15:40 ` Ben Dooks [this message]
2008-12-21 14:07   ` [patch 7/8] SDHCI: Add change_clock callback for glue drivers Pierre Ossman
2008-12-02 15:40 ` [patch 8/8] SDHCI: Add maintainers entry for Samsung SoC Ben Dooks
2008-12-21 14:09   ` Pierre Ossman

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20081202154112.967292876@fluff.org.uk \
    --to=ben-linux@fluff.org \
    --cc=drzeus-sdhci@drzeus.cx \
    --cc=linux-kernel@vger.kernel.org \
    --cc=sdhci-devel@list.drzeus.cx \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.