diff options
Diffstat (limited to 'arch/sh')
-rw-r--r-- | arch/sh/boards/overdrive/Makefile | 2 | ||||
-rw-r--r-- | arch/sh/boards/overdrive/setup.c | 6 | ||||
-rw-r--r-- | arch/sh/boards/overdrive/time.c | 119 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/clock.c | 287 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh3/Makefile | 7 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh3/clock-sh3.c | 89 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh3/clock-sh7300.c | 78 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh3/clock-sh7705.c | 84 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh3/clock-sh7709.c | 96 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh4/Makefile | 11 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh4/clock-sh4-202.c | 179 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh4/clock-sh4.c | 80 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh4/clock-sh73180.c | 81 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh4/clock-sh7770.c | 73 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh4/clock-sh7780.c | 126 | ||||
-rw-r--r-- | arch/sh/kernel/time.c | 518 |
16 files changed, 1224 insertions, 612 deletions
diff --git a/arch/sh/boards/overdrive/Makefile b/arch/sh/boards/overdrive/Makefile index 1762b59e927..245f03baf76 100644 --- a/arch/sh/boards/overdrive/Makefile +++ b/arch/sh/boards/overdrive/Makefile @@ -2,7 +2,7 @@ # Makefile for the STMicroelectronics Overdrive specific parts of the kernel # -obj-y := mach.o setup.o io.o irq.o led.o time.o +obj-y := mach.o setup.o io.o irq.o led.o obj-$(CONFIG_PCI) += fpga.o galileo.o pcidma.o diff --git a/arch/sh/boards/overdrive/setup.c b/arch/sh/boards/overdrive/setup.c index a36ce0284ed..94f6165d33b 100644 --- a/arch/sh/boards/overdrive/setup.c +++ b/arch/sh/boards/overdrive/setup.c @@ -17,8 +17,6 @@ #include <asm/overdrive/overdrive.h> #include <asm/overdrive/fpga.h> -extern void od_time_init(void); - const char *get_system_type(void) { return "SH7750 Overdrive"; @@ -31,11 +29,9 @@ int __init platform_setup(void) { #ifdef CONFIG_PCI init_overdrive_fpga(); - galileo_init(); + galileo_init(); #endif - board_time_init = od_time_init; - /* Enable RS232 receive buffers */ writel(0x1e, OVERDRIVE_CTRL); } diff --git a/arch/sh/boards/overdrive/time.c b/arch/sh/boards/overdrive/time.c deleted file mode 100644 index 68533690e09..00000000000 --- a/arch/sh/boards/overdrive/time.c +++ /dev/null @@ -1,119 +0,0 @@ -/* - * arch/sh/boards/overdrive/time.c - * - * Copyright (C) 2000 Stuart Menefy (stuart.menefy@st.com) - * Copyright (C) 2002 Paul Mundt (lethal@chaoticdreams.org) - * - * May be copied or modified under the terms of the GNU General Public - * License. See linux/COPYING for more information. - * - * STMicroelectronics Overdrive Support. - */ - -void od_time_init(void) -{ - struct frqcr_data { - unsigned short frqcr; - struct { - unsigned char multiplier; - unsigned char divisor; - } factor[3]; - }; - - static struct frqcr_data st40_frqcr_table[] = { - { 0x000, {{1,1}, {1,1}, {1,2}}}, - { 0x002, {{1,1}, {1,1}, {1,4}}}, - { 0x004, {{1,1}, {1,1}, {1,8}}}, - { 0x008, {{1,1}, {1,2}, {1,2}}}, - { 0x00A, {{1,1}, {1,2}, {1,4}}}, - { 0x00C, {{1,1}, {1,2}, {1,8}}}, - { 0x011, {{1,1}, {2,3}, {1,6}}}, - { 0x013, {{1,1}, {2,3}, {1,3}}}, - { 0x01A, {{1,1}, {1,2}, {1,4}}}, - { 0x01C, {{1,1}, {1,2}, {1,8}}}, - { 0x023, {{1,1}, {2,3}, {1,3}}}, - { 0x02C, {{1,1}, {1,2}, {1,8}}}, - { 0x048, {{1,2}, {1,2}, {1,4}}}, - { 0x04A, {{1,2}, {1,2}, {1,6}}}, - { 0x04C, {{1,2}, {1,2}, {1,8}}}, - { 0x05A, {{1,2}, {1,3}, {1,6}}}, - { 0x05C, {{1,2}, {1,3}, {1,6}}}, - { 0x063, {{1,2}, {1,4}, {1,4}}}, - { 0x06C, {{1,2}, {1,4}, {1,8}}}, - { 0x091, {{1,3}, {1,3}, {1,6}}}, - { 0x093, {{1,3}, {1,3}, {1,6}}}, - { 0x0A3, {{1,3}, {1,6}, {1,6}}}, - { 0x0DA, {{1,4}, {1,4}, {1,8}}}, - { 0x0DC, {{1,4}, {1,4}, {1,8}}}, - { 0x0EC, {{1,4}, {1,8}, {1,8}}}, - { 0x123, {{1,4}, {1,4}, {1,8}}}, - { 0x16C, {{1,4}, {1,8}, {1,8}}}, - }; - - struct memclk_data { - unsigned char multiplier; - unsigned char divisor; - }; - static struct memclk_data st40_memclk_table[8] = { - {1,1}, // 000 - {1,2}, // 001 - {1,3}, // 010 - {2,3}, // 011 - {1,4}, // 100 - {1,6}, // 101 - {1,8}, // 110 - {1,8} // 111 - }; - - unsigned long pvr; - - /* - * This should probably be moved into the SH3 probing code, and then - * use the processor structure to determine which CPU we are running - * on. - */ - pvr = ctrl_inl(CCN_PVR); - printk("PVR %08x\n", pvr); - - if (((pvr >> CCN_PVR_CHIP_SHIFT) & CCN_PVR_CHIP_MASK) == CCN_PVR_CHIP_ST40STB1) { - /* - * Unfortunatly the STB1 FRQCR values are different from the - * 7750 ones. - */ - struct frqcr_data *d; - int a; - unsigned long memclkcr; - struct memclk_data *e; - - for (a=0; a<ARRAY_SIZE(st40_frqcr_table); a++) { - d = &st40_frqcr_table[a]; - if (d->frqcr == (frqcr & 0x1ff)) - break; - } - if (a == ARRAY_SIZE(st40_frqcr_table)) { - d = st40_frqcr_table; - printk("ERROR: Unrecognised FRQCR value, using default multipliers\n"); - } - - memclkcr = ctrl_inl(CLOCKGEN_MEMCLKCR); - e = &st40_memclk_table[memclkcr & MEMCLKCR_RATIO_MASK]; - - printk("Clock multipliers: CPU: %d/%d Bus: %d/%d Mem: %d/%d Periph: %d/%d\n", - d->factor[0].multiplier, d->factor[0].divisor, - d->factor[1].multiplier, d->factor[1].divisor, - e->multiplier, e->divisor, - d->factor[2].multiplier, d->factor[2].divisor); - - current_cpu_data.master_clock = current_cpu_data.module_clock * - d->factor[2].divisor / - d->factor[2].multiplier; - current_cpu_data.bus_clock = current_cpu_data.master_clock * - d->factor[1].multiplier / - d->factor[1].divisor; - current_cpu_data.memory_clock = current_cpu_data.master_clock * - e->multiplier / e->divisor; - current_cpu_data.cpu_clock = current_cpu_data.master_clock * - d->factor[0].multiplier / - d->factor[0].divisor; -} - diff --git a/arch/sh/kernel/cpu/clock.c b/arch/sh/kernel/cpu/clock.c new file mode 100644 index 00000000000..989e7fdd524 --- /dev/null +++ b/arch/sh/kernel/cpu/clock.c @@ -0,0 +1,287 @@ +/* + * arch/sh/kernel/cpu/clock.c - SuperH clock framework + * + * Copyright (C) 2005 Paul Mundt + * + * This clock framework is derived from the OMAP version by: + * + * Copyright (C) 2004 Nokia Corporation + * Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com> + * + * 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/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/list.h> +#include <linux/kref.h> +#include <linux/seq_file.h> +#include <linux/err.h> +#include <asm/clock.h> +#include <asm/timer.h> + +static LIST_HEAD(clock_list); +static DEFINE_SPINLOCK(clock_lock); +static DECLARE_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, +#ifdef CONFIG_SH_PCLK_FREQ_BOOL + .rate = CONFIG_SH_PCLK_FREQ, +#endif +}; + +static struct clk module_clk = { + .name = "module_clk", + .parent = &master_clk, + .flags = CLK_ALWAYS_ENABLED | CLK_RATE_PROPAGATES, +}; + +static struct clk bus_clk = { + .name = "bus_clk", + .parent = &master_clk, + .flags = CLK_ALWAYS_ENABLED | CLK_RATE_PROPAGATES, +}; + +static struct clk cpu_clk = { + .name = "cpu_clk", + .parent = &master_clk, + .flags = CLK_ALWAYS_ENABLED, +}; + +/* + * The ordering of these clocks matters, do not change it. + */ +static struct clk *onchip_clocks[] = { + &master_clk, + &module_clk, + &bus_clk, + &cpu_clk, +}; + +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); + } +} + +int __clk_enable(struct clk *clk) +{ + /* + * 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. + */ + if (unlikely(atomic_read(&clk->kref.refcount) == 1)) + if (clk->ops && clk->ops->init) + clk->ops->init(clk); + + if (clk->flags & CLK_ALWAYS_ENABLED) + return 0; + + if (likely(clk->ops && clk->ops->enable)) + clk->ops->enable(clk); + + kref_get(&clk->kref); + return 0; +} + +int clk_enable(struct clk *clk) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&clock_lock, flags); + ret = __clk_enable(clk); + spin_unlock_irqrestore(&clock_lock, flags); + + return ret; +} + +static void clk_kref_release(struct kref *kref) +{ + /* Nothing to do */ +} + +void __clk_disable(struct clk *clk) +{ + if (clk->flags & CLK_ALWAYS_ENABLED) + return; + + kref_put(&clk->kref, clk_kref_release); +} + +void clk_disable(struct clk *clk) +{ + unsigned long flags; + + spin_lock_irqsave(&clock_lock, flags); + __clk_disable(clk); + spin_unlock_irqrestore(&clock_lock, flags); +} + +int clk_register(struct clk *clk) +{ + down(&clock_list_sem); + + list_add(&clk->node, &clock_list); + kref_init(&clk->kref); + + up(&clock_list_sem); + + return 0; +} + +void clk_unregister(struct clk *clk) +{ + down(&clock_list_sem); + list_del(&clk->node); + up(&clock_list_sem); +} + +inline unsigned long clk_get_rate(struct clk *clk) +{ + return clk->rate; +} + +int clk_set_rate(struct clk *clk, unsigned long rate) +{ + int ret = -EOPNOTSUPP; + + if (likely(clk->ops && clk->ops->set_rate)) { + unsigned long flags; + + spin_lock_irqsave(&clock_lock, flags); + ret = clk->ops->set_rate(clk, rate); + spin_unlock_irqrestore(&clock_lock, flags); + } + + if (unlikely(clk->flags & CLK_RATE_PROPAGATES)) + propagate_rate(clk); + + return ret; +} + +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); + } + + if (unlikely(clk->flags & CLK_RATE_PROPAGATES)) + propagate_rate(clk); +} + +struct clk *clk_get(const char *id) +{ + struct clk *p, *clk = ERR_PTR(-ENOENT); + + down(&clock_list_sem); + list_for_each_entry(p, &clock_list, node) { + if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) { + clk = p; + break; + } + } + up(&clock_list_sem); + + return clk; +} + +void clk_put(struct clk *clk) +{ + if (clk && !IS_ERR(clk)) + module_put(clk->owner); +} + +void __init __attribute__ ((weak)) +arch_init_clk_ops(struct clk_ops **ops, int type) +{ +} + +int __init clk_init(void) +{ + int i, ret = 0; + + if (unlikely(!master_clk.rate)) + /* + * NOTE: This will break if the default divisor has been + * changed. + * + * No one should be changing the default on us however, + * expect that a sane value for CONFIG_SH_PCLK_FREQ will + * be defined in the event of a different divisor. + */ + master_clk.rate = get_timer_frequency() * 4; + + for (i = 0; i < ARRAY_SIZE(onchip_clocks); i++) { + struct clk *clk = onchip_clocks[i]; + + arch_init_clk_ops(&clk->ops, i); + ret |= clk_register(clk); + clk_enable(clk); + } + + /* Kick the child clocks.. */ + propagate_rate(&master_clk); + propagate_rate(&bus_clk); + + return ret; +} + +int show_clocks(struct seq_file *m) +{ + struct clk *clk; + + list_for_each_entry_reverse(clk, &clock_list, node) { + unsigned long rate = clk_get_rate(clk); + + /* + * Don't bother listing dummy clocks with no ancestry + * that only support enable and disable ops. + */ + if (unlikely(!rate && !clk->parent)) + continue; + + seq_printf(m, "%-12s\t: %ld.%02ldMHz\n", clk->name, + rate / 1000000, (rate % 1000000) / 10000); + } + + return 0; +} + +EXPORT_SYMBOL_GPL(clk_register); +EXPORT_SYMBOL_GPL(clk_unregister); +EXPORT_SYMBOL_GPL(clk_get); +EXPORT_SYMBOL_GPL(clk_put); +EXPORT_SYMBOL_GPL(clk_enable); +EXPORT_SYMBOL_GPL(clk_disable); +EXPORT_SYMBOL_GPL(__clk_enable); +EXPORT_SYMBOL_GPL(__clk_disable); +EXPORT_SYMBOL_GPL(clk_get_rate); +EXPORT_SYMBOL_GPL(clk_set_rate); +EXPORT_SYMBOL_GPL(clk_recalc_rate); diff --git a/arch/sh/kernel/cpu/sh3/Makefile b/arch/sh/kernel/cpu/sh3/Makefile index a64532e4dc6..b54dbb9a0c8 100644 --- a/arch/sh/kernel/cpu/sh3/Makefile +++ b/arch/sh/kernel/cpu/sh3/Makefile @@ -4,3 +4,10 @@ obj-y := ex.o probe.o +clock-$(CONFIG_CPU_SH3) := clock-sh3.o +clock-$(CONFIG_CPU_SUBTYPE_SH7300) := clock-sh7300.o +clock-$(CONFIG_CPU_SUBTYPE_SH7705) := clock-sh7705.o +clock-$(CONFIG_CPU_SUBTYPE_SH7709) := clock-sh7709.o + +obj-y += $(clock-y) + diff --git a/arch/sh/kernel/cpu/sh3/clock-sh3.c b/arch/sh/kernel/cpu/sh3/clock-sh3.c new file mode 100644 index 00000000000..c3c945958ba --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/clock-sh3.c @@ -0,0 +1,89 @@ +/* + * arch/sh/kernel/cpu/sh3/clock-sh3.c + * + * Generic SH-3 support for the clock framework + * + * Copyright (C) 2005 Paul Mundt + * + * FRQCR parsing hacked out of arch/sh/kernel/time.c + * + * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka + * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> + * Copyright (C) 2002, 2003, 2004 Paul Mundt + * Copyright (C) 2002 M. R. Brown <mrbrown@linux-sh.org> + * + * 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/kernel.h> +#include <asm/clock.h> +#include <asm/freq.h> +#include <asm/io.h> + +static int stc_multipliers[] = { 1, 2, 3, 4, 6, 1, 1, 1 }; +static int ifc_divisors[] = { 1, 2, 3, 4, 1, 1, 1, 1 }; +static int pfc_divisors[] = { 1, 2, 3, 4, 6, 1, 1, 1 }; + +static void master_clk_init(struct clk *clk) +{ + int frqcr = ctrl_inw(FRQCR); + int idx = ((frqcr & 0x2000) >> 11) | (frqcr & 0x0003); + + clk->rate *= pfc_divisors[idx]; +} + +static struct clk_ops sh3_master_clk_ops = { + .init = master_clk_init, +}; + +static void module_clk_recalc(struct clk *clk) +{ + int frqcr = ctrl_inw(FRQCR); + int idx = ((frqcr & 0x2000) >> 11) | (frqcr & 0x0003); + + clk->rate = clk->parent->rate / pfc_divisors[idx]; +} + +static struct clk_ops sh3_module_clk_ops = { + .recalc = module_clk_recalc, +}; + +static void bus_clk_recalc(struct clk *clk) +{ + int frqcr = ctrl_inw(FRQCR); + int idx = ((frqcr & 0x8000) >> 13) | ((frqcr & 0x0030) >> 4); + + clk->rate = clk->parent->rate / stc_multipliers[idx]; +} + +static struct clk_ops sh3_bus_clk_ops = { + .recalc = bus_clk_recalc, +}; + +static void cpu_clk_recalc(struct clk *clk) +{ + int frqcr = ctrl_inw(FRQCR); + int idx = ((frqcr & 0x4000) >> 12) | ((frqcr & 0x000c) >> 2); + + clk->rate = clk->parent->rate / ifc_divisors[idx]; +} + +static struct clk_ops sh3_cpu_clk_ops = { + .recalc = cpu_clk_recalc, +}; + +static struct clk_ops *sh3_clk_ops[] = { + &sh3_master_clk_ops, + &sh3_module_clk_ops, + &sh3_bus_clk_ops, + &sh3_cpu_clk_ops, +}; + +void __init arch_init_clk_ops(struct clk_ops **ops, int idx) +{ + if (idx < ARRAY_SIZE(sh3_clk_ops)) + *ops = sh3_clk_ops[idx]; +} + diff --git a/arch/sh/kernel/cpu/sh3/clock-sh7300.c b/arch/sh/kernel/cpu/sh3/clock-sh7300.c new file mode 100644 index 00000000000..e804174b962 --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/clock-sh7300.c @@ -0,0 +1,78 @@ +/* + * arch/sh/kernel/cpu/sh3/clock-sh7300.c + * + * SH7300 support for the clock framework + * + * Copyright (C) 2005 Paul Mundt + * + * FRQCR parsing hacked out of arch/sh/kernel/time.c + * + * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka + * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> + * Copyright (C) 2002, 2003, 2004 Paul Mundt + * Copyright (C) 2002 M. R. Brown <mrbrown@linux-sh.org> + * + * 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/kernel.h> +#include <asm/clock.h> +#include <asm/freq.h> +#include <asm/io.h> + +static int md_table[] = { 1, 2, 3, 4, 6, 8, 12 }; + +static void master_clk_init(struct clk *clk) +{ + clk->rate *= md_table[ctrl_inw(FRQCR) & 0x0007]; +} + +static struct clk_ops sh7300_master_clk_ops = { + .init = master_clk_init, +}; + +static void module_clk_recalc(struct clk *clk) +{ + int idx = (ctrl_inw(FRQCR) & 0x0007); + clk->rate = clk->parent->rate / md_table[idx]; +} + +static struct clk_ops sh7300_module_clk_ops = { + .recalc = module_clk_recalc, +}; + +static void bus_clk_recalc(struct clk *clk) +{ + int idx = (ctrl_inw(FRQCR) & 0x0700) >> 8; + clk->rate = clk->parent->rate / md_table[idx]; +} + +static struct clk_ops sh7300_bus_clk_ops = { + .recalc = bus_clk_recalc, +}; + +static void cpu_clk_recalc(struct clk *clk) +{ + int idx = (ctrl_inw(FRQCR) & 0x0070) >> 4; + clk->rate = clk->parent->rate / md_table[idx]; +} + +static struct clk_ops sh7300_cpu_clk_ops = { + .recalc = cpu_clk_recalc, +}; + +static struct clk_ops *sh7300_clk_ops[] = { + &sh7300_master_clk_ops, + &sh7300_module_clk_ops, + &sh7300_bus_clk_ops, + &sh7300_cpu_clk_ops, +}; + +void __init arch_init_clk_ops(struct clk_ops **ops, int idx) +{ + if (idx < ARRAY_SIZE(sh7300_clk_ops)) + *ops = sh7300_clk_ops[idx]; +} + diff --git a/arch/sh/kernel/cpu/sh3/clock-sh7705.c b/arch/sh/kernel/cpu/sh3/clock-sh7705.c new file mode 100644 index 00000000000..dfdbf3277fd --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/clock-sh7705.c @@ -0,0 +1,84 @@ +/* + * arch/sh/kernel/cpu/sh3/clock-sh7705.c + * + * SH7705 support for the clock framework + * + * Copyright (C) 2005 Paul Mundt + * + * FRQCR parsing hacked out of arch/sh/kernel/time.c + * + * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka + * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> + * Copyright (C) 2002, 2003, 2004 Paul Mundt + * Copyright (C) 2002 M. R. Brown <mrbrown@linux-sh.org> + * + * 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/kernel.h> +#include <asm/clock.h> +#include <asm/freq.h> +#include <asm/io.h> + +/* + * SH7705 uses the same divisors as the generic SH-3 case, it's just the + * FRQCR layout that is a bit different.. + */ +static int stc_multipliers[] = { 1, 2, 3, 4, 6, 1, 1, 1 }; +static int ifc_divisors[] = { 1, 2, 3, 4, 1, 1, 1, 1 }; +static int pfc_divisors[] = { 1, 2, 3, 4, 6, 1, 1, 1 }; + +static void master_clk_init(struct clk *clk) +{ + clk->rate *= pfc_divisors[ctrl_inw(FRQCR) & 0x0003]; +} + +static struct clk_ops sh7705_master_clk_ops = { + .init = master_clk_init, +}; + +static void module_clk_recalc(struct clk *clk) +{ + int idx = ctrl_inw(FRQCR) & 0x0003; + clk->rate = clk->parent->rate / pfc_divisors[idx]; +} + +static struct clk_ops sh7705_module_clk_ops = { + .recalc = module_clk_recalc, +}; + +static void bus_clk_recalc(struct clk *clk) +{ + int idx = (ctrl_inw(FRQCR) & 0x0300) >> 8; + clk->rate = clk->parent->rate / stc_multipliers[idx]; +} + +static struct clk_ops sh7705_bus_clk_ops = { + .recalc = bus_clk_recalc, +}; + +static void cpu_clk_recalc(struct clk *clk) +{ + int idx = (ctrl_inw(FRQCR) & 0x0030) >> 4; + clk->rate = clk->parent->rate / ifc_divisors[idx]; +} + +static struct clk_ops sh7705_cpu_clk_ops = { + .recalc = cpu_clk_recalc, +}; + +static struct clk_ops *sh7705_clk_ops[] = { + &sh7705_master_clk_ops, + &sh7705_module_clk_ops, + &sh7705_bus_clk_ops, + &sh7705_cpu_clk_ops, +}; + +void __init arch_init_clk_ops(struct clk_ops **ops, int idx) +{ + if (idx < ARRAY_SIZE(sh7705_clk_ops)) + *ops = sh7705_clk_ops[idx]; +} + diff --git a/arch/sh/kernel/cpu/sh3/clock-sh7709.c b/arch/sh/kernel/cpu/sh3/clock-sh7709.c new file mode 100644 index 00000000000..10461a745e5 --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/clock-sh7709.c @@ -0,0 +1,96 @@ +/* + * arch/sh/kernel/cpu/sh3/clock-sh7709.c + * + * SH7709 support for the clock framework + * + * Copyright (C) 2005 Andriy Skulysh + * + * Based on arch/sh/kernel/cpu/sh3/clock-sh7705.c + * Copyright (C) 2005 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/kernel.h> +#include <asm/clock.h> +#include <asm/freq.h> +#include <asm/io.h> + +static int stc_multipliers[] = { 1, 2, 4, 8, 3, 6, 1, 1 }; +static int ifc_divisors[] = { 1, 2, 4, 1, 3, 1, 1, 1 }; +static int pfc_divisors[] = { 1, 2, 4, 1, 3, 6, 1, 1 }; + +static void set_bus_parent(struct clk *clk) +{ + struct clk *bus_clk = clk_get("bus_clk"); + clk->parent = bus_clk; + clk_put(bus_clk); +} + +static void master_clk_init(struct clk *clk) +{ + int frqcr = ctrl_inw(FRQCR); + int idx = ((frqcr & 0x2000) >> 11) | (frqcr & 0x0003); + + clk->rate *= pfc_divisors[idx]; +} + +static struct clk_ops sh7709_master_clk_ops = { + .init = master_clk_init, +}; + +static void module_clk_recalc(struct clk *clk) +{ + int frqcr = ctrl_inw(FRQCR); + int idx = ((frqcr & 0x2000) >> 11) | (frqcr & 0x0003); + + clk->rate = clk->parent->rate / pfc_divisors[idx]; +} + +static struct clk_ops sh7709_module_clk_ops = { +#ifdef CLOCK_MODE_0_1_2_7 + .init = set_bus_parent, +#endif + .recalc = module_clk_recalc, +}; + +static void bus_clk_recalc(struct clk *clk) +{ + int frqcr = ctrl_inw(FRQCR); + int idx = (frqcr & 0x0080) ? + ((frqcr & 0x8000) >> 13) | ((frqcr & 0x0030) >> 4) : 1; + + clk->rate = clk->parent->rate * stc_multipliers[idx]; +} + +static struct clk_ops sh7709_bus_clk_ops = { + .recalc = bus_clk_recalc, +}; + +static void cpu_clk_recalc(struct clk *clk) +{ + int frqcr = ctrl_inw(FRQCR); + int idx = ((frqcr & 0x4000) >> 12) | ((frqcr & 0x000c) >> 2); + + clk->rate = clk->parent->rate / ifc_divisors[idx]; +} + +static struct clk_ops sh7709_cpu_clk_ops = { + .init = set_bus_parent, + .recalc = cpu_clk_recalc, +}; + +static struct clk_ops *sh7709_clk_ops[] = { + &sh7709_master_clk_ops, + &sh7709_module_clk_ops, + &sh7709_bus_clk_ops, + &sh7709_cpu_clk_ops, +}; + +void __init arch_init_clk_ops(struct clk_ops **ops, int idx) +{ + if (idx < ARRAY_SIZE(sh7709_clk_ops)) + *ops = sh7709_clk_ops[idx]; +} diff --git a/arch/sh/kernel/cpu/sh4/Makefile b/arch/sh/kernel/cpu/sh4/Makefile index ead1071eac7..3d5cafc71ae 100644 --- a/arch/sh/kernel/cpu/sh4/Makefile +++ b/arch/sh/kernel/cpu/sh4/Makefile @@ -5,6 +5,15 @@ obj-y := ex.o probe.o obj-$(CONFIG_SH_FPU) += fpu.o -obj-$(CONFIG_CPU_SUBTYPE_ST40STB1) += irq_intc2.o obj-$(CONFIG_SH_STORE_QUEUES) += sq.o +# Primary on-chip clocks (common) +clock-$(CONFIG_CPU_SH4) := clock-sh4.o +clock-$(CONFIG_CPU_SUBTYPE_SH73180) := clock-sh73180.o +clock-$(CONFIG_CPU_SUBTYPE_SH7770) := clock-sh7770.o +clock-$(CONFIG_CPU_SUBTYPE_SH7780) := clock-sh7780.o + +# Additional clocks by subtype +clock-$(CONFIG_CPU_SUBTYPE_SH4_202) += clock-sh4-202.o + +obj-y += $(clock-y) diff --git a/arch/sh/kernel/cpu/sh4/clock-sh4-202.c b/arch/sh/kernel/cpu/sh4/clock-sh4-202.c new file mode 100644 index 00000000000..bfdf5fe8d94 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/clock-sh4-202.c @@ -0,0 +1,179 @@ +/* + * arch/sh/kernel/cpu/sh4/clock-sh4-202.c + * + * Additional SH4-202 support for the clock framework + * + * Copyright (C) 2005 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/kernel.h> +#include <linux/err.h> +#include <asm/clock.h> +#include <asm/freq.h> +#include <asm/io.h> + +#define CPG2_FRQCR3 0xfe0a0018 + +static int frqcr3_divisors[] = { 1, 2, 3, 4, 6, 8, 16 }; +static int frqcr3_values[] = { 0, 1, 2, 3, 4, 5, 6 }; + +static void emi_clk_recalc(struct clk *clk) +{ + int idx = ctrl_inl(CPG2_FRQCR3) & 0x0007; + clk->rate = clk->parent->rate / frqcr3_divisors[idx]; +} + +static inline int frqcr3_lookup(struct clk *clk, unsigned long rate) +{ + int divisor = clk->parent->rate / rate; + int i; + + for (i = 0; i < ARRAY_SIZE(frqcr3_divisors); i++) + if (frqcr3_divisors[i] == divisor) + return frqcr3_values[i]; + + /* Safe fallback */ + return 5; +} + +static struct clk_ops sh4202_emi_clk_ops = { + .recalc = emi_clk_recalc, +}; + +static struct clk sh4202_emi_clk = { + .name = "emi_clk", + .flags = CLK_ALWAYS_ENABLED, + .ops = &sh4202_emi_clk_ops, +}; + +static void femi_clk_recalc(struct clk *clk) +{ + int idx = (ctrl_inl(CPG2_FRQCR3) >> 3) & 0x0007; + clk->rate = clk->parent->rate / frqcr3_divisors[idx]; +} + +static struct clk_ops sh4202_femi_clk_ops = { + .recalc = femi_clk_recalc, +}; + +static struct clk sh4202_femi_clk = { + .name = "femi_clk", + .flags = CLK_ALWAYS_ENABLED, + .ops = &sh4202_femi_clk_ops, +}; + +static void shoc_clk_init(struct clk *clk) +{ + int i; + + /* + * For some reason, the shoc_clk seems to be set to some really + * insane value at boot (values outside of the allowable frequency + * range for instance). We deal with this by scaling it back down + * to something sensible just in case. + * + * Start scaling from the high end down until we find something + * that passes rate verification.. + */ + for (i = 0; i < ARRAY_SIZE(frqcr3_divisors); i++) { + int divisor = frqcr3_divisors[i]; + + if (clk->ops->set_rate(clk, clk->parent->rate / divisor) == 0) + break; + } + + WARN_ON(i == ARRAY_SIZE(frqcr3_divisors)); /* Undefined clock */ +} + +static void shoc_clk_recalc(struct clk *clk) +{ + int idx = (ctrl_inl(CPG2_FRQCR3) >> 6) & 0x0007; + clk->rate = clk->parent->rate / frqcr3_divisors[idx]; +} + +static int shoc_clk_verify_rate(struct clk *clk, unsigned long rate) +{ + struct clk *bclk = clk_get("bus_clk"); + unsigned long bclk_rate = clk_get_rate(bclk); + + clk_put(bclk); + + if (rate > bclk_rate) + return 1; + if (rate > 66000000) + return 1; + + return 0; +} + +static int shoc_clk_set_rate(struct clk *clk, unsigned long rate) +{ + unsigned long frqcr3; + unsigned int tmp; + + /* Make sure we have something sensible to switch to */ + if (shoc_clk_verify_rate(clk, rate) != 0) + return -EINVAL; + + tmp = frqcr3_lookup(clk, rate); + + frqcr3 = ctrl_inl(CPG2_FRQCR3); + frqcr3 &= ~(0x0007 << 6); + frqcr3 |= tmp << 6; + ctrl_outl(frqcr3, CPG2_FRQCR3); + + clk->rate = clk->parent->rate / frqcr3_divisors[tmp]; + + return 0; +} + +static struct clk_ops sh4202_shoc_clk_ops = { + .init = shoc_clk_init, + .recalc = shoc_clk_recalc, + .set_rate = shoc_clk_set_rate, +}; + +static struct clk sh4202_shoc_clk = { + .name = "shoc_clk", + .flags = CLK_ALWAYS_ENABLED, + .ops = &sh4202_shoc_clk_ops, +}; + +static struct clk *sh4202_onchip_clocks[] = { + &sh4202_emi_clk, + &sh4202_femi_clk, + &sh4202_shoc_clk, +}; + +static int __init sh4202_clk_init(void) +{ + struct clk *clk = clk_get("master_clk"); + int i; + + for (i = 0; i < ARRAY_SIZE(sh4202_onchip_clocks); i++) { + struct clk *clkp = sh4202_onchip_clocks[i]; + + clkp->parent = clk; + clk_register(clkp); + clk_enable(clkp); + } + + /* + * Now that we have the rest of the clocks registered, we need to + * force the parent clock to propagate so that these clocks will + * automatically figure out their rate. We cheat by handing the + * parent clock its current rate and forcing child propagation. + */ + clk_set_rate(clk, clk_get_rate(clk)); + + clk_put(clk); + + return 0; +} + +arch_initcall(sh4202_clk_init); + diff --git a/arch/sh/kernel/cpu/sh4/clock-sh4.c b/arch/sh/kernel/cpu/sh4/clock-sh4.c new file mode 100644 index 00000000000..dca9f87a12d --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/clock-sh4.c @@ -0,0 +1,80 @@ +/* + * arch/sh/kernel/cpu/sh4/clock-sh4.c + * + * Generic SH-4 support for the clock framework + * + * Copyright (C) 2005 Paul Mundt + * + * FRQCR parsing hacked out of arch/sh/kernel/time.c + * + * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka + * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> + * Copyright (C) 2002, 2003, 2004 Paul Mundt + * Copyright (C) 2002 M. R. Brown <mrbrown@linux-sh.org> + * + * 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/kernel.h> +#include <asm/clock.h> +#include <asm/fr |