* [PATCH, RFC] mfd: add clock API support to asic3 using clocklib
@ 2008-06-26 14:50 Philipp Zabel
0 siblings, 0 replies; only message in thread
From: Philipp Zabel @ 2008-06-26 14:50 UTC (permalink / raw)
To: LKML; +Cc: Samuel Ortiz, Dmitry Baryshkov
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
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2008-06-26 14:51 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-06-26 14:50 [PATCH, RFC] mfd: add clock API support to asic3 using clocklib Philipp Zabel
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.