aboutsummaryrefslogtreecommitdiff
path: root/arch/sh
diff options
context:
space:
mode:
authorPaul Mundt <lethal@linux-sh.org>2009-05-26 23:50:40 +0900
committerPaul Mundt <lethal@linux-sh.org>2009-05-26 23:50:40 +0900
commit464c9e10376b24f04402fa8ef72b66257561bffa (patch)
treeac95ad416a2d3f3afcc92ca1790267a03b2be10d /arch/sh
parentb7e2ac619465f1774b827d9997109ceef4a61851 (diff)
parent61ce5393e4c8914c46ec99cbda76823515109709 (diff)
Merge branch 'sh/clkfwk'
Diffstat (limited to 'arch/sh')
-rw-r--r--arch/sh/Kconfig7
-rw-r--r--arch/sh/boards/board-sh7785lcr.c22
-rw-r--r--arch/sh/include/asm/clock.h69
-rw-r--r--arch/sh/include/asm/machvec.h2
-rw-r--r--arch/sh/kernel/cpu/Makefile1
-rw-r--r--arch/sh/kernel/cpu/clock-cpg.c62
-rw-r--r--arch/sh/kernel/cpu/clock.c598
-rw-r--r--arch/sh/kernel/cpu/sh2/clock-sh7619.c16
-rw-r--r--arch/sh/kernel/cpu/sh2/setup-sh7619.c4
-rw-r--r--arch/sh/kernel/cpu/sh2a/clock-sh7201.c14
-rw-r--r--arch/sh/kernel/cpu/sh2a/clock-sh7203.c15
-rw-r--r--arch/sh/kernel/cpu/sh2a/clock-sh7206.c12
-rw-r--r--arch/sh/kernel/cpu/sh2a/setup-mxg.c6
-rw-r--r--arch/sh/kernel/cpu/sh2a/setup-sh7201.c6
-rw-r--r--arch/sh/kernel/cpu/sh2a/setup-sh7203.c8
-rw-r--r--arch/sh/kernel/cpu/sh2a/setup-sh7206.c10
-rw-r--r--arch/sh/kernel/cpu/sh3/clock-sh3.c12
-rw-r--r--arch/sh/kernel/cpu/sh3/clock-sh7705.c12
-rw-r--r--arch/sh/kernel/cpu/sh3/clock-sh7706.c12
-rw-r--r--arch/sh/kernel/cpu/sh3/clock-sh7709.c12
-rw-r--r--arch/sh/kernel/cpu/sh3/clock-sh7710.c12
-rw-r--r--arch/sh/kernel/cpu/sh3/clock-sh7712.c8
-rw-r--r--arch/sh/kernel/cpu/sh3/setup-sh7705.c6
-rw-r--r--arch/sh/kernel/cpu/sh3/setup-sh770x.c6
-rw-r--r--arch/sh/kernel/cpu/sh3/setup-sh7710.c6
-rw-r--r--arch/sh/kernel/cpu/sh3/setup-sh7720.c16
-rw-r--r--arch/sh/kernel/cpu/sh4/clock-sh4-202.c43
-rw-r--r--arch/sh/kernel/cpu/sh4/clock-sh4.c12
-rw-r--r--arch/sh/kernel/cpu/sh4/setup-sh4-202.c6
-rw-r--r--arch/sh/kernel/cpu/sh4/setup-sh7750.c10
-rw-r--r--arch/sh/kernel/cpu/sh4/setup-sh7760.c6
-rw-r--r--arch/sh/kernel/cpu/sh4a/clock-sh7722.c424
-rw-r--r--arch/sh/kernel/cpu/sh4a/clock-sh7763.c46
-rw-r--r--arch/sh/kernel/cpu/sh4a/clock-sh7770.c12
-rw-r--r--arch/sh/kernel/cpu/sh4a/clock-sh7780.c43
-rw-r--r--arch/sh/kernel/cpu/sh4a/clock-sh7785.c317
-rw-r--r--arch/sh/kernel/cpu/sh4a/clock-sh7786.c47
-rw-r--r--arch/sh/kernel/cpu/sh4a/clock-shx3.c41
-rw-r--r--arch/sh/kernel/cpu/sh4a/setup-sh7343.c6
-rw-r--r--arch/sh/kernel/cpu/sh4a/setup-sh7366.c6
-rw-r--r--arch/sh/kernel/cpu/sh4a/setup-sh7722.c6
-rw-r--r--arch/sh/kernel/cpu/sh4a/setup-sh7723.c5
-rw-r--r--arch/sh/kernel/cpu/sh4a/setup-sh7724.c4
-rw-r--r--arch/sh/kernel/cpu/sh4a/setup-sh7763.c12
-rw-r--r--arch/sh/kernel/cpu/sh4a/setup-sh7770.c18
-rw-r--r--arch/sh/kernel/cpu/sh4a/setup-sh7780.c12
-rw-r--r--arch/sh/kernel/cpu/sh4a/setup-sh7785.c18
-rw-r--r--arch/sh/kernel/cpu/sh4a/setup-sh7786.c24
-rw-r--r--arch/sh/kernel/cpu/sh4a/setup-shx3.c12
-rw-r--r--arch/sh/kernel/cpu/sh5/clock-sh5.c12
-rw-r--r--arch/sh/kernel/cpu/sh5/setup-sh5.c6
51 files changed, 1211 insertions, 891 deletions
diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig
index fb75c2d1928..c815975b8d7 100644
--- a/arch/sh/Kconfig
+++ b/arch/sh/Kconfig
@@ -513,6 +513,13 @@ config SH_PCLK_FREQ
This is necessary for determining the reference clock value on
platforms lacking an RTC.
+config SH_CLK_CPG
+ def_bool y
+
+config SH_CLK_CPG_LEGACY
+ depends on SH_CLK_CPG
+ def_bool y if !CPU_SUBTYPE_SH7785
+
config SH_CLK_MD
int "CPU Mode Pin Setting"
depends on CPU_SH2
diff --git a/arch/sh/boards/board-sh7785lcr.c b/arch/sh/boards/board-sh7785lcr.c
index 6f94f17adc4..33b194b0454 100644
--- a/arch/sh/boards/board-sh7785lcr.c
+++ b/arch/sh/boards/board-sh7785lcr.c
@@ -2,12 +2,12 @@
* Renesas Technology Corp. R0P7785LC0011RL Support.
*
* Copyright (C) 2008 Yoshihiro Shimoda
+ * Copyright (C) 2009 Paul Mundt
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
-
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/sm501.h>
@@ -19,8 +19,11 @@
#include <linux/i2c-pca-platform.h>
#include <linux/i2c-algo-pca.h>
#include <linux/irq.h>
-#include <asm/heartbeat.h>
+#include <linux/clk.h>
+#include <linux/errno.h>
#include <mach/sh7785lcr.h>
+#include <asm/heartbeat.h>
+#include <asm/clock.h>
/*
* NOTE: This board has 2 physical memory maps.
@@ -273,6 +276,20 @@ void __init init_sh7785lcr_IRQ(void)
plat_irq_setup_pins(IRQ_MODE_IRQ3210);
}
+static int sh7785lcr_clk_init(void)
+{
+ struct clk *clk;
+ int ret;
+
+ clk = clk_get(NULL, "extal");
+ if (!clk || IS_ERR(clk))
+ return PTR_ERR(clk);
+ ret = clk_set_rate(clk, 33333333);
+ clk_put(clk);
+
+ return ret;
+}
+
static void sh7785lcr_power_off(void)
{
unsigned char *p;
@@ -309,6 +326,7 @@ static void __init sh7785lcr_setup(char **cmdline_p)
static struct sh_machine_vector mv_sh7785lcr __initmv = {
.mv_name = "SH7785LCR",
.mv_setup = sh7785lcr_setup,
+ .mv_clk_init = sh7785lcr_clk_init,
.mv_init_irq = init_sh7785lcr_IRQ,
};
diff --git a/arch/sh/include/asm/clock.h b/arch/sh/include/asm/clock.h
index b1f29199e4b..aa9480d4aa0 100644
--- a/arch/sh/include/asm/clock.h
+++ b/arch/sh/include/asm/clock.h
@@ -10,9 +10,9 @@ struct clk;
struct clk_ops {
void (*init)(struct clk *clk);
- void (*enable)(struct clk *clk);
+ int (*enable)(struct clk *clk);
void (*disable)(struct clk *clk);
- void (*recalc)(struct clk *clk);
+ unsigned long (*recalc)(struct clk *clk);
int (*set_rate)(struct clk *clk, unsigned long rate, int algo_id);
int (*set_parent)(struct clk *clk, struct clk *parent);
long (*round_rate)(struct clk *clk, unsigned long rate);
@@ -27,44 +27,46 @@ struct clk {
struct clk *parent;
struct clk_ops *ops;
+ struct list_head children;
+ struct list_head sibling; /* node for children */
+
int usecount;
unsigned long rate;
unsigned long flags;
+
+ void __iomem *enable_reg;
+ unsigned int enable_bit;
+
unsigned long arch_flags;
+ void *priv;
+ struct dentry *dentry;
+};
+
+struct clk_lookup {
+ struct list_head node;
+ const char *dev_id;
+ const char *con_id;
+ struct clk *clk;
};
-#define CLK_ALWAYS_ENABLED (1 << 0)
-#define CLK_RATE_PROPAGATES (1 << 1)
-#define CLK_NEEDS_INIT (1 << 2)
+#define CLK_ENABLE_ON_INIT (1 << 0)
/* Should be defined by processor-specific code */
-void arch_init_clk_ops(struct clk_ops **, int type);
+void __deprecated arch_init_clk_ops(struct clk_ops **, int type);
int __init arch_clk_init(void);
/* arch/sh/kernel/cpu/clock.c */
int clk_init(void);
-
-void clk_recalc_rate(struct clk *);
-
+unsigned long followparent_recalc(struct clk *);
+void recalculate_root_clocks(void);
+void propagate_rate(struct clk *);
+int clk_reparent(struct clk *child, struct clk *parent);
int clk_register(struct clk *);
void clk_unregister(struct clk *);
-static inline int clk_always_enable(const char *id)
-{
- struct clk *clk;
- int ret;
-
- clk = clk_get(NULL, id);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
-
- ret = clk_enable(clk);
- if (ret)
- clk_put(clk);
-
- return ret;
-}
+/* arch/sh/kernel/cpu/clock-cpg.c */
+int __init __deprecated cpg_clk_init(void);
/* the exported API, in addition to clk_set_rate */
/**
@@ -96,4 +98,23 @@ enum clk_sh_algo_id {
IP_N1,
};
+
+struct clk_div_mult_table {
+ unsigned int *divisors;
+ unsigned int nr_divisors;
+ unsigned int *multipliers;
+ unsigned int nr_multipliers;
+};
+
+struct cpufreq_frequency_table;
+void clk_rate_table_build(struct clk *clk,
+ struct cpufreq_frequency_table *freq_table,
+ int nr_freqs,
+ struct clk_div_mult_table *src_table,
+ unsigned long *bitmap);
+
+long clk_rate_table_round(struct clk *clk,
+ struct cpufreq_frequency_table *freq_table,
+ unsigned long rate);
+
#endif /* __ASM_SH_CLOCK_H */
diff --git a/arch/sh/include/asm/machvec.h b/arch/sh/include/asm/machvec.h
index 64b1c16a0f0..73d6d16fa06 100644
--- a/arch/sh/include/asm/machvec.h
+++ b/arch/sh/include/asm/machvec.h
@@ -46,6 +46,8 @@ struct sh_machine_vector {
void __iomem *(*mv_ioport_map)(unsigned long port, unsigned int size);
void (*mv_ioport_unmap)(void __iomem *);
+
+ int (*mv_clk_init)(void);
};
extern struct sh_machine_vector sh_mv;
diff --git a/arch/sh/kernel/cpu/Makefile b/arch/sh/kernel/cpu/Makefile
index 2600641a483..eecad7cbd61 100644
--- a/arch/sh/kernel/cpu/Makefile
+++ b/arch/sh/kernel/cpu/Makefile
@@ -17,5 +17,6 @@ obj-$(CONFIG_ARCH_SHMOBILE) += shmobile/
obj-$(CONFIG_UBC_WAKEUP) += ubc.o
obj-$(CONFIG_SH_ADC) += adc.o
+obj-$(CONFIG_SH_CLK_CPG) += clock-cpg.o
obj-y += irq/ init.o clock.o
diff --git a/arch/sh/kernel/cpu/clock-cpg.c b/arch/sh/kernel/cpu/clock-cpg.c
new file mode 100644
index 00000000000..b78c237ab36
--- /dev/null
+++ b/arch/sh/kernel/cpu/clock-cpg.c
@@ -0,0 +1,62 @@
+#include <linux/clk.h>
+#include <linux/compiler.h>
+#include <asm/clock.h>
+
+#ifdef CONFIG_SH_CLK_CPG_LEGACY
+static struct clk master_clk = {
+ .name = "master_clk",
+ .flags = CLK_ENABLE_ON_INIT,
+ .rate = CONFIG_SH_PCLK_FREQ,
+};
+
+static struct clk peripheral_clk = {
+ .name = "peripheral_clk",
+ .parent = &master_clk,
+ .flags = CLK_ENABLE_ON_INIT,
+};
+
+static struct clk bus_clk = {
+ .name = "bus_clk",
+ .parent = &master_clk,
+ .flags = CLK_ENABLE_ON_INIT,
+};
+
+static struct clk cpu_clk = {
+ .name = "cpu_clk",
+ .parent = &master_clk,
+ .flags = CLK_ENABLE_ON_INIT,
+};
+
+/*
+ * The ordering of these clocks matters, do not change it.
+ */
+static struct clk *onchip_clocks[] = {
+ &master_clk,
+ &peripheral_clk,
+ &bus_clk,
+ &cpu_clk,
+};
+
+int __init __deprecated cpg_clk_init(void)
+{
+ int i, ret = 0;
+
+ for (i = 0; i < ARRAY_SIZE(onchip_clocks); i++) {
+ struct clk *clk = onchip_clocks[i];
+ arch_init_clk_ops(&clk->ops, i);
+ if (clk->ops)
+ ret |= clk_register(clk);
+ }
+
+ return ret;
+}
+
+/*
+ * Placeholder for compatability, until the lazy CPUs do this
+ * on their own.
+ */
+int __init __weak arch_clk_init(void)
+{
+ return cpg_clk_init();
+}
+#endif /* CONFIG_SH_CPG_CLK_LEGACY */
diff --git a/arch/sh/kernel/cpu/clock.c b/arch/sh/kernel/cpu/clock.c
index f54769f455b..aa0fd089358 100644
--- a/arch/sh/kernel/cpu/clock.c
+++ b/arch/sh/kernel/cpu/clock.c
@@ -1,15 +1,19 @@
/*
* arch/sh/kernel/cpu/clock.c - SuperH clock framework
*
- * Copyright (C) 2005, 2006, 2007 Paul Mundt
+ * Copyright (C) 2005 - 2009 Paul Mundt
*
* This clock framework is derived from the OMAP version by:
*
- * Copyright (C) 2004 - 2005 Nokia Corporation
+ * Copyright (C) 2004 - 2008 Nokia Corporation
* Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>
*
* Modified for omap shared clock framework by Tony Lindgren <tony@atomide.com>
*
+ * With clkdev bits:
+ *
+ * Copyright (C) 2008 Russell King.
+ *
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
@@ -24,141 +28,135 @@
#include <linux/seq_file.h>
#include <linux/err.h>
#include <linux/platform_device.h>
-#include <linux/proc_fs.h>
+#include <linux/debugfs.h>
+#include <linux/cpufreq.h>
#include <asm/clock.h>
+#include <asm/machvec.h>
static LIST_HEAD(clock_list);
static DEFINE_SPINLOCK(clock_lock);
static DEFINE_MUTEX(clock_list_sem);
-/*
- * Each subtype is expected to define the init routines for these clocks,
- * as each subtype (or processor family) will have these clocks at the
- * very least. These are all provided through the CPG, which even some of
- * the more quirky parts (such as ST40, SH4-202, etc.) still have.
- *
- * The processor-specific code is expected to register any additional
- * clock sources that are of interest.
- */
-static struct clk master_clk = {
- .name = "master_clk",
- .flags = CLK_ALWAYS_ENABLED | CLK_RATE_PROPAGATES,
- .rate = CONFIG_SH_PCLK_FREQ,
-};
+void clk_rate_table_build(struct clk *clk,
+ struct cpufreq_frequency_table *freq_table,
+ int nr_freqs,
+ struct clk_div_mult_table *src_table,
+ unsigned long *bitmap)
+{
+ unsigned long mult, div;
+ unsigned long freq;
+ int i;
-static struct clk module_clk = {
- .name = "module_clk",
- .parent = &master_clk,
- .flags = CLK_ALWAYS_ENABLED | CLK_RATE_PROPAGATES,
-};
+ for (i = 0; i < nr_freqs; i++) {
+ div = 1;
+ mult = 1;
-static struct clk bus_clk = {
- .name = "bus_clk",
- .parent = &master_clk,
- .flags = CLK_ALWAYS_ENABLED | CLK_RATE_PROPAGATES,
-};
+ if (src_table->divisors && i < src_table->nr_divisors)
+ div = src_table->divisors[i];
-static struct clk cpu_clk = {
- .name = "cpu_clk",
- .parent = &master_clk,
- .flags = CLK_ALWAYS_ENABLED,
-};
+ if (src_table->multipliers && i < src_table->nr_multipliers)
+ mult = src_table->multipliers[i];
-/*
- * The ordering of these clocks matters, do not change it.
- */
-static struct clk *onchip_clocks[] = {
- &master_clk,
- &module_clk,
- &bus_clk,
- &cpu_clk,
-};
+ if (!div || !mult || (bitmap && !test_bit(i, bitmap)))
+ freq = CPUFREQ_ENTRY_INVALID;
+ else
+ freq = clk->parent->rate * mult / div;
-static void propagate_rate(struct clk *clk)
-{
- struct clk *clkp;
-
- list_for_each_entry(clkp, &clock_list, node) {
- if (likely(clkp->parent != clk))
- continue;
- if (likely(clkp->ops && clkp->ops->recalc))
- clkp->ops->recalc(clkp);
- if (unlikely(clkp->flags & CLK_RATE_PROPAGATES))
- propagate_rate(clkp);
+ freq_table[i].index = i;
+ freq_table[i].frequency = freq;
}
+
+ /* Termination entry */
+ freq_table[i].index = i;
+ freq_table[i].frequency = CPUFREQ_TABLE_END;
}
-static void __clk_init(struct clk *clk)
+long clk_rate_table_round(struct clk *clk,
+ struct cpufreq_frequency_table *freq_table,
+ unsigned long rate)
{
- /*
- * See if this is the first time we're enabling the clock, some
- * clocks that are always enabled still require "special"
- * initialization. This is especially true if the clock mode
- * changes and the clock needs to hunt for the proper set of
- * divisors to use before it can effectively recalc.
- */
+ unsigned long rate_error, rate_error_prev = ~0UL;
+ unsigned long rate_best_fit = rate;
+ unsigned long highest, lowest;
+ int i;
- if (clk->flags & CLK_NEEDS_INIT) {
- if (clk->ops && clk->ops->init)
- clk->ops->init(clk);
+ highest = lowest = 0;
- clk->flags &= ~CLK_NEEDS_INIT;
- }
-}
+ for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
+ unsigned long freq = freq_table[i].frequency;
-static int __clk_enable(struct clk *clk)
-{
- if (!clk)
- return -EINVAL;
+ if (freq == CPUFREQ_ENTRY_INVALID)
+ continue;
- clk->usecount++;
+ if (freq > highest)
+ highest = freq;
+ if (freq < lowest)
+ lowest = freq;
- /* nothing to do if always enabled */
- if (clk->flags & CLK_ALWAYS_ENABLED)
- return 0;
+ rate_error = abs(freq - rate);
+ if (rate_error < rate_error_prev) {
+ rate_best_fit = freq;
+ rate_error_prev = rate_error;
+ }
- if (clk->usecount == 1) {
- __clk_init(clk);
+ if (rate_error == 0)
+ break;
+ }
- __clk_enable(clk->parent);
+ if (rate >= highest)
+ rate_best_fit = highest;
+ if (rate <= lowest)
+ rate_best_fit = lowest;
- if (clk->ops && clk->ops->enable)
- clk->ops->enable(clk);
- }
+ return rate_best_fit;
+}
- return 0;
+/* Used for clocks that always have same value as the parent clock */
+unsigned long followparent_recalc(struct clk *clk)
+{
+ return clk->parent ? clk->parent->rate : 0;
}
-int clk_enable(struct clk *clk)
+int clk_reparent(struct clk *child, struct clk *parent)
{
- unsigned long flags;
- int ret;
+ list_del_init(&child->sibling);
+ if (parent)
+ list_add(&child->sibling, &parent->children);
+ child->parent = parent;
- spin_lock_irqsave(&clock_lock, flags);
- ret = __clk_enable(clk);
- spin_unlock_irqrestore(&clock_lock, flags);
+ /* now do the debugfs renaming to reattach the child
+ to the proper parent */
- return ret;
+ return 0;
}
-EXPORT_SYMBOL_GPL(clk_enable);
-static void __clk_disable(struct clk *clk)
+/* Propagate rate to children */
+void propagate_rate(struct clk *tclk)
{
- if (!clk)
- return;
+ struct clk *clkp;
- clk->usecount--;
+ list_for_each_entry(clkp, &tclk->children, sibling) {
+ if (clkp->ops && clkp->ops->recalc)
+ clkp->rate = clkp->ops->recalc(clkp);
- WARN_ON(clk->usecount < 0);
+ propagate_rate(clkp);
+ }
+}
- if (clk->flags & CLK_ALWAYS_ENABLED)
+static void __clk_disable(struct clk *clk)
+{
+ if (clk->usecount == 0) {
+ printk(KERN_ERR "Trying disable clock %s with 0 usecount\n",
+ clk->name);
+ WARN_ON(1);
return;
+ }
- if (clk->usecount == 0) {
+ if (!(--clk->usecount)) {
if (likely(clk->ops && clk->ops->disable))
clk->ops->disable(clk);
-
- __clk_disable(clk->parent);
+ if (likely(clk->parent))
+ __clk_disable(clk->parent);
}
}
@@ -166,29 +164,103 @@ void clk_disable(struct clk *clk)
{
unsigned long flags;
+ if (!clk)
+ return;
+
spin_lock_irqsave(&clock_lock, flags);
__clk_disable(clk);
spin_unlock_irqrestore(&clock_lock, flags);
}
EXPORT_SYMBOL_GPL(clk_disable);
+static int __clk_enable(struct clk *clk)
+{
+ int ret = 0;
+
+ if (clk->usecount++ == 0) {
+ if (clk->parent) {
+ ret = __clk_enable(clk->parent);
+ if (unlikely(ret))
+ goto err;
+ }
+
+ if (clk->ops && clk->ops->enable) {
+ ret = clk->ops->enable(clk);
+ if (ret) {
+ if (clk->parent)
+ __clk_disable(clk->parent);
+ goto err;
+ }
+ }
+ }
+
+ return ret;
+err:
+ clk->usecount--;
+ return ret;
+}
+
+int clk_enable(struct clk *clk)
+{
+ unsigned long flags;
+ int ret;
+
+ if (!clk)
+ return -EINVAL;
+
+ spin_lock_irqsave(&clock_lock, flags);
+ ret = __clk_enable(clk);
+ spin_unlock_irqrestore(&clock_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_enable);
+
+static LIST_HEAD(root_clks);
+
+/**
+ * recalculate_root_clocks - recalculate and propagate all root clocks
+ *
+ * Recalculates all root clocks (clocks with no parent), which if the
+ * clock's .recalc is set correctly, should also propagate their rates.
+ * Called at init.
+ */
+void recalculate_root_clocks(void)
+{
+ struct clk *clkp;
+
+ list_for_each_entry(clkp, &root_clks, sibling) {
+ if (clkp->ops && clkp->ops->recalc)
+ clkp->rate = clkp->ops->recalc(clkp);
+ propagate_rate(clkp);
+ }
+}
+
int clk_register(struct clk *clk)
{
+ if (clk == NULL || IS_ERR(clk))
+ return -EINVAL;
+
+ /*
+ * trap out already registered clocks
+ */
+ if (clk->node.next || clk->node.prev)
+ return 0;
+
mutex_lock(&clock_list_sem);
- list_add(&clk->node, &clock_list);
+ INIT_LIST_HEAD(&clk->children);
clk->usecount = 0;
- clk->flags |= CLK_NEEDS_INIT;
- mutex_unlock(&clock_list_sem);
+ if (clk->parent)
+ list_add(&clk->sibling, &clk->parent->children);
+ else
+ list_add(&clk->sibling, &root_clks);
- if (clk->flags & CLK_ALWAYS_ENABLED) {
- __clk_init(clk);
- pr_debug( "Clock '%s' is ALWAYS_ENABLED\n", clk->name);
- if (clk->ops && clk->ops->enable)
- clk->ops->enable(clk);
- pr_debug( "Enabled.");
- }
+ list_add(&clk->node, &clock_list);
+ if (clk->ops && clk->ops->init)
+ clk->ops->init(clk);
+ mutex_unlock(&clock_list_sem);
return 0;
}
@@ -197,11 +269,21 @@ EXPORT_SYMBOL_GPL(clk_register);
void clk_unregister(struct clk *clk)
{
mutex_lock(&clock_list_sem);
+ list_del(&clk->sibling);
list_del(&clk->node);
mutex_unlock(&clock_list_sem);
}
EXPORT_SYMBOL_GPL(clk_unregister);
+static void clk_enable_init_clocks(void)
+{
+ struct clk *clkp;
+
+ list_for_each_entry(clkp, &clock_list, node)
+ if (clkp->flags & CLK_ENABLE_ON_INIT)
+ clk_enable(clkp);
+}
+
unsigned long clk_get_rate(struct clk *clk)
{
return clk->rate;
@@ -217,56 +299,59 @@ EXPORT_SYMBOL_GPL(clk_set_rate);
int clk_set_rate_ex(struct clk *clk, unsigned long rate, int algo_id)
{
int ret = -EOPNOTSUPP;
+ unsigned long flags;
- if (likely(clk->ops && clk->ops->set_rate)) {
- unsigned long flags;
+ spin_lock_irqsave(&clock_lock, flags);
- spin_lock_irqsave(&clock_lock, flags);
+ if (likely(clk->ops && clk->ops->set_rate)) {
ret = clk->ops->set_rate(clk, rate, algo_id);
- spin_unlock_irqrestore(&clock_lock, flags);
+ if (ret != 0)
+ goto out_unlock;
+ } else {
+ clk->rate = rate;
+ ret = 0;
}
- if (unlikely(clk->flags & CLK_RATE_PROPAGATES))
- propagate_rate(clk);
+ if (clk->ops && clk->ops->recalc)
+ clk->rate = clk->ops->recalc(clk);
- return ret;
-}
-EXPORT_SYMBOL_GPL(clk_set_rate_ex);
+ propagate_rate(clk);
-void clk_recalc_rate(struct clk *clk)
-{
- if (likely(clk->ops && clk->ops->recalc)) {
- unsigned long flags;
-
- spin_lock_irqsave(&clock_lock, flags);
- clk->ops->recalc(clk);
- spin_unlock_irqrestore(&clock_lock, flags);
- }
+out_unlock:
+ spin_unlock_irqrestore(&clock_lock, flags);
- if (unlikely(clk->flags & CLK_RATE_PROPAGATES))
- propagate_rate(clk);
+ return ret;
}
-EXPORT_SYMBOL_GPL(clk_recalc_rate);
+EXPORT_SYMBOL_GPL(clk_set_rate_ex);
int clk_set_parent(struct clk *clk, struct clk *parent)
{
+ unsigned long flags;
int ret = -EINVAL;
- struct clk