sh: SH-5 clk fwk support.

Signed-off-by: Paul Mundt <lethal@linux-sh.org>
This commit is contained in:
Paul Mundt 2008-09-29 20:09:17 +09:00
parent 50b72e600b
commit 4d01cdafba
3 changed files with 91 additions and 161 deletions

View File

@ -5,3 +5,8 @@ obj-y := entry.o probe.o switchto.o
obj-$(CONFIG_SH_FPU) += fpu.o
obj-$(CONFIG_KALLSYMS) += unwind.o
# Primary on-chip clocks (common)
clock-$(CONFIG_CPU_SH5) := clock-sh5.o
obj-y += $(clock-y)

View File

@ -0,0 +1,79 @@
/*
* arch/sh/kernel/cpu/sh5/clock-sh5.c
*
* SH-5 support for the clock framework
*
* Copyright (C) 2008 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/io.h>
static int ifc_table[] = { 2, 4, 6, 8, 10, 12, 16, 24 };
/* Clock, Power and Reset Controller */
#define CPRC_BLOCK_OFF 0x01010000
#define CPRC_BASE (PHYS_PERIPHERAL_BLOCK + CPRC_BLOCK_OFF)
static unsigned long cprc_base;
static void master_clk_init(struct clk *clk)
{
int idx = (ctrl_inl(cprc_base + 0x00) >> 6) & 0x0007;
clk->rate *= ifc_table[idx];
}
static struct clk_ops sh5_master_clk_ops = {
.init = master_clk_init,
};
static void module_clk_recalc(struct clk *clk)
{
int idx = (ctrl_inw(cprc_base) >> 12) & 0x0007;
clk->rate = clk->parent->rate / ifc_table[idx];
}
static struct clk_ops sh5_module_clk_ops = {
.recalc = module_clk_recalc,
};
static void bus_clk_recalc(struct clk *clk)
{
int idx = (ctrl_inw(cprc_base) >> 3) & 0x0007;
clk->rate = clk->parent->rate / ifc_table[idx];
}
static struct clk_ops sh5_bus_clk_ops = {
.recalc = bus_clk_recalc,
};
static void cpu_clk_recalc(struct clk *clk)
{
int idx = (ctrl_inw(cprc_base) & 0x0007);
clk->rate = clk->parent->rate / ifc_table[idx];
}
static struct clk_ops sh5_cpu_clk_ops = {
.recalc = cpu_clk_recalc,
};
static struct clk_ops *sh5_clk_ops[] = {
&sh5_master_clk_ops,
&sh5_module_clk_ops,
&sh5_bus_clk_ops,
&sh5_cpu_clk_ops,
};
void __init arch_init_clk_ops(struct clk_ops **ops, int idx)
{
cprc_base = onchip_remap(CPRC_BASE, 1024, "CPRC");
BUG_ON(!cprc_base);
if (idx < ARRAY_SIZE(sh5_clk_ops))
*ops = sh5_clk_ops[idx];
}

View File

