Commit 36ddf31b authored by Paul Mundt's avatar Paul Mundt Committed by Linus Torvalds

[PATCH] sh: Simplistic clock framework

This adds a relatively simplistic clock framework for sh.  The initial goal
behind this is to clean up the arch/sh/kernel/time.c mess and to get the CPU
subtype-specific frequency setting and calculation code moved somewhere more
sensible.

This only deals with the core clocks at the moment, though it's trivial for
other drivers to define their own clocks as desired.
Signed-off-by: default avatarPaul Mundt <lethal@linux-sh.org>
Cc: john stultz <johnstul@us.ibm.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent b66c1a39
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
# Makefile for the STMicroelectronics Overdrive specific parts of the kernel # 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 obj-$(CONFIG_PCI) += fpga.o galileo.o pcidma.o
...@@ -17,8 +17,6 @@ ...@@ -17,8 +17,6 @@
#include <asm/overdrive/overdrive.h> #include <asm/overdrive/overdrive.h>
#include <asm/overdrive/fpga.h> #include <asm/overdrive/fpga.h>
extern void od_time_init(void);
const char *get_system_type(void) const char *get_system_type(void)
{ {
return "SH7750 Overdrive"; return "SH7750 Overdrive";
...@@ -31,11 +29,9 @@ int __init platform_setup(void) ...@@ -31,11 +29,9 @@ int __init platform_setup(void)
{ {
#ifdef CONFIG_PCI #ifdef CONFIG_PCI
init_overdrive_fpga(); init_overdrive_fpga();
galileo_init(); galileo_init();
#endif #endif
board_time_init = od_time_init;
/* Enable RS232 receive buffers */ /* Enable RS232 receive buffers */
writel(0x1e, OVERDRIVE_CTRL); writel(0x1e, OVERDRIVE_CTRL);
} }
/*
* 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;
}
/*
* 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);
...@@ -4,3 +4,10 @@ ...@@ -4,3 +4,10 @@
obj-y := ex.o probe.o 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)
/*
* 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];
}
/*
* 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];
}
/*
* 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];
}
/*
* 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];
}
...@@ -5,6 +5,15 @@ ...@@ -5,6 +5,15 @@
obj-y := ex.o probe.o obj-y := ex.o probe.o
obj-$(CONFIG_SH_FPU) += fpu.o obj-$(CONFIG_SH_FPU) += fpu.o
obj-$(CONFIG_CPU_SUBTYPE_ST40STB1) += irq_intc2.o
obj-$(CONFIG_SH_STORE_QUEUES) += sq.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)
/*
* 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);
/*
* 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/freq.h>
#include <asm/io.h>
static int ifc_divisors[] = { 1, 2, 3, 4, 6, 8, 1, 1 };
#define bfc_divisors ifc_divisors /* Same */
static int pfc_divisors[] = { 2, 3, 4, 6, 8, 2, 2, 2 };
static void master_clk_init(struct clk *clk)
{
clk->rate *= pfc_divisors[ctrl_inw(FRQCR) & 0x0007];
}
static struct clk_ops sh4_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 / pfc_divisors[idx];
}
static struct clk_ops sh4_module_clk_ops = {
.recalc = module_clk_recalc,
};
static void bus_clk_recalc(struct clk *clk)
{
int idx = (ctrl_inw(FRQCR) >> 3) & 0x0007;
clk->rate = clk->parent->rate / bfc_divisors[idx];
}
static struct clk_ops sh4_bus_clk_ops = {
.recalc = bus_clk_recalc,
};
static void cpu_clk_recalc(struct clk *clk)
{
int idx = (ctrl_inw(FRQCR) >> 6) & 0x0007;
clk->rate = clk->parent->rate / ifc_divisors[idx];
}
static struct clk_ops sh4_cpu_clk_ops = {
.recalc = cpu_clk_recalc,
};
static struct clk_ops *sh4_clk_ops[] = {
&sh4_master_clk_ops,
&sh4_module_clk_ops,
&sh4_bus_clk_ops,
&sh4_cpu_clk_ops,
};
void __init arch_init_clk_ops(struct clk_ops **ops, int idx)
{
if (idx < ARRAY_SIZE(sh4_clk_ops))
*ops = sh4_clk_ops[idx];
}
/*
* arch/sh/kernel/cpu/sh4/clock-sh73180.c
*
* SH73180 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>
/*
* SH73180 uses a common set of divisors, so this is quite simple..
*/
static int divisors[] = { 1, 2, 3, 4, 6, 8, 12, 16 };
static void master_clk_init(struct clk *clk)
{
clk->rate *= divisors[ctrl_inl(FRQCR) & 0x0007];
}
static struct clk_ops sh73180_master_clk_ops = {
.init = master_clk_init,
};
static void module_clk_recalc(struct clk *clk)
{
int idx = (ctrl_inl(FRQCR) & 0x0007);
clk->rate = clk->parent->rate / divisors[idx];
}
static struct clk_ops sh73180_module_clk_ops = {
.recalc = module_clk_recalc,
};
static void bus_clk_recalc(struct clk *clk)
{
int idx = (ctrl_inl(FRQCR) >> 12) & 0x0007;
clk->rate = clk->parent->rate / divisors[idx];
}
static struct clk_ops sh73180_bus_clk_ops = {
.recalc = bus_clk_recalc,
};
static void cpu_clk_recalc(struct clk *clk)
{
int idx = (ctrl_inl(FRQCR) >> 20) & 0x0007;
clk->rate = clk->parent->rate / divisors[idx];
}
static struct clk_ops sh73180_cpu_clk_ops = {
.recalc = cpu_clk_recalc,
};
static struct clk_ops *sh73180_clk_ops[] = {
&sh73180_master_clk_ops,
&sh73180_module_clk_ops,
&sh73180_bus_clk_ops,
&sh73180_cpu_clk_ops,
};
void __init arch_init_clk_ops(struct clk_ops **ops, int idx)
{
if (idx < ARRAY_SIZE(sh73180_clk_ops))
*ops = sh73180_clk_ops[idx];
}
/*
* arch/sh/kernel/cpu/sh4/clock-sh7770.c
*
* SH7770 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 <asm/clock.h>
#include <asm/freq.h>
#include <asm/io.h>
static int ifc_divisors[] = { 1, 1, 1, 1, 1, 1, 1, 1 };
static int bfc_divisors[] = { 1, 1, 1, 1, 1, 8,12, 1 };
static int pfc_divisors[] = { 1, 8, 1,10,12,16, 1, 1 };
static void master_clk_init(struct clk *clk)
{
clk->rate *= pfc_divisors[(ctrl_inl(FRQCR) >> 28) & 0x000f];
}
static struct clk_ops sh7770_master_clk_ops = {
.init = master_clk_init,
};
static void module_clk_recalc(struct clk *clk)
{
int idx = ((ctrl_inl(FRQCR) >> 28) & 0x000f);
clk->rate = clk->parent->rate / pfc_divisors[idx];
}
static struct clk_ops sh7770_module_clk_ops = {
.recalc = module_clk_recalc,
};
static void bus_clk_recalc(struct clk *clk)
{
int idx = (ctrl_inl(FRQCR) & 0x000f);
clk->rate = clk->parent->rate / bfc_divisors[idx];
}
static struct clk_ops sh7770_bus_clk_ops = {
.recalc = bus_clk_recalc,
};
static void cpu_clk_recalc(struct clk *clk)
{
int idx = ((ctrl_inl(FRQCR) >> 24) & 0x000f);
clk->rate = clk->parent->rate / ifc_divisors[idx];
}
static struct clk_ops sh7770_cpu_clk_ops = {
.recalc = cpu_clk_recalc,
};
static struct clk_ops *sh7770_clk_ops[] = {
&sh7770_master_clk_ops,
&sh7770_module_clk_ops,
&sh7770_bus_clk_ops,
&sh7770_cpu_clk_ops,
};
void __init arch_init_clk_ops(struct clk_ops **ops, int idx)
{
if (idx < ARRAY_SIZE(sh7770_clk_ops))
*ops = sh7770_clk_ops[idx];
}
/*
* arch/sh/kernel/cpu/sh4/clock-sh7780.c
*
* SH7780 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 <asm/clock.h>
#include <asm/freq.h>
#include <asm/io.h>
static int ifc_divisors[] = { 2, 4 };
static int bfc_divisors[] = { 1, 1, 1, 8, 12, 16, 24, 1 };
static int pfc_divisors[] = { 1, 24, 24, 1 };
static int cfc_divisors[] = { 1, 1, 4, 1, 6, 1, 1, 1 };
static void master_clk_init(struct clk *clk)
{
clk->rate *= pfc_divisors[ctrl_inl(FRQCR) & 0x0003];
}
static struct clk_ops sh7780_master_clk_ops = {
.init = master_clk_init,
};
static void module_clk_recalc(struct clk *clk)
{
int idx = (ctrl_inl(FRQCR) & 0x0003);
clk->rate = clk->parent->rate / pfc_divisors[idx];
}
static struct clk_ops sh7780_module_clk_ops = {
.recalc = module_clk_recalc,
};
static void bus_clk_recalc(struct clk *clk)
{
int idx = ((ctrl_inl(FRQCR) >> 16) & 0x0007);
clk->rate = clk->parent->rate / bfc_divisors[idx];
}
static struct clk_ops sh7780_bus_clk_ops = {
.recalc = bus_clk_recalc,
};
static void cpu_clk_recalc(struct clk *clk)
{
int idx = ((ctrl_inl(FRQCR) >> 24) & 0x0001);
clk->rate = clk->parent->rate / ifc_divisors[idx];
}
static struct clk_ops sh7780_cpu_clk_ops = {
.recalc = cpu_clk_recalc,
};
static struct clk_ops *sh7780_clk_ops[] = {
&sh7780_master_clk_ops,
&sh7780_module_clk_ops,
&sh7780_bus_clk_ops,
&sh7780_cpu_clk_ops,
};
void __init arch_init_clk_ops(struct clk_ops **ops, int idx)
{
if (idx < ARRAY_SIZE(sh7780_clk_ops))
*ops = sh7780_clk_ops[idx];
}
static void shyway_clk_recalc(struct clk *clk)
{
int idx = ((ctrl_inl(FRQCR) >> 20) & 0x0007);
clk->rate = clk->parent->rate / cfc_divisors[idx];
}
static struct clk_ops sh7780_shyway_clk_ops = {
.recalc = shyway_clk_recalc,
};
static struct clk sh7780_shyway_clk = {
.name = "shyway_clk",
.flags = CLK_ALWAYS_ENABLED,
.ops = &sh7780_shyway_clk_ops,
};
/*
* Additional SH7780-specific on-chip clocks that aren't already part of the
* clock framework
*/
static struct clk *sh7780_onchip_clocks[] = {
&sh7780_shyway_clk,
};
static int __init sh7780_clk_init(void)
{
struct clk *clk = clk_get("master_clk");
int i;
for (i = 0; i < ARRAY_SIZE(sh7780_onchip_clocks); i++) {
struct clk *clkp = sh7780_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(sh7780_clk_init);
This diff is collapsed.
#ifndef __ASM_SH_CLOCK_H
#define __ASM_SH_CLOCK_H
#include <linux/kref.h>
#include <linux/list.h>
#include <linux/seq_file.h>
struct clk;
struct clk_ops {
void (*init)(struct clk *clk);
void (*enable)(struct clk *clk);
void (*disable)(struct clk *clk);
void (*recalc)(struct clk *clk);
int (*set_rate)(struct clk *clk, unsigned long rate);
};
struct clk {
struct list_head node;
const char *name;
struct module *owner;
struct clk *parent;
struct clk_ops *ops;
struct kref kref;
unsigned long rate;
unsigned long flags;
};
#define CLK_ALWAYS_ENABLED (1 << 0)
#define CLK_RATE_PROPAGATES (1 << 1)
/* Should be defined by processor-specific code */
void arch_init_clk_ops(struct clk_ops **, int type);
/* arch/sh/kernel/cpu/clock.c */
int clk_init(void);
int __clk_enable(struct clk *);
int clk_enable(struct clk *);
void __clk_disable(struct clk *);
void clk_disable(struct clk *);
int clk_set_rate(struct clk *, unsigned long rate);
unsigned long clk_get_rate(struct clk *);
void clk_recalc_rate(struct clk *);
struct clk *clk_get(const char *id);
void clk_put(struct clk *);
int clk_register(struct clk *);
void clk_unregister(struct clk *);
int show_clocks(struct seq_file *m);
#endif /* __ASM_SH_CLOCK_H */
...@@ -12,6 +12,8 @@ ...@@ -12,6 +12,8 @@
#if defined(CONFIG_CPU_SUBTYPE_SH73180) #if defined(CONFIG_CPU_SUBTYPE_SH73180)
#define FRQCR 0xa4150000 #define FRQCR 0xa4150000
#elif defined(CONFIG_CPU_SUBTYPE_SH7780)
#define FRQCR 0xffc80000
#else #else
#define FRQCR 0xffc00000 #define FRQCR 0xffc00000
#endif #endif
......
...@@ -14,16 +14,5 @@ ...@@ -14,16 +14,5 @@
#include <asm/cpu/freq.h> #include <asm/cpu/freq.h>
/* arch/sh/kernel/time.c */
extern void get_current_frequency_divisors(unsigned int *ifc, unsigned int *pfc, unsigned int *bfc);
extern unsigned int get_ifc_divisor(unsigned int value);
extern unsigned int get_ifc_divisor(unsigned int value);
extern unsigned int get_ifc_divisor(unsigned int value);
extern unsigned int get_ifc_value(unsigned int divisor);
extern unsigned int get_pfc_value(unsigned int divisor);
extern unsigned int get_bfc_value(unsigned int divisor);
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
#endif /* __ASM_SH_FREQ_H */ #endif /* __ASM_SH_FREQ_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment