All of lore.kernel.org
 help / color / mirror / Atom feed
From: Philipp Zabel <philipp.zabel@gmail.com>
To: LKML <linux-kernel@vger.kernel.org>
Cc: Samuel Ortiz <sameo@openedhand.com>,
	Dmitry Baryshkov <dbaryshkov@gmail.com>
Subject: [PATCH, RFC] mfd: add clock API support to asic3 using clocklib
Date: Thu, 26 Jun 2008 16:50:52 +0200	[thread overview]
Message-ID: <1214491852.30078.2.camel@localhost.localdomain> (raw)

This patch adds basic clock API support for ASIC3 clocks using Dmitry
Baryshkov's clocklib (http://lkml.org/lkml/2008/6/26/278).
The CDEX clocks can be enabled/disabled and the SD host and bus clock 
rates can be changed. Also the clock settings comment for SD HCLK and 
BCLK in asic3.h is moved to the right place.

I am missing information about the spi, pwm, led, smbus and cx clock's
rates, and I'm not sure if the SOURCE0/1 bits in CLOCK_CDEX should be
handled here somehow. The CLOCK_SEL_CX bit in CLOCK_SEL is only used
internally by the IRQ controller, so I guess not handling that is fine.

Signed-off-by: Philipp Zabel <philipp.zabel@gmail.com>
---
 drivers/mfd/Kconfig       |    2 +-
 drivers/mfd/asic3.c       |  146 ++++++++++++++++++++++++++++++++++++++++++++-
 include/linux/mfd/asic3.h |    2 +-
 3 files changed, 147 insertions(+), 3 deletions(-)

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 5f87a28..a0da724 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -17,7 +17,7 @@ config MFD_SM501
 
 config MFD_ASIC3
 	bool "Support for Compaq ASIC3"
-	depends on GENERIC_HARDIRQS && HAVE_GPIO_LIB && ARM
+	depends on GENERIC_HARDIRQS && HAVE_GPIO_LIB && HAVE_CLOCKLIB && ARM
 	 ---help---
 	  This driver supports the ASIC3 multifunction chip found on many
 	  PDAs (mainly iPAQ and HTC based ones)
diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c
index 50c773c..ced53c8 100644
--- a/drivers/mfd/asic3.c
+++ b/drivers/mfd/asic3.c
@@ -18,11 +18,13 @@
 
 #include <linux/version.h>
 #include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/clocklib.h>
 #include <linux/irq.h>
 #include <linux/gpio.h>
 #include <linux/io.h>
 #include <linux/spinlock.h>
-#include <linux/platform_device.h>
 
 #include <linux/mfd/asic3.h>
 
@@ -53,6 +55,132 @@ static inline u32 asic3_read_register(struct asic3 *asic,
 			(reg >> asic->bus_shift));
 }
 