@ -39,6 +39,7 @@
#include <asm/processor.h>
#include <asm/uaccess.h>
#include <asm/delay.h>
#include <asm/clock.h>
#define TMU_TOCR_INIT 0x00
#define TMU0_TCR_INIT 0x0020
@ -51,14 +52,6 @@
#define RTC_RCR1_CIE 0x10 /* Carry Interrupt Enable */
#define RTC_RCR1 (rtc_base + 0x38)
/* Clock, Power and Reset Controller */
#define CPRC_BLOCK_OFF 0x01010000
#define CPRC_BASE PHYS_PERIPHERAL_BLOCK + CPRC_BLOCK_OFF
#define FRQCR (cprc_base+0x0)
#define WTCSR (cprc_base+0x0018)
#define STBCR (cprc_base+0x0030)
/* Time Management Unit */
#define TMU_BLOCK_OFF 0x01020000
#define TMU_BASE PHYS_PERIPHERAL_BLOCK + TMU_BLOCK_OFF
@ -293,103 +286,17 @@ static irqreturn_t timer_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
static __init unsigned int get_cpu_hz(void)
{
unsigned int count;
unsigned long __dummy;
unsigned long ctc_val_init, ctc_val;
/*
** Regardless the toolchain, force the compiler to use the
** arbitrary register r3 as a clock tick counter.
** NOTE: r3 must be in accordance with sh64_rtc_interrupt()
*/
register unsigned long long __rtc_irq_flag __asm__ ("r3");
local_irq_enable();
do {} while (ctrl_inb(rtc_base) != 0);
ctrl_outb(RTC_RCR1_CIE, RTC_RCR1); /* Enable carry interrupt */
/*
* r3 is arbitrary. CDC does not support "=z".
*/
ctc_val_init = 0xffffffff;
ctc_val = ctc_val_init;
asm volatile("gettr tr0, %1\n\t"
"putcon %0, " __CTC "\n\t"
"and %2, r63, %2\n\t"
"pta $+4, tr0\n\t"
"beq/l %2, r63, tr0\n\t"
"ptabs %1, tr0\n\t"
"getcon " __CTC ", %0\n\t"
: "=r"(ctc_val), "=r" (__dummy), "=r" (__rtc_irq_flag)
: "0" (0));
local_irq_disable();
/*
* SH-3:
* CPU clock = 4 stages * loop
* tst rm,rm if id ex
* bt/s 1b if id ex
* add #1,rd if id ex
* (if) pipe line stole
* tst rm,rm if id ex
* ....
*
*
* SH-4:
* CPU clock = 6 stages * loop
* I don't know why.
* ....
*
* SH-5:
* Use CTC register to count. This approach returns the right value
* even if the I-cache is disabled (e.g. whilst debugging.)
*
*/
count = ctc_val_init - ctc_val; /* CTC counts down */
/*
* This really is count by the number of clock cycles
* by the ratio between a complete R64CNT
* wrap-around (128) and CUI interrupt being raised (64).
*/
return count*2;
}
static irqreturn_t sh64_rtc_interrupt(int irq, void *dev_id)
{
struct pt_regs *regs = get_irq_regs();
ctrl_outb(0, RTC_RCR1); /* Disable Carry Interrupts */
regs->regs[3] = 1; /* Using r3 */
return IRQ_HANDLED;
}
static struct irqaction irq0 = {
.handler = timer_interrupt,
.flags = IRQF_DISABLED,
.mask = CPU_MASK_NONE,
.name = "timer",
};
static struct irqaction irq1 = {
.handler = sh64_rtc_interrupt,
.flags = IRQF_DISABLED,
.mask = CPU_MASK_NONE,
.name = "rtc",
};
void __init time_init(void)
{
unsigned int cpu_clock, master_clock, bus_clock, module_clock;
unsigned long interval;
unsigned long frqcr, ifc, pfc;
static int ifc_table[] = { 2, 4, 6, 8, 10, 12, 16, 24 };
#define bfc_table ifc_table /* Same */
#define pfc_table ifc_table /* Same */
struct clk *clk;
tmu_base = onchip_remap(TMU_BASE, 1024, "TMU");
if (!tmu_base) {
@ -401,50 +308,19 @@ void __init time_init(void)
panic("Unable to remap RTC\n");
}
cprc_base = onchip_remap(CPRC_BASE, 1024, "CPRC");
if (!cprc_base) {
panic("Unable to remap CPRC\n");
}
clk = clk_get(NULL, "cpu_clk");
scaled_recip_ctc_ticks_per_jiffy = ((1ULL << CTC_JIFFY_SCALE_SHIFT) /
(unsigned long long)(clk_get_rate(clk) / HZ));
rtc_sh_get_time(&xtime);
setup_irq(TIMER_IRQ, &irq0);
setup_irq(RTC_IRQ, &irq1);
/* Check how fast it is.. */
cpu_clock = get_cpu_hz();
/* Note careful order of operations to maintain reasonable precision and avoid overflow. */
scaled_recip_ctc_ticks_per_jiffy = ((1ULL << CTC_JIFFY_SCALE_SHIFT) / (unsigned long long)(cpu_clock / HZ));
free_irq(RTC_IRQ, NULL);
printk("CPU clock: %d.%02dMHz\n",
(cpu_clock / 1000000), (cpu_clock % 1000000)/10000);
{
unsigned short bfc;
frqcr = ctrl_inl(FRQCR);
ifc = ifc_table[(frqcr>> 6) & 0x0007];
bfc = bfc_table[(frqcr>> 3) & 0x0007];
pfc = pfc_table[(frqcr>> 12) & 0x0007];
master_clock = cpu_clock * ifc;
bus_clock = master_clock/bfc;
}
printk("Bus clock: %d.%02dMHz\n",
(bus_clock/1000000), (bus_clock % 1000000)/10000);
module_clock = master_clock/pfc;
printk("Module clock: %d.%02dMHz\n",
(module_clock/1000000), (module_clock % 1000000)/10000);
interval = (module_clock/(HZ*4));
clk = clk_get(NULL, "module_clk");
interval = (clk_get_rate(clk)/(HZ*4));
printk("Interval = %ld\n", interval);
current_cpu_data.cpu_clock = cpu_clock;
current_cpu_data.master_clock = master_clock;
current_cpu_data.bus_clock = bus_clock;
current_cpu_data.module_clock = module_clock;
/* Start TMU0 */
ctrl_outb(TMU_TSTR_OFF, TMU_TSTR);
ctrl_outb(TMU_TOCR_INIT, TMU_TOCR);
@ -454,36 +330,6 @@ void __init time_init(void)
ctrl_outb(TMU_TSTR_INIT, TMU_TSTR);
}
void enter_deep_standby(void)
{
/* Disable watchdog timer */
ctrl_outl(0xa5000000, WTCSR);
/* Configure deep standby on sleep */
ctrl_outl(0x03, STBCR);
#ifdef CONFIG_SH_ALPHANUMERIC
{
extern void mach_alphanum(int position, unsigned char value);
extern void mach_alphanum_brightness(int setting);
char halted[] = "Halted. ";
int i;
mach_alphanum_brightness(6); /* dimmest setting above off */
for (i=0; i<8; i++) {
mach_alphanum(i, halted[i]);
}
asm __volatile__ ("synco");
}
#endif
asm __volatile__ ("sleep");
asm __volatile__ ("synci");
asm __volatile__ ("nop");
asm __volatile__ ("nop");
asm __volatile__ ("nop");
asm __volatile__ ("nop");
panic("Unexpected wakeup!\n");
}
static struct resource rtc_resources[] = {
[0] = {
/* RTC base, filled in by rtc_init */