sh: SH-5 clk fwk support.
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
This commit is contained in:
parent
50b72e600b
commit
4d01cdafba
@ -5,3 +5,8 @@ obj-y := entry.o probe.o switchto.o
|
|||||||
|
|
||||||
obj-$(CONFIG_SH_FPU) += fpu.o
|
obj-$(CONFIG_SH_FPU) += fpu.o
|
||||||
obj-$(CONFIG_KALLSYMS) += unwind.o
|
obj-$(CONFIG_KALLSYMS) += unwind.o
|
||||||
|
|
||||||
|
# Primary on-chip clocks (common)
|
||||||
|
clock-$(CONFIG_CPU_SH5) := clock-sh5.o
|
||||||
|
|
||||||
|
obj-y += $(clock-y)
|
||||||
|
79
arch/sh/kernel/cpu/sh5/clock-sh5.c
Normal file
79
arch/sh/kernel/cpu/sh5/clock-sh5.c
Normal 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];
|
||||||
|
}
|
@ -39,6 +39,7 @@
|
|||||||
#include <asm/processor.h>
|
#include <asm/processor.h>
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
#include <asm/delay.h>
|
#include <asm/delay.h>
|
||||||
|
#include <asm/clock.h>
|
||||||
|
|
||||||
#define TMU_TOCR_INIT 0x00
|
#define TMU_TOCR_INIT 0x00
|
||||||
#define TMU0_TCR_INIT 0x0020
|
#define TMU0_TCR_INIT 0x0020
|
||||||
@ -51,14 +52,6 @@
|
|||||||
#define RTC_RCR1_CIE 0x10 /* Carry Interrupt Enable */
|
#define RTC_RCR1_CIE 0x10 /* Carry Interrupt Enable */
|
||||||
#define RTC_RCR1 (rtc_base + 0x38)
|
#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 */
|
/* Time Management Unit */
|
||||||
#define TMU_BLOCK_OFF 0x01020000
|
#define TMU_BLOCK_OFF 0x01020000
|
||||||
#define TMU_BASE PHYS_PERIPHERAL_BLOCK + TMU_BLOCK_OFF
|
#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;
|
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 = {
|
static struct irqaction irq0 = {
|
||||||
.handler = timer_interrupt,
|
.handler = timer_interrupt,
|
||||||
.flags = IRQF_DISABLED,
|
.flags = IRQF_DISABLED,
|
||||||
.mask = CPU_MASK_NONE,
|
.mask = CPU_MASK_NONE,
|
||||||
.name = "timer",
|
.name = "timer",
|
||||||
};
|
};
|
||||||
static struct irqaction irq1 = {
|
|
||||||
.handler = sh64_rtc_interrupt,
|
|
||||||
.flags = IRQF_DISABLED,
|
|
||||||
.mask = CPU_MASK_NONE,
|
|
||||||
.name = "rtc",
|
|
||||||
};
|
|
||||||
|
|
||||||
void __init time_init(void)
|
void __init time_init(void)
|
||||||
{
|
{
|
||||||
unsigned int cpu_clock, master_clock, bus_clock, module_clock;
|
|
||||||
unsigned long interval;
|
unsigned long interval;
|
||||||
unsigned long frqcr, ifc, pfc;
|
struct clk *clk;
|
||||||
static int ifc_table[] = { 2, 4, 6, 8, 10, 12, 16, 24 };
|
|
||||||
#define bfc_table ifc_table /* Same */
|
|
||||||
#define pfc_table ifc_table /* Same */
|
|
||||||
|
|
||||||
tmu_base = onchip_remap(TMU_BASE, 1024, "TMU");
|
tmu_base = onchip_remap(TMU_BASE, 1024, "TMU");
|
||||||
if (!tmu_base) {
|
if (!tmu_base) {
|
||||||
@ -401,50 +308,19 @@ void __init time_init(void)
|
|||||||
panic("Unable to remap RTC\n");
|
panic("Unable to remap RTC\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
cprc_base = onchip_remap(CPRC_BASE, 1024, "CPRC");
|
clk = clk_get(NULL, "cpu_clk");
|
||||||
if (!cprc_base) {
|
scaled_recip_ctc_ticks_per_jiffy = ((1ULL << CTC_JIFFY_SCALE_SHIFT) /
|
||||||
panic("Unable to remap CPRC\n");
|
(unsigned long long)(clk_get_rate(clk) / HZ));
|
||||||
}
|
|
||||||
|
|
||||||
rtc_sh_get_time(&xtime);
|
rtc_sh_get_time(&xtime);
|
||||||
|
|
||||||
setup_irq(TIMER_IRQ, &irq0);
|
setup_irq(TIMER_IRQ, &irq0);
|
||||||
setup_irq(RTC_IRQ, &irq1);
|
|
||||||
|
|
||||||
/* Check how fast it is.. */
|
clk = clk_get(NULL, "module_clk");
|
||||||
cpu_clock = get_cpu_hz();
|
interval = (clk_get_rate(clk)/(HZ*4));
|
||||||
|
|
||||||
/* 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));
|
|
||||||
|
|
||||||
printk("Interval = %ld\n", interval);
|
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 */
|
/* Start TMU0 */
|
||||||
ctrl_outb(TMU_TSTR_OFF, TMU_TSTR);
|
ctrl_outb(TMU_TSTR_OFF, TMU_TSTR);
|
||||||
ctrl_outb(TMU_TOCR_INIT, TMU_TOCR);
|
ctrl_outb(TMU_TOCR_INIT, TMU_TOCR);
|
||||||
@ -454,36 +330,6 @@ void __init time_init(void)
|
|||||||
ctrl_outb(TMU_TSTR_INIT, TMU_TSTR);
|
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[] = {
|
static struct resource rtc_resources[] = {
|
||||||
[0] = {
|
[0] = {
|
||||||
/* RTC base, filled in by rtc_init */
|
/* RTC base, filled in by rtc_init */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user