+/* clocks */
+struct clk_cdex {
+	unsigned int cdex;
+	unsigned long rate;
+	struct asic3 *asic;
+	struct clk clk;
+};
+
+static int asic3_clock_cdex_enable(struct clk *clk)
+{
+	struct clk_cdex *cclk = container_of(clk, struct clk_cdex, clk);
+	struct asic3 *asic = cclk->asic;
+        int cdex = cclk->cdex;
+	unsigned long flags, val;
+
+	local_irq_save(flags);
+
+	val = asic3_read_register(asic, ASIC3_OFFSET(CLOCK, CDEX));
+	val |= cdex;
+	asic3_write_register(asic, ASIC3_OFFSET(CLOCK, CDEX), val);
+
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+static void asic3_clock_cdex_disable(struct clk *clk)
+{
+	struct clk_cdex *cclk = container_of(clk, struct clk_cdex, clk);
+	struct asic3 *asic = cclk->asic;
+        int cdex = cclk->cdex;
+	unsigned long flags, val;
+
+	local_irq_save(flags);
+
+	val = asic3_read_register(asic, ASIC3_OFFSET(CLOCK, CDEX));
+	val &= ~cdex;
+	asic3_write_register(asic, ASIC3_OFFSET(CLOCK, CDEX), val);
+
+	local_irq_restore(flags);
+}
+
+static unsigned long asic3_clock_cdex_get_rate(struct clk *clk)
+{
+	struct clk_cdex *cclk = container_of(clk, struct clk_cdex, clk);
+
+	return cclk->rate;
+}
+
+static long asic3_clock_cdex_round_rate(struct clk *clk, unsigned long hz,
+					bool apply)
+{
+	struct clk_cdex *cclk = container_of(clk, struct clk_cdex, clk);
+	unsigned long bit = 0;
+	long rate;
+
+	if (cclk->cdex == CLOCK_CDEX_SD_HOST)
+		bit = CLOCK_SEL_SD_HCLK_SEL;
+	if (cclk->cdex == CLOCK_CDEX_SD_BUS)
+		bit = CLOCK_SEL_SD_BCLK_SEL;
+
+	if (!bit) {
+		rate = cclk->rate;
+
+		if (apply && hz != rate)
+			return -EINVAL;
+
+		return rate;
+	}
+
+	if (hz < 18432000) /* (12.288 MHz + 24.576 MHz)/2 */
+		rate = 12288000;
+	else
+		rate = 24576000;
+
+	if (apply) {
+		struct asic3 *asic = cclk->asic;
+		unsigned long flags, val;
+
+		local_irq_save(flags);
+		val = asic3_read_register(asic, ASIC3_OFFSET(CLOCK, SEL));
+		if (hz < 18432000)
+			val &= ~bit;
+		else
+			val |= bit;
+		asic3_write_register(asic, ASIC3_OFFSET(CLOCK, SEL), val);
+		local_irq_restore(flags);
+		cclk->rate = rate;
+	}
+
+	return rate;
+}
+
+static struct clk_ops clk_cdex_ops = {
+	.enable = asic3_clock_cdex_enable,
+	.disable = asic3_clock_cdex_disable,
+	.get_rate = asic3_clock_cdex_get_rate,
+	.round_rate = asic3_clock_cdex_round_rate,
+};
+
+#define INIT_CLOCK_CDEX(_name, _cdex, _rate)		\
+	&(struct clk_cdex) {				\
+		.cdex = CLOCK_CDEX_##_cdex,		\
+		.rate = _rate,				\
+		.clk = {				\
+			.name = _name,			\
+			.ops  = &clk_cdex_ops,		\
+		},					\
+	}.clk
+
+static struct clk *asic3_clks[] = {
+	INIT_CLOCK_CDEX("spi", SPI, 0),
+	INIT_CLOCK_CDEX("ds1wm", OWM, 5000000),
+	INIT_CLOCK_CDEX("pwm0", PWM0, 0),
+	INIT_CLOCK_CDEX("pwm1", PWM1, 0),
+	INIT_CLOCK_CDEX("led0", LED0, 0),
+	INIT_CLOCK_CDEX("led1", LED1, 0),
+	INIT_CLOCK_CDEX("led2", LED2, 0),
+	INIT_CLOCK_CDEX("sdhost", SD_HOST, 12288000),
+	INIT_CLOCK_CDEX("sdbus", SD_BUS, 12288000),
+	INIT_CLOCK_CDEX("smbus", SMBUS, 0),
+	INIT_CLOCK_CDEX("cx", CONTROL_CX, 0),
+	INIT_CLOCK_CDEX("ex0", EX0, 32768),
+	INIT_CLOCK_CDEX("ex1", EX1, 24576000),
+};
+
 /* IRQs */
 #define MAX_ASIC_ISR_LOOPS    20
 #define ASIC3_GPIO_BASE_INCR \
@@ -533,6 +661,7 @@ static int __init asic3_probe(struct platform_device *pdev)
 	struct resource *mem;
 	unsigned long clksel;
 	int ret = 0;
+	int i;
 
 	asic = kzalloc(sizeof(struct asic3), GFP_KERNEL);
 	if (asic == NULL) {
@@ -569,6 +698,17 @@ static int __init asic3_probe(struct platform_device *pdev)
 	clksel = 0;
 	asic3_write_register(asic, ASIC3_OFFSET(CLOCK, SEL), clksel);
 
+	/* Register ASIC3's clocks. */
+	for (i = 0; i < ARRAY_SIZE(asic3_clks); i++) {
+		struct clk_cdex *cclk = container_of(asic3_clks[i], struct clk_cdex, clk);
+		cclk->asic = asic;
+		ret = clk_register(asic3_clks[i]);
+		if (ret < 0)
+			dev_err(asic->dev,
+			       "failed to register clock %s (%d)\n",
+			       asic3_clks[i]->name, ret);
+	}
+
 	ret = asic3_irq_probe(pdev);
 	if (ret < 0) {
 		dev_err(asic->dev, "Couldn't probe IRQs\n");
@@ -608,6 +748,7 @@ static int __init asic3_probe(struct platform_device *pdev)
 
 static int asic3_remove(struct platform_device *pdev)
 {
+	int i;
 	int ret;
 	struct asic3 *asic = platform_get_drvdata(pdev);
 
@@ -616,6 +757,9 @@ static int asic3_remove(struct platform_device *pdev)
 		return ret;
 	asic3_irq_remove(pdev);
 
+	for (i = 0; i < ARRAY_SIZE(asic3_clks); i++)
+		clk_unregister(asic3_clks[i]);
+
 	asic3_write_register(asic, ASIC3_OFFSET(CLOCK, SEL), 0);
 
 	iounmap(asic->mapping);
diff --git a/include/linux/mfd/asic3.h b/include/linux/mfd/asic3.h
index 6461a2a..0610449 100644
--- a/include/linux/mfd/asic3.h
+++ b/include/linux/mfd/asic3.h
@@ -152,7 +152,6 @@ struct asic3_platform_data {
 #define CLOCK_CDEX_LED1         (1 << 7)
 #define CLOCK_CDEX_LED2         (1 << 8)
 
-/* Clocks settings: 1 for 24.576 MHz, 0 for 12.288Mhz */
 #define CLOCK_CDEX_SD_HOST      (1 << 9)   /* R/W: SD host clock source */
 #define CLOCK_CDEX_SD_BUS       (1 << 10)  /* R/W: SD bus clock source ctrl */
 #define CLOCK_CDEX_SMBUS        (1 << 11)
@@ -161,6 +160,7 @@ struct asic3_platform_data {
 #define CLOCK_CDEX_EX0          (1 << 13)  /* R/W: 32.768 kHz crystal */
 #define CLOCK_CDEX_EX1          (1 << 14)  /* R/W: 24.576 MHz crystal */
 
+/* Clocks settings: 1 for 24.576 MHz, 0 for 12.288Mhz */
 #define CLOCK_SEL_SD_HCLK_SEL   (1 << 0)   /* R/W: SDIO host clock select */
 #define CLOCK_SEL_SD_BCLK_SEL   (1 << 1)   /* R/W: SDIO bus clock select */
 
-- 
1.5.5.4



                 reply	other threads:[~2008-06-26 14:51 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

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=1214491852.30078.2.camel@localhost.localdomain \
    --to=philipp.zabel@gmail.com \
    --cc=dbaryshkov@gmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=sameo@openedhand.com \
    /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.