diff options
author | Paul Mundt <lethal@linux-sh.org> | 2009-05-26 23:50:40 +0900 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2009-05-26 23:50:40 +0900 |
commit | 464c9e10376b24f04402fa8ef72b66257561bffa (patch) | |
tree | ac95ad416a2d3f3afcc92ca1790267a03b2be10d /arch/sh | |
parent | b7e2ac619465f1774b827d9997109ceef4a61851 (diff) | |
parent | 61ce5393e4c8914c46ec99cbda76823515109709 (diff) |
Merge branch 'sh/clkfwk'
Diffstat (limited to 'arch/sh')
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 |