diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 01:02:30 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 01:02:30 +0000 |
commit | 76cb841cb886eef6b3bee341a2266c76578724ad (patch) | |
tree | f5892e5ba6cc11949952a6ce4ecbe6d516d6ce58 /arch/sh/kernel/cpu | |
parent | Initial commit. (diff) | |
download | linux-76cb841cb886eef6b3bee341a2266c76578724ad.tar.xz linux-76cb841cb886eef6b3bee341a2266c76578724ad.zip |
Adding upstream version 4.19.249.upstream/4.19.249
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'arch/sh/kernel/cpu')
122 files changed, 28131 insertions, 0 deletions
diff --git a/arch/sh/kernel/cpu/Makefile b/arch/sh/kernel/cpu/Makefile new file mode 100644 index 000000000..f7c22ea98 --- /dev/null +++ b/arch/sh/kernel/cpu/Makefile @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the Linux/SuperH CPU-specific backends. +# + +obj-$(CONFIG_CPU_SH2) = sh2/ +obj-$(CONFIG_CPU_SH2A) = sh2a/ +obj-$(CONFIG_CPU_SH3) = sh3/ +obj-$(CONFIG_CPU_SH4) = sh4/ +obj-$(CONFIG_CPU_SH5) = sh5/ + +# Special cases for family ancestry. + +obj-$(CONFIG_CPU_SH4A) += sh4a/ +obj-$(CONFIG_ARCH_SHMOBILE) += shmobile/ + +# Common interfaces. + +obj-$(CONFIG_SH_ADC) += adc.o +obj-$(CONFIG_SH_CLK_CPG_LEGACY) += clock-cpg.o + +obj-y += irq/ init.o clock.o fpu.o pfc.o proc.o diff --git a/arch/sh/kernel/cpu/adc.c b/arch/sh/kernel/cpu/adc.c new file mode 100644 index 000000000..509136715 --- /dev/null +++ b/arch/sh/kernel/cpu/adc.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/arch/sh/kernel/adc.c -- SH3 on-chip ADC support + * + * Copyright (C) 2004 Andriy Skulysh <askulysh@image.kiev.ua> + */ + +#include <linux/module.h> +#include <asm/adc.h> +#include <asm/io.h> + + +int adc_single(unsigned int channel) +{ + int off; + unsigned char csr; + + if (channel >= 8) return -1; + + off = (channel & 0x03) << 2; + + csr = __raw_readb(ADCSR); + csr = channel | ADCSR_ADST | ADCSR_CKS; + __raw_writeb(csr, ADCSR); + + do { + csr = __raw_readb(ADCSR); + } while ((csr & ADCSR_ADF) == 0); + + csr &= ~(ADCSR_ADF | ADCSR_ADST); + __raw_writeb(csr, ADCSR); + + return (((__raw_readb(ADDRAH + off) << 8) | + __raw_readb(ADDRAL + off)) >> 6); +} + +EXPORT_SYMBOL(adc_single); diff --git a/arch/sh/kernel/cpu/clock-cpg.c b/arch/sh/kernel/cpu/clock-cpg.c new file mode 100644 index 000000000..5b75a384c --- /dev/null +++ b/arch/sh/kernel/cpu/clock-cpg.c @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/clk.h> +#include <linux/compiler.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/clkdev.h> +#include <asm/clock.h> + +static struct clk master_clk = { + .flags = CLK_ENABLE_ON_INIT, + .rate = CONFIG_SH_PCLK_FREQ, +}; + +static struct clk peripheral_clk = { + .parent = &master_clk, + .flags = CLK_ENABLE_ON_INIT, +}; + +static struct clk bus_clk = { + .parent = &master_clk, + .flags = CLK_ENABLE_ON_INIT, +}; + +static struct clk 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, +}; + +static struct clk_lookup lookups[] = { + /* main clocks */ + CLKDEV_CON_ID("master_clk", &master_clk), + CLKDEV_CON_ID("peripheral_clk", &peripheral_clk), + CLKDEV_CON_ID("bus_clk", &bus_clk), + CLKDEV_CON_ID("cpu_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); + } + + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); + + clk_add_alias("fck", "sh-tmu-sh3.0", "peripheral_clk", NULL); + clk_add_alias("fck", "sh-tmu.0", "peripheral_clk", NULL); + clk_add_alias("fck", "sh-tmu.1", "peripheral_clk", NULL); + clk_add_alias("fck", "sh-tmu.2", "peripheral_clk", NULL); + clk_add_alias("fck", "sh-mtu2", "peripheral_clk", NULL); + clk_add_alias("fck", "sh-cmt-16.0", "peripheral_clk", NULL); + clk_add_alias("fck", "sh-cmt-32.0", "peripheral_clk", NULL); + + return ret; +} + +/* + * Placeholder for compatibility, until the lazy CPUs do this + * on their own. + */ +int __init __weak arch_clk_init(void) +{ + return cpg_clk_init(); +} diff --git a/arch/sh/kernel/cpu/clock.c b/arch/sh/kernel/cpu/clock.c new file mode 100644 index 000000000..fca9b1e78 --- /dev/null +++ b/arch/sh/kernel/cpu/clock.c @@ -0,0 +1,55 @@ +/* + * arch/sh/kernel/cpu/clock.c - SuperH clock framework + * + * Copyright (C) 2005 - 2009 Paul Mundt + * + * This clock framework is derived from the OMAP version by: + * + * 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> + * + * 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/clk.h> +#include <asm/clock.h> +#include <asm/machvec.h> + +int __init clk_init(void) +{ + int ret; + +#ifndef CONFIG_COMMON_CLK + ret = arch_clk_init(); + if (unlikely(ret)) { + pr_err("%s: CPU clock registration failed.\n", __func__); + return ret; + } +#endif + + if (sh_mv.mv_clk_init) { + ret = sh_mv.mv_clk_init(); + if (unlikely(ret)) { + pr_err("%s: machvec clock initialization failed.\n", + __func__); + return ret; + } + } + +#ifndef CONFIG_COMMON_CLK + /* Kick the child clocks.. */ + recalculate_root_clocks(); + + /* Enable the necessary init clocks */ + clk_enable_init_clocks(); +#endif + + return ret; +} + + diff --git a/arch/sh/kernel/cpu/fpu.c b/arch/sh/kernel/cpu/fpu.c new file mode 100644 index 000000000..ae354a293 --- /dev/null +++ b/arch/sh/kernel/cpu/fpu.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/sched/signal.h> +#include <linux/sched/task.h> +#include <linux/sched/task_stack.h> +#include <linux/slab.h> +#include <asm/processor.h> +#include <asm/fpu.h> +#include <asm/traps.h> +#include <asm/ptrace.h> + +int init_fpu(struct task_struct *tsk) +{ + if (tsk_used_math(tsk)) { + if ((boot_cpu_data.flags & CPU_HAS_FPU) && tsk == current) + unlazy_fpu(tsk, task_pt_regs(tsk)); + return 0; + } + + /* + * Memory allocation at the first usage of the FPU and other state. + */ + if (!tsk->thread.xstate) { + tsk->thread.xstate = kmem_cache_alloc(task_xstate_cachep, + GFP_KERNEL); + if (!tsk->thread.xstate) + return -ENOMEM; + } + + if (boot_cpu_data.flags & CPU_HAS_FPU) { + struct sh_fpu_hard_struct *fp = &tsk->thread.xstate->hardfpu; + memset(fp, 0, xstate_size); + fp->fpscr = FPSCR_INIT; + } else { + struct sh_fpu_soft_struct *fp = &tsk->thread.xstate->softfpu; + memset(fp, 0, xstate_size); + fp->fpscr = FPSCR_INIT; + } + + set_stopped_child_used_math(tsk); + return 0; +} + +#ifdef CONFIG_SH_FPU +void __fpu_state_restore(void) +{ + struct task_struct *tsk = current; + + restore_fpu(tsk); + + task_thread_info(tsk)->status |= TS_USEDFPU; + tsk->thread.fpu_counter++; +} + +void fpu_state_restore(struct pt_regs *regs) +{ + struct task_struct *tsk = current; + + if (unlikely(!user_mode(regs))) { + printk(KERN_ERR "BUG: FPU is used in kernel mode.\n"); + BUG(); + return; + } + + if (!tsk_used_math(tsk)) { + local_irq_enable(); + /* + * does a slab alloc which can sleep + */ + if (init_fpu(tsk)) { + /* + * ran out of memory! + */ + do_group_exit(SIGKILL); + return; + } + local_irq_disable(); + } + + grab_fpu(regs); + + __fpu_state_restore(); +} + +BUILD_TRAP_HANDLER(fpu_state_restore) +{ + TRAP_HANDLER_DECL; + + fpu_state_restore(regs); +} +#endif /* CONFIG_SH_FPU */ diff --git a/arch/sh/kernel/cpu/init.c b/arch/sh/kernel/cpu/init.c new file mode 100644 index 000000000..c4f01c5c8 --- /dev/null +++ b/arch/sh/kernel/cpu/init.c @@ -0,0 +1,369 @@ +/* + * arch/sh/kernel/cpu/init.c + * + * CPU init code + * + * Copyright (C) 2002 - 2009 Paul Mundt + * Copyright (C) 2003 Richard Curnow + * + * 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/mm.h> +#include <linux/log2.h> +#include <asm/mmu_context.h> +#include <asm/processor.h> +#include <linux/uaccess.h> +#include <asm/page.h> +#include <asm/cacheflush.h> +#include <asm/cache.h> +#include <asm/elf.h> +#include <asm/io.h> +#include <asm/smp.h> +#include <asm/sh_bios.h> +#include <asm/setup.h> + +#ifdef CONFIG_SH_FPU +#define cpu_has_fpu 1 +#else +#define cpu_has_fpu 0 +#endif + +#ifdef CONFIG_SH_DSP +#define cpu_has_dsp 1 +#else +#define cpu_has_dsp 0 +#endif + +/* + * Generic wrapper for command line arguments to disable on-chip + * peripherals (nofpu, nodsp, and so forth). + */ +#define onchip_setup(x) \ +static int x##_disabled = !cpu_has_##x; \ + \ +static int x##_setup(char *opts) \ +{ \ + x##_disabled = 1; \ + return 1; \ +} \ +__setup("no" __stringify(x), x##_setup); + +onchip_setup(fpu); +onchip_setup(dsp); + +#ifdef CONFIG_SPECULATIVE_EXECUTION +#define CPUOPM 0xff2f0000 +#define CPUOPM_RABD (1 << 5) + +static void speculative_execution_init(void) +{ + /* Clear RABD */ + __raw_writel(__raw_readl(CPUOPM) & ~CPUOPM_RABD, CPUOPM); + + /* Flush the update */ + (void)__raw_readl(CPUOPM); + ctrl_barrier(); +} +#else +#define speculative_execution_init() do { } while (0) +#endif + +#ifdef CONFIG_CPU_SH4A +#define EXPMASK 0xff2f0004 +#define EXPMASK_RTEDS (1 << 0) +#define EXPMASK_BRDSSLP (1 << 1) +#define EXPMASK_MMCAW (1 << 4) + +static void expmask_init(void) +{ + unsigned long expmask = __raw_readl(EXPMASK); + + /* + * Future proofing. + * + * Disable support for slottable sleep instruction, non-nop + * instructions in the rte delay slot, and associative writes to + * the memory-mapped cache array. + */ + expmask &= ~(EXPMASK_RTEDS | EXPMASK_BRDSSLP | EXPMASK_MMCAW); + + __raw_writel(expmask, EXPMASK); + ctrl_barrier(); +} +#else +#define expmask_init() do { } while (0) +#endif + +/* 2nd-level cache init */ +void __attribute__ ((weak)) l2_cache_init(void) +{ +} + +/* + * Generic first-level cache init + */ +#if defined(CONFIG_SUPERH32) && !defined(CONFIG_CPU_J2) +static void cache_init(void) +{ + unsigned long ccr, flags; + + jump_to_uncached(); + ccr = __raw_readl(SH_CCR); + + /* + * At this point we don't know whether the cache is enabled or not - a + * bootloader may have enabled it. There are at least 2 things that + * could be dirty in the cache at this point: + * 1. kernel command line set up by boot loader + * 2. spilled registers from the prolog of this function + * => before re-initialising the cache, we must do a purge of the whole + * cache out to memory for safety. As long as nothing is spilled + * during the loop to lines that have already been done, this is safe. + * - RPC + */ + if (ccr & CCR_CACHE_ENABLE) { + unsigned long ways, waysize, addrstart; + + waysize = current_cpu_data.dcache.sets; + +#ifdef CCR_CACHE_ORA + /* + * If the OC is already in RAM mode, we only have + * half of the entries to flush.. + */ + if (ccr & CCR_CACHE_ORA) + waysize >>= 1; +#endif + + waysize <<= current_cpu_data.dcache.entry_shift; + +#ifdef CCR_CACHE_EMODE + /* If EMODE is not set, we only have 1 way to flush. */ + if (!(ccr & CCR_CACHE_EMODE)) + ways = 1; + else +#endif + ways = current_cpu_data.dcache.ways; + + addrstart = CACHE_OC_ADDRESS_ARRAY; + do { + unsigned long addr; + + for (addr = addrstart; + addr < addrstart + waysize; + addr += current_cpu_data.dcache.linesz) + __raw_writel(0, addr); + + addrstart += current_cpu_data.dcache.way_incr; + } while (--ways); + } + + /* + * Default CCR values .. enable the caches + * and invalidate them immediately.. + */ + flags = CCR_CACHE_ENABLE | CCR_CACHE_INVALIDATE; + +#ifdef CCR_CACHE_EMODE + /* Force EMODE if possible */ + if (current_cpu_data.dcache.ways > 1) + flags |= CCR_CACHE_EMODE; + else + flags &= ~CCR_CACHE_EMODE; +#endif + +#if defined(CONFIG_CACHE_WRITETHROUGH) + /* Write-through */ + flags |= CCR_CACHE_WT; +#elif defined(CONFIG_CACHE_WRITEBACK) + /* Write-back */ + flags |= CCR_CACHE_CB; +#else + /* Off */ + flags &= ~CCR_CACHE_ENABLE; +#endif + + l2_cache_init(); + + __raw_writel(flags, SH_CCR); + back_to_cached(); +} +#else +#define cache_init() do { } while (0) +#endif + +#define CSHAPE(totalsize, linesize, assoc) \ + ((totalsize & ~0xff) | (linesize << 4) | assoc) + +#define CACHE_DESC_SHAPE(desc) \ + CSHAPE((desc).way_size * (desc).ways, ilog2((desc).linesz), (desc).ways) + +static void detect_cache_shape(void) +{ + l1d_cache_shape = CACHE_DESC_SHAPE(current_cpu_data.dcache); + + if (current_cpu_data.dcache.flags & SH_CACHE_COMBINED) + l1i_cache_shape = l1d_cache_shape; + else + l1i_cache_shape = CACHE_DESC_SHAPE(current_cpu_data.icache); + + if (current_cpu_data.flags & CPU_HAS_L2_CACHE) + l2_cache_shape = CACHE_DESC_SHAPE(current_cpu_data.scache); + else + l2_cache_shape = -1; /* No S-cache */ +} + +static void fpu_init(void) +{ + /* Disable the FPU */ + if (fpu_disabled && (current_cpu_data.flags & CPU_HAS_FPU)) { + printk("FPU Disabled\n"); + current_cpu_data.flags &= ~CPU_HAS_FPU; + } + + disable_fpu(); + clear_used_math(); +} + +#ifdef CONFIG_SH_DSP +static void release_dsp(void) +{ + unsigned long sr; + + /* Clear SR.DSP bit */ + __asm__ __volatile__ ( + "stc\tsr, %0\n\t" + "and\t%1, %0\n\t" + "ldc\t%0, sr\n\t" + : "=&r" (sr) + : "r" (~SR_DSP) + ); +} + +static void dsp_init(void) +{ + unsigned long sr; + + /* + * Set the SR.DSP bit, wait for one instruction, and then read + * back the SR value. + */ + __asm__ __volatile__ ( + "stc\tsr, %0\n\t" + "or\t%1, %0\n\t" + "ldc\t%0, sr\n\t" + "nop\n\t" + "stc\tsr, %0\n\t" + : "=&r" (sr) + : "r" (SR_DSP) + ); + + /* If the DSP bit is still set, this CPU has a DSP */ + if (sr & SR_DSP) + current_cpu_data.flags |= CPU_HAS_DSP; + + /* Disable the DSP */ + if (dsp_disabled && (current_cpu_data.flags & CPU_HAS_DSP)) { + printk("DSP Disabled\n"); + current_cpu_data.flags &= ~CPU_HAS_DSP; + } + + /* Now that we've determined the DSP status, clear the DSP bit. */ + release_dsp(); +} +#else +static inline void dsp_init(void) { } +#endif /* CONFIG_SH_DSP */ + +/** + * cpu_init + * + * This is our initial entry point for each CPU, and is invoked on the + * boot CPU prior to calling start_kernel(). For SMP, a combination of + * this and start_secondary() will bring up each processor to a ready + * state prior to hand forking the idle loop. + * + * We do all of the basic processor init here, including setting up + * the caches, FPU, DSP, etc. By the time start_kernel() is hit (and + * subsequently platform_setup()) things like determining the CPU + * subtype and initial configuration will all be done. + * + * Each processor family is still responsible for doing its own probing + * and cache configuration in cpu_probe(). + */ +asmlinkage void cpu_init(void) +{ + current_thread_info()->cpu = hard_smp_processor_id(); + + /* First, probe the CPU */ + cpu_probe(); + + if (current_cpu_data.type == CPU_SH_NONE) + panic("Unknown CPU"); + + /* First setup the rest of the I-cache info */ + current_cpu_data.icache.entry_mask = current_cpu_data.icache.way_incr - + current_cpu_data.icache.linesz; + + current_cpu_data.icache.way_size = current_cpu_data.icache.sets * + current_cpu_data.icache.linesz; + + /* And the D-cache too */ + current_cpu_data.dcache.entry_mask = current_cpu_data.dcache.way_incr - + current_cpu_data.dcache.linesz; + + current_cpu_data.dcache.way_size = current_cpu_data.dcache.sets * + current_cpu_data.dcache.linesz; + + /* Init the cache */ + cache_init(); + + if (raw_smp_processor_id() == 0) { +#ifdef CONFIG_MMU + shm_align_mask = max_t(unsigned long, + current_cpu_data.dcache.way_size - 1, + PAGE_SIZE - 1); +#else + shm_align_mask = PAGE_SIZE - 1; +#endif + + /* Boot CPU sets the cache shape */ + detect_cache_shape(); + } + + fpu_init(); + dsp_init(); + + /* + * Initialize the per-CPU ASID cache very early, since the + * TLB flushing routines depend on this being setup. + */ + current_cpu_data.asid_cache = NO_CONTEXT; + + current_cpu_data.phys_bits = __in_29bit_mode() ? 29 : 32; + + speculative_execution_init(); + expmask_init(); + + /* Do the rest of the boot processor setup */ + if (raw_smp_processor_id() == 0) { + /* Save off the BIOS VBR, if there is one */ + sh_bios_vbr_init(); + + /* + * Setup VBR for boot CPU. Secondary CPUs do this through + * start_secondary(). + */ + per_cpu_trap_init(); + + /* + * Boot processor to setup the FP and extended state + * context info. + */ + init_thread_xstate(); + } +} diff --git a/arch/sh/kernel/cpu/irq/Makefile b/arch/sh/kernel/cpu/irq/Makefile new file mode 100644 index 000000000..3f8e79402 --- /dev/null +++ b/arch/sh/kernel/cpu/irq/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for the Linux/SuperH CPU-specific IRQ handlers. +# +obj-$(CONFIG_SUPERH32) += imask.o +obj-$(CONFIG_CPU_SH5) += intc-sh5.o +obj-$(CONFIG_CPU_HAS_IPR_IRQ) += ipr.o diff --git a/arch/sh/kernel/cpu/irq/imask.c b/arch/sh/kernel/cpu/irq/imask.c new file mode 100644 index 000000000..572585c3f --- /dev/null +++ b/arch/sh/kernel/cpu/irq/imask.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * arch/sh/kernel/cpu/irq/imask.c + * + * Copyright (C) 1999, 2000 Niibe Yutaka + * + * Simple interrupt handling using IMASK of SR register. + * + */ +/* NOTE: Will not work on level 15 */ +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/kernel_stat.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/bitops.h> +#include <linux/spinlock.h> +#include <linux/cache.h> +#include <linux/irq.h> +#include <linux/bitmap.h> +#include <asm/irq.h> + +/* Bitmap of IRQ masked */ +#define IMASK_PRIORITY 15 + +static DECLARE_BITMAP(imask_mask, IMASK_PRIORITY); +static int interrupt_priority; + +static inline void set_interrupt_registers(int ip) +{ + unsigned long __dummy; + + asm volatile( +#ifdef CONFIG_CPU_HAS_SR_RB + "ldc %2, r6_bank\n\t" +#endif + "stc sr, %0\n\t" + "and #0xf0, %0\n\t" + "shlr2 %0\n\t" + "cmp/eq #0x3c, %0\n\t" + "bt/s 1f ! CLI-ed\n\t" + " stc sr, %0\n\t" + "and %1, %0\n\t" + "or %2, %0\n\t" + "ldc %0, sr\n" + "1:" + : "=&z" (__dummy) + : "r" (~0xf0), "r" (ip << 4) + : "t"); +} + +static void mask_imask_irq(struct irq_data *data) +{ + unsigned int irq = data->irq; + + clear_bit(irq, imask_mask); + if (interrupt_priority < IMASK_PRIORITY - irq) + interrupt_priority = IMASK_PRIORITY - irq; + set_interrupt_registers(interrupt_priority); +} + +static void unmask_imask_irq(struct irq_data *data) +{ + unsigned int irq = data->irq; + + set_bit(irq, imask_mask); + interrupt_priority = IMASK_PRIORITY - + find_first_zero_bit(imask_mask, IMASK_PRIORITY); + set_interrupt_registers(interrupt_priority); +} + +static struct irq_chip imask_irq_chip = { + .name = "SR.IMASK", + .irq_mask = mask_imask_irq, + .irq_unmask = unmask_imask_irq, + .irq_mask_ack = mask_imask_irq, +}; + +void make_imask_irq(unsigned int irq) +{ + irq_set_chip_and_handler_name(irq, &imask_irq_chip, handle_level_irq, + "level"); +} diff --git a/arch/sh/kernel/cpu/irq/intc-sh5.c b/arch/sh/kernel/cpu/irq/intc-sh5.c new file mode 100644 index 000000000..9e056a3a0 --- /dev/null +++ b/arch/sh/kernel/cpu/irq/intc-sh5.c @@ -0,0 +1,197 @@ +/* + * arch/sh/kernel/cpu/irq/intc-sh5.c + * + * Interrupt Controller support for SH5 INTC. + * + * Copyright (C) 2000, 2001 Paolo Alberelli + * Copyright (C) 2003 Paul Mundt + * + * Per-interrupt selective. IRLM=0 (Fixed priority) is not + * supported being useless without a cascaded interrupt + * controller. + * + * 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/interrupt.h> +#include <linux/irq.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/bitops.h> +#include <cpu/irq.h> +#include <asm/page.h> + +/* + * Maybe the generic Peripheral block could move to a more + * generic include file. INTC Block will be defined here + * and only here to make INTC self-contained in a single + * file. + */ +#define INTC_BLOCK_OFFSET 0x01000000 + +/* Base */ +#define INTC_BASE PHYS_PERIPHERAL_BLOCK + \ + INTC_BLOCK_OFFSET + +/* Address */ +#define INTC_ICR_SET (intc_virt + 0x0) +#define INTC_ICR_CLEAR (intc_virt + 0x8) +#define INTC_INTPRI_0 (intc_virt + 0x10) +#define INTC_INTSRC_0 (intc_virt + 0x50) +#define INTC_INTSRC_1 (intc_virt + 0x58) +#define INTC_INTREQ_0 (intc_virt + 0x60) +#define INTC_INTREQ_1 (intc_virt + 0x68) +#define INTC_INTENB_0 (intc_virt + 0x70) +#define INTC_INTENB_1 (intc_virt + 0x78) +#define INTC_INTDSB_0 (intc_virt + 0x80) +#define INTC_INTDSB_1 (intc_virt + 0x88) + +#define INTC_ICR_IRLM 0x1 +#define INTC_INTPRI_PREGS 8 /* 8 Priority Registers */ +#define INTC_INTPRI_PPREG 8 /* 8 Priorities per Register */ + + +/* + * Mapper between the vector ordinal and the IRQ number + * passed to kernel/device drivers. + */ +int intc_evt_to_irq[(0xE20/0x20)+1] = { + -1, -1, -1, -1, -1, -1, -1, -1, /* 0x000 - 0x0E0 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 0x100 - 0x1E0 */ + 0, 0, 0, 0, 0, 1, 0, 0, /* 0x200 - 0x2E0 */ + 2, 0, 0, 3, 0, 0, 0, -1, /* 0x300 - 0x3E0 */ + 32, 33, 34, 35, 36, 37, 38, -1, /* 0x400 - 0x4E0 */ + -1, -1, -1, 63, -1, -1, -1, -1, /* 0x500 - 0x5E0 */ + -1, -1, 18, 19, 20, 21, 22, -1, /* 0x600 - 0x6E0 */ + 39, 40, 41, 42, -1, -1, -1, -1, /* 0x700 - 0x7E0 */ + 4, 5, 6, 7, -1, -1, -1, -1, /* 0x800 - 0x8E0 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 0x900 - 0x9E0 */ + 12, 13, 14, 15, 16, 17, -1, -1, /* 0xA00 - 0xAE0 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 0xB00 - 0xBE0 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 0xC00 - 0xCE0 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 0xD00 - 0xDE0 */ + -1, -1 /* 0xE00 - 0xE20 */ +}; + +static unsigned long intc_virt; +static int irlm; /* IRL mode */ + +static void enable_intc_irq(struct irq_data *data) +{ + unsigned int irq = data->irq; + unsigned long reg; + unsigned long bitmask; + + if ((irq <= IRQ_IRL3) && (irlm == NO_PRIORITY)) + printk("Trying to use straight IRL0-3 with an encoding platform.\n"); + + if (irq < 32) { + reg = INTC_INTENB_0; + bitmask = 1 << irq; + } else { + reg = INTC_INTENB_1; + bitmask = 1 << (irq - 32); + } + + __raw_writel(bitmask, reg); +} + +static void disable_intc_irq(struct irq_data *data) +{ + unsigned int irq = data->irq; + unsigned long reg; + unsigned long bitmask; + + if (irq < 32) { + reg = INTC_INTDSB_0; + bitmask = 1 << irq; + } else { + reg = INTC_INTDSB_1; + bitmask = 1 << (irq - 32); + } + + __raw_writel(bitmask, reg); +} + +static struct irq_chip intc_irq_type = { + .name = "INTC", + .irq_enable = enable_intc_irq, + .irq_disable = disable_intc_irq, +}; + +void __init plat_irq_setup(void) +{ + unsigned long long __dummy0, __dummy1=~0x00000000100000f0; + unsigned long reg; + int i; + + intc_virt = (unsigned long)ioremap_nocache(INTC_BASE, 1024); + if (!intc_virt) { + panic("Unable to remap INTC\n"); + } + + + /* Set default: per-line enable/disable, priority driven ack/eoi */ + for (i = 0; i < NR_INTC_IRQS; i++) + irq_set_chip_and_handler(i, &intc_irq_type, handle_level_irq); + + + /* Disable all interrupts and set all priorities to 0 to avoid trouble */ + __raw_writel(-1, INTC_INTDSB_0); + __raw_writel(-1, INTC_INTDSB_1); + + for (reg = INTC_INTPRI_0, i = 0; i < INTC_INTPRI_PREGS; i++, reg += 8) + __raw_writel( NO_PRIORITY, reg); + + +#ifdef CONFIG_SH_CAYMAN + { + unsigned long data; + + /* Set IRLM */ + /* If all the priorities are set to 'no priority', then + * assume we are using encoded mode. + */ + irlm = platform_int_priority[IRQ_IRL0] + + platform_int_priority[IRQ_IRL1] + + platform_int_priority[IRQ_IRL2] + + platform_int_priority[IRQ_IRL3]; + if (irlm == NO_PRIORITY) { + /* IRLM = 0 */ + reg = INTC_ICR_CLEAR; + i = IRQ_INTA; + printk("Trying to use encoded IRL0-3. IRLs unsupported.\n"); + } else { + /* IRLM = 1 */ + reg = INTC_ICR_SET; + i = IRQ_IRL0; + } + __raw_writel(INTC_ICR_IRLM, reg); + + /* Set interrupt priorities according to platform description */ + for (data = 0, reg = INTC_INTPRI_0; i < NR_INTC_IRQS; i++) { + data |= platform_int_priority[i] << + ((i % INTC_INTPRI_PPREG) * 4); + if ((i % INTC_INTPRI_PPREG) == (INTC_INTPRI_PPREG - 1)) { + /* Upon the 7th, set Priority Register */ + __raw_writel(data, reg); + data = 0; + reg += 8; + } + } + } +#endif + + /* + * And now let interrupts come in. + * sti() is not enough, we need to + * lower priority, too. + */ + __asm__ __volatile__("getcon " __SR ", %0\n\t" + "and %0, %1, %0\n\t" + "putcon %0, " __SR "\n\t" + : "=&r" (__dummy0) + : "r" (__dummy1)); +} diff --git a/arch/sh/kernel/cpu/irq/ipr.c b/arch/sh/kernel/cpu/irq/ipr.c new file mode 100644 index 000000000..5de6dff5c --- /dev/null +++ b/arch/sh/kernel/cpu/irq/ipr.c @@ -0,0 +1,83 @@ +/* + * Interrupt handling for IPR-based IRQ. + * + * Copyright (C) 1999 Niibe Yutaka & Takeshi Yaegashi + * Copyright (C) 2000 Kazumoto Kojima + * Copyright (C) 2003 Takashi Kusuda <kusuda-takashi@hitachi-ul.co.jp> + * Copyright (C) 2006 Paul Mundt + * + * Supported system: + * On-chip supporting modules (TMU, RTC, etc.). + * On-chip supporting modules for SH7709/SH7709A/SH7729. + * Hitachi SolutionEngine external I/O: + * MS7709SE01, MS7709ASE01, and MS7750SE01 + * + * 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/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/topology.h> + +static inline struct ipr_desc *get_ipr_desc(struct irq_data *data) +{ + struct irq_chip *chip = irq_data_get_irq_chip(data); + return container_of(chip, struct ipr_desc, chip); +} + +static void disable_ipr_irq(struct irq_data *data) +{ + struct ipr_data *p = irq_data_get_irq_chip_data(data); + unsigned long addr = get_ipr_desc(data)->ipr_offsets[p->ipr_idx]; + /* Set the priority in IPR to 0 */ + __raw_writew(__raw_readw(addr) & (0xffff ^ (0xf << p->shift)), addr); + (void)__raw_readw(addr); /* Read back to flush write posting */ +} + +static void enable_ipr_irq(struct irq_data *data) +{ + struct ipr_data *p = irq_data_get_irq_chip_data(data); + unsigned long addr = get_ipr_desc(data)->ipr_offsets[p->ipr_idx]; + /* Set priority in IPR back to original value */ + __raw_writew(__raw_readw(addr) | (p->priority << p->shift), addr); +} + +/* + * The shift value is now the number of bits to shift, not the number of + * bits/4. This is to make it easier to read the value directly from the + * datasheets. The IPR address is calculated using the ipr_offset table. + */ +void register_ipr_controller(struct ipr_desc *desc) +{ + int i; + + desc->chip.irq_mask = disable_ipr_irq; + desc->chip.irq_unmask = enable_ipr_irq; + + for (i = 0; i < desc->nr_irqs; i++) { + struct ipr_data *p = desc->ipr_data + i; + int res; + + BUG_ON(p->ipr_idx >= desc->nr_offsets); + BUG_ON(!desc->ipr_offsets[p->ipr_idx]); + + res = irq_alloc_desc_at(p->irq, numa_node_id()); + if (unlikely(res != p->irq && res != -EEXIST)) { + printk(KERN_INFO "can not get irq_desc for %d\n", + p->irq); + continue; + } + + disable_irq_nosync(p->irq); + irq_set_chip_and_handler_name(p->irq, &desc->chip, + handle_level_irq, "level"); + irq_set_chip_data(p->irq, p); + disable_ipr_irq(irq_get_irq_data(p->irq)); + } +} +EXPORT_SYMBOL(register_ipr_controller); diff --git a/arch/sh/kernel/cpu/pfc.c b/arch/sh/kernel/cpu/pfc.c new file mode 100644 index 000000000..d766564ef --- /dev/null +++ b/arch/sh/kernel/cpu/pfc.c @@ -0,0 +1,33 @@ +/* + * SH Pin Function Control Initialization + * + * Copyright (C) 2012 Renesas Solutions Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/init.h> +#include <linux/platform_device.h> + +#include <cpu/pfc.h> + +static struct platform_device sh_pfc_device = { + .id = -1, +}; + +int __init sh_pfc_register(const char *name, + struct resource *resource, u32 num_resources) +{ + sh_pfc_device.name = name; + sh_pfc_device.num_resources = num_resources; + sh_pfc_device.resource = resource; + + return platform_device_register(&sh_pfc_device); +} diff --git a/arch/sh/kernel/cpu/proc.c b/arch/sh/kernel/cpu/proc.c new file mode 100644 index 000000000..85961b4f9 --- /dev/null +++ b/arch/sh/kernel/cpu/proc.c @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/seq_file.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <asm/machvec.h> +#include <asm/processor.h> + +static const char *cpu_name[] = { + [CPU_SH7201] = "SH7201", + [CPU_SH7203] = "SH7203", [CPU_SH7263] = "SH7263", + [CPU_SH7264] = "SH7264", [CPU_SH7269] = "SH7269", + [CPU_SH7206] = "SH7206", [CPU_SH7619] = "SH7619", + [CPU_SH7705] = "SH7705", [CPU_SH7706] = "SH7706", + [CPU_SH7707] = "SH7707", [CPU_SH7708] = "SH7708", + [CPU_SH7709] = "SH7709", [CPU_SH7710] = "SH7710", + [CPU_SH7712] = "SH7712", [CPU_SH7720] = "SH7720", + [CPU_SH7721] = "SH7721", [CPU_SH7729] = "SH7729", + [CPU_SH7750] = "SH7750", [CPU_SH7750S] = "SH7750S", + [CPU_SH7750R] = "SH7750R", [CPU_SH7751] = "SH7751", + [CPU_SH7751R] = "SH7751R", [CPU_SH7760] = "SH7760", + [CPU_SH4_202] = "SH4-202", [CPU_SH4_501] = "SH4-501", + [CPU_SH7763] = "SH7763", [CPU_SH7770] = "SH7770", + [CPU_SH7780] = "SH7780", [CPU_SH7781] = "SH7781", + [CPU_SH7343] = "SH7343", [CPU_SH7785] = "SH7785", + [CPU_SH7786] = "SH7786", [CPU_SH7757] = "SH7757", + [CPU_SH7722] = "SH7722", [CPU_SHX3] = "SH-X3", + [CPU_SH5_101] = "SH5-101", [CPU_SH5_103] = "SH5-103", + [CPU_MXG] = "MX-G", [CPU_SH7723] = "SH7723", + [CPU_SH7366] = "SH7366", [CPU_SH7724] = "SH7724", + [CPU_SH7372] = "SH7372", [CPU_SH7734] = "SH7734", + [CPU_J2] = "J2", + [CPU_SH_NONE] = "Unknown" +}; + +const char *get_cpu_subtype(struct sh_cpuinfo *c) +{ + return cpu_name[c->type]; +} +EXPORT_SYMBOL(get_cpu_subtype); + +#ifdef CONFIG_PROC_FS +/* Symbolic CPU flags, keep in sync with asm/cpu-features.h */ +static const char *cpu_flags[] = { + "none", "fpu", "p2flush", "mmuassoc", "dsp", "perfctr", + "ptea", "llsc", "l2", "op32", "pteaex", NULL +}; + +static void show_cpuflags(struct seq_file *m, struct sh_cpuinfo *c) +{ + unsigned long i; + + seq_printf(m, "cpu flags\t:"); + + if (!c->flags) { + seq_printf(m, " %s\n", cpu_flags[0]); + return; + } + + for (i = 0; cpu_flags[i]; i++) + if ((c->flags & (1 << i))) + seq_printf(m, " %s", cpu_flags[i+1]); + + seq_printf(m, "\n"); +} + +static void show_cacheinfo(struct seq_file *m, const char *type, + struct cache_info info) +{ + unsigned int cache_size; + + cache_size = info.ways * info.sets * info.linesz; + + seq_printf(m, "%s size\t: %2dKiB (%d-way)\n", + type, cache_size >> 10, info.ways); +} + +/* + * Get CPU information for use by the procfs. + */ +static int show_cpuinfo(struct seq_file *m, void *v) +{ + struct sh_cpuinfo *c = v; + unsigned int cpu = c - cpu_data; + + if (!cpu_online(cpu)) + return 0; + + if (cpu == 0) + seq_printf(m, "machine\t\t: %s\n", get_system_type()); + else + seq_printf(m, "\n"); + + seq_printf(m, "processor\t: %d\n", cpu); + seq_printf(m, "cpu family\t: %s\n", init_utsname()->machine); + seq_printf(m, "cpu type\t: %s\n", get_cpu_subtype(c)); + if (c->cut_major == -1) + seq_printf(m, "cut\t\t: unknown\n"); + else if (c->cut_minor == -1) + seq_printf(m, "cut\t\t: %d.x\n", c->cut_major); + else + seq_printf(m, "cut\t\t: %d.%d\n", c->cut_major, c->cut_minor); + + show_cpuflags(m, c); + + seq_printf(m, "cache type\t: "); + + /* + * Check for what type of cache we have, we support both the + * unified cache on the SH-2 and SH-3, as well as the harvard + * style cache on the SH-4. + */ + if (c->icache.flags & SH_CACHE_COMBINED) { + seq_printf(m, "unified\n"); + show_cacheinfo(m, "cache", c->icache); + } else { + seq_printf(m, "split (harvard)\n"); + show_cacheinfo(m, "icache", c->icache); + show_cacheinfo(m, "dcache", c->dcache); + } + + /* Optional secondary cache */ + if (c->flags & CPU_HAS_L2_CACHE) + show_cacheinfo(m, "scache", c->scache); + + seq_printf(m, "address sizes\t: %u bits physical\n", c->phys_bits); + + seq_printf(m, "bogomips\t: %lu.%02lu\n", + c->loops_per_jiffy/(500000/HZ), + (c->loops_per_jiffy/(5000/HZ)) % 100); + + return 0; +} + +static void *c_start(struct seq_file *m, loff_t *pos) +{ + return *pos < NR_CPUS ? cpu_data + *pos : NULL; +} +static void *c_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return c_start(m, pos); +} +static void c_stop(struct seq_file *m, void *v) +{ +} +const struct seq_operations cpuinfo_op = { + .start = c_start, + .next = c_next, + .stop = c_stop, + .show = show_cpuinfo, +}; +#endif /* CONFIG_PROC_FS */ diff --git a/arch/sh/kernel/cpu/sh2/Makefile b/arch/sh/kernel/cpu/sh2/Makefile new file mode 100644 index 000000000..904c4283d --- /dev/null +++ b/arch/sh/kernel/cpu/sh2/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for the Linux/SuperH SH-2 backends. +# + +obj-y := ex.o probe.o entry.o + +obj-$(CONFIG_CPU_SUBTYPE_SH7619) += setup-sh7619.o clock-sh7619.o + +# SMP setup +smp-$(CONFIG_CPU_J2) := smp-j2.o +obj-$(CONFIG_SMP) += $(smp-y) diff --git a/arch/sh/kernel/cpu/sh2/clock-sh7619.c b/arch/sh/kernel/cpu/sh2/clock-sh7619.c new file mode 100644 index 000000000..e80252ae5 --- /dev/null +++ b/arch/sh/kernel/cpu/sh2/clock-sh7619.c @@ -0,0 +1,77 @@ +/* + * arch/sh/kernel/cpu/sh2/clock-sh7619.c + * + * SH7619 support for the clock framework + * + * Copyright (C) 2006 Yoshinori Sato + * + * Based on clock-sh4.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 <linux/io.h> +#include <asm/clock.h> +#include <asm/freq.h> +#include <asm/processor.h> + +static const int pll1rate[] = {1,2}; +static const int pfc_divisors[] = {1,2,0,4}; +static unsigned int pll2_mult; + +static void master_clk_init(struct clk *clk) +{ + clk->rate *= pll2_mult * pll1rate[(__raw_readw(FREQCR) >> 8) & 7]; +} + +static struct sh_clk_ops sh7619_master_clk_ops = { + .init = master_clk_init, +}; + +static unsigned long module_clk_recalc(struct clk *clk) +{ + int idx = (__raw_readw(FREQCR) & 0x0007); + return clk->parent->rate / pfc_divisors[idx]; +} + +static struct sh_clk_ops sh7619_module_clk_ops = { + .recalc = module_clk_recalc, +}; + +static unsigned long bus_clk_recalc(struct clk *clk) +{ + return clk->parent->rate / pll1rate[(__raw_readw(FREQCR) >> 8) & 7]; +} + +static struct sh_clk_ops sh7619_bus_clk_ops = { + .recalc = bus_clk_recalc, +}; + +static struct sh_clk_ops sh7619_cpu_clk_ops = { + .recalc = followparent_recalc, +}; + +static struct sh_clk_ops *sh7619_clk_ops[] = { + &sh7619_master_clk_ops, + &sh7619_module_clk_ops, + &sh7619_bus_clk_ops, + &sh7619_cpu_clk_ops, +}; + +void __init arch_init_clk_ops(struct sh_clk_ops **ops, int idx) +{ + if (test_mode_pin(MODE_PIN2 | MODE_PIN0) || + test_mode_pin(MODE_PIN2 | MODE_PIN1)) + pll2_mult = 2; + else if (test_mode_pin(MODE_PIN0) || test_mode_pin(MODE_PIN1)) + pll2_mult = 4; + + BUG_ON(!pll2_mult); + + if (idx < ARRAY_SIZE(sh7619_clk_ops)) + *ops = sh7619_clk_ops[idx]; +} diff --git a/arch/sh/kernel/cpu/sh2/entry.S b/arch/sh/kernel/cpu/sh2/entry.S new file mode 100644 index 000000000..1ee0a6e77 --- /dev/null +++ b/arch/sh/kernel/cpu/sh2/entry.S @@ -0,0 +1,376 @@ +/* + * arch/sh/kernel/cpu/sh2/entry.S + * + * The SH-2 exception entry + * + * Copyright (C) 2005-2008 Yoshinori Sato + * Copyright (C) 2005 AXE,Inc. + * + * 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/linkage.h> +#include <asm/asm-offsets.h> +#include <asm/thread_info.h> +#include <cpu/mmu_context.h> +#include <asm/unistd.h> +#include <asm/errno.h> +#include <asm/page.h> + +/* Offsets to the stack */ +OFF_R0 = 0 /* Return value. New ABI also arg4 */ +OFF_R1 = 4 /* New ABI: arg5 */ +OFF_R2 = 8 /* New ABI: arg6 */ +OFF_R3 = 12 /* New ABI: syscall_nr */ +OFF_R4 = 16 /* New ABI: arg0 */ +OFF_R5 = 20 /* New ABI: arg1 */ +OFF_R6 = 24 /* New ABI: arg2 */ +OFF_R7 = 28 /* New ABI: arg3 */ +OFF_SP = (15*4) +OFF_PC = (16*4) +OFF_SR = (16*4+2*4) +OFF_TRA = (16*4+6*4) + +#include <asm/entry-macros.S> + +ENTRY(exception_handler) + ! stack + ! r0 <- point sp + ! r1 + ! pc + ! sr + ! r0 = temporary + ! r1 = vector (pseudo EXPEVT / INTEVT / TRA) + mov.l r2,@-sp + mov.l r3,@-sp + cli + mov.l $cpu_mode,r2 +#ifdef CONFIG_SMP + mov.l $cpuid,r3 + mov.l @r3,r3 + mov.l @r3,r3 + shll2 r3 + add r3,r2 +#endif + mov.l @r2,r0 + mov.l @(5*4,r15),r3 ! previous SR + or r0,r3 ! set MD + tst r0,r0 + bf/s 1f ! previous mode check + mov.l r3,@(5*4,r15) ! update SR + ! switch to kernel mode + mov.l __md_bit,r0 + mov.l r0,@r2 ! enter kernel mode + mov.l $current_thread_info,r2 +#ifdef CONFIG_SMP + mov.l $cpuid,r0 + mov.l @r0,r0 + mov.l @r0,r0 + shll2 r0 + add r0,r2 +#endif + mov.l @r2,r2 + mov #(THREAD_SIZE >> 8),r0 + shll8 r0 + add r2,r0 + mov r15,r2 ! r2 = user stack top + mov r0,r15 ! switch kernel stack + mov.l r1,@-r15 ! TRA + sts.l macl, @-r15 + sts.l mach, @-r15 + stc.l gbr, @-r15 + mov.l @(5*4,r2),r0 + mov.l r0,@-r15 ! original SR + sts.l pr,@-r15 + mov.l @(4*4,r2),r0 + mov.l r0,@-r15 ! original PC + mov r2,r3 + add #(4+2)*4,r3 ! rewind r0 - r3 + exception frame + mov.l r3,@-r15 ! original SP + mov.l r14,@-r15 + mov.l r13,@-r15 + mov.l r12,@-r15 + mov.l r11,@-r15 + mov.l r10,@-r15 + mov.l r9,@-r15 + mov.l r8,@-r15 + mov.l r7,@-r15 + mov.l r6,@-r15 + mov.l r5,@-r15 + mov.l r4,@-r15 + mov r1,r9 ! save TRA + mov r2,r8 ! copy user -> kernel stack + mov.l @(0,r8),r3 + mov.l r3,@-r15 + mov.l @(4,r8),r2 + mov.l r2,@-r15 + mov.l @(12,r8),r1 + mov.l r1,@-r15 + mov.l @(8,r8),r0 + bra 2f + mov.l r0,@-r15 +1: + ! in kernel exception + mov #(22-4-4-1)*4+4,r0 + mov r15,r2 + sub r0,r15 + mov.l @r2+,r0 ! old R3 + mov.l r0,@-r15 + mov.l @r2+,r0 ! old R2 + mov.l r0,@-r15 + mov.l @(4,r2),r0 ! old R1 + mov.l r0,@-r15 + mov.l @r2,r0 ! old R0 + mov.l r0,@-r15 + add #8,r2 + mov.l @r2+,r3 ! old PC + mov.l @r2+,r0 ! old SR + add #-4,r2 ! exception frame stub (sr) + mov.l r1,@-r2 ! TRA + sts.l macl, @-r2 + sts.l mach, @-r2 + stc.l gbr, @-r2 + mov.l r0,@-r2 ! save old SR + sts.l pr,@-r2 + mov.l r3,@-r2 ! save old PC + mov r2,r0 + add #8*4,r0 + mov.l r0,@-r2 ! save old SP + mov.l r14,@-r2 + mov.l r13,@-r2 + mov.l r12,@-r2 + mov.l r11,@-r2 + mov.l r10,@-r2 + mov.l r9,@-r2 + mov.l r8,@-r2 + mov.l r7,@-r2 + mov.l r6,@-r2 + mov.l r5,@-r2 + mov.l r4,@-r2 + mov r1,r9 + mov.l @(OFF_R0,r15),r0 + mov.l @(OFF_R1,r15),r1 + mov.l @(OFF_R2,r15),r2 + mov.l @(OFF_R3,r15),r3 +2: + mov #64,r8 + cmp/hs r8,r9 + bt interrupt_entry ! vec >= 64 is interrupt + mov #31,r8 + cmp/hs r8,r9 + bt trap_entry ! 64 > vec >= 31 is trap +#ifdef CONFIG_CPU_J2 + mov #16,r8 + cmp/hs r8,r9 + bt interrupt_entry ! 31 > vec >= 16 is interrupt +#endif + + mov.l 4f,r8 + mov r9,r4 + shll2 r9 + add r9,r8 + mov.l @r8,r8 ! exception handler address + tst r8,r8 + bf 3f + mov.l 8f,r8 ! unhandled exception +3: + mov.l 5f,r10 + jmp @r8 + lds r10,pr + +interrupt_entry: + mov r9,r4 + mov r15,r5 + mov.l 6f,r9 + mov.l 7f,r8 + jmp @r8 + lds r9,pr + + .align 2 +4: .long exception_handling_table +5: .long ret_from_exception +6: .long ret_from_irq +7: .long do_IRQ +8: .long exception_error + +trap_entry: + mov #0x30,r8 + cmp/ge r8,r9 ! vector 0x1f-0x2f is systemcall + bt 1f + mov #0x1f,r9 ! convert to unified SH2/3/4 trap number +1: + shll2 r9 ! TRA + bra system_call ! jump common systemcall entry + mov r9,r8 + +#if defined(CONFIG_SH_STANDARD_BIOS) + /* Unwind the stack and jmp to the debug entry */ +ENTRY(sh_bios_handler) + mov r15,r0 + add #(22-4)*4-4,r0 + ldc.l @r0+,gbr + lds.l @r0+,mach + lds.l @r0+,macl + mov r15,r0 + mov.l @(OFF_SP,r0),r1 + mov #OFF_SR,r2 + mov.l @(r0,r2),r3 + mov.l r3,@-r1 + mov #OFF_SP,r2 + mov.l @(r0,r2),r3 + mov.l r3,@-r1 + mov r15,r0 + add #(22-4)*4-8,r0 + mov.l 1f,r2 + mov.l @r2,r2 + stc sr,r3 + mov.l r2,@r0 + mov.l r3,@(4,r0) + mov.l r1,@(8,r0) + mov.l @r15+, r0 + mov.l @r15+, r1 + mov.l @r15+, r2 + mov.l @r15+, r3 + mov.l @r15+, r4 + mov.l @r15+, r5 + mov.l @r15+, r6 + mov.l @r15+, r7 + mov.l @r15+, r8 + mov.l @r15+, r9 + mov.l @r15+, r10 + mov.l @r15+, r11 + mov.l @r15+, r12 + mov.l @r15+, r13 + mov.l @r15+, r14 + add #8,r15 + lds.l @r15+, pr + mov.l @r15+,r15 + rte + nop + .align 2 +1: .long gdb_vbr_vector +#endif /* CONFIG_SH_STANDARD_BIOS */ + +ENTRY(address_error_trap_handler) + mov r15,r4 ! regs + mov #OFF_PC,r0 + mov.l @(r0,r15),r6 ! pc + mov.l 1f,r0 + jmp @r0 + mov #0,r5 ! writeaccess is unknown + + .align 2 +1: .long do_address_error + +restore_all: + stc sr,r0 + or #0xf0,r0 + ldc r0,sr ! all interrupt block (same BL = 1) + ! restore special register + ! overlap exception frame + mov r15,r0 + add #17*4,r0 + lds.l @r0+,pr + add #4,r0 + ldc.l @r0+,gbr + lds.l @r0+,mach + lds.l @r0+,macl + mov r15,r0 + mov.l $cpu_mode,r2 +#ifdef CONFIG_SMP + mov.l $cpuid,r3 + mov.l @r3,r3 + mov.l @r3,r3 + shll2 r3 + add r3,r2 +#endif + mov #OFF_SR,r3 + mov.l @(r0,r3),r1 + mov.l __md_bit,r3 + and r1,r3 ! copy MD bit + mov.l r3,@r2 + shll2 r1 ! clear MD bit + shlr2 r1 + mov.l @(OFF_SP,r0),r2 + add #-8,r2 + mov.l r2,@(OFF_SP,r0) ! point exception frame top + mov.l r1,@(4,r2) ! set sr + mov #OFF_PC,r3 + mov.l @(r0,r3),r1 + mov.l r1,@r2 ! set pc + get_current_thread_info r0, r1 + mov.l $current_thread_info,r1 +#ifdef CONFIG_SMP + mov.l $cpuid,r3 + mov.l @r3,r3 + mov.l @r3,r3 + shll2 r3 + add r3,r1 +#endif + mov.l r0,@r1 + mov.l @r15+,r0 + mov.l @r15+,r1 + mov.l @r15+,r2 + mov.l @r15+,r3 + mov.l @r15+,r4 + mov.l @r15+,r5 + mov.l @r15+,r6 + mov.l @r15+,r7 + mov.l @r15+,r8 + mov.l @r15+,r9 + mov.l @r15+,r10 + mov.l @r15+,r11 + mov.l @r15+,r12 + mov.l @r15+,r13 + mov.l @r15+,r14 + mov.l @r15,r15 + rte + nop + + .align 2 +__md_bit: + .long 0x40000000 +$current_thread_info: + .long __current_thread_info +$cpu_mode: + .long __cpu_mode +#ifdef CONFIG_SMP +$cpuid: + .long sh2_cpuid_addr +#endif + +! common exception handler +#include "../../entry-common.S" + +#ifdef CONFIG_NR_CPUS +#define NR_CPUS CONFIG_NR_CPUS +#else +#define NR_CPUS 1 +#endif + + .data +! cpu operation mode +! bit30 = MD (compatible SH3/4) +__cpu_mode: + .rept NR_CPUS + .long 0x40000000 + .endr + +#ifdef CONFIG_SMP +.global sh2_cpuid_addr +sh2_cpuid_addr: + .long dummy_cpuid +dummy_cpuid: + .long 0 +#endif + + .section .bss +__current_thread_info: + .rept NR_CPUS + .long 0 + .endr + +ENTRY(exception_handling_table) + .space 4*32 diff --git a/arch/sh/kernel/cpu/sh2/ex.S b/arch/sh/kernel/cpu/sh2/ex.S new file mode 100644 index 000000000..85b0bf81f --- /dev/null +++ b/arch/sh/kernel/cpu/sh2/ex.S @@ -0,0 +1,47 @@ +/* + * arch/sh/kernel/cpu/sh2/ex.S + * + * The SH-2 exception vector table + * + * Copyright (C) 2005 Yoshinori Sato + * + * 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/linkage.h> + +! +! convert Exception Vector to Exception Number +! +exception_entry: +no = 0 + .rept 256 + mov.l r1,@-sp + bra exception_trampoline + mov #no,r1 +no = no + 1 + .endr +exception_trampoline: + mov.l r0,@-sp + mov.l $exception_handler,r0 + extu.b r1,r1 + jmp @r0 + extu.w r1,r1 + + .align 2 +$exception_entry: + .long exception_entry +$exception_handler: + .long exception_handler +! +! Exception Vector Base +! + .align 2 +ENTRY(vbr_base) +vector = 0 + .rept 256 + .long exception_entry + vector * 6 +vector = vector + 1 + .endr diff --git a/arch/sh/kernel/cpu/sh2/probe.c b/arch/sh/kernel/cpu/sh2/probe.c new file mode 100644 index 000000000..a5bd03642 --- /dev/null +++ b/arch/sh/kernel/cpu/sh2/probe.c @@ -0,0 +1,74 @@ +/* + * arch/sh/kernel/cpu/sh2/probe.c + * + * CPU Subtype Probing for SH-2. + * + * Copyright (C) 2002 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/of_fdt.h> +#include <linux/smp.h> +#include <linux/io.h> +#include <asm/processor.h> +#include <asm/cache.h> + +#if defined(CONFIG_CPU_J2) +extern u32 __iomem *j2_ccr_base; +static int __init scan_cache(unsigned long node, const char *uname, + int depth, void *data) +{ + if (!of_flat_dt_is_compatible(node, "jcore,cache")) + return 0; + + j2_ccr_base = (u32 __iomem *)of_flat_dt_translate_address(node); + + return 1; +} +#endif + +void __ref cpu_probe(void) +{ +#if defined(CONFIG_CPU_SUBTYPE_SH7619) + boot_cpu_data.type = CPU_SH7619; + boot_cpu_data.dcache.ways = 4; + boot_cpu_data.dcache.way_incr = (1<<12); + boot_cpu_data.dcache.sets = 256; + boot_cpu_data.dcache.entry_shift = 4; + boot_cpu_data.dcache.linesz = L1_CACHE_BYTES; + boot_cpu_data.dcache.flags = 0; +#endif + +#if defined(CONFIG_CPU_J2) +#if defined(CONFIG_SMP) + unsigned cpu = hard_smp_processor_id(); +#else + unsigned cpu = 0; +#endif + if (cpu == 0) of_scan_flat_dt(scan_cache, NULL); + if (j2_ccr_base) __raw_writel(0x80000303, j2_ccr_base + 4*cpu); + if (cpu != 0) return; + boot_cpu_data.type = CPU_J2; + + /* These defaults are appropriate for the original/current + * J2 cache. Once there is a proper framework for getting cache + * info from device tree, we should switch to that. */ + boot_cpu_data.dcache.ways = 1; + boot_cpu_data.dcache.sets = 256; + boot_cpu_data.dcache.entry_shift = 5; + boot_cpu_data.dcache.linesz = 32; + boot_cpu_data.dcache.flags = 0; + + boot_cpu_data.flags |= CPU_HAS_CAS_L; +#else + /* + * SH-2 doesn't have separate caches + */ + boot_cpu_data.dcache.flags |= SH_CACHE_COMBINED; +#endif + boot_cpu_data.icache = boot_cpu_data.dcache; + boot_cpu_data.family = CPU_FAMILY_SH2; +} diff --git a/arch/sh/kernel/cpu/sh2/setup-sh7619.c b/arch/sh/kernel/cpu/sh2/setup-sh7619.c new file mode 100644 index 000000000..d08db08de --- /dev/null +++ b/arch/sh/kernel/cpu/sh2/setup-sh7619.c @@ -0,0 +1,207 @@ +/* + * SH7619 Setup + * + * Copyright (C) 2006 Yoshinori Sato + * 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/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <linux/serial_sci.h> +#include <linux/sh_eth.h> +#include <linux/sh_timer.h> +#include <linux/io.h> + +enum { + UNUSED = 0, + + /* interrupt sources */ + IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7, + WDT, EDMAC, CMT0, CMT1, + SCIF0, SCIF1, SCIF2, + HIF_HIFI, HIF_HIFBI, + DMAC0, DMAC1, DMAC2, DMAC3, + SIOF, +}; + +static struct intc_vect vectors[] __initdata = { + INTC_IRQ(IRQ0, 64), INTC_IRQ(IRQ1, 65), + INTC_IRQ(IRQ2, 66), INTC_IRQ(IRQ3, 67), + INTC_IRQ(IRQ4, 80), INTC_IRQ(IRQ5, 81), + INTC_IRQ(IRQ6, 82), INTC_IRQ(IRQ7, 83), + INTC_IRQ(WDT, 84), INTC_IRQ(EDMAC, 85), + INTC_IRQ(CMT0, 86), INTC_IRQ(CMT1, 87), + INTC_IRQ(SCIF0, 88), INTC_IRQ(SCIF0, 89), + INTC_IRQ(SCIF0, 90), INTC_IRQ(SCIF0, 91), + INTC_IRQ(SCIF1, 92), INTC_IRQ(SCIF1, 93), + INTC_IRQ(SCIF1, 94), INTC_IRQ(SCIF1, 95), + INTC_IRQ(SCIF2, 96), INTC_IRQ(SCIF2, 97), + INTC_IRQ(SCIF2, 98), INTC_IRQ(SCIF2, 99), + INTC_IRQ(HIF_HIFI, 100), INTC_IRQ(HIF_HIFBI, 101), + INTC_IRQ(DMAC0, 104), INTC_IRQ(DMAC1, 105), + INTC_IRQ(DMAC2, 106), INTC_IRQ(DMAC3, 107), + INTC_IRQ(SIOF, 108), +}; + +static struct intc_prio_reg prio_registers[] __initdata = { + { 0xf8140006, 0, 16, 4, /* IPRA */ { IRQ0, IRQ1, IRQ2, IRQ3 } }, + { 0xf8140008, 0, 16, 4, /* IPRB */ { IRQ4, IRQ5, IRQ6, IRQ7 } }, + { 0xf8080000, 0, 16, 4, /* IPRC */ { WDT, EDMAC, CMT0, CMT1 } }, + { 0xf8080002, 0, 16, 4, /* IPRD */ { SCIF0, SCIF1, SCIF2 } }, + { 0xf8080004, 0, 16, 4, /* IPRE */ { HIF_HIFI, HIF_HIFBI } }, + { 0xf8080006, 0, 16, 4, /* IPRF */ { DMAC0, DMAC1, DMAC2, DMAC3 } }, + { 0xf8080008, 0, 16, 4, /* IPRG */ { SIOF } }, +}; + +static DECLARE_INTC_DESC(intc_desc, "sh7619", vectors, NULL, + NULL, prio_registers, NULL); + +static struct plat_sci_port scif0_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, +}; + +static struct resource scif0_resources[] = { + DEFINE_RES_MEM(0xf8400000, 0x100), + DEFINE_RES_IRQ(88), +}; + +static struct platform_device scif0_device = { + .name = "sh-sci", + .id = 0, + .resource = scif0_resources, + .num_resources = ARRAY_SIZE(scif0_resources), + .dev = { + .platform_data = &scif0_platform_data, + }, +}; + +static struct plat_sci_port scif1_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, +}; + +static struct resource scif1_resources[] = { + DEFINE_RES_MEM(0xf8410000, 0x100), + DEFINE_RES_IRQ(92), +}; + +static struct platform_device scif1_device = { + .name = "sh-sci", + .id = 1, + .resource = scif1_resources, + .num_resources = ARRAY_SIZE(scif1_resources), + .dev = { + .platform_data = &scif1_platform_data, + }, +}; + +static struct plat_sci_port scif2_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, +}; + +static struct resource scif2_resources[] = { + DEFINE_RES_MEM(0xf8420000, 0x100), + DEFINE_RES_IRQ(96), +}; + +static struct platform_device scif2_device = { + .name = "sh-sci", + .id = 2, + .resource = scif2_resources, + .num_resources = ARRAY_SIZE(scif2_resources), + .dev = { + .platform_data = &scif2_platform_data, + }, +}; + +static struct sh_eth_plat_data eth_platform_data = { + .phy = 1, + .phy_interface = PHY_INTERFACE_MODE_MII, +}; + +static struct resource eth_resources[] = { + [0] = { + .start = 0xfb000000, + .end = 0xfb0001c7, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = 85, + .end = 85, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device eth_device = { + .name = "sh7619-ether", + .id = -1, + .dev = { + .platform_data = ð_platform_data, + }, + .num_resources = ARRAY_SIZE(eth_resources), + .resource = eth_resources, +}; + +static struct sh_timer_config cmt_platform_data = { + .channels_mask = 3, +}; + +static struct resource cmt_resources[] = { + DEFINE_RES_MEM(0xf84a0070, 0x10), + DEFINE_RES_IRQ(86), + DEFINE_RES_IRQ(87), +}; + +static struct platform_device cmt_device = { + .name = "sh-cmt-16", + .id = 0, + .dev = { + .platform_data = &cmt_platform_data, + }, + .resource = cmt_resources, + .num_resources = ARRAY_SIZE(cmt_resources), +}; + +static struct platform_device *sh7619_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + ð_device, + &cmt_device, +}; + +static int __init sh7619_devices_setup(void) +{ + return platform_add_devices(sh7619_devices, + ARRAY_SIZE(sh7619_devices)); +} +arch_initcall(sh7619_devices_setup); + +void __init plat_irq_setup(void) +{ + register_intc_controller(&intc_desc); +} + +static struct platform_device *sh7619_early_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + &cmt_device, +}; + +#define STBCR3 0xf80a0000 + +void __init plat_early_device_setup(void) +{ + /* enable CMT clock */ + __raw_writeb(__raw_readb(STBCR3) & ~0x10, STBCR3); + + early_platform_add_devices(sh7619_early_devices, + ARRAY_SIZE(sh7619_early_devices)); +} diff --git a/arch/sh/kernel/cpu/sh2/smp-j2.c b/arch/sh/kernel/cpu/sh2/smp-j2.c new file mode 100644 index 000000000..6ccd7e4dc --- /dev/null +++ b/arch/sh/kernel/cpu/sh2/smp-j2.c @@ -0,0 +1,139 @@ +/* + * SMP support for J2 processor + * + * Copyright (C) 2015-2016 Smart Energy Instruments, Inc. + * + * 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/smp.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <asm/cmpxchg.h> + +DEFINE_PER_CPU(unsigned, j2_ipi_messages); + +extern u32 *sh2_cpuid_addr; +static u32 *j2_ipi_trigger; +static int j2_ipi_irq; + +static irqreturn_t j2_ipi_interrupt_handler(int irq, void *arg) +{ + unsigned cpu = hard_smp_processor_id(); + volatile unsigned *pmsg = &per_cpu(j2_ipi_messages, cpu); + unsigned messages, i; + + do messages = *pmsg; + while (cmpxchg(pmsg, messages, 0) != messages); + + if (!messages) return IRQ_NONE; + + for (i=0; i<SMP_MSG_NR; i++) + if (messages & (1U<<i)) + smp_message_recv(i); + + return IRQ_HANDLED; +} + +static void j2_smp_setup(void) +{ +} + +static void j2_prepare_cpus(unsigned int max_cpus) +{ + struct device_node *np; + unsigned i, max = 1; + + np = of_find_compatible_node(NULL, NULL, "jcore,ipi-controller"); + if (!np) + goto out; + + j2_ipi_irq = irq_of_parse_and_map(np, 0); + j2_ipi_trigger = of_iomap(np, 0); + if (!j2_ipi_irq || !j2_ipi_trigger) + goto out; + + np = of_find_compatible_node(NULL, NULL, "jcore,cpuid-mmio"); + if (!np) + goto out; + + sh2_cpuid_addr = of_iomap(np, 0); + if (!sh2_cpuid_addr) + goto out; + + if (request_irq(j2_ipi_irq, j2_ipi_interrupt_handler, IRQF_PERCPU, + "ipi", (void *)j2_ipi_interrupt_handler) != 0) + goto out; + + max = max_cpus; +out: + /* Disable any cpus past max_cpus, or all secondaries if we didn't + * get the necessary resources to support SMP. */ + for (i=max; i<NR_CPUS; i++) { + set_cpu_possible(i, false); + set_cpu_present(i, false); + } +} + +static void j2_start_cpu(unsigned int cpu, unsigned long entry_point) +{ + struct device_node *np; + u32 regs[2]; + void __iomem *release, *initpc; + + if (!cpu) return; + + np = of_get_cpu_node(cpu, NULL); + if (!np) return; + + if (of_property_read_u32_array(np, "cpu-release-addr", regs, 2)) return; + release = ioremap_nocache(regs[0], sizeof(u32)); + initpc = ioremap_nocache(regs[1], sizeof(u32)); + + __raw_writel(entry_point, initpc); + __raw_writel(1, release); + + iounmap(initpc); + iounmap(release); + + pr_info("J2 SMP: requested start of cpu %u\n", cpu); +} + +static unsigned int j2_smp_processor_id(void) +{ + return __raw_readl(sh2_cpuid_addr); +} + +static void j2_send_ipi(unsigned int cpu, unsigned int message) +{ + volatile unsigned *pmsg; + unsigned old; + unsigned long val; + + /* There is only one IPI interrupt shared by all messages, so + * we keep a separate interrupt flag per message type in sw. */ + pmsg = &per_cpu(j2_ipi_messages, cpu); + do old = *pmsg; + while (cmpxchg(pmsg, old, old|(1U<<message)) != old); + + /* Generate the actual interrupt by writing to CCRn bit 28. */ + val = __raw_readl(j2_ipi_trigger + cpu); + __raw_writel(val | (1U<<28), j2_ipi_trigger + cpu); +} + +static struct plat_smp_ops j2_smp_ops = { + .smp_setup = j2_smp_setup, + .prepare_cpus = j2_prepare_cpus, + .start_cpu = j2_start_cpu, + .smp_processor_id = j2_smp_processor_id, + .send_ipi = j2_send_ipi, + .cpu_die = native_cpu_die, + .cpu_disable = native_cpu_disable, + .play_dead = native_play_dead, +}; + +CPU_METHOD_OF_DECLARE(j2_cpu_method, "jcore,spin-table", &j2_smp_ops); diff --git a/arch/sh/kernel/cpu/sh2a/Makefile b/arch/sh/kernel/cpu/sh2a/Makefile new file mode 100644 index 000000000..2a7515b65 --- /dev/null +++ b/arch/sh/kernel/cpu/sh2a/Makefile @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the Linux/SuperH SH-2A backends. +# + +obj-y := common.o probe.o opcode_helper.o + +common-y += ex.o entry.o + +obj-$(CONFIG_SH_FPU) += fpu.o + +obj-$(CONFIG_CPU_SUBTYPE_SH7201) += setup-sh7201.o clock-sh7201.o +obj-$(CONFIG_CPU_SUBTYPE_SH7203) += setup-sh7203.o clock-sh7203.o +obj-$(CONFIG_CPU_SUBTYPE_SH7263) += setup-sh7203.o clock-sh7203.o +obj-$(CONFIG_CPU_SUBTYPE_SH7264) += setup-sh7264.o clock-sh7264.o +obj-$(CONFIG_CPU_SUBTYPE_SH7206) += setup-sh7206.o clock-sh7206.o +obj-$(CONFIG_CPU_SUBTYPE_SH7269) += setup-sh7269.o clock-sh7269.o +obj-$(CONFIG_CPU_SUBTYPE_MXG) += setup-mxg.o clock-sh7206.o + +# Pinmux setup +pinmux-$(CONFIG_CPU_SUBTYPE_SH7203) := pinmux-sh7203.o +pinmux-$(CONFIG_CPU_SUBTYPE_SH7264) := pinmux-sh7264.o +pinmux-$(CONFIG_CPU_SUBTYPE_SH7269) := pinmux-sh7269.o + +obj-$(CONFIG_GPIOLIB) += $(pinmux-y) diff --git a/arch/sh/kernel/cpu/sh2a/clock-sh7201.c b/arch/sh/kernel/cpu/sh2a/clock-sh7201.c new file mode 100644 index 000000000..532a36c72 --- /dev/null +++ b/arch/sh/kernel/cpu/sh2a/clock-sh7201.c @@ -0,0 +1,85 @@ +/* + * arch/sh/kernel/cpu/sh2a/clock-sh7201.c + * + * SH7201 support for the clock framework + * + * Copyright (C) 2008 Peter Griffin <pgriffin@mpc-data.co.uk> + * + * Based on clock-sh4.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 const int pll1rate[]={1,2,3,4,6,8}; +static const int pfc_divisors[]={1,2,3,4,6,8,12}; +#define ifc_divisors pfc_divisors + +static unsigned int pll2_mult; + +static void master_clk_init(struct clk *clk) +{ + clk->rate = 10000000 * pll2_mult * + pll1rate[(__raw_readw(FREQCR) >> 8) & 0x0007]; +} + +static struct sh_clk_ops sh7201_master_clk_ops = { + .init = master_clk_init, +}; + +static unsigned long module_clk_recalc(struct clk *clk) +{ + int idx = (__raw_readw(FREQCR) & 0x0007); + return clk->parent->rate / pfc_divisors[idx]; +} + +static struct sh_clk_ops sh7201_module_clk_ops = { + .recalc = module_clk_recalc, +}; + +static unsigned long bus_clk_recalc(struct clk *clk) +{ + int idx = (__raw_readw(FREQCR) & 0x0007); + return clk->parent->rate / pfc_divisors[idx]; +} + +static struct sh_clk_ops sh7201_bus_clk_ops = { + .recalc = bus_clk_recalc, +}; + +static unsigned long cpu_clk_recalc(struct clk *clk) +{ + int idx = ((__raw_readw(FREQCR) >> 4) & 0x0007); + return clk->parent->rate / ifc_divisors[idx]; +} + +static struct sh_clk_ops sh7201_cpu_clk_ops = { + .recalc = cpu_clk_recalc, +}; + +static struct sh_clk_ops *sh7201_clk_ops[] = { + &sh7201_master_clk_ops, + &sh7201_module_clk_ops, + &sh7201_bus_clk_ops, + &sh7201_cpu_clk_ops, +}; + +void __init arch_init_clk_ops(struct sh_clk_ops **ops, int idx) +{ + if (test_mode_pin(MODE_PIN1 | MODE_PIN0)) + pll2_mult = 1; + else if (test_mode_pin(MODE_PIN1)) + pll2_mult = 2; + else + pll2_mult = 4; + + if (idx < ARRAY_SIZE(sh7201_clk_ops)) + *ops = sh7201_clk_ops[idx]; +} diff --git a/arch/sh/kernel/cpu/sh2a/clock-sh7203.c b/arch/sh/kernel/cpu/sh2a/clock-sh7203.c new file mode 100644 index 000000000..529f719b6 --- /dev/null +++ b/arch/sh/kernel/cpu/sh2a/clock-sh7203.c @@ -0,0 +1,81 @@ +/* + * arch/sh/kernel/cpu/sh2a/clock-sh7203.c + * + * SH7203 support for the clock framework + * + * Copyright (C) 2007 Kieran Bingham (MPC-Data Ltd) + * + * Based on clock-sh7263.c + * Copyright (C) 2006 Yoshinori Sato + * + * Based on clock-sh4.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 const int pll1rate[]={8,12,16,0}; +static const int pfc_divisors[]={1,2,3,4,6,8,12}; +#define ifc_divisors pfc_divisors + +static unsigned int pll2_mult; + +static void master_clk_init(struct clk *clk) +{ + clk->rate *= pll1rate[(__raw_readw(FREQCR) >> 8) & 0x0003] * pll2_mult; +} + +static struct sh_clk_ops sh7203_master_clk_ops = { + .init = master_clk_init, +}; + +static unsigned long module_clk_recalc(struct clk *clk) +{ + int idx = (__raw_readw(FREQCR) & 0x0007); + return clk->parent->rate / pfc_divisors[idx]; +} + +static struct sh_clk_ops sh7203_module_clk_ops = { + .recalc = module_clk_recalc, +}; + +static unsigned long bus_clk_recalc(struct clk *clk) +{ + int idx = (__raw_readw(FREQCR) & 0x0007); + return clk->parent->rate / pfc_divisors[idx-2]; +} + +static struct sh_clk_ops sh7203_bus_clk_ops = { + .recalc = bus_clk_recalc, +}; + +static struct sh_clk_ops sh7203_cpu_clk_ops = { + .recalc = followparent_recalc, +}; + +static struct sh_clk_ops *sh7203_clk_ops[] = { + &sh7203_master_clk_ops, + &sh7203_module_clk_ops, + &sh7203_bus_clk_ops, + &sh7203_cpu_clk_ops, +}; + +void __init arch_init_clk_ops(struct sh_clk_ops **ops, int idx) +{ + if (test_mode_pin(MODE_PIN1)) + pll2_mult = 4; + else if (test_mode_pin(MODE_PIN0)) + pll2_mult = 2; + else + pll2_mult = 1; + + if (idx < ARRAY_SIZE(sh7203_clk_ops)) + *ops = sh7203_clk_ops[idx]; +} diff --git a/arch/sh/kernel/cpu/sh2a/clock-sh7206.c b/arch/sh/kernel/cpu/sh2a/clock-sh7206.c new file mode 100644 index 000000000..177789834 --- /dev/null +++ b/arch/sh/kernel/cpu/sh2a/clock-sh7206.c @@ -0,0 +1,83 @@ +/* + * arch/sh/kernel/cpu/sh2a/clock-sh7206.c + * + * SH7206 support for the clock framework + * + * Copyright (C) 2006 Yoshinori Sato + * + * Based on clock-sh4.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 const int pll1rate[]={1,2,3,4,6,8}; +static const int pfc_divisors[]={1,2,3,4,6,8,12}; +#define ifc_divisors pfc_divisors + +static unsigned int pll2_mult; + +static void master_clk_init(struct clk *clk) +{ + clk->rate *= pll2_mult * pll1rate[(__raw_readw(FREQCR) >> 8) & 0x0007]; +} + +static struct sh_clk_ops sh7206_master_clk_ops = { + .init = master_clk_init, +}; + +static unsigned long module_clk_recalc(struct clk *clk) +{ + int idx = (__raw_readw(FREQCR) & 0x0007); + return clk->parent->rate / pfc_divisors[idx]; +} + +static struct sh_clk_ops sh7206_module_clk_ops = { + .recalc = module_clk_recalc, +}; + +static unsigned long bus_clk_recalc(struct clk *clk) +{ + return clk->parent->rate / pll1rate[(__raw_readw(FREQCR) >> 8) & 0x0007]; +} + +static struct sh_clk_ops sh7206_bus_clk_ops = { + .recalc = bus_clk_recalc, +}; + +static unsigned long cpu_clk_recalc(struct clk *clk) +{ + int idx = (__raw_readw(FREQCR) & 0x0007); + return clk->parent->rate / ifc_divisors[idx]; +} + +static struct sh_clk_ops sh7206_cpu_clk_ops = { + .recalc = cpu_clk_recalc, +}; + +static struct sh_clk_ops *sh7206_clk_ops[] = { + &sh7206_master_clk_ops, + &sh7206_module_clk_ops, + &sh7206_bus_clk_ops, + &sh7206_cpu_clk_ops, +}; + +void __init arch_init_clk_ops(struct sh_clk_ops **ops, int idx) +{ + if (test_mode_pin(MODE_PIN2 | MODE_PIN1 | MODE_PIN0)) + pll2_mult = 1; + else if (test_mode_pin(MODE_PIN2 | MODE_PIN1)) + pll2_mult = 2; + else if (test_mode_pin(MODE_PIN1)) + pll2_mult = 4; + + if (idx < ARRAY_SIZE(sh7206_clk_ops)) + *ops = sh7206_clk_ops[idx]; +} diff --git a/arch/sh/kernel/cpu/sh2a/clock-sh7264.c b/arch/sh/kernel/cpu/sh2a/clock-sh7264.c new file mode 100644 index 000000000..7e06e39b0 --- /dev/null +++ b/arch/sh/kernel/cpu/sh2a/clock-sh7264.c @@ -0,0 +1,160 @@ +/* + * arch/sh/kernel/cpu/sh2a/clock-sh7264.c + * + * SH7264 clock framework support + * + * Copyright (C) 2012 Phil Edworthy + * + * 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/io.h> +#include <linux/clkdev.h> +#include <asm/clock.h> + +/* SH7264 registers */ +#define FRQCR 0xfffe0010 +#define STBCR3 0xfffe0408 +#define STBCR4 0xfffe040c +#define STBCR5 0xfffe0410 +#define STBCR6 0xfffe0414 +#define STBCR7 0xfffe0418 +#define STBCR8 0xfffe041c + +static const unsigned int pll1rate[] = {8, 12}; + +static unsigned int pll1_div; + +/* Fixed 32 KHz root clock for RTC */ +static struct clk r_clk = { + .rate = 32768, +}; + +/* + * Default rate for the root input clock, reset this with clk_set_rate() + * from the platform code. + */ +static struct clk extal_clk = { + .rate = 18000000, +}; + +static unsigned long pll_recalc(struct clk *clk) +{ + unsigned long rate = clk->parent->rate / pll1_div; + return rate * pll1rate[(__raw_readw(FRQCR) >> 8) & 1]; +} + +static struct sh_clk_ops pll_clk_ops = { + .recalc = pll_recalc, +}; + +static struct clk pll_clk = { + .ops = &pll_clk_ops, + .parent = &extal_clk, + .flags = CLK_ENABLE_ON_INIT, +}; + +struct clk *main_clks[] = { + &r_clk, + &extal_clk, + &pll_clk, +}; + +static int div2[] = { 1, 2, 3, 4, 6, 8, 12 }; + +static struct clk_div_mult_table div4_div_mult_table = { + .divisors = div2, + .nr_divisors = ARRAY_SIZE(div2), +}; + +static struct clk_div4_table div4_table = { + .div_mult_table = &div4_div_mult_table, +}; + +enum { DIV4_I, DIV4_P, + DIV4_NR }; + +#define DIV4(_reg, _bit, _mask, _flags) \ + SH_CLK_DIV4(&pll_clk, _reg, _bit, _mask, _flags) + +/* The mask field specifies the div2 entries that are valid */ +struct clk div4_clks[DIV4_NR] = { + [DIV4_I] = DIV4(FRQCR, 4, 0x7, CLK_ENABLE_REG_16BIT + | CLK_ENABLE_ON_INIT), + [DIV4_P] = DIV4(FRQCR, 0, 0x78, CLK_ENABLE_REG_16BIT), +}; + +enum { MSTP77, MSTP74, MSTP72, + MSTP60, + MSTP35, MSTP34, MSTP33, MSTP32, MSTP30, + MSTP_NR }; + +static struct clk mstp_clks[MSTP_NR] = { + [MSTP77] = SH_CLK_MSTP8(&div4_clks[DIV4_P], STBCR7, 7, 0), /* SCIF */ + [MSTP74] = SH_CLK_MSTP8(&div4_clks[DIV4_P], STBCR7, 4, 0), /* VDC */ + [MSTP72] = SH_CLK_MSTP8(&div4_clks[DIV4_P], STBCR7, 2, 0), /* CMT */ + [MSTP60] = SH_CLK_MSTP8(&div4_clks[DIV4_P], STBCR6, 0, 0), /* USB */ + [MSTP35] = SH_CLK_MSTP8(&div4_clks[DIV4_P], STBCR3, 6, 0), /* MTU2 */ + [MSTP34] = SH_CLK_MSTP8(&div4_clks[DIV4_P], STBCR3, 4, 0), /* SDHI0 */ + [MSTP33] = SH_CLK_MSTP8(&div4_clks[DIV4_P], STBCR3, 3, 0), /* SDHI1 */ + [MSTP32] = SH_CLK_MSTP8(&div4_clks[DIV4_P], STBCR3, 2, 0), /* ADC */ + [MSTP30] = SH_CLK_MSTP8(&r_clk, STBCR3, 0, 0), /* RTC */ +}; + +static struct clk_lookup lookups[] = { + /* main clocks */ + CLKDEV_CON_ID("rclk", &r_clk), + CLKDEV_CON_ID("extal", &extal_clk), + CLKDEV_CON_ID("pll_clk", &pll_clk), + + /* DIV4 clocks */ + CLKDEV_CON_ID("cpu_clk", &div4_clks[DIV4_I]), + CLKDEV_CON_ID("peripheral_clk", &div4_clks[DIV4_P]), + + /* MSTP clocks */ + CLKDEV_ICK_ID("fck", "sh-sci.0", &mstp_clks[MSTP77]), + CLKDEV_ICK_ID("fck", "sh-sci.1", &mstp_clks[MSTP77]), + CLKDEV_ICK_ID("fck", "sh-sci.2", &mstp_clks[MSTP77]), + CLKDEV_ICK_ID("fck", "sh-sci.3", &mstp_clks[MSTP77]), + CLKDEV_ICK_ID("fck", "sh-sci.4", &mstp_clks[MSTP77]), + CLKDEV_ICK_ID("fck", "sh-sci.5", &mstp_clks[MSTP77]), + CLKDEV_ICK_ID("fck", "sh-sci.6", &mstp_clks[MSTP77]), + CLKDEV_ICK_ID("fck", "sh-sci.7", &mstp_clks[MSTP77]), + CLKDEV_CON_ID("vdc3", &mstp_clks[MSTP74]), + CLKDEV_ICK_ID("fck", "sh-cmt-16.0", &mstp_clks[MSTP72]), + CLKDEV_CON_ID("usb0", &mstp_clks[MSTP60]), + CLKDEV_ICK_ID("fck", "sh-mtu2", &mstp_clks[MSTP35]), + CLKDEV_CON_ID("sdhi0", &mstp_clks[MSTP34]), + CLKDEV_CON_ID("sdhi1", &mstp_clks[MSTP33]), + CLKDEV_CON_ID("adc0", &mstp_clks[MSTP32]), + CLKDEV_CON_ID("rtc0", &mstp_clks[MSTP30]), +}; + +int __init arch_clk_init(void) +{ + int k, ret = 0; + + if (test_mode_pin(MODE_PIN0)) { + if (test_mode_pin(MODE_PIN1)) + pll1_div = 3; + else + pll1_div = 4; + } else + pll1_div = 1; + + for (k = 0; !ret && (k < ARRAY_SIZE(main_clks)); k++) + ret = clk_register(main_clks[k]); + + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); + + if (!ret) + ret = sh_clk_div4_register(div4_clks, DIV4_NR, &div4_table); + + if (!ret) + ret = sh_clk_mstp_register(mstp_clks, MSTP_NR); + + return ret; +} diff --git a/arch/sh/kernel/cpu/sh2a/clock-sh7269.c b/arch/sh/kernel/cpu/sh2a/clock-sh7269.c new file mode 100644 index 000000000..663a97bed --- /dev/null +++ b/arch/sh/kernel/cpu/sh2a/clock-sh7269.c @@ -0,0 +1,184 @@ +/* + * arch/sh/kernel/cpu/sh2a/clock-sh7269.c + * + * SH7269 clock framework support + * + * Copyright (C) 2012 Phil Edworthy + * + * 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/io.h> +#include <linux/clkdev.h> +#include <asm/clock.h> + +/* SH7269 registers */ +#define FRQCR 0xfffe0010 +#define STBCR3 0xfffe0408 +#define STBCR4 0xfffe040c +#define STBCR5 0xfffe0410 +#define STBCR6 0xfffe0414 +#define STBCR7 0xfffe0418 + +#define PLL_RATE 20 + +/* Fixed 32 KHz root clock for RTC */ +static struct clk r_clk = { + .rate = 32768, +}; + +/* + * Default rate for the root input clock, reset this with clk_set_rate() + * from the platform code. + */ +static struct clk extal_clk = { + .rate = 13340000, +}; + +static unsigned long pll_recalc(struct clk *clk) +{ + return clk->parent->rate * PLL_RATE; +} + +static struct sh_clk_ops pll_clk_ops = { + .recalc = pll_recalc, +}; + +static struct clk pll_clk = { + .ops = &pll_clk_ops, + .parent = &extal_clk, + .flags = CLK_ENABLE_ON_INIT, +}; + +static unsigned long peripheral0_recalc(struct clk *clk) +{ + return clk->parent->rate / 8; +} + +static struct sh_clk_ops peripheral0_clk_ops = { + .recalc = peripheral0_recalc, +}; + +static struct clk peripheral0_clk = { + .ops = &peripheral0_clk_ops, + .parent = &pll_clk, + .flags = CLK_ENABLE_ON_INIT, +}; + +static unsigned long peripheral1_recalc(struct clk *clk) +{ + return clk->parent->rate / 4; +} + +static struct sh_clk_ops peripheral1_clk_ops = { + .recalc = peripheral1_recalc, +}; + +static struct clk peripheral1_clk = { + .ops = &peripheral1_clk_ops, + .parent = &pll_clk, + .flags = CLK_ENABLE_ON_INIT, +}; + +struct clk *main_clks[] = { + &r_clk, + &extal_clk, + &pll_clk, + &peripheral0_clk, + &peripheral1_clk, +}; + +static int div2[] = { 1, 2, 0, 4 }; + +static struct clk_div_mult_table div4_div_mult_table = { + .divisors = div2, + .nr_divisors = ARRAY_SIZE(div2), +}; + +static struct clk_div4_table div4_table = { + .div_mult_table = &div4_div_mult_table, +}; + +enum { DIV4_I, DIV4_B, + DIV4_NR }; + +#define DIV4(_reg, _bit, _mask, _flags) \ + SH_CLK_DIV4(&pll_clk, _reg, _bit, _mask, _flags) + +/* The mask field specifies the div2 entries that are valid */ +struct clk div4_clks[DIV4_NR] = { + [DIV4_I] = DIV4(FRQCR, 8, 0xB, CLK_ENABLE_REG_16BIT + | CLK_ENABLE_ON_INIT), + [DIV4_B] = DIV4(FRQCR, 4, 0xA, CLK_ENABLE_REG_16BIT + | CLK_ENABLE_ON_INIT), +}; + +enum { MSTP72, + MSTP60, + MSTP47, MSTP46, MSTP45, MSTP44, MSTP43, MSTP42, MSTP41, MSTP40, + MSTP35, MSTP32, MSTP30, + MSTP_NR }; + +static struct clk mstp_clks[MSTP_NR] = { + [MSTP72] = SH_CLK_MSTP8(&peripheral0_clk, STBCR7, 2, 0), /* CMT */ + [MSTP60] = SH_CLK_MSTP8(&peripheral1_clk, STBCR6, 0, 0), /* USB */ + [MSTP47] = SH_CLK_MSTP8(&peripheral1_clk, STBCR4, 7, 0), /* SCIF0 */ + [MSTP46] = SH_CLK_MSTP8(&peripheral1_clk, STBCR4, 6, 0), /* SCIF1 */ + [MSTP45] = SH_CLK_MSTP8(&peripheral1_clk, STBCR4, 5, 0), /* SCIF2 */ + [MSTP44] = SH_CLK_MSTP8(&peripheral1_clk, STBCR4, 4, 0), /* SCIF3 */ + [MSTP43] = SH_CLK_MSTP8(&peripheral1_clk, STBCR4, 3, 0), /* SCIF4 */ + [MSTP42] = SH_CLK_MSTP8(&peripheral1_clk, STBCR4, 2, 0), /* SCIF5 */ + [MSTP41] = SH_CLK_MSTP8(&peripheral1_clk, STBCR4, 1, 0), /* SCIF6 */ + [MSTP40] = SH_CLK_MSTP8(&peripheral1_clk, STBCR4, 0, 0), /* SCIF7 */ + [MSTP35] = SH_CLK_MSTP8(&peripheral0_clk, STBCR3, 5, 0), /* MTU2 */ + [MSTP32] = SH_CLK_MSTP8(&peripheral1_clk, STBCR3, 2, 0), /* ADC */ + [MSTP30] = SH_CLK_MSTP8(&r_clk, STBCR3, 0, 0), /* RTC */ +}; + +static struct clk_lookup lookups[] = { + /* main clocks */ + CLKDEV_CON_ID("rclk", &r_clk), + CLKDEV_CON_ID("extal", &extal_clk), + CLKDEV_CON_ID("pll_clk", &pll_clk), + CLKDEV_CON_ID("peripheral_clk", &peripheral1_clk), + + /* DIV4 clocks */ + CLKDEV_CON_ID("cpu_clk", &div4_clks[DIV4_I]), + CLKDEV_CON_ID("bus_clk", &div4_clks[DIV4_B]), + + /* MSTP clocks */ + CLKDEV_ICK_ID("fck", "sh-sci.0", &mstp_clks[MSTP47]), + CLKDEV_ICK_ID("fck", "sh-sci.1", &mstp_clks[MSTP46]), + CLKDEV_ICK_ID("fck", "sh-sci.2", &mstp_clks[MSTP45]), + CLKDEV_ICK_ID("fck", "sh-sci.3", &mstp_clks[MSTP44]), + CLKDEV_ICK_ID("fck", "sh-sci.4", &mstp_clks[MSTP43]), + CLKDEV_ICK_ID("fck", "sh-sci.5", &mstp_clks[MSTP42]), + CLKDEV_ICK_ID("fck", "sh-sci.6", &mstp_clks[MSTP41]), + CLKDEV_ICK_ID("fck", "sh-sci.7", &mstp_clks[MSTP40]), + CLKDEV_ICK_ID("fck", "sh-cmt-16.0", &mstp_clks[MSTP72]), + CLKDEV_CON_ID("usb0", &mstp_clks[MSTP60]), + CLKDEV_ICK_ID("fck", "sh-mtu2", &mstp_clks[MSTP35]), + CLKDEV_CON_ID("adc0", &mstp_clks[MSTP32]), + CLKDEV_CON_ID("rtc0", &mstp_clks[MSTP30]), +}; + +int __init arch_clk_init(void) +{ + int k, ret = 0; + + for (k = 0; !ret && (k < ARRAY_SIZE(main_clks)); k++) + ret = clk_register(main_clks[k]); + + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); + + if (!ret) + ret = sh_clk_div4_register(div4_clks, DIV4_NR, &div4_table); + + if (!ret) + ret = sh_clk_mstp_register(mstp_clks, MSTP_NR); + + return ret; +} diff --git a/arch/sh/kernel/cpu/sh2a/entry.S b/arch/sh/kernel/cpu/sh2a/entry.S new file mode 100644 index 000000000..da77a8ef4 --- /dev/null +++ b/arch/sh/kernel/cpu/sh2a/entry.S @@ -0,0 +1,250 @@ +/* + * arch/sh/kernel/cpu/sh2a/entry.S + * + * The SH-2A exception entry + * + * Copyright (C) 2008 Yoshinori Sato + * Based on arch/sh/kernel/cpu/sh2/entry.S + * + * 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/linkage.h> +#include <asm/asm-offsets.h> +#include <asm/thread_info.h> +#include <cpu/mmu_context.h> +#include <asm/unistd.h> +#include <asm/errno.h> +#include <asm/page.h> + +/* Offsets to the stack */ +OFF_R0 = 0 /* Return value. New ABI also arg4 */ +OFF_R1 = 4 /* New ABI: arg5 */ +OFF_R2 = 8 /* New ABI: arg6 */ +OFF_R3 = 12 /* New ABI: syscall_nr */ +OFF_R4 = 16 /* New ABI: arg0 */ +OFF_R5 = 20 /* New ABI: arg1 */ +OFF_R6 = 24 /* New ABI: arg2 */ +OFF_R7 = 28 /* New ABI: arg3 */ +OFF_SP = (15*4) +OFF_PC = (16*4) +OFF_SR = (16*4+2*4) +OFF_TRA = (16*4+6*4) + +#include <asm/entry-macros.S> + +ENTRY(exception_handler) + ! stack + ! r0 <- point sp + ! r1 + ! pc + ! sr + ! r0 = temporary + ! r1 = vector (pseudo EXPEVT / INTEVT / TRA) + mov.l r2,@-sp + cli + mov.l $cpu_mode,r2 + bld.b #6,@(0,r2) !previus SR.MD + bst.b #6,@(4*4,r15) !set cpu mode to SR.MD + bt 1f + ! switch to kernel mode + bset.b #6,@(0,r2) !set SR.MD + mov.l $current_thread_info,r2 + mov.l @r2,r2 + mov #(THREAD_SIZE >> 8),r0 + shll8 r0 + add r2,r0 ! r0 = kernel stack tail + mov r15,r2 ! r2 = user stack top + mov r0,r15 ! switch kernel stack + mov.l r1,@-r15 ! TRA + sts.l macl, @-r15 + sts.l mach, @-r15 + stc.l gbr, @-r15 + mov.l @(4*4,r2),r0 + mov.l r0,@-r15 ! original SR + sts.l pr,@-r15 + mov.l @(3*4,r2),r0 + mov.l r0,@-r15 ! original PC + mov r2,r0 + add #(3+2)*4,r0 ! rewind r0 - r3 + exception frame + lds r0,pr ! pr = original SP + movmu.l r3,@-r15 ! save regs + mov r2,r8 ! r8 = previus stack top + mov r1,r9 ! r9 = interrupt vector + ! restore previous stack + mov.l @r8+,r2 + mov.l @r8+,r0 + mov.l @r8+,r1 + bra 2f + movml.l r2,@-r15 +1: + ! in kernel exception + mov r15,r2 + add #-((OFF_TRA + 4) - OFF_PC) + 5*4,r15 + movmu.l r3,@-r15 + mov r2,r8 ! r8 = previous stack top + mov r1,r9 ! r9 = interrupt vector + ! restore exception frame & regs + mov.l @r8+,r2 ! old R2 + mov.l @r8+,r0 ! old R0 + mov.l @r8+,r1 ! old R1 + mov.l @r8+,r10 ! old PC + mov.l @r8+,r11 ! old SR + movml.l r2,@-r15 + mov.l r10,@(OFF_PC,r15) + mov.l r11,@(OFF_SR,r15) + mov.l r8,@(OFF_SP,r15) ! save old sp + mov r15,r8 + add #OFF_TRA + 4,r8 + mov.l r9,@-r8 + sts.l macl,@-r8 + sts.l mach,@-r8 + stc.l gbr,@-r8 + add #-4,r8 + sts.l pr,@-r8 +2: + ! dispatch exception / interrupt + mov #64,r8 + cmp/hs r8,r9 + bt interrupt_entry ! vec >= 64 is interrupt + mov #31,r8 + cmp/hs r8,r9 + bt trap_entry ! 64 > vec >= 31 is trap + + mov.l 4f,r8 + mov r9,r4 + shll2 r9 + add r9,r8 + mov.l @r8,r8 ! exception handler address + tst r8,r8 + bf 3f + mov.l 8f,r8 ! unhandled exception +3: + mov.l 5f,r10 + jmp @r8 + lds r10,pr + +interrupt_entry: + mov r9,r4 + mov r15,r5 + mov.l 7f,r8 + mov.l 6f,r9 + jmp @r8 + lds r9,pr + + .align 2 +4: .long exception_handling_table +5: .long ret_from_exception +6: .long ret_from_irq +7: .long do_IRQ +8: .long exception_error + +trap_entry: + mov #0x30,r8 + cmp/ge r8,r9 ! vector 0x1f-0x2f is systemcall + bt 1f + mov #0x1f,r9 ! convert to unified SH2/3/4 trap number +1: + shll2 r9 ! TRA + bra system_call ! jump common systemcall entry + mov r9,r8 + +#if defined(CONFIG_SH_STANDARD_BIOS) + /* Unwind the stack and jmp to the debug entry */ +ENTRY(sh_bios_handler) + mov r15,r0 + add #(22-4)*4-4,r0 + ldc.l @r0+,gbr + lds.l @r0+,mach + lds.l @r0+,macl + mov r15,r0 + mov.l @(OFF_SP,r0),r1 + mov.l @(OFF_SR,r2),r3 + mov.l r3,@-r1 + mov.l @(OFF_SP,r2),r3 + mov.l r3,@-r1 + mov r15,r0 + add #(22-4)*4-8,r0 + mov.l 1f,r2 + mov.l @r2,r2 + stc sr,r3 + mov.l r2,@r0 + mov.l r3,@(4,r0) + mov.l r1,@(8,r0) + movml.l @r15+,r14 + add #8,r15 + lds.l @r15+, pr + mov.l @r15+,r15 + rte + nop + .align 2 +1: .long gdb_vbr_vector +#endif /* CONFIG_SH_STANDARD_BIOS */ + +ENTRY(address_error_trap_handler) + mov r15,r4 ! regs + mov.l @(OFF_PC,r15),r6 ! pc + mov.l 1f,r0 + jmp @r0 + mov #0,r5 ! writeaccess is unknown + + .align 2 +1: .long do_address_error + +restore_all: + stc sr,r0 + or #0xf0,r0 + ldc r0,sr ! all interrupt block (same BL = 1) + ! restore special register + ! overlap exception frame + mov r15,r0 + add #17*4,r0 + lds.l @r0+,pr + add #4,r0 + ldc.l @r0+,gbr + lds.l @r0+,mach + lds.l @r0+,macl + mov r15,r0 + mov.l $cpu_mode,r2 + bld.b #6,@(OFF_SR,r15) + bst.b #6,@(0,r2) ! save CPU mode + mov.l @(OFF_SR,r0),r1 + shll2 r1 + shlr2 r1 ! clear MD bit + mov.l @(OFF_SP,r0),r2 + add #-8,r2 + mov.l r2,@(OFF_SP,r0) ! point exception frame top + mov.l r1,@(4,r2) ! set sr + mov.l @(OFF_PC,r0),r1 + mov.l r1,@r2 ! set pc + get_current_thread_info r0, r1 + mov.l $current_thread_info,r1 + mov.l r0,@r1 + movml.l @r15+,r14 + mov.l @r15,r15 + rte + nop + + .align 2 +$current_thread_info: + .long __current_thread_info +$cpu_mode: + .long __cpu_mode + +! common exception handler +#include "../../entry-common.S" + + .data +! cpu operation mode +! bit30 = MD (compatible SH3/4) +__cpu_mode: + .long 0x40000000 + + .section .bss +__current_thread_info: + .long 0 + +ENTRY(exception_handling_table) + .space 4*32 diff --git a/arch/sh/kernel/cpu/sh2a/ex.S b/arch/sh/kernel/cpu/sh2a/ex.S new file mode 100644 index 000000000..456806670 --- /dev/null +++ b/arch/sh/kernel/cpu/sh2a/ex.S @@ -0,0 +1,73 @@ +/* + * arch/sh/kernel/cpu/sh2a/ex.S + * + * The SH-2A exception vector table + * + * Copyright (C) 2008 Yoshinori Sato + * + * 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/linkage.h> + +! +! convert Exception Vector to Exception Number +! + +! exception no 0 to 255 +exception_entry0: +no = 0 + .rept 256 + mov.l r1,@-sp + bra exception_trampoline0 + mov #no,r1 +no = no + 1 + .endr +exception_trampoline0: + mov.l r0,@-sp + mov.l 1f,r0 + extu.b r1,r1 + jmp @r0 + extu.w r1,r1 + + .align 2 +1: .long exception_handler + +! exception no 256 to 511 +exception_entry1: +no = 0 + .rept 256 + mov.l r1,@-sp + bra exception_trampoline1 + mov #no,r1 +no = no + 1 + .endr +exception_trampoline1: + mov.l r0,@-sp + extu.b r1,r1 + movi20 #0x100,r0 + add r0,r1 + mov.l 1f,r0 + jmp @r0 + extu.w r1,r1 + + .align 2 +1: .long exception_handler + + ! +! Exception Vector Base +! + .align 2 +ENTRY(vbr_base) +vector = 0 + .rept 256 + .long exception_entry0 + vector * 6 +vector = vector + 1 + .endr +vector = 0 + .rept 256 + .long exception_entry1 + vector * 6 +vector = vector + 1 + .endr diff --git a/arch/sh/kernel/cpu/sh2a/fpu.c b/arch/sh/kernel/cpu/sh2a/fpu.c new file mode 100644 index 000000000..352f894be --- /dev/null +++ b/arch/sh/kernel/cpu/sh2a/fpu.c @@ -0,0 +1,575 @@ +/* + * Save/restore floating point context for signal handlers. + * + * Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka + * + * 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. + * + * FIXME! These routines can be optimized in big endian case. + */ +#include <linux/sched/signal.h> +#include <linux/signal.h> +#include <asm/processor.h> +#include <asm/io.h> +#include <asm/fpu.h> +#include <asm/traps.h> + +/* The PR (precision) bit in the FP Status Register must be clear when + * an frchg instruction is executed, otherwise the instruction is undefined. + * Executing frchg with PR set causes a trap on some SH4 implementations. + */ + +#define FPSCR_RCHG 0x00000000 + + +/* + * Save FPU registers onto task structure. + */ +void save_fpu(struct task_struct *tsk) +{ + unsigned long dummy; + + enable_fpu(); + asm volatile("sts.l fpul, @-%0\n\t" + "sts.l fpscr, @-%0\n\t" + "fmov.s fr15, @-%0\n\t" + "fmov.s fr14, @-%0\n\t" + "fmov.s fr13, @-%0\n\t" + "fmov.s fr12, @-%0\n\t" + "fmov.s fr11, @-%0\n\t" + "fmov.s fr10, @-%0\n\t" + "fmov.s fr9, @-%0\n\t" + "fmov.s fr8, @-%0\n\t" + "fmov.s fr7, @-%0\n\t" + "fmov.s fr6, @-%0\n\t" + "fmov.s fr5, @-%0\n\t" + "fmov.s fr4, @-%0\n\t" + "fmov.s fr3, @-%0\n\t" + "fmov.s fr2, @-%0\n\t" + "fmov.s fr1, @-%0\n\t" + "fmov.s fr0, @-%0\n\t" + "lds %3, fpscr\n\t" + : "=r" (dummy) + : "0" ((char *)(&tsk->thread.xstate->hardfpu.status)), + "r" (FPSCR_RCHG), + "r" (FPSCR_INIT) + : "memory"); + + disable_fpu(); +} + +void restore_fpu(struct task_struct *tsk) +{ + unsigned long dummy; + + enable_fpu(); + asm volatile("fmov.s @%0+, fr0\n\t" + "fmov.s @%0+, fr1\n\t" + "fmov.s @%0+, fr2\n\t" + "fmov.s @%0+, fr3\n\t" + "fmov.s @%0+, fr4\n\t" + "fmov.s @%0+, fr5\n\t" + "fmov.s @%0+, fr6\n\t" + "fmov.s @%0+, fr7\n\t" + "fmov.s @%0+, fr8\n\t" + "fmov.s @%0+, fr9\n\t" + "fmov.s @%0+, fr10\n\t" + "fmov.s @%0+, fr11\n\t" + "fmov.s @%0+, fr12\n\t" + "fmov.s @%0+, fr13\n\t" + "fmov.s @%0+, fr14\n\t" + "fmov.s @%0+, fr15\n\t" + "lds.l @%0+, fpscr\n\t" + "lds.l @%0+, fpul\n\t" + : "=r" (dummy) + : "0" (tsk->thread.xstate), "r" (FPSCR_RCHG) + : "memory"); + disable_fpu(); +} + +/* + * Emulate arithmetic ops on denormalized number for some FPU insns. + */ + +/* denormalized float * float */ +static int denormal_mulf(int hx, int hy) +{ + unsigned int ix, iy; + unsigned long long m, n; + int exp, w; + + ix = hx & 0x7fffffff; + iy = hy & 0x7fffffff; + if (iy < 0x00800000 || ix == 0) + return ((hx ^ hy) & 0x80000000); + + exp = (iy & 0x7f800000) >> 23; + ix &= 0x007fffff; + iy = (iy & 0x007fffff) | 0x00800000; + m = (unsigned long long)ix * iy; + n = m; + w = -1; + while (n) { n >>= 1; w++; } + + /* FIXME: use guard bits */ + exp += w - 126 - 46; + if (exp > 0) + ix = ((int) (m >> (w - 23)) & 0x007fffff) | (exp << 23); + else if (exp + 22 >= 0) + ix = (int) (m >> (w - 22 - exp)) & 0x007fffff; + else + ix = 0; + + ix |= (hx ^ hy) & 0x80000000; + return ix; +} + +/* denormalized double * double */ +static void mult64(unsigned long long x, unsigned long long y, + unsigned long long *highp, unsigned long long *lowp) +{ + unsigned long long sub0, sub1, sub2, sub3; + unsigned long long high, low; + + sub0 = (x >> 32) * (unsigned long) (y >> 32); + sub1 = (x & 0xffffffffLL) * (unsigned long) (y >> 32); + sub2 = (x >> 32) * (unsigned long) (y & 0xffffffffLL); + sub3 = (x & 0xffffffffLL) * (unsigned long) (y & 0xffffffffLL); + low = sub3; + high = 0LL; + sub3 += (sub1 << 32); + if (low > sub3) + high++; + low = sub3; + sub3 += (sub2 << 32); + if (low > sub3) + high++; + low = sub3; + high += (sub1 >> 32) + (sub2 >> 32); + high += sub0; + *lowp = low; + *highp = high; +} + +static inline long long rshift64(unsigned long long mh, + unsigned long long ml, int n) +{ + if (n >= 64) + return mh >> (n - 64); + return (mh << (64 - n)) | (ml >> n); +} + +static long long denormal_muld(long long hx, long long hy) +{ + unsigned long long ix, iy; + unsigned long long mh, ml, nh, nl; + int exp, w; + + ix = hx & 0x7fffffffffffffffLL; + iy = hy & 0x7fffffffffffffffLL; + if (iy < 0x0010000000000000LL || ix == 0) + return ((hx ^ hy) & 0x8000000000000000LL); + + exp = (iy & 0x7ff0000000000000LL) >> 52; + ix &= 0x000fffffffffffffLL; + iy = (iy & 0x000fffffffffffffLL) | 0x0010000000000000LL; + mult64(ix, iy, &mh, &ml); + nh = mh; + nl = ml; + w = -1; + if (nh) { + while (nh) { nh >>= 1; w++;} + w += 64; + } else + while (nl) { nl >>= 1; w++;} + + /* FIXME: use guard bits */ + exp += w - 1022 - 52 * 2; + if (exp > 0) + ix = (rshift64(mh, ml, w - 52) & 0x000fffffffffffffLL) + | ((long long)exp << 52); + else if (exp + 51 >= 0) + ix = rshift64(mh, ml, w - 51 - exp) & 0x000fffffffffffffLL; + else + ix = 0; + + ix |= (hx ^ hy) & 0x8000000000000000LL; + return ix; +} + +/* ix - iy where iy: denormal and ix, iy >= 0 */ +static int denormal_subf1(unsigned int ix, unsigned int iy) +{ + int frac; + int exp; + + if (ix < 0x00800000) + return ix - iy; + + exp = (ix & 0x7f800000) >> 23; + if (exp - 1 > 31) + return ix; + iy >>= exp - 1; + if (iy == 0) + return ix; + + frac = (ix & 0x007fffff) | 0x00800000; + frac -= iy; + while (frac < 0x00800000) { + if (--exp == 0) + return frac; + frac <<= 1; + } + + return (exp << 23) | (frac & 0x007fffff); +} + +/* ix + iy where iy: denormal and ix, iy >= 0 */ +static int denormal_addf1(unsigned int ix, unsigned int iy) +{ + int frac; + int exp; + + if (ix < 0x00800000) + return ix + iy; + + exp = (ix & 0x7f800000) >> 23; + if (exp - 1 > 31) + return ix; + iy >>= exp - 1; + if (iy == 0) + return ix; + + frac = (ix & 0x007fffff) | 0x00800000; + frac += iy; + if (frac >= 0x01000000) { + frac >>= 1; + ++exp; + } + + return (exp << 23) | (frac & 0x007fffff); +} + +static int denormal_addf(int hx, int hy) +{ + unsigned int ix, iy; + int sign; + + if ((hx ^ hy) & 0x80000000) { + sign = hx & 0x80000000; + ix = hx & 0x7fffffff; + iy = hy & 0x7fffffff; + if (iy < 0x00800000) { + ix = denormal_subf1(ix, iy); + if ((int) ix < 0) { + ix = -ix; + sign ^= 0x80000000; + } + } else { + ix = denormal_subf1(iy, ix); + sign ^= 0x80000000; + } + } else { + sign = hx & 0x80000000; + ix = hx & 0x7fffffff; + iy = hy & 0x7fffffff; + if (iy < 0x00800000) + ix = denormal_addf1(ix, iy); + else + ix = denormal_addf1(iy, ix); + } + + return sign | ix; +} + +/* ix - iy where iy: denormal and ix, iy >= 0 */ +static long long denormal_subd1(unsigned long long ix, unsigned long long iy) +{ + long long frac; + int exp; + + if (ix < 0x0010000000000000LL) + return ix - iy; + + exp = (ix & 0x7ff0000000000000LL) >> 52; + if (exp - 1 > 63) + return ix; + iy >>= exp - 1; + if (iy == 0) + return ix; + + frac = (ix & 0x000fffffffffffffLL) | 0x0010000000000000LL; + frac -= iy; + while (frac < 0x0010000000000000LL) { + if (--exp == 0) + return frac; + frac <<= 1; + } + + return ((long long)exp << 52) | (frac & 0x000fffffffffffffLL); +} + +/* ix + iy where iy: denormal and ix, iy >= 0 */ +static long long denormal_addd1(unsigned long long ix, unsigned long long iy) +{ + long long frac; + long long exp; + + if (ix < 0x0010000000000000LL) + return ix + iy; + + exp = (ix & 0x7ff0000000000000LL) >> 52; + if (exp - 1 > 63) + return ix; + iy >>= exp - 1; + if (iy == 0) + return ix; + + frac = (ix & 0x000fffffffffffffLL) | 0x0010000000000000LL; + frac += iy; + if (frac >= 0x0020000000000000LL) { + frac >>= 1; + ++exp; + } + + return (exp << 52) | (frac & 0x000fffffffffffffLL); +} + +static long long denormal_addd(long long hx, long long hy) +{ + unsigned long long ix, iy; + long long sign; + + if ((hx ^ hy) & 0x8000000000000000LL) { + sign = hx & 0x8000000000000000LL; + ix = hx & 0x7fffffffffffffffLL; + iy = hy & 0x7fffffffffffffffLL; + if (iy < 0x0010000000000000LL) { + ix = denormal_subd1(ix, iy); + if ((int) ix < 0) { + ix = -ix; + sign ^= 0x8000000000000000LL; + } + } else { + ix = denormal_subd1(iy, ix); + sign ^= 0x8000000000000000LL; + } + } else { + sign = hx & 0x8000000000000000LL; + ix = hx & 0x7fffffffffffffffLL; + iy = hy & 0x7fffffffffffffffLL; + if (iy < 0x0010000000000000LL) + ix = denormal_addd1(ix, iy); + else + ix = denormal_addd1(iy, ix); + } + + return sign | ix; +} + +/** + * denormal_to_double - Given denormalized float number, + * store double float + * + * @fpu: Pointer to sh_fpu_hard structure + * @n: Index to FP register + */ +static void +denormal_to_double (struct sh_fpu_hard_struct *fpu, int n) +{ + unsigned long du, dl; + unsigned long x = fpu->fpul; + int exp = 1023 - 126; + + if (x != 0 && (x & 0x7f800000) == 0) { + du = (x & 0x80000000); + while ((x & 0x00800000) == 0) { + x <<= 1; + exp--; + } + x &= 0x007fffff; + du |= (exp << 20) | (x >> 3); + dl = x << 29; + + fpu->fp_regs[n] = du; + fpu->fp_regs[n+1] = dl; + } +} + +/** + * ieee_fpe_handler - Handle denormalized number exception + * + * @regs: Pointer to register structure + * + * Returns 1 when it's handled (should not cause exception). + */ +static int +ieee_fpe_handler (struct pt_regs *regs) +{ + unsigned short insn = *(unsigned short *) regs->pc; + unsigned short finsn; + unsigned long nextpc; + int nib[4] = { + (insn >> 12) & 0xf, + (insn >> 8) & 0xf, + (insn >> 4) & 0xf, + insn & 0xf}; + + if (nib[0] == 0xb || + (nib[0] == 0x4 && nib[2] == 0x0 && nib[3] == 0xb)) /* bsr & jsr */ + regs->pr = regs->pc + 4; + if (nib[0] == 0xa || nib[0] == 0xb) { /* bra & bsr */ + nextpc = regs->pc + 4 + ((short) ((insn & 0xfff) << 4) >> 3); + finsn = *(unsigned short *) (regs->pc + 2); + } else if (nib[0] == 0x8 && nib[1] == 0xd) { /* bt/s */ + if (regs->sr & 1) + nextpc = regs->pc + 4 + ((char) (insn & 0xff) << 1); + else + nextpc = regs->pc + 4; + finsn = *(unsigned short *) (regs->pc + 2); + } else if (nib[0] == 0x8 && nib[1] == 0xf) { /* bf/s */ + if (regs->sr & 1) + nextpc = regs->pc + 4; + else + nextpc = regs->pc + 4 + ((char) (insn & 0xff) << 1); + finsn = *(unsigned short *) (regs->pc + 2); + } else if (nib[0] == 0x4 && nib[3] == 0xb && + (nib[2] == 0x0 || nib[2] == 0x2)) { /* jmp & jsr */ + nextpc = regs->regs[nib[1]]; + finsn = *(unsigned short *) (regs->pc + 2); + } else if (nib[0] == 0x0 && nib[3] == 0x3 && + (nib[2] == 0x0 || nib[2] == 0x2)) { /* braf & bsrf */ + nextpc = regs->pc + 4 + regs->regs[nib[1]]; + finsn = *(unsigned short *) (regs->pc + 2); + } else if (insn == 0x000b) { /* rts */ + nextpc = regs->pr; + finsn = *(unsigned short *) (regs->pc + 2); + } else { + nextpc = regs->pc + 2; + finsn = insn; + } + +#define FPSCR_FPU_ERROR (1 << 17) + + if ((finsn & 0xf1ff) == 0xf0ad) { /* fcnvsd */ + struct task_struct *tsk = current; + + if ((tsk->thread.xstate->hardfpu.fpscr & FPSCR_FPU_ERROR)) { + /* FPU error */ + denormal_to_double (&tsk->thread.xstate->hardfpu, + (finsn >> 8) & 0xf); + } else + return 0; + + regs->pc = nextpc; + return 1; + } else if ((finsn & 0xf00f) == 0xf002) { /* fmul */ + struct task_struct *tsk = current; + int fpscr; + int n, m, prec; + unsigned int hx, hy; + + n = (finsn >> 8) & 0xf; + m = (finsn >> 4) & 0xf; + hx = tsk->thread.xstate->hardfpu.fp_regs[n]; + hy = tsk->thread.xstate->hardfpu.fp_regs[m]; + fpscr = tsk->thread.xstate->hardfpu.fpscr; + prec = fpscr & (1 << 19); + + if ((fpscr & FPSCR_FPU_ERROR) + && (prec && ((hx & 0x7fffffff) < 0x00100000 + || (hy & 0x7fffffff) < 0x00100000))) { + long long llx, lly; + + /* FPU error because of denormal */ + llx = ((long long) hx << 32) + | tsk->thread.xstate->hardfpu.fp_regs[n+1]; + lly = ((long long) hy << 32) + | tsk->thread.xstate->hardfpu.fp_regs[m+1]; + if ((hx & 0x7fffffff) >= 0x00100000) + llx = denormal_muld(lly, llx); + else + llx = denormal_muld(llx, lly); + tsk->thread.xstate->hardfpu.fp_regs[n] = llx >> 32; + tsk->thread.xstate->hardfpu.fp_regs[n+1] = llx & 0xffffffff; + } else if ((fpscr & FPSCR_FPU_ERROR) + && (!prec && ((hx & 0x7fffffff) < 0x00800000 + || (hy & 0x7fffffff) < 0x00800000))) { + /* FPU error because of denormal */ + if ((hx & 0x7fffffff) >= 0x00800000) + hx = denormal_mulf(hy, hx); + else + hx = denormal_mulf(hx, hy); + tsk->thread.xstate->hardfpu.fp_regs[n] = hx; + } else + return 0; + + regs->pc = nextpc; + return 1; + } else if ((finsn & 0xf00e) == 0xf000) { /* fadd, fsub */ + struct task_struct *tsk = current; + int fpscr; + int n, m, prec; + unsigned int hx, hy; + + n = (finsn >> 8) & 0xf; + m = (finsn >> 4) & 0xf; + hx = tsk->thread.xstate->hardfpu.fp_regs[n]; + hy = tsk->thread.xstate->hardfpu.fp_regs[m]; + fpscr = tsk->thread.xstate->hardfpu.fpscr; + prec = fpscr & (1 << 19); + + if ((fpscr & FPSCR_FPU_ERROR) + && (prec && ((hx & 0x7fffffff) < 0x00100000 + || (hy & 0x7fffffff) < 0x00100000))) { + long long llx, lly; + + /* FPU error because of denormal */ + llx = ((long long) hx << 32) + | tsk->thread.xstate->hardfpu.fp_regs[n+1]; + lly = ((long long) hy << 32) + | tsk->thread.xstate->hardfpu.fp_regs[m+1]; + if ((finsn & 0xf00f) == 0xf000) + llx = denormal_addd(llx, lly); + else + llx = denormal_addd(llx, lly ^ (1LL << 63)); + tsk->thread.xstate->hardfpu.fp_regs[n] = llx >> 32; + tsk->thread.xstate->hardfpu.fp_regs[n+1] = llx & 0xffffffff; + } else if ((fpscr & FPSCR_FPU_ERROR) + && (!prec && ((hx & 0x7fffffff) < 0x00800000 + || (hy & 0x7fffffff) < 0x00800000))) { + /* FPU error because of denormal */ + if ((finsn & 0xf00f) == 0xf000) + hx = denormal_addf(hx, hy); + else + hx = denormal_addf(hx, hy ^ 0x80000000); + tsk->thread.xstate->hardfpu.fp_regs[n] = hx; + } else + return 0; + + regs->pc = nextpc; + return 1; + } + + return 0; +} + +BUILD_TRAP_HANDLER(fpu_error) +{ + struct task_struct *tsk = current; + TRAP_HANDLER_DECL; + + __unlazy_fpu(tsk, regs); + if (ieee_fpe_handler(regs)) { + tsk->thread.xstate->hardfpu.fpscr &= + ~(FPSCR_CAUSE_MASK | FPSCR_FLAG_MASK); + grab_fpu(regs); + restore_fpu(tsk); + task_thread_info(tsk)->status |= TS_USEDFPU; + return; + } + + force_sig(SIGFPE, tsk); +} diff --git a/arch/sh/kernel/cpu/sh2a/opcode_helper.c b/arch/sh/kernel/cpu/sh2a/opcode_helper.c new file mode 100644 index 000000000..72aa61c81 --- /dev/null +++ b/arch/sh/kernel/cpu/sh2a/opcode_helper.c @@ -0,0 +1,54 @@ +/* + * arch/sh/kernel/cpu/sh2a/opcode_helper.c + * + * Helper for the SH-2A 32-bit opcodes. + * + * Copyright (C) 2007 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/kernel.h> + +/* + * Instructions on SH are generally fixed at 16-bits, however, SH-2A + * introduces some 32-bit instructions. Since there are no real + * constraints on their use (and they can be mixed and matched), we need + * to check the instruction encoding to work out if it's a true 32-bit + * instruction or not. + * + * Presently, 32-bit opcodes have only slight variations in what the + * actual encoding looks like in the first-half of the instruction, which + * makes it fairly straightforward to differentiate from the 16-bit ones. + * + * First 16-bits of encoding Used by + * + * 0011nnnnmmmm0001 mov.b, mov.w, mov.l, fmov.d, + * fmov.s, movu.b, movu.w + * + * 0011nnnn0iii1001 bclr.b, bld.b, bset.b, bst.b, band.b, + * bandnot.b, bldnot.b, bor.b, bornot.b, + * bxor.b + * + * 0000nnnniiii0000 movi20 + * 0000nnnniiii0001 movi20s + */ +unsigned int instruction_size(unsigned int insn) +{ + /* Look for the common cases */ + switch ((insn & 0xf00f)) { + case 0x0000: /* movi20 */ + case 0x0001: /* movi20s */ + case 0x3001: /* 32-bit mov/fmov/movu variants */ + return 4; + } + + /* And the special cases.. */ + switch ((insn & 0xf08f)) { + case 0x3009: /* 32-bit b*.b bit operations */ + return 4; + } + + return 2; +} diff --git a/arch/sh/kernel/cpu/sh2a/pinmux-sh7203.c b/arch/sh/kernel/cpu/sh2a/pinmux-sh7203.c new file mode 100644 index 000000000..eef17dcc3 --- /dev/null +++ b/arch/sh/kernel/cpu/sh2a/pinmux-sh7203.c @@ -0,0 +1,30 @@ +/* + * SH7203 Pinmux + * + * Copyright (C) 2008 Magnus Damm + * + * 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/bug.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <cpu/pfc.h> + +static struct resource sh7203_pfc_resources[] = { + [0] = { + .start = 0xfffe3800, + .end = 0xfffe3a9f, + .flags = IORESOURCE_MEM, + }, +}; + +static int __init plat_pinmux_setup(void) +{ + return sh_pfc_register("pfc-sh7203", sh7203_pfc_resources, + ARRAY_SIZE(sh7203_pfc_resources)); +} +arch_initcall(plat_pinmux_setup); diff --git a/arch/sh/kernel/cpu/sh2a/pinmux-sh7264.c b/arch/sh/kernel/cpu/sh2a/pinmux-sh7264.c new file mode 100644 index 000000000..569decbd6 --- /dev/null +++ b/arch/sh/kernel/cpu/sh2a/pinmux-sh7264.c @@ -0,0 +1,30 @@ +/* + * SH7264 Pinmux + * + * Copyright (C) 2012 Renesas Electronics Europe Ltd + * + * 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/bug.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <cpu/pfc.h> + +static struct resource sh7264_pfc_resources[] = { + [0] = { + .start = 0xfffe3800, + .end = 0xfffe393f, + .flags = IORESOURCE_MEM, + }, +}; + +static int __init plat_pinmux_setup(void) +{ + return sh_pfc_register("pfc-sh7264", sh7264_pfc_resources, + ARRAY_SIZE(sh7264_pfc_resources)); +} +arch_initcall(plat_pinmux_setup); diff --git a/arch/sh/kernel/cpu/sh2a/pinmux-sh7269.c b/arch/sh/kernel/cpu/sh2a/pinmux-sh7269.c new file mode 100644 index 000000000..4c17fb697 --- /dev/null +++ b/arch/sh/kernel/cpu/sh2a/pinmux-sh7269.c @@ -0,0 +1,31 @@ +/* + * SH7269 Pinmux + * + * Copyright (C) 2012 Renesas Electronics Europe Ltd + * Copyright (C) 2012 Phil Edworthy + * + * 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/bug.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <cpu/pfc.h> + +static struct resource sh7269_pfc_resources[] = { + [0] = { + .start = 0xfffe3800, + .end = 0xfffe391f, + .flags = IORESOURCE_MEM, + }, +}; + +static int __init plat_pinmux_setup(void) +{ + return sh_pfc_register("pfc-sh7269", sh7269_pfc_resources, + ARRAY_SIZE(sh7269_pfc_resources)); +} +arch_initcall(plat_pinmux_setup); diff --git a/arch/sh/kernel/cpu/sh2a/probe.c b/arch/sh/kernel/cpu/sh2a/probe.c new file mode 100644 index 000000000..3f8797108 --- /dev/null +++ b/arch/sh/kernel/cpu/sh2a/probe.c @@ -0,0 +1,60 @@ +/* + * arch/sh/kernel/cpu/sh2a/probe.c + * + * CPU Subtype Probing for SH-2A. + * + * Copyright (C) 2004 - 2007 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 <asm/processor.h> +#include <asm/cache.h> + +void cpu_probe(void) +{ + boot_cpu_data.family = CPU_FAMILY_SH2A; + + /* All SH-2A CPUs have support for 16 and 32-bit opcodes.. */ + boot_cpu_data.flags |= CPU_HAS_OP32; + +#if defined(CONFIG_CPU_SUBTYPE_SH7201) + boot_cpu_data.type = CPU_SH7201; + boot_cpu_data.flags |= CPU_HAS_FPU; +#elif defined(CONFIG_CPU_SUBTYPE_SH7203) + boot_cpu_data.type = CPU_SH7203; + boot_cpu_data.flags |= CPU_HAS_FPU; +#elif defined(CONFIG_CPU_SUBTYPE_SH7263) + boot_cpu_data.type = CPU_SH7263; + boot_cpu_data.flags |= CPU_HAS_FPU; +#elif defined(CONFIG_CPU_SUBTYPE_SH7264) + boot_cpu_data.type = CPU_SH7264; + boot_cpu_data.flags |= CPU_HAS_FPU; +#elif defined(CONFIG_CPU_SUBTYPE_SH7269) + boot_cpu_data.type = CPU_SH7269; + boot_cpu_data.flags |= CPU_HAS_FPU; +#elif defined(CONFIG_CPU_SUBTYPE_SH7206) + boot_cpu_data.type = CPU_SH7206; + boot_cpu_data.flags |= CPU_HAS_DSP; +#elif defined(CONFIG_CPU_SUBTYPE_MXG) + boot_cpu_data.type = CPU_MXG; + boot_cpu_data.flags |= CPU_HAS_DSP; +#endif + + boot_cpu_data.dcache.ways = 4; + boot_cpu_data.dcache.way_incr = (1 << 11); + boot_cpu_data.dcache.sets = 128; + boot_cpu_data.dcache.entry_shift = 4; + boot_cpu_data.dcache.linesz = L1_CACHE_BYTES; + boot_cpu_data.dcache.flags = 0; + + /* + * The icache is the same as the dcache as far as this setup is + * concerned. The only real difference in hardware is that the icache + * lacks the U bit that the dcache has, none of this has any bearing + * on the cache info. + */ + boot_cpu_data.icache = boot_cpu_data.dcache; +} diff --git a/arch/sh/kernel/cpu/sh2a/setup-mxg.c b/arch/sh/kernel/cpu/sh2a/setup-mxg.c new file mode 100644 index 000000000..060fdd369 --- /dev/null +++ b/arch/sh/kernel/cpu/sh2a/setup-mxg.c @@ -0,0 +1,177 @@ +/* + * Renesas MX-G (R8A03022BG) Setup + * + * Copyright (C) 2008, 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/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <linux/serial_sci.h> +#include <linux/sh_timer.h> + +enum { + UNUSED = 0, + + /* interrupt sources */ + IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7, + IRQ8, IRQ9, IRQ10, IRQ11, IRQ12, IRQ13, IRQ14, IRQ15, + + PINT0, PINT1, PINT2, PINT3, PINT4, PINT5, PINT6, PINT7, + SINT8, SINT7, SINT6, SINT5, SINT4, SINT3, SINT2, SINT1, + + SCIF0, SCIF1, + + MTU2_GROUP1, MTU2_GROUP2, MTU2_GROUP3, MTU2_GROUP4, MTU2_GROUP5, + MTU2_TGI3B, MTU2_TGI3C, + + /* interrupt groups */ + PINT, +}; + +static struct intc_vect vectors[] __initdata = { + INTC_IRQ(IRQ0, 64), INTC_IRQ(IRQ1, 65), + INTC_IRQ(IRQ2, 66), INTC_IRQ(IRQ3, 67), + INTC_IRQ(IRQ4, 68), INTC_IRQ(IRQ5, 69), + INTC_IRQ(IRQ6, 70), INTC_IRQ(IRQ7, 71), + INTC_IRQ(IRQ8, 72), INTC_IRQ(IRQ9, 73), + INTC_IRQ(IRQ10, 74), INTC_IRQ(IRQ11, 75), + INTC_IRQ(IRQ12, 76), INTC_IRQ(IRQ13, 77), + INTC_IRQ(IRQ14, 78), INTC_IRQ(IRQ15, 79), + + INTC_IRQ(PINT0, 80), INTC_IRQ(PINT1, 81), + INTC_IRQ(PINT2, 82), INTC_IRQ(PINT3, 83), + INTC_IRQ(PINT4, 84), INTC_IRQ(PINT5, 85), + INTC_IRQ(PINT6, 86), INTC_IRQ(PINT7, 87), + + INTC_IRQ(SINT8, 94), INTC_IRQ(SINT7, 95), + INTC_IRQ(SINT6, 96), INTC_IRQ(SINT5, 97), + INTC_IRQ(SINT4, 98), INTC_IRQ(SINT3, 99), + INTC_IRQ(SINT2, 100), INTC_IRQ(SINT1, 101), + + INTC_IRQ(SCIF0, 220), INTC_IRQ(SCIF0, 221), + INTC_IRQ(SCIF0, 222), INTC_IRQ(SCIF0, 223), + INTC_IRQ(SCIF1, 224), INTC_IRQ(SCIF1, 225), + INTC_IRQ(SCIF1, 226), INTC_IRQ(SCIF1, 227), + + INTC_IRQ(MTU2_GROUP1, 228), INTC_IRQ(MTU2_GROUP1, 229), + INTC_IRQ(MTU2_GROUP1, 230), INTC_IRQ(MTU2_GROUP1, 231), + INTC_IRQ(MTU2_GROUP1, 232), INTC_IRQ(MTU2_GROUP1, 233), + + INTC_IRQ(MTU2_GROUP2, 234), INTC_IRQ(MTU2_GROUP2, 235), + INTC_IRQ(MTU2_GROUP2, 236), INTC_IRQ(MTU2_GROUP2, 237), + INTC_IRQ(MTU2_GROUP2, 238), INTC_IRQ(MTU2_GROUP2, 239), + + INTC_IRQ(MTU2_GROUP3, 240), INTC_IRQ(MTU2_GROUP3, 241), + INTC_IRQ(MTU2_GROUP3, 242), INTC_IRQ(MTU2_GROUP3, 243), + + INTC_IRQ(MTU2_TGI3B, 244), + INTC_IRQ(MTU2_TGI3C, 245), + + INTC_IRQ(MTU2_GROUP4, 246), INTC_IRQ(MTU2_GROUP4, 247), + INTC_IRQ(MTU2_GROUP4, 248), INTC_IRQ(MTU2_GROUP4, 249), + INTC_IRQ(MTU2_GROUP4, 250), INTC_IRQ(MTU2_GROUP4, 251), + + INTC_IRQ(MTU2_GROUP5, 252), INTC_IRQ(MTU2_GROUP5, 253), + INTC_IRQ(MTU2_GROUP5, 254), INTC_IRQ(MTU2_GROUP5, 255), +}; + +static struct intc_group groups[] __initdata = { + INTC_GROUP(PINT, PINT0, PINT1, PINT2, PINT3, + PINT4, PINT5, PINT6, PINT7), +}; + +static struct intc_prio_reg prio_registers[] __initdata = { + { 0xfffd9418, 0, 16, 4, /* IPR01 */ { IRQ0, IRQ1, IRQ2, IRQ3 } }, + { 0xfffd941a, 0, 16, 4, /* IPR02 */ { IRQ4, IRQ5, IRQ6, IRQ7 } }, + { 0xfffd941c, 0, 16, 4, /* IPR03 */ { IRQ8, IRQ9, IRQ10, IRQ11 } }, + { 0xfffd941e, 0, 16, 4, /* IPR04 */ { IRQ12, IRQ13, IRQ14, IRQ15 } }, + { 0xfffd9420, 0, 16, 4, /* IPR05 */ { PINT, 0, 0, 0 } }, + { 0xfffd9800, 0, 16, 4, /* IPR06 */ { } }, + { 0xfffd9802, 0, 16, 4, /* IPR07 */ { } }, + { 0xfffd9804, 0, 16, 4, /* IPR08 */ { } }, + { 0xfffd9806, 0, 16, 4, /* IPR09 */ { } }, + { 0xfffd9808, 0, 16, 4, /* IPR10 */ { } }, + { 0xfffd980a, 0, 16, 4, /* IPR11 */ { } }, + { 0xfffd980c, 0, 16, 4, /* IPR12 */ { } }, + { 0xfffd980e, 0, 16, 4, /* IPR13 */ { } }, + { 0xfffd9810, 0, 16, 4, /* IPR14 */ { 0, 0, 0, SCIF0 } }, + { 0xfffd9812, 0, 16, 4, /* IPR15 */ + { SCIF1, MTU2_GROUP1, MTU2_GROUP2, MTU2_GROUP3 } }, + { 0xfffd9814, 0, 16, 4, /* IPR16 */ + { MTU2_TGI3B, MTU2_TGI3C, MTU2_GROUP4, MTU2_GROUP5 } }, +}; + +static struct intc_mask_reg mask_registers[] __initdata = { + { 0xfffd9408, 0, 16, /* PINTER */ + { 0, 0, 0, 0, 0, 0, 0, 0, + PINT7, PINT6, PINT5, PINT4, PINT3, PINT2, PINT1, PINT0 } }, +}; + +static DECLARE_INTC_DESC(intc_desc, "mxg", vectors, groups, + mask_registers, prio_registers, NULL); + +static struct resource mtu2_resources[] = { + DEFINE_RES_MEM(0xff801000, 0x400), + DEFINE_RES_IRQ_NAMED(228, "tgi0a"), + DEFINE_RES_IRQ_NAMED(234, "tgi1a"), + DEFINE_RES_IRQ_NAMED(240, "tgi2a"), +}; + +static struct platform_device mtu2_device = { + .name = "sh-mtu2", + .id = -1, + .resource = mtu2_resources, + .num_resources = ARRAY_SIZE(mtu2_resources), +}; + +static struct plat_sci_port scif0_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, +}; + +static struct resource scif0_resources[] = { + DEFINE_RES_MEM(0xff804000, 0x100), + DEFINE_RES_IRQ(220), +}; + +static struct platform_device scif0_device = { + .name = "sh-sci", + .id = 0, + .resource = scif0_resources, + .num_resources = ARRAY_SIZE(scif0_resources), + .dev = { + .platform_data = &scif0_platform_data, + }, +}; + +static struct platform_device *mxg_devices[] __initdata = { + &scif0_device, + &mtu2_device, +}; + +static int __init mxg_devices_setup(void) +{ + return platform_add_devices(mxg_devices, + ARRAY_SIZE(mxg_devices)); +} +arch_initcall(mxg_devices_setup); + +void __init plat_irq_setup(void) +{ + register_intc_controller(&intc_desc); +} + +static struct platform_device *mxg_early_devices[] __initdata = { + &scif0_device, + &mtu2_device, +}; + +void __init plat_early_device_setup(void) +{ + early_platform_add_devices(mxg_early_devices, + ARRAY_SIZE(mxg_early_devices)); +} diff --git a/arch/sh/kernel/cpu/sh2a/setup-sh7201.c b/arch/sh/kernel/cpu/sh2a/setup-sh7201.c new file mode 100644 index 000000000..c1301f68d --- /dev/null +++ b/arch/sh/kernel/cpu/sh2a/setup-sh7201.c @@ -0,0 +1,420 @@ +/* + * SH7201 setup + * + * Copyright (C) 2008 Peter Griffin pgriffin@mpc-data.co.uk + * 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/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <linux/serial_sci.h> +#include <linux/sh_timer.h> +#include <linux/io.h> + +enum { + UNUSED = 0, + + /* interrupt sources */ + IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7, + PINT0, PINT1, PINT2, PINT3, PINT4, PINT5, PINT6, PINT7, + + ADC_ADI, + + MTU20_ABCD, MTU20_VEF, MTU21_AB, MTU21_VU, MTU22_AB, MTU22_VU, + MTU23_ABCD, MTU24_ABCD, MTU25_UVW, MTU2_TCI3V, MTU2_TCI4V, + + RTC, WDT, + + IIC30, IIC31, IIC32, + + DMAC0_DMINT0, DMAC1_DMINT1, + DMAC2_DMINT2, DMAC3_DMINT3, + + SCIF0, SCIF1, SCIF2, SCIF3, SCIF4, SCIF5, SCIF6, SCIF7, + + DMAC0_DMINTA, DMAC4_DMINT4, DMAC5_DMINT5, DMAC6_DMINT6, + DMAC7_DMINT7, + + RCAN0, RCAN1, + + SSI0_SSII, SSI1_SSII, + + TMR0, TMR1, + + /* interrupt groups */ + PINT, +}; + +static struct intc_vect vectors[] __initdata = { + INTC_IRQ(IRQ0, 64), INTC_IRQ(IRQ1, 65), + INTC_IRQ(IRQ2, 66), INTC_IRQ(IRQ3, 67), + INTC_IRQ(IRQ4, 68), INTC_IRQ(IRQ5, 69), + INTC_IRQ(IRQ6, 70), INTC_IRQ(IRQ7, 71), + + INTC_IRQ(PINT0, 80), INTC_IRQ(PINT1, 81), + INTC_IRQ(PINT2, 82), INTC_IRQ(PINT3, 83), + INTC_IRQ(PINT4, 84), INTC_IRQ(PINT5, 85), + INTC_IRQ(PINT6, 86), INTC_IRQ(PINT7, 87), + + INTC_IRQ(ADC_ADI, 92), + + INTC_IRQ(MTU20_ABCD, 108), INTC_IRQ(MTU20_ABCD, 109), + INTC_IRQ(MTU20_ABCD, 110), INTC_IRQ(MTU20_ABCD, 111), + + INTC_IRQ(MTU20_VEF, 112), INTC_IRQ(MTU20_VEF, 113), + INTC_IRQ(MTU20_VEF, 114), + + INTC_IRQ(MTU21_AB, 116), INTC_IRQ(MTU21_AB, 117), + INTC_IRQ(MTU21_VU, 120), INTC_IRQ(MTU21_VU, 121), + + INTC_IRQ(MTU22_AB, 124), INTC_IRQ(MTU22_AB, 125), + INTC_IRQ(MTU22_VU, 128), INTC_IRQ(MTU22_VU, 129), + + INTC_IRQ(MTU23_ABCD, 132), INTC_IRQ(MTU23_ABCD, 133), + INTC_IRQ(MTU23_ABCD, 134), INTC_IRQ(MTU23_ABCD, 135), + + INTC_IRQ(MTU2_TCI3V, 136), + + INTC_IRQ(MTU24_ABCD, 140), INTC_IRQ(MTU24_ABCD, 141), + INTC_IRQ(MTU24_ABCD, 142), INTC_IRQ(MTU24_ABCD, 143), + + INTC_IRQ(MTU2_TCI4V, 144), + + INTC_IRQ(MTU25_UVW, 148), INTC_IRQ(MTU25_UVW, 149), + INTC_IRQ(MTU25_UVW, 150), + + INTC_IRQ(RTC, 152), INTC_IRQ(RTC, 153), + INTC_IRQ(RTC, 154), + + INTC_IRQ(WDT, 156), + + INTC_IRQ(IIC30, 157), INTC_IRQ(IIC30, 158), + INTC_IRQ(IIC30, 159), INTC_IRQ(IIC30, 160), + INTC_IRQ(IIC30, 161), + + INTC_IRQ(IIC31, 164), INTC_IRQ(IIC31, 165), + INTC_IRQ(IIC31, 166), INTC_IRQ(IIC31, 167), + INTC_IRQ(IIC31, 168), + + INTC_IRQ(IIC32, 170), INTC_IRQ(IIC32, 171), + INTC_IRQ(IIC32, 172), INTC_IRQ(IIC32, 173), + INTC_IRQ(IIC32, 174), + + INTC_IRQ(DMAC0_DMINT0, 176), INTC_IRQ(DMAC1_DMINT1, 177), + INTC_IRQ(DMAC2_DMINT2, 178), INTC_IRQ(DMAC3_DMINT3, 179), + + INTC_IRQ(SCIF0, 180), INTC_IRQ(SCIF0, 181), + INTC_IRQ(SCIF0, 182), INTC_IRQ(SCIF0, 183), + INTC_IRQ(SCIF1, 184), INTC_IRQ(SCIF1, 185), + INTC_IRQ(SCIF1, 186), INTC_IRQ(SCIF1, 187), + INTC_IRQ(SCIF2, 188), INTC_IRQ(SCIF2, 189), + INTC_IRQ(SCIF2, 190), INTC_IRQ(SCIF2, 191), + INTC_IRQ(SCIF3, 192), INTC_IRQ(SCIF3, 193), + INTC_IRQ(SCIF3, 194), INTC_IRQ(SCIF3, 195), + INTC_IRQ(SCIF4, 196), INTC_IRQ(SCIF4, 197), + INTC_IRQ(SCIF4, 198), INTC_IRQ(SCIF4, 199), + INTC_IRQ(SCIF5, 200), INTC_IRQ(SCIF5, 201), + INTC_IRQ(SCIF5, 202), INTC_IRQ(SCIF5, 203), + INTC_IRQ(SCIF6, 204), INTC_IRQ(SCIF6, 205), + INTC_IRQ(SCIF6, 206), INTC_IRQ(SCIF6, 207), + INTC_IRQ(SCIF7, 208), INTC_IRQ(SCIF7, 209), + INTC_IRQ(SCIF7, 210), INTC_IRQ(SCIF7, 211), + + INTC_IRQ(DMAC0_DMINTA, 212), INTC_IRQ(DMAC4_DMINT4, 216), + INTC_IRQ(DMAC5_DMINT5, 217), INTC_IRQ(DMAC6_DMINT6, 218), + INTC_IRQ(DMAC7_DMINT7, 219), + + INTC_IRQ(RCAN0, 228), INTC_IRQ(RCAN0, 229), + INTC_IRQ(RCAN0, 230), + INTC_IRQ(RCAN0, 231), INTC_IRQ(RCAN0, 232), + + INTC_IRQ(RCAN1, 234), INTC_IRQ(RCAN1, 235), + INTC_IRQ(RCAN1, 236), + INTC_IRQ(RCAN1, 237), INTC_IRQ(RCAN1, 238), + + INTC_IRQ(SSI0_SSII, 244), INTC_IRQ(SSI1_SSII, 245), + + INTC_IRQ(TMR0, 246), INTC_IRQ(TMR0, 247), + INTC_IRQ(TMR0, 248), + + INTC_IRQ(TMR1, 252), INTC_IRQ(TMR1, 253), + INTC_IRQ(TMR1, 254), +}; + +static struct intc_group groups[] __initdata = { + INTC_GROUP(PINT, PINT0, PINT1, PINT2, PINT3, + PINT4, PINT5, PINT6, PINT7), +}; + +static struct intc_prio_reg prio_registers[] __initdata = { + { 0xfffe9418, 0, 16, 4, /* IPR01 */ { IRQ0, IRQ1, IRQ2, IRQ3 } }, + { 0xfffe941a, 0, 16, 4, /* IPR02 */ { IRQ4, IRQ5, IRQ6, IRQ7 } }, + { 0xfffe9420, 0, 16, 4, /* IPR05 */ { PINT, 0, ADC_ADI, 0 } }, + { 0xfffe9800, 0, 16, 4, /* IPR06 */ { 0, MTU20_ABCD, MTU20_VEF, MTU21_AB } }, + { 0xfffe9802, 0, 16, 4, /* IPR07 */ { MTU21_VU, MTU22_AB, MTU22_VU, MTU23_ABCD } }, + { 0xfffe9804, 0, 16, 4, /* IPR08 */ { MTU2_TCI3V, MTU24_ABCD, MTU2_TCI4V, MTU25_UVW } }, + + { 0xfffe9806, 0, 16, 4, /* IPR09 */ { RTC, WDT, IIC30, 0 } }, + { 0xfffe9808, 0, 16, 4, /* IPR10 */ { IIC31, IIC32, DMAC0_DMINT0, DMAC1_DMINT1 } }, + { 0xfffe980a, 0, 16, 4, /* IPR11 */ { DMAC2_DMINT2, DMAC3_DMINT3, SCIF0, SCIF1 } }, + { 0xfffe980c, 0, 16, 4, /* IPR12 */ { SCIF2, SCIF3, SCIF4, SCIF5 } }, + { 0xfffe980e, 0, 16, 4, /* IPR13 */ { SCIF6, SCIF7, DMAC0_DMINTA, DMAC4_DMINT4 } }, + { 0xfffe9810, 0, 16, 4, /* IPR14 */ { DMAC5_DMINT5, DMAC6_DMINT6, DMAC7_DMINT7, 0 } }, + { 0xfffe9812, 0, 16, 4, /* IPR15 */ { 0, RCAN0, RCAN1, 0 } }, + { 0xfffe9814, 0, 16, 4, /* IPR16 */ { SSI0_SSII, SSI1_SSII, TMR0, TMR1 } }, +}; + +static struct intc_mask_reg mask_registers[] __initdata = { + { 0xfffe9408, 0, 16, /* PINTER */ + { 0, 0, 0, 0, 0, 0, 0, 0, + PINT7, PINT6, PINT5, PINT4, PINT3, PINT2, PINT1, PINT0 } }, +}; + +static DECLARE_INTC_DESC(intc_desc, "sh7201", vectors, groups, + mask_registers, prio_registers, NULL); + +static struct plat_sci_port scif0_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, +}; + +static struct resource scif0_resources[] = { + DEFINE_RES_MEM(0xfffe8000, 0x100), + DEFINE_RES_IRQ(180), +}; + +static struct platform_device scif0_device = { + .name = "sh-sci", + .id = 0, + .resource = scif0_resources, + .num_resources = ARRAY_SIZE(scif0_resources), + .dev = { + .platform_data = &scif0_platform_data, + }, +}; + +static struct plat_sci_port scif1_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, +}; + +static struct resource scif1_resources[] = { + DEFINE_RES_MEM(0xfffe8800, 0x100), + DEFINE_RES_IRQ(184), +}; + +static struct platform_device scif1_device = { + .name = "sh-sci", + .id = 1, + .resource = scif1_resources, + .num_resources = ARRAY_SIZE(scif1_resources), + .dev = { + .platform_data = &scif1_platform_data, + }, +}; + +static struct plat_sci_port scif2_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, +}; + +static struct resource scif2_resources[] = { + DEFINE_RES_MEM(0xfffe9000, 0x100), + DEFINE_RES_IRQ(188), +}; + +static struct platform_device scif2_device = { + .name = "sh-sci", + .id = 2, + .resource = scif2_resources, + .num_resources = ARRAY_SIZE(scif2_resources), + .dev = { + .platform_data = &scif2_platform_data, + }, +}; + +static struct plat_sci_port scif3_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, +}; + +static struct resource scif3_resources[] = { + DEFINE_RES_MEM(0xfffe9800, 0x100), + DEFINE_RES_IRQ(192), +}; + +static struct platform_device scif3_device = { + .name = "sh-sci", + .id = 3, + .resource = scif3_resources, + .num_resources = ARRAY_SIZE(scif3_resources), + .dev = { + .platform_data = &scif3_platform_data, + }, +}; + +static struct plat_sci_port scif4_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, +}; + +static struct resource scif4_resources[] = { + DEFINE_RES_MEM(0xfffea000, 0x100), + DEFINE_RES_IRQ(196), +}; + +static struct platform_device scif4_device = { + .name = "sh-sci", + .id = 4, + .resource = scif4_resources, + .num_resources = ARRAY_SIZE(scif4_resources), + .dev = { + .platform_data = &scif4_platform_data, + }, +}; + +static struct plat_sci_port scif5_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, +}; + +static struct resource scif5_resources[] = { + DEFINE_RES_MEM(0xfffea800, 0x100), + DEFINE_RES_IRQ(200), +}; + +static struct platform_device scif5_device = { + .name = "sh-sci", + .id = 5, + .resource = scif5_resources, + .num_resources = ARRAY_SIZE(scif5_resources), + .dev = { + .platform_data = &scif5_platform_data, + }, +}; + +static struct plat_sci_port scif6_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, +}; + +static struct resource scif6_resources[] = { + DEFINE_RES_MEM(0xfffeb000, 0x100), + DEFINE_RES_IRQ(204), +}; + +static struct platform_device scif6_device = { + .name = "sh-sci", + .id = 6, + .resource = scif6_resources, + .num_resources = ARRAY_SIZE(scif6_resources), + .dev = { + .platform_data = &scif6_platform_data, + }, +}; + +static struct plat_sci_port scif7_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, +}; + +static struct resource scif7_resources[] = { + DEFINE_RES_MEM(0xfffeb800, 0x100), + DEFINE_RES_IRQ(208), +}; + +static struct platform_device scif7_device = { + .name = "sh-sci", + .id = 7, + .resource = scif7_resources, + .num_resources = ARRAY_SIZE(scif7_resources), + .dev = { + .platform_data = &scif7_platform_data, + }, +}; + +static struct resource rtc_resources[] = { + [0] = { + .start = 0xffff0800, + .end = 0xffff2000 + 0x58 - 1, + .flags = IORESOURCE_IO, + }, + [1] = { + /* Shared Period/Carry/Alarm IRQ */ + .start = 152, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device rtc_device = { + .name = "sh-rtc", + .id = -1, + .num_resources = ARRAY_SIZE(rtc_resources), + .resource = rtc_resources, +}; + +static struct resource mtu2_resources[] = { + DEFINE_RES_MEM(0xfffe4000, 0x400), + DEFINE_RES_IRQ_NAMED(108, "tgi0a"), + DEFINE_RES_IRQ_NAMED(116, "tgi1a"), + DEFINE_RES_IRQ_NAMED(124, "tgi1b"), +}; + +static struct platform_device mtu2_device = { + .name = "sh-mtu2", + .id = -1, + .resource = mtu2_resources, + .num_resources = ARRAY_SIZE(mtu2_resources), +}; + +static struct platform_device *sh7201_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + &scif3_device, + &scif4_device, + &scif5_device, + &scif6_device, + &scif7_device, + &rtc_device, + &mtu2_device, +}; + +static int __init sh7201_devices_setup(void) +{ + return platform_add_devices(sh7201_devices, + ARRAY_SIZE(sh7201_devices)); +} +arch_initcall(sh7201_devices_setup); + +void __init plat_irq_setup(void) +{ + register_intc_controller(&intc_desc); +} + +static struct platform_device *sh7201_early_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + &scif3_device, + &scif4_device, + &scif5_device, + &scif6_device, + &scif7_device, + &mtu2_device, +}; + +#define STBCR3 0xfffe0408 + +void __init plat_early_device_setup(void) +{ + /* enable MTU2 clock */ + __raw_writeb(__raw_readb(STBCR3) & ~0x20, STBCR3); + + early_platform_add_devices(sh7201_early_devices, + ARRAY_SIZE(sh7201_early_devices)); +} diff --git a/arch/sh/kernel/cpu/sh2a/setup-sh7203.c b/arch/sh/kernel/cpu/sh2a/setup-sh7203.c new file mode 100644 index 000000000..32ec732e2 --- /dev/null +++ b/arch/sh/kernel/cpu/sh2a/setup-sh7203.c @@ -0,0 +1,357 @@ +/* + * SH7203 and SH7263 Setup + * + * Copyright (C) 2007 - 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/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <linux/serial_sci.h> +#include <linux/sh_timer.h> +#include <linux/io.h> + +enum { + UNUSED = 0, + + /* interrupt sources */ + IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7, + PINT0, PINT1, PINT2, PINT3, PINT4, PINT5, PINT6, PINT7, + DMAC0, DMAC1, DMAC2, DMAC3, DMAC4, DMAC5, DMAC6, DMAC7, + USB, LCDC, CMT0, CMT1, BSC, WDT, + + MTU0_ABCD, MTU0_VEF, MTU1_AB, MTU1_VU, MTU2_AB, MTU2_VU, + MTU3_ABCD, MTU4_ABCD, MTU2_TCI3V, MTU2_TCI4V, + + ADC_ADI, + + IIC30, IIC31, IIC32, IIC33, + SCIF0, SCIF1, SCIF2, SCIF3, + + SSU0, SSU1, + + SSI0_SSII, SSI1_SSII, SSI2_SSII, SSI3_SSII, + + /* ROM-DEC, SDHI, SRC, and IEB are SH7263 specific */ + ROMDEC, FLCTL, SDHI, RTC, RCAN0, RCAN1, + SRC, IEBI, + + /* interrupt groups */ + PINT, +}; + +static struct intc_vect vectors[] __initdata = { + INTC_IRQ(IRQ0, 64), INTC_IRQ(IRQ1, 65), + INTC_IRQ(IRQ2, 66), INTC_IRQ(IRQ3, 67), + INTC_IRQ(IRQ4, 68), INTC_IRQ(IRQ5, 69), + INTC_IRQ(IRQ6, 70), INTC_IRQ(IRQ7, 71), + INTC_IRQ(PINT0, 80), INTC_IRQ(PINT1, 81), + INTC_IRQ(PINT2, 82), INTC_IRQ(PINT3, 83), + INTC_IRQ(PINT4, 84), INTC_IRQ(PINT5, 85), + INTC_IRQ(PINT6, 86), INTC_IRQ(PINT7, 87), + INTC_IRQ(DMAC0, 108), INTC_IRQ(DMAC0, 109), + INTC_IRQ(DMAC1, 112), INTC_IRQ(DMAC1, 113), + INTC_IRQ(DMAC2, 116), INTC_IRQ(DMAC2, 117), + INTC_IRQ(DMAC3, 120), INTC_IRQ(DMAC3, 121), + INTC_IRQ(DMAC4, 124), INTC_IRQ(DMAC4, 125), + INTC_IRQ(DMAC5, 128), INTC_IRQ(DMAC5, 129), + INTC_IRQ(DMAC6, 132), INTC_IRQ(DMAC6, 133), + INTC_IRQ(DMAC7, 136), INTC_IRQ(DMAC7, 137), + INTC_IRQ(USB, 140), INTC_IRQ(LCDC, 141), + INTC_IRQ(CMT0, 142), INTC_IRQ(CMT1, 143), + INTC_IRQ(BSC, 144), INTC_IRQ(WDT, 145), + INTC_IRQ(MTU0_ABCD, 146), INTC_IRQ(MTU0_ABCD, 147), + INTC_IRQ(MTU0_ABCD, 148), INTC_IRQ(MTU0_ABCD, 149), + INTC_IRQ(MTU0_VEF, 150), + INTC_IRQ(MTU0_VEF, 151), INTC_IRQ(MTU0_VEF, 152), + INTC_IRQ(MTU1_AB, 153), INTC_IRQ(MTU1_AB, 154), + INTC_IRQ(MTU1_VU, 155), INTC_IRQ(MTU1_VU, 156), + INTC_IRQ(MTU2_AB, 157), INTC_IRQ(MTU2_AB, 158), + INTC_IRQ(MTU2_VU, 159), INTC_IRQ(MTU2_VU, 160), + INTC_IRQ(MTU3_ABCD, 161), INTC_IRQ(MTU3_ABCD, 162), + INTC_IRQ(MTU3_ABCD, 163), INTC_IRQ(MTU3_ABCD, 164), + INTC_IRQ(MTU2_TCI3V, 165), + INTC_IRQ(MTU4_ABCD, 166), INTC_IRQ(MTU4_ABCD, 167), + INTC_IRQ(MTU4_ABCD, 168), INTC_IRQ(MTU4_ABCD, 169), + INTC_IRQ(MTU2_TCI4V, 170), + INTC_IRQ(ADC_ADI, 171), + INTC_IRQ(IIC30, 172), INTC_IRQ(IIC30, 173), + INTC_IRQ(IIC30, 174), INTC_IRQ(IIC30, 175), + INTC_IRQ(IIC30, 176), + INTC_IRQ(IIC31, 177), INTC_IRQ(IIC31, 178), + INTC_IRQ(IIC31, 179), INTC_IRQ(IIC31, 180), + INTC_IRQ(IIC31, 181), + INTC_IRQ(IIC32, 182), INTC_IRQ(IIC32, 183), + INTC_IRQ(IIC32, 184), INTC_IRQ(IIC32, 185), + INTC_IRQ(IIC32, 186), + INTC_IRQ(IIC33, 187), INTC_IRQ(IIC33, 188), + INTC_IRQ(IIC33, 189), INTC_IRQ(IIC33, 190), + INTC_IRQ(IIC33, 191), + INTC_IRQ(SCIF0, 192), INTC_IRQ(SCIF0, 193), + INTC_IRQ(SCIF0, 194), INTC_IRQ(SCIF0, 195), + INTC_IRQ(SCIF1, 196), INTC_IRQ(SCIF1, 197), + INTC_IRQ(SCIF1, 198), INTC_IRQ(SCIF1, 199), + INTC_IRQ(SCIF2, 200), INTC_IRQ(SCIF2, 201), + INTC_IRQ(SCIF2, 202), INTC_IRQ(SCIF2, 203), + INTC_IRQ(SCIF3, 204), INTC_IRQ(SCIF3, 205), + INTC_IRQ(SCIF3, 206), INTC_IRQ(SCIF3, 207), + INTC_IRQ(SSU0, 208), INTC_IRQ(SSU0, 209), + INTC_IRQ(SSU0, 210), + INTC_IRQ(SSU1, 211), INTC_IRQ(SSU1, 212), + INTC_IRQ(SSU1, 213), + INTC_IRQ(SSI0_SSII, 214), INTC_IRQ(SSI1_SSII, 215), + INTC_IRQ(SSI2_SSII, 216), INTC_IRQ(SSI3_SSII, 217), + INTC_IRQ(FLCTL, 224), INTC_IRQ(FLCTL, 225), + INTC_IRQ(FLCTL, 226), INTC_IRQ(FLCTL, 227), + INTC_IRQ(RTC, 231), INTC_IRQ(RTC, 232), + INTC_IRQ(RTC, 233), + INTC_IRQ(RCAN0, 234), INTC_IRQ(RCAN0, 235), + INTC_IRQ(RCAN0, 236), INTC_IRQ(RCAN0, 237), + INTC_IRQ(RCAN0, 238), + INTC_IRQ(RCAN1, 239), INTC_IRQ(RCAN1, 240), + INTC_IRQ(RCAN1, 241), INTC_IRQ(RCAN1, 242), + INTC_IRQ(RCAN1, 243), + + /* SH7263-specific trash */ +#ifdef CONFIG_CPU_SUBTYPE_SH7263 + INTC_IRQ(ROMDEC, 218), INTC_IRQ(ROMDEC, 219), + INTC_IRQ(ROMDEC, 220), INTC_IRQ(ROMDEC, 221), + INTC_IRQ(ROMDEC, 222), INTC_IRQ(ROMDEC, 223), + + INTC_IRQ(SDHI, 228), INTC_IRQ(SDHI, 229), + INTC_IRQ(SDHI, 230), + + INTC_IRQ(SRC, 244), INTC_IRQ(SRC, 245), + INTC_IRQ(SRC, 246), + + INTC_IRQ(IEBI, 247), +#endif +}; + +static struct intc_group groups[] __initdata = { + INTC_GROUP(PINT, PINT0, PINT1, PINT2, PINT3, + PINT4, PINT5, PINT6, PINT7), +}; + +static struct intc_prio_reg prio_registers[] __initdata = { + { 0xfffe0818, 0, 16, 4, /* IPR01 */ { IRQ0, IRQ1, IRQ2, IRQ3 } }, + { 0xfffe081a, 0, 16, 4, /* IPR02 */ { IRQ4, IRQ5, IRQ6, IRQ7 } }, + { 0xfffe0820, 0, 16, 4, /* IPR05 */ { PINT, 0, 0, 0 } }, + { 0xfffe0c00, 0, 16, 4, /* IPR06 */ { DMAC0, DMAC1, DMAC2, DMAC3 } }, + { 0xfffe0c02, 0, 16, 4, /* IPR07 */ { DMAC4, DMAC5, DMAC6, DMAC7 } }, + { 0xfffe0c04, 0, 16, 4, /* IPR08 */ { USB, LCDC, CMT0, CMT1 } }, + { 0xfffe0c06, 0, 16, 4, /* IPR09 */ { BSC, WDT, MTU0_ABCD, MTU0_VEF } }, + { 0xfffe0c08, 0, 16, 4, /* IPR10 */ { MTU1_AB, MTU1_VU, MTU2_AB, + MTU2_VU } }, + { 0xfffe0c0a, 0, 16, 4, /* IPR11 */ { MTU3_ABCD, MTU2_TCI3V, MTU4_ABCD, + MTU2_TCI4V } }, + { 0xfffe0c0c, 0, 16, 4, /* IPR12 */ { ADC_ADI, IIC30, IIC31, IIC32 } }, + { 0xfffe0c0e, 0, 16, 4, /* IPR13 */ { IIC33, SCIF0, SCIF1, SCIF2 } }, + { 0xfffe0c10, 0, 16, 4, /* IPR14 */ { SCIF3, SSU0, SSU1, SSI0_SSII } }, +#ifdef CONFIG_CPU_SUBTYPE_SH7203 + { 0xfffe0c12, 0, 16, 4, /* IPR15 */ { SSI1_SSII, SSI2_SSII, + SSI3_SSII, 0 } }, + { 0xfffe0c14, 0, 16, 4, /* IPR16 */ { FLCTL, 0, RTC, RCAN0 } }, + { 0xfffe0c16, 0, 16, 4, /* IPR17 */ { RCAN1, 0, 0, 0 } }, +#else + { 0xfffe0c12, 0, 16, 4, /* IPR15 */ { SSI1_SSII, SSI2_SSII, + SSI3_SSII, ROMDEC } }, + { 0xfffe0c14, 0, 16, 4, /* IPR16 */ { FLCTL, SDHI, RTC, RCAN0 } }, + { 0xfffe0c16, 0, 16, 4, /* IPR17 */ { RCAN1, SRC, IEBI, 0 } }, +#endif +}; + +static struct intc_mask_reg mask_registers[] __initdata = { + { 0xfffe0808, 0, 16, /* PINTER */ + { 0, 0, 0, 0, 0, 0, 0, 0, + PINT7, PINT6, PINT5, PINT4, PINT3, PINT2, PINT1, PINT0 } }, +}; + +static DECLARE_INTC_DESC(intc_desc, "sh7203", vectors, groups, + mask_registers, prio_registers, NULL); + +static struct plat_sci_port scif0_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH2_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif0_resources[] = { + DEFINE_RES_MEM(0xfffe8000, 0x100), + DEFINE_RES_IRQ(192), +}; + +static struct platform_device scif0_device = { + .name = "sh-sci", + .id = 0, + .resource = scif0_resources, + .num_resources = ARRAY_SIZE(scif0_resources), + .dev = { + .platform_data = &scif0_platform_data, + }, +}; + +static struct plat_sci_port scif1_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH2_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif1_resources[] = { + DEFINE_RES_MEM(0xfffe8800, 0x100), + DEFINE_RES_IRQ(196), +}; + +static struct platform_device scif1_device = { + .name = "sh-sci", + .id = 1, + .resource = scif1_resources, + .num_resources = ARRAY_SIZE(scif1_resources), + .dev = { + .platform_data = &scif1_platform_data, + }, +}; + +static struct plat_sci_port scif2_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH2_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif2_resources[] = { + DEFINE_RES_MEM(0xfffe9000, 0x100), + DEFINE_RES_IRQ(200), +}; + +static struct platform_device scif2_device = { + .name = "sh-sci", + .id = 2, + .resource = scif2_resources, + .num_resources = ARRAY_SIZE(scif2_resources), + .dev = { + .platform_data = &scif2_platform_data, + }, +}; + +static struct plat_sci_port scif3_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH2_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif3_resources[] = { + DEFINE_RES_MEM(0xfffe9800, 0x100), + DEFINE_RES_IRQ(204), +}; + +static struct platform_device scif3_device = { + .name = "sh-sci", + .id = 3, + .resource = scif3_resources, + .num_resources = ARRAY_SIZE(scif3_resources), + .dev = { + .platform_data = &scif3_platform_data, + }, +}; + +static struct sh_timer_config cmt_platform_data = { + .channels_mask = 3, +}; + +static struct resource cmt_resources[] = { + DEFINE_RES_MEM(0xfffec000, 0x10), + DEFINE_RES_IRQ(142), + DEFINE_RES_IRQ(143), +}; + +static struct platform_device cmt_device = { + .name = "sh-cmt-16", + .id = 0, + .dev = { + .platform_data = &cmt_platform_data, + }, + .resource = cmt_resources, + .num_resources = ARRAY_SIZE(cmt_resources), +}; + +static struct resource mtu2_resources[] = { + DEFINE_RES_MEM(0xfffe4000, 0x400), + DEFINE_RES_IRQ_NAMED(146, "tgi0a"), + DEFINE_RES_IRQ_NAMED(153, "tgi1a"), +}; + +static struct platform_device mtu2_device = { + .name = "sh-mtu2", + .id = -1, + .resource = mtu2_resources, + .num_resources = ARRAY_SIZE(mtu2_resources), +}; + +static struct resource rtc_resources[] = { + [0] = { + .start = 0xffff2000, + .end = 0xffff2000 + 0x58 - 1, + .flags = IORESOURCE_IO, + }, + [1] = { + /* Shared Period/Carry/Alarm IRQ */ + .start = 231, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device rtc_device = { + .name = "sh-rtc", + .id = -1, + .num_resources = ARRAY_SIZE(rtc_resources), + .resource = rtc_resources, +}; + +static struct platform_device *sh7203_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + &scif3_device, + &cmt_device, + &mtu2_device, + &rtc_device, +}; + +static int __init sh7203_devices_setup(void) +{ + return platform_add_devices(sh7203_devices, + ARRAY_SIZE(sh7203_devices)); +} +arch_initcall(sh7203_devices_setup); + +void __init plat_irq_setup(void) +{ + register_intc_controller(&intc_desc); +} + +static struct platform_device *sh7203_early_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + &scif3_device, + &cmt_device, + &mtu2_device, +}; + +#define STBCR3 0xfffe0408 +#define STBCR4 0xfffe040c + +void __init plat_early_device_setup(void) +{ + /* enable CMT clock */ + __raw_writeb(__raw_readb(STBCR4) & ~0x04, STBCR4); + + /* enable MTU2 clock */ + __raw_writeb(__raw_readb(STBCR3) & ~0x20, STBCR3); + + early_platform_add_devices(sh7203_early_devices, + ARRAY_SIZE(sh7203_early_devices)); +} diff --git a/arch/sh/kernel/cpu/sh2a/setup-sh7206.c b/arch/sh/kernel/cpu/sh2a/setup-sh7206.c new file mode 100644 index 000000000..8d8d35485 --- /dev/null +++ b/arch/sh/kernel/cpu/sh2a/setup-sh7206.c @@ -0,0 +1,293 @@ +/* + * SH7206 Setup + * + * Copyright (C) 2006 Yoshinori Sato + * 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/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <linux/serial_sci.h> +#include <linux/sh_timer.h> +#include <linux/io.h> + +enum { + UNUSED = 0, + + /* interrupt sources */ + IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7, + PINT0, PINT1, PINT2, PINT3, PINT4, PINT5, PINT6, PINT7, + ADC_ADI0, ADC_ADI1, + + DMAC0, DMAC1, DMAC2, DMAC3, DMAC4, DMAC5, DMAC6, DMAC7, + + MTU0_ABCD, MTU0_VEF, MTU1_AB, MTU1_VU, MTU2_AB, MTU2_VU, + MTU3_ABCD, MTU4_ABCD, MTU5, POE2_12, MTU3S_ABCD, MTU4S_ABCD, MTU5S, + IIC3, + + CMT0, CMT1, BSC, WDT, + + MTU2_TCI3V, MTU2_TCI4V, MTU2S_TCI3V, MTU2S_TCI4V, + + POE2_OEI3, + + SCIF0, SCIF1, SCIF2, SCIF3, + + /* interrupt groups */ + PINT, +}; + +static struct intc_vect vectors[] __initdata = { + INTC_IRQ(IRQ0, 64), INTC_IRQ(IRQ1, 65), + INTC_IRQ(IRQ2, 66), INTC_IRQ(IRQ3, 67), + INTC_IRQ(IRQ4, 68), INTC_IRQ(IRQ5, 69), + INTC_IRQ(IRQ6, 70), INTC_IRQ(IRQ7, 71), + INTC_IRQ(PINT0, 80), INTC_IRQ(PINT1, 81), + INTC_IRQ(PINT2, 82), INTC_IRQ(PINT3, 83), + INTC_IRQ(PINT4, 84), INTC_IRQ(PINT5, 85), + INTC_IRQ(PINT6, 86), INTC_IRQ(PINT7, 87), + INTC_IRQ(ADC_ADI0, 92), INTC_IRQ(ADC_ADI1, 96), + INTC_IRQ(DMAC0, 108), INTC_IRQ(DMAC0, 109), + INTC_IRQ(DMAC1, 112), INTC_IRQ(DMAC1, 113), + INTC_IRQ(DMAC2, 116), INTC_IRQ(DMAC2, 117), + INTC_IRQ(DMAC3, 120), INTC_IRQ(DMAC3, 121), + INTC_IRQ(DMAC4, 124), INTC_IRQ(DMAC4, 125), + INTC_IRQ(DMAC5, 128), INTC_IRQ(DMAC5, 129), + INTC_IRQ(DMAC6, 132), INTC_IRQ(DMAC6, 133), + INTC_IRQ(DMAC7, 136), INTC_IRQ(DMAC7, 137), + INTC_IRQ(CMT0, 140), INTC_IRQ(CMT1, 144), + INTC_IRQ(BSC, 148), INTC_IRQ(WDT, 152), + INTC_IRQ(MTU0_ABCD, 156), INTC_IRQ(MTU0_ABCD, 157), + INTC_IRQ(MTU0_ABCD, 158), INTC_IRQ(MTU0_ABCD, 159), + INTC_IRQ(MTU0_VEF, 160), INTC_IRQ(MTU0_VEF, 161), + INTC_IRQ(MTU0_VEF, 162), + INTC_IRQ(MTU1_AB, 164), INTC_IRQ(MTU1_AB, 165), + INTC_IRQ(MTU1_VU, 168), INTC_IRQ(MTU1_VU, 169), + INTC_IRQ(MTU2_AB, 172), INTC_IRQ(MTU2_AB, 173), + INTC_IRQ(MTU2_VU, 176), INTC_IRQ(MTU2_VU, 177), + INTC_IRQ(MTU3_ABCD, 180), INTC_IRQ(MTU3_ABCD, 181), + INTC_IRQ(MTU3_ABCD, 182), INTC_IRQ(MTU3_ABCD, 183), + INTC_IRQ(MTU2_TCI3V, 184), + INTC_IRQ(MTU4_ABCD, 188), INTC_IRQ(MTU4_ABCD, 189), + INTC_IRQ(MTU4_ABCD, 190), INTC_IRQ(MTU4_ABCD, 191), + INTC_IRQ(MTU2_TCI4V, 192), + INTC_IRQ(MTU5, 196), INTC_IRQ(MTU5, 197), + INTC_IRQ(MTU5, 198), + INTC_IRQ(POE2_12, 200), INTC_IRQ(POE2_12, 201), + INTC_IRQ(MTU3S_ABCD, 204), INTC_IRQ(MTU3S_ABCD, 205), + INTC_IRQ(MTU3S_ABCD, 206), INTC_IRQ(MTU3S_ABCD, 207), + INTC_IRQ(MTU2S_TCI3V, 208), + INTC_IRQ(MTU4S_ABCD, 212), INTC_IRQ(MTU4S_ABCD, 213), + INTC_IRQ(MTU4S_ABCD, 214), INTC_IRQ(MTU4S_ABCD, 215), + INTC_IRQ(MTU2S_TCI4V, 216), + INTC_IRQ(MTU5S, 220), INTC_IRQ(MTU5S, 221), + INTC_IRQ(MTU5S, 222), + INTC_IRQ(POE2_OEI3, 224), + INTC_IRQ(IIC3, 228), INTC_IRQ(IIC3, 229), + INTC_IRQ(IIC3, 230), INTC_IRQ(IIC3, 231), + INTC_IRQ(IIC3, 232), + INTC_IRQ(SCIF0, 240), INTC_IRQ(SCIF0, 241), + INTC_IRQ(SCIF0, 242), INTC_IRQ(SCIF0, 243), + INTC_IRQ(SCIF1, 244), INTC_IRQ(SCIF1, 245), + INTC_IRQ(SCIF1, 246), INTC_IRQ(SCIF1, 247), + INTC_IRQ(SCIF2, 248), INTC_IRQ(SCIF2, 249), + INTC_IRQ(SCIF2, 250), INTC_IRQ(SCIF2, 251), + INTC_IRQ(SCIF3, 252), INTC_IRQ(SCIF3, 253), + INTC_IRQ(SCIF3, 254), INTC_IRQ(SCIF3, 255), +}; + +static struct intc_group groups[] __initdata = { + INTC_GROUP(PINT, PINT0, PINT1, PINT2, PINT3, + PINT4, PINT5, PINT6, PINT7), +}; + +static struct intc_prio_reg prio_registers[] __initdata = { + { 0xfffe0818, 0, 16, 4, /* IPR01 */ { IRQ0, IRQ1, IRQ2, IRQ3 } }, + { 0xfffe081a, 0, 16, 4, /* IPR02 */ { IRQ4, IRQ5, IRQ6, IRQ7 } }, + { 0xfffe0820, 0, 16, 4, /* IPR05 */ { PINT, 0, ADC_ADI0, ADC_ADI1 } }, + { 0xfffe0c00, 0, 16, 4, /* IPR06 */ { DMAC0, DMAC1, DMAC2, DMAC3 } }, + { 0xfffe0c02, 0, 16, 4, /* IPR07 */ { DMAC4, DMAC5, DMAC6, DMAC7 } }, + { 0xfffe0c04, 0, 16, 4, /* IPR08 */ { CMT0, CMT1, BSC, WDT } }, + { 0xfffe0c06, 0, 16, 4, /* IPR09 */ { MTU0_ABCD, MTU0_VEF, + MTU1_AB, MTU1_VU } }, + { 0xfffe0c08, 0, 16, 4, /* IPR10 */ { MTU2_AB, MTU2_VU, + MTU3_ABCD, MTU2_TCI3V } }, + { 0xfffe0c0a, 0, 16, 4, /* IPR11 */ { MTU4_ABCD, MTU2_TCI4V, + MTU5, POE2_12 } }, + { 0xfffe0c0c, 0, 16, 4, /* IPR12 */ { MTU3S_ABCD, MTU2S_TCI3V, + MTU4S_ABCD, MTU2S_TCI4V } }, + { 0xfffe0c0e, 0, 16, 4, /* IPR13 */ { MTU5S, POE2_OEI3, IIC3, 0 } }, + { 0xfffe0c10, 0, 16, 4, /* IPR14 */ { SCIF0, SCIF1, SCIF2, SCIF3 } }, +}; + +static struct intc_mask_reg mask_registers[] __initdata = { + { 0xfffe0808, 0, 16, /* PINTER */ + { 0, 0, 0, 0, 0, 0, 0, 0, + PINT7, PINT6, PINT5, PINT4, PINT3, PINT2, PINT1, PINT0 } }, +}; + +static DECLARE_INTC_DESC(intc_desc, "sh7206", vectors, groups, + mask_registers, prio_registers, NULL); + +static struct plat_sci_port scif0_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, +}; + +static struct resource scif0_resources[] = { + DEFINE_RES_MEM(0xfffe8000, 0x100), + DEFINE_RES_IRQ(240), +}; + +static struct platform_device scif0_device = { + .name = "sh-sci", + .id = 0, + .resource = scif0_resources, + .num_resources = ARRAY_SIZE(scif0_resources), + .dev = { + .platform_data = &scif0_platform_data, + }, +}; + +static struct plat_sci_port scif1_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, +}; + +static struct resource scif1_resources[] = { + DEFINE_RES_MEM(0xfffe8800, 0x100), + DEFINE_RES_IRQ(244), +}; + +static struct platform_device scif1_device = { + .name = "sh-sci", + .id = 1, + .resource = scif1_resources, + .num_resources = ARRAY_SIZE(scif1_resources), + .dev = { + .platform_data = &scif1_platform_data, + }, +}; + +static struct plat_sci_port scif2_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, +}; + +static struct resource scif2_resources[] = { + DEFINE_RES_MEM(0xfffe9000, 0x100), + DEFINE_RES_IRQ(248), +}; + +static struct platform_device scif2_device = { + .name = "sh-sci", + .id = 2, + .resource = scif2_resources, + .num_resources = ARRAY_SIZE(scif2_resources), + .dev = { + .platform_data = &scif2_platform_data, + }, +}; + +static struct plat_sci_port scif3_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, +}; + +static struct resource scif3_resources[] = { + DEFINE_RES_MEM(0xfffe9800, 0x100), + DEFINE_RES_IRQ(252), +}; + +static struct platform_device scif3_device = { + .name = "sh-sci", + .id = 3, + .resource = scif3_resources, + .num_resources = ARRAY_SIZE(scif3_resources), + .dev = { + .platform_data = &scif3_platform_data, + }, +}; + +static struct sh_timer_config cmt_platform_data = { + .channels_mask = 3, +}; + +static struct resource cmt_resources[] = { + DEFINE_RES_MEM(0xfffec000, 0x10), + DEFINE_RES_IRQ(140), + DEFINE_RES_IRQ(144), +}; + +static struct platform_device cmt_device = { + .name = "sh-cmt-16", + .id = 0, + .dev = { + .platform_data = &cmt_platform_data, + }, + .resource = cmt_resources, + .num_resources = ARRAY_SIZE(cmt_resources), +}; + +static struct resource mtu2_resources[] = { + DEFINE_RES_MEM(0xfffe4000, 0x400), + DEFINE_RES_IRQ_NAMED(156, "tgi0a"), + DEFINE_RES_IRQ_NAMED(164, "tgi1a"), + DEFINE_RES_IRQ_NAMED(180, "tgi2a"), +}; + +static struct platform_device mtu2_device = { + .name = "sh-mtu2s", + .id = -1, + .resource = mtu2_resources, + .num_resources = ARRAY_SIZE(mtu2_resources), +}; + +static struct platform_device *sh7206_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + &scif3_device, + &cmt_device, + &mtu2_device, +}; + +static int __init sh7206_devices_setup(void) +{ + return platform_add_devices(sh7206_devices, + ARRAY_SIZE(sh7206_devices)); +} +arch_initcall(sh7206_devices_setup); + +void __init plat_irq_setup(void) +{ + register_intc_controller(&intc_desc); +} + +static struct platform_device *sh7206_early_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + &scif3_device, + &cmt_device, + &mtu2_device, +}; + +#define STBCR3 0xfffe0408 +#define STBCR4 0xfffe040c + +void __init plat_early_device_setup(void) +{ + /* enable CMT clock */ + __raw_writeb(__raw_readb(STBCR4) & ~0x04, STBCR4); + + /* enable MTU2 clock */ + __raw_writeb(__raw_readb(STBCR3) & ~0x20, STBCR3); + + early_platform_add_devices(sh7206_early_devices, + ARRAY_SIZE(sh7206_early_devices)); +} diff --git a/arch/sh/kernel/cpu/sh2a/setup-sh7264.c b/arch/sh/kernel/cpu/sh2a/setup-sh7264.c new file mode 100644 index 000000000..ab71eab69 --- /dev/null +++ b/arch/sh/kernel/cpu/sh2a/setup-sh7264.c @@ -0,0 +1,554 @@ +/* + * SH7264 Setup + * + * Copyright (C) 2012 Renesas Electronics Europe Ltd + * + * 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/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <linux/serial_sci.h> +#include <linux/usb/r8a66597.h> +#include <linux/sh_timer.h> +#include <linux/io.h> + +enum { + UNUSED = 0, + + /* interrupt sources */ + IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7, + PINT0, PINT1, PINT2, PINT3, PINT4, PINT5, PINT6, PINT7, + + DMAC0, DMAC1, DMAC2, DMAC3, DMAC4, DMAC5, DMAC6, DMAC7, + DMAC8, DMAC9, DMAC10, DMAC11, DMAC12, DMAC13, DMAC14, DMAC15, + USB, VDC3, CMT0, CMT1, BSC, WDT, + MTU0_ABCD, MTU0_VEF, MTU1_AB, MTU1_VU, MTU2_AB, MTU2_VU, + MTU3_ABCD, MTU3_TCI3V, MTU4_ABCD, MTU4_TCI4V, + PWMT1, PWMT2, ADC_ADI, + SSIF0, SSII1, SSII2, SSII3, + RSPDIF, + IIC30, IIC31, IIC32, IIC33, + SCIF0_BRI, SCIF0_ERI, SCIF0_RXI, SCIF0_TXI, + SCIF1_BRI, SCIF1_ERI, SCIF1_RXI, SCIF1_TXI, + SCIF2_BRI, SCIF2_ERI, SCIF2_RXI, SCIF2_TXI, + SCIF3_BRI, SCIF3_ERI, SCIF3_RXI, SCIF3_TXI, + SCIF4_BRI, SCIF4_ERI, SCIF4_RXI, SCIF4_TXI, + SCIF5_BRI, SCIF5_ERI, SCIF5_RXI, SCIF5_TXI, + SCIF6_BRI, SCIF6_ERI, SCIF6_RXI, SCIF6_TXI, + SCIF7_BRI, SCIF7_ERI, SCIF7_RXI, SCIF7_TXI, + SIO_FIFO, RSPIC0, RSPIC1, + RCAN0, RCAN1, IEBC, CD_ROMD, + NFMC, SDHI, RTC, + SRCC0, SRCC1, DCOMU, OFFI, IFEI, + + /* interrupt groups */ + PINT, SCIF0, SCIF1, SCIF2, SCIF3, SCIF4, SCIF5, SCIF6, SCIF7, +}; + +static struct intc_vect vectors[] __initdata = { + INTC_IRQ(IRQ0, 64), INTC_IRQ(IRQ1, 65), + INTC_IRQ(IRQ2, 66), INTC_IRQ(IRQ3, 67), + INTC_IRQ(IRQ4, 68), INTC_IRQ(IRQ5, 69), + INTC_IRQ(IRQ6, 70), INTC_IRQ(IRQ7, 71), + + INTC_IRQ(PINT0, 80), INTC_IRQ(PINT1, 81), + INTC_IRQ(PINT2, 82), INTC_IRQ(PINT3, 83), + INTC_IRQ(PINT4, 84), INTC_IRQ(PINT5, 85), + INTC_IRQ(PINT6, 86), INTC_IRQ(PINT7, 87), + + INTC_IRQ(DMAC0, 108), INTC_IRQ(DMAC0, 109), + INTC_IRQ(DMAC1, 112), INTC_IRQ(DMAC1, 113), + INTC_IRQ(DMAC2, 116), INTC_IRQ(DMAC2, 117), + INTC_IRQ(DMAC3, 120), INTC_IRQ(DMAC3, 121), + INTC_IRQ(DMAC4, 124), INTC_IRQ(DMAC4, 125), + INTC_IRQ(DMAC5, 128), INTC_IRQ(DMAC5, 129), + INTC_IRQ(DMAC6, 132), INTC_IRQ(DMAC6, 133), + INTC_IRQ(DMAC7, 136), INTC_IRQ(DMAC7, 137), + INTC_IRQ(DMAC8, 140), INTC_IRQ(DMAC8, 141), + INTC_IRQ(DMAC9, 144), INTC_IRQ(DMAC9, 145), + INTC_IRQ(DMAC10, 148), INTC_IRQ(DMAC10, 149), + INTC_IRQ(DMAC11, 152), INTC_IRQ(DMAC11, 153), + INTC_IRQ(DMAC12, 156), INTC_IRQ(DMAC12, 157), + INTC_IRQ(DMAC13, 160), INTC_IRQ(DMAC13, 161), + INTC_IRQ(DMAC14, 164), INTC_IRQ(DMAC14, 165), + INTC_IRQ(DMAC15, 168), INTC_IRQ(DMAC15, 169), + + INTC_IRQ(USB, 170), + INTC_IRQ(VDC3, 171), INTC_IRQ(VDC3, 172), + INTC_IRQ(VDC3, 173), INTC_IRQ(VDC3, 174), + INTC_IRQ(CMT0, 175), INTC_IRQ(CMT1, 176), + INTC_IRQ(BSC, 177), INTC_IRQ(WDT, 178), + + INTC_IRQ(MTU0_ABCD, 179), INTC_IRQ(MTU0_ABCD, 180), + INTC_IRQ(MTU0_ABCD, 181), INTC_IRQ(MTU0_ABCD, 182), + INTC_IRQ(MTU0_VEF, 183), + INTC_IRQ(MTU0_VEF, 184), INTC_IRQ(MTU0_VEF, 185), + INTC_IRQ(MTU1_AB, 186), INTC_IRQ(MTU1_AB, 187), + INTC_IRQ(MTU1_VU, 188), INTC_IRQ(MTU1_VU, 189), + INTC_IRQ(MTU2_AB, 190), INTC_IRQ(MTU2_AB, 191), + INTC_IRQ(MTU2_VU, 192), INTC_IRQ(MTU2_VU, 193), + INTC_IRQ(MTU3_ABCD, 194), INTC_IRQ(MTU3_ABCD, 195), + INTC_IRQ(MTU3_ABCD, 196), INTC_IRQ(MTU3_ABCD, 197), + INTC_IRQ(MTU3_TCI3V, 198), + INTC_IRQ(MTU4_ABCD, 199), INTC_IRQ(MTU4_ABCD, 200), + INTC_IRQ(MTU4_ABCD, 201), INTC_IRQ(MTU4_ABCD, 202), + INTC_IRQ(MTU4_TCI4V, 203), + + INTC_IRQ(PWMT1, 204), INTC_IRQ(PWMT2, 205), + + INTC_IRQ(ADC_ADI, 206), + + INTC_IRQ(SSIF0, 207), INTC_IRQ(SSIF0, 208), + INTC_IRQ(SSIF0, 209), + INTC_IRQ(SSII1, 210), INTC_IRQ(SSII1, 211), + INTC_IRQ(SSII2, 212), INTC_IRQ(SSII2, 213), + INTC_IRQ(SSII3, 214), INTC_IRQ(SSII3, 215), + + INTC_IRQ(RSPDIF, 216), + + INTC_IRQ(IIC30, 217), INTC_IRQ(IIC30, 218), + INTC_IRQ(IIC30, 219), INTC_IRQ(IIC30, 220), + INTC_IRQ(IIC30, 221), + INTC_IRQ(IIC31, 222), INTC_IRQ(IIC31, 223), + INTC_IRQ(IIC31, 224), INTC_IRQ(IIC31, 225), + INTC_IRQ(IIC31, 226), + INTC_IRQ(IIC32, 227), INTC_IRQ(IIC32, 228), + INTC_IRQ(IIC32, 229), INTC_IRQ(IIC32, 230), + INTC_IRQ(IIC32, 231), + + INTC_IRQ(SCIF0_BRI, 232), INTC_IRQ(SCIF0_ERI, 233), + INTC_IRQ(SCIF0_RXI, 234), INTC_IRQ(SCIF0_TXI, 235), + INTC_IRQ(SCIF1_BRI, 236), INTC_IRQ(SCIF1_ERI, 237), + INTC_IRQ(SCIF1_RXI, 238), INTC_IRQ(SCIF1_TXI, 239), + INTC_IRQ(SCIF2_BRI, 240), INTC_IRQ(SCIF2_ERI, 241), + INTC_IRQ(SCIF2_RXI, 242), INTC_IRQ(SCIF2_TXI, 243), + INTC_IRQ(SCIF3_BRI, 244), INTC_IRQ(SCIF3_ERI, 245), + INTC_IRQ(SCIF3_RXI, 246), INTC_IRQ(SCIF3_TXI, 247), + INTC_IRQ(SCIF4_BRI, 248), INTC_IRQ(SCIF4_ERI, 249), + INTC_IRQ(SCIF4_RXI, 250), INTC_IRQ(SCIF4_TXI, 251), + INTC_IRQ(SCIF5_BRI, 252), INTC_IRQ(SCIF5_ERI, 253), + INTC_IRQ(SCIF5_RXI, 254), INTC_IRQ(SCIF5_TXI, 255), + INTC_IRQ(SCIF6_BRI, 256), INTC_IRQ(SCIF6_ERI, 257), + INTC_IRQ(SCIF6_RXI, 258), INTC_IRQ(SCIF6_TXI, 259), + INTC_IRQ(SCIF7_BRI, 260), INTC_IRQ(SCIF7_ERI, 261), + INTC_IRQ(SCIF7_RXI, 262), INTC_IRQ(SCIF7_TXI, 263), + + INTC_IRQ(SIO_FIFO, 264), + + INTC_IRQ(RSPIC0, 265), INTC_IRQ(RSPIC0, 266), + INTC_IRQ(RSPIC0, 267), + INTC_IRQ(RSPIC1, 268), INTC_IRQ(RSPIC1, 269), + INTC_IRQ(RSPIC1, 270), + + INTC_IRQ(RCAN0, 271), INTC_IRQ(RCAN0, 272), + INTC_IRQ(RCAN0, 273), INTC_IRQ(RCAN0, 274), + INTC_IRQ(RCAN0, 275), + INTC_IRQ(RCAN1, 276), INTC_IRQ(RCAN1, 277), + INTC_IRQ(RCAN1, 278), INTC_IRQ(RCAN1, 279), + INTC_IRQ(RCAN1, 280), + + INTC_IRQ(IEBC, 281), + + INTC_IRQ(CD_ROMD, 282), INTC_IRQ(CD_ROMD, 283), + INTC_IRQ(CD_ROMD, 284), INTC_IRQ(CD_ROMD, 285), + INTC_IRQ(CD_ROMD, 286), INTC_IRQ(CD_ROMD, 287), + + INTC_IRQ(NFMC, 288), INTC_IRQ(NFMC, 289), + INTC_IRQ(NFMC, 290), INTC_IRQ(NFMC, 291), + + INTC_IRQ(SDHI, 292), INTC_IRQ(SDHI, 293), + INTC_IRQ(SDHI, 294), + + INTC_IRQ(RTC, 296), INTC_IRQ(RTC, 297), + INTC_IRQ(RTC, 298), + + INTC_IRQ(SRCC0, 299), INTC_IRQ(SRCC0, 300), + INTC_IRQ(SRCC0, 301), INTC_IRQ(SRCC0, 302), + INTC_IRQ(SRCC0, 303), + INTC_IRQ(SRCC1, 304), INTC_IRQ(SRCC1, 305), + INTC_IRQ(SRCC1, 306), INTC_IRQ(SRCC1, 307), + INTC_IRQ(SRCC1, 308), + + INTC_IRQ(DCOMU, 310), INTC_IRQ(DCOMU, 311), + INTC_IRQ(DCOMU, 312), +}; + +static struct intc_group groups[] __initdata = { + INTC_GROUP(PINT, PINT0, PINT1, PINT2, PINT3, + PINT4, PINT5, PINT6, PINT7), + INTC_GROUP(SCIF0, SCIF0_BRI, SCIF0_ERI, SCIF0_RXI, SCIF0_TXI), + INTC_GROUP(SCIF1, SCIF1_BRI, SCIF1_ERI, SCIF1_RXI, SCIF1_TXI), + INTC_GROUP(SCIF2, SCIF2_BRI, SCIF2_ERI, SCIF2_RXI, SCIF2_TXI), + INTC_GROUP(SCIF3, SCIF3_BRI, SCIF3_ERI, SCIF3_RXI, SCIF3_TXI), + INTC_GROUP(SCIF4, SCIF4_BRI, SCIF4_ERI, SCIF4_RXI, SCIF4_TXI), + INTC_GROUP(SCIF5, SCIF5_BRI, SCIF5_ERI, SCIF5_RXI, SCIF5_TXI), + INTC_GROUP(SCIF6, SCIF6_BRI, SCIF6_ERI, SCIF6_RXI, SCIF6_TXI), + INTC_GROUP(SCIF7, SCIF7_BRI, SCIF7_ERI, SCIF7_RXI, SCIF7_TXI), +}; + +static struct intc_prio_reg prio_registers[] __initdata = { + { 0xfffe0818, 0, 16, 4, /* IPR01 */ { IRQ0, IRQ1, IRQ2, IRQ3 } }, + { 0xfffe081a, 0, 16, 4, /* IPR02 */ { IRQ4, IRQ5, IRQ6, IRQ7 } }, + { 0xfffe0820, 0, 16, 4, /* IPR05 */ { PINT, 0, 0, 0 } }, + { 0xfffe0c00, 0, 16, 4, /* IPR06 */ { DMAC0, DMAC1, DMAC2, DMAC3 } }, + { 0xfffe0c02, 0, 16, 4, /* IPR07 */ { DMAC4, DMAC5, DMAC6, DMAC7 } }, + { 0xfffe0c04, 0, 16, 4, /* IPR08 */ { DMAC8, DMAC9, + DMAC10, DMAC11 } }, + { 0xfffe0c06, 0, 16, 4, /* IPR09 */ { DMAC12, DMAC13, + DMAC14, DMAC15 } }, + { 0xfffe0c08, 0, 16, 4, /* IPR10 */ { USB, VDC3, CMT0, CMT1 } }, + { 0xfffe0c0a, 0, 16, 4, /* IPR11 */ { BSC, WDT, MTU0_ABCD, MTU0_VEF } }, + { 0xfffe0c0c, 0, 16, 4, /* IPR12 */ { MTU1_AB, MTU1_VU, + MTU2_AB, MTU2_VU } }, + { 0xfffe0c0e, 0, 16, 4, /* IPR13 */ { MTU3_ABCD, MTU3_TCI3V, + MTU4_ABCD, MTU4_TCI4V } }, + { 0xfffe0c10, 0, 16, 4, /* IPR14 */ { PWMT1, PWMT2, ADC_ADI, 0 } }, + { 0xfffe0c12, 0, 16, 4, /* IPR15 */ { SSIF0, SSII1, SSII2, SSII3 } }, + { 0xfffe0c14, 0, 16, 4, /* IPR16 */ { RSPDIF, IIC30, IIC31, IIC32 } }, + { 0xfffe0c16, 0, 16, 4, /* IPR17 */ { SCIF0, SCIF1, SCIF2, SCIF3 } }, + { 0xfffe0c18, 0, 16, 4, /* IPR18 */ { SCIF4, SCIF5, SCIF6, SCIF7 } }, + { 0xfffe0c1a, 0, 16, 4, /* IPR19 */ { SIO_FIFO, 0, RSPIC0, RSPIC1, } }, + { 0xfffe0c1c, 0, 16, 4, /* IPR20 */ { RCAN0, RCAN1, IEBC, CD_ROMD } }, + { 0xfffe0c1e, 0, 16, 4, /* IPR21 */ { NFMC, SDHI, RTC, 0 } }, + { 0xfffe0c20, 0, 16, 4, /* IPR22 */ { SRCC0, SRCC1, 0, DCOMU } }, +}; + +static struct intc_mask_reg mask_registers[] __initdata = { + { 0xfffe0808, 0, 16, /* PINTER */ + { 0, 0, 0, 0, 0, 0, 0, 0, + PINT7, PINT6, PINT5, PINT4, PINT3, PINT2, PINT1, PINT0 } }, +}; + +static DECLARE_INTC_DESC(intc_desc, "sh7264", vectors, groups, + mask_registers, prio_registers, NULL); + +static struct plat_sci_port scif0_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH2_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif0_resources[] = { + DEFINE_RES_MEM(0xfffe8000, 0x100), + DEFINE_RES_IRQ(233), + DEFINE_RES_IRQ(234), + DEFINE_RES_IRQ(235), + DEFINE_RES_IRQ(232), +}; + +static struct platform_device scif0_device = { + .name = "sh-sci", + .id = 0, + .resource = scif0_resources, + .num_resources = ARRAY_SIZE(scif0_resources), + .dev = { + .platform_data = &scif0_platform_data, + }, +}; + +static struct plat_sci_port scif1_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH2_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif1_resources[] = { + DEFINE_RES_MEM(0xfffe8800, 0x100), + DEFINE_RES_IRQ(237), + DEFINE_RES_IRQ(238), + DEFINE_RES_IRQ(239), + DEFINE_RES_IRQ(236), +}; + +static struct platform_device scif1_device = { + .name = "sh-sci", + .id = 1, + .resource = scif1_resources, + .num_resources = ARRAY_SIZE(scif1_resources), + .dev = { + .platform_data = &scif1_platform_data, + }, +}; + +static struct plat_sci_port scif2_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH2_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif2_resources[] = { + DEFINE_RES_MEM(0xfffe9000, 0x100), + DEFINE_RES_IRQ(241), + DEFINE_RES_IRQ(242), + DEFINE_RES_IRQ(243), + DEFINE_RES_IRQ(240), +}; + +static struct platform_device scif2_device = { + .name = "sh-sci", + .id = 2, + .resource = scif2_resources, + .num_resources = ARRAY_SIZE(scif2_resources), + .dev = { + .platform_data = &scif2_platform_data, + }, +}; + +static struct plat_sci_port scif3_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH2_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif3_resources[] = { + DEFINE_RES_MEM(0xfffe9800, 0x100), + DEFINE_RES_IRQ(245), + DEFINE_RES_IRQ(246), + DEFINE_RES_IRQ(247), + DEFINE_RES_IRQ(244), +}; + +static struct platform_device scif3_device = { + .name = "sh-sci", + .id = 3, + .resource = scif3_resources, + .num_resources = ARRAY_SIZE(scif3_resources), + .dev = { + .platform_data = &scif3_platform_data, + }, +}; + +static struct plat_sci_port scif4_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH2_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif4_resources[] = { + DEFINE_RES_MEM(0xfffea000, 0x100), + DEFINE_RES_IRQ(249), + DEFINE_RES_IRQ(250), + DEFINE_RES_IRQ(251), + DEFINE_RES_IRQ(248), +}; + +static struct platform_device scif4_device = { + .name = "sh-sci", + .id = 4, + .resource = scif4_resources, + .num_resources = ARRAY_SIZE(scif4_resources), + .dev = { + .platform_data = &scif4_platform_data, + }, +}; + +static struct plat_sci_port scif5_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH2_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif5_resources[] = { + DEFINE_RES_MEM(0xfffea800, 0x100), + DEFINE_RES_IRQ(253), + DEFINE_RES_IRQ(254), + DEFINE_RES_IRQ(255), + DEFINE_RES_IRQ(252), +}; + +static struct platform_device scif5_device = { + .name = "sh-sci", + .id = 5, + .resource = scif5_resources, + .num_resources = ARRAY_SIZE(scif5_resources), + .dev = { + .platform_data = &scif5_platform_data, + }, +}; + +static struct plat_sci_port scif6_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH2_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif6_resources[] = { + DEFINE_RES_MEM(0xfffeb000, 0x100), + DEFINE_RES_IRQ(257), + DEFINE_RES_IRQ(258), + DEFINE_RES_IRQ(259), + DEFINE_RES_IRQ(256), +}; + +static struct platform_device scif6_device = { + .name = "sh-sci", + .id = 6, + .resource = scif6_resources, + .num_resources = ARRAY_SIZE(scif6_resources), + .dev = { + .platform_data = &scif6_platform_data, + }, +}; + +static struct plat_sci_port scif7_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH2_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif7_resources[] = { + DEFINE_RES_MEM(0xfffeb800, 0x100), + DEFINE_RES_IRQ(261), + DEFINE_RES_IRQ(262), + DEFINE_RES_IRQ(263), + DEFINE_RES_IRQ(260), +}; + +static struct platform_device scif7_device = { + .name = "sh-sci", + .id = 7, + .resource = scif7_resources, + .num_resources = ARRAY_SIZE(scif7_resources), + .dev = { + .platform_data = &scif7_platform_data, + }, +}; + +static struct sh_timer_config cmt_platform_data = { + .channels_mask = 3, +}; + +static struct resource cmt_resources[] = { + DEFINE_RES_MEM(0xfffec000, 0x10), + DEFINE_RES_IRQ(175), + DEFINE_RES_IRQ(176), +}; + +static struct platform_device cmt_device = { + .name = "sh-cmt-16", + .id = 0, + .dev = { + .platform_data = &cmt_platform_data, + }, + .resource = cmt_resources, + .num_resources = ARRAY_SIZE(cmt_resources), +}; + +static struct resource mtu2_resources[] = { + DEFINE_RES_MEM(0xfffe4000, 0x400), + DEFINE_RES_IRQ_NAMED(179, "tgi0a"), + DEFINE_RES_IRQ_NAMED(186, "tgi1a"), +}; + +static struct platform_device mtu2_device = { + .name = "sh-mtu2", + .id = -1, + .resource = mtu2_resources, + .num_resources = ARRAY_SIZE(mtu2_resources), +}; + +static struct resource rtc_resources[] = { + [0] = { + .start = 0xfffe6000, + .end = 0xfffe6000 + 0x30 - 1, + .flags = IORESOURCE_IO, + }, + [1] = { + /* Shared Period/Carry/Alarm IRQ */ + .start = 296, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device rtc_device = { + .name = "sh-rtc", + .id = -1, + .num_resources = ARRAY_SIZE(rtc_resources), + .resource = rtc_resources, +}; + +/* USB Host */ +static void usb_port_power(int port, int power) +{ + __raw_writew(0x200 , 0xffffc0c2) ; /* Initialise UACS25 */ +} + +static struct r8a66597_platdata r8a66597_data = { + .on_chip = 1, + .endian = 1, + .port_power = usb_port_power, +}; + +static struct resource r8a66597_usb_host_resources[] = { + [0] = { + .start = 0xffffc000, + .end = 0xffffc0e4, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = 170, + .end = 170, + .flags = IORESOURCE_IRQ | IRQF_TRIGGER_LOW, + }, +}; + +static struct platform_device r8a66597_usb_host_device = { + .name = "r8a66597_hcd", + .id = 0, + .dev = { + .dma_mask = NULL, /* not use dma */ + .coherent_dma_mask = 0xffffffff, + .platform_data = &r8a66597_data, + }, + .num_resources = ARRAY_SIZE(r8a66597_usb_host_resources), + .resource = r8a66597_usb_host_resources, +}; + +static struct platform_device *sh7264_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + &scif3_device, + &scif4_device, + &scif5_device, + &scif6_device, + &scif7_device, + &cmt_device, + &mtu2_device, + &rtc_device, + &r8a66597_usb_host_device, +}; + +static int __init sh7264_devices_setup(void) +{ + return platform_add_devices(sh7264_devices, + ARRAY_SIZE(sh7264_devices)); +} +arch_initcall(sh7264_devices_setup); + +void __init plat_irq_setup(void) +{ + register_intc_controller(&intc_desc); +} + +static struct platform_device *sh7264_early_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + &scif3_device, + &scif4_device, + &scif5_device, + &scif6_device, + &scif7_device, + &cmt_device, + &mtu2_device, +}; + +void __init plat_early_device_setup(void) +{ + early_platform_add_devices(sh7264_early_devices, + ARRAY_SIZE(sh7264_early_devices)); +} diff --git a/arch/sh/kernel/cpu/sh2a/setup-sh7269.c b/arch/sh/kernel/cpu/sh2a/setup-sh7269.c new file mode 100644 index 000000000..c7e81b209 --- /dev/null +++ b/arch/sh/kernel/cpu/sh2a/setup-sh7269.c @@ -0,0 +1,570 @@ +/* + * SH7269 Setup + * + * Copyright (C) 2012 Renesas Electronics Europe Ltd + * Copyright (C) 2012 Phil Edworthy + * + * 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/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <linux/serial_sci.h> +#include <linux/usb/r8a66597.h> +#include <linux/sh_timer.h> +#include <linux/io.h> + +enum { + UNUSED = 0, + + /* interrupt sources */ + IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7, + PINT0, PINT1, PINT2, PINT3, PINT4, PINT5, PINT6, PINT7, + + DMAC0, DMAC1, DMAC2, DMAC3, DMAC4, DMAC5, DMAC6, DMAC7, + DMAC8, DMAC9, DMAC10, DMAC11, DMAC12, DMAC13, DMAC14, DMAC15, + USB, VDC4, CMT0, CMT1, BSC, WDT, + MTU0_ABCD, MTU0_VEF, MTU1_AB, MTU1_VU, MTU2_AB, MTU2_VU, + MTU3_ABCD, MTU3_TCI3V, MTU4_ABCD, MTU4_TCI4V, + PWMT1, PWMT2, ADC_ADI, + SSIF0, SSII1, SSII2, SSII3, SSII4, SSII5, + RSPDIF, + IIC30, IIC31, IIC32, IIC33, + SCIF0_BRI, SCIF0_ERI, SCIF0_RXI, SCIF0_TXI, + SCIF1_BRI, SCIF1_ERI, SCIF1_RXI, SCIF1_TXI, + SCIF2_BRI, SCIF2_ERI, SCIF2_RXI, SCIF2_TXI, + SCIF3_BRI, SCIF3_ERI, SCIF3_RXI, SCIF3_TXI, + SCIF4_BRI, SCIF4_ERI, SCIF4_RXI, SCIF4_TXI, + SCIF5_BRI, SCIF5_ERI, SCIF5_RXI, SCIF5_TXI, + SCIF6_BRI, SCIF6_ERI, SCIF6_RXI, SCIF6_TXI, + SCIF7_BRI, SCIF7_ERI, SCIF7_RXI, SCIF7_TXI, + RCAN0, RCAN1, RCAN2, + RSPIC0, RSPIC1, + IEBC, CD_ROMD, + NFMC, + SDHI0, SDHI1, + RTC, + SRCC0, SRCC1, SRCC2, + + /* interrupt groups */ + PINT, SCIF0, SCIF1, SCIF2, SCIF3, SCIF4, SCIF5, SCIF6, SCIF7, +}; + +static struct intc_vect vectors[] __initdata = { + INTC_IRQ(IRQ0, 64), INTC_IRQ(IRQ1, 65), + INTC_IRQ(IRQ2, 66), INTC_IRQ(IRQ3, 67), + INTC_IRQ(IRQ4, 68), INTC_IRQ(IRQ5, 69), + INTC_IRQ(IRQ6, 70), INTC_IRQ(IRQ7, 71), + + INTC_IRQ(PINT0, 80), INTC_IRQ(PINT1, 81), + INTC_IRQ(PINT2, 82), INTC_IRQ(PINT3, 83), + INTC_IRQ(PINT4, 84), INTC_IRQ(PINT5, 85), + INTC_IRQ(PINT6, 86), INTC_IRQ(PINT7, 87), + + INTC_IRQ(DMAC0, 108), INTC_IRQ(DMAC0, 109), + INTC_IRQ(DMAC1, 112), INTC_IRQ(DMAC1, 113), + INTC_IRQ(DMAC2, 116), INTC_IRQ(DMAC2, 117), + INTC_IRQ(DMAC3, 120), INTC_IRQ(DMAC3, 121), + INTC_IRQ(DMAC4, 124), INTC_IRQ(DMAC4, 125), + INTC_IRQ(DMAC5, 128), INTC_IRQ(DMAC5, 129), + INTC_IRQ(DMAC6, 132), INTC_IRQ(DMAC6, 133), + INTC_IRQ(DMAC7, 136), INTC_IRQ(DMAC7, 137), + INTC_IRQ(DMAC8, 140), INTC_IRQ(DMAC8, 141), + INTC_IRQ(DMAC9, 144), INTC_IRQ(DMAC9, 145), + INTC_IRQ(DMAC10, 148), INTC_IRQ(DMAC10, 149), + INTC_IRQ(DMAC11, 152), INTC_IRQ(DMAC11, 153), + INTC_IRQ(DMAC12, 156), INTC_IRQ(DMAC12, 157), + INTC_IRQ(DMAC13, 160), INTC_IRQ(DMAC13, 161), + INTC_IRQ(DMAC14, 164), INTC_IRQ(DMAC14, 165), + INTC_IRQ(DMAC15, 168), INTC_IRQ(DMAC15, 169), + + INTC_IRQ(USB, 170), + + INTC_IRQ(VDC4, 171), INTC_IRQ(VDC4, 172), + INTC_IRQ(VDC4, 173), INTC_IRQ(VDC4, 174), + INTC_IRQ(VDC4, 175), INTC_IRQ(VDC4, 176), + INTC_IRQ(VDC4, 177), INTC_IRQ(VDC4, 177), + + INTC_IRQ(CMT0, 188), INTC_IRQ(CMT1, 189), + + INTC_IRQ(BSC, 190), INTC_IRQ(WDT, 191), + + INTC_IRQ(MTU0_ABCD, 192), INTC_IRQ(MTU0_ABCD, 193), + INTC_IRQ(MTU0_ABCD, 194), INTC_IRQ(MTU0_ABCD, 195), + INTC_IRQ(MTU0_VEF, 196), INTC_IRQ(MTU0_VEF, 197), + INTC_IRQ(MTU0_VEF, 198), + INTC_IRQ(MTU1_AB, 199), INTC_IRQ(MTU1_AB, 200), + INTC_IRQ(MTU1_VU, 201), INTC_IRQ(MTU1_VU, 202), + INTC_IRQ(MTU2_AB, 203), INTC_IRQ(MTU2_AB, 204), + INTC_IRQ(MTU2_VU, 205), INTC_IRQ(MTU2_VU, 206), + INTC_IRQ(MTU3_ABCD, 207), INTC_IRQ(MTU3_ABCD, 208), + INTC_IRQ(MTU3_ABCD, 209), INTC_IRQ(MTU3_ABCD, 210), + INTC_IRQ(MTU3_TCI3V, 211), + INTC_IRQ(MTU4_ABCD, 212), INTC_IRQ(MTU4_ABCD, 213), + INTC_IRQ(MTU4_ABCD, 214), INTC_IRQ(MTU4_ABCD, 215), + INTC_IRQ(MTU4_TCI4V, 216), + + INTC_IRQ(PWMT1, 217), INTC_IRQ(PWMT2, 218), + + INTC_IRQ(ADC_ADI, 223), + + INTC_IRQ(SSIF0, 224), INTC_IRQ(SSIF0, 225), + INTC_IRQ(SSIF0, 226), + INTC_IRQ(SSII1, 227), INTC_IRQ(SSII1, 228), + INTC_IRQ(SSII2, 229), INTC_IRQ(SSII2, 230), + INTC_IRQ(SSII3, 231), INTC_IRQ(SSII3, 232), + INTC_IRQ(SSII4, 233), INTC_IRQ(SSII4, 234), + INTC_IRQ(SSII5, 235), INTC_IRQ(SSII5, 236), + + INTC_IRQ(RSPDIF, 237), + + INTC_IRQ(IIC30, 238), INTC_IRQ(IIC30, 239), + INTC_IRQ(IIC30, 240), INTC_IRQ(IIC30, 241), + INTC_IRQ(IIC30, 242), + INTC_IRQ(IIC31, 243), INTC_IRQ(IIC31, 244), + INTC_IRQ(IIC31, 245), INTC_IRQ(IIC31, 246), + INTC_IRQ(IIC31, 247), + INTC_IRQ(IIC32, 248), INTC_IRQ(IIC32, 249), + INTC_IRQ(IIC32, 250), INTC_IRQ(IIC32, 251), + INTC_IRQ(IIC32, 252), + INTC_IRQ(IIC33, 253), INTC_IRQ(IIC33, 254), + INTC_IRQ(IIC33, 255), INTC_IRQ(IIC33, 256), + INTC_IRQ(IIC33, 257), + + INTC_IRQ(SCIF0_BRI, 258), INTC_IRQ(SCIF0_ERI, 259), + INTC_IRQ(SCIF0_RXI, 260), INTC_IRQ(SCIF0_TXI, 261), + INTC_IRQ(SCIF1_BRI, 262), INTC_IRQ(SCIF1_ERI, 263), + INTC_IRQ(SCIF1_RXI, 264), INTC_IRQ(SCIF1_TXI, 265), + INTC_IRQ(SCIF2_BRI, 266), INTC_IRQ(SCIF2_ERI, 267), + INTC_IRQ(SCIF2_RXI, 268), INTC_IRQ(SCIF2_TXI, 269), + INTC_IRQ(SCIF3_BRI, 270), INTC_IRQ(SCIF3_ERI, 271), + INTC_IRQ(SCIF3_RXI, 272), INTC_IRQ(SCIF3_TXI, 273), + INTC_IRQ(SCIF4_BRI, 274), INTC_IRQ(SCIF4_ERI, 275), + INTC_IRQ(SCIF4_RXI, 276), INTC_IRQ(SCIF4_TXI, 277), + INTC_IRQ(SCIF5_BRI, 278), INTC_IRQ(SCIF5_ERI, 279), + INTC_IRQ(SCIF5_RXI, 280), INTC_IRQ(SCIF5_TXI, 281), + INTC_IRQ(SCIF6_BRI, 282), INTC_IRQ(SCIF6_ERI, 283), + INTC_IRQ(SCIF6_RXI, 284), INTC_IRQ(SCIF6_TXI, 285), + INTC_IRQ(SCIF7_BRI, 286), INTC_IRQ(SCIF7_ERI, 287), + INTC_IRQ(SCIF7_RXI, 288), INTC_IRQ(SCIF7_TXI, 289), + + INTC_IRQ(RCAN0, 291), INTC_IRQ(RCAN0, 292), + INTC_IRQ(RCAN0, 293), INTC_IRQ(RCAN0, 294), + INTC_IRQ(RCAN0, 295), + INTC_IRQ(RCAN1, 296), INTC_IRQ(RCAN1, 297), + INTC_IRQ(RCAN1, 298), INTC_IRQ(RCAN1, 299), + INTC_IRQ(RCAN1, 300), + INTC_IRQ(RCAN2, 301), INTC_IRQ(RCAN2, 302), + INTC_IRQ(RCAN2, 303), INTC_IRQ(RCAN2, 304), + INTC_IRQ(RCAN2, 305), + + INTC_IRQ(RSPIC0, 306), INTC_IRQ(RSPIC0, 307), + INTC_IRQ(RSPIC0, 308), + INTC_IRQ(RSPIC1, 309), INTC_IRQ(RSPIC1, 310), + INTC_IRQ(RSPIC1, 311), + + INTC_IRQ(IEBC, 318), + + INTC_IRQ(CD_ROMD, 319), INTC_IRQ(CD_ROMD, 320), + INTC_IRQ(CD_ROMD, 321), INTC_IRQ(CD_ROMD, 322), + INTC_IRQ(CD_ROMD, 323), INTC_IRQ(CD_ROMD, 324), + + INTC_IRQ(NFMC, 325), INTC_IRQ(NFMC, 326), + INTC_IRQ(NFMC, 327), INTC_IRQ(NFMC, 328), + + INTC_IRQ(SDHI0, 332), INTC_IRQ(SDHI0, 333), + INTC_IRQ(SDHI0, 334), + INTC_IRQ(SDHI1, 335), INTC_IRQ(SDHI1, 336), + INTC_IRQ(SDHI1, 337), + + INTC_IRQ(RTC, 338), INTC_IRQ(RTC, 339), + INTC_IRQ(RTC, 340), + + INTC_IRQ(SRCC0, 341), INTC_IRQ(SRCC0, 342), + INTC_IRQ(SRCC0, 343), INTC_IRQ(SRCC0, 344), + INTC_IRQ(SRCC0, 345), + INTC_IRQ(SRCC1, 346), INTC_IRQ(SRCC1, 347), + INTC_IRQ(SRCC1, 348), INTC_IRQ(SRCC1, 349), + INTC_IRQ(SRCC1, 350), + INTC_IRQ(SRCC2, 351), INTC_IRQ(SRCC2, 352), + INTC_IRQ(SRCC2, 353), INTC_IRQ(SRCC2, 354), + INTC_IRQ(SRCC2, 355), +}; + +static struct intc_group groups[] __initdata = { + INTC_GROUP(PINT, PINT0, PINT1, PINT2, PINT3, + PINT4, PINT5, PINT6, PINT7), + INTC_GROUP(SCIF0, SCIF0_BRI, SCIF0_ERI, SCIF0_RXI, SCIF0_TXI), + INTC_GROUP(SCIF1, SCIF1_BRI, SCIF1_ERI, SCIF1_RXI, SCIF1_TXI), + INTC_GROUP(SCIF2, SCIF2_BRI, SCIF2_ERI, SCIF2_RXI, SCIF2_TXI), + INTC_GROUP(SCIF3, SCIF3_BRI, SCIF3_ERI, SCIF3_RXI, SCIF3_TXI), + INTC_GROUP(SCIF4, SCIF4_BRI, SCIF4_ERI, SCIF4_RXI, SCIF4_TXI), + INTC_GROUP(SCIF5, SCIF5_BRI, SCIF5_ERI, SCIF5_RXI, SCIF5_TXI), + INTC_GROUP(SCIF6, SCIF6_BRI, SCIF6_ERI, SCIF6_RXI, SCIF6_TXI), + INTC_GROUP(SCIF7, SCIF7_BRI, SCIF7_ERI, SCIF7_RXI, SCIF7_TXI), +}; + +static struct intc_prio_reg prio_registers[] __initdata = { + { 0xfffe0818, 0, 16, 4, /* IPR01 */ { IRQ0, IRQ1, IRQ2, IRQ3 } }, + { 0xfffe081a, 0, 16, 4, /* IPR02 */ { IRQ4, IRQ5, IRQ6, IRQ7 } }, + { 0xfffe0820, 0, 16, 4, /* IPR05 */ { PINT, 0, 0, 0 } }, + { 0xfffe0c00, 0, 16, 4, /* IPR06 */ { DMAC0, DMAC1, DMAC2, DMAC3 } }, + { 0xfffe0c02, 0, 16, 4, /* IPR07 */ { DMAC4, DMAC5, DMAC6, DMAC7 } }, + { 0xfffe0c04, 0, 16, 4, /* IPR08 */ { DMAC8, DMAC9, + DMAC10, DMAC11 } }, + { 0xfffe0c06, 0, 16, 4, /* IPR09 */ { DMAC12, DMAC13, + DMAC14, DMAC15 } }, + { 0xfffe0c08, 0, 16, 4, /* IPR10 */ { USB, VDC4, VDC4, VDC4 } }, + { 0xfffe0c0a, 0, 16, 4, /* IPR11 */ { 0, 0, 0, 0 } }, + { 0xfffe0c0c, 0, 16, 4, /* IPR12 */ { CMT0, CMT1, BSC, WDT } }, + { 0xfffe0c0e, 0, 16, 4, /* IPR13 */ { MTU0_ABCD, MTU0_VEF, + MTU1_AB, MTU1_VU } }, + { 0xfffe0c10, 0, 16, 4, /* IPR14 */ { MTU2_AB, MTU2_VU, + MTU3_ABCD, MTU3_TCI3V } }, + { 0xfffe0c12, 0, 16, 4, /* IPR15 */ { MTU4_ABCD, MTU4_TCI4V, + PWMT1, PWMT2 } }, + { 0xfffe0c14, 0, 16, 4, /* IPR16 */ { 0, 0, 0, 0 } }, + { 0xfffe0c16, 0, 16, 4, /* IPR17 */ { ADC_ADI, SSIF0, SSII1, SSII2 } }, + { 0xfffe0c18, 0, 16, 4, /* IPR18 */ { SSII3, SSII4, SSII5, RSPDIF} }, + { 0xfffe0c1a, 0, 16, 4, /* IPR19 */ { IIC30, IIC31, IIC32, IIC33 } }, + { 0xfffe0c1c, 0, 16, 4, /* IPR20 */ { SCIF0, SCIF1, SCIF2, SCIF3 } }, + { 0xfffe0c1e, 0, 16, 4, /* IPR21 */ { SCIF4, SCIF5, SCIF6, SCIF7 } }, + { 0xfffe0c20, 0, 16, 4, /* IPR22 */ { 0, RCAN0, RCAN1, RCAN2 } }, + { 0xfffe0c22, 0, 16, 4, /* IPR23 */ { RSPIC0, RSPIC1, 0, 0 } }, + { 0xfffe0c24, 0, 16, 4, /* IPR24 */ { IEBC, CD_ROMD, NFMC, 0 } }, + { 0xfffe0c26, 0, 16, 4, /* IPR25 */ { SDHI0, SDHI1, RTC, 0 } }, + { 0xfffe0c28, 0, 16, 4, /* IPR26 */ { SRCC0, SRCC1, SRCC2, 0 } }, +}; + +static struct intc_mask_reg mask_registers[] __initdata = { + { 0xfffe0808, 0, 16, /* PINTER */ + { 0, 0, 0, 0, 0, 0, 0, 0, + PINT7, PINT6, PINT5, PINT4, PINT3, PINT2, PINT1, PINT0 } }, +}; + +static DECLARE_INTC_DESC(intc_desc, "sh7269", vectors, groups, + mask_registers, prio_registers, NULL); + +static struct plat_sci_port scif0_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH2_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif0_resources[] = { + DEFINE_RES_MEM(0xe8007000, 0x100), + DEFINE_RES_IRQ(259), + DEFINE_RES_IRQ(260), + DEFINE_RES_IRQ(261), + DEFINE_RES_IRQ(258), +}; + +static struct platform_device scif0_device = { + .name = "sh-sci", + .id = 0, + .resource = scif0_resources, + .num_resources = ARRAY_SIZE(scif0_resources), + .dev = { + .platform_data = &scif0_platform_data, + }, +}; + +static struct plat_sci_port scif1_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH2_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif1_resources[] = { + DEFINE_RES_MEM(0xe8007800, 0x100), + DEFINE_RES_IRQ(263), + DEFINE_RES_IRQ(264), + DEFINE_RES_IRQ(265), + DEFINE_RES_IRQ(262), +}; + +static struct platform_device scif1_device = { + .name = "sh-sci", + .id = 1, + .resource = scif1_resources, + .num_resources = ARRAY_SIZE(scif1_resources), + .dev = { + .platform_data = &scif1_platform_data, + }, +}; + +static struct plat_sci_port scif2_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH2_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif2_resources[] = { + DEFINE_RES_MEM(0xe8008000, 0x100), + DEFINE_RES_IRQ(267), + DEFINE_RES_IRQ(268), + DEFINE_RES_IRQ(269), + DEFINE_RES_IRQ(266), +}; + +static struct platform_device scif2_device = { + .name = "sh-sci", + .id = 2, + .resource = scif2_resources, + .num_resources = ARRAY_SIZE(scif2_resources), + .dev = { + .platform_data = &scif2_platform_data, + }, +}; + +static struct plat_sci_port scif3_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH2_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif3_resources[] = { + DEFINE_RES_MEM(0xe8008800, 0x100), + DEFINE_RES_IRQ(271), + DEFINE_RES_IRQ(272), + DEFINE_RES_IRQ(273), + DEFINE_RES_IRQ(270), +}; + +static struct platform_device scif3_device = { + .name = "sh-sci", + .id = 3, + .resource = scif3_resources, + .num_resources = ARRAY_SIZE(scif3_resources), + .dev = { + .platform_data = &scif3_platform_data, + }, +}; + +static struct plat_sci_port scif4_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH2_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif4_resources[] = { + DEFINE_RES_MEM(0xe8009000, 0x100), + DEFINE_RES_IRQ(275), + DEFINE_RES_IRQ(276), + DEFINE_RES_IRQ(277), + DEFINE_RES_IRQ(274), +}; + +static struct platform_device scif4_device = { + .name = "sh-sci", + .id = 4, + .resource = scif4_resources, + .num_resources = ARRAY_SIZE(scif4_resources), + .dev = { + .platform_data = &scif4_platform_data, + }, +}; + +static struct plat_sci_port scif5_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH2_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif5_resources[] = { + DEFINE_RES_MEM(0xe8009800, 0x100), + DEFINE_RES_IRQ(279), + DEFINE_RES_IRQ(280), + DEFINE_RES_IRQ(281), + DEFINE_RES_IRQ(278), +}; + +static struct platform_device scif5_device = { + .name = "sh-sci", + .id = 5, + .resource = scif5_resources, + .num_resources = ARRAY_SIZE(scif5_resources), + .dev = { + .platform_data = &scif5_platform_data, + }, +}; + +static struct plat_sci_port scif6_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH2_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif6_resources[] = { + DEFINE_RES_MEM(0xe800a000, 0x100), + DEFINE_RES_IRQ(283), + DEFINE_RES_IRQ(284), + DEFINE_RES_IRQ(285), + DEFINE_RES_IRQ(282), +}; + +static struct platform_device scif6_device = { + .name = "sh-sci", + .id = 6, + .resource = scif6_resources, + .num_resources = ARRAY_SIZE(scif6_resources), + .dev = { + .platform_data = &scif6_platform_data, + }, +}; + +static struct plat_sci_port scif7_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH2_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif7_resources[] = { + DEFINE_RES_MEM(0xe800a800, 0x100), + DEFINE_RES_IRQ(287), + DEFINE_RES_IRQ(288), + DEFINE_RES_IRQ(289), + DEFINE_RES_IRQ(286), +}; + +static struct platform_device scif7_device = { + .name = "sh-sci", + .id = 7, + .resource = scif7_resources, + .num_resources = ARRAY_SIZE(scif7_resources), + .dev = { + .platform_data = &scif7_platform_data, + }, +}; + +static struct sh_timer_config cmt_platform_data = { + .channels_mask = 3, +}; + +static struct resource cmt_resources[] = { + DEFINE_RES_MEM(0xfffec000, 0x10), + DEFINE_RES_IRQ(188), + DEFINE_RES_IRQ(189), +}; + +static struct platform_device cmt_device = { + .name = "sh-cmt-16", + .id = 0, + .dev = { + .platform_data = &cmt_platform_data, + }, + .resource = cmt_resources, + .num_resources = ARRAY_SIZE(cmt_resources), +}; + +static struct resource mtu2_resources[] = { + DEFINE_RES_MEM(0xfffe4000, 0x400), + DEFINE_RES_IRQ_NAMED(192, "tgi0a"), + DEFINE_RES_IRQ_NAMED(203, "tgi1a"), +}; + +static struct platform_device mtu2_device = { + .name = "sh-mtu2", + .id = -1, + .resource = mtu2_resources, + .num_resources = ARRAY_SIZE(mtu2_resources), +}; + +static struct resource rtc_resources[] = { + [0] = { + .start = 0xfffe6000, + .end = 0xfffe6000 + 0x30 - 1, + .flags = IORESOURCE_IO, + }, + [1] = { + /* Shared Period/Carry/Alarm IRQ */ + .start = 338, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device rtc_device = { + .name = "sh-rtc", + .id = -1, + .num_resources = ARRAY_SIZE(rtc_resources), + .resource = rtc_resources, +}; + +/* USB Host */ +static struct r8a66597_platdata r8a66597_data = { + .on_chip = 1, + .endian = 1, +}; + +static struct resource r8a66597_usb_host_resources[] = { + [0] = { + .start = 0xe8010000, + .end = 0xe80100e4, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = 170, + .end = 170, + .flags = IORESOURCE_IRQ | IRQF_TRIGGER_LOW, + }, +}; + +static struct platform_device r8a66597_usb_host_device = { + .name = "r8a66597_hcd", + .id = 0, + .dev = { + .dma_mask = NULL, /* not use dma */ + .coherent_dma_mask = 0xffffffff, + .platform_data = &r8a66597_data, + }, + .num_resources = ARRAY_SIZE(r8a66597_usb_host_resources), + .resource = r8a66597_usb_host_resources, +}; + +static struct platform_device *sh7269_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + &scif3_device, + &scif4_device, + &scif5_device, + &scif6_device, + &scif7_device, + &cmt_device, + &mtu2_device, + &rtc_device, + &r8a66597_usb_host_device, +}; + +static int __init sh7269_devices_setup(void) +{ + return platform_add_devices(sh7269_devices, + ARRAY_SIZE(sh7269_devices)); +} +arch_initcall(sh7269_devices_setup); + +void __init plat_irq_setup(void) +{ + register_intc_controller(&intc_desc); +} + +static struct platform_device *sh7269_early_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + &scif3_device, + &scif4_device, + &scif5_device, + &scif6_device, + &scif7_device, + &cmt_device, + &mtu2_device, +}; + +void __init plat_early_device_setup(void) +{ + early_platform_add_devices(sh7269_early_devices, + ARRAY_SIZE(sh7269_early_devices)); +} diff --git a/arch/sh/kernel/cpu/sh3/Makefile b/arch/sh/kernel/cpu/sh3/Makefile new file mode 100644 index 000000000..1dcb43d93 --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/Makefile @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the Linux/SuperH SH-3 backends. +# + +obj-y := ex.o probe.o entry.o setup-sh3.o + +obj-$(CONFIG_HIBERNATION) += swsusp.o + +# CPU subtype setup +obj-$(CONFIG_CPU_SUBTYPE_SH7705) += setup-sh7705.o serial-sh770x.o +obj-$(CONFIG_CPU_SUBTYPE_SH7706) += setup-sh770x.o serial-sh770x.o +obj-$(CONFIG_CPU_SUBTYPE_SH7707) += setup-sh770x.o serial-sh770x.o +obj-$(CONFIG_CPU_SUBTYPE_SH7708) += setup-sh770x.o serial-sh770x.o +obj-$(CONFIG_CPU_SUBTYPE_SH7709) += setup-sh770x.o serial-sh770x.o +obj-$(CONFIG_CPU_SUBTYPE_SH7710) += setup-sh7710.o serial-sh7710.o +obj-$(CONFIG_CPU_SUBTYPE_SH7712) += setup-sh7710.o serial-sh7710.o +obj-$(CONFIG_CPU_SUBTYPE_SH7720) += setup-sh7720.o serial-sh7720.o +obj-$(CONFIG_CPU_SUBTYPE_SH7721) += setup-sh7720.o serial-sh7720.o + +# Primary on-chip clocks (common) +clock-$(CONFIG_CPU_SH3) := clock-sh3.o +clock-$(CONFIG_CPU_SUBTYPE_SH7705) := clock-sh7705.o +clock-$(CONFIG_CPU_SUBTYPE_SH7706) := clock-sh7706.o +clock-$(CONFIG_CPU_SUBTYPE_SH7709) := clock-sh7709.o +clock-$(CONFIG_CPU_SUBTYPE_SH7710) := clock-sh7710.o +clock-$(CONFIG_CPU_SUBTYPE_SH7720) := clock-sh7710.o +clock-$(CONFIG_CPU_SUBTYPE_SH7712) := clock-sh7712.o + +# Pinmux setup +pinmux-$(CONFIG_CPU_SUBTYPE_SH7720) := pinmux-sh7720.o + +obj-y += $(clock-y) +obj-$(CONFIG_GPIOLIB) += $(pinmux-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 000000000..90faa44ca --- /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 = __raw_readw(FRQCR); + int idx = ((frqcr & 0x2000) >> 11) | (frqcr & 0x0003); + + clk->rate *= pfc_divisors[idx]; +} + +static struct sh_clk_ops sh3_master_clk_ops = { + .init = master_clk_init, +}; + +static unsigned long module_clk_recalc(struct clk *clk) +{ + int frqcr = __raw_readw(FRQCR); + int idx = ((frqcr & 0x2000) >> 11) | (frqcr & 0x0003); + + return clk->parent->rate / pfc_divisors[idx]; +} + +static struct sh_clk_ops sh3_module_clk_ops = { + .recalc = module_clk_recalc, +}; + +static unsigned long bus_clk_recalc(struct clk *clk) +{ + int frqcr = __raw_readw(FRQCR); + int idx = ((frqcr & 0x8000) >> 13) | ((frqcr & 0x0030) >> 4); + + return clk->parent->rate / stc_multipliers[idx]; +} + +static struct sh_clk_ops sh3_bus_clk_ops = { + .recalc = bus_clk_recalc, +}; + +static unsigned long cpu_clk_recalc(struct clk *clk) +{ + int frqcr = __raw_readw(FRQCR); + int idx = ((frqcr & 0x4000) >> 12) | ((frqcr & 0x000c) >> 2); + + return clk->parent->rate / ifc_divisors[idx]; +} + +static struct sh_clk_ops sh3_cpu_clk_ops = { + .recalc = cpu_clk_recalc, +}; + +static struct sh_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 sh_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-sh7705.c b/arch/sh/kernel/cpu/sh3/clock-sh7705.c new file mode 100644 index 000000000..a8da4a998 --- /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[__raw_readw(FRQCR) & 0x0003]; +} + +static struct sh_clk_ops sh7705_master_clk_ops = { + .init = master_clk_init, +}; + +static unsigned long module_clk_recalc(struct clk *clk) +{ + int idx = __raw_readw(FRQCR) & 0x0003; + return clk->parent->rate / pfc_divisors[idx]; +} + +static struct sh_clk_ops sh7705_module_clk_ops = { + .recalc = module_clk_recalc, +}; + +static unsigned long bus_clk_recalc(struct clk *clk) +{ + int idx = (__raw_readw(FRQCR) & 0x0300) >> 8; + return clk->parent->rate / stc_multipliers[idx]; +} + +static struct sh_clk_ops sh7705_bus_clk_ops = { + .recalc = bus_clk_recalc, +}; + +static unsigned long cpu_clk_recalc(struct clk *clk) +{ + int idx = (__raw_readw(FRQCR) & 0x0030) >> 4; + return clk->parent->rate / ifc_divisors[idx]; +} + +static struct sh_clk_ops sh7705_cpu_clk_ops = { + .recalc = cpu_clk_recalc, +}; + +static struct sh_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 sh_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-sh7706.c b/arch/sh/kernel/cpu/sh3/clock-sh7706.c new file mode 100644 index 000000000..a4088e5b2 --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/clock-sh7706.c @@ -0,0 +1,84 @@ +/* + * arch/sh/kernel/cpu/sh3/clock-sh7706.c + * + * SH7706 support for the clock framework + * + * Copyright (C) 2006 Takashi YOSHII + * + * Based on arch/sh/kernel/cpu/sh3/clock-sh7709.c + * Copyright (C) 2005 Andriy Skulysh + * + * 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, 1, 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 master_clk_init(struct clk *clk) +{ + int frqcr = __raw_readw(FRQCR); + int idx = ((frqcr & 0x2000) >> 11) | (frqcr & 0x0003); + + clk->rate *= pfc_divisors[idx]; +} + +static struct sh_clk_ops sh7706_master_clk_ops = { + .init = master_clk_init, +}; + +static unsigned long module_clk_recalc(struct clk *clk) +{ + int frqcr = __raw_readw(FRQCR); + int idx = ((frqcr & 0x2000) >> 11) | (frqcr & 0x0003); + + return clk->parent->rate / pfc_divisors[idx]; +} + +static struct sh_clk_ops sh7706_module_clk_ops = { + .recalc = module_clk_recalc, +}; + +static unsigned long bus_clk_recalc(struct clk *clk) +{ + int frqcr = __raw_readw(FRQCR); + int idx = ((frqcr & 0x8000) >> 13) | ((frqcr & 0x0030) >> 4); + + return clk->parent->rate / stc_multipliers[idx]; +} + +static struct sh_clk_ops sh7706_bus_clk_ops = { + .recalc = bus_clk_recalc, +}; + +static unsigned long cpu_clk_recalc(struct clk *clk) +{ + int frqcr = __raw_readw(FRQCR); + int idx = ((frqcr & 0x4000) >> 12) | ((frqcr & 0x000c) >> 2); + + return clk->parent->rate / ifc_divisors[idx]; +} + +static struct sh_clk_ops sh7706_cpu_clk_ops = { + .recalc = cpu_clk_recalc, +}; + +static struct sh_clk_ops *sh7706_clk_ops[] = { + &sh7706_master_clk_ops, + &sh7706_module_clk_ops, + &sh7706_bus_clk_ops, + &sh7706_cpu_clk_ops, +}; + +void __init arch_init_clk_ops(struct sh_clk_ops **ops, int idx) +{ + if (idx < ARRAY_SIZE(sh7706_clk_ops)) + *ops = sh7706_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 000000000..54a6d4bcc --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/clock-sh7709.c @@ -0,0 +1,85 @@ +/* + * 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 master_clk_init(struct clk *clk) +{ + int frqcr = __raw_readw(FRQCR); + int idx = ((frqcr & 0x2000) >> 11) | (frqcr & 0x0003); + + clk->rate *= pfc_divisors[idx]; +} + +static struct sh_clk_ops sh7709_master_clk_ops = { + .init = master_clk_init, +}; + +static unsigned long module_clk_recalc(struct clk *clk) +{ + int frqcr = __raw_readw(FRQCR); + int idx = ((frqcr & 0x2000) >> 11) | (frqcr & 0x0003); + + return clk->parent->rate / pfc_divisors[idx]; +} + +static struct sh_clk_ops sh7709_module_clk_ops = { + .recalc = module_clk_recalc, +}; + +static unsigned long bus_clk_recalc(struct clk *clk) +{ + int frqcr = __raw_readw(FRQCR); + int idx = (frqcr & 0x0080) ? + ((frqcr & 0x8000) >> 13) | ((frqcr & 0x0030) >> 4) : 1; + + return clk->parent->rate * stc_multipliers[idx]; +} + +static struct sh_clk_ops sh7709_bus_clk_ops = { + .recalc = bus_clk_recalc, +}; + +static unsigned long cpu_clk_recalc(struct clk *clk) +{ + int frqcr = __raw_readw(FRQCR); + int idx = ((frqcr & 0x4000) >> 12) | ((frqcr & 0x000c) >> 2); + + return clk->parent->rate / ifc_divisors[idx]; +} + +static struct sh_clk_ops sh7709_cpu_clk_ops = { + .recalc = cpu_clk_recalc, +}; + +static struct sh_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 sh_clk_ops **ops, int idx) +{ + if (idx < ARRAY_SIZE(sh7709_clk_ops)) + *ops = sh7709_clk_ops[idx]; +} diff --git a/arch/sh/kernel/cpu/sh3/clock-sh7710.c b/arch/sh/kernel/cpu/sh3/clock-sh7710.c new file mode 100644 index 000000000..ce601b2e3 --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/clock-sh7710.c @@ -0,0 +1,78 @@ +/* + * arch/sh/kernel/cpu/sh3/clock-sh7710.c + * + * SH7710 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[__raw_readw(FRQCR) & 0x0007]; +} + +static struct sh_clk_ops sh7710_master_clk_ops = { + .init = master_clk_init, +}; + +static unsigned long module_clk_recalc(struct clk *clk) +{ + int idx = (__raw_readw(FRQCR) & 0x0007); + return clk->parent->rate / md_table[idx]; +} + +static struct sh_clk_ops sh7710_module_clk_ops = { + .recalc = module_clk_recalc, +}; + +static unsigned long bus_clk_recalc(struct clk *clk) +{ + int idx = (__raw_readw(FRQCR) & 0x0700) >> 8; + return clk->parent->rate / md_table[idx]; +} + +static struct sh_clk_ops sh7710_bus_clk_ops = { + .recalc = bus_clk_recalc, +}; + +static unsigned long cpu_clk_recalc(struct clk *clk) +{ + int idx = (__raw_readw(FRQCR) & 0x0070) >> 4; + return clk->parent->rate / md_table[idx]; +} + +static struct sh_clk_ops sh7710_cpu_clk_ops = { + .recalc = cpu_clk_recalc, +}; + +static struct sh_clk_ops *sh7710_clk_ops[] = { + &sh7710_master_clk_ops, + &sh7710_module_clk_ops, + &sh7710_bus_clk_ops, + &sh7710_cpu_clk_ops, +}; + +void __init arch_init_clk_ops(struct sh_clk_ops **ops, int idx) +{ + if (idx < ARRAY_SIZE(sh7710_clk_ops)) + *ops = sh7710_clk_ops[idx]; +} + diff --git a/arch/sh/kernel/cpu/sh3/clock-sh7712.c b/arch/sh/kernel/cpu/sh3/clock-sh7712.c new file mode 100644 index 000000000..21438a9a1 --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/clock-sh7712.c @@ -0,0 +1,71 @@ +/* + * arch/sh/kernel/cpu/sh3/clock-sh7712.c + * + * SH7712 support for the clock framework + * + * Copyright (C) 2007 Andrew Murray <amurray@mpc-data.co.uk> + * + * Based on arch/sh/kernel/cpu/sh3/clock-sh3.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 multipliers[] = { 1, 2, 3 }; +static int divisors[] = { 1, 2, 3, 4, 6 }; + +static void master_clk_init(struct clk *clk) +{ + int frqcr = __raw_readw(FRQCR); + int idx = (frqcr & 0x0300) >> 8; + + clk->rate *= multipliers[idx]; +} + +static struct sh_clk_ops sh7712_master_clk_ops = { + .init = master_clk_init, +}; + +static unsigned long module_clk_recalc(struct clk *clk) +{ + int frqcr = __raw_readw(FRQCR); + int idx = frqcr & 0x0007; + + return clk->parent->rate / divisors[idx]; +} + +static struct sh_clk_ops sh7712_module_clk_ops = { + .recalc = module_clk_recalc, +}; + +static unsigned long cpu_clk_recalc(struct clk *clk) +{ + int frqcr = __raw_readw(FRQCR); + int idx = (frqcr & 0x0030) >> 4; + + return clk->parent->rate / divisors[idx]; +} + +static struct sh_clk_ops sh7712_cpu_clk_ops = { + .recalc = cpu_clk_recalc, +}; + +static struct sh_clk_ops *sh7712_clk_ops[] = { + &sh7712_master_clk_ops, + &sh7712_module_clk_ops, + &sh7712_cpu_clk_ops, +}; + +void __init arch_init_clk_ops(struct sh_clk_ops **ops, int idx) +{ + if (idx < ARRAY_SIZE(sh7712_clk_ops)) + *ops = sh7712_clk_ops[idx]; +} + diff --git a/arch/sh/kernel/cpu/sh3/entry.S b/arch/sh/kernel/cpu/sh3/entry.S new file mode 100644 index 000000000..262db6ec0 --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/entry.S @@ -0,0 +1,513 @@ +/* + * arch/sh/kernel/cpu/sh3/entry.S + * + * Copyright (C) 1999, 2000, 2002 Niibe Yutaka + * Copyright (C) 2003 - 2012 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/sys.h> +#include <linux/errno.h> +#include <linux/linkage.h> +#include <asm/asm-offsets.h> +#include <asm/thread_info.h> +#include <asm/unistd.h> +#include <cpu/mmu_context.h> +#include <asm/page.h> +#include <asm/cache.h> +#include <asm/thread_info.h> + +! NOTE: +! GNU as (as of 2.9.1) changes bf/s into bt/s and bra, when the address +! to be jumped is too far, but it causes illegal slot exception. + +/* + * entry.S contains the system-call and fault low-level handling routines. + * This also contains the timer-interrupt handler, as well as all interrupts + * and faults that can result in a task-switch. + * + * NOTE: This code handles signal-recognition, which happens every time + * after a timer-interrupt and after each system call. + * + * NOTE: This code uses a convention that instructions in the delay slot + * of a transfer-control instruction are indented by an extra space, thus: + * + * jmp @k0 ! control-transfer instruction + * ldc k1, ssr ! delay slot + * + * Stack layout in 'ret_from_syscall': + * ptrace needs to have all regs on the stack. + * if the order here is changed, it needs to be + * updated in ptrace.c and ptrace.h + * + * r0 + * ... + * r15 = stack pointer + * spc + * pr + * ssr + * gbr + * mach + * macl + * syscall # + * + */ +/* Offsets to the stack */ +OFF_R0 = 0 /* Return value. New ABI also arg4 */ +OFF_R1 = 4 /* New ABI: arg5 */ +OFF_R2 = 8 /* New ABI: arg6 */ +OFF_R3 = 12 /* New ABI: syscall_nr */ +OFF_R4 = 16 /* New ABI: arg0 */ +OFF_R5 = 20 /* New ABI: arg1 */ +OFF_R6 = 24 /* New ABI: arg2 */ +OFF_R7 = 28 /* New ABI: arg3 */ +OFF_SP = (15*4) +OFF_PC = (16*4) +OFF_SR = (16*4+8) +OFF_TRA = (16*4+6*4) + +#define k0 r0 +#define k1 r1 +#define k2 r2 +#define k3 r3 +#define k4 r4 + +#define g_imask r6 /* r6_bank1 */ +#define k_g_imask r6_bank /* r6_bank1 */ +#define current r7 /* r7_bank1 */ + +#include <asm/entry-macros.S> + +/* + * Kernel mode register usage: + * k0 scratch + * k1 scratch + * k2 scratch (Exception code) + * k3 scratch (Return address) + * k4 scratch + * k5 reserved + * k6 Global Interrupt Mask (0--15 << 4) + * k7 CURRENT_THREAD_INFO (pointer to current thread info) + */ + +! +! TLB Miss / Initial Page write exception handling +! _and_ +! TLB hits, but the access violate the protection. +! It can be valid access, such as stack grow and/or C-O-W. +! +! +! Find the pmd/pte entry and loadtlb +! If it's not found, cause address error (SEGV) +! +! Although this could be written in assembly language (and it'd be faster), +! this first version depends *much* on C implementation. +! + +#if defined(CONFIG_MMU) + .align 2 +ENTRY(tlb_miss_load) + bra call_handle_tlbmiss + mov #0, r5 + + .align 2 +ENTRY(tlb_miss_store) + bra call_handle_tlbmiss + mov #FAULT_CODE_WRITE, r5 + + .align 2 +ENTRY(initial_page_write) + bra call_handle_tlbmiss + mov #FAULT_CODE_INITIAL, r5 + + .align 2 +ENTRY(tlb_protection_violation_load) + bra call_do_page_fault + mov #FAULT_CODE_PROT, r5 + + .align 2 +ENTRY(tlb_protection_violation_store) + bra call_do_page_fault + mov #(FAULT_CODE_PROT | FAULT_CODE_WRITE), r5 + +call_handle_tlbmiss: + mov.l 1f, r0 + mov r5, r8 + mov.l @r0, r6 + mov.l 2f, r0 + sts pr, r10 + jsr @r0 + mov r15, r4 + ! + tst r0, r0 + bf/s 0f + lds r10, pr + rts + nop +0: + mov r8, r5 +call_do_page_fault: + mov.l 1f, r0 + mov.l @r0, r6 + + mov.l 3f, r0 + mov.l 4f, r1 + mov r15, r4 + jmp @r0 + lds r1, pr + + .align 2 +1: .long MMU_TEA +2: .long handle_tlbmiss +3: .long do_page_fault +4: .long ret_from_exception + + .align 2 +ENTRY(address_error_load) + bra call_dae + mov #0,r5 ! writeaccess = 0 + + .align 2 +ENTRY(address_error_store) + bra call_dae + mov #1,r5 ! writeaccess = 1 + + .align 2 +call_dae: + mov.l 1f, r0 + mov.l @r0, r6 ! address + mov.l 2f, r0 + jmp @r0 + mov r15, r4 ! regs + + .align 2 +1: .long MMU_TEA +2: .long do_address_error +#endif /* CONFIG_MMU */ + +#if defined(CONFIG_SH_STANDARD_BIOS) + /* Unwind the stack and jmp to the debug entry */ +ENTRY(sh_bios_handler) + mov.l 1f, r8 + bsr restore_regs + nop + + lds k2, pr ! restore pr + mov k4, r15 + ! + mov.l 2f, k0 + mov.l @k0, k0 + jmp @k0 + ldc k3, ssr + .align 2 +1: .long 0x300000f0 +2: .long gdb_vbr_vector +#endif /* CONFIG_SH_STANDARD_BIOS */ + +! restore_regs() +! - restore r0, r1, r2, r3, r4, r5, r6, r7 from the stack +! - switch bank +! - restore r8, r9, r10, r11, r12, r13, r14, r15 from the stack +! - restore spc, pr*, ssr, gbr, mach, macl, skip default tra +! k2 returns original pr +! k3 returns original sr +! k4 returns original stack pointer +! r8 passes SR bitmask, overwritten with restored data on return +! r9 trashed +! BL=0 on entry, on exit BL=1 (depending on r8). + +ENTRY(restore_regs) + mov.l @r15+, r0 + mov.l @r15+, r1 + mov.l @r15+, r2 + mov.l @r15+, r3 + mov.l @r15+, r4 + mov.l @r15+, r5 + mov.l @r15+, r6 + mov.l @r15+, r7 + ! + stc sr, r9 + or r8, r9 + ldc r9, sr + ! + mov.l @r15+, r8 + mov.l @r15+, r9 + mov.l @r15+, r10 + mov.l @r15+, r11 + mov.l @r15+, r12 + mov.l @r15+, r13 + mov.l @r15+, r14 + mov.l @r15+, k4 ! original stack pointer + ldc.l @r15+, spc + mov.l @r15+, k2 ! original PR + mov.l @r15+, k3 ! original SR + ldc.l @r15+, gbr + lds.l @r15+, mach + lds.l @r15+, macl + rts + add #4, r15 ! Skip syscall number + +restore_all: + mov.l 7f, r8 + bsr restore_regs + nop + + lds k2, pr ! restore pr + ! + ! Calculate new SR value + mov k3, k2 ! original SR value + mov #0xfffffff0, k1 + extu.b k1, k1 + not k1, k1 + and k1, k2 ! Mask original SR value + ! + mov k3, k0 ! Calculate IMASK-bits + shlr2 k0 + and #0x3c, k0 + cmp/eq #0x3c, k0 + bt/s 6f + shll2 k0 + mov g_imask, k0 + ! +6: or k0, k2 ! Set the IMASK-bits + ldc k2, ssr + ! + mov k4, r15 + rte + nop + + .align 2 +5: .long 0x00001000 ! DSP +7: .long 0x30000000 + +! common exception handler +#include "../../entry-common.S" + +! Exception Vector Base +! +! Should be aligned page boundary. +! + .balign 4096,0,4096 +ENTRY(vbr_base) + .long 0 +! +! 0x100: General exception vector +! + .balign 256,0,256 +general_exception: + bra handle_exception + sts pr, k3 ! save original pr value in k3 + +! prepare_stack() +! - roll back gRB +! - switch to kernel stack +! k0 returns original sp (after roll back) +! k1 trashed +! k2 trashed + +prepare_stack: +#ifdef CONFIG_GUSA + ! Check for roll back gRB (User and Kernel) + mov r15, k0 + shll k0 + bf/s 1f + shll k0 + bf/s 1f + stc spc, k1 + stc r0_bank, k0 + cmp/hs k0, k1 ! test k1 (saved PC) >= k0 (saved r0) + bt/s 2f + stc r1_bank, k1 + + add #-2, k0 + add r15, k0 + ldc k0, spc ! PC = saved r0 + r15 - 2 +2: mov k1, r15 ! SP = r1 +1: +#endif + ! Switch to kernel stack if needed + stc ssr, k0 ! Is it from kernel space? + shll k0 ! Check MD bit (bit30) by shifting it into... + shll k0 ! ...the T bit + bt/s 1f ! It's a kernel to kernel transition. + mov r15, k0 ! save original stack to k0 + /* User space to kernel */ + mov #(THREAD_SIZE >> 10), k1 + shll8 k1 ! k1 := THREAD_SIZE + shll2 k1 + add current, k1 + mov k1, r15 ! change to kernel stack + ! +1: + rts + nop + +! +! 0x400: Instruction and Data TLB miss exception vector +! + .balign 1024,0,1024 +tlb_miss: + sts pr, k3 ! save original pr value in k3 + +handle_exception: + mova exception_data, k0 + + ! Setup stack and save DSP context (k0 contains original r15 on return) + bsr prepare_stack + PREF(k0) + + ! Save registers / Switch to bank 0 + mov.l 5f, k2 ! vector register address + mov.l 1f, k4 ! SR bits to clear in k4 + bsr save_regs ! needs original pr value in k3 + mov.l @k2, k2 ! read out vector and keep in k2 + +handle_exception_special: + setup_frame_reg + + ! Setup return address and jump to exception handler + mov.l 7f, r9 ! fetch return address + stc r2_bank, r0 ! k2 (vector) + mov.l 6f, r10 + shlr2 r0 + shlr r0 + mov.l @(r0, r10), r10 + jmp @r10 + lds r9, pr ! put return address in pr + + .align L1_CACHE_SHIFT + +! save_regs() +! - save default tra, macl, mach, gbr, ssr, pr* and spc on the stack +! - save r15*, r14, r13, r12, r11, r10, r9, r8 on the stack +! - switch bank +! - save r7, r6, r5, r4, r3, r2, r1, r0 on the stack +! k0 contains original stack pointer* +! k1 trashed +! k3 passes original pr* +! k4 passes SR bitmask +! BL=1 on entry, on exit BL=0. + +ENTRY(save_regs) + mov #-1, r1 + mov.l k1, @-r15 ! set TRA (default: -1) + sts.l macl, @-r15 + sts.l mach, @-r15 + stc.l gbr, @-r15 + stc.l ssr, @-r15 + mov.l k3, @-r15 ! original pr in k3 + stc.l spc, @-r15 + + mov.l k0, @-r15 ! original stack pointer in k0 + mov.l r14, @-r15 + mov.l r13, @-r15 + mov.l r12, @-r15 + mov.l r11, @-r15 + mov.l r10, @-r15 + mov.l r9, @-r15 + mov.l r8, @-r15 + + mov.l 0f, k3 ! SR bits to set in k3 + + ! fall-through + +! save_low_regs() +! - modify SR for bank switch +! - save r7, r6, r5, r4, r3, r2, r1, r0 on the stack +! k3 passes bits to set in SR +! k4 passes bits to clear in SR + +ENTRY(save_low_regs) + stc sr, r8 + or k3, r8 + and k4, r8 + ldc r8, sr + + mov.l r7, @-r15 + mov.l r6, @-r15 + mov.l r5, @-r15 + mov.l r4, @-r15 + mov.l r3, @-r15 + mov.l r2, @-r15 + mov.l r1, @-r15 + rts + mov.l r0, @-r15 + +! +! 0x600: Interrupt / NMI vector +! + .balign 512,0,512 +ENTRY(handle_interrupt) + sts pr, k3 ! save original pr value in k3 + mova exception_data, k0 + + ! Setup stack and save DSP context (k0 contains original r15 on return) + bsr prepare_stack + PREF(k0) + + ! Save registers / Switch to bank 0 + mov.l 1f, k4 ! SR bits to clear in k4 + bsr save_regs ! needs original pr value in k3 + mov #-1, k2 ! default vector kept in k2 + + setup_frame_reg + + stc sr, r0 ! get status register + shlr2 r0 + and #0x3c, r0 + cmp/eq #0x3c, r0 + bf 9f + TRACE_IRQS_OFF +9: + + ! Setup return address and jump to do_IRQ + mov.l 4f, r9 ! fetch return address + lds r9, pr ! put return address in pr + mov.l 2f, r4 + mov.l 3f, r9 + mov.l @r4, r4 ! pass INTEVT vector as arg0 + + shlr2 r4 + shlr r4 + mov r4, r0 ! save vector->jmp table offset for later + + shlr2 r4 ! vector to IRQ# conversion + add #-0x10, r4 + + cmp/pz r4 ! is it a valid IRQ? + bt 10f + + /* + * We got here as a result of taking the INTEVT path for something + * that isn't a valid hard IRQ, therefore we bypass the do_IRQ() + * path and special case the event dispatch instead. This is the + * expected path for the NMI (and any other brilliantly implemented + * exception), which effectively wants regular exception dispatch + * but is unfortunately reported through INTEVT rather than + * EXPEVT. Grr. + */ + mov.l 6f, r9 + mov.l @(r0, r9), r9 + jmp @r9 + mov r15, r8 ! trap handlers take saved regs in r8 + +10: + jmp @r9 ! Off to do_IRQ() we go. + mov r15, r5 ! pass saved registers as arg1 + +ENTRY(exception_none) + rts + nop + + .align L1_CACHE_SHIFT +exception_data: +0: .long 0x000080f0 ! FD=1, IMASK=15 +1: .long 0xcfffffff ! RB=0, BL=0 +2: .long INTEVT +3: .long do_IRQ +4: .long ret_from_irq +5: .long EXPEVT +6: .long exception_handling_table +7: .long ret_from_exception diff --git a/arch/sh/kernel/cpu/sh3/ex.S b/arch/sh/kernel/cpu/sh3/ex.S new file mode 100644 index 000000000..99b4d0201 --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/ex.S @@ -0,0 +1,59 @@ +/* + * arch/sh/kernel/cpu/sh3/ex.S + * + * The SH-3 and SH-4 exception vector table. + + * Copyright (C) 1999, 2000, 2002 Niibe Yutaka + * Copyright (C) 2003 - 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/linkage.h> + +#if !defined(CONFIG_MMU) +#define tlb_miss_load exception_error +#define tlb_miss_store exception_error +#define initial_page_write exception_error +#define tlb_protection_violation_load exception_error +#define tlb_protection_violation_store exception_error +#define address_error_load exception_error +#define address_error_store exception_error +#endif + +#if !defined(CONFIG_SH_FPU) +#define fpu_error_trap_handler exception_error +#endif + +#if !defined(CONFIG_KGDB) +#define kgdb_handle_exception exception_error +#endif + + .align 2 + .data + +ENTRY(exception_handling_table) + .long exception_error /* 000 */ + .long exception_error + .long tlb_miss_load /* 040 */ + .long tlb_miss_store + .long initial_page_write + .long tlb_protection_violation_load + .long tlb_protection_violation_store + .long address_error_load + .long address_error_store /* 100 */ + .long fpu_error_trap_handler /* 120 */ + .long exception_error /* 140 */ + .long system_call ! Unconditional Trap /* 160 */ + .long exception_error ! reserved_instruction (filled by trap_init) /* 180 */ + .long exception_error ! illegal_slot_instruction (filled by trap_init) /*1A0*/ + .long nmi_trap_handler /* 1C0 */ ! Allow trap to debugger + .long breakpoint_trap_handler /* 1E0 */ + + /* + * Pad the remainder of the table out, exceptions residing in far + * away offsets can be manually inserted in to their appropriate + * location via set_exception_table_{evt,vec}(). + */ + .balign 4096,0,4096 diff --git a/arch/sh/kernel/cpu/sh3/pinmux-sh7720.c b/arch/sh/kernel/cpu/sh3/pinmux-sh7720.c new file mode 100644 index 000000000..26e90a66e --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/pinmux-sh7720.c @@ -0,0 +1,30 @@ +/* + * SH7720 Pinmux + * + * Copyright (C) 2008 Magnus Damm + * + * 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/bug.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <cpu/pfc.h> + +static struct resource sh7720_pfc_resources[] = { + [0] = { + .start = 0xa4050100, + .end = 0xa405016f, + .flags = IORESOURCE_MEM, + }, +}; + +static int __init plat_pinmux_setup(void) +{ + return sh_pfc_register("pfc-sh7720", sh7720_pfc_resources, + ARRAY_SIZE(sh7720_pfc_resources)); +} +arch_initcall(plat_pinmux_setup); diff --git a/arch/sh/kernel/cpu/sh3/probe.c b/arch/sh/kernel/cpu/sh3/probe.c new file mode 100644 index 000000000..426e1e1dc --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/probe.c @@ -0,0 +1,111 @@ +/* + * arch/sh/kernel/cpu/sh3/probe.c + * + * CPU Subtype Probing for SH-3. + * + * Copyright (C) 1999, 2000 Niibe Yutaka + * Copyright (C) 2002 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 <asm/processor.h> +#include <asm/cache.h> +#include <asm/io.h> + +void cpu_probe(void) +{ + unsigned long addr0, addr1, data0, data1, data2, data3; + + jump_to_uncached(); + /* + * Check if the entry shadows or not. + * When shadowed, it's 128-entry system. + * Otherwise, it's 256-entry system. + */ + addr0 = CACHE_OC_ADDRESS_ARRAY + (3 << 12); + addr1 = CACHE_OC_ADDRESS_ARRAY + (1 << 12); + + /* First, write back & invalidate */ + data0 = __raw_readl(addr0); + __raw_writel(data0&~(SH_CACHE_VALID|SH_CACHE_UPDATED), addr0); + data1 = __raw_readl(addr1); + __raw_writel(data1&~(SH_CACHE_VALID|SH_CACHE_UPDATED), addr1); + + /* Next, check if there's shadow or not */ + data0 = __raw_readl(addr0); + data0 ^= SH_CACHE_VALID; + __raw_writel(data0, addr0); + data1 = __raw_readl(addr1); + data2 = data1 ^ SH_CACHE_VALID; + __raw_writel(data2, addr1); + data3 = __raw_readl(addr0); + + /* Lastly, invaliate them. */ + __raw_writel(data0&~SH_CACHE_VALID, addr0); + __raw_writel(data2&~SH_CACHE_VALID, addr1); + + back_to_cached(); + + boot_cpu_data.dcache.ways = 4; + boot_cpu_data.dcache.entry_shift = 4; + boot_cpu_data.dcache.linesz = L1_CACHE_BYTES; + boot_cpu_data.dcache.flags = 0; + + /* + * 7709A/7729 has 16K cache (256-entry), while 7702 has only + * 2K(direct) 7702 is not supported (yet) + */ + if (data0 == data1 && data2 == data3) { /* Shadow */ + boot_cpu_data.dcache.way_incr = (1 << 11); + boot_cpu_data.dcache.entry_mask = 0x7f0; + boot_cpu_data.dcache.sets = 128; + boot_cpu_data.type = CPU_SH7708; + + boot_cpu_data.flags |= CPU_HAS_MMU_PAGE_ASSOC; + } else { /* 7709A or 7729 */ + boot_cpu_data.dcache.way_incr = (1 << 12); + boot_cpu_data.dcache.entry_mask = 0xff0; + boot_cpu_data.dcache.sets = 256; + boot_cpu_data.type = CPU_SH7729; + +#if defined(CONFIG_CPU_SUBTYPE_SH7706) + boot_cpu_data.type = CPU_SH7706; +#endif +#if defined(CONFIG_CPU_SUBTYPE_SH7710) + boot_cpu_data.type = CPU_SH7710; +#endif +#if defined(CONFIG_CPU_SUBTYPE_SH7712) + boot_cpu_data.type = CPU_SH7712; +#endif +#if defined(CONFIG_CPU_SUBTYPE_SH7720) + boot_cpu_data.type = CPU_SH7720; +#endif +#if defined(CONFIG_CPU_SUBTYPE_SH7721) + boot_cpu_data.type = CPU_SH7721; +#endif +#if defined(CONFIG_CPU_SUBTYPE_SH7705) + boot_cpu_data.type = CPU_SH7705; + +#if defined(CONFIG_SH7705_CACHE_32KB) + boot_cpu_data.dcache.way_incr = (1 << 13); + boot_cpu_data.dcache.entry_mask = 0x1ff0; + boot_cpu_data.dcache.sets = 512; + __raw_writel(CCR_CACHE_32KB, CCR3_REG); +#else + __raw_writel(CCR_CACHE_16KB, CCR3_REG); +#endif +#endif + } + + /* + * SH-3 doesn't have separate caches + */ + boot_cpu_data.dcache.flags |= SH_CACHE_COMBINED; + boot_cpu_data.icache = boot_cpu_data.dcache; + + boot_cpu_data.family = CPU_FAMILY_SH3; +} diff --git a/arch/sh/kernel/cpu/sh3/serial-sh770x.c b/arch/sh/kernel/cpu/sh3/serial-sh770x.c new file mode 100644 index 000000000..dec027f23 --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/serial-sh770x.c @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/serial_sci.h> +#include <linux/serial_core.h> +#include <linux/io.h> +#include <cpu/serial.h> + +#define SCPCR 0xA4000116 +#define SCPDR 0xA4000136 + +static void sh770x_sci_init_pins(struct uart_port *port, unsigned int cflag) +{ + unsigned short data; + + /* We need to set SCPCR to enable RTS/CTS */ + data = __raw_readw(SCPCR); + /* Clear out SCP7MD1,0, SCP6MD1,0, SCP4MD1,0*/ + __raw_writew(data & 0x0fcf, SCPCR); + + if (!(cflag & CRTSCTS)) { + /* We need to set SCPCR to enable RTS/CTS */ + data = __raw_readw(SCPCR); + /* Clear out SCP7MD1,0, SCP4MD1,0, + Set SCP6MD1,0 = {01} (output) */ + __raw_writew((data & 0x0fcf) | 0x1000, SCPCR); + + data = __raw_readb(SCPDR); + /* Set /RTS2 (bit6) = 0 */ + __raw_writeb(data & 0xbf, SCPDR); + } +} + +struct plat_sci_port_ops sh770x_sci_port_ops = { + .init_pins = sh770x_sci_init_pins, +}; diff --git a/arch/sh/kernel/cpu/sh3/serial-sh7710.c b/arch/sh/kernel/cpu/sh3/serial-sh7710.c new file mode 100644 index 000000000..ee04052e5 --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/serial-sh7710.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/serial_sci.h> +#include <linux/serial_core.h> +#include <linux/io.h> +#include <cpu/serial.h> + +#define PACR 0xa4050100 +#define PBCR 0xa4050102 + +static void sh7710_sci_init_pins(struct uart_port *port, unsigned int cflag) +{ + if (port->mapbase == 0xA4400000) { + __raw_writew(__raw_readw(PACR) & 0xffc0, PACR); + __raw_writew(__raw_readw(PBCR) & 0x0fff, PBCR); + } else if (port->mapbase == 0xA4410000) + __raw_writew(__raw_readw(PBCR) & 0xf003, PBCR); +} + +struct plat_sci_port_ops sh7710_sci_port_ops = { + .init_pins = sh7710_sci_init_pins, +}; diff --git a/arch/sh/kernel/cpu/sh3/serial-sh7720.c b/arch/sh/kernel/cpu/sh3/serial-sh7720.c new file mode 100644 index 000000000..75aaea49d --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/serial-sh7720.c @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/serial_sci.h> +#include <linux/serial_core.h> +#include <linux/io.h> +#include <cpu/serial.h> +#include <cpu/gpio.h> + +static void sh7720_sci_init_pins(struct uart_port *port, unsigned int cflag) +{ + unsigned short data; + + if (cflag & CRTSCTS) { + /* enable RTS/CTS */ + if (port->mapbase == 0xa4430000) { /* SCIF0 */ + /* Clear PTCR bit 9-2; enable all scif pins but sck */ + data = __raw_readw(PORT_PTCR); + __raw_writew((data & 0xfc03), PORT_PTCR); + } else if (port->mapbase == 0xa4438000) { /* SCIF1 */ + /* Clear PVCR bit 9-2 */ + data = __raw_readw(PORT_PVCR); + __raw_writew((data & 0xfc03), PORT_PVCR); + } + } else { + if (port->mapbase == 0xa4430000) { /* SCIF0 */ + /* Clear PTCR bit 5-2; enable only tx and rx */ + data = __raw_readw(PORT_PTCR); + __raw_writew((data & 0xffc3), PORT_PTCR); + } else if (port->mapbase == 0xa4438000) { /* SCIF1 */ + /* Clear PVCR bit 5-2 */ + data = __raw_readw(PORT_PVCR); + __raw_writew((data & 0xffc3), PORT_PVCR); + } + } +} + +struct plat_sci_port_ops sh7720_sci_port_ops = { + .init_pins = sh7720_sci_init_pins, +}; diff --git a/arch/sh/kernel/cpu/sh3/setup-sh3.c b/arch/sh/kernel/cpu/sh3/setup-sh3.c new file mode 100644 index 000000000..53be70b98 --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/setup-sh3.c @@ -0,0 +1,71 @@ +/* + * Shared SH3 Setup code + * + * Copyright (C) 2008 Magnus Damm + * + * 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/irq.h> +#include <linux/io.h> + +/* All SH3 devices are equipped with IRQ0->5 (except sh7708) */ + +enum { + UNUSED = 0, + + /* interrupt sources */ + IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, +}; + +static struct intc_vect vectors_irq0123[] __initdata = { + INTC_VECT(IRQ0, 0x600), INTC_VECT(IRQ1, 0x620), + INTC_VECT(IRQ2, 0x640), INTC_VECT(IRQ3, 0x660), +}; + +static struct intc_vect vectors_irq45[] __initdata = { + INTC_VECT(IRQ4, 0x680), INTC_VECT(IRQ5, 0x6a0), +}; + +static struct intc_prio_reg prio_registers[] __initdata = { + { 0xa4000016, 0, 16, 4, /* IPRC */ { IRQ3, IRQ2, IRQ1, IRQ0 } }, + { 0xa4000018, 0, 16, 4, /* IPRD */ { 0, 0, IRQ5, IRQ4 } }, +}; + +static struct intc_mask_reg ack_registers[] __initdata = { + { 0xa4000004, 0, 8, /* IRR0 */ + { 0, 0, IRQ5, IRQ4, IRQ3, IRQ2, IRQ1, IRQ0 } }, +}; + +static struct intc_sense_reg sense_registers[] __initdata = { + { 0xa4000010, 16, 2, { 0, 0, IRQ5, IRQ4, IRQ3, IRQ2, IRQ1, IRQ0 } }, +}; + +static DECLARE_INTC_DESC_ACK(intc_desc_irq0123, "sh3-irq0123", + vectors_irq0123, NULL, NULL, + prio_registers, sense_registers, ack_registers); + +static DECLARE_INTC_DESC_ACK(intc_desc_irq45, "sh3-irq45", + vectors_irq45, NULL, NULL, + prio_registers, sense_registers, ack_registers); + +#define INTC_ICR1 0xa4000010UL +#define INTC_ICR1_IRQLVL (1<<14) + +void __init plat_irq_setup_pins(int mode) +{ + if (mode == IRQ_MODE_IRQ) { + __raw_writew(__raw_readw(INTC_ICR1) & ~INTC_ICR1_IRQLVL, INTC_ICR1); + register_intc_controller(&intc_desc_irq0123); + return; + } + BUG(); +} + +void __init plat_irq_setup_sh3(void) +{ + register_intc_controller(&intc_desc_irq45); +} diff --git a/arch/sh/kernel/cpu/sh3/setup-sh7705.c b/arch/sh/kernel/cpu/sh3/setup-sh7705.c new file mode 100644 index 000000000..f6e392e0d --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/setup-sh7705.c @@ -0,0 +1,192 @@ +/* + * SH7705 Setup + * + * Copyright (C) 2006 - 2009 Paul Mundt + * Copyright (C) 2007 Nobuhiro Iwamatsu + * + * 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/platform_device.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/serial.h> +#include <linux/serial_sci.h> +#include <linux/sh_timer.h> +#include <linux/sh_intc.h> +#include <asm/rtc.h> +#include <cpu/serial.h> + +enum { + UNUSED = 0, + + /* interrupt sources */ + IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, + PINT07, PINT815, + + DMAC, SCIF0, SCIF2, ADC_ADI, USB, + + TPU0, TPU1, TPU2, TPU3, + TMU0, TMU1, TMU2, + + RTC, WDT, REF_RCMI, +}; + +static struct intc_vect vectors[] __initdata = { + /* IRQ0->5 are handled in setup-sh3.c */ + INTC_VECT(PINT07, 0x700), INTC_VECT(PINT815, 0x720), + INTC_VECT(DMAC, 0x800), INTC_VECT(DMAC, 0x820), + INTC_VECT(DMAC, 0x840), INTC_VECT(DMAC, 0x860), + INTC_VECT(SCIF0, 0x880), INTC_VECT(SCIF0, 0x8a0), + INTC_VECT(SCIF0, 0x8e0), + INTC_VECT(SCIF2, 0x900), INTC_VECT(SCIF2, 0x920), + INTC_VECT(SCIF2, 0x960), + INTC_VECT(ADC_ADI, 0x980), + INTC_VECT(USB, 0xa20), INTC_VECT(USB, 0xa40), + INTC_VECT(TPU0, 0xc00), INTC_VECT(TPU1, 0xc20), + INTC_VECT(TPU2, 0xc80), INTC_VECT(TPU3, 0xca0), + INTC_VECT(TMU0, 0x400), INTC_VECT(TMU1, 0x420), + INTC_VECT(TMU2, 0x440), INTC_VECT(TMU2, 0x460), + INTC_VECT(RTC, 0x480), INTC_VECT(RTC, 0x4a0), + INTC_VECT(RTC, 0x4c0), + INTC_VECT(WDT, 0x560), + INTC_VECT(REF_RCMI, 0x580), +}; + +static struct intc_prio_reg prio_registers[] __initdata = { + { 0xfffffee2, 0, 16, 4, /* IPRA */ { TMU0, TMU1, TMU2, RTC } }, + { 0xfffffee4, 0, 16, 4, /* IPRB */ { WDT, REF_RCMI, 0, 0 } }, + { 0xa4000016, 0, 16, 4, /* IPRC */ { IRQ3, IRQ2, IRQ1, IRQ0 } }, + { 0xa4000018, 0, 16, 4, /* IPRD */ { PINT07, PINT815, IRQ5, IRQ4 } }, + { 0xa400001a, 0, 16, 4, /* IPRE */ { DMAC, SCIF0, SCIF2, ADC_ADI } }, + { 0xa4080000, 0, 16, 4, /* IPRF */ { 0, 0, USB } }, + { 0xa4080002, 0, 16, 4, /* IPRG */ { TPU0, TPU1 } }, + { 0xa4080004, 0, 16, 4, /* IPRH */ { TPU2, TPU3 } }, + +}; + +static DECLARE_INTC_DESC(intc_desc, "sh7705", vectors, NULL, + NULL, prio_registers, NULL); + +static struct plat_sci_port scif0_platform_data = { + .scscr = SCSCR_CKE1, + .type = PORT_SCIF, + .ops = &sh770x_sci_port_ops, + .regtype = SCIx_SH7705_SCIF_REGTYPE, +}; + +static struct resource scif0_resources[] = { + DEFINE_RES_MEM(0xa4410000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x900)), +}; + +static struct platform_device scif0_device = { + .name = "sh-sci", + .id = 0, + .resource = scif0_resources, + .num_resources = ARRAY_SIZE(scif0_resources), + .dev = { + .platform_data = &scif0_platform_data, + }, +}; + +static struct plat_sci_port scif1_platform_data = { + .type = PORT_SCIF, + .ops = &sh770x_sci_port_ops, + .regtype = SCIx_SH7705_SCIF_REGTYPE, +}; + +static struct resource scif1_resources[] = { + DEFINE_RES_MEM(0xa4400000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x880)), +}; + +static struct platform_device scif1_device = { + .name = "sh-sci", + .id = 1, + .resource = scif1_resources, + .num_resources = ARRAY_SIZE(scif1_resources), + .dev = { + .platform_data = &scif1_platform_data, + }, +}; + +static struct resource rtc_resources[] = { + [0] = { + .start = 0xfffffec0, + .end = 0xfffffec0 + 0x1e, + .flags = IORESOURCE_IO, + }, + [1] = { + .start = evt2irq(0x480), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct sh_rtc_platform_info rtc_info = { + .capabilities = RTC_CAP_4_DIGIT_YEAR, +}; + +static struct platform_device rtc_device = { + .name = "sh-rtc", + .id = -1, + .num_resources = ARRAY_SIZE(rtc_resources), + .resource = rtc_resources, + .dev = { + .platform_data = &rtc_info, + }, +}; + +static struct sh_timer_config tmu0_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu0_resources[] = { + DEFINE_RES_MEM(0xfffffe90, 0x2c), + DEFINE_RES_IRQ(evt2irq(0x400)), + DEFINE_RES_IRQ(evt2irq(0x420)), + DEFINE_RES_IRQ(evt2irq(0x440)), +}; + +static struct platform_device tmu0_device = { + .name = "sh-tmu-sh3", + .id = 0, + .dev = { + .platform_data = &tmu0_platform_data, + }, + .resource = tmu0_resources, + .num_resources = ARRAY_SIZE(tmu0_resources), +}; + +static struct platform_device *sh7705_devices[] __initdata = { + &scif0_device, + &scif1_device, + &tmu0_device, + &rtc_device, +}; + +static int __init sh7705_devices_setup(void) +{ + return platform_add_devices(sh7705_devices, + ARRAY_SIZE(sh7705_devices)); +} +arch_initcall(sh7705_devices_setup); + +static struct platform_device *sh7705_early_devices[] __initdata = { + &scif0_device, + &scif1_device, + &tmu0_device, +}; + +void __init plat_early_device_setup(void) +{ + early_platform_add_devices(sh7705_early_devices, + ARRAY_SIZE(sh7705_early_devices)); +} + +void __init plat_irq_setup(void) +{ + register_intc_controller(&intc_desc); + plat_irq_setup_sh3(); +} diff --git a/arch/sh/kernel/cpu/sh3/setup-sh770x.c b/arch/sh/kernel/cpu/sh3/setup-sh770x.c new file mode 100644 index 000000000..59a88611d --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/setup-sh770x.c @@ -0,0 +1,248 @@ +/* + * SH3 Setup code for SH7706, SH7707, SH7708, SH7709 + * + * Copyright (C) 2007 Magnus Damm + * Copyright (C) 2009 Paul Mundt + * + * Based on setup-sh7709.c + * + * Copyright (C) 2006 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/io.h> +#include <linux/irq.h> +#include <linux/platform_device.h> +#include <linux/serial.h> +#include <linux/serial_sci.h> +#include <linux/sh_timer.h> +#include <linux/sh_intc.h> +#include <cpu/serial.h> + +enum { + UNUSED = 0, + + /* interrupt sources */ + IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, + PINT07, PINT815, + DMAC, SCIF0, SCIF2, SCI, ADC_ADI, + LCDC, PCC0, PCC1, + TMU0, TMU1, TMU2, + RTC, WDT, REF, +}; + +static struct intc_vect vectors[] __initdata = { + INTC_VECT(TMU0, 0x400), INTC_VECT(TMU1, 0x420), + INTC_VECT(TMU2, 0x440), INTC_VECT(TMU2, 0x460), + INTC_VECT(RTC, 0x480), INTC_VECT(RTC, 0x4a0), + INTC_VECT(RTC, 0x4c0), + INTC_VECT(SCI, 0x4e0), INTC_VECT(SCI, 0x500), + INTC_VECT(SCI, 0x520), INTC_VECT(SCI, 0x540), + INTC_VECT(WDT, 0x560), + INTC_VECT(REF, 0x580), + INTC_VECT(REF, 0x5a0), +#if defined(CONFIG_CPU_SUBTYPE_SH7706) || \ + defined(CONFIG_CPU_SUBTYPE_SH7707) || \ + defined(CONFIG_CPU_SUBTYPE_SH7709) + /* IRQ0->5 are handled in setup-sh3.c */ + INTC_VECT(DMAC, 0x800), INTC_VECT(DMAC, 0x820), + INTC_VECT(DMAC, 0x840), INTC_VECT(DMAC, 0x860), + INTC_VECT(ADC_ADI, 0x980), + INTC_VECT(SCIF2, 0x900), INTC_VECT(SCIF2, 0x920), + INTC_VECT(SCIF2, 0x940), INTC_VECT(SCIF2, 0x960), +#endif +#if defined(CONFIG_CPU_SUBTYPE_SH7707) || \ + defined(CONFIG_CPU_SUBTYPE_SH7709) + INTC_VECT(PINT07, 0x700), INTC_VECT(PINT815, 0x720), + INTC_VECT(SCIF0, 0x880), INTC_VECT(SCIF0, 0x8a0), + INTC_VECT(SCIF0, 0x8c0), INTC_VECT(SCIF0, 0x8e0), +#endif +#if defined(CONFIG_CPU_SUBTYPE_SH7707) + INTC_VECT(LCDC, 0x9a0), + INTC_VECT(PCC0, 0x9c0), INTC_VECT(PCC1, 0x9e0), +#endif +}; + +static struct intc_prio_reg prio_registers[] __initdata = { + { 0xfffffee2, 0, 16, 4, /* IPRA */ { TMU0, TMU1, TMU2, RTC } }, + { 0xfffffee4, 0, 16, 4, /* IPRB */ { WDT, REF, SCI, 0 } }, +#if defined(CONFIG_CPU_SUBTYPE_SH7706) || \ + defined(CONFIG_CPU_SUBTYPE_SH7707) || \ + defined(CONFIG_CPU_SUBTYPE_SH7709) + { 0xa4000016, 0, 16, 4, /* IPRC */ { IRQ3, IRQ2, IRQ1, IRQ0 } }, + { 0xa4000018, 0, 16, 4, /* IPRD */ { 0, 0, IRQ5, IRQ4 } }, + { 0xa400001a, 0, 16, 4, /* IPRE */ { DMAC, 0, SCIF2, ADC_ADI } }, +#endif +#if defined(CONFIG_CPU_SUBTYPE_SH7707) || \ + defined(CONFIG_CPU_SUBTYPE_SH7709) + { 0xa4000018, 0, 16, 4, /* IPRD */ { PINT07, PINT815, } }, + { 0xa400001a, 0, 16, 4, /* IPRE */ { 0, SCIF0 } }, +#endif +#if defined(CONFIG_CPU_SUBTYPE_SH7707) + { 0xa400001c, 0, 16, 4, /* IPRF */ { 0, LCDC, PCC0, PCC1, } }, +#endif +}; + +static DECLARE_INTC_DESC(intc_desc, "sh770x", vectors, NULL, + NULL, prio_registers, NULL); + +static struct resource rtc_resources[] = { + [0] = { + .start = 0xfffffec0, + .end = 0xfffffec0 + 0x1e, + .flags = IORESOURCE_IO, + }, + [1] = { + .start = evt2irq(0x480), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device rtc_device = { + .name = "sh-rtc", + .id = -1, + .num_resources = ARRAY_SIZE(rtc_resources), + .resource = rtc_resources, +}; + +static struct plat_sci_port scif0_platform_data = { + .type = PORT_SCI, + .ops = &sh770x_sci_port_ops, +}; + +static struct resource scif0_resources[] = { + DEFINE_RES_MEM(0xfffffe80, 0x10), + DEFINE_RES_IRQ(evt2irq(0x4e0)), +}; + +static struct platform_device scif0_device = { + .name = "sh-sci", + .id = 0, + .resource = scif0_resources, + .num_resources = ARRAY_SIZE(scif0_resources), + .dev = { + .platform_data = &scif0_platform_data, + }, +}; +#if defined(CONFIG_CPU_SUBTYPE_SH7706) || \ + defined(CONFIG_CPU_SUBTYPE_SH7707) || \ + defined(CONFIG_CPU_SUBTYPE_SH7709) +static struct plat_sci_port scif1_platform_data = { + .type = PORT_SCIF, + .ops = &sh770x_sci_port_ops, + .regtype = SCIx_SH3_SCIF_REGTYPE, +}; + +static struct resource scif1_resources[] = { + DEFINE_RES_MEM(0xa4000150, 0x10), + DEFINE_RES_IRQ(evt2irq(0x900)), +}; + +static struct platform_device scif1_device = { + .name = "sh-sci", + .id = 1, + .resource = scif1_resources, + .num_resources = ARRAY_SIZE(scif1_resources), + .dev = { + .platform_data = &scif1_platform_data, + }, +}; +#endif +#if defined(CONFIG_CPU_SUBTYPE_SH7707) || \ + defined(CONFIG_CPU_SUBTYPE_SH7709) +static struct plat_sci_port scif2_platform_data = { + .type = PORT_IRDA, + .ops = &sh770x_sci_port_ops, +}; + +static struct resource scif2_resources[] = { + DEFINE_RES_MEM(0xa4000140, 0x10), + DEFINE_RES_IRQ(evt2irq(0x880)), +}; + +static struct platform_device scif2_device = { + .name = "sh-sci", + .id = 2, + .resource = scif2_resources, + .num_resources = ARRAY_SIZE(scif2_resources), + .dev = { + .platform_data = &scif2_platform_data, + }, +}; +#endif + +static struct sh_timer_config tmu0_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu0_resources[] = { + DEFINE_RES_MEM(0xfffffe90, 0x2c), + DEFINE_RES_IRQ(evt2irq(0x400)), + DEFINE_RES_IRQ(evt2irq(0x420)), + DEFINE_RES_IRQ(evt2irq(0x440)), +}; + +static struct platform_device tmu0_device = { + .name = "sh-tmu-sh3", + .id = 0, + .dev = { + .platform_data = &tmu0_platform_data, + }, + .resource = tmu0_resources, + .num_resources = ARRAY_SIZE(tmu0_resources), +}; + +static struct platform_device *sh770x_devices[] __initdata = { + &scif0_device, +#if defined(CONFIG_CPU_SUBTYPE_SH7706) || \ + defined(CONFIG_CPU_SUBTYPE_SH7707) || \ + defined(CONFIG_CPU_SUBTYPE_SH7709) + &scif1_device, +#endif +#if defined(CONFIG_CPU_SUBTYPE_SH7707) || \ + defined(CONFIG_CPU_SUBTYPE_SH7709) + &scif2_device, +#endif + &tmu0_device, + &rtc_device, +}; + +static int __init sh770x_devices_setup(void) +{ + return platform_add_devices(sh770x_devices, + ARRAY_SIZE(sh770x_devices)); +} +arch_initcall(sh770x_devices_setup); + +static struct platform_device *sh770x_early_devices[] __initdata = { + &scif0_device, +#if defined(CONFIG_CPU_SUBTYPE_SH7706) || \ + defined(CONFIG_CPU_SUBTYPE_SH7707) || \ + defined(CONFIG_CPU_SUBTYPE_SH7709) + &scif1_device, +#endif +#if defined(CONFIG_CPU_SUBTYPE_SH7707) || \ + defined(CONFIG_CPU_SUBTYPE_SH7709) + &scif2_device, +#endif + &tmu0_device, +}; + +void __init plat_early_device_setup(void) +{ + early_platform_add_devices(sh770x_early_devices, + ARRAY_SIZE(sh770x_early_devices)); +} + +void __init plat_irq_setup(void) +{ + register_intc_controller(&intc_desc); +#if defined(CONFIG_CPU_SUBTYPE_SH7706) || \ + defined(CONFIG_CPU_SUBTYPE_SH7707) || \ + defined(CONFIG_CPU_SUBTYPE_SH7709) + plat_irq_setup_sh3(); +#endif +} diff --git a/arch/sh/kernel/cpu/sh3/setup-sh7710.c b/arch/sh/kernel/cpu/sh3/setup-sh7710.c new file mode 100644 index 000000000..ea52410b4 --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/setup-sh7710.c @@ -0,0 +1,191 @@ +/* + * SH3 Setup code for SH7710, SH7712 + * + * Copyright (C) 2006 - 2009 Paul Mundt + * Copyright (C) 2007 Nobuhiro Iwamatsu + * + * 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/platform_device.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/serial.h> +#include <linux/serial_sci.h> +#include <linux/sh_timer.h> +#include <linux/sh_intc.h> +#include <asm/rtc.h> + +enum { + UNUSED = 0, + + /* interrupt sources */ + IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, + DMAC1, SCIF0, SCIF1, DMAC2, IPSEC, + EDMAC0, EDMAC1, EDMAC2, + SIOF0, SIOF1, + + TMU0, TMU1, TMU2, + RTC, WDT, REF, +}; + +static struct intc_vect vectors[] __initdata = { + /* IRQ0->5 are handled in setup-sh3.c */ + INTC_VECT(DMAC1, 0x800), INTC_VECT(DMAC1, 0x820), + INTC_VECT(DMAC1, 0x840), INTC_VECT(DMAC1, 0x860), + INTC_VECT(SCIF0, 0x880), INTC_VECT(SCIF0, 0x8a0), + INTC_VECT(SCIF0, 0x8c0), INTC_VECT(SCIF0, 0x8e0), + INTC_VECT(SCIF1, 0x900), INTC_VECT(SCIF1, 0x920), + INTC_VECT(SCIF1, 0x940), INTC_VECT(SCIF1, 0x960), + INTC_VECT(DMAC2, 0xb80), INTC_VECT(DMAC2, 0xba0), +#ifdef CONFIG_CPU_SUBTYPE_SH7710 + INTC_VECT(IPSEC, 0xbe0), +#endif + INTC_VECT(EDMAC0, 0xc00), INTC_VECT(EDMAC1, 0xc20), + INTC_VECT(EDMAC2, 0xc40), + INTC_VECT(SIOF0, 0xe00), INTC_VECT(SIOF0, 0xe20), + INTC_VECT(SIOF0, 0xe40), INTC_VECT(SIOF0, 0xe60), + INTC_VECT(SIOF1, 0xe80), INTC_VECT(SIOF1, 0xea0), + INTC_VECT(SIOF1, 0xec0), INTC_VECT(SIOF1, 0xee0), + INTC_VECT(TMU0, 0x400), INTC_VECT(TMU1, 0x420), + INTC_VECT(TMU2, 0x440), + INTC_VECT(RTC, 0x480), INTC_VECT(RTC, 0x4a0), + INTC_VECT(RTC, 0x4c0), + INTC_VECT(WDT, 0x560), + INTC_VECT(REF, 0x580), +}; + +static struct intc_prio_reg prio_registers[] __initdata = { + { 0xfffffee2, 0, 16, 4, /* IPRA */ { TMU0, TMU1, TMU2, RTC } }, + { 0xfffffee4, 0, 16, 4, /* IPRB */ { WDT, REF, 0, 0 } }, + { 0xa4000016, 0, 16, 4, /* IPRC */ { IRQ3, IRQ2, IRQ1, IRQ0 } }, + { 0xa4000018, 0, 16, 4, /* IPRD */ { 0, 0, IRQ5, IRQ4 } }, + { 0xa400001a, 0, 16, 4, /* IPRE */ { DMAC1, SCIF0, SCIF1 } }, + { 0xa4080000, 0, 16, 4, /* IPRF */ { IPSEC, DMAC2 } }, + { 0xa4080002, 0, 16, 4, /* IPRG */ { EDMAC0, EDMAC1, EDMAC2 } }, + { 0xa4080004, 0, 16, 4, /* IPRH */ { 0, 0, 0, SIOF0 } }, + { 0xa4080006, 0, 16, 4, /* IPRI */ { 0, 0, SIOF1 } }, +}; + +static DECLARE_INTC_DESC(intc_desc, "sh7710", vectors, NULL, + NULL, prio_registers, NULL); + +static struct resource rtc_resources[] = { + [0] = { + .start = 0xa413fec0, + .end = 0xa413fec0 + 0x1e, + .flags = IORESOURCE_IO, + }, + [1] = { + .start = evt2irq(0x480), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct sh_rtc_platform_info rtc_info = { + .capabilities = RTC_CAP_4_DIGIT_YEAR, +}; + +static struct platform_device rtc_device = { + .name = "sh-rtc", + .id = -1, + .num_resources = ARRAY_SIZE(rtc_resources), + .resource = rtc_resources, + .dev = { + .platform_data = &rtc_info, + }, +}; + +static struct plat_sci_port scif0_platform_data = { + .scscr = SCSCR_REIE | SCSCR_CKE1, + .type = PORT_SCIF, +}; + +static struct resource scif0_resources[] = { + DEFINE_RES_MEM(0xa4400000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x880)), +}; + +static struct platform_device scif0_device = { + .name = "sh-sci", + .id = 0, + .resource = scif0_resources, + .num_resources = ARRAY_SIZE(scif0_resources), + .dev = { + .platform_data = &scif0_platform_data, + }, +}; + +static struct plat_sci_port scif1_platform_data = { + .scscr = SCSCR_REIE | SCSCR_CKE1, + .type = PORT_SCIF, +}; + +static struct resource scif1_resources[] = { + DEFINE_RES_MEM(0xa4410000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x900)), +}; + +static struct platform_device scif1_device = { + .name = "sh-sci", + .id = 1, + .resource = scif1_resources, + .num_resources = ARRAY_SIZE(scif1_resources), + .dev = { + .platform_data = &scif1_platform_data, + }, +}; + +static struct sh_timer_config tmu0_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu0_resources[] = { + DEFINE_RES_MEM(0xa412fe90, 0x28), + DEFINE_RES_IRQ(evt2irq(0x400)), + DEFINE_RES_IRQ(evt2irq(0x420)), + DEFINE_RES_IRQ(evt2irq(0x440)), +}; + +static struct platform_device tmu0_device = { + .name = "sh-tmu-sh3", + .id = 0, + .dev = { + .platform_data = &tmu0_platform_data, + }, + .resource = tmu0_resources, + .num_resources = ARRAY_SIZE(tmu0_resources), +}; + +static struct platform_device *sh7710_devices[] __initdata = { + &scif0_device, + &scif1_device, + &tmu0_device, + &rtc_device, +}; + +static int __init sh7710_devices_setup(void) +{ + return platform_add_devices(sh7710_devices, + ARRAY_SIZE(sh7710_devices)); +} +arch_initcall(sh7710_devices_setup); + +static struct platform_device *sh7710_early_devices[] __initdata = { + &scif0_device, + &scif1_device, + &tmu0_device, +}; + +void __init plat_early_device_setup(void) +{ + early_platform_add_devices(sh7710_early_devices, + ARRAY_SIZE(sh7710_early_devices)); +} + +void __init plat_irq_setup(void) +{ + register_intc_controller(&intc_desc); + plat_irq_setup_sh3(); +} diff --git a/arch/sh/kernel/cpu/sh3/setup-sh7720.c b/arch/sh/kernel/cpu/sh3/setup-sh7720.c new file mode 100644 index 000000000..bf34b4e2e --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/setup-sh7720.c @@ -0,0 +1,288 @@ +/* + * Setup code for SH7720, SH7721. + * + * Copyright (C) 2007 Markus Brunner, Mark Jonas + * Copyright (C) 2009 Paul Mundt + * + * Based on arch/sh/kernel/cpu/sh4/setup-sh7750.c: + * + * Copyright (C) 2006 Paul Mundt + * Copyright (C) 2006 Jamie Lenehan + * + * 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/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <linux/io.h> +#include <linux/serial_sci.h> +#include <linux/sh_timer.h> +#include <linux/sh_intc.h> +#include <linux/usb/ohci_pdriver.h> +#include <asm/rtc.h> +#include <cpu/serial.h> + +static struct resource rtc_resources[] = { + [0] = { + .start = 0xa413fec0, + .end = 0xa413fec0 + 0x28 - 1, + .flags = IORESOURCE_IO, + }, + [1] = { + /* Shared Period/Carry/Alarm IRQ */ + .start = evt2irq(0x480), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct sh_rtc_platform_info rtc_info = { + .capabilities = RTC_CAP_4_DIGIT_YEAR, +}; + +static struct platform_device rtc_device = { + .name = "sh-rtc", + .id = -1, + .num_resources = ARRAY_SIZE(rtc_resources), + .resource = rtc_resources, + .dev = { + .platform_data = &rtc_info, + }, +}; + +static struct plat_sci_port scif0_platform_data = { + .type = PORT_SCIF, + .ops = &sh7720_sci_port_ops, + .regtype = SCIx_SH7705_SCIF_REGTYPE, +}; + +static struct resource scif0_resources[] = { + DEFINE_RES_MEM(0xa4430000, 0x100), + DEFINE_RES_IRQ(evt2irq(0xc00)), +}; + +static struct platform_device scif0_device = { + .name = "sh-sci", + .id = 0, + .resource = scif0_resources, + .num_resources = ARRAY_SIZE(scif0_resources), + .dev = { + .platform_data = &scif0_platform_data, + }, +}; + +static struct plat_sci_port scif1_platform_data = { + .type = PORT_SCIF, + .ops = &sh7720_sci_port_ops, + .regtype = SCIx_SH7705_SCIF_REGTYPE, +}; + +static struct resource scif1_resources[] = { + DEFINE_RES_MEM(0xa4438000, 0x100), + DEFINE_RES_IRQ(evt2irq(0xc20)), +}; + +static struct platform_device scif1_device = { + .name = "sh-sci", + .id = 1, + .resource = scif1_resources, + .num_resources = ARRAY_SIZE(scif1_resources), + .dev = { + .platform_data = &scif1_platform_data, + }, +}; + +static struct resource usb_ohci_resources[] = { + [0] = { + .start = 0xA4428000, + .end = 0xA44280FF, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = evt2irq(0xa60), + .end = evt2irq(0xa60), + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 usb_ohci_dma_mask = 0xffffffffUL; + +static struct usb_ohci_pdata usb_ohci_pdata; + +static struct platform_device usb_ohci_device = { + .name = "ohci-platform", + .id = -1, + .dev = { + .dma_mask = &usb_ohci_dma_mask, + .coherent_dma_mask = 0xffffffff, + .platform_data = &usb_ohci_pdata, + }, + .num_resources = ARRAY_SIZE(usb_ohci_resources), + .resource = usb_ohci_resources, +}; + +static struct resource usbf_resources[] = { + [0] = { + .name = "sh_udc", + .start = 0xA4420000, + .end = 0xA44200FF, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "sh_udc", + .start = evt2irq(0xa20), + .end = evt2irq(0xa20), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device usbf_device = { + .name = "sh_udc", + .id = -1, + .dev = { + .dma_mask = NULL, + .coherent_dma_mask = 0xffffffff, + }, + .num_resources = ARRAY_SIZE(usbf_resources), + .resource = usbf_resources, +}; + +static struct sh_timer_config cmt_platform_data = { + .channels_mask = 0x1f, +}; + +static struct resource cmt_resources[] = { + DEFINE_RES_MEM(0x044a0000, 0x60), + DEFINE_RES_IRQ(evt2irq(0xf00)), +}; + +static struct platform_device cmt_device = { + .name = "sh-cmt-32", + .id = 0, + .dev = { + .platform_data = &cmt_platform_data, + }, + .resource = cmt_resources, + .num_resources = ARRAY_SIZE(cmt_resources), +}; + +static struct sh_timer_config tmu0_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu0_resources[] = { + DEFINE_RES_MEM(0xa412fe90, 0x28), + DEFINE_RES_IRQ(evt2irq(0x400)), + DEFINE_RES_IRQ(evt2irq(0x420)), + DEFINE_RES_IRQ(evt2irq(0x440)), +}; + +static struct platform_device tmu0_device = { + .name = "sh-tmu-sh3", + .id = 0, + .dev = { + .platform_data = &tmu0_platform_data, + }, + .resource = tmu0_resources, + .num_resources = ARRAY_SIZE(tmu0_resources), +}; + +static struct platform_device *sh7720_devices[] __initdata = { + &scif0_device, + &scif1_device, + &cmt_device, + &tmu0_device, + &rtc_device, + &usb_ohci_device, + &usbf_device, +}; + +static int __init sh7720_devices_setup(void) +{ + return platform_add_devices(sh7720_devices, + ARRAY_SIZE(sh7720_devices)); +} +arch_initcall(sh7720_devices_setup); + +static struct platform_device *sh7720_early_devices[] __initdata = { + &scif0_device, + &scif1_device, + &cmt_device, + &tmu0_device, +}; + +void __init plat_early_device_setup(void) +{ + early_platform_add_devices(sh7720_early_devices, + ARRAY_SIZE(sh7720_early_devices)); +} + +enum { + UNUSED = 0, + + /* interrupt sources */ + TMU0, TMU1, TMU2, RTC, + WDT, REF_RCMI, SIM, + IRQ0, IRQ1, IRQ2, IRQ3, + USBF_SPD, TMU_SUNI, IRQ5, IRQ4, + DMAC1, LCDC, SSL, + ADC, DMAC2, USBFI, CMT, + SCIF0, SCIF1, + PINT07, PINT815, TPU, IIC, + SIOF0, SIOF1, MMC, PCC, + USBHI, AFEIF, + H_UDI, +}; + +static struct intc_vect vectors[] __initdata = { + /* IRQ0->5 are handled in setup-sh3.c */ + INTC_VECT(TMU0, 0x400), INTC_VECT(TMU1, 0x420), + INTC_VECT(TMU2, 0x440), INTC_VECT(RTC, 0x480), + INTC_VECT(RTC, 0x4a0), INTC_VECT(RTC, 0x4c0), + INTC_VECT(SIM, 0x4e0), INTC_VECT(SIM, 0x500), + INTC_VECT(SIM, 0x520), INTC_VECT(SIM, 0x540), + INTC_VECT(WDT, 0x560), INTC_VECT(REF_RCMI, 0x580), + /* H_UDI cannot be masked */ INTC_VECT(TMU_SUNI, 0x6c0), + INTC_VECT(USBF_SPD, 0x6e0), INTC_VECT(DMAC1, 0x800), + INTC_VECT(DMAC1, 0x820), INTC_VECT(DMAC1, 0x840), + INTC_VECT(DMAC1, 0x860), INTC_VECT(LCDC, 0x900), +#if defined(CONFIG_CPU_SUBTYPE_SH7720) + INTC_VECT(SSL, 0x980), +#endif + INTC_VECT(USBFI, 0xa20), INTC_VECT(USBFI, 0xa40), + INTC_VECT(USBHI, 0xa60), + INTC_VECT(DMAC2, 0xb80), INTC_VECT(DMAC2, 0xba0), + INTC_VECT(ADC, 0xbe0), INTC_VECT(SCIF0, 0xc00), + INTC_VECT(SCIF1, 0xc20), INTC_VECT(PINT07, 0xc80), + INTC_VECT(PINT815, 0xca0), INTC_VECT(SIOF0, 0xd00), + INTC_VECT(SIOF1, 0xd20), INTC_VECT(TPU, 0xd80), + INTC_VECT(TPU, 0xda0), INTC_VECT(TPU, 0xdc0), + INTC_VECT(TPU, 0xde0), INTC_VECT(IIC, 0xe00), + INTC_VECT(MMC, 0xe80), INTC_VECT(MMC, 0xea0), + INTC_VECT(MMC, 0xec0), INTC_VECT(MMC, 0xee0), + INTC_VECT(CMT, 0xf00), INTC_VECT(PCC, 0xf60), + INTC_VECT(AFEIF, 0xfe0), +}; + +static struct intc_prio_reg prio_registers[] __initdata = { + { 0xA414FEE2UL, 0, 16, 4, /* IPRA */ { TMU0, TMU1, TMU2, RTC } }, + { 0xA414FEE4UL, 0, 16, 4, /* IPRB */ { WDT, REF_RCMI, SIM, 0 } }, + { 0xA4140016UL, 0, 16, 4, /* IPRC */ { IRQ3, IRQ2, IRQ1, IRQ0 } }, + { 0xA4140018UL, 0, 16, 4, /* IPRD */ { USBF_SPD, TMU_SUNI, IRQ5, IRQ4 } }, + { 0xA414001AUL, 0, 16, 4, /* IPRE */ { DMAC1, 0, LCDC, SSL } }, + { 0xA4080000UL, 0, 16, 4, /* IPRF */ { ADC, DMAC2, USBFI, CMT } }, + { 0xA4080002UL, 0, 16, 4, /* IPRG */ { SCIF0, SCIF1, 0, 0 } }, + { 0xA4080004UL, 0, 16, 4, /* IPRH */ { PINT07, PINT815, TPU, IIC } }, + { 0xA4080006UL, 0, 16, 4, /* IPRI */ { SIOF0, SIOF1, MMC, PCC } }, + { 0xA4080008UL, 0, 16, 4, /* IPRJ */ { 0, USBHI, 0, AFEIF } }, +}; + +static DECLARE_INTC_DESC(intc_desc, "sh7720", vectors, NULL, + NULL, prio_registers, NULL); + +void __init plat_irq_setup(void) +{ + register_intc_controller(&intc_desc); + plat_irq_setup_sh3(); +} diff --git a/arch/sh/kernel/cpu/sh3/swsusp.S b/arch/sh/kernel/cpu/sh3/swsusp.S new file mode 100644 index 000000000..01145426a --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/swsusp.S @@ -0,0 +1,147 @@ +/* + * arch/sh/kernel/cpu/sh3/swsusp.S + * + * Copyright (C) 2009 Magnus Damm + * + * 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/sys.h> +#include <linux/errno.h> +#include <linux/linkage.h> +#include <asm/asm-offsets.h> +#include <asm/page.h> + +#define k0 r0 +#define k1 r1 +#define k2 r2 +#define k3 r3 +#define k4 r4 + +! swsusp_arch_resume() +! - copy restore_pblist pages +! - restore registers from swsusp_arch_regs_cpu0 + +ENTRY(swsusp_arch_resume) + mov.l 1f, r15 + mov.l 2f, r4 + mov.l @r4, r4 + +swsusp_copy_loop: + mov r4, r0 + cmp/eq #0, r0 + bt swsusp_restore_regs + + mov.l @(PBE_ADDRESS, r4), r2 + mov.l @(PBE_ORIG_ADDRESS, r4), r5 + + mov #(PAGE_SIZE >> 10), r3 + shll8 r3 + shlr2 r3 /* PAGE_SIZE / 16 */ +swsusp_copy_page: + dt r3 + mov.l @r2+,r1 /* 16n+0 */ + mov.l r1,@r5 + add #4,r5 + mov.l @r2+,r1 /* 16n+4 */ + mov.l r1,@r5 + add #4,r5 + mov.l @r2+,r1 /* 16n+8 */ + mov.l r1,@r5 + add #4,r5 + mov.l @r2+,r1 /* 16n+12 */ + mov.l r1,@r5 + bf/s swsusp_copy_page + add #4,r5 + + bra swsusp_copy_loop + mov.l @(PBE_NEXT, r4), r4 + +swsusp_restore_regs: + ! BL=0: R7->R0 is bank0 + mov.l 3f, r8 + mov.l 4f, r5 + jsr @r5 + nop + + ! BL=1: R7->R0 is bank1 + lds k2, pr + ldc k3, ssr + + mov.l @r15+, r0 + mov.l @r15+, r1 + mov.l @r15+, r2 + mov.l @r15+, r3 + mov.l @r15+, r4 + mov.l @r15+, r5 + mov.l @r15+, r6 + mov.l @r15+, r7 + + rte + nop + ! BL=0: R7->R0 is bank0 + + .align 2 +1: .long swsusp_arch_regs_cpu0 +2: .long restore_pblist +3: .long 0x20000000 ! RB=1 +4: .long restore_regs + +! swsusp_arch_suspend() +! - prepare pc for resume, return from function without swsusp_save on resume +! - save registers in swsusp_arch_regs_cpu0 +! - call swsusp_save write suspend image + +ENTRY(swsusp_arch_suspend) + sts pr, r0 ! save pr in r0 + mov r15, r2 ! save sp in r2 + mov r8, r5 ! save r8 in r5 + stc sr, r1 + ldc r1, ssr ! save sr in ssr + mov.l 1f, r1 + ldc r1, spc ! setup pc value for resuming + mov.l 5f, r15 ! use swsusp_arch_regs_cpu0 as stack + mov.l 6f, r3 + add r3, r15 ! save from top of structure + + ! BL=0: R7->R0 is bank0 + mov.l 2f, r3 ! get new SR value for bank1 + mov #0, r4 + mov.l 7f, r1 + jsr @r1 ! switch to bank1 and save bank1 r7->r0 + not r4, r4 + + ! BL=1: R7->R0 is bank1 + stc r2_bank, k0 ! fetch old sp from r2_bank0 + mov.l 3f, k4 ! SR bits to clear in k4 + mov.l 8f, k1 + jsr @k1 ! switch to bank0 and save all regs + stc r0_bank, k3 ! fetch old pr from r0_bank0 + + ! BL=0: R7->R0 is bank0 + mov r2, r15 ! restore old sp + mov r5, r8 ! restore old r8 + stc ssr, r1 + ldc r1, sr ! restore old sr + lds r0, pr ! restore old pr + mov.l 4f, r0 + jmp @r0 + nop + +swsusp_call_save: + mov r2, r15 ! restore old sp + mov r5, r8 ! restore old r8 + lds r0, pr ! restore old pr + rts + mov #0, r0 + + .align 2 +1: .long swsusp_call_save +2: .long 0x20000000 ! RB=1 +3: .long 0xdfffffff ! RB=0 +4: .long swsusp_save +5: .long swsusp_arch_regs_cpu0 +6: .long SWSUSP_ARCH_REGS_SIZE +7: .long save_low_regs +8: .long save_regs diff --git a/arch/sh/kernel/cpu/sh4/Makefile b/arch/sh/kernel/cpu/sh4/Makefile new file mode 100644 index 000000000..00c16331e --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/Makefile @@ -0,0 +1,37 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the Linux/SuperH SH-4 backends. +# + +obj-y := probe.o common.o +common-y += $(addprefix ../sh3/, entry.o ex.o) + +obj-$(CONFIG_HIBERNATION) += $(addprefix ../sh3/, swsusp.o) +obj-$(CONFIG_SH_FPU) += fpu.o softfloat.o +obj-$(CONFIG_SH_STORE_QUEUES) += sq.o + +# Perf events +perf-$(CONFIG_CPU_SUBTYPE_SH7750) := perf_event.o +perf-$(CONFIG_CPU_SUBTYPE_SH7750S) := perf_event.o +perf-$(CONFIG_CPU_SUBTYPE_SH7091) := perf_event.o + +# CPU subtype setup +obj-$(CONFIG_CPU_SUBTYPE_SH7750) += setup-sh7750.o +obj-$(CONFIG_CPU_SUBTYPE_SH7750R) += setup-sh7750.o +obj-$(CONFIG_CPU_SUBTYPE_SH7750S) += setup-sh7750.o +obj-$(CONFIG_CPU_SUBTYPE_SH7091) += setup-sh7750.o +obj-$(CONFIG_CPU_SUBTYPE_SH7751) += setup-sh7750.o +obj-$(CONFIG_CPU_SUBTYPE_SH7751R) += setup-sh7750.o +obj-$(CONFIG_CPU_SUBTYPE_SH7760) += setup-sh7760.o +obj-$(CONFIG_CPU_SUBTYPE_SH4_202) += setup-sh4-202.o + +# Primary on-chip clocks (common) +ifndef CONFIG_CPU_SH4A +clock-$(CONFIG_CPU_SH4) := clock-sh4.o +endif + +# Additional clocks by subtype +clock-$(CONFIG_CPU_SUBTYPE_SH4_202) += clock-sh4-202.o + +obj-y += $(clock-y) +obj-$(CONFIG_PERF_EVENTS) += $(perf-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 000000000..4b5bab5f8 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/clock-sh4-202.c @@ -0,0 +1,177 @@ +/* + * 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 <linux/io.h> +#include <linux/clkdev.h> +#include <asm/clock.h> +#include <asm/freq.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 unsigned long emi_clk_recalc(struct clk *clk) +{ + int idx = __raw_readl(CPG2_FRQCR3) & 0x0007; + return 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 sh_clk_ops sh4202_emi_clk_ops = { + .recalc = emi_clk_recalc, +}; + +static struct clk sh4202_emi_clk = { + .flags = CLK_ENABLE_ON_INIT, + .ops = &sh4202_emi_clk_ops, +}; + +static unsigned long femi_clk_recalc(struct clk *clk) +{ + int idx = (__raw_readl(CPG2_FRQCR3) >> 3) & 0x0007; + return clk->parent->rate / frqcr3_divisors[idx]; +} + +static struct sh_clk_ops sh4202_femi_clk_ops = { + .recalc = femi_clk_recalc, +}; + +static struct clk sh4202_femi_clk = { + .flags = CLK_ENABLE_ON_INIT, + .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 unsigned long shoc_clk_recalc(struct clk *clk) +{ + int idx = (__raw_readl(CPG2_FRQCR3) >> 6) & 0x0007; + return clk->parent->rate / frqcr3_divisors[idx]; +} + +static int shoc_clk_verify_rate(struct clk *clk, unsigned long rate) +{ + struct clk *bclk = clk_get(NULL, "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 = __raw_readl(CPG2_FRQCR3); + frqcr3 &= ~(0x0007 << 6); + frqcr3 |= tmp << 6; + __raw_writel(frqcr3, CPG2_FRQCR3); + + clk->rate = clk->parent->rate / frqcr3_divisors[tmp]; + + return 0; +} + +static struct sh_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 = { + .flags = CLK_ENABLE_ON_INIT, + .ops = &sh4202_shoc_clk_ops, +}; + +static struct clk *sh4202_onchip_clocks[] = { + &sh4202_emi_clk, + &sh4202_femi_clk, + &sh4202_shoc_clk, +}; + +static struct clk_lookup lookups[] = { + /* main clocks */ + CLKDEV_CON_ID("emi_clk", &sh4202_emi_clk), + CLKDEV_CON_ID("femi_clk", &sh4202_femi_clk), + CLKDEV_CON_ID("shoc_clk", &sh4202_shoc_clk), +}; + +int __init arch_clk_init(void) +{ + struct clk *clk; + int i, ret = 0; + + cpg_clk_init(); + + clk = clk_get(NULL, "master_clk"); + for (i = 0; i < ARRAY_SIZE(sh4202_onchip_clocks); i++) { + struct clk *clkp = sh4202_onchip_clocks[i]; + + clkp->parent = clk; + ret |= clk_register(clkp); + } + + clk_put(clk); + + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); + + return ret; +} 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 000000000..99e5ec8b4 --- /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/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[__raw_readw(FRQCR) & 0x0007]; +} + +static struct sh_clk_ops sh4_master_clk_ops = { + .init = master_clk_init, +}; + +static unsigned long module_clk_recalc(struct clk *clk) +{ + int idx = (__raw_readw(FRQCR) & 0x0007); + return clk->parent->rate / pfc_divisors[idx]; +} + +static struct sh_clk_ops sh4_module_clk_ops = { + .recalc = module_clk_recalc, +}; + +static unsigned long bus_clk_recalc(struct clk *clk) +{ + int idx = (__raw_readw(FRQCR) >> 3) & 0x0007; + return clk->parent->rate / bfc_divisors[idx]; +} + +static struct sh_clk_ops sh4_bus_clk_ops = { + .recalc = bus_clk_recalc, +}; + +static unsigned long cpu_clk_recalc(struct clk *clk) +{ + int idx = (__raw_readw(FRQCR) >> 6) & 0x0007; + return clk->parent->rate / ifc_divisors[idx]; +} + +static struct sh_clk_ops sh4_cpu_clk_ops = { + .recalc = cpu_clk_recalc, +}; + +static struct sh_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 sh_clk_ops **ops, int idx) +{ + if (idx < ARRAY_SIZE(sh4_clk_ops)) + *ops = sh4_clk_ops[idx]; +} + diff --git a/arch/sh/kernel/cpu/sh4/fpu.c b/arch/sh/kernel/cpu/sh4/fpu.c new file mode 100644 index 000000000..95fd2dcb8 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/fpu.c @@ -0,0 +1,428 @@ +/* + * Save/restore floating point context for signal handlers. + * + * 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. + * + * Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka + * Copyright (C) 2006 ST Microelectronics Ltd. (denorm support) + * + * FIXME! These routines have not been tested for big endian case. + */ +#include <linux/sched/signal.h> +#include <linux/io.h> +#include <cpu/fpu.h> +#include <asm/processor.h> +#include <asm/fpu.h> +#include <asm/traps.h> + +/* The PR (precision) bit in the FP Status Register must be clear when + * an frchg instruction is executed, otherwise the instruction is undefined. + * Executing frchg with PR set causes a trap on some SH4 implementations. + */ + +#define FPSCR_RCHG 0x00000000 +extern unsigned long long float64_div(unsigned long long a, + unsigned long long b); +extern unsigned long int float32_div(unsigned long int a, unsigned long int b); +extern unsigned long long float64_mul(unsigned long long a, + unsigned long long b); +extern unsigned long int float32_mul(unsigned long int a, unsigned long int b); +extern unsigned long long float64_add(unsigned long long a, + unsigned long long b); +extern unsigned long int float32_add(unsigned long int a, unsigned long int b); +extern unsigned long long float64_sub(unsigned long long a, + unsigned long long b); +extern unsigned long int float32_sub(unsigned long int a, unsigned long int b); +extern unsigned long int float64_to_float32(unsigned long long a); +static unsigned int fpu_exception_flags; + +/* + * Save FPU registers onto task structure. + */ +void save_fpu(struct task_struct *tsk) +{ + unsigned long dummy; + + enable_fpu(); + asm volatile ("sts.l fpul, @-%0\n\t" + "sts.l fpscr, @-%0\n\t" + "lds %2, fpscr\n\t" + "frchg\n\t" + "fmov.s fr15, @-%0\n\t" + "fmov.s fr14, @-%0\n\t" + "fmov.s fr13, @-%0\n\t" + "fmov.s fr12, @-%0\n\t" + "fmov.s fr11, @-%0\n\t" + "fmov.s fr10, @-%0\n\t" + "fmov.s fr9, @-%0\n\t" + "fmov.s fr8, @-%0\n\t" + "fmov.s fr7, @-%0\n\t" + "fmov.s fr6, @-%0\n\t" + "fmov.s fr5, @-%0\n\t" + "fmov.s fr4, @-%0\n\t" + "fmov.s fr3, @-%0\n\t" + "fmov.s fr2, @-%0\n\t" + "fmov.s fr1, @-%0\n\t" + "fmov.s fr0, @-%0\n\t" + "frchg\n\t" + "fmov.s fr15, @-%0\n\t" + "fmov.s fr14, @-%0\n\t" + "fmov.s fr13, @-%0\n\t" + "fmov.s fr12, @-%0\n\t" + "fmov.s fr11, @-%0\n\t" + "fmov.s fr10, @-%0\n\t" + "fmov.s fr9, @-%0\n\t" + "fmov.s fr8, @-%0\n\t" + "fmov.s fr7, @-%0\n\t" + "fmov.s fr6, @-%0\n\t" + "fmov.s fr5, @-%0\n\t" + "fmov.s fr4, @-%0\n\t" + "fmov.s fr3, @-%0\n\t" + "fmov.s fr2, @-%0\n\t" + "fmov.s fr1, @-%0\n\t" + "fmov.s fr0, @-%0\n\t" + "lds %3, fpscr\n\t":"=r" (dummy) + :"0"((char *)(&tsk->thread.xstate->hardfpu.status)), + "r"(FPSCR_RCHG), "r"(FPSCR_INIT) + :"memory"); + + disable_fpu(); +} + +void restore_fpu(struct task_struct *tsk) +{ + unsigned long dummy; + + enable_fpu(); + asm volatile ("lds %2, fpscr\n\t" + "fmov.s @%0+, fr0\n\t" + "fmov.s @%0+, fr1\n\t" + "fmov.s @%0+, fr2\n\t" + "fmov.s @%0+, fr3\n\t" + "fmov.s @%0+, fr4\n\t" + "fmov.s @%0+, fr5\n\t" + "fmov.s @%0+, fr6\n\t" + "fmov.s @%0+, fr7\n\t" + "fmov.s @%0+, fr8\n\t" + "fmov.s @%0+, fr9\n\t" + "fmov.s @%0+, fr10\n\t" + "fmov.s @%0+, fr11\n\t" + "fmov.s @%0+, fr12\n\t" + "fmov.s @%0+, fr13\n\t" + "fmov.s @%0+, fr14\n\t" + "fmov.s @%0+, fr15\n\t" + "frchg\n\t" + "fmov.s @%0+, fr0\n\t" + "fmov.s @%0+, fr1\n\t" + "fmov.s @%0+, fr2\n\t" + "fmov.s @%0+, fr3\n\t" + "fmov.s @%0+, fr4\n\t" + "fmov.s @%0+, fr5\n\t" + "fmov.s @%0+, fr6\n\t" + "fmov.s @%0+, fr7\n\t" + "fmov.s @%0+, fr8\n\t" + "fmov.s @%0+, fr9\n\t" + "fmov.s @%0+, fr10\n\t" + "fmov.s @%0+, fr11\n\t" + "fmov.s @%0+, fr12\n\t" + "fmov.s @%0+, fr13\n\t" + "fmov.s @%0+, fr14\n\t" + "fmov.s @%0+, fr15\n\t" + "frchg\n\t" + "lds.l @%0+, fpscr\n\t" + "lds.l @%0+, fpul\n\t" + :"=r" (dummy) + :"0" (tsk->thread.xstate), "r" (FPSCR_RCHG) + :"memory"); + disable_fpu(); +} + +/** + * denormal_to_double - Given denormalized float number, + * store double float + * + * @fpu: Pointer to sh_fpu_hard structure + * @n: Index to FP register + */ +static void denormal_to_double(struct sh_fpu_hard_struct *fpu, int n) +{ + unsigned long du, dl; + unsigned long x = fpu->fpul; + int exp = 1023 - 126; + + if (x != 0 && (x & 0x7f800000) == 0) { + du = (x & 0x80000000); + while ((x & 0x00800000) == 0) { + x <<= 1; + exp--; + } + x &= 0x007fffff; + du |= (exp << 20) | (x >> 3); + dl = x << 29; + + fpu->fp_regs[n] = du; + fpu->fp_regs[n + 1] = dl; + } +} + +/** + * ieee_fpe_handler - Handle denormalized number exception + * + * @regs: Pointer to register structure + * + * Returns 1 when it's handled (should not cause exception). + */ +static int ieee_fpe_handler(struct pt_regs *regs) +{ + unsigned short insn = *(unsigned short *)regs->pc; + unsigned short finsn; + unsigned long nextpc; + int nib[4] = { + (insn >> 12) & 0xf, + (insn >> 8) & 0xf, + (insn >> 4) & 0xf, + insn & 0xf + }; + + if (nib[0] == 0xb || (nib[0] == 0x4 && nib[2] == 0x0 && nib[3] == 0xb)) + regs->pr = regs->pc + 4; /* bsr & jsr */ + + if (nib[0] == 0xa || nib[0] == 0xb) { + /* bra & bsr */ + nextpc = regs->pc + 4 + ((short)((insn & 0xfff) << 4) >> 3); + finsn = *(unsigned short *)(regs->pc + 2); + } else if (nib[0] == 0x8 && nib[1] == 0xd) { + /* bt/s */ + if (regs->sr & 1) + nextpc = regs->pc + 4 + ((char)(insn & 0xff) << 1); + else + nextpc = regs->pc + 4; + finsn = *(unsigned short *)(regs->pc + 2); + } else if (nib[0] == 0x8 && nib[1] == 0xf) { + /* bf/s */ + if (regs->sr & 1) + nextpc = regs->pc + 4; + else + nextpc = regs->pc + 4 + ((char)(insn & 0xff) << 1); + finsn = *(unsigned short *)(regs->pc + 2); + } else if (nib[0] == 0x4 && nib[3] == 0xb && + (nib[2] == 0x0 || nib[2] == 0x2)) { + /* jmp & jsr */ + nextpc = regs->regs[nib[1]]; + finsn = *(unsigned short *)(regs->pc + 2); + } else if (nib[0] == 0x0 && nib[3] == 0x3 && + (nib[2] == 0x0 || nib[2] == 0x2)) { + /* braf & bsrf */ + nextpc = regs->pc + 4 + regs->regs[nib[1]]; + finsn = *(unsigned short *)(regs->pc + 2); + } else if (insn == 0x000b) { + /* rts */ + nextpc = regs->pr; + finsn = *(unsigned short *)(regs->pc + 2); + } else { + nextpc = regs->pc + instruction_size(insn); + finsn = insn; + } + + if ((finsn & 0xf1ff) == 0xf0ad) { + /* fcnvsd */ + struct task_struct *tsk = current; + + if ((tsk->thread.xstate->hardfpu.fpscr & FPSCR_CAUSE_ERROR)) + /* FPU error */ + denormal_to_double(&tsk->thread.xstate->hardfpu, + (finsn >> 8) & 0xf); + else + return 0; + + regs->pc = nextpc; + return 1; + } else if ((finsn & 0xf00f) == 0xf002) { + /* fmul */ + struct task_struct *tsk = current; + int fpscr; + int n, m, prec; + unsigned int hx, hy; + + n = (finsn >> 8) & 0xf; + m = (finsn >> 4) & 0xf; + hx = tsk->thread.xstate->hardfpu.fp_regs[n]; + hy = tsk->thread.xstate->hardfpu.fp_regs[m]; + fpscr = tsk->thread.xstate->hardfpu.fpscr; + prec = fpscr & FPSCR_DBL_PRECISION; + + if ((fpscr & FPSCR_CAUSE_ERROR) + && (prec && ((hx & 0x7fffffff) < 0x00100000 + || (hy & 0x7fffffff) < 0x00100000))) { + long long llx, lly; + + /* FPU error because of denormal (doubles) */ + llx = ((long long)hx << 32) + | tsk->thread.xstate->hardfpu.fp_regs[n + 1]; + lly = ((long long)hy << 32) + | tsk->thread.xstate->hardfpu.fp_regs[m + 1]; + llx = float64_mul(llx, lly); + tsk->thread.xstate->hardfpu.fp_regs[n] = llx >> 32; + tsk->thread.xstate->hardfpu.fp_regs[n + 1] = llx & 0xffffffff; + } else if ((fpscr & FPSCR_CAUSE_ERROR) + && (!prec && ((hx & 0x7fffffff) < 0x00800000 + || (hy & 0x7fffffff) < 0x00800000))) { + /* FPU error because of denormal (floats) */ + hx = float32_mul(hx, hy); + tsk->thread.xstate->hardfpu.fp_regs[n] = hx; + } else + return 0; + + regs->pc = nextpc; + return 1; + } else if ((finsn & 0xf00e) == 0xf000) { + /* fadd, fsub */ + struct task_struct *tsk = current; + int fpscr; + int n, m, prec; + unsigned int hx, hy; + + n = (finsn >> 8) & 0xf; + m = (finsn >> 4) & 0xf; + hx = tsk->thread.xstate->hardfpu.fp_regs[n]; + hy = tsk->thread.xstate->hardfpu.fp_regs[m]; + fpscr = tsk->thread.xstate->hardfpu.fpscr; + prec = fpscr & FPSCR_DBL_PRECISION; + + if ((fpscr & FPSCR_CAUSE_ERROR) + && (prec && ((hx & 0x7fffffff) < 0x00100000 + || (hy & 0x7fffffff) < 0x00100000))) { + long long llx, lly; + + /* FPU error because of denormal (doubles) */ + llx = ((long long)hx << 32) + | tsk->thread.xstate->hardfpu.fp_regs[n + 1]; + lly = ((long long)hy << 32) + | tsk->thread.xstate->hardfpu.fp_regs[m + 1]; + if ((finsn & 0xf00f) == 0xf000) + llx = float64_add(llx, lly); + else + llx = float64_sub(llx, lly); + tsk->thread.xstate->hardfpu.fp_regs[n] = llx >> 32; + tsk->thread.xstate->hardfpu.fp_regs[n + 1] = llx & 0xffffffff; + } else if ((fpscr & FPSCR_CAUSE_ERROR) + && (!prec && ((hx & 0x7fffffff) < 0x00800000 + || (hy & 0x7fffffff) < 0x00800000))) { + /* FPU error because of denormal (floats) */ + if ((finsn & 0xf00f) == 0xf000) + hx = float32_add(hx, hy); + else + hx = float32_sub(hx, hy); + tsk->thread.xstate->hardfpu.fp_regs[n] = hx; + } else + return 0; + + regs->pc = nextpc; + return 1; + } else if ((finsn & 0xf003) == 0xf003) { + /* fdiv */ + struct task_struct *tsk = current; + int fpscr; + int n, m, prec; + unsigned int hx, hy; + + n = (finsn >> 8) & 0xf; + m = (finsn >> 4) & 0xf; + hx = tsk->thread.xstate->hardfpu.fp_regs[n]; + hy = tsk->thread.xstate->hardfpu.fp_regs[m]; + fpscr = tsk->thread.xstate->hardfpu.fpscr; + prec = fpscr & FPSCR_DBL_PRECISION; + + if ((fpscr & FPSCR_CAUSE_ERROR) + && (prec && ((hx & 0x7fffffff) < 0x00100000 + || (hy & 0x7fffffff) < 0x00100000))) { + long long llx, lly; + + /* FPU error because of denormal (doubles) */ + llx = ((long long)hx << 32) + | tsk->thread.xstate->hardfpu.fp_regs[n + 1]; + lly = ((long long)hy << 32) + | tsk->thread.xstate->hardfpu.fp_regs[m + 1]; + + llx = float64_div(llx, lly); + + tsk->thread.xstate->hardfpu.fp_regs[n] = llx >> 32; + tsk->thread.xstate->hardfpu.fp_regs[n + 1] = llx & 0xffffffff; + } else if ((fpscr & FPSCR_CAUSE_ERROR) + && (!prec && ((hx & 0x7fffffff) < 0x00800000 + || (hy & 0x7fffffff) < 0x00800000))) { + /* FPU error because of denormal (floats) */ + hx = float32_div(hx, hy); + tsk->thread.xstate->hardfpu.fp_regs[n] = hx; + } else + return 0; + + regs->pc = nextpc; + return 1; + } else if ((finsn & 0xf0bd) == 0xf0bd) { + /* fcnvds - double to single precision convert */ + struct task_struct *tsk = current; + int m; + unsigned int hx; + + m = (finsn >> 8) & 0x7; + hx = tsk->thread.xstate->hardfpu.fp_regs[m]; + + if ((tsk->thread.xstate->hardfpu.fpscr & FPSCR_CAUSE_ERROR) + && ((hx & 0x7fffffff) < 0x00100000)) { + /* subnormal double to float conversion */ + long long llx; + + llx = ((long long)tsk->thread.xstate->hardfpu.fp_regs[m] << 32) + | tsk->thread.xstate->hardfpu.fp_regs[m + 1]; + + tsk->thread.xstate->hardfpu.fpul = float64_to_float32(llx); + } else + return 0; + + regs->pc = nextpc; + return 1; + } + + return 0; +} + +void float_raise(unsigned int flags) +{ + fpu_exception_flags |= flags; +} + +int float_rounding_mode(void) +{ + struct task_struct *tsk = current; + int roundingMode = FPSCR_ROUNDING_MODE(tsk->thread.xstate->hardfpu.fpscr); + return roundingMode; +} + +BUILD_TRAP_HANDLER(fpu_error) +{ + struct task_struct *tsk = current; + TRAP_HANDLER_DECL; + + __unlazy_fpu(tsk, regs); + fpu_exception_flags = 0; + if (ieee_fpe_handler(regs)) { + tsk->thread.xstate->hardfpu.fpscr &= + ~(FPSCR_CAUSE_MASK | FPSCR_FLAG_MASK); + tsk->thread.xstate->hardfpu.fpscr |= fpu_exception_flags; + /* Set the FPSCR flag as well as cause bits - simply + * replicate the cause */ + tsk->thread.xstate->hardfpu.fpscr |= (fpu_exception_flags >> 10); + grab_fpu(regs); + restore_fpu(tsk); + task_thread_info(tsk)->status |= TS_USEDFPU; + if ((((tsk->thread.xstate->hardfpu.fpscr & FPSCR_ENABLE_MASK) >> 7) & + (fpu_exception_flags >> 2)) == 0) { + return; + } + } + + force_sig(SIGFPE, tsk); +} diff --git a/arch/sh/kernel/cpu/sh4/perf_event.c b/arch/sh/kernel/cpu/sh4/perf_event.c new file mode 100644 index 000000000..fa4f724b2 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/perf_event.c @@ -0,0 +1,268 @@ +/* + * Performance events support for SH7750-style performance counters + * + * 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/kernel.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/perf_event.h> +#include <asm/processor.h> + +#define PM_CR_BASE 0xff000084 /* 16-bit */ +#define PM_CTR_BASE 0xff100004 /* 32-bit */ + +#define PMCR(n) (PM_CR_BASE + ((n) * 0x04)) +#define PMCTRH(n) (PM_CTR_BASE + 0x00 + ((n) * 0x08)) +#define PMCTRL(n) (PM_CTR_BASE + 0x04 + ((n) * 0x08)) + +#define PMCR_PMM_MASK 0x0000003f + +#define PMCR_CLKF 0x00000100 +#define PMCR_PMCLR 0x00002000 +#define PMCR_PMST 0x00004000 +#define PMCR_PMEN 0x00008000 + +static struct sh_pmu sh7750_pmu; + +/* + * There are a number of events supported by each counter (33 in total). + * Since we have 2 counters, each counter will take the event code as it + * corresponds to the PMCR PMM setting. Each counter can be configured + * independently. + * + * Event Code Description + * ---------- ----------- + * + * 0x01 Operand read access + * 0x02 Operand write access + * 0x03 UTLB miss + * 0x04 Operand cache read miss + * 0x05 Operand cache write miss + * 0x06 Instruction fetch (w/ cache) + * 0x07 Instruction TLB miss + * 0x08 Instruction cache miss + * 0x09 All operand accesses + * 0x0a All instruction accesses + * 0x0b OC RAM operand access + * 0x0d On-chip I/O space access + * 0x0e Operand access (r/w) + * 0x0f Operand cache miss (r/w) + * 0x10 Branch instruction + * 0x11 Branch taken + * 0x12 BSR/BSRF/JSR + * 0x13 Instruction execution + * 0x14 Instruction execution in parallel + * 0x15 FPU Instruction execution + * 0x16 Interrupt + * 0x17 NMI + * 0x18 trapa instruction execution + * 0x19 UBCA match + * 0x1a UBCB match + * 0x21 Instruction cache fill + * 0x22 Operand cache fill + * 0x23 Elapsed time + * 0x24 Pipeline freeze by I-cache miss + * 0x25 Pipeline freeze by D-cache miss + * 0x27 Pipeline freeze by branch instruction + * 0x28 Pipeline freeze by CPU register + * 0x29 Pipeline freeze by FPU + */ + +static const int sh7750_general_events[] = { + [PERF_COUNT_HW_CPU_CYCLES] = 0x0023, + [PERF_COUNT_HW_INSTRUCTIONS] = 0x000a, + [PERF_COUNT_HW_CACHE_REFERENCES] = 0x0006, /* I-cache */ + [PERF_COUNT_HW_CACHE_MISSES] = 0x0008, /* I-cache */ + [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x0010, + [PERF_COUNT_HW_BRANCH_MISSES] = -1, + [PERF_COUNT_HW_BUS_CYCLES] = -1, +}; + +#define C(x) PERF_COUNT_HW_CACHE_##x + +static const int sh7750_cache_events + [PERF_COUNT_HW_CACHE_MAX] + [PERF_COUNT_HW_CACHE_OP_MAX] + [PERF_COUNT_HW_CACHE_RESULT_MAX] = +{ + [ C(L1D) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x0001, + [ C(RESULT_MISS) ] = 0x0004, + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = 0x0002, + [ C(RESULT_MISS) ] = 0x0005, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = 0, + [ C(RESULT_MISS) ] = 0, + }, + }, + + [ C(L1I) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x0006, + [ C(RESULT_MISS) ] = 0x0008, + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = 0, + [ C(RESULT_MISS) ] = 0, + }, + }, + + [ C(LL) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0, + [ C(RESULT_MISS) ] = 0, + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = 0, + [ C(RESULT_MISS) ] = 0, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = 0, + [ C(RESULT_MISS) ] = 0, + }, + }, + + [ C(DTLB) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0, + [ C(RESULT_MISS) ] = 0x0003, + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = 0, + [ C(RESULT_MISS) ] = 0, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = 0, + [ C(RESULT_MISS) ] = 0, + }, + }, + + [ C(ITLB) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0, + [ C(RESULT_MISS) ] = 0x0007, + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + }, + + [ C(BPU) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + }, + + [ C(NODE) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + }, +}; + +static int sh7750_event_map(int event) +{ + return sh7750_general_events[event]; +} + +static u64 sh7750_pmu_read(int idx) +{ + return (u64)((u64)(__raw_readl(PMCTRH(idx)) & 0xffff) << 32) | + __raw_readl(PMCTRL(idx)); +} + +static void sh7750_pmu_disable(struct hw_perf_event *hwc, int idx) +{ + unsigned int tmp; + + tmp = __raw_readw(PMCR(idx)); + tmp &= ~(PMCR_PMM_MASK | PMCR_PMEN); + __raw_writew(tmp, PMCR(idx)); +} + +static void sh7750_pmu_enable(struct hw_perf_event *hwc, int idx) +{ + __raw_writew(__raw_readw(PMCR(idx)) | PMCR_PMCLR, PMCR(idx)); + __raw_writew(hwc->config | PMCR_PMEN | PMCR_PMST, PMCR(idx)); +} + +static void sh7750_pmu_disable_all(void) +{ + int i; + + for (i = 0; i < sh7750_pmu.num_events; i++) + __raw_writew(__raw_readw(PMCR(i)) & ~PMCR_PMEN, PMCR(i)); +} + +static void sh7750_pmu_enable_all(void) +{ + int i; + + for (i = 0; i < sh7750_pmu.num_events; i++) + __raw_writew(__raw_readw(PMCR(i)) | PMCR_PMEN, PMCR(i)); +} + +static struct sh_pmu sh7750_pmu = { + .name = "sh7750", + .num_events = 2, + .event_map = sh7750_event_map, + .max_events = ARRAY_SIZE(sh7750_general_events), + .raw_event_mask = PMCR_PMM_MASK, + .cache_events = &sh7750_cache_events, + .read = sh7750_pmu_read, + .disable = sh7750_pmu_disable, + .enable = sh7750_pmu_enable, + .disable_all = sh7750_pmu_disable_all, + .enable_all = sh7750_pmu_enable_all, +}; + +static int __init sh7750_pmu_init(void) +{ + /* + * Make sure this CPU actually has perf counters. + */ + if (!(boot_cpu_data.flags & CPU_HAS_PERF_COUNTER)) { + pr_notice("HW perf events unsupported, software events only.\n"); + return -ENODEV; + } + + return register_sh_pmu(&sh7750_pmu); +} +early_initcall(sh7750_pmu_init); diff --git a/arch/sh/kernel/cpu/sh4/probe.c b/arch/sh/kernel/cpu/sh4/probe.c new file mode 100644 index 000000000..a521bcf50 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/probe.c @@ -0,0 +1,263 @@ +/* + * arch/sh/kernel/cpu/sh4/probe.c + * + * CPU Subtype Probing for SH-4. + * + * Copyright (C) 2001 - 2007 Paul Mundt + * Copyright (C) 2003 Richard Curnow + * + * 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/io.h> +#include <asm/processor.h> +#include <asm/cache.h> + +void cpu_probe(void) +{ + unsigned long pvr, prr, cvr; + unsigned long size; + + static unsigned long sizes[16] = { + [1] = (1 << 12), + [2] = (1 << 13), + [4] = (1 << 14), + [8] = (1 << 15), + [9] = (1 << 16) + }; + + pvr = (__raw_readl(CCN_PVR) >> 8) & 0xffffff; + prr = (__raw_readl(CCN_PRR) >> 4) & 0xff; + cvr = (__raw_readl(CCN_CVR)); + + /* + * Setup some sane SH-4 defaults for the icache + */ + boot_cpu_data.icache.way_incr = (1 << 13); + boot_cpu_data.icache.entry_shift = 5; + boot_cpu_data.icache.sets = 256; + boot_cpu_data.icache.ways = 1; + boot_cpu_data.icache.linesz = L1_CACHE_BYTES; + + /* + * And again for the dcache .. + */ + boot_cpu_data.dcache.way_incr = (1 << 14); + boot_cpu_data.dcache.entry_shift = 5; + boot_cpu_data.dcache.sets = 512; + boot_cpu_data.dcache.ways = 1; + boot_cpu_data.dcache.linesz = L1_CACHE_BYTES; + + /* We don't know the chip cut */ + boot_cpu_data.cut_major = boot_cpu_data.cut_minor = -1; + + /* + * Setup some generic flags we can probe on SH-4A parts + */ + if (((pvr >> 16) & 0xff) == 0x10) { + boot_cpu_data.family = CPU_FAMILY_SH4A; + + if ((cvr & 0x10000000) == 0) { + boot_cpu_data.flags |= CPU_HAS_DSP; + boot_cpu_data.family = CPU_FAMILY_SH4AL_DSP; + } + + boot_cpu_data.flags |= CPU_HAS_LLSC | CPU_HAS_PERF_COUNTER; + boot_cpu_data.cut_major = pvr & 0x7f; + + boot_cpu_data.icache.ways = 4; + boot_cpu_data.dcache.ways = 4; + } else { + /* And some SH-4 defaults.. */ + boot_cpu_data.flags |= CPU_HAS_PTEA | CPU_HAS_FPU; + boot_cpu_data.family = CPU_FAMILY_SH4; + } + + /* FPU detection works for almost everyone */ + if ((cvr & 0x20000000)) + boot_cpu_data.flags |= CPU_HAS_FPU; + + /* Mask off the upper chip ID */ + pvr &= 0xffff; + + /* + * Probe the underlying processor version/revision and + * adjust cpu_data setup accordingly. + */ + switch (pvr) { + case 0x205: + boot_cpu_data.type = CPU_SH7750; + boot_cpu_data.flags |= CPU_HAS_P2_FLUSH_BUG | + CPU_HAS_PERF_COUNTER; + break; + case 0x206: + boot_cpu_data.type = CPU_SH7750S; + boot_cpu_data.flags |= CPU_HAS_P2_FLUSH_BUG | + CPU_HAS_PERF_COUNTER; + break; + case 0x1100: + boot_cpu_data.type = CPU_SH7751; + break; + case 0x2001: + case 0x2004: + boot_cpu_data.type = CPU_SH7770; + break; + case 0x2006: + case 0x200A: + if (prr == 0x61) + boot_cpu_data.type = CPU_SH7781; + else if (prr == 0xa1) + boot_cpu_data.type = CPU_SH7763; + else + boot_cpu_data.type = CPU_SH7780; + + break; + case 0x3000: + case 0x3003: + case 0x3009: + boot_cpu_data.type = CPU_SH7343; + break; + case 0x3004: + case 0x3007: + boot_cpu_data.type = CPU_SH7785; + break; + case 0x4004: + case 0x4005: + boot_cpu_data.type = CPU_SH7786; + boot_cpu_data.flags |= CPU_HAS_PTEAEX | CPU_HAS_L2_CACHE; + break; + case 0x3008: + switch (prr) { + case 0x50: + case 0x51: + boot_cpu_data.type = CPU_SH7723; + boot_cpu_data.flags |= CPU_HAS_L2_CACHE; + break; + case 0x70: + boot_cpu_data.type = CPU_SH7366; + break; + case 0xa0: + case 0xa1: + boot_cpu_data.type = CPU_SH7722; + break; + } + break; + case 0x300b: + switch (prr) { + case 0x20: + boot_cpu_data.type = CPU_SH7724; + boot_cpu_data.flags |= CPU_HAS_L2_CACHE; + break; + case 0x10: + case 0x11: + boot_cpu_data.type = CPU_SH7757; + break; + case 0xd0: + case 0x40: /* yon-ten-go */ + boot_cpu_data.type = CPU_SH7372; + break; + case 0xE0: /* 0x4E0 */ + boot_cpu_data.type = CPU_SH7734; /* SH7733/SH7734 */ + break; + + } + break; + case 0x4000: /* 1st cut */ + case 0x4001: /* 2nd cut */ + boot_cpu_data.type = CPU_SHX3; + break; + case 0x700: + boot_cpu_data.type = CPU_SH4_501; + boot_cpu_data.flags &= ~CPU_HAS_FPU; + boot_cpu_data.icache.ways = 2; + boot_cpu_data.dcache.ways = 2; + break; + case 0x600: + boot_cpu_data.type = CPU_SH4_202; + boot_cpu_data.icache.ways = 2; + boot_cpu_data.dcache.ways = 2; + break; + case 0x500 ... 0x501: + switch (prr) { + case 0x10: + boot_cpu_data.type = CPU_SH7750R; + break; + case 0x11: + boot_cpu_data.type = CPU_SH7751R; + break; + case 0x50 ... 0x5f: + boot_cpu_data.type = CPU_SH7760; + break; + } + + boot_cpu_data.icache.ways = 2; + boot_cpu_data.dcache.ways = 2; + + break; + } + + /* + * On anything that's not a direct-mapped cache, look to the CVR + * for I/D-cache specifics. + */ + if (boot_cpu_data.icache.ways > 1) { + size = sizes[(cvr >> 20) & 0xf]; + boot_cpu_data.icache.way_incr = (size >> 1); + boot_cpu_data.icache.sets = (size >> 6); + + } + + /* And the rest of the D-cache */ + if (boot_cpu_data.dcache.ways > 1) { + size = sizes[(cvr >> 16) & 0xf]; + boot_cpu_data.dcache.way_incr = (size >> 1); + boot_cpu_data.dcache.sets = (size >> 6); + } + + /* + * SH-4A's have an optional PIPT L2. + */ + if (boot_cpu_data.flags & CPU_HAS_L2_CACHE) { + /* + * Verify that it really has something hooked up, this + * is the safety net for CPUs that have optional L2 + * support yet do not implement it. + */ + if ((cvr & 0xf) == 0) + boot_cpu_data.flags &= ~CPU_HAS_L2_CACHE; + else { + /* + * Silicon and specifications have clearly never + * met.. + */ + cvr ^= 0xf; + + /* + * Size calculation is much more sensible + * than it is for the L1. + * + * Sizes are 128KB, 256KB, 512KB, and 1MB. + */ + size = (cvr & 0xf) << 17; + + boot_cpu_data.scache.way_incr = (1 << 16); + boot_cpu_data.scache.entry_shift = 5; + boot_cpu_data.scache.ways = 4; + boot_cpu_data.scache.linesz = L1_CACHE_BYTES; + + boot_cpu_data.scache.entry_mask = + (boot_cpu_data.scache.way_incr - + boot_cpu_data.scache.linesz); + + boot_cpu_data.scache.sets = size / + (boot_cpu_data.scache.linesz * + boot_cpu_data.scache.ways); + + boot_cpu_data.scache.way_size = + (boot_cpu_data.scache.sets * + boot_cpu_data.scache.linesz); + } + } +} diff --git a/arch/sh/kernel/cpu/sh4/setup-sh4-202.c b/arch/sh/kernel/cpu/sh4/setup-sh4-202.c new file mode 100644 index 000000000..2623f820d --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/setup-sh4-202.c @@ -0,0 +1,141 @@ +/* + * SH4-202 Setup + * + * Copyright (C) 2006 Paul Mundt + * Copyright (C) 2009 Magnus Damm + * + * 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/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <linux/serial_sci.h> +#include <linux/sh_timer.h> +#include <linux/sh_intc.h> +#include <linux/io.h> + +static struct plat_sci_port scif0_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, +}; + +static struct resource scif0_resources[] = { + DEFINE_RES_MEM(0xffe80000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x700)), + DEFINE_RES_IRQ(evt2irq(0x720)), + DEFINE_RES_IRQ(evt2irq(0x760)), + DEFINE_RES_IRQ(evt2irq(0x740)), +}; + +static struct platform_device scif0_device = { + .name = "sh-sci", + .id = 0, + .resource = scif0_resources, + .num_resources = ARRAY_SIZE(scif0_resources), + .dev = { + .platform_data = &scif0_platform_data, + }, +}; + +static struct sh_timer_config tmu0_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu0_resources[] = { + DEFINE_RES_MEM(0xffd80000, 0x30), + DEFINE_RES_IRQ(evt2irq(0x400)), + DEFINE_RES_IRQ(evt2irq(0x420)), + DEFINE_RES_IRQ(evt2irq(0x440)), +}; + +static struct platform_device tmu0_device = { + .name = "sh-tmu", + .id = 0, + .dev = { + .platform_data = &tmu0_platform_data, + }, + .resource = tmu0_resources, + .num_resources = ARRAY_SIZE(tmu0_resources), +}; + +static struct platform_device *sh4202_devices[] __initdata = { + &scif0_device, + &tmu0_device, +}; + +static int __init sh4202_devices_setup(void) +{ + return platform_add_devices(sh4202_devices, + ARRAY_SIZE(sh4202_devices)); +} +arch_initcall(sh4202_devices_setup); + +static struct platform_device *sh4202_early_devices[] __initdata = { + &scif0_device, + &tmu0_device, +}; + +void __init plat_early_device_setup(void) +{ + early_platform_add_devices(sh4202_early_devices, + ARRAY_SIZE(sh4202_early_devices)); +} + +enum { + UNUSED = 0, + + /* interrupt sources */ + IRL0, IRL1, IRL2, IRL3, /* only IRLM mode supported */ + HUDI, TMU0, TMU1, TMU2, RTC, SCIF, WDT, +}; + +static struct intc_vect vectors[] __initdata = { + INTC_VECT(HUDI, 0x600), + INTC_VECT(TMU0, 0x400), INTC_VECT(TMU1, 0x420), + INTC_VECT(TMU2, 0x440), INTC_VECT(TMU2, 0x460), + INTC_VECT(RTC, 0x480), INTC_VECT(RTC, 0x4a0), + INTC_VECT(RTC, 0x4c0), + INTC_VECT(SCIF, 0x700), INTC_VECT(SCIF, 0x720), + INTC_VECT(SCIF, 0x740), INTC_VECT(SCIF, 0x760), + INTC_VECT(WDT, 0x560), +}; + +static struct intc_prio_reg prio_registers[] __initdata = { + { 0xffd00004, 0, 16, 4, /* IPRA */ { TMU0, TMU1, TMU2, RTC } }, + { 0xffd00008, 0, 16, 4, /* IPRB */ { WDT, 0, 0, 0 } }, + { 0xffd0000c, 0, 16, 4, /* IPRC */ { 0, 0, SCIF, HUDI } }, + { 0xffd00010, 0, 16, 4, /* IPRD */ { IRL0, IRL1, IRL2, IRL3 } }, +}; + +static DECLARE_INTC_DESC(intc_desc, "sh4-202", vectors, NULL, + NULL, prio_registers, NULL); + +static struct intc_vect vectors_irlm[] __initdata = { + INTC_VECT(IRL0, 0x240), INTC_VECT(IRL1, 0x2a0), + INTC_VECT(IRL2, 0x300), INTC_VECT(IRL3, 0x360), +}; + +static DECLARE_INTC_DESC(intc_desc_irlm, "sh4-202_irlm", vectors_irlm, NULL, + NULL, prio_registers, NULL); + +void __init plat_irq_setup(void) +{ + register_intc_controller(&intc_desc); +} + +#define INTC_ICR 0xffd00000UL +#define INTC_ICR_IRLM (1<<7) + +void __init plat_irq_setup_pins(int mode) +{ + switch (mode) { + case IRQ_MODE_IRQ: /* individual interrupt mode for IRL3-0 */ + __raw_writew(__raw_readw(INTC_ICR) | INTC_ICR_IRLM, INTC_ICR); + register_intc_controller(&intc_desc_irlm); + break; + default: + BUG(); + } +} diff --git a/arch/sh/kernel/cpu/sh4/setup-sh7750.c b/arch/sh/kernel/cpu/sh4/setup-sh7750.c new file mode 100644 index 000000000..57d306892 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/setup-sh7750.c @@ -0,0 +1,361 @@ +/* + * SH7091/SH7750/SH7750S/SH7750R/SH7751/SH7751R Setup + * + * Copyright (C) 2006 Paul Mundt + * Copyright (C) 2006 Jamie Lenehan + * + * 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/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <linux/io.h> +#include <linux/sh_timer.h> +#include <linux/sh_intc.h> +#include <linux/serial_sci.h> +#include <generated/machtypes.h> + +static struct resource rtc_resources[] = { + [0] = { + .start = 0xffc80000, + .end = 0xffc80000 + 0x58 - 1, + .flags = IORESOURCE_IO, + }, + [1] = { + /* Shared Period/Carry/Alarm IRQ */ + .start = evt2irq(0x480), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device rtc_device = { + .name = "sh-rtc", + .id = -1, + .num_resources = ARRAY_SIZE(rtc_resources), + .resource = rtc_resources, +}; + +static struct plat_sci_port sci_platform_data = { + .type = PORT_SCI, +}; + +static struct resource sci_resources[] = { + DEFINE_RES_MEM(0xffe00000, 0x20), + DEFINE_RES_IRQ(evt2irq(0x4e0)), +}; + +static struct platform_device sci_device = { + .name = "sh-sci", + .id = 0, + .resource = sci_resources, + .num_resources = ARRAY_SIZE(sci_resources), + .dev = { + .platform_data = &sci_platform_data, + }, +}; + +static struct plat_sci_port scif_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, +}; + +static struct resource scif_resources[] = { + DEFINE_RES_MEM(0xffe80000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x700)), +}; + +static struct platform_device scif_device = { + .name = "sh-sci", + .id = 1, + .resource = scif_resources, + .num_resources = ARRAY_SIZE(scif_resources), + .dev = { + .platform_data = &scif_platform_data, + }, +}; + +static struct sh_timer_config tmu0_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu0_resources[] = { + DEFINE_RES_MEM(0xffd80000, 0x30), + DEFINE_RES_IRQ(evt2irq(0x400)), + DEFINE_RES_IRQ(evt2irq(0x420)), + DEFINE_RES_IRQ(evt2irq(0x440)), +}; + +static struct platform_device tmu0_device = { + .name = "sh-tmu", + .id = 0, + .dev = { + .platform_data = &tmu0_platform_data, + }, + .resource = tmu0_resources, + .num_resources = ARRAY_SIZE(tmu0_resources), +}; + +/* SH7750R, SH7751 and SH7751R all have two extra timer channels */ +#if defined(CONFIG_CPU_SUBTYPE_SH7750R) || \ + defined(CONFIG_CPU_SUBTYPE_SH7751) || \ + defined(CONFIG_CPU_SUBTYPE_SH7751R) + +static struct sh_timer_config tmu1_platform_data = { + .channels_mask = 3, +}; + +static struct resource tmu1_resources[] = { + DEFINE_RES_MEM(0xfe100000, 0x20), + DEFINE_RES_IRQ(evt2irq(0xb00)), + DEFINE_RES_IRQ(evt2irq(0xb80)), +}; + +static struct platform_device tmu1_device = { + .name = "sh-tmu", + .id = 1, + .dev = { + .platform_data = &tmu1_platform_data, + }, + .resource = tmu1_resources, + .num_resources = ARRAY_SIZE(tmu1_resources), +}; + +#endif + +static struct platform_device *sh7750_devices[] __initdata = { + &rtc_device, + &tmu0_device, +#if defined(CONFIG_CPU_SUBTYPE_SH7750R) || \ + defined(CONFIG_CPU_SUBTYPE_SH7751) || \ + defined(CONFIG_CPU_SUBTYPE_SH7751R) + &tmu1_device, +#endif +}; + +static int __init sh7750_devices_setup(void) +{ + if (mach_is_rts7751r2d()) { + platform_device_register(&scif_device); + } else { + platform_device_register(&sci_device); + platform_device_register(&scif_device); + } + + return platform_add_devices(sh7750_devices, + ARRAY_SIZE(sh7750_devices)); +} +arch_initcall(sh7750_devices_setup); + +static struct platform_device *sh7750_early_devices[] __initdata = { + &tmu0_device, +#if defined(CONFIG_CPU_SUBTYPE_SH7750R) || \ + defined(CONFIG_CPU_SUBTYPE_SH7751) || \ + defined(CONFIG_CPU_SUBTYPE_SH7751R) + &tmu1_device, +#endif +}; + +void __init plat_early_device_setup(void) +{ + struct platform_device *dev[1]; + + if (mach_is_rts7751r2d()) { + scif_platform_data.scscr |= SCSCR_CKE1; + dev[0] = &scif_device; + early_platform_add_devices(dev, 1); + } else { + dev[0] = &sci_device; + early_platform_add_devices(dev, 1); + dev[0] = &scif_device; + early_platform_add_devices(dev, 1); + } + + early_platform_add_devices(sh7750_early_devices, + ARRAY_SIZE(sh7750_early_devices)); +} + +enum { + UNUSED = 0, + + /* interrupt sources */ + IRL0, IRL1, IRL2, IRL3, /* only IRLM mode supported */ + HUDI, GPIOI, DMAC, + PCIC0_PCISERR, PCIC1_PCIERR, PCIC1_PCIPWDWN, PCIC1_PCIPWON, + PCIC1_PCIDMA0, PCIC1_PCIDMA1, PCIC1_PCIDMA2, PCIC1_PCIDMA3, + TMU3, TMU4, TMU0, TMU1, TMU2, RTC, SCI1, SCIF, WDT, REF, + + /* interrupt groups */ + PCIC1, +}; + +static struct intc_vect vectors[] __initdata = { + INTC_VECT(HUDI, 0x600), INTC_VECT(GPIOI, 0x620), + INTC_VECT(TMU0, 0x400), INTC_VECT(TMU1, 0x420), + INTC_VECT(TMU2, 0x440), INTC_VECT(TMU2, 0x460), + INTC_VECT(RTC, 0x480), INTC_VECT(RTC, 0x4a0), + INTC_VECT(RTC, 0x4c0), + INTC_VECT(SCI1, 0x4e0), INTC_VECT(SCI1, 0x500), + INTC_VECT(SCI1, 0x520), INTC_VECT(SCI1, 0x540), + INTC_VECT(SCIF, 0x700), INTC_VECT(SCIF, 0x720), + INTC_VECT(SCIF, 0x740), INTC_VECT(SCIF, 0x760), + INTC_VECT(WDT, 0x560), + INTC_VECT(REF, 0x580), INTC_VECT(REF, 0x5a0), +}; + +static struct intc_prio_reg prio_registers[] __initdata = { + { 0xffd00004, 0, 16, 4, /* IPRA */ { TMU0, TMU1, TMU2, RTC } }, + { 0xffd00008, 0, 16, 4, /* IPRB */ { WDT, REF, SCI1, 0 } }, + { 0xffd0000c, 0, 16, 4, /* IPRC */ { GPIOI, DMAC, SCIF, HUDI } }, + { 0xffd00010, 0, 16, 4, /* IPRD */ { IRL0, IRL1, IRL2, IRL3 } }, + { 0xfe080000, 0, 32, 4, /* INTPRI00 */ { 0, 0, 0, 0, + TMU4, TMU3, + PCIC1, PCIC0_PCISERR } }, +}; + +static DECLARE_INTC_DESC(intc_desc, "sh7750", vectors, NULL, + NULL, prio_registers, NULL); + +/* SH7750, SH7750S, SH7751 and SH7091 all have 4-channel DMA controllers */ +#if defined(CONFIG_CPU_SUBTYPE_SH7750) || \ + defined(CONFIG_CPU_SUBTYPE_SH7750S) || \ + defined(CONFIG_CPU_SUBTYPE_SH7751) || \ + defined(CONFIG_CPU_SUBTYPE_SH7091) +static struct intc_vect vectors_dma4[] __initdata = { + INTC_VECT(DMAC, 0x640), INTC_VECT(DMAC, 0x660), + INTC_VECT(DMAC, 0x680), INTC_VECT(DMAC, 0x6a0), + INTC_VECT(DMAC, 0x6c0), +}; + +static DECLARE_INTC_DESC(intc_desc_dma4, "sh7750_dma4", + vectors_dma4, NULL, + NULL, prio_registers, NULL); +#endif + +/* SH7750R and SH7751R both have 8-channel DMA controllers */ +#if defined(CONFIG_CPU_SUBTYPE_SH7750R) || defined(CONFIG_CPU_SUBTYPE_SH7751R) +static struct intc_vect vectors_dma8[] __initdata = { + INTC_VECT(DMAC, 0x640), INTC_VECT(DMAC, 0x660), + INTC_VECT(DMAC, 0x680), INTC_VECT(DMAC, 0x6a0), + INTC_VECT(DMAC, 0x780), INTC_VECT(DMAC, 0x7a0), + INTC_VECT(DMAC, 0x7c0), INTC_VECT(DMAC, 0x7e0), + INTC_VECT(DMAC, 0x6c0), +}; + +static DECLARE_INTC_DESC(intc_desc_dma8, "sh7750_dma8", + vectors_dma8, NULL, + NULL, prio_registers, NULL); +#endif + +/* SH7750R, SH7751 and SH7751R all have two extra timer channels */ +#if defined(CONFIG_CPU_SUBTYPE_SH7750R) || \ + defined(CONFIG_CPU_SUBTYPE_SH7751) || \ + defined(CONFIG_CPU_SUBTYPE_SH7751R) +static struct intc_vect vectors_tmu34[] __initdata = { + INTC_VECT(TMU3, 0xb00), INTC_VECT(TMU4, 0xb80), +}; + +static struct intc_mask_reg mask_registers[] __initdata = { + { 0xfe080040, 0xfe080060, 32, /* INTMSK00 / INTMSKCLR00 */ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, TMU4, TMU3, + PCIC1_PCIERR, PCIC1_PCIPWDWN, PCIC1_PCIPWON, + PCIC1_PCIDMA0, PCIC1_PCIDMA1, PCIC1_PCIDMA2, + PCIC1_PCIDMA3, PCIC0_PCISERR } }, +}; + +static DECLARE_INTC_DESC(intc_desc_tmu34, "sh7750_tmu34", + vectors_tmu34, NULL, + mask_registers, prio_registers, NULL); +#endif + +/* SH7750S, SH7750R, SH7751 and SH7751R all have IRLM priority registers */ +static struct intc_vect vectors_irlm[] __initdata = { + INTC_VECT(IRL0, 0x240), INTC_VECT(IRL1, 0x2a0), + INTC_VECT(IRL2, 0x300), INTC_VECT(IRL3, 0x360), +}; + +static DECLARE_INTC_DESC(intc_desc_irlm, "sh7750_irlm", vectors_irlm, NULL, + NULL, prio_registers, NULL); + +/* SH7751 and SH7751R both have PCI */ +#if defined(CONFIG_CPU_SUBTYPE_SH7751) || defined(CONFIG_CPU_SUBTYPE_SH7751R) +static struct intc_vect vectors_pci[] __initdata = { + INTC_VECT(PCIC0_PCISERR, 0xa00), INTC_VECT(PCIC1_PCIERR, 0xae0), + INTC_VECT(PCIC1_PCIPWDWN, 0xac0), INTC_VECT(PCIC1_PCIPWON, 0xaa0), + INTC_VECT(PCIC1_PCIDMA0, 0xa80), INTC_VECT(PCIC1_PCIDMA1, 0xa60), + INTC_VECT(PCIC1_PCIDMA2, 0xa40), INTC_VECT(PCIC1_PCIDMA3, 0xa20), +}; + +static struct intc_group groups_pci[] __initdata = { + INTC_GROUP(PCIC1, PCIC1_PCIERR, PCIC1_PCIPWDWN, PCIC1_PCIPWON, + PCIC1_PCIDMA0, PCIC1_PCIDMA1, PCIC1_PCIDMA2, PCIC1_PCIDMA3), +}; + +static DECLARE_INTC_DESC(intc_desc_pci, "sh7750_pci", vectors_pci, groups_pci, + mask_registers, prio_registers, NULL); +#endif + +#if defined(CONFIG_CPU_SUBTYPE_SH7750) || \ + defined(CONFIG_CPU_SUBTYPE_SH7750S) || \ + defined(CONFIG_CPU_SUBTYPE_SH7091) +void __init plat_irq_setup(void) +{ + /* + * same vectors for SH7750, SH7750S and SH7091 except for IRLM, + * see below.. + */ + register_intc_controller(&intc_desc); + register_intc_controller(&intc_desc_dma4); +} +#endif + +#if defined(CONFIG_CPU_SUBTYPE_SH7750R) +void __init plat_irq_setup(void) +{ + register_intc_controller(&intc_desc); + register_intc_controller(&intc_desc_dma8); + register_intc_controller(&intc_desc_tmu34); +} +#endif + +#if defined(CONFIG_CPU_SUBTYPE_SH7751) +void __init plat_irq_setup(void) +{ + register_intc_controller(&intc_desc); + register_intc_controller(&intc_desc_dma4); + register_intc_controller(&intc_desc_tmu34); + register_intc_controller(&intc_desc_pci); +} +#endif + +#if defined(CONFIG_CPU_SUBTYPE_SH7751R) +void __init plat_irq_setup(void) +{ + register_intc_controller(&intc_desc); + register_intc_controller(&intc_desc_dma8); + register_intc_controller(&intc_desc_tmu34); + register_intc_controller(&intc_desc_pci); +} +#endif + +#define INTC_ICR 0xffd00000UL +#define INTC_ICR_IRLM (1<<7) + +void __init plat_irq_setup_pins(int mode) +{ +#if defined(CONFIG_CPU_SUBTYPE_SH7750) || defined(CONFIG_CPU_SUBTYPE_SH7091) + BUG(); /* impossible to mask interrupts on SH7750 and SH7091 */ + return; +#endif + + switch (mode) { + case IRQ_MODE_IRQ: /* individual interrupt mode for IRL3-0 */ + __raw_writew(__raw_readw(INTC_ICR) | INTC_ICR_IRLM, INTC_ICR); + register_intc_controller(&intc_desc_irlm); + break; + default: + BUG(); + } +} diff --git a/arch/sh/kernel/cpu/sh4/setup-sh7760.c b/arch/sh/kernel/cpu/sh4/setup-sh7760.c new file mode 100644 index 000000000..e51fe1734 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/setup-sh7760.c @@ -0,0 +1,299 @@ +/* + * SH7760 Setup + * + * Copyright (C) 2006 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/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <linux/sh_timer.h> +#include <linux/sh_intc.h> +#include <linux/serial_sci.h> +#include <linux/io.h> + +enum { + UNUSED = 0, + + /* interrupt sources */ + IRL0, IRL1, IRL2, IRL3, + HUDI, GPIOI, DMAC, + IRQ4, IRQ5, IRQ6, IRQ7, + HCAN20, HCAN21, + SSI0, SSI1, + HAC0, HAC1, + I2C0, I2C1, + USB, LCDC, + DMABRG0, DMABRG1, DMABRG2, + SCIF0_ERI, SCIF0_RXI, SCIF0_BRI, SCIF0_TXI, + SCIF1_ERI, SCIF1_RXI, SCIF1_BRI, SCIF1_TXI, + SCIF2_ERI, SCIF2_RXI, SCIF2_BRI, SCIF2_TXI, + SIM_ERI, SIM_RXI, SIM_TXI, SIM_TEI, + HSPI, + MMCIF0, MMCIF1, MMCIF2, MMCIF3, + MFI, ADC, CMT, + TMU0, TMU1, TMU2, + WDT, REF, + + /* interrupt groups */ + DMABRG, SCIF0, SCIF1, SCIF2, SIM, MMCIF, +}; + +static struct intc_vect vectors[] __initdata = { + INTC_VECT(HUDI, 0x600), INTC_VECT(GPIOI, 0x620), + INTC_VECT(DMAC, 0x640), INTC_VECT(DMAC, 0x660), + INTC_VECT(DMAC, 0x680), INTC_VECT(DMAC, 0x6a0), + INTC_VECT(DMAC, 0x780), INTC_VECT(DMAC, 0x7a0), + INTC_VECT(DMAC, 0x7c0), INTC_VECT(DMAC, 0x7e0), + INTC_VECT(DMAC, 0x6c0), + INTC_VECT(IRQ4, 0x800), INTC_VECT(IRQ5, 0x820), + INTC_VECT(IRQ6, 0x840), INTC_VECT(IRQ6, 0x860), + INTC_VECT(HCAN20, 0x900), INTC_VECT(HCAN21, 0x920), + INTC_VECT(SSI0, 0x940), INTC_VECT(SSI1, 0x960), + INTC_VECT(HAC0, 0x980), INTC_VECT(HAC1, 0x9a0), + INTC_VECT(I2C0, 0x9c0), INTC_VECT(I2C1, 0x9e0), + INTC_VECT(USB, 0xa00), INTC_VECT(LCDC, 0xa20), + INTC_VECT(DMABRG0, 0xa80), INTC_VECT(DMABRG1, 0xaa0), + INTC_VECT(DMABRG2, 0xac0), + INTC_VECT(SCIF0_ERI, 0x880), INTC_VECT(SCIF0_RXI, 0x8a0), + INTC_VECT(SCIF0_BRI, 0x8c0), INTC_VECT(SCIF0_TXI, 0x8e0), + INTC_VECT(SCIF1_ERI, 0xb00), INTC_VECT(SCIF1_RXI, 0xb20), + INTC_VECT(SCIF1_BRI, 0xb40), INTC_VECT(SCIF1_TXI, 0xb60), + INTC_VECT(SCIF2_ERI, 0xb80), INTC_VECT(SCIF2_RXI, 0xba0), + INTC_VECT(SCIF2_BRI, 0xbc0), INTC_VECT(SCIF2_TXI, 0xbe0), + INTC_VECT(SIM_ERI, 0xc00), INTC_VECT(SIM_RXI, 0xc20), + INTC_VECT(SIM_TXI, 0xc40), INTC_VECT(SIM_TEI, 0xc60), + INTC_VECT(HSPI, 0xc80), + INTC_VECT(MMCIF0, 0xd00), INTC_VECT(MMCIF1, 0xd20), + INTC_VECT(MMCIF2, 0xd40), INTC_VECT(MMCIF3, 0xd60), + INTC_VECT(MFI, 0xe80), /* 0xf80 according to data sheet */ + INTC_VECT(ADC, 0xf80), INTC_VECT(CMT, 0xfa0), + INTC_VECT(TMU0, 0x400), INTC_VECT(TMU1, 0x420), + INTC_VECT(TMU2, 0x440), INTC_VECT(TMU2, 0x460), + INTC_VECT(WDT, 0x560), + INTC_VECT(REF, 0x580), INTC_VECT(REF, 0x5a0), +}; + +static struct intc_group groups[] __initdata = { + INTC_GROUP(DMABRG, DMABRG0, DMABRG1, DMABRG2), + INTC_GROUP(SCIF0, SCIF0_ERI, SCIF0_RXI, SCIF0_BRI, SCIF0_TXI), + INTC_GROUP(SCIF1, SCIF1_ERI, SCIF1_RXI, SCIF1_BRI, SCIF1_TXI), + INTC_GROUP(SCIF2, SCIF2_ERI, SCIF2_RXI, SCIF2_BRI, SCIF2_TXI), + INTC_GROUP(SIM, SIM_ERI, SIM_RXI, SIM_TXI, SIM_TEI), + INTC_GROUP(MMCIF, MMCIF0, MMCIF1, MMCIF2, MMCIF3), +}; + +static struct intc_mask_reg mask_registers[] __initdata = { + { 0xfe080040, 0xfe080060, 32, /* INTMSK00 / INTMSKCLR00 */ + { IRQ4, IRQ5, IRQ6, IRQ7, 0, 0, HCAN20, HCAN21, + SSI0, SSI1, HAC0, HAC1, I2C0, I2C1, USB, LCDC, + 0, DMABRG0, DMABRG1, DMABRG2, + SCIF0_ERI, SCIF0_RXI, SCIF0_BRI, SCIF0_TXI, + SCIF1_ERI, SCIF1_RXI, SCIF1_BRI, SCIF1_TXI, + SCIF2_ERI, SCIF2_RXI, SCIF2_BRI, SCIF2_TXI, } }, + { 0xfe080044, 0xfe080064, 32, /* INTMSK04 / INTMSKCLR04 */ + { 0, 0, 0, 0, 0, 0, 0, 0, + SIM_ERI, SIM_RXI, SIM_TXI, SIM_TEI, + HSPI, MMCIF0, MMCIF1, MMCIF2, + MMCIF3, 0, 0, 0, 0, 0, 0, 0, + 0, MFI, 0, 0, 0, 0, ADC, CMT, } }, +}; + +static struct intc_prio_reg prio_registers[] __initdata = { + { 0xffd00004, 0, 16, 4, /* IPRA */ { TMU0, TMU1, TMU2 } }, + { 0xffd00008, 0, 16, 4, /* IPRB */ { WDT, REF, 0, 0 } }, + { 0xffd0000c, 0, 16, 4, /* IPRC */ { GPIOI, DMAC, 0, HUDI } }, + { 0xffd00010, 0, 16, 4, /* IPRD */ { IRL0, IRL1, IRL2, IRL3 } }, + { 0xfe080000, 0, 32, 4, /* INTPRI00 */ { IRQ4, IRQ5, IRQ6, IRQ7 } }, + { 0xfe080004, 0, 32, 4, /* INTPRI04 */ { HCAN20, HCAN21, SSI0, SSI1, + HAC0, HAC1, I2C0, I2C1 } }, + { 0xfe080008, 0, 32, 4, /* INTPRI08 */ { USB, LCDC, DMABRG, SCIF0, + SCIF1, SCIF2, SIM, HSPI } }, + { 0xfe08000c, 0, 32, 4, /* INTPRI0C */ { 0, 0, MMCIF, 0, + MFI, 0, ADC, CMT } }, +}; + +static DECLARE_INTC_DESC(intc_desc, "sh7760", vectors, groups, + mask_registers, prio_registers, NULL); + +static struct intc_vect vectors_irq[] __initdata = { + INTC_VECT(IRL0, 0x240), INTC_VECT(IRL1, 0x2a0), + INTC_VECT(IRL2, 0x300), INTC_VECT(IRL3, 0x360), +}; + +static DECLARE_INTC_DESC(intc_desc_irq, "sh7760-irq", vectors_irq, groups, + mask_registers, prio_registers, NULL); + +static struct plat_sci_port scif0_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif0_resources[] = { + DEFINE_RES_MEM(0xfe600000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x880)), + DEFINE_RES_IRQ(evt2irq(0x8a0)), + DEFINE_RES_IRQ(evt2irq(0x8e0)), + DEFINE_RES_IRQ(evt2irq(0x8c0)), +}; + +static struct platform_device scif0_device = { + .name = "sh-sci", + .id = 0, + .resource = scif0_resources, + .num_resources = ARRAY_SIZE(scif0_resources), + .dev = { + .platform_data = &scif0_platform_data, + }, +}; + +static struct plat_sci_port scif1_platform_data = { + .type = PORT_SCIF, + .scscr = SCSCR_REIE, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif1_resources[] = { + DEFINE_RES_MEM(0xfe610000, 0x100), + DEFINE_RES_IRQ(evt2irq(0xb00)), + DEFINE_RES_IRQ(evt2irq(0xb20)), + DEFINE_RES_IRQ(evt2irq(0xb60)), + DEFINE_RES_IRQ(evt2irq(0xb40)), +}; + +static struct platform_device scif1_device = { + .name = "sh-sci", + .id = 1, + .resource = scif1_resources, + .num_resources = ARRAY_SIZE(scif1_resources), + .dev = { + .platform_data = &scif1_platform_data, + }, +}; + +static struct plat_sci_port scif2_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif2_resources[] = { + DEFINE_RES_MEM(0xfe620000, 0x100), + DEFINE_RES_IRQ(evt2irq(0xb80)), + DEFINE_RES_IRQ(evt2irq(0xba0)), + DEFINE_RES_IRQ(evt2irq(0xbe0)), + DEFINE_RES_IRQ(evt2irq(0xbc0)), +}; + +static struct platform_device scif2_device = { + .name = "sh-sci", + .id = 2, + .resource = scif2_resources, + .num_resources = ARRAY_SIZE(scif2_resources), + .dev = { + .platform_data = &scif2_platform_data, + }, +}; + +static struct plat_sci_port scif3_platform_data = { + /* + * This is actually a SIM card module serial port, based on an SCI with + * additional registers. The sh-sci driver doesn't support the SIM port + * type, declare it as a SCI. Don't declare the additional registers in + * the memory resource or the driver will compute an incorrect regshift + * value. + */ + .type = PORT_SCI, +}; + +static struct resource scif3_resources[] = { + DEFINE_RES_MEM(0xfe480000, 0x10), + DEFINE_RES_IRQ(evt2irq(0xc00)), + DEFINE_RES_IRQ(evt2irq(0xc20)), + DEFINE_RES_IRQ(evt2irq(0xc40)), +}; + +static struct platform_device scif3_device = { + .name = "sh-sci", + .id = 3, + .resource = scif3_resources, + .num_resources = ARRAY_SIZE(scif3_resources), + .dev = { + .platform_data = &scif3_platform_data, + }, +}; + +static struct sh_timer_config tmu0_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu0_resources[] = { + DEFINE_RES_MEM(0xffd80000, 0x30), + DEFINE_RES_IRQ(evt2irq(0x400)), + DEFINE_RES_IRQ(evt2irq(0x420)), + DEFINE_RES_IRQ(evt2irq(0x440)), +}; + +static struct platform_device tmu0_device = { + .name = "sh-tmu", + .id = 0, + .dev = { + .platform_data = &tmu0_platform_data, + }, + .resource = tmu0_resources, + .num_resources = ARRAY_SIZE(tmu0_resources), +}; + + +static struct platform_device *sh7760_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + &scif3_device, + &tmu0_device, +}; + +static int __init sh7760_devices_setup(void) +{ + return platform_add_devices(sh7760_devices, + ARRAY_SIZE(sh7760_devices)); +} +arch_initcall(sh7760_devices_setup); + +static struct platform_device *sh7760_early_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + &scif3_device, + &tmu0_device, +}; + +void __init plat_early_device_setup(void) +{ + early_platform_add_devices(sh7760_early_devices, + ARRAY_SIZE(sh7760_early_devices)); +} + +#define INTC_ICR 0xffd00000UL +#define INTC_ICR_IRLM (1 << 7) + +void __init plat_irq_setup_pins(int mode) +{ + switch (mode) { + case IRQ_MODE_IRQ: + __raw_writew(__raw_readw(INTC_ICR) | INTC_ICR_IRLM, INTC_ICR); + register_intc_controller(&intc_desc_irq); + break; + default: + BUG(); + } +} + +void __init plat_irq_setup(void) +{ + register_intc_controller(&intc_desc); +} diff --git a/arch/sh/kernel/cpu/sh4/softfloat.c b/arch/sh/kernel/cpu/sh4/softfloat.c new file mode 100644 index 000000000..42edf2e54 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/softfloat.c @@ -0,0 +1,930 @@ +/* + * Floating point emulation support for subnormalised numbers on SH4 + * architecture This file is derived from the SoftFloat IEC/IEEE + * Floating-point Arithmetic Package, Release 2 the original license of + * which is reproduced below. + * + * ======================================================================== + * + * This C source file is part of the SoftFloat IEC/IEEE Floating-point + * Arithmetic Package, Release 2. + * + * Written by John R. Hauser. This work was made possible in part by the + * International Computer Science Institute, located at Suite 600, 1947 Center + * Street, Berkeley, California 94704. Funding was partially provided by the + * National Science Foundation under grant MIP-9311980. The original version + * of this code was written as part of a project to build a fixed-point vector + * processor in collaboration with the University of California at Berkeley, + * overseen by Profs. Nelson Morgan and John Wawrzynek. More information + * is available through the web page `http://HTTP.CS.Berkeley.EDU/~jhauser/ + * arithmetic/softfloat.html'. + * + * THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort + * has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT + * TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO + * PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY + * AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE. + * + * Derivative works are acceptable, even for commercial purposes, so long as + * (1) they include prominent notice that the work is derivative, and (2) they + * include prominent notice akin to these three paragraphs for those parts of + * this code that are retained. + * + * ======================================================================== + * + * SH4 modifications by Ismail Dhaoui <ismail.dhaoui@st.com> + * and Kamel Khelifi <kamel.khelifi@st.com> + */ +#include <linux/kernel.h> +#include <cpu/fpu.h> +#include <asm/div64.h> + +#define LIT64( a ) a##LL + +typedef char flag; +typedef unsigned char uint8; +typedef signed char int8; +typedef int uint16; +typedef int int16; +typedef unsigned int uint32; +typedef signed int int32; + +typedef unsigned long long int bits64; +typedef signed long long int sbits64; + +typedef unsigned char bits8; +typedef signed char sbits8; +typedef unsigned short int bits16; +typedef signed short int sbits16; +typedef unsigned int bits32; +typedef signed int sbits32; + +typedef unsigned long long int uint64; +typedef signed long long int int64; + +typedef unsigned long int float32; +typedef unsigned long long float64; + +extern void float_raise(unsigned int flags); /* in fpu.c */ +extern int float_rounding_mode(void); /* in fpu.c */ + +bits64 extractFloat64Frac(float64 a); +flag extractFloat64Sign(float64 a); +int16 extractFloat64Exp(float64 a); +int16 extractFloat32Exp(float32 a); +flag extractFloat32Sign(float32 a); +bits32 extractFloat32Frac(float32 a); +float64 packFloat64(flag zSign, int16 zExp, bits64 zSig); +void shift64RightJamming(bits64 a, int16 count, bits64 * zPtr); +float32 packFloat32(flag zSign, int16 zExp, bits32 zSig); +void shift32RightJamming(bits32 a, int16 count, bits32 * zPtr); +float64 float64_sub(float64 a, float64 b); +float32 float32_sub(float32 a, float32 b); +float32 float32_add(float32 a, float32 b); +float64 float64_add(float64 a, float64 b); +float64 float64_div(float64 a, float64 b); +float32 float32_div(float32 a, float32 b); +float32 float32_mul(float32 a, float32 b); +float64 float64_mul(float64 a, float64 b); +float32 float64_to_float32(float64 a); +void add128(bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 * z0Ptr, + bits64 * z1Ptr); +void sub128(bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 * z0Ptr, + bits64 * z1Ptr); +void mul64To128(bits64 a, bits64 b, bits64 * z0Ptr, bits64 * z1Ptr); + +static int8 countLeadingZeros32(bits32 a); +static int8 countLeadingZeros64(bits64 a); +static float64 normalizeRoundAndPackFloat64(flag zSign, int16 zExp, + bits64 zSig); +static float64 subFloat64Sigs(float64 a, float64 b, flag zSign); +static float64 addFloat64Sigs(float64 a, float64 b, flag zSign); +static float32 roundAndPackFloat32(flag zSign, int16 zExp, bits32 zSig); +static float32 normalizeRoundAndPackFloat32(flag zSign, int16 zExp, + bits32 zSig); +static float64 roundAndPackFloat64(flag zSign, int16 zExp, bits64 zSig); +static float32 subFloat32Sigs(float32 a, float32 b, flag zSign); +static float32 addFloat32Sigs(float32 a, float32 b, flag zSign); +static void normalizeFloat64Subnormal(bits64 aSig, int16 * zExpPtr, + bits64 * zSigPtr); +static bits64 estimateDiv128To64(bits64 a0, bits64 a1, bits64 b); +static void normalizeFloat32Subnormal(bits32 aSig, int16 * zExpPtr, + bits32 * zSigPtr); + +bits64 extractFloat64Frac(float64 a) +{ + return a & LIT64(0x000FFFFFFFFFFFFF); +} + +flag extractFloat64Sign(float64 a) +{ + return a >> 63; +} + +int16 extractFloat64Exp(float64 a) +{ + return (a >> 52) & 0x7FF; +} + +int16 extractFloat32Exp(float32 a) +{ + return (a >> 23) & 0xFF; +} + +flag extractFloat32Sign(float32 a) +{ + return a >> 31; +} + +bits32 extractFloat32Frac(float32 a) +{ + return a & 0x007FFFFF; +} + +float64 packFloat64(flag zSign, int16 zExp, bits64 zSig) +{ + return (((bits64) zSign) << 63) + (((bits64) zExp) << 52) + zSig; +} + +void shift64RightJamming(bits64 a, int16 count, bits64 * zPtr) +{ + bits64 z; + + if (count == 0) { + z = a; + } else if (count < 64) { + z = (a >> count) | ((a << ((-count) & 63)) != 0); + } else { + z = (a != 0); + } + *zPtr = z; +} + +static int8 countLeadingZeros32(bits32 a) +{ + static const int8 countLeadingZerosHigh[] = { + 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + int8 shiftCount; + + shiftCount = 0; + if (a < 0x10000) { + shiftCount += 16; + a <<= 16; + } + if (a < 0x1000000) { + shiftCount += 8; + a <<= 8; + } + shiftCount += countLeadingZerosHigh[a >> 24]; + return shiftCount; + +} + +static int8 countLeadingZeros64(bits64 a) +{ + int8 shiftCount; + + shiftCount = 0; + if (a < ((bits64) 1) << 32) { + shiftCount += 32; + } else { + a >>= 32; + } + shiftCount += countLeadingZeros32(a); + return shiftCount; + +} + +static float64 normalizeRoundAndPackFloat64(flag zSign, int16 zExp, bits64 zSig) +{ + int8 shiftCount; + + shiftCount = countLeadingZeros64(zSig) - 1; + return roundAndPackFloat64(zSign, zExp - shiftCount, + zSig << shiftCount); + +} + +static float64 subFloat64Sigs(float64 a, float64 b, flag zSign) +{ + int16 aExp, bExp, zExp; + bits64 aSig, bSig, zSig; + int16 expDiff; + + aSig = extractFloat64Frac(a); + aExp = extractFloat64Exp(a); + bSig = extractFloat64Frac(b); + bExp = extractFloat64Exp(b); + expDiff = aExp - bExp; + aSig <<= 10; + bSig <<= 10; + if (0 < expDiff) + goto aExpBigger; + if (expDiff < 0) + goto bExpBigger; + if (aExp == 0) { + aExp = 1; + bExp = 1; + } + if (bSig < aSig) + goto aBigger; + if (aSig < bSig) + goto bBigger; + return packFloat64(float_rounding_mode() == FPSCR_RM_ZERO, 0, 0); + bExpBigger: + if (bExp == 0x7FF) { + return packFloat64(zSign ^ 1, 0x7FF, 0); + } + if (aExp == 0) { + ++expDiff; + } else { + aSig |= LIT64(0x4000000000000000); + } + shift64RightJamming(aSig, -expDiff, &aSig); + bSig |= LIT64(0x4000000000000000); + bBigger: + zSig = bSig - aSig; + zExp = bExp; + zSign ^= 1; + goto normalizeRoundAndPack; + aExpBigger: + if (aExp == 0x7FF) { + return a; + } + if (bExp == 0) { + --expDiff; + } else { + bSig |= LIT64(0x4000000000000000); + } + shift64RightJamming(bSig, expDiff, &bSig); + aSig |= LIT64(0x4000000000000000); + aBigger: + zSig = aSig - bSig; + zExp = aExp; + normalizeRoundAndPack: + --zExp; + return normalizeRoundAndPackFloat64(zSign, zExp, zSig); + +} +static float64 addFloat64Sigs(float64 a, float64 b, flag zSign) +{ + int16 aExp, bExp, zExp; + bits64 aSig, bSig, zSig; + int16 expDiff; + + aSig = extractFloat64Frac(a); + aExp = extractFloat64Exp(a); + bSig = extractFloat64Frac(b); + bExp = extractFloat64Exp(b); + expDiff = aExp - bExp; + aSig <<= 9; + bSig <<= 9; + if (0 < expDiff) { + if (aExp == 0x7FF) { + return a; + } + if (bExp == 0) { + --expDiff; + } else { + bSig |= LIT64(0x2000000000000000); + } + shift64RightJamming(bSig, expDiff, &bSig); + zExp = aExp; + } else if (expDiff < 0) { + if (bExp == 0x7FF) { + return packFloat64(zSign, 0x7FF, 0); + } + if (aExp == 0) { + ++expDiff; + } else { + aSig |= LIT64(0x2000000000000000); + } + shift64RightJamming(aSig, -expDiff, &aSig); + zExp = bExp; + } else { + if (aExp == 0x7FF) { + return a; + } + if (aExp == 0) + return packFloat64(zSign, 0, (aSig + bSig) >> 9); + zSig = LIT64(0x4000000000000000) + aSig + bSig; + zExp = aExp; + goto roundAndPack; + } + aSig |= LIT64(0x2000000000000000); + zSig = (aSig + bSig) << 1; + --zExp; + if ((sbits64) zSig < 0) { + zSig = aSig + bSig; + ++zExp; + } + roundAndPack: + return roundAndPackFloat64(zSign, zExp, zSig); + +} + +float32 packFloat32(flag zSign, int16 zExp, bits32 zSig) +{ + return (((bits32) zSign) << 31) + (((bits32) zExp) << 23) + zSig; +} + +void shift32RightJamming(bits32 a, int16 count, bits32 * zPtr) +{ + bits32 z; + if (count == 0) { + z = a; + } else if (count < 32) { + z = (a >> count) | ((a << ((-count) & 31)) != 0); + } else { + z = (a != 0); + } + *zPtr = z; +} + +static float32 roundAndPackFloat32(flag zSign, int16 zExp, bits32 zSig) +{ + flag roundNearestEven; + int8 roundIncrement, roundBits; + flag isTiny; + + /* SH4 has only 2 rounding modes - round to nearest and round to zero */ + roundNearestEven = (float_rounding_mode() == FPSCR_RM_NEAREST); + roundIncrement = 0x40; + if (!roundNearestEven) { + roundIncrement = 0; + } + roundBits = zSig & 0x7F; + if (0xFD <= (bits16) zExp) { + if ((0xFD < zExp) + || ((zExp == 0xFD) + && ((sbits32) (zSig + roundIncrement) < 0)) + ) { + float_raise(FPSCR_CAUSE_OVERFLOW | FPSCR_CAUSE_INEXACT); + return packFloat32(zSign, 0xFF, + 0) - (roundIncrement == 0); + } + if (zExp < 0) { + isTiny = (zExp < -1) + || (zSig + roundIncrement < 0x80000000); + shift32RightJamming(zSig, -zExp, &zSig); + zExp = 0; + roundBits = zSig & 0x7F; + if (isTiny && roundBits) + float_raise(FPSCR_CAUSE_UNDERFLOW); + } + } + if (roundBits) + float_raise(FPSCR_CAUSE_INEXACT); + zSig = (zSig + roundIncrement) >> 7; + zSig &= ~(((roundBits ^ 0x40) == 0) & roundNearestEven); + if (zSig == 0) + zExp = 0; + return packFloat32(zSign, zExp, zSig); + +} + +static float32 normalizeRoundAndPackFloat32(flag zSign, int16 zExp, bits32 zSig) +{ + int8 shiftCount; + + shiftCount = countLeadingZeros32(zSig) - 1; + return roundAndPackFloat32(zSign, zExp - shiftCount, + zSig << shiftCount); +} + +static float64 roundAndPackFloat64(flag zSign, int16 zExp, bits64 zSig) +{ + flag roundNearestEven; + int16 roundIncrement, roundBits; + flag isTiny; + + /* SH4 has only 2 rounding modes - round to nearest and round to zero */ + roundNearestEven = (float_rounding_mode() == FPSCR_RM_NEAREST); + roundIncrement = 0x200; + if (!roundNearestEven) { + roundIncrement = 0; + } + roundBits = zSig & 0x3FF; + if (0x7FD <= (bits16) zExp) { + if ((0x7FD < zExp) + || ((zExp == 0x7FD) + && ((sbits64) (zSig + roundIncrement) < 0)) + ) { + float_raise(FPSCR_CAUSE_OVERFLOW | FPSCR_CAUSE_INEXACT); + return packFloat64(zSign, 0x7FF, + 0) - (roundIncrement == 0); + } + if (zExp < 0) { + isTiny = (zExp < -1) + || (zSig + roundIncrement < + LIT64(0x8000000000000000)); + shift64RightJamming(zSig, -zExp, &zSig); + zExp = 0; + roundBits = zSig & 0x3FF; + if (isTiny && roundBits) + float_raise(FPSCR_CAUSE_UNDERFLOW); + } + } + if (roundBits) + float_raise(FPSCR_CAUSE_INEXACT); + zSig = (zSig + roundIncrement) >> 10; + zSig &= ~(((roundBits ^ 0x200) == 0) & roundNearestEven); + if (zSig == 0) + zExp = 0; + return packFloat64(zSign, zExp, zSig); + +} + +static float32 subFloat32Sigs(float32 a, float32 b, flag zSign) +{ + int16 aExp, bExp, zExp; + bits32 aSig, bSig, zSig; + int16 expDiff; + + aSig = extractFloat32Frac(a); + aExp = extractFloat32Exp(a); + bSig = extractFloat32Frac(b); + bExp = extractFloat32Exp(b); + expDiff = aExp - bExp; + aSig <<= 7; + bSig <<= 7; + if (0 < expDiff) + goto aExpBigger; + if (expDiff < 0) + goto bExpBigger; + if (aExp == 0) { + aExp = 1; + bExp = 1; + } + if (bSig < aSig) + goto aBigger; + if (aSig < bSig) + goto bBigger; + return packFloat32(float_rounding_mode() == FPSCR_RM_ZERO, 0, 0); + bExpBigger: + if (bExp == 0xFF) { + return packFloat32(zSign ^ 1, 0xFF, 0); + } + if (aExp == 0) { + ++expDiff; + } else { + aSig |= 0x40000000; + } + shift32RightJamming(aSig, -expDiff, &aSig); + bSig |= 0x40000000; + bBigger: + zSig = bSig - aSig; + zExp = bExp; + zSign ^= 1; + goto normalizeRoundAndPack; + aExpBigger: + if (aExp == 0xFF) { + return a; + } + if (bExp == 0) { + --expDiff; + } else { + bSig |= 0x40000000; + } + shift32RightJamming(bSig, expDiff, &bSig); + aSig |= 0x40000000; + aBigger: + zSig = aSig - bSig; + zExp = aExp; + normalizeRoundAndPack: + --zExp; + return normalizeRoundAndPackFloat32(zSign, zExp, zSig); + +} + +static float32 addFloat32Sigs(float32 a, float32 b, flag zSign) +{ + int16 aExp, bExp, zExp; + bits32 aSig, bSig, zSig; + int16 expDiff; + + aSig = extractFloat32Frac(a); + aExp = extractFloat32Exp(a); + bSig = extractFloat32Frac(b); + bExp = extractFloat32Exp(b); + expDiff = aExp - bExp; + aSig <<= 6; + bSig <<= 6; + if (0 < expDiff) { + if (aExp == 0xFF) { + return a; + } + if (bExp == 0) { + --expDiff; + } else { + bSig |= 0x20000000; + } + shift32RightJamming(bSig, expDiff, &bSig); + zExp = aExp; + } else if (expDiff < 0) { + if (bExp == 0xFF) { + return packFloat32(zSign, 0xFF, 0); + } + if (aExp == 0) { + ++expDiff; + } else { + aSig |= 0x20000000; + } + shift32RightJamming(aSig, -expDiff, &aSig); + zExp = bExp; + } else { + if (aExp == 0xFF) { + return a; + } + if (aExp == 0) + return packFloat32(zSign, 0, (aSig + bSig) >> 6); + zSig = 0x40000000 + aSig + bSig; + zExp = aExp; + goto roundAndPack; + } + aSig |= 0x20000000; + zSig = (aSig + bSig) << 1; + --zExp; + if ((sbits32) zSig < 0) { + zSig = aSig + bSig; + ++zExp; + } + roundAndPack: + return roundAndPackFloat32(zSign, zExp, zSig); + +} + +float64 float64_sub(float64 a, float64 b) +{ + flag aSign, bSign; + + aSign = extractFloat64Sign(a); + bSign = extractFloat64Sign(b); + if (aSign == bSign) { + return subFloat64Sigs(a, b, aSign); + } else { + return addFloat64Sigs(a, b, aSign); + } + +} + +float32 float32_sub(float32 a, float32 b) +{ + flag aSign, bSign; + + aSign = extractFloat32Sign(a); + bSign = extractFloat32Sign(b); + if (aSign == bSign) { + return subFloat32Sigs(a, b, aSign); + } else { + return addFloat32Sigs(a, b, aSign); + } + +} + +float32 float32_add(float32 a, float32 b) +{ + flag aSign, bSign; + + aSign = extractFloat32Sign(a); + bSign = extractFloat32Sign(b); + if (aSign == bSign) { + return addFloat32Sigs(a, b, aSign); + } else { + return subFloat32Sigs(a, b, aSign); + } + +} + +float64 float64_add(float64 a, float64 b) +{ + flag aSign, bSign; + + aSign = extractFloat64Sign(a); + bSign = extractFloat64Sign(b); + if (aSign == bSign) { + return addFloat64Sigs(a, b, aSign); + } else { + return subFloat64Sigs(a, b, aSign); + } +} + +static void +normalizeFloat64Subnormal(bits64 aSig, int16 * zExpPtr, bits64 * zSigPtr) +{ + int8 shiftCount; + + shiftCount = countLeadingZeros64(aSig) - 11; + *zSigPtr = aSig << shiftCount; + *zExpPtr = 1 - shiftCount; +} + +void add128(bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 * z0Ptr, + bits64 * z1Ptr) +{ + bits64 z1; + + z1 = a1 + b1; + *z1Ptr = z1; + *z0Ptr = a0 + b0 + (z1 < a1); +} + +void +sub128(bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 * z0Ptr, + bits64 * z1Ptr) +{ + *z1Ptr = a1 - b1; + *z0Ptr = a0 - b0 - (a1 < b1); +} + +static bits64 estimateDiv128To64(bits64 a0, bits64 a1, bits64 b) +{ + bits64 b0, b1; + bits64 rem0, rem1, term0, term1; + bits64 z, tmp; + if (b <= a0) + return LIT64(0xFFFFFFFFFFFFFFFF); + b0 = b >> 32; + tmp = a0; + do_div(tmp, b0); + + z = (b0 << 32 <= a0) ? LIT64(0xFFFFFFFF00000000) : tmp << 32; + mul64To128(b, z, &term0, &term1); + sub128(a0, a1, term0, term1, &rem0, &rem1); + while (((sbits64) rem0) < 0) { + z -= LIT64(0x100000000); + b1 = b << 32; + add128(rem0, rem1, b0, b1, &rem0, &rem1); + } + rem0 = (rem0 << 32) | (rem1 >> 32); + tmp = rem0; + do_div(tmp, b0); + z |= (b0 << 32 <= rem0) ? 0xFFFFFFFF : tmp; + return z; +} + +void mul64To128(bits64 a, bits64 b, bits64 * z0Ptr, bits64 * z1Ptr) +{ + bits32 aHigh, aLow, bHigh, bLow; + bits64 z0, zMiddleA, zMiddleB, z1; + + aLow = a; + aHigh = a >> 32; + bLow = b; + bHigh = b >> 32; + z1 = ((bits64) aLow) * bLow; + zMiddleA = ((bits64) aLow) * bHigh; + zMiddleB = ((bits64) aHigh) * bLow; + z0 = ((bits64) aHigh) * bHigh; + zMiddleA += zMiddleB; + z0 += (((bits64) (zMiddleA < zMiddleB)) << 32) + (zMiddleA >> 32); + zMiddleA <<= 32; + z1 += zMiddleA; + z0 += (z1 < zMiddleA); + *z1Ptr = z1; + *z0Ptr = z0; + +} + +static void normalizeFloat32Subnormal(bits32 aSig, int16 * zExpPtr, + bits32 * zSigPtr) +{ + int8 shiftCount; + + shiftCount = countLeadingZeros32(aSig) - 8; + *zSigPtr = aSig << shiftCount; + *zExpPtr = 1 - shiftCount; + +} + +float64 float64_div(float64 a, float64 b) +{ + flag aSign, bSign, zSign; + int16 aExp, bExp, zExp; + bits64 aSig, bSig, zSig; + bits64 rem0, rem1; + bits64 term0, term1; + + aSig = extractFloat64Frac(a); + aExp = extractFloat64Exp(a); + aSign = extractFloat64Sign(a); + bSig = extractFloat64Frac(b); + bExp = extractFloat64Exp(b); + bSign = extractFloat64Sign(b); + zSign = aSign ^ bSign; + if (aExp == 0x7FF) { + if (bExp == 0x7FF) { + } + return packFloat64(zSign, 0x7FF, 0); + } + if (bExp == 0x7FF) { + return packFloat64(zSign, 0, 0); + } + if (bExp == 0) { + if (bSig == 0) { + if ((aExp | aSig) == 0) { + float_raise(FPSCR_CAUSE_INVALID); + } + return packFloat64(zSign, 0x7FF, 0); + } + normalizeFloat64Subnormal(bSig, &bExp, &bSig); + } + if (aExp == 0) { + if (aSig == 0) + return packFloat64(zSign, 0, 0); + normalizeFloat64Subnormal(aSig, &aExp, &aSig); + } + zExp = aExp - bExp + 0x3FD; + aSig = (aSig | LIT64(0x0010000000000000)) << 10; + bSig = (bSig | LIT64(0x0010000000000000)) << 11; + if (bSig <= (aSig + aSig)) { + aSig >>= 1; + ++zExp; + } + zSig = estimateDiv128To64(aSig, 0, bSig); + if ((zSig & 0x1FF) <= 2) { + mul64To128(bSig, zSig, &term0, &term1); + sub128(aSig, 0, term0, term1, &rem0, &rem1); + while ((sbits64) rem0 < 0) { + --zSig; + add128(rem0, rem1, 0, bSig, &rem0, &rem1); + } + zSig |= (rem1 != 0); + } + return roundAndPackFloat64(zSign, zExp, zSig); + +} + +float32 float32_div(float32 a, float32 b) +{ + flag aSign, bSign, zSign; + int16 aExp, bExp, zExp; + bits32 aSig, bSig; + uint64_t zSig; + + aSig = extractFloat32Frac(a); + aExp = extractFloat32Exp(a); + aSign = extractFloat32Sign(a); + bSig = extractFloat32Frac(b); + bExp = extractFloat32Exp(b); + bSign = extractFloat32Sign(b); + zSign = aSign ^ bSign; + if (aExp == 0xFF) { + if (bExp == 0xFF) { + } + return packFloat32(zSign, 0xFF, 0); + } + if (bExp == 0xFF) { + return packFloat32(zSign, 0, 0); + } + if (bExp == 0) { + if (bSig == 0) { + return packFloat32(zSign, 0xFF, 0); + } + normalizeFloat32Subnormal(bSig, &bExp, &bSig); + } + if (aExp == 0) { + if (aSig == 0) + return packFloat32(zSign, 0, 0); + normalizeFloat32Subnormal(aSig, &aExp, &aSig); + } + zExp = aExp - bExp + 0x7D; + aSig = (aSig | 0x00800000) << 7; + bSig = (bSig | 0x00800000) << 8; + if (bSig <= (aSig + aSig)) { + aSig >>= 1; + ++zExp; + } + zSig = (((bits64) aSig) << 32); + do_div(zSig, bSig); + + if ((zSig & 0x3F) == 0) { + zSig |= (((bits64) bSig) * zSig != ((bits64) aSig) << 32); + } + return roundAndPackFloat32(zSign, zExp, (bits32)zSig); + +} + +float32 float32_mul(float32 a, float32 b) +{ + char aSign, bSign, zSign; + int aExp, bExp, zExp; + unsigned int aSig, bSig; + unsigned long long zSig64; + unsigned int zSig; + + aSig = extractFloat32Frac(a); + aExp = extractFloat32Exp(a); + aSign = extractFloat32Sign(a); + bSig = extractFloat32Frac(b); + bExp = extractFloat32Exp(b); + bSign = extractFloat32Sign(b); + zSign = aSign ^ bSign; + if (aExp == 0) { + if (aSig == 0) + return packFloat32(zSign, 0, 0); + normalizeFloat32Subnormal(aSig, &aExp, &aSig); + } + if (bExp == 0) { + if (bSig == 0) + return packFloat32(zSign, 0, 0); + normalizeFloat32Subnormal(bSig, &bExp, &bSig); + } + if ((bExp == 0xff && bSig == 0) || (aExp == 0xff && aSig == 0)) + return roundAndPackFloat32(zSign, 0xff, 0); + + zExp = aExp + bExp - 0x7F; + aSig = (aSig | 0x00800000) << 7; + bSig = (bSig | 0x00800000) << 8; + shift64RightJamming(((unsigned long long)aSig) * bSig, 32, &zSig64); + zSig = zSig64; + if (0 <= (signed int)(zSig << 1)) { + zSig <<= 1; + --zExp; + } + return roundAndPackFloat32(zSign, zExp, zSig); + +} + +float64 float64_mul(float64 a, float64 b) +{ + char aSign, bSign, zSign; + int aExp, bExp, zExp; + unsigned long long int aSig, bSig, zSig0, zSig1; + + aSig = extractFloat64Frac(a); + aExp = extractFloat64Exp(a); + aSign = extractFloat64Sign(a); + bSig = extractFloat64Frac(b); + bExp = extractFloat64Exp(b); + bSign = extractFloat64Sign(b); + zSign = aSign ^ bSign; + + if (aExp == 0) { + if (aSig == 0) + return packFloat64(zSign, 0, 0); + normalizeFloat64Subnormal(aSig, &aExp, &aSig); + } + if (bExp == 0) { + if (bSig == 0) + return packFloat64(zSign, 0, 0); + normalizeFloat64Subnormal(bSig, &bExp, &bSig); + } + if ((aExp == 0x7ff && aSig == 0) || (bExp == 0x7ff && bSig == 0)) + return roundAndPackFloat64(zSign, 0x7ff, 0); + + zExp = aExp + bExp - 0x3FF; + aSig = (aSig | 0x0010000000000000LL) << 10; + bSig = (bSig | 0x0010000000000000LL) << 11; + mul64To128(aSig, bSig, &zSig0, &zSig1); + zSig0 |= (zSig1 != 0); + if (0 <= (signed long long int)(zSig0 << 1)) { + zSig0 <<= 1; + --zExp; + } + return roundAndPackFloat64(zSign, zExp, zSig0); +} + +/* + * ------------------------------------------------------------------------------- + * Returns the result of converting the double-precision floating-point value + * `a' to the single-precision floating-point format. The conversion is + * performed according to the IEC/IEEE Standard for Binary Floating-point + * Arithmetic. + * ------------------------------------------------------------------------------- + * */ +float32 float64_to_float32(float64 a) +{ + flag aSign; + int16 aExp; + bits64 aSig; + bits32 zSig; + + aSig = extractFloat64Frac( a ); + aExp = extractFloat64Exp( a ); + aSign = extractFloat64Sign( a ); + + shift64RightJamming( aSig, 22, &aSig ); + zSig = aSig; + if ( aExp || zSig ) { + zSig |= 0x40000000; + aExp -= 0x381; + } + return roundAndPackFloat32(aSign, aExp, zSig); +} diff --git a/arch/sh/kernel/cpu/sh4/sq.c b/arch/sh/kernel/cpu/sh4/sq.c new file mode 100644 index 000000000..4ca78ed71 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/sq.c @@ -0,0 +1,415 @@ +/* + * arch/sh/kernel/cpu/sh4/sq.c + * + * General management API for SH-4 integrated Store Queues + * + * Copyright (C) 2001 - 2006 Paul Mundt + * Copyright (C) 2001, 2002 M. R. Brown + * + * 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/cpu.h> +#include <linux/bitmap.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/mm.h> +#include <linux/io.h> +#include <linux/prefetch.h> +#include <asm/page.h> +#include <asm/cacheflush.h> +#include <cpu/sq.h> + +struct sq_mapping; + +struct sq_mapping { + const char *name; + + unsigned long sq_addr; + unsigned long addr; + unsigned int size; + + struct sq_mapping *next; +}; + +static struct sq_mapping *sq_mapping_list; +static DEFINE_SPINLOCK(sq_mapping_lock); +static struct kmem_cache *sq_cache; +static unsigned long *sq_bitmap; + +#define store_queue_barrier() \ +do { \ + (void)__raw_readl(P4SEG_STORE_QUE); \ + __raw_writel(0, P4SEG_STORE_QUE + 0); \ + __raw_writel(0, P4SEG_STORE_QUE + 8); \ +} while (0); + +/** + * sq_flush_range - Flush (prefetch) a specific SQ range + * @start: the store queue address to start flushing from + * @len: the length to flush + * + * Flushes the store queue cache from @start to @start + @len in a + * linear fashion. + */ +void sq_flush_range(unsigned long start, unsigned int len) +{ + unsigned long *sq = (unsigned long *)start; + + /* Flush the queues */ + for (len >>= 5; len--; sq += 8) + prefetchw(sq); + + /* Wait for completion */ + store_queue_barrier(); +} +EXPORT_SYMBOL(sq_flush_range); + +static inline void sq_mapping_list_add(struct sq_mapping *map) +{ + struct sq_mapping **p, *tmp; + + spin_lock_irq(&sq_mapping_lock); + + p = &sq_mapping_list; + while ((tmp = *p) != NULL) + p = &tmp->next; + + map->next = tmp; + *p = map; + + spin_unlock_irq(&sq_mapping_lock); +} + +static inline void sq_mapping_list_del(struct sq_mapping *map) +{ + struct sq_mapping **p, *tmp; + + spin_lock_irq(&sq_mapping_lock); + + for (p = &sq_mapping_list; (tmp = *p); p = &tmp->next) + if (tmp == map) { + *p = tmp->next; + break; + } + + spin_unlock_irq(&sq_mapping_lock); +} + +static int __sq_remap(struct sq_mapping *map, pgprot_t prot) +{ +#if defined(CONFIG_MMU) + struct vm_struct *vma; + + vma = __get_vm_area(map->size, VM_ALLOC, map->sq_addr, SQ_ADDRMAX); + if (!vma) + return -ENOMEM; + + vma->phys_addr = map->addr; + + if (ioremap_page_range((unsigned long)vma->addr, + (unsigned long)vma->addr + map->size, + vma->phys_addr, prot)) { + vunmap(vma->addr); + return -EAGAIN; + } +#else + /* + * Without an MMU (or with it turned off), this is much more + * straightforward, as we can just load up each queue's QACR with + * the physical address appropriately masked. + */ + __raw_writel(((map->addr >> 26) << 2) & 0x1c, SQ_QACR0); + __raw_writel(((map->addr >> 26) << 2) & 0x1c, SQ_QACR1); +#endif + + return 0; +} + +/** + * sq_remap - Map a physical address through the Store Queues + * @phys: Physical address of mapping. + * @size: Length of mapping. + * @name: User invoking mapping. + * @prot: Protection bits. + * + * Remaps the physical address @phys through the next available store queue + * address of @size length. @name is logged at boot time as well as through + * the sysfs interface. + */ +unsigned long sq_remap(unsigned long phys, unsigned int size, + const char *name, pgprot_t prot) +{ + struct sq_mapping *map; + unsigned long end; + unsigned int psz; + int ret, page; + + /* Don't allow wraparound or zero size */ + end = phys + size - 1; + if (unlikely(!size || end < phys)) + return -EINVAL; + /* Don't allow anyone to remap normal memory.. */ + if (unlikely(phys < virt_to_phys(high_memory))) + return -EINVAL; + + phys &= PAGE_MASK; + size = PAGE_ALIGN(end + 1) - phys; + + map = kmem_cache_alloc(sq_cache, GFP_KERNEL); + if (unlikely(!map)) + return -ENOMEM; + + map->addr = phys; + map->size = size; + map->name = name; + + page = bitmap_find_free_region(sq_bitmap, 0x04000000 >> PAGE_SHIFT, + get_order(map->size)); + if (unlikely(page < 0)) { + ret = -ENOSPC; + goto out; + } + + map->sq_addr = P4SEG_STORE_QUE + (page << PAGE_SHIFT); + + ret = __sq_remap(map, prot); + if (unlikely(ret != 0)) + goto out; + + psz = (size + (PAGE_SIZE - 1)) >> PAGE_SHIFT; + pr_info("sqremap: %15s [%4d page%s] va 0x%08lx pa 0x%08lx\n", + likely(map->name) ? map->name : "???", + psz, psz == 1 ? " " : "s", + map->sq_addr, map->addr); + + sq_mapping_list_add(map); + + return map->sq_addr; + +out: + kmem_cache_free(sq_cache, map); + return ret; +} +EXPORT_SYMBOL(sq_remap); + +/** + * sq_unmap - Unmap a Store Queue allocation + * @vaddr: Pre-allocated Store Queue mapping. + * + * Unmaps the store queue allocation @map that was previously created by + * sq_remap(). Also frees up the pte that was previously inserted into + * the kernel page table and discards the UTLB translation. + */ +void sq_unmap(unsigned long vaddr) +{ + struct sq_mapping **p, *map; + int page; + + for (p = &sq_mapping_list; (map = *p); p = &map->next) + if (map->sq_addr == vaddr) + break; + + if (unlikely(!map)) { + printk("%s: bad store queue address 0x%08lx\n", + __func__, vaddr); + return; + } + + page = (map->sq_addr - P4SEG_STORE_QUE) >> PAGE_SHIFT; + bitmap_release_region(sq_bitmap, page, get_order(map->size)); + +#ifdef CONFIG_MMU + { + /* + * Tear down the VMA in the MMU case. + */ + struct vm_struct *vma; + + vma = remove_vm_area((void *)(map->sq_addr & PAGE_MASK)); + if (!vma) { + printk(KERN_ERR "%s: bad address 0x%08lx\n", + __func__, map->sq_addr); + return; + } + } +#endif + + sq_mapping_list_del(map); + + kmem_cache_free(sq_cache, map); +} +EXPORT_SYMBOL(sq_unmap); + +/* + * Needlessly complex sysfs interface. Unfortunately it doesn't seem like + * there is any other easy way to add things on a per-cpu basis without + * putting the directory entries somewhere stupid and having to create + * links in sysfs by hand back in to the per-cpu directories. + * + * Some day we may want to have an additional abstraction per store + * queue, but considering the kobject hell we already have to deal with, + * it's simply not worth the trouble. + */ +static struct kobject *sq_kobject[NR_CPUS]; + +struct sq_sysfs_attr { + struct attribute attr; + ssize_t (*show)(char *buf); + ssize_t (*store)(const char *buf, size_t count); +}; + +#define to_sq_sysfs_attr(a) container_of(a, struct sq_sysfs_attr, attr) + +static ssize_t sq_sysfs_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct sq_sysfs_attr *sattr = to_sq_sysfs_attr(attr); + + if (likely(sattr->show)) + return sattr->show(buf); + + return -EIO; +} + +static ssize_t sq_sysfs_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + struct sq_sysfs_attr *sattr = to_sq_sysfs_attr(attr); + + if (likely(sattr->store)) + return sattr->store(buf, count); + + return -EIO; +} + +static ssize_t mapping_show(char *buf) +{ + struct sq_mapping **list, *entry; + char *p = buf; + + for (list = &sq_mapping_list; (entry = *list); list = &entry->next) + p += sprintf(p, "%08lx-%08lx [%08lx]: %s\n", + entry->sq_addr, entry->sq_addr + entry->size, + entry->addr, entry->name); + + return p - buf; +} + +static ssize_t mapping_store(const char *buf, size_t count) +{ + unsigned long base = 0, len = 0; + + sscanf(buf, "%lx %lx", &base, &len); + if (!base) + return -EIO; + + if (likely(len)) { + int ret = sq_remap(base, len, "Userspace", PAGE_SHARED); + if (ret < 0) + return ret; + } else + sq_unmap(base); + + return count; +} + +static struct sq_sysfs_attr mapping_attr = + __ATTR(mapping, 0644, mapping_show, mapping_store); + +static struct attribute *sq_sysfs_attrs[] = { + &mapping_attr.attr, + NULL, +}; + +static const struct sysfs_ops sq_sysfs_ops = { + .show = sq_sysfs_show, + .store = sq_sysfs_store, +}; + +static struct kobj_type ktype_percpu_entry = { + .sysfs_ops = &sq_sysfs_ops, + .default_attrs = sq_sysfs_attrs, +}; + +static int sq_dev_add(struct device *dev, struct subsys_interface *sif) +{ + unsigned int cpu = dev->id; + struct kobject *kobj; + int error; + + sq_kobject[cpu] = kzalloc(sizeof(struct kobject), GFP_KERNEL); + if (unlikely(!sq_kobject[cpu])) + return -ENOMEM; + + kobj = sq_kobject[cpu]; + error = kobject_init_and_add(kobj, &ktype_percpu_entry, &dev->kobj, + "%s", "sq"); + if (!error) + kobject_uevent(kobj, KOBJ_ADD); + return error; +} + +static void sq_dev_remove(struct device *dev, struct subsys_interface *sif) +{ + unsigned int cpu = dev->id; + struct kobject *kobj = sq_kobject[cpu]; + + kobject_put(kobj); +} + +static struct subsys_interface sq_interface = { + .name = "sq", + .subsys = &cpu_subsys, + .add_dev = sq_dev_add, + .remove_dev = sq_dev_remove, +}; + +static int __init sq_api_init(void) +{ + unsigned int nr_pages = 0x04000000 >> PAGE_SHIFT; + unsigned int size = (nr_pages + (BITS_PER_LONG - 1)) / BITS_PER_LONG; + int ret = -ENOMEM; + + printk(KERN_NOTICE "sq: Registering store queue API.\n"); + + sq_cache = kmem_cache_create("store_queue_cache", + sizeof(struct sq_mapping), 0, 0, NULL); + if (unlikely(!sq_cache)) + return ret; + + sq_bitmap = kzalloc(size, GFP_KERNEL); + if (unlikely(!sq_bitmap)) + goto out; + + ret = subsys_interface_register(&sq_interface); + if (unlikely(ret != 0)) + goto out; + + return 0; + +out: + kfree(sq_bitmap); + kmem_cache_destroy(sq_cache); + + return ret; +} + +static void __exit sq_api_exit(void) +{ + subsys_interface_unregister(&sq_interface); + kfree(sq_bitmap); + kmem_cache_destroy(sq_cache); +} + +module_init(sq_api_init); +module_exit(sq_api_exit); + +MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>, M. R. Brown <mrbrown@0xd6.org>"); +MODULE_DESCRIPTION("Simple API for SH-4 integrated Store Queues"); +MODULE_LICENSE("GPL"); diff --git a/arch/sh/kernel/cpu/sh4a/Makefile b/arch/sh/kernel/cpu/sh4a/Makefile new file mode 100644 index 000000000..baf73a8fc --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/Makefile @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the Linux/SuperH SH-4 backends. +# + +# CPU subtype setup +obj-$(CONFIG_CPU_SUBTYPE_SH7757) += setup-sh7757.o +obj-$(CONFIG_CPU_SUBTYPE_SH7763) += setup-sh7763.o +obj-$(CONFIG_CPU_SUBTYPE_SH7770) += setup-sh7770.o +obj-$(CONFIG_CPU_SUBTYPE_SH7780) += setup-sh7780.o +obj-$(CONFIG_CPU_SUBTYPE_SH7785) += setup-sh7785.o +obj-$(CONFIG_CPU_SUBTYPE_SH7786) += setup-sh7786.o intc-shx3.o +obj-$(CONFIG_CPU_SUBTYPE_SH7343) += setup-sh7343.o +obj-$(CONFIG_CPU_SUBTYPE_SH7722) += setup-sh7722.o serial-sh7722.o +obj-$(CONFIG_CPU_SUBTYPE_SH7723) += setup-sh7723.o +obj-$(CONFIG_CPU_SUBTYPE_SH7724) += setup-sh7724.o +obj-$(CONFIG_CPU_SUBTYPE_SH7734) += setup-sh7734.o +obj-$(CONFIG_CPU_SUBTYPE_SH7366) += setup-sh7366.o +obj-$(CONFIG_CPU_SUBTYPE_SHX3) += setup-shx3.o intc-shx3.o + +# SMP setup +smp-$(CONFIG_CPU_SHX3) := smp-shx3.o + +# Primary on-chip clocks (common) +clock-$(CONFIG_CPU_SUBTYPE_SH7757) := clock-sh7757.o +clock-$(CONFIG_CPU_SUBTYPE_SH7763) := clock-sh7763.o +clock-$(CONFIG_CPU_SUBTYPE_SH7770) := clock-sh7770.o +clock-$(CONFIG_CPU_SUBTYPE_SH7780) := clock-sh7780.o +clock-$(CONFIG_CPU_SUBTYPE_SH7785) := clock-sh7785.o +clock-$(CONFIG_CPU_SUBTYPE_SH7786) := clock-sh7786.o +clock-$(CONFIG_CPU_SUBTYPE_SH7343) := clock-sh7343.o +clock-$(CONFIG_CPU_SUBTYPE_SH7722) := clock-sh7722.o +clock-$(CONFIG_CPU_SUBTYPE_SH7723) := clock-sh7723.o +clock-$(CONFIG_CPU_SUBTYPE_SH7724) := clock-sh7724.o +clock-$(CONFIG_CPU_SUBTYPE_SH7734) := clock-sh7734.o +clock-$(CONFIG_CPU_SUBTYPE_SH7366) := clock-sh7366.o +clock-$(CONFIG_CPU_SUBTYPE_SHX3) := clock-shx3.o + +# Pinmux setup +pinmux-$(CONFIG_CPU_SUBTYPE_SH7722) := pinmux-sh7722.o +pinmux-$(CONFIG_CPU_SUBTYPE_SH7723) := pinmux-sh7723.o +pinmux-$(CONFIG_CPU_SUBTYPE_SH7724) := pinmux-sh7724.o +pinmux-$(CONFIG_CPU_SUBTYPE_SH7734) := pinmux-sh7734.o +pinmux-$(CONFIG_CPU_SUBTYPE_SH7757) := pinmux-sh7757.o +pinmux-$(CONFIG_CPU_SUBTYPE_SH7785) := pinmux-sh7785.o +pinmux-$(CONFIG_CPU_SUBTYPE_SH7786) := pinmux-sh7786.o +pinmux-$(CONFIG_CPU_SUBTYPE_SHX3) := pinmux-shx3.o + +obj-y += $(clock-y) +obj-$(CONFIG_SMP) += $(smp-y) +obj-$(CONFIG_GPIOLIB) += $(pinmux-y) +obj-$(CONFIG_PERF_EVENTS) += perf_event.o +obj-$(CONFIG_HAVE_HW_BREAKPOINT) += ubc.o diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7343.c b/arch/sh/kernel/cpu/sh4a/clock-sh7343.c new file mode 100644 index 000000000..a907ee238 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/clock-sh7343.c @@ -0,0 +1,289 @@ +/* + * arch/sh/kernel/cpu/sh4a/clock-sh7343.c + * + * SH7343 clock framework support + * + * Copyright (C) 2009 Magnus Damm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/clkdev.h> +#include <asm/clock.h> + +/* SH7343 registers */ +#define FRQCR 0xa4150000 +#define VCLKCR 0xa4150004 +#define SCLKACR 0xa4150008 +#define SCLKBCR 0xa415000c +#define PLLCR 0xa4150024 +#define MSTPCR0 0xa4150030 +#define MSTPCR1 0xa4150034 +#define MSTPCR2 0xa4150038 +#define DLLFRQ 0xa4150050 + +/* Fixed 32 KHz root clock for RTC and Power Management purposes */ +static struct clk r_clk = { + .rate = 32768, +}; + +/* + * Default rate for the root input clock, reset this with clk_set_rate() + * from the platform code. + */ +struct clk extal_clk = { + .rate = 33333333, +}; + +/* The dll block multiplies the 32khz r_clk, may be used instead of extal */ +static unsigned long dll_recalc(struct clk *clk) +{ + unsigned long mult; + + if (__raw_readl(PLLCR) & 0x1000) + mult = __raw_readl(DLLFRQ); + else + mult = 0; + + return clk->parent->rate * mult; +} + +static struct sh_clk_ops dll_clk_ops = { + .recalc = dll_recalc, +}; + +static struct clk dll_clk = { + .ops = &dll_clk_ops, + .parent = &r_clk, + .flags = CLK_ENABLE_ON_INIT, +}; + +static unsigned long pll_recalc(struct clk *clk) +{ + unsigned long mult = 1; + + if (__raw_readl(PLLCR) & 0x4000) + mult = (((__raw_readl(FRQCR) >> 24) & 0x1f) + 1); + + return clk->parent->rate * mult; +} + +static struct sh_clk_ops pll_clk_ops = { + .recalc = pll_recalc, +}; + +static struct clk pll_clk = { + .ops = &pll_clk_ops, + .flags = CLK_ENABLE_ON_INIT, +}; + +struct clk *main_clks[] = { + &r_clk, + &extal_clk, + &dll_clk, + &pll_clk, +}; + +static int multipliers[] = { 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; +static int divisors[] = { 1, 3, 2, 5, 3, 4, 5, 6, 8, 10, 12, 16, 20 }; + +static struct clk_div_mult_table div4_div_mult_table = { + .divisors = divisors, + .nr_divisors = ARRAY_SIZE(divisors), + .multipliers = multipliers, + .nr_multipliers = ARRAY_SIZE(multipliers), +}; + +static struct clk_div4_table div4_table = { + .div_mult_table = &div4_div_mult_table, +}; + +enum { DIV4_I, DIV4_U, DIV4_SH, DIV4_B, DIV4_B3, DIV4_P, + DIV4_SIUA, DIV4_SIUB, DIV4_NR }; + +#define DIV4(_reg, _bit, _mask, _flags) \ + SH_CLK_DIV4(&pll_clk, _reg, _bit, _mask, _flags) + +struct clk div4_clks[DIV4_NR] = { + [DIV4_I] = DIV4(FRQCR, 20, 0x1fff, CLK_ENABLE_ON_INIT), + [DIV4_U] = DIV4(FRQCR, 16, 0x1fff, CLK_ENABLE_ON_INIT), + [DIV4_SH] = DIV4(FRQCR, 12, 0x1fff, CLK_ENABLE_ON_INIT), + [DIV4_B] = DIV4(FRQCR, 8, 0x1fff, CLK_ENABLE_ON_INIT), + [DIV4_B3] = DIV4(FRQCR, 4, 0x1fff, CLK_ENABLE_ON_INIT), + [DIV4_P] = DIV4(FRQCR, 0, 0x1fff, 0), + [DIV4_SIUA] = DIV4(SCLKACR, 0, 0x1fff, 0), + [DIV4_SIUB] = DIV4(SCLKBCR, 0, 0x1fff, 0), +}; + +enum { DIV6_V, DIV6_NR }; + +struct clk div6_clks[DIV6_NR] = { + [DIV6_V] = SH_CLK_DIV6(&pll_clk, VCLKCR, 0), +}; + +#define MSTP(_parent, _reg, _bit, _flags) \ + SH_CLK_MSTP32(_parent, _reg, _bit, _flags) + +enum { MSTP031, MSTP030, MSTP029, MSTP028, MSTP026, + MSTP023, MSTP022, MSTP021, MSTP020, MSTP019, MSTP018, MSTP017, MSTP016, + MSTP015, MSTP014, MSTP013, MSTP012, MSTP011, MSTP010, + MSTP007, MSTP006, MSTP005, MSTP004, MSTP003, MSTP002, MSTP001, + MSTP109, MSTP108, MSTP100, + MSTP225, MSTP224, MSTP218, MSTP217, MSTP216, + MSTP214, MSTP213, MSTP212, MSTP211, MSTP208, + MSTP206, MSTP205, MSTP204, MSTP203, MSTP202, MSTP201, MSTP200, + MSTP_NR }; + +static struct clk mstp_clks[MSTP_NR] = { + [MSTP031] = MSTP(&div4_clks[DIV4_I], MSTPCR0, 31, CLK_ENABLE_ON_INIT), + [MSTP030] = MSTP(&div4_clks[DIV4_I], MSTPCR0, 30, CLK_ENABLE_ON_INIT), + [MSTP029] = MSTP(&div4_clks[DIV4_I], MSTPCR0, 29, CLK_ENABLE_ON_INIT), + [MSTP028] = MSTP(&div4_clks[DIV4_U], MSTPCR0, 28, CLK_ENABLE_ON_INIT), + [MSTP026] = MSTP(&div4_clks[DIV4_B], MSTPCR0, 26, CLK_ENABLE_ON_INIT), + [MSTP023] = MSTP(&div4_clks[DIV4_P], MSTPCR0, 23, 0), + [MSTP022] = MSTP(&div4_clks[DIV4_P], MSTPCR0, 22, 0), + [MSTP021] = MSTP(&div4_clks[DIV4_P], MSTPCR0, 21, 0), + [MSTP020] = MSTP(&div4_clks[DIV4_P], MSTPCR0, 20, 0), + [MSTP019] = MSTP(&div4_clks[DIV4_P], MSTPCR0, 19, 0), + [MSTP017] = MSTP(&div4_clks[DIV4_P], MSTPCR0, 17, 0), + [MSTP015] = MSTP(&div4_clks[DIV4_P], MSTPCR0, 15, 0), + [MSTP014] = MSTP(&r_clk, MSTPCR0, 14, 0), + [MSTP013] = MSTP(&r_clk, MSTPCR0, 13, 0), + [MSTP011] = MSTP(&div4_clks[DIV4_P], MSTPCR0, 11, 0), + [MSTP010] = MSTP(&div4_clks[DIV4_P], MSTPCR0, 10, 0), + [MSTP007] = MSTP(&div4_clks[DIV4_P], MSTPCR0, 7, 0), + [MSTP006] = MSTP(&div4_clks[DIV4_P], MSTPCR0, 6, 0), + [MSTP005] = MSTP(&div4_clks[DIV4_P], MSTPCR0, 5, 0), + [MSTP004] = MSTP(&div4_clks[DIV4_P], MSTPCR0, 4, 0), + [MSTP003] = MSTP(&div4_clks[DIV4_P], MSTPCR0, 3, 0), + [MSTP002] = MSTP(&div4_clks[DIV4_P], MSTPCR0, 2, 0), + [MSTP001] = MSTP(&div4_clks[DIV4_P], MSTPCR0, 1, 0), + + [MSTP109] = MSTP(&div4_clks[DIV4_P], MSTPCR1, 9, 0), + [MSTP108] = MSTP(&div4_clks[DIV4_P], MSTPCR1, 8, 0), + + [MSTP225] = MSTP(&div4_clks[DIV4_P], MSTPCR2, 25, 0), + [MSTP224] = MSTP(&div4_clks[DIV4_P], MSTPCR2, 24, 0), + [MSTP218] = MSTP(&div4_clks[DIV4_P], MSTPCR2, 18, 0), + [MSTP217] = MSTP(&div4_clks[DIV4_P], MSTPCR2, 17, 0), + [MSTP216] = MSTP(&div4_clks[DIV4_P], MSTPCR2, 16, 0), + [MSTP214] = MSTP(&r_clk, MSTPCR2, 14, 0), + [MSTP213] = MSTP(&div4_clks[DIV4_P], MSTPCR2, 13, 0), + [MSTP212] = MSTP(&div4_clks[DIV4_P], MSTPCR2, 12, 0), + [MSTP211] = MSTP(&div4_clks[DIV4_P], MSTPCR2, 11, 0), + [MSTP208] = MSTP(&div4_clks[DIV4_B], MSTPCR2, 8, 0), + [MSTP206] = MSTP(&div4_clks[DIV4_B], MSTPCR2, 6, CLK_ENABLE_ON_INIT), + [MSTP205] = MSTP(&div4_clks[DIV4_B], MSTPCR2, 5, 0), + [MSTP204] = MSTP(&div4_clks[DIV4_B], MSTPCR2, 4, 0), + [MSTP203] = MSTP(&div4_clks[DIV4_B], MSTPCR2, 3, 0), + [MSTP202] = MSTP(&div4_clks[DIV4_B], MSTPCR2, 2, CLK_ENABLE_ON_INIT), + [MSTP201] = MSTP(&div4_clks[DIV4_B], MSTPCR2, 1, CLK_ENABLE_ON_INIT), + [MSTP200] = MSTP(&div4_clks[DIV4_B], MSTPCR2, 0, 0), +}; + +static struct clk_lookup lookups[] = { + /* main clocks */ + CLKDEV_CON_ID("rclk", &r_clk), + CLKDEV_CON_ID("extal", &extal_clk), + CLKDEV_CON_ID("dll_clk", &dll_clk), + CLKDEV_CON_ID("pll_clk", &pll_clk), + + /* DIV4 clocks */ + CLKDEV_CON_ID("cpu_clk", &div4_clks[DIV4_I]), + CLKDEV_CON_ID("umem_clk", &div4_clks[DIV4_U]), + CLKDEV_CON_ID("shyway_clk", &div4_clks[DIV4_SH]), + CLKDEV_CON_ID("bus_clk", &div4_clks[DIV4_B]), + CLKDEV_CON_ID("b3_clk", &div4_clks[DIV4_B3]), + CLKDEV_CON_ID("peripheral_clk", &div4_clks[DIV4_P]), + CLKDEV_CON_ID("siua_clk", &div4_clks[DIV4_SIUA]), + CLKDEV_CON_ID("siub_clk", &div4_clks[DIV4_SIUB]), + + /* DIV6 clocks */ + CLKDEV_CON_ID("video_clk", &div6_clks[DIV6_V]), + + /* MSTP32 clocks */ + CLKDEV_CON_ID("tlb0", &mstp_clks[MSTP031]), + CLKDEV_CON_ID("ic0", &mstp_clks[MSTP030]), + CLKDEV_CON_ID("oc0", &mstp_clks[MSTP029]), + CLKDEV_CON_ID("uram0", &mstp_clks[MSTP028]), + CLKDEV_CON_ID("xymem0", &mstp_clks[MSTP026]), + CLKDEV_CON_ID("intc3", &mstp_clks[MSTP023]), + CLKDEV_CON_ID("intc0", &mstp_clks[MSTP022]), + CLKDEV_CON_ID("dmac0", &mstp_clks[MSTP021]), + CLKDEV_CON_ID("sh0", &mstp_clks[MSTP020]), + CLKDEV_CON_ID("hudi0", &mstp_clks[MSTP019]), + CLKDEV_CON_ID("ubc0", &mstp_clks[MSTP017]), + CLKDEV_CON_ID("tmu_fck", &mstp_clks[MSTP015]), + CLKDEV_ICK_ID("fck", "sh-cmt-32.0", &mstp_clks[MSTP014]), + CLKDEV_CON_ID("rwdt0", &mstp_clks[MSTP013]), + CLKDEV_CON_ID("mfi0", &mstp_clks[MSTP011]), + CLKDEV_CON_ID("flctl0", &mstp_clks[MSTP010]), + + CLKDEV_ICK_ID("fck", "sh-sci.0", &mstp_clks[MSTP007]), + CLKDEV_ICK_ID("fck", "sh-sci.1", &mstp_clks[MSTP006]), + CLKDEV_ICK_ID("fck", "sh-sci.2", &mstp_clks[MSTP005]), + CLKDEV_ICK_ID("fck", "sh-sci.3", &mstp_clks[MSTP004]), + + CLKDEV_CON_ID("sio0", &mstp_clks[MSTP003]), + CLKDEV_CON_ID("siof0", &mstp_clks[MSTP002]), + CLKDEV_CON_ID("siof1", &mstp_clks[MSTP001]), + CLKDEV_DEV_ID("i2c-sh_mobile.0", &mstp_clks[MSTP109]), + CLKDEV_DEV_ID("i2c-sh_mobile.1", &mstp_clks[MSTP108]), + CLKDEV_CON_ID("tpu0", &mstp_clks[MSTP225]), + CLKDEV_CON_ID("irda0", &mstp_clks[MSTP224]), + CLKDEV_CON_ID("sdhi0", &mstp_clks[MSTP218]), + CLKDEV_CON_ID("mmcif0", &mstp_clks[MSTP217]), + CLKDEV_CON_ID("sim0", &mstp_clks[MSTP216]), + CLKDEV_CON_ID("keysc0", &mstp_clks[MSTP214]), + CLKDEV_CON_ID("tsif0", &mstp_clks[MSTP213]), + CLKDEV_CON_ID("s3d40", &mstp_clks[MSTP212]), + CLKDEV_CON_ID("usbf0", &mstp_clks[MSTP211]), + CLKDEV_CON_ID("siu0", &mstp_clks[MSTP208]), + CLKDEV_CON_ID("jpu0", &mstp_clks[MSTP206]), + CLKDEV_CON_ID("vou0", &mstp_clks[MSTP205]), + CLKDEV_CON_ID("beu0", &mstp_clks[MSTP204]), + CLKDEV_CON_ID("ceu0", &mstp_clks[MSTP203]), + CLKDEV_CON_ID("veu0", &mstp_clks[MSTP202]), + CLKDEV_CON_ID("vpu0", &mstp_clks[MSTP201]), + CLKDEV_CON_ID("lcdc0", &mstp_clks[MSTP200]), +}; + +int __init arch_clk_init(void) +{ + int k, ret = 0; + + /* autodetect extal or dll configuration */ + if (__raw_readl(PLLCR) & 0x1000) + pll_clk.parent = &dll_clk; + else + pll_clk.parent = &extal_clk; + + for (k = 0; !ret && (k < ARRAY_SIZE(main_clks)); k++) + ret = clk_register(main_clks[k]); + + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); + + if (!ret) + ret = sh_clk_div4_register(div4_clks, DIV4_NR, &div4_table); + + if (!ret) + ret = sh_clk_div6_register(div6_clks, DIV6_NR); + + if (!ret) + ret = sh_clk_mstp_register(mstp_clks, MSTP_NR); + + return ret; +} diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7366.c b/arch/sh/kernel/cpu/sh4a/clock-sh7366.c new file mode 100644 index 000000000..ac9854179 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/clock-sh7366.c @@ -0,0 +1,282 @@ +/* + * arch/sh/kernel/cpu/sh4a/clock-sh7366.c + * + * SH7366 clock framework support + * + * Copyright (C) 2009 Magnus Damm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/clkdev.h> +#include <asm/clock.h> + +/* SH7366 registers */ +#define FRQCR 0xa4150000 +#define VCLKCR 0xa4150004 +#define SCLKACR 0xa4150008 +#define SCLKBCR 0xa415000c +#define PLLCR 0xa4150024 +#define MSTPCR0 0xa4150030 +#define MSTPCR1 0xa4150034 +#define MSTPCR2 0xa4150038 +#define DLLFRQ 0xa4150050 + +/* Fixed 32 KHz root clock for RTC and Power Management purposes */ +static struct clk r_clk = { + .rate = 32768, +}; + +/* + * Default rate for the root input clock, reset this with clk_set_rate() + * from the platform code. + */ +struct clk extal_clk = { + .rate = 33333333, +}; + +/* The dll block multiplies the 32khz r_clk, may be used instead of extal */ +static unsigned long dll_recalc(struct clk *clk) +{ + unsigned long mult; + + if (__raw_readl(PLLCR) & 0x1000) + mult = __raw_readl(DLLFRQ); + else + mult = 0; + + return clk->parent->rate * mult; +} + +static struct sh_clk_ops dll_clk_ops = { + .recalc = dll_recalc, +}; + +static struct clk dll_clk = { + .ops = &dll_clk_ops, + .parent = &r_clk, + .flags = CLK_ENABLE_ON_INIT, +}; + +static unsigned long pll_recalc(struct clk *clk) +{ + unsigned long mult = 1; + unsigned long div = 1; + + if (__raw_readl(PLLCR) & 0x4000) + mult = (((__raw_readl(FRQCR) >> 24) & 0x1f) + 1); + else + div = 2; + + return (clk->parent->rate * mult) / div; +} + +static struct sh_clk_ops pll_clk_ops = { + .recalc = pll_recalc, +}; + +static struct clk pll_clk = { + .ops = &pll_clk_ops, + .flags = CLK_ENABLE_ON_INIT, +}; + +struct clk *main_clks[] = { + &r_clk, + &extal_clk, + &dll_clk, + &pll_clk, +}; + +static int multipliers[] = { 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; +static int divisors[] = { 1, 3, 2, 5, 3, 4, 5, 6, 8, 10, 12, 16, 20 }; + +static struct clk_div_mult_table div4_div_mult_table = { + .divisors = divisors, + .nr_divisors = ARRAY_SIZE(divisors), + .multipliers = multipliers, + .nr_multipliers = ARRAY_SIZE(multipliers), +}; + +static struct clk_div4_table div4_table = { + .div_mult_table = &div4_div_mult_table, +}; + +enum { DIV4_I, DIV4_U, DIV4_SH, DIV4_B, DIV4_B3, DIV4_P, + DIV4_SIUA, DIV4_SIUB, DIV4_NR }; + +#define DIV4(_reg, _bit, _mask, _flags) \ + SH_CLK_DIV4(&pll_clk, _reg, _bit, _mask, _flags) + +struct clk div4_clks[DIV4_NR] = { + [DIV4_I] = DIV4(FRQCR, 20, 0x1fef, CLK_ENABLE_ON_INIT), + [DIV4_U] = DIV4(FRQCR, 16, 0x1fff, CLK_ENABLE_ON_INIT), + [DIV4_SH] = DIV4(FRQCR, 12, 0x1fff, CLK_ENABLE_ON_INIT), + [DIV4_B] = DIV4(FRQCR, 8, 0x1fff, CLK_ENABLE_ON_INIT), + [DIV4_B3] = DIV4(FRQCR, 4, 0x1fff, CLK_ENABLE_ON_INIT), + [DIV4_P] = DIV4(FRQCR, 0, 0x1fff, 0), + [DIV4_SIUA] = DIV4(SCLKACR, 0, 0x1fff, 0), + [DIV4_SIUB] = DIV4(SCLKBCR, 0, 0x1fff, 0), +}; + +enum { DIV6_V, DIV6_NR }; + +struct clk div6_clks[DIV6_NR] = { + [DIV6_V] = SH_CLK_DIV6(&pll_clk, VCLKCR, 0), +}; + +#define MSTP(_parent, _reg, _bit, _flags) \ + SH_CLK_MSTP32(_parent, _reg, _bit, _flags) + +enum { MSTP031, MSTP030, MSTP029, MSTP028, MSTP026, + MSTP023, MSTP022, MSTP021, MSTP020, MSTP019, MSTP018, MSTP017, MSTP016, + MSTP015, MSTP014, MSTP013, MSTP012, MSTP011, MSTP010, + MSTP007, MSTP006, MSTP005, MSTP002, MSTP001, + MSTP109, MSTP100, + MSTP227, MSTP226, MSTP224, MSTP223, MSTP222, MSTP218, MSTP217, + MSTP211, MSTP207, MSTP205, MSTP204, MSTP203, MSTP202, MSTP201, MSTP200, + MSTP_NR }; + +static struct clk mstp_clks[MSTP_NR] = { + /* See page 52 of Datasheet V0.40: Overview -> Block Diagram */ + [MSTP031] = MSTP(&div4_clks[DIV4_I], MSTPCR0, 31, CLK_ENABLE_ON_INIT), + [MSTP030] = MSTP(&div4_clks[DIV4_I], MSTPCR0, 30, CLK_ENABLE_ON_INIT), + [MSTP029] = MSTP(&div4_clks[DIV4_I], MSTPCR0, 29, CLK_ENABLE_ON_INIT), + [MSTP028] = MSTP(&div4_clks[DIV4_SH], MSTPCR0, 28, CLK_ENABLE_ON_INIT), + [MSTP026] = MSTP(&div4_clks[DIV4_B], MSTPCR0, 26, CLK_ENABLE_ON_INIT), + [MSTP023] = MSTP(&div4_clks[DIV4_P], MSTPCR0, 23, 0), + [MSTP022] = MSTP(&div4_clks[DIV4_P], MSTPCR0, 22, 0), + [MSTP021] = MSTP(&div4_clks[DIV4_P], MSTPCR0, 21, 0), + [MSTP020] = MSTP(&div4_clks[DIV4_P], MSTPCR0, 20, 0), + [MSTP019] = MSTP(&div4_clks[DIV4_P], MSTPCR0, 19, 0), + [MSTP017] = MSTP(&div4_clks[DIV4_P], MSTPCR0, 17, 0), + [MSTP015] = MSTP(&div4_clks[DIV4_P], MSTPCR0, 15, 0), + [MSTP014] = MSTP(&r_clk, MSTPCR0, 14, 0), + [MSTP013] = MSTP(&r_clk, MSTPCR0, 13, 0), + [MSTP011] = MSTP(&div4_clks[DIV4_P], MSTPCR0, 11, 0), + [MSTP010] = MSTP(&div4_clks[DIV4_P], MSTPCR0, 10, 0), + [MSTP007] = MSTP(&div4_clks[DIV4_P], MSTPCR0, 7, 0), + [MSTP006] = MSTP(&div4_clks[DIV4_P], MSTPCR0, 6, 0), + [MSTP005] = MSTP(&div4_clks[DIV4_P], MSTPCR0, 5, 0), + [MSTP002] = MSTP(&div4_clks[DIV4_P], MSTPCR0, 2, 0), + [MSTP001] = MSTP(&div4_clks[DIV4_P], MSTPCR0, 1, 0), + + [MSTP109] = MSTP(&div4_clks[DIV4_P], MSTPCR1, 9, 0), + + [MSTP227] = MSTP(&div4_clks[DIV4_P], MSTPCR2, 27, 0), + [MSTP226] = MSTP(&div4_clks[DIV4_P], MSTPCR2, 26, 0), + [MSTP224] = MSTP(&div4_clks[DIV4_P], MSTPCR2, 24, 0), + [MSTP223] = MSTP(&div4_clks[DIV4_P], MSTPCR2, 23, 0), + [MSTP222] = MSTP(&div4_clks[DIV4_P], MSTPCR2, 22, 0), + [MSTP218] = MSTP(&div4_clks[DIV4_P], MSTPCR2, 18, 0), + [MSTP217] = MSTP(&div4_clks[DIV4_P], MSTPCR2, 17, 0), + [MSTP211] = MSTP(&div4_clks[DIV4_P], MSTPCR2, 11, 0), + [MSTP207] = MSTP(&div4_clks[DIV4_B], MSTPCR2, 7, CLK_ENABLE_ON_INIT), + [MSTP205] = MSTP(&div4_clks[DIV4_B], MSTPCR2, 5, 0), + [MSTP204] = MSTP(&div4_clks[DIV4_B], MSTPCR2, 4, 0), + [MSTP203] = MSTP(&div4_clks[DIV4_B], MSTPCR2, 3, 0), + [MSTP202] = MSTP(&div4_clks[DIV4_B], MSTPCR2, 2, CLK_ENABLE_ON_INIT), + [MSTP201] = MSTP(&div4_clks[DIV4_B], MSTPCR2, 1, CLK_ENABLE_ON_INIT), + [MSTP200] = MSTP(&div4_clks[DIV4_B], MSTPCR2, 0, 0), +}; + +static struct clk_lookup lookups[] = { + /* main clocks */ + CLKDEV_CON_ID("rclk", &r_clk), + CLKDEV_CON_ID("extal", &extal_clk), + CLKDEV_CON_ID("dll_clk", &dll_clk), + CLKDEV_CON_ID("pll_clk", &pll_clk), + + /* DIV4 clocks */ + CLKDEV_CON_ID("cpu_clk", &div4_clks[DIV4_I]), + CLKDEV_CON_ID("umem_clk", &div4_clks[DIV4_U]), + CLKDEV_CON_ID("shyway_clk", &div4_clks[DIV4_SH]), + CLKDEV_CON_ID("bus_clk", &div4_clks[DIV4_B]), + CLKDEV_CON_ID("b3_clk", &div4_clks[DIV4_B3]), + CLKDEV_CON_ID("peripheral_clk", &div4_clks[DIV4_P]), + CLKDEV_CON_ID("siua_clk", &div4_clks[DIV4_SIUA]), + CLKDEV_CON_ID("siub_clk", &div4_clks[DIV4_SIUB]), + + /* DIV6 clocks */ + CLKDEV_CON_ID("video_clk", &div6_clks[DIV6_V]), + + /* MSTP32 clocks */ + CLKDEV_CON_ID("tlb0", &mstp_clks[MSTP031]), + CLKDEV_CON_ID("ic0", &mstp_clks[MSTP030]), + CLKDEV_CON_ID("oc0", &mstp_clks[MSTP029]), + CLKDEV_CON_ID("rsmem0", &mstp_clks[MSTP028]), + CLKDEV_CON_ID("xymem0", &mstp_clks[MSTP026]), + CLKDEV_CON_ID("intc3", &mstp_clks[MSTP023]), + CLKDEV_CON_ID("intc0", &mstp_clks[MSTP022]), + CLKDEV_CON_ID("dmac0", &mstp_clks[MSTP021]), + CLKDEV_CON_ID("sh0", &mstp_clks[MSTP020]), + CLKDEV_CON_ID("hudi0", &mstp_clks[MSTP019]), + CLKDEV_CON_ID("ubc0", &mstp_clks[MSTP017]), + CLKDEV_CON_ID("tmu_fck", &mstp_clks[MSTP015]), + CLKDEV_ICK_ID("fck", "sh-cmt-32.0", &mstp_clks[MSTP014]), + CLKDEV_CON_ID("rwdt0", &mstp_clks[MSTP013]), + CLKDEV_CON_ID("mfi0", &mstp_clks[MSTP011]), + CLKDEV_CON_ID("flctl0", &mstp_clks[MSTP010]), + + CLKDEV_ICK_ID("fck", "sh-sci.0", &mstp_clks[MSTP007]), + CLKDEV_ICK_ID("fck", "sh-sci.1", &mstp_clks[MSTP006]), + CLKDEV_ICK_ID("fck", "sh-sci.2", &mstp_clks[MSTP005]), + + CLKDEV_CON_ID("msiof0", &mstp_clks[MSTP002]), + CLKDEV_CON_ID("sbr0", &mstp_clks[MSTP001]), + CLKDEV_DEV_ID("i2c-sh_mobile.0", &mstp_clks[MSTP109]), + CLKDEV_CON_ID("icb0", &mstp_clks[MSTP227]), + CLKDEV_CON_ID("meram0", &mstp_clks[MSTP226]), + CLKDEV_CON_ID("dacy1", &mstp_clks[MSTP224]), + CLKDEV_CON_ID("dacy0", &mstp_clks[MSTP223]), + CLKDEV_CON_ID("tsif0", &mstp_clks[MSTP222]), + CLKDEV_CON_ID("sdhi0", &mstp_clks[MSTP218]), + CLKDEV_CON_ID("mmcif0", &mstp_clks[MSTP217]), + CLKDEV_CON_ID("usbf0", &mstp_clks[MSTP211]), + CLKDEV_CON_ID("veu1", &mstp_clks[MSTP207]), + CLKDEV_CON_ID("vou0", &mstp_clks[MSTP205]), + CLKDEV_CON_ID("beu0", &mstp_clks[MSTP204]), + CLKDEV_CON_ID("ceu0", &mstp_clks[MSTP203]), + CLKDEV_CON_ID("veu0", &mstp_clks[MSTP202]), + CLKDEV_CON_ID("vpu0", &mstp_clks[MSTP201]), + CLKDEV_CON_ID("lcdc0", &mstp_clks[MSTP200]), +}; + +int __init arch_clk_init(void) +{ + int k, ret = 0; + + /* autodetect extal or dll configuration */ + if (__raw_readl(PLLCR) & 0x1000) + pll_clk.parent = &dll_clk; + else + pll_clk.parent = &extal_clk; + + for (k = 0; !ret && (k < ARRAY_SIZE(main_clks)); k++) + ret = clk_register(main_clks[k]); + + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); + + if (!ret) + ret = sh_clk_div4_register(div4_clks, DIV4_NR, &div4_table); + + if (!ret) + ret = sh_clk_div6_register(div6_clks, DIV6_NR); + + if (!ret) + ret = sh_clk_mstp_register(mstp_clks, MSTP_NR); + + return ret; +} diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7722.c b/arch/sh/kernel/cpu/sh4a/clock-sh7722.c new file mode 100644 index 000000000..d85091ec4 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/clock-sh7722.c @@ -0,0 +1,265 @@ +/* + * arch/sh/kernel/cpu/sh4a/clock-sh7722.c + * + * SH7722 clock framework support + * + * Copyright (C) 2009 Magnus Damm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/clkdev.h> +#include <linux/sh_clk.h> +#include <asm/clock.h> +#include <cpu/sh7722.h> + +/* SH7722 registers */ +#define FRQCR 0xa4150000 +#define VCLKCR 0xa4150004 +#define SCLKACR 0xa4150008 +#define SCLKBCR 0xa415000c +#define IRDACLKCR 0xa4150018 +#define PLLCR 0xa4150024 +#define MSTPCR0 0xa4150030 +#define MSTPCR1 0xa4150034 +#define MSTPCR2 0xa4150038 +#define DLLFRQ 0xa4150050 + +/* Fixed 32 KHz root clock for RTC and Power Management purposes */ +static struct clk r_clk = { + .rate = 32768, +}; + +/* + * Default rate for the root input clock, reset this with clk_set_rate() + * from the platform code. + */ +struct clk extal_clk = { + .rate = 33333333, +}; + +/* The dll block multiplies the 32khz r_clk, may be used instead of extal */ +static unsigned long dll_recalc(struct clk *clk) +{ + unsigned long mult; + + if (__raw_readl(PLLCR) & 0x1000) + mult = __raw_readl(DLLFRQ); + else + mult = 0; + + return clk->parent->rate * mult; +} + +static struct sh_clk_ops dll_clk_ops = { + .recalc = dll_recalc, +}; + +static struct clk dll_clk = { + .ops = &dll_clk_ops, + .parent = &r_clk, + .flags = CLK_ENABLE_ON_INIT, +}; + +static unsigned long pll_recalc(struct clk *clk) +{ + unsigned long mult = 1; + unsigned long div = 1; + + if (__raw_readl(PLLCR) & 0x4000) + mult = (((__raw_readl(FRQCR) >> 24) & 0x1f) + 1); + else + div = 2; + + return (clk->parent->rate * mult) / div; +} + +static struct sh_clk_ops pll_clk_ops = { + .recalc = pll_recalc, +}; + +static struct clk pll_clk = { + .ops = &pll_clk_ops, + .flags = CLK_ENABLE_ON_INIT, +}; + +struct clk *main_clks[] = { + &r_clk, + &extal_clk, + &dll_clk, + &pll_clk, +}; + +static int multipliers[] = { 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; +static int divisors[] = { 1, 3, 2, 5, 3, 4, 5, 6, 8, 10, 12, 16, 20 }; + +static struct clk_div_mult_table div4_div_mult_table = { + .divisors = divisors, + .nr_divisors = ARRAY_SIZE(divisors), + .multipliers = multipliers, + .nr_multipliers = ARRAY_SIZE(multipliers), +}; + +static struct clk_div4_table div4_table = { + .div_mult_table = &div4_div_mult_table, +}; + +#define DIV4(_reg, _bit, _mask, _flags) \ + SH_CLK_DIV4(&pll_clk, _reg, _bit, _mask, _flags) + +enum { DIV4_I, DIV4_U, DIV4_SH, DIV4_B, DIV4_B3, DIV4_P, DIV4_NR }; + +struct clk div4_clks[DIV4_NR] = { + [DIV4_I] = DIV4(FRQCR, 20, 0x1fef, CLK_ENABLE_ON_INIT), + [DIV4_U] = DIV4(FRQCR, 16, 0x1fff, CLK_ENABLE_ON_INIT), + [DIV4_SH] = DIV4(FRQCR, 12, 0x1fff, CLK_ENABLE_ON_INIT), + [DIV4_B] = DIV4(FRQCR, 8, 0x1fff, CLK_ENABLE_ON_INIT), + [DIV4_B3] = DIV4(FRQCR, 4, 0x1fff, CLK_ENABLE_ON_INIT), + [DIV4_P] = DIV4(FRQCR, 0, 0x1fff, 0), +}; + +enum { DIV4_IRDA, DIV4_ENABLE_NR }; + +struct clk div4_enable_clks[DIV4_ENABLE_NR] = { + [DIV4_IRDA] = DIV4(IRDACLKCR, 0, 0x1fff, 0), +}; + +enum { DIV4_SIUA, DIV4_SIUB, DIV4_REPARENT_NR }; + +struct clk div4_reparent_clks[DIV4_REPARENT_NR] = { + [DIV4_SIUA] = DIV4(SCLKACR, 0, 0x1fff, 0), + [DIV4_SIUB] = DIV4(SCLKBCR, 0, 0x1fff, 0), +}; + +enum { DIV6_V, DIV6_NR }; + +struct clk div6_clks[DIV6_NR] = { + [DIV6_V] = SH_CLK_DIV6(&pll_clk, VCLKCR, 0), +}; + +static struct clk mstp_clks[HWBLK_NR] = { + [HWBLK_URAM] = SH_CLK_MSTP32(&div4_clks[DIV4_U], MSTPCR0, 28, CLK_ENABLE_ON_INIT), + [HWBLK_XYMEM] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR0, 26, CLK_ENABLE_ON_INIT), + [HWBLK_TMU] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 15, 0), + [HWBLK_CMT] = SH_CLK_MSTP32(&r_clk, MSTPCR0, 14, 0), + [HWBLK_RWDT] = SH_CLK_MSTP32(&r_clk, MSTPCR0, 13, 0), + [HWBLK_FLCTL] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 10, 0), + [HWBLK_SCIF0] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 7, 0), + [HWBLK_SCIF1] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 6, 0), + [HWBLK_SCIF2] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 5, 0), + + [HWBLK_IIC] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 9, 0), + [HWBLK_RTC] = SH_CLK_MSTP32(&r_clk, MSTPCR1, 8, 0), + + [HWBLK_SDHI] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR2, 18, 0), + [HWBLK_KEYSC] = SH_CLK_MSTP32(&r_clk, MSTPCR2, 14, 0), + [HWBLK_USBF] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR2, 11, 0), + [HWBLK_2DG] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 9, 0), + [HWBLK_SIU] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 8, 0), + [HWBLK_JPU] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 6, 0), + [HWBLK_VOU] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 5, 0), + [HWBLK_BEU] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 4, 0), + [HWBLK_CEU] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 3, 0), + [HWBLK_VEU] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 2, 0), + [HWBLK_VPU] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 1, 0), + [HWBLK_LCDC] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR2, 0, 0), +}; + +static struct clk_lookup lookups[] = { + /* main clocks */ + CLKDEV_CON_ID("rclk", &r_clk), + CLKDEV_CON_ID("extal", &extal_clk), + CLKDEV_CON_ID("dll_clk", &dll_clk), + CLKDEV_CON_ID("pll_clk", &pll_clk), + + /* DIV4 clocks */ + CLKDEV_CON_ID("cpu_clk", &div4_clks[DIV4_I]), + CLKDEV_CON_ID("umem_clk", &div4_clks[DIV4_U]), + CLKDEV_CON_ID("shyway_clk", &div4_clks[DIV4_SH]), + CLKDEV_CON_ID("bus_clk", &div4_clks[DIV4_B]), + CLKDEV_CON_ID("b3_clk", &div4_clks[DIV4_B3]), + CLKDEV_CON_ID("peripheral_clk", &div4_clks[DIV4_P]), + CLKDEV_CON_ID("irda_clk", &div4_enable_clks[DIV4_IRDA]), + CLKDEV_CON_ID("siua_clk", &div4_reparent_clks[DIV4_SIUA]), + CLKDEV_CON_ID("siub_clk", &div4_reparent_clks[DIV4_SIUB]), + + /* DIV6 clocks */ + CLKDEV_CON_ID("video_clk", &div6_clks[DIV6_V]), + + /* MSTP clocks */ + CLKDEV_CON_ID("uram0", &mstp_clks[HWBLK_URAM]), + CLKDEV_CON_ID("xymem0", &mstp_clks[HWBLK_XYMEM]), + + CLKDEV_ICK_ID("fck", "sh-tmu.0", &mstp_clks[HWBLK_TMU]), + + CLKDEV_ICK_ID("fck", "sh-cmt-32.0", &mstp_clks[HWBLK_CMT]), + CLKDEV_DEV_ID("sh-wdt.0", &mstp_clks[HWBLK_RWDT]), + CLKDEV_CON_ID("flctl0", &mstp_clks[HWBLK_FLCTL]), + + CLKDEV_DEV_ID("sh-sci.0", &mstp_clks[HWBLK_SCIF0]), + CLKDEV_DEV_ID("sh-sci.1", &mstp_clks[HWBLK_SCIF1]), + CLKDEV_DEV_ID("sh-sci.2", &mstp_clks[HWBLK_SCIF2]), + + CLKDEV_DEV_ID("i2c-sh_mobile.0", &mstp_clks[HWBLK_IIC]), + CLKDEV_CON_ID("rtc0", &mstp_clks[HWBLK_RTC]), + CLKDEV_DEV_ID("sh_mobile_sdhi.0", &mstp_clks[HWBLK_SDHI]), + CLKDEV_DEV_ID("sh_keysc.0", &mstp_clks[HWBLK_KEYSC]), + CLKDEV_CON_ID("usbf0", &mstp_clks[HWBLK_USBF]), + CLKDEV_CON_ID("2dg0", &mstp_clks[HWBLK_2DG]), + CLKDEV_DEV_ID("siu-pcm-audio", &mstp_clks[HWBLK_SIU]), + CLKDEV_DEV_ID("sh-vou.0", &mstp_clks[HWBLK_VOU]), + CLKDEV_CON_ID("jpu0", &mstp_clks[HWBLK_JPU]), + CLKDEV_CON_ID("beu0", &mstp_clks[HWBLK_BEU]), + CLKDEV_DEV_ID("renesas-ceu.0", &mstp_clks[HWBLK_CEU]), + CLKDEV_CON_ID("veu0", &mstp_clks[HWBLK_VEU]), + CLKDEV_CON_ID("vpu0", &mstp_clks[HWBLK_VPU]), + CLKDEV_DEV_ID("sh_mobile_lcdc_fb.0", &mstp_clks[HWBLK_LCDC]), +}; + +int __init arch_clk_init(void) +{ + int k, ret = 0; + + /* autodetect extal or dll configuration */ + if (__raw_readl(PLLCR) & 0x1000) + pll_clk.parent = &dll_clk; + else + pll_clk.parent = &extal_clk; + + for (k = 0; !ret && (k < ARRAY_SIZE(main_clks)); k++) + ret = clk_register(main_clks[k]); + + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); + + if (!ret) + ret = sh_clk_div4_register(div4_clks, DIV4_NR, &div4_table); + + if (!ret) + ret = sh_clk_div4_enable_register(div4_enable_clks, + DIV4_ENABLE_NR, &div4_table); + + if (!ret) + ret = sh_clk_div4_reparent_register(div4_reparent_clks, + DIV4_REPARENT_NR, &div4_table); + + if (!ret) + ret = sh_clk_div6_register(div6_clks, DIV6_NR); + + if (!ret) + ret = sh_clk_mstp_register(mstp_clks, HWBLK_NR); + + return ret; +} diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7723.c b/arch/sh/kernel/cpu/sh4a/clock-sh7723.c new file mode 100644 index 000000000..af01664f7 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/clock-sh7723.c @@ -0,0 +1,313 @@ +/* + * arch/sh/kernel/cpu/sh4a/clock-sh7723.c + * + * SH7723 clock framework support + * + * Copyright (C) 2009 Magnus Damm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/sh_clk.h> +#include <asm/clock.h> +#include <cpu/sh7723.h> + +/* SH7723 registers */ +#define FRQCR 0xa4150000 +#define VCLKCR 0xa4150004 +#define SCLKACR 0xa4150008 +#define SCLKBCR 0xa415000c +#define IRDACLKCR 0xa4150018 +#define PLLCR 0xa4150024 +#define MSTPCR0 0xa4150030 +#define MSTPCR1 0xa4150034 +#define MSTPCR2 0xa4150038 +#define DLLFRQ 0xa4150050 + +/* Fixed 32 KHz root clock for RTC and Power Management purposes */ +static struct clk r_clk = { + .rate = 32768, +}; + +/* + * Default rate for the root input clock, reset this with clk_set_rate() + * from the platform code. + */ +struct clk extal_clk = { + .rate = 33333333, +}; + +/* The dll multiplies the 32khz r_clk, may be used instead of extal */ +static unsigned long dll_recalc(struct clk *clk) +{ + unsigned long mult; + + if (__raw_readl(PLLCR) & 0x1000) + mult = __raw_readl(DLLFRQ); + else + mult = 0; + + return clk->parent->rate * mult; +} + +static struct sh_clk_ops dll_clk_ops = { + .recalc = dll_recalc, +}; + +static struct clk dll_clk = { + .ops = &dll_clk_ops, + .parent = &r_clk, + .flags = CLK_ENABLE_ON_INIT, +}; + +static unsigned long pll_recalc(struct clk *clk) +{ + unsigned long mult = 1; + unsigned long div = 1; + + if (__raw_readl(PLLCR) & 0x4000) + mult = (((__raw_readl(FRQCR) >> 24) & 0x1f) + 1); + else + div = 2; + + return (clk->parent->rate * mult) / div; +} + +static struct sh_clk_ops pll_clk_ops = { + .recalc = pll_recalc, +}; + +static struct clk pll_clk = { + .ops = &pll_clk_ops, + .flags = CLK_ENABLE_ON_INIT, +}; + +struct clk *main_clks[] = { + &r_clk, + &extal_clk, + &dll_clk, + &pll_clk, +}; + +static int multipliers[] = { 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; +static int divisors[] = { 1, 3, 2, 5, 3, 4, 5, 6, 8, 10, 12, 16, 20 }; + +static struct clk_div_mult_table div4_div_mult_table = { + .divisors = divisors, + .nr_divisors = ARRAY_SIZE(divisors), + .multipliers = multipliers, + .nr_multipliers = ARRAY_SIZE(multipliers), +}; + +static struct clk_div4_table div4_table = { + .div_mult_table = &div4_div_mult_table, +}; + +enum { DIV4_I, DIV4_U, DIV4_SH, DIV4_B, DIV4_B3, DIV4_P, DIV4_NR }; + +#define DIV4(_reg, _bit, _mask, _flags) \ + SH_CLK_DIV4(&pll_clk, _reg, _bit, _mask, _flags) + +struct clk div4_clks[DIV4_NR] = { + [DIV4_I] = DIV4(FRQCR, 20, 0x0dbf, CLK_ENABLE_ON_INIT), + [DIV4_U] = DIV4(FRQCR, 16, 0x0dbf, CLK_ENABLE_ON_INIT), + [DIV4_SH] = DIV4(FRQCR, 12, 0x0dbf, CLK_ENABLE_ON_INIT), + [DIV4_B] = DIV4(FRQCR, 8, 0x0dbf, CLK_ENABLE_ON_INIT), + [DIV4_B3] = DIV4(FRQCR, 4, 0x0db4, CLK_ENABLE_ON_INIT), + [DIV4_P] = DIV4(FRQCR, 0, 0x0dbf, 0), +}; + +enum { DIV4_IRDA, DIV4_ENABLE_NR }; + +struct clk div4_enable_clks[DIV4_ENABLE_NR] = { + [DIV4_IRDA] = DIV4(IRDACLKCR, 0, 0x0dbf, 0), +}; + +enum { DIV4_SIUA, DIV4_SIUB, DIV4_REPARENT_NR }; + +struct clk div4_reparent_clks[DIV4_REPARENT_NR] = { + [DIV4_SIUA] = DIV4(SCLKACR, 0, 0x0dbf, 0), + [DIV4_SIUB] = DIV4(SCLKBCR, 0, 0x0dbf, 0), +}; +enum { DIV6_V, DIV6_NR }; + +struct clk div6_clks[DIV6_NR] = { + [DIV6_V] = SH_CLK_DIV6(&pll_clk, VCLKCR, 0), +}; + +static struct clk mstp_clks[] = { + /* See page 60 of Datasheet V1.0: Overview -> Block Diagram */ + [HWBLK_TLB] = SH_CLK_MSTP32(&div4_clks[DIV4_I], MSTPCR0, 31, CLK_ENABLE_ON_INIT), + [HWBLK_IC] = SH_CLK_MSTP32(&div4_clks[DIV4_I], MSTPCR0, 30, CLK_ENABLE_ON_INIT), + [HWBLK_OC] = SH_CLK_MSTP32(&div4_clks[DIV4_I], MSTPCR0, 29, CLK_ENABLE_ON_INIT), + [HWBLK_L2C] = SH_CLK_MSTP32(&div4_clks[DIV4_SH], MSTPCR0, 28, CLK_ENABLE_ON_INIT), + [HWBLK_ILMEM] = SH_CLK_MSTP32(&div4_clks[DIV4_I], MSTPCR0, 27, CLK_ENABLE_ON_INIT), + [HWBLK_FPU] = SH_CLK_MSTP32(&div4_clks[DIV4_I], MSTPCR0, 24, CLK_ENABLE_ON_INIT), + [HWBLK_INTC] = SH_CLK_MSTP32(&div4_clks[DIV4_I], MSTPCR0, 22, CLK_ENABLE_ON_INIT), + [HWBLK_DMAC0] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR0, 21, 0), + [HWBLK_SHYWAY] = SH_CLK_MSTP32(&div4_clks[DIV4_SH], MSTPCR0, 20, CLK_ENABLE_ON_INIT), + [HWBLK_HUDI] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 19, 0), + [HWBLK_UBC] = SH_CLK_MSTP32(&div4_clks[DIV4_I], MSTPCR0, 17, 0), + [HWBLK_TMU0] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 15, 0), + [HWBLK_CMT] = SH_CLK_MSTP32(&r_clk, MSTPCR0, 14, 0), + [HWBLK_RWDT] = SH_CLK_MSTP32(&r_clk, MSTPCR0, 13, 0), + [HWBLK_DMAC1] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR0, 12, 0), + [HWBLK_TMU1] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 11, 0), + [HWBLK_FLCTL] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 10, 0), + [HWBLK_SCIF0] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 9, 0), + [HWBLK_SCIF1] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 8, 0), + [HWBLK_SCIF2] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 7, 0), + [HWBLK_SCIF3] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR0, 6, 0), + [HWBLK_SCIF4] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR0, 5, 0), + [HWBLK_SCIF5] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR0, 4, 0), + [HWBLK_MSIOF0] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR0, 2, 0), + [HWBLK_MSIOF1] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR0, 1, 0), + [HWBLK_MERAM] = SH_CLK_MSTP32(&div4_clks[DIV4_SH], MSTPCR0, 0, 0), + + [HWBLK_IIC] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 9, 0), + [HWBLK_RTC] = SH_CLK_MSTP32(&r_clk, MSTPCR1, 8, 0), + + [HWBLK_ATAPI] = SH_CLK_MSTP32(&div4_clks[DIV4_SH], MSTPCR2, 28, 0), + [HWBLK_ADC] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR2, 27, 0), + [HWBLK_TPU] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 25, 0), + [HWBLK_IRDA] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR2, 24, 0), + [HWBLK_TSIF] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 22, 0), + [HWBLK_ICB] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 21, CLK_ENABLE_ON_INIT), + [HWBLK_SDHI0] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 18, 0), + [HWBLK_SDHI1] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 17, 0), + [HWBLK_KEYSC] = SH_CLK_MSTP32(&r_clk, MSTPCR2, 14, 0), + [HWBLK_USB] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 11, 0), + [HWBLK_2DG] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 10, 0), + [HWBLK_SIU] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 8, 0), + [HWBLK_VEU2H1] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 6, 0), + [HWBLK_VOU] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 5, 0), + [HWBLK_BEU] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 4, 0), + [HWBLK_CEU] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 3, 0), + [HWBLK_VEU2H0] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 2, 0), + [HWBLK_VPU] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 1, 0), + [HWBLK_LCDC] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 0, 0), +}; + +static struct clk_lookup lookups[] = { + /* main clocks */ + CLKDEV_CON_ID("rclk", &r_clk), + CLKDEV_CON_ID("extal", &extal_clk), + CLKDEV_CON_ID("dll_clk", &dll_clk), + CLKDEV_CON_ID("pll_clk", &pll_clk), + + /* DIV4 clocks */ + CLKDEV_CON_ID("cpu_clk", &div4_clks[DIV4_I]), + CLKDEV_CON_ID("umem_clk", &div4_clks[DIV4_U]), + CLKDEV_CON_ID("shyway_clk", &div4_clks[DIV4_SH]), + CLKDEV_CON_ID("bus_clk", &div4_clks[DIV4_B]), + CLKDEV_CON_ID("b3_clk", &div4_clks[DIV4_B3]), + CLKDEV_CON_ID("peripheral_clk", &div4_clks[DIV4_P]), + CLKDEV_CON_ID("irda_clk", &div4_enable_clks[DIV4_IRDA]), + CLKDEV_CON_ID("siua_clk", &div4_reparent_clks[DIV4_SIUA]), + CLKDEV_CON_ID("siub_clk", &div4_reparent_clks[DIV4_SIUB]), + + /* DIV6 clocks */ + CLKDEV_CON_ID("video_clk", &div6_clks[DIV6_V]), + + /* MSTP clocks */ + CLKDEV_CON_ID("tlb0", &mstp_clks[HWBLK_TLB]), + CLKDEV_CON_ID("ic0", &mstp_clks[HWBLK_IC]), + CLKDEV_CON_ID("oc0", &mstp_clks[HWBLK_OC]), + CLKDEV_CON_ID("l2c0", &mstp_clks[HWBLK_L2C]), + CLKDEV_CON_ID("ilmem0", &mstp_clks[HWBLK_ILMEM]), + CLKDEV_CON_ID("fpu0", &mstp_clks[HWBLK_FPU]), + CLKDEV_CON_ID("intc0", &mstp_clks[HWBLK_INTC]), + CLKDEV_DEV_ID("sh-dma-engine.0", &mstp_clks[HWBLK_DMAC0]), + CLKDEV_CON_ID("sh0", &mstp_clks[HWBLK_SHYWAY]), + CLKDEV_CON_ID("hudi0", &mstp_clks[HWBLK_HUDI]), + CLKDEV_CON_ID("ubc0", &mstp_clks[HWBLK_UBC]), + CLKDEV_ICK_ID("fck", "sh-cmt-32.0", &mstp_clks[HWBLK_CMT]), + CLKDEV_DEV_ID("sh-wdt.0", &mstp_clks[HWBLK_RWDT]), + CLKDEV_DEV_ID("sh-dma-engine.1", &mstp_clks[HWBLK_DMAC1]), + CLKDEV_CON_ID("flctl0", &mstp_clks[HWBLK_FLCTL]), + CLKDEV_DEV_ID("spi_sh_msiof.0", &mstp_clks[HWBLK_MSIOF0]), + CLKDEV_DEV_ID("spi_sh_msiof.1", &mstp_clks[HWBLK_MSIOF1]), + CLKDEV_DEV_ID("sh_mobile_meram.0", &mstp_clks[HWBLK_MERAM]), + CLKDEV_DEV_ID("i2c-sh_mobile.0", &mstp_clks[HWBLK_IIC]), + CLKDEV_CON_ID("rtc0", &mstp_clks[HWBLK_RTC]), + CLKDEV_CON_ID("atapi0", &mstp_clks[HWBLK_ATAPI]), + CLKDEV_CON_ID("adc0", &mstp_clks[HWBLK_ADC]), + CLKDEV_CON_ID("tpu0", &mstp_clks[HWBLK_TPU]), + CLKDEV_CON_ID("irda0", &mstp_clks[HWBLK_IRDA]), + CLKDEV_CON_ID("tsif0", &mstp_clks[HWBLK_TSIF]), + CLKDEV_CON_ID("icb0", &mstp_clks[HWBLK_ICB]), + CLKDEV_DEV_ID("sh_mobile_sdhi.0", &mstp_clks[HWBLK_SDHI0]), + CLKDEV_DEV_ID("sh_mobile_sdhi.1", &mstp_clks[HWBLK_SDHI1]), + CLKDEV_DEV_ID("sh_keysc.0", &mstp_clks[HWBLK_KEYSC]), + CLKDEV_CON_ID("usb0", &mstp_clks[HWBLK_USB]), + CLKDEV_CON_ID("2dg0", &mstp_clks[HWBLK_2DG]), + CLKDEV_DEV_ID("siu-pcm-audio", &mstp_clks[HWBLK_SIU]), + CLKDEV_CON_ID("veu1", &mstp_clks[HWBLK_VEU2H1]), + CLKDEV_DEV_ID("sh-vou.0", &mstp_clks[HWBLK_VOU]), + CLKDEV_CON_ID("beu0", &mstp_clks[HWBLK_BEU]), + CLKDEV_DEV_ID("ceu.0", &mstp_clks[HWBLK_CEU]), + CLKDEV_CON_ID("veu0", &mstp_clks[HWBLK_VEU2H0]), + CLKDEV_CON_ID("vpu0", &mstp_clks[HWBLK_VPU]), + + CLKDEV_ICK_ID("fck", "sh-tmu.0", &mstp_clks[HWBLK_TMU0]), + CLKDEV_ICK_ID("fck", "sh-tmu.1", &mstp_clks[HWBLK_TMU1]), + + CLKDEV_ICK_ID("fck", "sh-sci.0", &mstp_clks[HWBLK_SCIF0]), + CLKDEV_ICK_ID("fck", "sh-sci.1", &mstp_clks[HWBLK_SCIF1]), + CLKDEV_ICK_ID("fck", "sh-sci.2", &mstp_clks[HWBLK_SCIF2]), + CLKDEV_ICK_ID("fck", "sh-sci.3", &mstp_clks[HWBLK_SCIF3]), + CLKDEV_ICK_ID("fck", "sh-sci.4", &mstp_clks[HWBLK_SCIF4]), + CLKDEV_ICK_ID("fck", "sh-sci.5", &mstp_clks[HWBLK_SCIF5]), + + CLKDEV_DEV_ID("sh_mobile_lcdc_fb.0", &mstp_clks[HWBLK_LCDC]), +}; + +int __init arch_clk_init(void) +{ + int k, ret = 0; + + /* autodetect extal or dll configuration */ + if (__raw_readl(PLLCR) & 0x1000) + pll_clk.parent = &dll_clk; + else + pll_clk.parent = &extal_clk; + + for (k = 0; !ret && (k < ARRAY_SIZE(main_clks)); k++) + ret |= clk_register(main_clks[k]); + + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); + + if (!ret) + ret = sh_clk_div4_register(div4_clks, DIV4_NR, &div4_table); + + if (!ret) + ret = sh_clk_div4_enable_register(div4_enable_clks, + DIV4_ENABLE_NR, &div4_table); + + if (!ret) + ret = sh_clk_div4_reparent_register(div4_reparent_clks, + DIV4_REPARENT_NR, &div4_table); + + if (!ret) + ret = sh_clk_div6_register(div6_clks, DIV6_NR); + + if (!ret) + ret = sh_clk_mstp_register(mstp_clks, HWBLK_NR); + + return ret; +} diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7724.c b/arch/sh/kernel/cpu/sh4a/clock-sh7724.c new file mode 100644 index 000000000..3194336a3 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/clock-sh7724.c @@ -0,0 +1,379 @@ +/* + * arch/sh/kernel/cpu/sh4a/clock-sh7724.c + * + * SH7724 clock framework support + * + * Copyright (C) 2009 Magnus Damm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/sh_clk.h> +#include <asm/clock.h> +#include <cpu/sh7724.h> + +/* SH7724 registers */ +#define FRQCRA 0xa4150000 +#define FRQCRB 0xa4150004 +#define VCLKCR 0xa4150048 +#define FCLKACR 0xa4150008 +#define FCLKBCR 0xa415000c +#define IRDACLKCR 0xa4150018 +#define PLLCR 0xa4150024 +#define MSTPCR0 0xa4150030 +#define MSTPCR1 0xa4150034 +#define MSTPCR2 0xa4150038 +#define SPUCLKCR 0xa415003c +#define FLLFRQ 0xa4150050 +#define LSTATS 0xa4150060 + +/* Fixed 32 KHz root clock for RTC and Power Management purposes */ +static struct clk r_clk = { + .rate = 32768, +}; + +/* + * Default rate for the root input clock, reset this with clk_set_rate() + * from the platform code. + */ +static struct clk extal_clk = { + .rate = 33333333, +}; + +/* The fll multiplies the 32khz r_clk, may be used instead of extal */ +static unsigned long fll_recalc(struct clk *clk) +{ + unsigned long mult = 0; + unsigned long div = 1; + + if (__raw_readl(PLLCR) & 0x1000) + mult = __raw_readl(FLLFRQ) & 0x3ff; + + if (__raw_readl(FLLFRQ) & 0x4000) + div = 2; + + return (clk->parent->rate * mult) / div; +} + +static struct sh_clk_ops fll_clk_ops = { + .recalc = fll_recalc, +}; + +static struct clk fll_clk = { + .ops = &fll_clk_ops, + .parent = &r_clk, + .flags = CLK_ENABLE_ON_INIT, +}; + +static unsigned long pll_recalc(struct clk *clk) +{ + unsigned long mult = 1; + + if (__raw_readl(PLLCR) & 0x4000) + mult = (((__raw_readl(FRQCRA) >> 24) & 0x3f) + 1) * 2; + + return clk->parent->rate * mult; +} + +static struct sh_clk_ops pll_clk_ops = { + .recalc = pll_recalc, +}; + +static struct clk pll_clk = { + .ops = &pll_clk_ops, + .flags = CLK_ENABLE_ON_INIT, +}; + +/* A fixed divide-by-3 block use by the div6 clocks */ +static unsigned long div3_recalc(struct clk *clk) +{ + return clk->parent->rate / 3; +} + +static struct sh_clk_ops div3_clk_ops = { + .recalc = div3_recalc, +}; + +static struct clk div3_clk = { + .ops = &div3_clk_ops, + .parent = &pll_clk, +}; + +/* External input clock (pin name: FSIMCKA/FSIMCKB/DV_CLKI ) */ +struct clk sh7724_fsimcka_clk = { +}; + +struct clk sh7724_fsimckb_clk = { +}; + +struct clk sh7724_dv_clki = { +}; + +static struct clk *main_clks[] = { + &r_clk, + &extal_clk, + &fll_clk, + &pll_clk, + &div3_clk, + &sh7724_fsimcka_clk, + &sh7724_fsimckb_clk, + &sh7724_dv_clki, +}; + +static void div4_kick(struct clk *clk) +{ + unsigned long value; + + /* set KICK bit in FRQCRA to update hardware setting */ + value = __raw_readl(FRQCRA); + value |= (1 << 31); + __raw_writel(value, FRQCRA); +} + +static int divisors[] = { 2, 3, 4, 6, 8, 12, 16, 0, 24, 32, 36, 48, 0, 72 }; + +static struct clk_div_mult_table div4_div_mult_table = { + .divisors = divisors, + .nr_divisors = ARRAY_SIZE(divisors), +}; + +static struct clk_div4_table div4_table = { + .div_mult_table = &div4_div_mult_table, + .kick = div4_kick, +}; + +enum { DIV4_I, DIV4_SH, DIV4_B, DIV4_P, DIV4_M1, DIV4_NR }; + +#define DIV4(_reg, _bit, _mask, _flags) \ + SH_CLK_DIV4(&pll_clk, _reg, _bit, _mask, _flags) + +struct clk div4_clks[DIV4_NR] = { + [DIV4_I] = DIV4(FRQCRA, 20, 0x2f7d, CLK_ENABLE_ON_INIT), + [DIV4_SH] = DIV4(FRQCRA, 12, 0x2f7c, CLK_ENABLE_ON_INIT), + [DIV4_B] = DIV4(FRQCRA, 8, 0x2f7c, CLK_ENABLE_ON_INIT), + [DIV4_P] = DIV4(FRQCRA, 0, 0x2f7c, 0), + [DIV4_M1] = DIV4(FRQCRB, 4, 0x2f7c, CLK_ENABLE_ON_INIT), +}; + +enum { DIV6_V, DIV6_I, DIV6_S, DIV6_FA, DIV6_FB, DIV6_NR }; + +/* Indices are important - they are the actual src selecting values */ +static struct clk *common_parent[] = { + [0] = &div3_clk, + [1] = NULL, +}; + +static struct clk *vclkcr_parent[8] = { + [0] = &div3_clk, + [2] = &sh7724_dv_clki, + [4] = &extal_clk, +}; + +static struct clk *fclkacr_parent[] = { + [0] = &div3_clk, + [1] = NULL, + [2] = &sh7724_fsimcka_clk, + [3] = NULL, +}; + +static struct clk *fclkbcr_parent[] = { + [0] = &div3_clk, + [1] = NULL, + [2] = &sh7724_fsimckb_clk, + [3] = NULL, +}; + +static struct clk div6_clks[DIV6_NR] = { + [DIV6_V] = SH_CLK_DIV6_EXT(VCLKCR, 0, + vclkcr_parent, ARRAY_SIZE(vclkcr_parent), 12, 3), + [DIV6_I] = SH_CLK_DIV6_EXT(IRDACLKCR, 0, + common_parent, ARRAY_SIZE(common_parent), 6, 1), + [DIV6_S] = SH_CLK_DIV6_EXT(SPUCLKCR, CLK_ENABLE_ON_INIT, + common_parent, ARRAY_SIZE(common_parent), 6, 1), + [DIV6_FA] = SH_CLK_DIV6_EXT(FCLKACR, 0, + fclkacr_parent, ARRAY_SIZE(fclkacr_parent), 6, 2), + [DIV6_FB] = SH_CLK_DIV6_EXT(FCLKBCR, 0, + fclkbcr_parent, ARRAY_SIZE(fclkbcr_parent), 6, 2), +}; + +static struct clk mstp_clks[HWBLK_NR] = { + [HWBLK_TLB] = SH_CLK_MSTP32(&div4_clks[DIV4_I], MSTPCR0, 31, CLK_ENABLE_ON_INIT), + [HWBLK_IC] = SH_CLK_MSTP32(&div4_clks[DIV4_I], MSTPCR0, 30, CLK_ENABLE_ON_INIT), + [HWBLK_OC] = SH_CLK_MSTP32(&div4_clks[DIV4_I], MSTPCR0, 29, CLK_ENABLE_ON_INIT), + [HWBLK_RSMEM] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR0, 28, CLK_ENABLE_ON_INIT), + [HWBLK_ILMEM] = SH_CLK_MSTP32(&div4_clks[DIV4_I], MSTPCR0, 27, CLK_ENABLE_ON_INIT), + [HWBLK_L2C] = SH_CLK_MSTP32(&div4_clks[DIV4_SH], MSTPCR0, 26, CLK_ENABLE_ON_INIT), + [HWBLK_FPU] = SH_CLK_MSTP32(&div4_clks[DIV4_I], MSTPCR0, 24, CLK_ENABLE_ON_INIT), + [HWBLK_INTC] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 22, CLK_ENABLE_ON_INIT), + [HWBLK_DMAC0] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR0, 21, 0), + [HWBLK_SHYWAY] = SH_CLK_MSTP32(&div4_clks[DIV4_SH], MSTPCR0, 20, CLK_ENABLE_ON_INIT), + [HWBLK_HUDI] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 19, 0), + [HWBLK_UBC] = SH_CLK_MSTP32(&div4_clks[DIV4_I], MSTPCR0, 17, 0), + [HWBLK_TMU0] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 15, 0), + [HWBLK_CMT] = SH_CLK_MSTP32(&r_clk, MSTPCR0, 14, 0), + [HWBLK_RWDT] = SH_CLK_MSTP32(&r_clk, MSTPCR0, 13, 0), + [HWBLK_DMAC1] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR0, 12, 0), + [HWBLK_TMU1] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 10, 0), + [HWBLK_SCIF0] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 9, 0), + [HWBLK_SCIF1] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 8, 0), + [HWBLK_SCIF2] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 7, 0), + [HWBLK_SCIF3] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR0, 6, 0), + [HWBLK_SCIF4] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR0, 5, 0), + [HWBLK_SCIF5] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR0, 4, 0), + [HWBLK_MSIOF0] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR0, 2, 0), + [HWBLK_MSIOF1] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR0, 1, 0), + + [HWBLK_KEYSC] = SH_CLK_MSTP32(&r_clk, MSTPCR1, 12, 0), + [HWBLK_RTC] = SH_CLK_MSTP32(&r_clk, MSTPCR1, 11, 0), + [HWBLK_IIC0] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 9, 0), + [HWBLK_IIC1] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 8, 0), + + [HWBLK_MMC] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 29, 0), + [HWBLK_ETHER] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 28, 0), + [HWBLK_ATAPI] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 26, 0), + [HWBLK_TPU] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 25, 0), + [HWBLK_IRDA] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR2, 24, 0), + [HWBLK_TSIF] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 22, 0), + [HWBLK_USB1] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 21, 0), + [HWBLK_USB0] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 20, 0), + [HWBLK_2DG] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 19, 0), + [HWBLK_SDHI0] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 18, 0), + [HWBLK_SDHI1] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 17, 0), + [HWBLK_VEU1] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 15, 0), + [HWBLK_CEU1] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 13, 0), + [HWBLK_BEU1] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 12, 0), + [HWBLK_2DDMAC] = SH_CLK_MSTP32(&div4_clks[DIV4_SH], MSTPCR2, 10, 0), + [HWBLK_SPU] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 9, 0), + [HWBLK_JPU] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 6, 0), + [HWBLK_VOU] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 5, 0), + [HWBLK_BEU0] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 4, 0), + [HWBLK_CEU0] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 3, 0), + [HWBLK_VEU0] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 2, 0), + [HWBLK_VPU] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 1, 0), + [HWBLK_LCDC] = SH_CLK_MSTP32(&div4_clks[DIV4_B], MSTPCR2, 0, 0), +}; + +static struct clk_lookup lookups[] = { + /* main clocks */ + CLKDEV_CON_ID("rclk", &r_clk), + CLKDEV_CON_ID("extal", &extal_clk), + CLKDEV_CON_ID("fll_clk", &fll_clk), + CLKDEV_CON_ID("pll_clk", &pll_clk), + CLKDEV_CON_ID("div3_clk", &div3_clk), + + /* DIV4 clocks */ + CLKDEV_CON_ID("cpu_clk", &div4_clks[DIV4_I]), + CLKDEV_CON_ID("shyway_clk", &div4_clks[DIV4_SH]), + CLKDEV_CON_ID("bus_clk", &div4_clks[DIV4_B]), + CLKDEV_CON_ID("peripheral_clk", &div4_clks[DIV4_P]), + CLKDEV_CON_ID("vpu_clk", &div4_clks[DIV4_M1]), + + /* DIV6 clocks */ + CLKDEV_CON_ID("video_clk", &div6_clks[DIV6_V]), + CLKDEV_CON_ID("fsia_clk", &div6_clks[DIV6_FA]), + CLKDEV_CON_ID("fsib_clk", &div6_clks[DIV6_FB]), + CLKDEV_CON_ID("irda_clk", &div6_clks[DIV6_I]), + CLKDEV_CON_ID("spu_clk", &div6_clks[DIV6_S]), + + /* MSTP clocks */ + CLKDEV_CON_ID("tlb0", &mstp_clks[HWBLK_TLB]), + CLKDEV_CON_ID("ic0", &mstp_clks[HWBLK_IC]), + CLKDEV_CON_ID("oc0", &mstp_clks[HWBLK_OC]), + CLKDEV_CON_ID("rs0", &mstp_clks[HWBLK_RSMEM]), + CLKDEV_CON_ID("ilmem0", &mstp_clks[HWBLK_ILMEM]), + CLKDEV_CON_ID("l2c0", &mstp_clks[HWBLK_L2C]), + CLKDEV_CON_ID("fpu0", &mstp_clks[HWBLK_FPU]), + CLKDEV_CON_ID("intc0", &mstp_clks[HWBLK_INTC]), + CLKDEV_DEV_ID("sh-dma-engine.0", &mstp_clks[HWBLK_DMAC0]), + CLKDEV_CON_ID("sh0", &mstp_clks[HWBLK_SHYWAY]), + CLKDEV_CON_ID("hudi0", &mstp_clks[HWBLK_HUDI]), + CLKDEV_CON_ID("ubc0", &mstp_clks[HWBLK_UBC]), + + CLKDEV_ICK_ID("fck", "sh-tmu.0", &mstp_clks[HWBLK_TMU0]), + CLKDEV_ICK_ID("fck", "sh-tmu.1", &mstp_clks[HWBLK_TMU1]), + + CLKDEV_ICK_ID("fck", "sh-cmt-32.0", &mstp_clks[HWBLK_CMT]), + CLKDEV_DEV_ID("sh-wdt.0", &mstp_clks[HWBLK_RWDT]), + CLKDEV_DEV_ID("sh-dma-engine.1", &mstp_clks[HWBLK_DMAC1]), + + CLKDEV_DEV_ID("sh-sci.0", &mstp_clks[HWBLK_SCIF0]), + CLKDEV_DEV_ID("sh-sci.1", &mstp_clks[HWBLK_SCIF1]), + CLKDEV_DEV_ID("sh-sci.2", &mstp_clks[HWBLK_SCIF2]), + CLKDEV_DEV_ID("sh-sci.3", &mstp_clks[HWBLK_SCIF3]), + CLKDEV_DEV_ID("sh-sci.4", &mstp_clks[HWBLK_SCIF4]), + CLKDEV_DEV_ID("sh-sci.5", &mstp_clks[HWBLK_SCIF5]), + + CLKDEV_DEV_ID("spi_sh_msiof.0", &mstp_clks[HWBLK_MSIOF0]), + CLKDEV_DEV_ID("spi_sh_msiof.1", &mstp_clks[HWBLK_MSIOF1]), + CLKDEV_DEV_ID("sh_keysc.0", &mstp_clks[HWBLK_KEYSC]), + CLKDEV_CON_ID("rtc0", &mstp_clks[HWBLK_RTC]), + CLKDEV_DEV_ID("i2c-sh_mobile.0", &mstp_clks[HWBLK_IIC0]), + CLKDEV_DEV_ID("i2c-sh_mobile.1", &mstp_clks[HWBLK_IIC1]), + CLKDEV_DEV_ID("sh_mmcif.0", &mstp_clks[HWBLK_MMC]), + CLKDEV_DEV_ID("sh7724-ether.0", &mstp_clks[HWBLK_ETHER]), + CLKDEV_CON_ID("atapi0", &mstp_clks[HWBLK_ATAPI]), + CLKDEV_CON_ID("tpu0", &mstp_clks[HWBLK_TPU]), + CLKDEV_CON_ID("irda0", &mstp_clks[HWBLK_IRDA]), + CLKDEV_CON_ID("tsif0", &mstp_clks[HWBLK_TSIF]), + CLKDEV_DEV_ID("renesas_usbhs.1", &mstp_clks[HWBLK_USB1]), + CLKDEV_DEV_ID("renesas_usbhs.0", &mstp_clks[HWBLK_USB0]), + CLKDEV_CON_ID("usb1", &mstp_clks[HWBLK_USB1]), + CLKDEV_CON_ID("usb0", &mstp_clks[HWBLK_USB0]), + CLKDEV_CON_ID("2dg0", &mstp_clks[HWBLK_2DG]), + CLKDEV_DEV_ID("sh_mobile_sdhi.0", &mstp_clks[HWBLK_SDHI0]), + CLKDEV_DEV_ID("sh_mobile_sdhi.1", &mstp_clks[HWBLK_SDHI1]), + CLKDEV_CON_ID("veu1", &mstp_clks[HWBLK_VEU1]), + CLKDEV_DEV_ID("renesas-ceu.1", &mstp_clks[HWBLK_CEU1]), + CLKDEV_CON_ID("beu1", &mstp_clks[HWBLK_BEU1]), + CLKDEV_CON_ID("2ddmac0", &mstp_clks[HWBLK_2DDMAC]), + CLKDEV_DEV_ID("sh_fsi.0", &mstp_clks[HWBLK_SPU]), + CLKDEV_CON_ID("jpu0", &mstp_clks[HWBLK_JPU]), + CLKDEV_DEV_ID("sh-vou", &mstp_clks[HWBLK_VOU]), + CLKDEV_CON_ID("beu0", &mstp_clks[HWBLK_BEU0]), + CLKDEV_DEV_ID("renesas-ceu.0", &mstp_clks[HWBLK_CEU0]), + CLKDEV_CON_ID("veu0", &mstp_clks[HWBLK_VEU0]), + CLKDEV_CON_ID("vpu0", &mstp_clks[HWBLK_VPU]), + CLKDEV_DEV_ID("sh_mobile_lcdc_fb.0", &mstp_clks[HWBLK_LCDC]), +}; + +int __init arch_clk_init(void) +{ + int k, ret = 0; + + /* autodetect extal or fll configuration */ + if (__raw_readl(PLLCR) & 0x1000) + pll_clk.parent = &fll_clk; + else + pll_clk.parent = &extal_clk; + + for (k = 0; !ret && (k < ARRAY_SIZE(main_clks)); k++) + ret = clk_register(main_clks[k]); + + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); + + if (!ret) + ret = sh_clk_div4_register(div4_clks, DIV4_NR, &div4_table); + + if (!ret) + ret = sh_clk_div6_reparent_register(div6_clks, DIV6_NR); + + if (!ret) + ret = sh_clk_mstp_register(mstp_clks, HWBLK_NR); + + return ret; +} diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7734.c b/arch/sh/kernel/cpu/sh4a/clock-sh7734.c new file mode 100644 index 000000000..354dcac5e --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/clock-sh7734.c @@ -0,0 +1,259 @@ +/* + * arch/sh/kernel/cpu/sh4a/clock-sh7734.c + * + * Clock framework for SH7734 + * + * Copyright (C) 2011, 2012 Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com> + * Copyright (C) 2011, 2012 Renesas Solutions Corp. + * + * 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/io.h> +#include <linux/clkdev.h> +#include <linux/delay.h> +#include <asm/clock.h> +#include <asm/freq.h> + +static struct clk extal_clk = { + .rate = 33333333, +}; + +#define MODEMR (0xFFCC0020) +#define MODEMR_MASK (0x6) +#define MODEMR_533MHZ (0x2) + +static unsigned long pll_recalc(struct clk *clk) +{ + int mode = 12; + u32 r = __raw_readl(MODEMR); + + if ((r & MODEMR_MASK) & MODEMR_533MHZ) + mode = 16; + + return clk->parent->rate * mode; +} + +static struct sh_clk_ops pll_clk_ops = { + .recalc = pll_recalc, +}; + +static struct clk pll_clk = { + .ops = &pll_clk_ops, + .parent = &extal_clk, + .flags = CLK_ENABLE_ON_INIT, +}; + +static struct clk *main_clks[] = { + &extal_clk, + &pll_clk, +}; + +static int multipliers[] = { 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; +static int divisors[] = { 1, 3, 2, 3, 4, 6, 8, 9, 12, 16, 18, 24 }; + +static struct clk_div_mult_table div4_div_mult_table = { + .divisors = divisors, + .nr_divisors = ARRAY_SIZE(divisors), + .multipliers = multipliers, + .nr_multipliers = ARRAY_SIZE(multipliers), +}; + +static struct clk_div4_table div4_table = { + .div_mult_table = &div4_div_mult_table, +}; + +enum { DIV4_I, DIV4_S, DIV4_B, DIV4_M, DIV4_S1, DIV4_P, DIV4_NR }; + +#define DIV4(_reg, _bit, _mask, _flags) \ + SH_CLK_DIV4(&pll_clk, _reg, _bit, _mask, _flags) + +struct clk div4_clks[DIV4_NR] = { + [DIV4_I] = DIV4(FRQMR1, 28, 0x0003, CLK_ENABLE_ON_INIT), + [DIV4_S] = DIV4(FRQMR1, 20, 0x000C, CLK_ENABLE_ON_INIT), + [DIV4_B] = DIV4(FRQMR1, 16, 0x0140, CLK_ENABLE_ON_INIT), + [DIV4_M] = DIV4(FRQMR1, 12, 0x0004, CLK_ENABLE_ON_INIT), + [DIV4_S1] = DIV4(FRQMR1, 4, 0x0030, CLK_ENABLE_ON_INIT), + [DIV4_P] = DIV4(FRQMR1, 0, 0x0140, CLK_ENABLE_ON_INIT), +}; + +#define MSTPCR0 0xFFC80030 +#define MSTPCR1 0xFFC80034 +#define MSTPCR3 0xFFC8003C + +enum { + MSTP030, MSTP029, /* IIC */ + MSTP026, MSTP025, MSTP024, /* SCIF */ + MSTP023, + MSTP022, MSTP021, + MSTP019, /* HSCIF */ + MSTP016, MSTP015, MSTP014, /* TMU / TIMER */ + MSTP012, MSTP011, MSTP010, MSTP009, MSTP008, /* SSI */ + MSTP007, /* HSPI */ + MSTP115, /* ADMAC */ + MSTP114, /* GETHER */ + MSTP111, /* DMAC */ + MSTP109, /* VIDEOIN1 */ + MSTP108, /* VIDEOIN0 */ + MSTP107, /* RGPVBG */ + MSTP106, /* 2DG */ + MSTP103, /* VIEW */ + MSTP100, /* USB */ + MSTP331, /* MMC */ + MSTP330, /* MIMLB */ + MSTP323, /* SDHI0 */ + MSTP322, /* SDHI1 */ + MSTP321, /* SDHI2 */ + MSTP320, /* RQSPI */ + MSTP319, /* SRC0 */ + MSTP318, /* SRC1 */ + MSTP317, /* RSPI */ + MSTP316, /* RCAN0 */ + MSTP315, /* RCAN1 */ + MSTP314, /* FLTCL */ + MSTP313, /* ADC */ + MSTP312, /* MTU */ + MSTP304, /* IE-BUS */ + MSTP303, /* RTC */ + MSTP302, /* HIF */ + MSTP301, /* STIF0 */ + MSTP300, /* STIF1 */ + MSTP_NR }; + +static struct clk mstp_clks[MSTP_NR] = { + /* MSTPCR0 */ + [MSTP030] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 30, 0), + [MSTP029] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 29, 0), + [MSTP026] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 26, 0), + [MSTP025] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 25, 0), + [MSTP024] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 24, 0), + [MSTP023] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 23, 0), + [MSTP022] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 22, 0), + [MSTP021] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 21, 0), + [MSTP019] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 19, 0), + [MSTP016] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 16, 0), + [MSTP015] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 15, 0), + [MSTP014] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 14, 0), + [MSTP012] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 12, 0), + [MSTP011] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 11, 0), + [MSTP010] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 10, 0), + [MSTP009] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 9, 0), + [MSTP008] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 8, 0), + [MSTP007] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 7, 0), + + /* MSTPCR1 */ + [MSTP115] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 15, 0), + [MSTP114] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 14, 0), + [MSTP111] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 11, 0), + [MSTP109] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 9, 0), + [MSTP108] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 8, 0), + [MSTP107] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 7, 0), + [MSTP106] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 6, 0), + [MSTP103] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 3, 0), + [MSTP100] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 0, 0), + + /* MSTPCR3 */ + [MSTP331] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 31, 0), + [MSTP330] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 30, 0), + [MSTP323] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 23, 0), + [MSTP322] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 22, 0), + [MSTP321] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 21, 0), + [MSTP320] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 20, 0), + [MSTP319] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 19, 0), + [MSTP318] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 18, 0), + [MSTP317] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 17, 0), + [MSTP316] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 16, 0), + [MSTP315] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 15, 0), + [MSTP314] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 14, 0), + [MSTP313] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 13, 0), + [MSTP312] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 12, 0), + [MSTP304] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 4, 0), + [MSTP303] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 3, 0), + [MSTP302] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 2, 0), + [MSTP301] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 1, 0), + [MSTP300] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR3, 0, 0), +}; + +static struct clk_lookup lookups[] = { + /* main clocks */ + CLKDEV_CON_ID("extal", &extal_clk), + CLKDEV_CON_ID("pll_clk", &pll_clk), + + /* clocks */ + CLKDEV_CON_ID("cpu_clk", &div4_clks[DIV4_I]), + CLKDEV_CON_ID("shyway_clk", &div4_clks[DIV4_S]), + CLKDEV_CON_ID("ddr_clk", &div4_clks[DIV4_M]), + CLKDEV_CON_ID("bus_clk", &div4_clks[DIV4_B]), + CLKDEV_CON_ID("shyway_clk1", &div4_clks[DIV4_S1]), + CLKDEV_CON_ID("peripheral_clk", &div4_clks[DIV4_P]), + + /* MSTP32 clocks */ + CLKDEV_DEV_ID("i2c-sh7734.0", &mstp_clks[MSTP030]), + CLKDEV_DEV_ID("i2c-sh7734.1", &mstp_clks[MSTP029]), + CLKDEV_ICK_ID("fck", "sh-sci.0", &mstp_clks[MSTP026]), + CLKDEV_ICK_ID("fck", "sh-sci.1", &mstp_clks[MSTP025]), + CLKDEV_ICK_ID("fck", "sh-sci.2", &mstp_clks[MSTP024]), + CLKDEV_ICK_ID("fck", "sh-sci.3", &mstp_clks[MSTP023]), + CLKDEV_ICK_ID("fck", "sh-sci.4", &mstp_clks[MSTP022]), + CLKDEV_ICK_ID("fck", "sh-sci.5", &mstp_clks[MSTP021]), + CLKDEV_CON_ID("hscif", &mstp_clks[MSTP019]), + CLKDEV_ICK_ID("fck", "sh-tmu.0", &mstp_clks[MSTP016]), + CLKDEV_ICK_ID("fck", "sh-tmu.1", &mstp_clks[MSTP015]), + CLKDEV_ICK_ID("fck", "sh-tmu.2", &mstp_clks[MSTP014]), + CLKDEV_CON_ID("ssi0", &mstp_clks[MSTP012]), + CLKDEV_CON_ID("ssi1", &mstp_clks[MSTP011]), + CLKDEV_CON_ID("ssi2", &mstp_clks[MSTP010]), + CLKDEV_CON_ID("ssi3", &mstp_clks[MSTP009]), + CLKDEV_CON_ID("sss", &mstp_clks[MSTP008]), + CLKDEV_CON_ID("hspi", &mstp_clks[MSTP007]), + CLKDEV_CON_ID("usb_fck", &mstp_clks[MSTP100]), + CLKDEV_CON_ID("videoin0", &mstp_clks[MSTP109]), + CLKDEV_CON_ID("videoin1", &mstp_clks[MSTP108]), + CLKDEV_CON_ID("rgpvg", &mstp_clks[MSTP107]), + CLKDEV_CON_ID("2dg", &mstp_clks[MSTP106]), + CLKDEV_CON_ID("view", &mstp_clks[MSTP103]), + + CLKDEV_CON_ID("mmc0", &mstp_clks[MSTP331]), + CLKDEV_CON_ID("mimlb0", &mstp_clks[MSTP330]), + CLKDEV_CON_ID("sdhi0", &mstp_clks[MSTP323]), + CLKDEV_CON_ID("sdhi1", &mstp_clks[MSTP322]), + CLKDEV_CON_ID("sdhi2", &mstp_clks[MSTP321]), + CLKDEV_CON_ID("rqspi0", &mstp_clks[MSTP320]), + CLKDEV_CON_ID("src0", &mstp_clks[MSTP319]), + CLKDEV_CON_ID("src1", &mstp_clks[MSTP318]), + CLKDEV_CON_ID("rsp0", &mstp_clks[MSTP317]), + CLKDEV_CON_ID("rcan0", &mstp_clks[MSTP316]), + CLKDEV_CON_ID("rcan1", &mstp_clks[MSTP315]), + CLKDEV_CON_ID("fltcl0", &mstp_clks[MSTP314]), + CLKDEV_CON_ID("adc0", &mstp_clks[MSTP313]), + CLKDEV_CON_ID("mtu0", &mstp_clks[MSTP312]), + CLKDEV_CON_ID("iebus0", &mstp_clks[MSTP304]), + CLKDEV_DEV_ID("sh7734-gether.0", &mstp_clks[MSTP114]), + CLKDEV_CON_ID("rtc0", &mstp_clks[MSTP303]), + CLKDEV_CON_ID("hif0", &mstp_clks[MSTP302]), + CLKDEV_CON_ID("stif0", &mstp_clks[MSTP301]), + CLKDEV_CON_ID("stif1", &mstp_clks[MSTP300]), +}; + +int __init arch_clk_init(void) +{ + int i, ret = 0; + + for (i = 0; i < ARRAY_SIZE(main_clks); i++) + ret |= clk_register(main_clks[i]); + + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); + + if (!ret) + ret = sh_clk_div4_register(div4_clks, ARRAY_SIZE(div4_clks), + &div4_table); + + if (!ret) + ret = sh_clk_mstp_register(mstp_clks, MSTP_NR); + + return ret; +} diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7757.c b/arch/sh/kernel/cpu/sh4a/clock-sh7757.c new file mode 100644 index 000000000..b10af2ae9 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/clock-sh7757.c @@ -0,0 +1,155 @@ +/* + * arch/sh/kernel/cpu/sh4/clock-sh7757.c + * + * SH7757 support for the clock framework + * + * Copyright (C) 2009-2010 Renesas Solutions Corp. + * + * 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/io.h> +#include <linux/clkdev.h> +#include <asm/clock.h> +#include <asm/freq.h> + +/* + * Default rate for the root input clock, reset this with clk_set_rate() + * from the platform code. + */ +static struct clk extal_clk = { + .rate = 48000000, +}; + +static unsigned long pll_recalc(struct clk *clk) +{ + int multiplier; + + multiplier = test_mode_pin(MODE_PIN0) ? 24 : 16; + + return clk->parent->rate * multiplier; +} + +static struct sh_clk_ops pll_clk_ops = { + .recalc = pll_recalc, +}; + +static struct clk pll_clk = { + .ops = &pll_clk_ops, + .parent = &extal_clk, + .flags = CLK_ENABLE_ON_INIT, +}; + +static struct clk *clks[] = { + &extal_clk, + &pll_clk, +}; + +static unsigned int div2[] = { 1, 1, 2, 1, 1, 4, 1, 6, + 1, 1, 1, 16, 1, 24, 1, 1 }; + +static struct clk_div_mult_table div4_div_mult_table = { + .divisors = div2, + .nr_divisors = ARRAY_SIZE(div2), +}; + +static struct clk_div4_table div4_table = { + .div_mult_table = &div4_div_mult_table, +}; + +enum { DIV4_I, DIV4_SH, DIV4_P, DIV4_NR }; + +#define DIV4(_bit, _mask, _flags) \ + SH_CLK_DIV4(&pll_clk, FRQCR, _bit, _mask, _flags) + +struct clk div4_clks[DIV4_NR] = { + /* + * P clock is always enable, because some P clock modules is used + * by Host PC. + */ + [DIV4_P] = DIV4(0, 0x2800, CLK_ENABLE_ON_INIT), + [DIV4_SH] = DIV4(12, 0x00a0, CLK_ENABLE_ON_INIT), + [DIV4_I] = DIV4(20, 0x0004, CLK_ENABLE_ON_INIT), +}; + +#define MSTPCR0 0xffc80030 +#define MSTPCR1 0xffc80034 +#define MSTPCR2 0xffc10028 + +enum { MSTP004, MSTP000, MSTP127, MSTP114, MSTP113, MSTP112, + MSTP111, MSTP110, MSTP103, MSTP102, MSTP220, + MSTP_NR }; + +static struct clk mstp_clks[MSTP_NR] = { + /* MSTPCR0 */ + [MSTP004] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 4, 0), + [MSTP000] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 0, 0), + + /* MSTPCR1 */ + [MSTP127] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 27, 0), + [MSTP114] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 14, 0), + [MSTP113] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 13, 0), + [MSTP112] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 12, 0), + [MSTP111] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 11, 0), + [MSTP110] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 10, 0), + [MSTP103] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 3, 0), + [MSTP102] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR1, 2, 0), + + /* MSTPCR2 */ + [MSTP220] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR2, 20, 0), +}; + +static struct clk_lookup lookups[] = { + /* main clocks */ + CLKDEV_CON_ID("extal", &extal_clk), + CLKDEV_CON_ID("pll_clk", &pll_clk), + + /* DIV4 clocks */ + CLKDEV_CON_ID("peripheral_clk", &div4_clks[DIV4_P]), + CLKDEV_CON_ID("shyway_clk", &div4_clks[DIV4_SH]), + CLKDEV_CON_ID("cpu_clk", &div4_clks[DIV4_I]), + + /* MSTP32 clocks */ + CLKDEV_DEV_ID("sh_mobile_sdhi.0", &mstp_clks[MSTP004]), + CLKDEV_CON_ID("riic0", &mstp_clks[MSTP000]), + CLKDEV_CON_ID("riic1", &mstp_clks[MSTP000]), + CLKDEV_CON_ID("riic2", &mstp_clks[MSTP000]), + CLKDEV_CON_ID("riic3", &mstp_clks[MSTP000]), + CLKDEV_CON_ID("riic4", &mstp_clks[MSTP000]), + CLKDEV_CON_ID("riic5", &mstp_clks[MSTP000]), + CLKDEV_CON_ID("riic6", &mstp_clks[MSTP000]), + CLKDEV_CON_ID("riic7", &mstp_clks[MSTP000]), + + CLKDEV_ICK_ID("fck", "sh-tmu.0", &mstp_clks[MSTP113]), + CLKDEV_ICK_ID("fck", "sh-tmu.1", &mstp_clks[MSTP114]), + CLKDEV_ICK_ID("fck", "sh-sci.2", &mstp_clks[MSTP112]), + CLKDEV_ICK_ID("fck", "sh-sci.1", &mstp_clks[MSTP111]), + CLKDEV_ICK_ID("fck", "sh-sci.0", &mstp_clks[MSTP110]), + + CLKDEV_CON_ID("usb_fck", &mstp_clks[MSTP103]), + CLKDEV_DEV_ID("renesas_usbhs.0", &mstp_clks[MSTP102]), + CLKDEV_CON_ID("mmc0", &mstp_clks[MSTP220]), + CLKDEV_DEV_ID("rspi.2", &mstp_clks[MSTP127]), +}; + +int __init arch_clk_init(void) +{ + int i, ret = 0; + + for (i = 0; i < ARRAY_SIZE(clks); i++) + ret |= clk_register(clks[i]); + + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); + + if (!ret) + ret = sh_clk_div4_register(div4_clks, ARRAY_SIZE(div4_clks), + &div4_table); + if (!ret) + ret = sh_clk_mstp_register(mstp_clks, MSTP_NR); + + return ret; +} + diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7763.c b/arch/sh/kernel/cpu/sh4a/clock-sh7763.c new file mode 100644 index 000000000..7707e35ae --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/clock-sh7763.c @@ -0,0 +1,119 @@ +/* + * arch/sh/kernel/cpu/sh4a/clock-sh7763.c + * + * SH7763 support for the clock framework + * + * Copyright (C) 2005 Paul Mundt + * Copyright (C) 2007 Yoshihiro Shimoda + * + * 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/io.h> +#include <linux/clkdev.h> +#include <asm/clock.h> +#include <asm/freq.h> +#include <asm/io.h> + +static int bfc_divisors[] = { 1, 1, 1, 8, 1, 1, 1, 1 }; +static int p0fc_divisors[] = { 1, 1, 1, 8, 1, 1, 1, 1 }; +static int cfc_divisors[] = { 1, 1, 4, 1, 1, 1, 1, 1 }; + +static void master_clk_init(struct clk *clk) +{ + clk->rate *= p0fc_divisors[(__raw_readl(FRQCR) >> 4) & 0x07]; +} + +static struct sh_clk_ops sh7763_master_clk_ops = { + .init = master_clk_init, +}; + +static unsigned long module_clk_recalc(struct clk *clk) +{ + int idx = ((__raw_readl(FRQCR) >> 4) & 0x07); + return clk->parent->rate / p0fc_divisors[idx]; +} + +static struct sh_clk_ops sh7763_module_clk_ops = { + .recalc = module_clk_recalc, +}; + +static unsigned long bus_clk_recalc(struct clk *clk) +{ + int idx = ((__raw_readl(FRQCR) >> 16) & 0x07); + return clk->parent->rate / bfc_divisors[idx]; +} + +static struct sh_clk_ops sh7763_bus_clk_ops = { + .recalc = bus_clk_recalc, +}; + +static struct sh_clk_ops sh7763_cpu_clk_ops = { + .recalc = followparent_recalc, +}; + +static struct sh_clk_ops *sh7763_clk_ops[] = { + &sh7763_master_clk_ops, + &sh7763_module_clk_ops, + &sh7763_bus_clk_ops, + &sh7763_cpu_clk_ops, +}; + +void __init arch_init_clk_ops(struct sh_clk_ops **ops, int idx) +{ + if (idx < ARRAY_SIZE(sh7763_clk_ops)) + *ops = sh7763_clk_ops[idx]; +} + +static unsigned long shyway_clk_recalc(struct clk *clk) +{ + int idx = ((__raw_readl(FRQCR) >> 20) & 0x07); + return clk->parent->rate / cfc_divisors[idx]; +} + +static struct sh_clk_ops sh7763_shyway_clk_ops = { + .recalc = shyway_clk_recalc, +}; + +static struct clk sh7763_shyway_clk = { + .flags = CLK_ENABLE_ON_INIT, + .ops = &sh7763_shyway_clk_ops, +}; + +/* + * Additional SH7763-specific on-chip clocks that aren't already part of the + * clock framework + */ +static struct clk *sh7763_onchip_clocks[] = { + &sh7763_shyway_clk, +}; + +static struct clk_lookup lookups[] = { + /* main clocks */ + CLKDEV_CON_ID("shyway_clk", &sh7763_shyway_clk), +}; + +int __init arch_clk_init(void) +{ + struct clk *clk; + int i, ret = 0; + + cpg_clk_init(); + + clk = clk_get(NULL, "master_clk"); + for (i = 0; i < ARRAY_SIZE(sh7763_onchip_clocks); i++) { + struct clk *clkp = sh7763_onchip_clocks[i]; + + clkp->parent = clk; + ret |= clk_register(clkp); + } + + clk_put(clk); + + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); + + return ret; +} diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7770.c b/arch/sh/kernel/cpu/sh4a/clock-sh7770.c new file mode 100644 index 000000000..5d36f334b --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/clock-sh7770.c @@ -0,0 +1,73 @@ +/* + * arch/sh/kernel/cpu/sh4a/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[(__raw_readl(FRQCR) >> 28) & 0x000f]; +} + +static struct sh_clk_ops sh7770_master_clk_ops = { + .init = master_clk_init, +}; + +static unsigned long module_clk_recalc(struct clk *clk) +{ + int idx = ((__raw_readl(FRQCR) >> 28) & 0x000f); + return clk->parent->rate / pfc_divisors[idx]; +} + +static struct sh_clk_ops sh7770_module_clk_ops = { + .recalc = module_clk_recalc, +}; + +static unsigned long bus_clk_recalc(struct clk *clk) +{ + int idx = (__raw_readl(FRQCR) & 0x000f); + return clk->parent->rate / bfc_divisors[idx]; +} + +static struct sh_clk_ops sh7770_bus_clk_ops = { + .recalc = bus_clk_recalc, +}; + +static unsigned long cpu_clk_recalc(struct clk *clk) +{ + int idx = ((__raw_readl(FRQCR) >> 24) & 0x000f); + return clk->parent->rate / ifc_divisors[idx]; +} + +static struct sh_clk_ops sh7770_cpu_clk_ops = { + .recalc = cpu_clk_recalc, +}; + +static struct sh_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 sh_clk_ops **ops, int idx) +{ + if (idx < ARRAY_SIZE(sh7770_clk_ops)) + *ops = sh7770_clk_ops[idx]; +} + diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7780.c b/arch/sh/kernel/cpu/sh4a/clock-sh7780.c new file mode 100644 index 000000000..793dae42a --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/clock-sh7780.c @@ -0,0 +1,125 @@ +/* + * arch/sh/kernel/cpu/sh4a/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 <linux/io.h> +#include <linux/clkdev.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[__raw_readl(FRQCR) & 0x0003]; +} + +static struct sh_clk_ops sh7780_master_clk_ops = { + .init = master_clk_init, +}; + +static unsigned long module_clk_recalc(struct clk *clk) +{ + int idx = (__raw_readl(FRQCR) & 0x0003); + return clk->parent->rate / pfc_divisors[idx]; +} + +static struct sh_clk_ops sh7780_module_clk_ops = { + .recalc = module_clk_recalc, +}; + +static unsigned long bus_clk_recalc(struct clk *clk) +{ + int idx = ((__raw_readl(FRQCR) >> 16) & 0x0007); + return clk->parent->rate / bfc_divisors[idx]; +} + +static struct sh_clk_ops sh7780_bus_clk_ops = { + .recalc = bus_clk_recalc, +}; + +static unsigned long cpu_clk_recalc(struct clk *clk) +{ + int idx = ((__raw_readl(FRQCR) >> 24) & 0x0001); + return clk->parent->rate / ifc_divisors[idx]; +} + +static struct sh_clk_ops sh7780_cpu_clk_ops = { + .recalc = cpu_clk_recalc, +}; + +static struct sh_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 sh_clk_ops **ops, int idx) +{ + if (idx < ARRAY_SIZE(sh7780_clk_ops)) + *ops = sh7780_clk_ops[idx]; +} + +static unsigned long shyway_clk_recalc(struct clk *clk) +{ + int idx = ((__raw_readl(FRQCR) >> 20) & 0x0007); + return clk->parent->rate / cfc_divisors[idx]; +} + +static struct sh_clk_ops sh7780_shyway_clk_ops = { + .recalc = shyway_clk_recalc, +}; + +static struct clk sh7780_shyway_clk = { + .flags = CLK_ENABLE_ON_INIT, + .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 struct clk_lookup lookups[] = { + /* main clocks */ + CLKDEV_CON_ID("shyway_clk", &sh7780_shyway_clk), +}; + +int __init arch_clk_init(void) +{ + struct clk *clk; + int i, ret = 0; + + cpg_clk_init(); + + clk = clk_get(NULL, "master_clk"); + for (i = 0; i < ARRAY_SIZE(sh7780_onchip_clocks); i++) { + struct clk *clkp = sh7780_onchip_clocks[i]; + + clkp->parent = clk; + ret |= clk_register(clkp); + } + + clk_put(clk); + + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); + + return ret; +} diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7785.c b/arch/sh/kernel/cpu/sh4a/clock-sh7785.c new file mode 100644 index 000000000..1aafd5496 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/clock-sh7785.c @@ -0,0 +1,177 @@ +/* + * arch/sh/kernel/cpu/sh4a/clock-sh7785.c + * + * SH7785 support for the clock framework + * + * Copyright (C) 2007 - 2010 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/clk.h> +#include <linux/io.h> +#include <linux/cpufreq.h> +#include <linux/clkdev.h> +#include <asm/clock.h> +#include <asm/freq.h> +#include <cpu/sh7785.h> + +/* + * Default rate for the root input clock, reset this with clk_set_rate() + * from the platform code. + */ +static struct clk extal_clk = { + .rate = 33333333, +}; + +static unsigned long pll_recalc(struct clk *clk) +{ + int multiplier; + + multiplier = test_mode_pin(MODE_PIN4) ? 36 : 72; + + return clk->parent->rate * multiplier; +} + +static struct sh_clk_ops pll_clk_ops = { + .recalc = pll_recalc, +}; + +static struct clk pll_clk = { + .ops = &pll_clk_ops, + .parent = &extal_clk, + .flags = CLK_ENABLE_ON_INIT, +}; + +static struct clk *clks[] = { + &extal_clk, + &pll_clk, +}; + +static unsigned int div2[] = { 1, 2, 4, 6, 8, 12, 16, 18, + 24, 32, 36, 48 }; + +static struct clk_div_mult_table div4_div_mult_table = { + .divisors = div2, + .nr_divisors = ARRAY_SIZE(div2), +}; + +static struct clk_div4_table div4_table = { + .div_mult_table = &div4_div_mult_table, +}; + +enum { DIV4_I, DIV4_U, DIV4_SH, DIV4_B, DIV4_DDR, DIV4_GA, + DIV4_DU, DIV4_P, DIV4_NR }; + +#define DIV4(_bit, _mask, _flags) \ + SH_CLK_DIV4(&pll_clk, FRQMR1, _bit, _mask, _flags) + +struct clk div4_clks[DIV4_NR] = { + [DIV4_P] = DIV4(0, 0x0f80, 0), + [DIV4_DU] = DIV4(4, 0x0ff0, 0), + [DIV4_GA] = DIV4(8, 0x0030, 0), + [DIV4_DDR] = DIV4(12, 0x000c, CLK_ENABLE_ON_INIT), + [DIV4_B] = DIV4(16, 0x0fe0, CLK_ENABLE_ON_INIT), + [DIV4_SH] = DIV4(20, 0x000c, CLK_ENABLE_ON_INIT), + [DIV4_U] = DIV4(24, 0x000c, CLK_ENABLE_ON_INIT), + [DIV4_I] = DIV4(28, 0x000e, CLK_ENABLE_ON_INIT), +}; + +#define MSTPCR0 0xffc80030 +#define MSTPCR1 0xffc80034 + +enum { MSTP029, MSTP028, MSTP027, MSTP026, MSTP025, MSTP024, + MSTP021, MSTP020, MSTP017, MSTP016, + MSTP013, MSTP012, MSTP009, MSTP008, MSTP003, MSTP002, + MSTP119, MSTP117, MSTP105, MSTP104, MSTP100, + MSTP_NR }; + +static struct clk mstp_clks[MSTP_NR] = { + /* MSTPCR0 */ + [MSTP029] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 29, 0), + [MSTP028] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 28, 0), + [MSTP027] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 27, 0), + [MSTP026] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 26, 0), + [MSTP025] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 25, 0), + [MSTP024] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 24, 0), + [MSTP021] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 21, 0), + [MSTP020] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 20, 0), + [MSTP017] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 17, 0), + [MSTP016] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 16, 0), + [MSTP013] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 13, 0), + [MSTP012] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 12, 0), + [MSTP009] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 9, 0), + [MSTP008] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 8, 0), + [MSTP003] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 3, 0), + [MSTP002] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 2, 0), + + /* MSTPCR1 */ + [MSTP119] = SH_CLK_MSTP32(NULL, MSTPCR1, 19, 0), + [MSTP117] = SH_CLK_MSTP32(NULL, MSTPCR1, 17, 0), + [MSTP105] = SH_CLK_MSTP32(NULL, MSTPCR1, 5, 0), + [MSTP104] = SH_CLK_MSTP32(NULL, MSTPCR1, 4, 0), + [MSTP100] = SH_CLK_MSTP32(NULL, MSTPCR1, 0, 0), +}; + +static struct clk_lookup lookups[] = { + /* main clocks */ + CLKDEV_CON_ID("extal", &extal_clk), + CLKDEV_CON_ID("pll_clk", &pll_clk), + + /* DIV4 clocks */ + CLKDEV_CON_ID("peripheral_clk", &div4_clks[DIV4_P]), + CLKDEV_CON_ID("du_clk", &div4_clks[DIV4_DU]), + CLKDEV_CON_ID("ga_clk", &div4_clks[DIV4_GA]), + CLKDEV_CON_ID("ddr_clk", &div4_clks[DIV4_DDR]), + CLKDEV_CON_ID("bus_clk", &div4_clks[DIV4_B]), + CLKDEV_CON_ID("shyway_clk", &div4_clks[DIV4_SH]), + CLKDEV_CON_ID("umem_clk", &div4_clks[DIV4_U]), + CLKDEV_CON_ID("cpu_clk", &div4_clks[DIV4_I]), + + /* MSTP32 clocks */ + CLKDEV_ICK_ID("fck", "sh-sci.5", &mstp_clks[MSTP029]), + CLKDEV_ICK_ID("fck", "sh-sci.4", &mstp_clks[MSTP028]), + CLKDEV_ICK_ID("fck", "sh-sci.3", &mstp_clks[MSTP027]), + CLKDEV_ICK_ID("fck", "sh-sci.2", &mstp_clks[MSTP026]), + CLKDEV_ICK_ID("fck", "sh-sci.1", &mstp_clks[MSTP025]), + CLKDEV_ICK_ID("fck", "sh-sci.0", &mstp_clks[MSTP024]), + + CLKDEV_CON_ID("ssi1_fck", &mstp_clks[MSTP021]), + CLKDEV_CON_ID("ssi0_fck", &mstp_clks[MSTP020]), + CLKDEV_CON_ID("hac1_fck", &mstp_clks[MSTP017]), + CLKDEV_CON_ID("hac0_fck", &mstp_clks[MSTP016]), + CLKDEV_CON_ID("mmcif_fck", &mstp_clks[MSTP013]), + CLKDEV_CON_ID("flctl_fck", &mstp_clks[MSTP012]), + + CLKDEV_ICK_ID("fck", "sh-tmu.0", &mstp_clks[MSTP008]), + CLKDEV_ICK_ID("fck", "sh-tmu.1", &mstp_clks[MSTP009]), + + CLKDEV_CON_ID("siof_fck", &mstp_clks[MSTP003]), + CLKDEV_CON_ID("hspi_fck", &mstp_clks[MSTP002]), + CLKDEV_CON_ID("hudi_fck", &mstp_clks[MSTP119]), + CLKDEV_CON_ID("ubc0", &mstp_clks[MSTP117]), + CLKDEV_CON_ID("dmac_11_6_fck", &mstp_clks[MSTP105]), + CLKDEV_CON_ID("dmac_5_0_fck", &mstp_clks[MSTP104]), + CLKDEV_CON_ID("gdta_fck", &mstp_clks[MSTP100]), +}; + +int __init arch_clk_init(void) +{ + int i, ret = 0; + + for (i = 0; i < ARRAY_SIZE(clks); i++) + ret |= clk_register(clks[i]); + + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); + + if (!ret) + ret = sh_clk_div4_register(div4_clks, ARRAY_SIZE(div4_clks), + &div4_table); + if (!ret) + ret = sh_clk_mstp_register(mstp_clks, MSTP_NR); + + return ret; +} diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7786.c b/arch/sh/kernel/cpu/sh4a/clock-sh7786.c new file mode 100644 index 000000000..ac3dcfe5d --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/clock-sh7786.c @@ -0,0 +1,192 @@ +/* + * arch/sh/kernel/cpu/sh4a/clock-sh7786.c + * + * SH7786 support for the clock framework + * + * Copyright (C) 2010 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/clk.h> +#include <linux/io.h> +#include <linux/clkdev.h> +#include <asm/clock.h> +#include <asm/freq.h> + +/* + * Default rate for the root input clock, reset this with clk_set_rate() + * from the platform code. + */ +static struct clk extal_clk = { + .rate = 33333333, +}; + +static unsigned long pll_recalc(struct clk *clk) +{ + int multiplier; + + /* + * Clock modes 0, 1, and 2 use an x64 multiplier against PLL1, + * while modes 3, 4, and 5 use an x32. + */ + multiplier = (sh_mv.mv_mode_pins() & 0xf) < 3 ? 64 : 32; + + return clk->parent->rate * multiplier; +} + +static struct sh_clk_ops pll_clk_ops = { + .recalc = pll_recalc, +}; + +static struct clk pll_clk = { + .ops = &pll_clk_ops, + .parent = &extal_clk, + .flags = CLK_ENABLE_ON_INIT, +}; + +static struct clk *clks[] = { + &extal_clk, + &pll_clk, +}; + +static unsigned int div2[] = { 1, 2, 4, 6, 8, 12, 16, 18, + 24, 32, 36, 48 }; + +static struct clk_div_mult_table div4_div_mult_table = { + .divisors = div2, + .nr_divisors = ARRAY_SIZE(div2), +}; + +static struct clk_div4_table div4_table = { + .div_mult_table = &div4_div_mult_table, +}; + +enum { DIV4_I, DIV4_SH, DIV4_B, DIV4_DDR, DIV4_DU, DIV4_P, DIV4_NR }; + +#define DIV4(_bit, _mask, _flags) \ + SH_CLK_DIV4(&pll_clk, FRQMR1, _bit, _mask, _flags) + +struct clk div4_clks[DIV4_NR] = { + [DIV4_P] = DIV4(0, 0x0b40, 0), + [DIV4_DU] = DIV4(4, 0x0010, 0), + [DIV4_DDR] = DIV4(12, 0x0002, CLK_ENABLE_ON_INIT), + [DIV4_B] = DIV4(16, 0x0360, CLK_ENABLE_ON_INIT), + [DIV4_SH] = DIV4(20, 0x0002, CLK_ENABLE_ON_INIT), + [DIV4_I] = DIV4(28, 0x0006, CLK_ENABLE_ON_INIT), +}; + +#define MSTPCR0 0xffc40030 +#define MSTPCR1 0xffc40034 + +enum { MSTP029, MSTP028, MSTP027, MSTP026, MSTP025, MSTP024, + MSTP023, MSTP022, MSTP021, MSTP020, MSTP017, MSTP016, + MSTP015, MSTP014, MSTP011, MSTP010, MSTP009, MSTP008, + MSTP005, MSTP004, MSTP002, + MSTP112, MSTP110, MSTP109, MSTP108, + MSTP105, MSTP104, MSTP103, MSTP102, + MSTP_NR }; + +static struct clk mstp_clks[MSTP_NR] = { + /* MSTPCR0 */ + [MSTP029] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 29, 0), + [MSTP028] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 28, 0), + [MSTP027] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 27, 0), + [MSTP026] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 26, 0), + [MSTP025] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 25, 0), + [MSTP024] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 24, 0), + [MSTP023] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 23, 0), + [MSTP022] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 22, 0), + [MSTP021] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 21, 0), + [MSTP020] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 20, 0), + [MSTP017] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 17, 0), + [MSTP016] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 16, 0), + [MSTP015] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 15, 0), + [MSTP014] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 14, 0), + [MSTP011] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 11, 0), + [MSTP010] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 10, 0), + [MSTP009] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 9, 0), + [MSTP008] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 8, 0), + [MSTP005] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 5, 0), + [MSTP004] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 4, 0), + [MSTP002] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 2, 0), + + /* MSTPCR1 */ + [MSTP112] = SH_CLK_MSTP32(NULL, MSTPCR1, 12, 0), + [MSTP110] = SH_CLK_MSTP32(NULL, MSTPCR1, 10, 0), + [MSTP109] = SH_CLK_MSTP32(NULL, MSTPCR1, 9, 0), + [MSTP108] = SH_CLK_MSTP32(NULL, MSTPCR1, 8, 0), + [MSTP105] = SH_CLK_MSTP32(NULL, MSTPCR1, 5, 0), + [MSTP104] = SH_CLK_MSTP32(NULL, MSTPCR1, 4, 0), + [MSTP103] = SH_CLK_MSTP32(NULL, MSTPCR1, 3, 0), + [MSTP102] = SH_CLK_MSTP32(NULL, MSTPCR1, 2, 0), +}; + +static struct clk_lookup lookups[] = { + /* main clocks */ + CLKDEV_CON_ID("extal", &extal_clk), + CLKDEV_CON_ID("pll_clk", &pll_clk), + + /* DIV4 clocks */ + CLKDEV_CON_ID("peripheral_clk", &div4_clks[DIV4_P]), + CLKDEV_CON_ID("du_clk", &div4_clks[DIV4_DU]), + CLKDEV_CON_ID("ddr_clk", &div4_clks[DIV4_DDR]), + CLKDEV_CON_ID("bus_clk", &div4_clks[DIV4_B]), + CLKDEV_CON_ID("shyway_clk", &div4_clks[DIV4_SH]), + CLKDEV_CON_ID("cpu_clk", &div4_clks[DIV4_I]), + + /* MSTP32 clocks */ + CLKDEV_ICK_ID("fck", "sh-sci.5", &mstp_clks[MSTP029]), + CLKDEV_ICK_ID("fck", "sh-sci.4", &mstp_clks[MSTP028]), + CLKDEV_ICK_ID("fck", "sh-sci.3", &mstp_clks[MSTP027]), + CLKDEV_ICK_ID("fck", "sh-sci.2", &mstp_clks[MSTP026]), + CLKDEV_ICK_ID("fck", "sh-sci.1", &mstp_clks[MSTP025]), + CLKDEV_ICK_ID("fck", "sh-sci.0", &mstp_clks[MSTP024]), + + CLKDEV_CON_ID("ssi3_fck", &mstp_clks[MSTP023]), + CLKDEV_CON_ID("ssi2_fck", &mstp_clks[MSTP022]), + CLKDEV_CON_ID("ssi1_fck", &mstp_clks[MSTP021]), + CLKDEV_CON_ID("ssi0_fck", &mstp_clks[MSTP020]), + CLKDEV_CON_ID("hac1_fck", &mstp_clks[MSTP017]), + CLKDEV_CON_ID("hac0_fck", &mstp_clks[MSTP016]), + CLKDEV_CON_ID("i2c1_fck", &mstp_clks[MSTP015]), + CLKDEV_CON_ID("i2c0_fck", &mstp_clks[MSTP014]), + + CLKDEV_ICK_ID("fck", "sh-tmu.0", &mstp_clks[MSTP008]), + CLKDEV_ICK_ID("fck", "sh-tmu.1", &mstp_clks[MSTP009]), + CLKDEV_ICK_ID("fck", "sh-tmu.2", &mstp_clks[MSTP010]), + CLKDEV_ICK_ID("fck", "sh-tmu.3", &mstp_clks[MSTP011]), + + CLKDEV_CON_ID("sdif1_fck", &mstp_clks[MSTP005]), + CLKDEV_CON_ID("sdif0_fck", &mstp_clks[MSTP004]), + CLKDEV_CON_ID("hspi_fck", &mstp_clks[MSTP002]), + CLKDEV_CON_ID("usb_fck", &mstp_clks[MSTP112]), + CLKDEV_CON_ID("pcie2_fck", &mstp_clks[MSTP110]), + CLKDEV_CON_ID("pcie1_fck", &mstp_clks[MSTP109]), + CLKDEV_CON_ID("pcie0_fck", &mstp_clks[MSTP108]), + CLKDEV_CON_ID("dmac_11_6_fck", &mstp_clks[MSTP105]), + CLKDEV_CON_ID("dmac_5_0_fck", &mstp_clks[MSTP104]), + CLKDEV_CON_ID("du_fck", &mstp_clks[MSTP103]), + CLKDEV_CON_ID("ether_fck", &mstp_clks[MSTP102]), +}; + +int __init arch_clk_init(void) +{ + int i, ret = 0; + + for (i = 0; i < ARRAY_SIZE(clks); i++) + ret |= clk_register(clks[i]); + + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); + + if (!ret) + ret = sh_clk_div4_register(div4_clks, ARRAY_SIZE(div4_clks), + &div4_table); + if (!ret) + ret = sh_clk_mstp_register(mstp_clks, MSTP_NR); + + return ret; +} diff --git a/arch/sh/kernel/cpu/sh4a/clock-shx3.c b/arch/sh/kernel/cpu/sh4a/clock-shx3.c new file mode 100644 index 000000000..b1bdbc3cb --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/clock-shx3.c @@ -0,0 +1,151 @@ +/* + * arch/sh/kernel/cpu/sh4/clock-shx3.c + * + * SH-X3 support for the clock framework + * + * Copyright (C) 2006-2007 Renesas Technology Corp. + * Copyright (C) 2006-2007 Renesas Solutions Corp. + * Copyright (C) 2006-2010 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/io.h> +#include <linux/clkdev.h> +#include <asm/clock.h> +#include <asm/freq.h> + +/* + * Default rate for the root input clock, reset this with clk_set_rate() + * from the platform code. + */ +static struct clk extal_clk = { + .rate = 16666666, +}; + +static unsigned long pll_recalc(struct clk *clk) +{ + /* PLL1 has a fixed x72 multiplier. */ + return clk->parent->rate * 72; +} + +static struct sh_clk_ops pll_clk_ops = { + .recalc = pll_recalc, +}; + +static struct clk pll_clk = { + .ops = &pll_clk_ops, + .parent = &extal_clk, + .flags = CLK_ENABLE_ON_INIT, +}; + +static struct clk *clks[] = { + &extal_clk, + &pll_clk, +}; + +static unsigned int div2[] = { 1, 2, 4, 6, 8, 12, 16, 18, + 24, 32, 36, 48 }; + +static struct clk_div_mult_table div4_div_mult_table = { + .divisors = div2, + .nr_divisors = ARRAY_SIZE(div2), +}; + +static struct clk_div4_table div4_table = { + .div_mult_table = &div4_div_mult_table, +}; + +enum { DIV4_I, DIV4_SH, DIV4_B, DIV4_DDR, DIV4_SHA, DIV4_P, DIV4_NR }; + +#define DIV4(_bit, _mask, _flags) \ + SH_CLK_DIV4(&pll_clk, FRQMR1, _bit, _mask, _flags) + +struct clk div4_clks[DIV4_NR] = { + [DIV4_P] = DIV4(0, 0x0f80, 0), + [DIV4_SHA] = DIV4(4, 0x0ff0, 0), + [DIV4_DDR] = DIV4(12, 0x000c, CLK_ENABLE_ON_INIT), + [DIV4_B] = DIV4(16, 0x0fe0, CLK_ENABLE_ON_INIT), + [DIV4_SH] = DIV4(20, 0x000c, CLK_ENABLE_ON_INIT), + [DIV4_I] = DIV4(28, 0x000e, CLK_ENABLE_ON_INIT), +}; + +#define MSTPCR0 0xffc00030 +#define MSTPCR1 0xffc00034 + +enum { MSTP027, MSTP026, MSTP025, MSTP024, + MSTP009, MSTP008, MSTP003, MSTP002, + MSTP001, MSTP000, MSTP119, MSTP105, + MSTP104, MSTP_NR }; + +static struct clk mstp_clks[MSTP_NR] = { + /* MSTPCR0 */ + [MSTP027] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 27, 0), + [MSTP026] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 26, 0), + [MSTP025] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 25, 0), + [MSTP024] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 24, 0), + [MSTP009] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 9, 0), + [MSTP008] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 8, 0), + [MSTP003] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 3, 0), + [MSTP002] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 2, 0), + [MSTP001] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 1, 0), + [MSTP000] = SH_CLK_MSTP32(&div4_clks[DIV4_P], MSTPCR0, 0, 0), + + /* MSTPCR1 */ + [MSTP119] = SH_CLK_MSTP32(NULL, MSTPCR1, 19, 0), + [MSTP105] = SH_CLK_MSTP32(NULL, MSTPCR1, 5, 0), + [MSTP104] = SH_CLK_MSTP32(NULL, MSTPCR1, 4, 0), +}; + +static struct clk_lookup lookups[] = { + /* main clocks */ + CLKDEV_CON_ID("extal", &extal_clk), + CLKDEV_CON_ID("pll_clk", &pll_clk), + + /* DIV4 clocks */ + CLKDEV_CON_ID("peripheral_clk", &div4_clks[DIV4_P]), + CLKDEV_CON_ID("shywaya_clk", &div4_clks[DIV4_SHA]), + CLKDEV_CON_ID("ddr_clk", &div4_clks[DIV4_DDR]), + CLKDEV_CON_ID("bus_clk", &div4_clks[DIV4_B]), + CLKDEV_CON_ID("shyway_clk", &div4_clks[DIV4_SH]), + CLKDEV_CON_ID("cpu_clk", &div4_clks[DIV4_I]), + + /* MSTP32 clocks */ + CLKDEV_ICK_ID("fck", "sh-sci.3", &mstp_clks[MSTP027]), + CLKDEV_ICK_ID("fck", "sh-sci.2", &mstp_clks[MSTP026]), + CLKDEV_ICK_ID("fck", "sh-sci.1", &mstp_clks[MSTP025]), + CLKDEV_ICK_ID("fck", "sh-sci.0", &mstp_clks[MSTP024]), + + CLKDEV_CON_ID("h8ex_fck", &mstp_clks[MSTP003]), + CLKDEV_CON_ID("csm_fck", &mstp_clks[MSTP002]), + CLKDEV_CON_ID("fe1_fck", &mstp_clks[MSTP001]), + CLKDEV_CON_ID("fe0_fck", &mstp_clks[MSTP000]), + + CLKDEV_ICK_ID("fck", "sh-tmu.0", &mstp_clks[MSTP008]), + CLKDEV_ICK_ID("fck", "sh-tmu.1", &mstp_clks[MSTP009]), + + CLKDEV_CON_ID("hudi_fck", &mstp_clks[MSTP119]), + CLKDEV_CON_ID("dmac_11_6_fck", &mstp_clks[MSTP105]), + CLKDEV_CON_ID("dmac_5_0_fck", &mstp_clks[MSTP104]), +}; + +int __init arch_clk_init(void) +{ + int i, ret = 0; + + for (i = 0; i < ARRAY_SIZE(clks); i++) + ret |= clk_register(clks[i]); + + clkdev_add_table(lookups, ARRAY_SIZE(lookups)); + + if (!ret) + ret = sh_clk_div4_register(div4_clks, ARRAY_SIZE(div4_clks), + &div4_table); + if (!ret) + ret = sh_clk_mstp_register(mstp_clks, MSTP_NR); + + return ret; +} diff --git a/arch/sh/kernel/cpu/sh4a/intc-shx3.c b/arch/sh/kernel/cpu/sh4a/intc-shx3.c new file mode 100644 index 000000000..78c971486 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/intc-shx3.c @@ -0,0 +1,34 @@ +/* + * Shared support for SH-X3 interrupt controllers. + * + * Copyright (C) 2009 - 2010 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/irq.h> +#include <linux/io.h> +#include <linux/init.h> + +#define INTACK 0xfe4100b8 +#define INTACKCLR 0xfe4100bc +#define INTC_USERIMASK 0xfe411000 + +#ifdef CONFIG_INTC_BALANCING +unsigned int irq_lookup(unsigned int irq) +{ + return __raw_readl(INTACK) & 1 ? irq : NO_IRQ_IGNORE; +} + +void irq_finish(unsigned int irq) +{ + __raw_writel(irq2evt(irq), INTACKCLR); +} +#endif + +static int __init shx3_irq_setup(void) +{ + return register_intc_userimask(INTC_USERIMASK); +} +arch_initcall(shx3_irq_setup); diff --git a/arch/sh/kernel/cpu/sh4a/perf_event.c b/arch/sh/kernel/cpu/sh4a/perf_event.c new file mode 100644 index 000000000..84a2c396c --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/perf_event.c @@ -0,0 +1,302 @@ +/* + * Performance events support for SH-4A performance counters + * + * Copyright (C) 2009, 2010 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/kernel.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/perf_event.h> +#include <asm/processor.h> + +#define PPC_CCBR(idx) (0xff200800 + (sizeof(u32) * idx)) +#define PPC_PMCTR(idx) (0xfc100000 + (sizeof(u32) * idx)) + +#define CCBR_CIT_MASK (0x7ff << 6) +#define CCBR_DUC (1 << 3) +#define CCBR_CMDS (1 << 1) +#define CCBR_PPCE (1 << 0) + +#ifdef CONFIG_CPU_SHX3 +/* + * The PMCAT location for SH-X3 CPUs was quietly moved, while the CCBR + * and PMCTR locations remains tentatively constant. This change remains + * wholly undocumented, and was simply found through trial and error. + * + * Early cuts of SH-X3 still appear to use the SH-X/SH-X2 locations, and + * it's unclear when this ceased to be the case. For now we always use + * the new location (if future parts keep up with this trend then + * scanning for them at runtime also remains a viable option.) + * + * The gap in the register space also suggests that there are other + * undocumented counters, so this will need to be revisited at a later + * point in time. + */ +#define PPC_PMCAT 0xfc100240 +#else +#define PPC_PMCAT 0xfc100080 +#endif + +#define PMCAT_OVF3 (1 << 27) +#define PMCAT_CNN3 (1 << 26) +#define PMCAT_CLR3 (1 << 25) +#define PMCAT_OVF2 (1 << 19) +#define PMCAT_CLR2 (1 << 17) +#define PMCAT_OVF1 (1 << 11) +#define PMCAT_CNN1 (1 << 10) +#define PMCAT_CLR1 (1 << 9) +#define PMCAT_OVF0 (1 << 3) +#define PMCAT_CLR0 (1 << 1) + +static struct sh_pmu sh4a_pmu; + +/* + * Supported raw event codes: + * + * Event Code Description + * ---------- ----------- + * + * 0x0000 number of elapsed cycles + * 0x0200 number of elapsed cycles in privileged mode + * 0x0280 number of elapsed cycles while SR.BL is asserted + * 0x0202 instruction execution + * 0x0203 instruction execution in parallel + * 0x0204 number of unconditional branches + * 0x0208 number of exceptions + * 0x0209 number of interrupts + * 0x0220 UTLB miss caused by instruction fetch + * 0x0222 UTLB miss caused by operand access + * 0x02a0 number of ITLB misses + * 0x0028 number of accesses to instruction memories + * 0x0029 number of accesses to instruction cache + * 0x002a instruction cache miss + * 0x022e number of access to instruction X/Y memory + * 0x0030 number of reads to operand memories + * 0x0038 number of writes to operand memories + * 0x0031 number of operand cache read accesses + * 0x0039 number of operand cache write accesses + * 0x0032 operand cache read miss + * 0x003a operand cache write miss + * 0x0236 number of reads to operand X/Y memory + * 0x023e number of writes to operand X/Y memory + * 0x0237 number of reads to operand U memory + * 0x023f number of writes to operand U memory + * 0x0337 number of U memory read buffer misses + * 0x02b4 number of wait cycles due to operand read access + * 0x02bc number of wait cycles due to operand write access + * 0x0033 number of wait cycles due to operand cache read miss + * 0x003b number of wait cycles due to operand cache write miss + */ + +/* + * Special reserved bits used by hardware emulators, read values will + * vary, but writes must always be 0. + */ +#define PMCAT_EMU_CLR_MASK ((1 << 24) | (1 << 16) | (1 << 8) | (1 << 0)) + +static const int sh4a_general_events[] = { + [PERF_COUNT_HW_CPU_CYCLES] = 0x0000, + [PERF_COUNT_HW_INSTRUCTIONS] = 0x0202, + [PERF_COUNT_HW_CACHE_REFERENCES] = 0x0029, /* I-cache */ + [PERF_COUNT_HW_CACHE_MISSES] = 0x002a, /* I-cache */ + [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x0204, + [PERF_COUNT_HW_BRANCH_MISSES] = -1, + [PERF_COUNT_HW_BUS_CYCLES] = -1, +}; + +#define C(x) PERF_COUNT_HW_CACHE_##x + +static const int sh4a_cache_events + [PERF_COUNT_HW_CACHE_MAX] + [PERF_COUNT_HW_CACHE_OP_MAX] + [PERF_COUNT_HW_CACHE_RESULT_MAX] = +{ + [ C(L1D) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x0031, + [ C(RESULT_MISS) ] = 0x0032, + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = 0x0039, + [ C(RESULT_MISS) ] = 0x003a, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = 0, + [ C(RESULT_MISS) ] = 0, + }, + }, + + [ C(L1I) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x0029, + [ C(RESULT_MISS) ] = 0x002a, + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = 0, + [ C(RESULT_MISS) ] = 0, + }, + }, + + [ C(LL) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x0030, + [ C(RESULT_MISS) ] = 0, + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = 0x0038, + [ C(RESULT_MISS) ] = 0, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = 0, + [ C(RESULT_MISS) ] = 0, + }, + }, + + [ C(DTLB) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x0222, + [ C(RESULT_MISS) ] = 0x0220, + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = 0, + [ C(RESULT_MISS) ] = 0, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = 0, + [ C(RESULT_MISS) ] = 0, + }, + }, + + [ C(ITLB) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0, + [ C(RESULT_MISS) ] = 0x02a0, + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + }, + + [ C(BPU) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + }, + + [ C(NODE) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + }, +}; + +static int sh4a_event_map(int event) +{ + return sh4a_general_events[event]; +} + +static u64 sh4a_pmu_read(int idx) +{ + return __raw_readl(PPC_PMCTR(idx)); +} + +static void sh4a_pmu_disable(struct hw_perf_event *hwc, int idx) +{ + unsigned int tmp; + + tmp = __raw_readl(PPC_CCBR(idx)); + tmp &= ~(CCBR_CIT_MASK | CCBR_DUC); + __raw_writel(tmp, PPC_CCBR(idx)); +} + +static void sh4a_pmu_enable(struct hw_perf_event *hwc, int idx) +{ + unsigned int tmp; + + tmp = __raw_readl(PPC_PMCAT); + tmp &= ~PMCAT_EMU_CLR_MASK; + tmp |= idx ? PMCAT_CLR1 : PMCAT_CLR0; + __raw_writel(tmp, PPC_PMCAT); + + tmp = __raw_readl(PPC_CCBR(idx)); + tmp |= (hwc->config << 6) | CCBR_CMDS | CCBR_PPCE; + __raw_writel(tmp, PPC_CCBR(idx)); + + __raw_writel(__raw_readl(PPC_CCBR(idx)) | CCBR_DUC, PPC_CCBR(idx)); +} + +static void sh4a_pmu_disable_all(void) +{ + int i; + + for (i = 0; i < sh4a_pmu.num_events; i++) + __raw_writel(__raw_readl(PPC_CCBR(i)) & ~CCBR_DUC, PPC_CCBR(i)); +} + +static void sh4a_pmu_enable_all(void) +{ + int i; + + for (i = 0; i < sh4a_pmu.num_events; i++) + __raw_writel(__raw_readl(PPC_CCBR(i)) | CCBR_DUC, PPC_CCBR(i)); +} + +static struct sh_pmu sh4a_pmu = { + .name = "sh4a", + .num_events = 2, + .event_map = sh4a_event_map, + .max_events = ARRAY_SIZE(sh4a_general_events), + .raw_event_mask = 0x3ff, + .cache_events = &sh4a_cache_events, + .read = sh4a_pmu_read, + .disable = sh4a_pmu_disable, + .enable = sh4a_pmu_enable, + .disable_all = sh4a_pmu_disable_all, + .enable_all = sh4a_pmu_enable_all, +}; + +static int __init sh4a_pmu_init(void) +{ + /* + * Make sure this CPU actually has perf counters. + */ + if (!(boot_cpu_data.flags & CPU_HAS_PERF_COUNTER)) { + pr_notice("HW perf events unsupported, software events only.\n"); + return -ENODEV; + } + + return register_sh_pmu(&sh4a_pmu); +} +early_initcall(sh4a_pmu_init); diff --git a/arch/sh/kernel/cpu/sh4a/pinmux-sh7722.c b/arch/sh/kernel/cpu/sh4a/pinmux-sh7722.c new file mode 100644 index 000000000..0dd5312f9 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/pinmux-sh7722.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/bug.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <cpu/pfc.h> + +static struct resource sh7722_pfc_resources[] = { + [0] = { + .start = 0xa4050100, + .end = 0xa405018f, + .flags = IORESOURCE_MEM, + }, +}; + +static int __init plat_pinmux_setup(void) +{ + return sh_pfc_register("pfc-sh7722", sh7722_pfc_resources, + ARRAY_SIZE(sh7722_pfc_resources)); +} +arch_initcall(plat_pinmux_setup); diff --git a/arch/sh/kernel/cpu/sh4a/pinmux-sh7723.c b/arch/sh/kernel/cpu/sh4a/pinmux-sh7723.c new file mode 100644 index 000000000..99c637d5b --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/pinmux-sh7723.c @@ -0,0 +1,30 @@ +/* + * SH7723 Pinmux + * + * Copyright (C) 2008 Magnus Damm + * + * 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/bug.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <cpu/pfc.h> + +static struct resource sh7723_pfc_resources[] = { + [0] = { + .start = 0xa4050100, + .end = 0xa405016f, + .flags = IORESOURCE_MEM, + }, +}; + +static int __init plat_pinmux_setup(void) +{ + return sh_pfc_register("pfc-sh7723", sh7723_pfc_resources, + ARRAY_SIZE(sh7723_pfc_resources)); +} +arch_initcall(plat_pinmux_setup); diff --git a/arch/sh/kernel/cpu/sh4a/pinmux-sh7724.c b/arch/sh/kernel/cpu/sh4a/pinmux-sh7724.c new file mode 100644 index 000000000..63be4749e --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/pinmux-sh7724.c @@ -0,0 +1,35 @@ +/* + * SH7724 Pinmux + * + * Copyright (C) 2009 Renesas Solutions Corp. + * + * Kuninori Morimoto <morimoto.kuninori@renesas.com> + * + * Based on SH7723 Pinmux + * Copyright (C) 2008 Magnus Damm + * + * 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/bug.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <cpu/pfc.h> + +static struct resource sh7724_pfc_resources[] = { + [0] = { + .start = 0xa4050100, + .end = 0xa405016f, + .flags = IORESOURCE_MEM, + }, +}; + +static int __init plat_pinmux_setup(void) +{ + return sh_pfc_register("pfc-sh7724", sh7724_pfc_resources, + ARRAY_SIZE(sh7724_pfc_resources)); +} +arch_initcall(plat_pinmux_setup); diff --git a/arch/sh/kernel/cpu/sh4a/pinmux-sh7734.c b/arch/sh/kernel/cpu/sh4a/pinmux-sh7734.c new file mode 100644 index 000000000..ea2db632a --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/pinmux-sh7734.c @@ -0,0 +1,35 @@ +/* + * SH7734 processor support - PFC hardware block + * + * Copyright (C) 2012 Renesas Solutions Corp. + * Copyright (C) 2012 Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.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/bug.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <cpu/pfc.h> + +static struct resource sh7734_pfc_resources[] = { + [0] = { /* PFC */ + .start = 0xFFFC0000, + .end = 0xFFFC011C, + .flags = IORESOURCE_MEM, + }, + [1] = { /* GPIO */ + .start = 0xFFC40000, + .end = 0xFFC4502B, + .flags = IORESOURCE_MEM, + } +}; + +static int __init plat_pinmux_setup(void) +{ + return sh_pfc_register("pfc-sh7734", sh7734_pfc_resources, + ARRAY_SIZE(sh7734_pfc_resources)); +} +arch_initcall(plat_pinmux_setup); diff --git a/arch/sh/kernel/cpu/sh4a/pinmux-sh7757.c b/arch/sh/kernel/cpu/sh4a/pinmux-sh7757.c new file mode 100644 index 000000000..567745d44 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/pinmux-sh7757.c @@ -0,0 +1,35 @@ +/* + * SH7757 (B0 step) Pinmux + * + * Copyright (C) 2009-2010 Renesas Solutions Corp. + * + * Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com> + * + * Based on SH7723 Pinmux + * Copyright (C) 2008 Magnus Damm + * + * 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/bug.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <cpu/pfc.h> + +static struct resource sh7757_pfc_resources[] = { + [0] = { + .start = 0xffec0000, + .end = 0xffec008f, + .flags = IORESOURCE_MEM, + }, +}; + +static int __init plat_pinmux_setup(void) +{ + return sh_pfc_register("pfc-sh7757", sh7757_pfc_resources, + ARRAY_SIZE(sh7757_pfc_resources)); +} +arch_initcall(plat_pinmux_setup); diff --git a/arch/sh/kernel/cpu/sh4a/pinmux-sh7785.c b/arch/sh/kernel/cpu/sh4a/pinmux-sh7785.c new file mode 100644 index 000000000..e336ab8b5 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/pinmux-sh7785.c @@ -0,0 +1,30 @@ +/* + * SH7785 Pinmux + * + * Copyright (C) 2008 Magnus Damm + * + * 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/bug.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <cpu/pfc.h> + +static struct resource sh7785_pfc_resources[] = { + [0] = { + .start = 0xffe70000, + .end = 0xffe7008f, + .flags = IORESOURCE_MEM, + }, +}; + +static int __init plat_pinmux_setup(void) +{ + return sh_pfc_register("pfc-sh7785", sh7785_pfc_resources, + ARRAY_SIZE(sh7785_pfc_resources)); +} +arch_initcall(plat_pinmux_setup); diff --git a/arch/sh/kernel/cpu/sh4a/pinmux-sh7786.c b/arch/sh/kernel/cpu/sh4a/pinmux-sh7786.c new file mode 100644 index 000000000..9a459556a --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/pinmux-sh7786.c @@ -0,0 +1,35 @@ +/* + * SH7786 Pinmux + * + * Copyright (C) 2008, 2009 Renesas Solutions Corp. + * Kuninori Morimoto <morimoto.kuninori@renesas.com> + * + * Based on SH7785 pinmux + * + * Copyright (C) 2008 Magnus Damm + * + * 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/bug.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <cpu/pfc.h> + +static struct resource sh7786_pfc_resources[] = { + [0] = { + .start = 0xffcc0000, + .end = 0xffcc008f, + .flags = IORESOURCE_MEM, + }, +}; + +static int __init plat_pinmux_setup(void) +{ + return sh_pfc_register("pfc-sh7786", sh7786_pfc_resources, + ARRAY_SIZE(sh7786_pfc_resources)); +} +arch_initcall(plat_pinmux_setup); diff --git a/arch/sh/kernel/cpu/sh4a/pinmux-shx3.c b/arch/sh/kernel/cpu/sh4a/pinmux-shx3.c new file mode 100644 index 000000000..444bf25c6 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/pinmux-shx3.c @@ -0,0 +1,29 @@ +/* + * SH-X3 prototype CPU pinmux + * + * Copyright (C) 2010 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/bug.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <cpu/pfc.h> + +static struct resource shx3_pfc_resources[] = { + [0] = { + .start = 0xffc70000, + .end = 0xffc7001f, + .flags = IORESOURCE_MEM, + }, +}; + +static int __init plat_pinmux_setup(void) +{ + return sh_pfc_register("pfc-shx3", shx3_pfc_resources, + ARRAY_SIZE(shx3_pfc_resources)); +} +arch_initcall(plat_pinmux_setup); diff --git a/arch/sh/kernel/cpu/sh4a/serial-sh7722.c b/arch/sh/kernel/cpu/sh4a/serial-sh7722.c new file mode 100644 index 000000000..6ecc8b6e1 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/serial-sh7722.c @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/serial_sci.h> +#include <linux/serial_core.h> +#include <linux/io.h> + +#define PSCR 0xA405011E + +static void sh7722_sci_init_pins(struct uart_port *port, unsigned int cflag) +{ + unsigned short data; + + if (port->mapbase == 0xffe00000) { + data = __raw_readw(PSCR); + data &= ~0x03cf; + if (!(cflag & CRTSCTS)) + data |= 0x0340; + + __raw_writew(data, PSCR); + } +} + +struct plat_sci_port_ops sh7722_sci_port_ops = { + .init_pins = sh7722_sci_init_pins, +}; diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7343.c b/arch/sh/kernel/cpu/sh4a/setup-sh7343.c new file mode 100644 index 000000000..5788073a7 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7343.c @@ -0,0 +1,446 @@ +/* + * SH7343 Setup + * + * Copyright (C) 2006 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/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <linux/serial_sci.h> +#include <linux/uio_driver.h> +#include <linux/sh_timer.h> +#include <linux/sh_intc.h> +#include <asm/clock.h> + +/* Serial */ +static struct plat_sci_port scif0_platform_data = { + .scscr = SCSCR_CKE1, + .type = PORT_SCIF, +}; + +static struct resource scif0_resources[] = { + DEFINE_RES_MEM(0xffe00000, 0x100), + DEFINE_RES_IRQ(evt2irq(0xc00)), +}; + +static struct platform_device scif0_device = { + .name = "sh-sci", + .id = 0, + .resource = scif0_resources, + .num_resources = ARRAY_SIZE(scif0_resources), + .dev = { + .platform_data = &scif0_platform_data, + }, +}; + +static struct plat_sci_port scif1_platform_data = { + .scscr = SCSCR_CKE1, + .type = PORT_SCIF, +}; + +static struct resource scif1_resources[] = { + DEFINE_RES_MEM(0xffe10000, 0x100), + DEFINE_RES_IRQ(evt2irq(0xc20)), +}; + +static struct platform_device scif1_device = { + .name = "sh-sci", + .id = 1, + .resource = scif1_resources, + .num_resources = ARRAY_SIZE(scif1_resources), + .dev = { + .platform_data = &scif1_platform_data, + }, +}; + +static struct plat_sci_port scif2_platform_data = { + .scscr = SCSCR_CKE1, + .type = PORT_SCIF, +}; + +static struct resource scif2_resources[] = { + DEFINE_RES_MEM(0xffe20000, 0x100), + DEFINE_RES_IRQ(evt2irq(0xc40)), +}; + +static struct platform_device scif2_device = { + .name = "sh-sci", + .id = 2, + .resource = scif2_resources, + .num_resources = ARRAY_SIZE(scif2_resources), + .dev = { + .platform_data = &scif2_platform_data, + }, +}; + +static struct plat_sci_port scif3_platform_data = { + .scscr = SCSCR_CKE1, + .type = PORT_SCIF, +}; + +static struct resource scif3_resources[] = { + DEFINE_RES_MEM(0xffe30000, 0x100), + DEFINE_RES_IRQ(evt2irq(0xc60)), +}; + +static struct platform_device scif3_device = { + .name = "sh-sci", + .id = 3, + .resource = scif3_resources, + .num_resources = ARRAY_SIZE(scif3_resources), + .dev = { + .platform_data = &scif3_platform_data, + }, +}; + +static struct resource iic0_resources[] = { + [0] = { + .name = "IIC0", + .start = 0x04470000, + .end = 0x04470017, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = evt2irq(0xe00), + .end = evt2irq(0xe60), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device iic0_device = { + .name = "i2c-sh_mobile", + .id = 0, /* "i2c0" clock */ + .num_resources = ARRAY_SIZE(iic0_resources), + .resource = iic0_resources, +}; + +static struct resource iic1_resources[] = { + [0] = { + .name = "IIC1", + .start = 0x04750000, + .end = 0x04750017, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = evt2irq(0x780), + .end = evt2irq(0x7e0), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device iic1_device = { + .name = "i2c-sh_mobile", + .id = 1, /* "i2c1" clock */ + .num_resources = ARRAY_SIZE(iic1_resources), + .resource = iic1_resources, +}; + +static struct uio_info vpu_platform_data = { + .name = "VPU4", + .version = "0", + .irq = evt2irq(0x980), +}; + +static struct resource vpu_resources[] = { + [0] = { + .name = "VPU", + .start = 0xfe900000, + .end = 0xfe9022eb, + .flags = IORESOURCE_MEM, + }, + [1] = { + /* place holder for contiguous memory */ + }, +}; + +static struct platform_device vpu_device = { + .name = "uio_pdrv_genirq", + .id = 0, + .dev = { + .platform_data = &vpu_platform_data, + }, + .resource = vpu_resources, + .num_resources = ARRAY_SIZE(vpu_resources), +}; + +static struct uio_info veu_platform_data = { + .name = "VEU", + .version = "0", + .irq = evt2irq(0x8c0), +}; + +static struct resource veu_resources[] = { + [0] = { + .name = "VEU", + .start = 0xfe920000, + .end = 0xfe9200b7, + .flags = IORESOURCE_MEM, + }, + [1] = { + /* place holder for contiguous memory */ + }, +}; + +static struct platform_device veu_device = { + .name = "uio_pdrv_genirq", + .id = 1, + .dev = { + .platform_data = &veu_platform_data, + }, + .resource = veu_resources, + .num_resources = ARRAY_SIZE(veu_resources), +}; + +static struct uio_info jpu_platform_data = { + .name = "JPU", + .version = "0", + .irq = evt2irq(0x560), +}; + +static struct resource jpu_resources[] = { + [0] = { + .name = "JPU", + .start = 0xfea00000, + .end = 0xfea102d3, + .flags = IORESOURCE_MEM, + }, + [1] = { + /* place holder for contiguous memory */ + }, +}; + +static struct platform_device jpu_device = { + .name = "uio_pdrv_genirq", + .id = 2, + .dev = { + .platform_data = &jpu_platform_data, + }, + .resource = jpu_resources, + .num_resources = ARRAY_SIZE(jpu_resources), +}; + +static struct sh_timer_config cmt_platform_data = { + .channels_mask = 0x20, +}; + +static struct resource cmt_resources[] = { + DEFINE_RES_MEM(0x044a0000, 0x70), + DEFINE_RES_IRQ(evt2irq(0xf00)), +}; + +static struct platform_device cmt_device = { + .name = "sh-cmt-32", + .id = 0, + .dev = { + .platform_data = &cmt_platform_data, + }, + .resource = cmt_resources, + .num_resources = ARRAY_SIZE(cmt_resources), +}; + +static struct sh_timer_config tmu0_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu0_resources[] = { + DEFINE_RES_MEM(0xffd80000, 0x2c), + DEFINE_RES_IRQ(evt2irq(0x400)), + DEFINE_RES_IRQ(evt2irq(0x420)), + DEFINE_RES_IRQ(evt2irq(0x440)), +}; + +static struct platform_device tmu0_device = { + .name = "sh-tmu", + .id = 0, + .dev = { + .platform_data = &tmu0_platform_data, + }, + .resource = tmu0_resources, + .num_resources = ARRAY_SIZE(tmu0_resources), +}; + +static struct platform_device *sh7343_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + &scif3_device, + &cmt_device, + &tmu0_device, + &iic0_device, + &iic1_device, + &vpu_device, + &veu_device, + &jpu_device, +}; + +static int __init sh7343_devices_setup(void) +{ + platform_resource_setup_memory(&vpu_device, "vpu", 1 << 20); + platform_resource_setup_memory(&veu_device, "veu", 2 << 20); + platform_resource_setup_memory(&jpu_device, "jpu", 2 << 20); + + return platform_add_devices(sh7343_devices, + ARRAY_SIZE(sh7343_devices)); +} +arch_initcall(sh7343_devices_setup); + +static struct platform_device *sh7343_early_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + &scif3_device, + &cmt_device, + &tmu0_device, +}; + +void __init plat_early_device_setup(void) +{ + early_platform_add_devices(sh7343_early_devices, + ARRAY_SIZE(sh7343_early_devices)); +} + +enum { + UNUSED = 0, + ENABLED, + DISABLED, + + /* interrupt sources */ + IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7, + DMAC0, DMAC1, DMAC2, DMAC3, + VIO_CEUI, VIO_BEUI, VIO_VEUI, VOU, + MFI, VPU, TPU, Z3D4, USBI0, USBI1, + MMC_ERR, MMC_TRAN, MMC_FSTAT, MMC_FRDY, + DMAC4, DMAC5, DMAC_DADERR, + KEYSC, + SCIF, SCIF1, SCIF2, SCIF3, + SIOF0, SIOF1, SIO, + FLCTL_FLSTEI, FLCTL_FLENDI, FLCTL_FLTREQ0I, FLCTL_FLTREQ1I, + I2C0_ALI, I2C0_TACKI, I2C0_WAITI, I2C0_DTEI, + I2C1_ALI, I2C1_TACKI, I2C1_WAITI, I2C1_DTEI, + SIM_TEI, SIM_TXI, SIM_RXI, SIM_ERI, + IRDA, SDHI, CMT, TSIF, SIU, + TMU0, TMU1, TMU2, + JPU, LCDC, + + /* interrupt groups */ + + DMAC0123, VIOVOU, MMC, DMAC45, FLCTL, I2C0, I2C1, SIM, USB, +}; + +static struct intc_vect vectors[] __initdata = { + INTC_VECT(IRQ0, 0x600), INTC_VECT(IRQ1, 0x620), + INTC_VECT(IRQ2, 0x640), INTC_VECT(IRQ3, 0x660), + INTC_VECT(IRQ4, 0x680), INTC_VECT(IRQ5, 0x6a0), + INTC_VECT(IRQ6, 0x6c0), INTC_VECT(IRQ7, 0x6e0), + INTC_VECT(I2C1_ALI, 0x780), INTC_VECT(I2C1_TACKI, 0x7a0), + INTC_VECT(I2C1_WAITI, 0x7c0), INTC_VECT(I2C1_DTEI, 0x7e0), + INTC_VECT(DMAC0, 0x800), INTC_VECT(DMAC1, 0x820), + INTC_VECT(DMAC2, 0x840), INTC_VECT(DMAC3, 0x860), + INTC_VECT(VIO_CEUI, 0x880), INTC_VECT(VIO_BEUI, 0x8a0), + INTC_VECT(VIO_VEUI, 0x8c0), INTC_VECT(VOU, 0x8e0), + INTC_VECT(MFI, 0x900), INTC_VECT(VPU, 0x980), + INTC_VECT(TPU, 0x9a0), INTC_VECT(Z3D4, 0x9e0), + INTC_VECT(USBI0, 0xa20), INTC_VECT(USBI1, 0xa40), + INTC_VECT(MMC_ERR, 0xb00), INTC_VECT(MMC_TRAN, 0xb20), + INTC_VECT(MMC_FSTAT, 0xb40), INTC_VECT(MMC_FRDY, 0xb60), + INTC_VECT(DMAC4, 0xb80), INTC_VECT(DMAC5, 0xba0), + INTC_VECT(DMAC_DADERR, 0xbc0), INTC_VECT(KEYSC, 0xbe0), + INTC_VECT(SCIF, 0xc00), INTC_VECT(SCIF1, 0xc20), + INTC_VECT(SCIF2, 0xc40), INTC_VECT(SCIF3, 0xc60), + INTC_VECT(SIOF0, 0xc80), INTC_VECT(SIOF1, 0xca0), + INTC_VECT(SIO, 0xd00), + INTC_VECT(FLCTL_FLSTEI, 0xd80), INTC_VECT(FLCTL_FLENDI, 0xda0), + INTC_VECT(FLCTL_FLTREQ0I, 0xdc0), INTC_VECT(FLCTL_FLTREQ1I, 0xde0), + INTC_VECT(I2C0_ALI, 0xe00), INTC_VECT(I2C0_TACKI, 0xe20), + INTC_VECT(I2C0_WAITI, 0xe40), INTC_VECT(I2C0_DTEI, 0xe60), + INTC_VECT(SDHI, 0xe80), INTC_VECT(SDHI, 0xea0), + INTC_VECT(SDHI, 0xec0), INTC_VECT(SDHI, 0xee0), + INTC_VECT(CMT, 0xf00), INTC_VECT(TSIF, 0xf20), + INTC_VECT(SIU, 0xf80), + INTC_VECT(TMU0, 0x400), INTC_VECT(TMU1, 0x420), + INTC_VECT(TMU2, 0x440), + INTC_VECT(JPU, 0x560), INTC_VECT(LCDC, 0x580), +}; + +static struct intc_group groups[] __initdata = { + INTC_GROUP(DMAC0123, DMAC0, DMAC1, DMAC2, DMAC3), + INTC_GROUP(VIOVOU, VIO_CEUI, VIO_BEUI, VIO_VEUI, VOU), + INTC_GROUP(MMC, MMC_FRDY, MMC_FSTAT, MMC_TRAN, MMC_ERR), + INTC_GROUP(DMAC45, DMAC4, DMAC5, DMAC_DADERR), + INTC_GROUP(FLCTL, FLCTL_FLSTEI, FLCTL_FLENDI, + FLCTL_FLTREQ0I, FLCTL_FLTREQ1I), + INTC_GROUP(I2C0, I2C0_ALI, I2C0_TACKI, I2C0_WAITI, I2C0_DTEI), + INTC_GROUP(I2C1, I2C1_ALI, I2C1_TACKI, I2C1_WAITI, I2C1_DTEI), + INTC_GROUP(SIM, SIM_TEI, SIM_TXI, SIM_RXI, SIM_ERI), + INTC_GROUP(USB, USBI0, USBI1), +}; + +static struct intc_mask_reg mask_registers[] __initdata = { + { 0xa4080084, 0xa40800c4, 8, /* IMR1 / IMCR1 */ + { VOU, VIO_VEUI, VIO_BEUI, VIO_CEUI, DMAC3, DMAC2, DMAC1, DMAC0 } }, + { 0xa4080088, 0xa40800c8, 8, /* IMR2 / IMCR2 */ + { 0, 0, 0, VPU, 0, 0, 0, MFI } }, + { 0xa408008c, 0xa40800cc, 8, /* IMR3 / IMCR3 */ + { SIM_TEI, SIM_TXI, SIM_RXI, SIM_ERI, 0, 0, 0, IRDA } }, + { 0xa4080090, 0xa40800d0, 8, /* IMR4 / IMCR4 */ + { 0, TMU2, TMU1, TMU0, JPU, 0, 0, LCDC } }, + { 0xa4080094, 0xa40800d4, 8, /* IMR5 / IMCR5 */ + { KEYSC, DMAC_DADERR, DMAC5, DMAC4, SCIF3, SCIF2, SCIF1, SCIF } }, + { 0xa4080098, 0xa40800d8, 8, /* IMR6 / IMCR6 */ + { 0, 0, 0, SIO, Z3D4, 0, SIOF1, SIOF0 } }, + { 0xa408009c, 0xa40800dc, 8, /* IMR7 / IMCR7 */ + { I2C0_DTEI, I2C0_WAITI, I2C0_TACKI, I2C0_ALI, + FLCTL_FLTREQ1I, FLCTL_FLTREQ0I, FLCTL_FLENDI, FLCTL_FLSTEI } }, + { 0xa40800a0, 0xa40800e0, 8, /* IMR8 / IMCR8 */ + { DISABLED, ENABLED, ENABLED, ENABLED, 0, 0, 0, SIU } }, + { 0xa40800a4, 0xa40800e4, 8, /* IMR9 / IMCR9 */ + { 0, 0, 0, CMT, 0, USBI1, USBI0 } }, + { 0xa40800a8, 0xa40800e8, 8, /* IMR10 / IMCR10 */ + { MMC_FRDY, MMC_FSTAT, MMC_TRAN, MMC_ERR } }, + { 0xa40800ac, 0xa40800ec, 8, /* IMR11 / IMCR11 */ + { I2C1_DTEI, I2C1_WAITI, I2C1_TACKI, I2C1_ALI, TPU, 0, 0, TSIF } }, + { 0xa4140044, 0xa4140064, 8, /* INTMSK00 / INTMSKCLR00 */ + { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } }, +}; + +static struct intc_prio_reg prio_registers[] __initdata = { + { 0xa4080000, 0, 16, 4, /* IPRA */ { TMU0, TMU1, TMU2 } }, + { 0xa4080004, 0, 16, 4, /* IPRB */ { JPU, LCDC, SIM } }, + { 0xa4080010, 0, 16, 4, /* IPRE */ { DMAC0123, VIOVOU, MFI, VPU } }, + { 0xa4080014, 0, 16, 4, /* IPRF */ { KEYSC, DMAC45, USB, CMT } }, + { 0xa4080018, 0, 16, 4, /* IPRG */ { SCIF, SCIF1, SCIF2, SCIF3 } }, + { 0xa408001c, 0, 16, 4, /* IPRH */ { SIOF0, SIOF1, FLCTL, I2C0 } }, + { 0xa4080020, 0, 16, 4, /* IPRI */ { SIO, 0, TSIF, I2C1 } }, + { 0xa4080024, 0, 16, 4, /* IPRJ */ { Z3D4, 0, SIU } }, + { 0xa4080028, 0, 16, 4, /* IPRK */ { 0, MMC, 0, SDHI } }, + { 0xa408002c, 0, 16, 4, /* IPRL */ { 0, 0, TPU } }, + { 0xa4140010, 0, 32, 4, /* INTPRI00 */ + { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } }, +}; + +static struct intc_sense_reg sense_registers[] __initdata = { + { 0xa414001c, 16, 2, /* ICR1 */ + { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } }, +}; + +static struct intc_mask_reg ack_registers[] __initdata = { + { 0xa4140024, 0, 8, /* INTREQ00 */ + { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } }, +}; + +static struct intc_desc intc_desc __initdata = { + .name = "sh7343", + .force_enable = ENABLED, + .force_disable = DISABLED, + .hw = INTC_HW_DESC(vectors, groups, mask_registers, + prio_registers, sense_registers, ack_registers), +}; + +void __init plat_irq_setup(void) +{ + register_intc_controller(&intc_desc); +} diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7366.c b/arch/sh/kernel/cpu/sh4a/setup-sh7366.c new file mode 100644 index 000000000..646918713 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7366.c @@ -0,0 +1,390 @@ +/* + * SH7366 Setup + * + * Copyright (C) 2008 Renesas Solutions + * + * Based on linux/arch/sh/kernel/cpu/sh4a/setup-sh7722.c + * + * 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/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <linux/serial_sci.h> +#include <linux/uio_driver.h> +#include <linux/sh_timer.h> +#include <linux/sh_intc.h> +#include <linux/usb/r8a66597.h> +#include <asm/clock.h> + +static struct plat_sci_port scif0_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, +}; + +static struct resource scif0_resources[] = { + DEFINE_RES_MEM(0xffe00000, 0x100), + DEFINE_RES_IRQ(evt2irq(0xc00)), +}; + +static struct platform_device scif0_device = { + .name = "sh-sci", + .id = 0, + .resource = scif0_resources, + .num_resources = ARRAY_SIZE(scif0_resources), + .dev = { + .platform_data = &scif0_platform_data, + }, +}; + +static struct resource iic_resources[] = { + [0] = { + .name = "IIC", + .start = 0x04470000, + .end = 0x04470017, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = evt2irq(0xe00), + .end = evt2irq(0xe60), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device iic_device = { + .name = "i2c-sh_mobile", + .id = 0, /* "i2c0" clock */ + .num_resources = ARRAY_SIZE(iic_resources), + .resource = iic_resources, +}; + +static struct r8a66597_platdata r8a66597_data = { + .on_chip = 1, +}; + +static struct resource usb_host_resources[] = { + [0] = { + .start = 0xa4d80000, + .end = 0xa4d800ff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = evt2irq(0xa20), + .end = evt2irq(0xa20), + .flags = IORESOURCE_IRQ | IRQF_TRIGGER_LOW, + }, +}; + +static struct platform_device usb_host_device = { + .name = "r8a66597_hcd", + .id = -1, + .dev = { + .dma_mask = NULL, + .coherent_dma_mask = 0xffffffff, + .platform_data = &r8a66597_data, + }, + .num_resources = ARRAY_SIZE(usb_host_resources), + .resource = usb_host_resources, +}; + +static struct uio_info vpu_platform_data = { + .name = "VPU5", + .version = "0", + .irq = evt2irq(0x980), +}; + +static struct resource vpu_resources[] = { + [0] = { + .name = "VPU", + .start = 0xfe900000, + .end = 0xfe902807, + .flags = IORESOURCE_MEM, + }, + [1] = { + /* place holder for contiguous memory */ + }, +}; + +static struct platform_device vpu_device = { + .name = "uio_pdrv_genirq", + .id = 0, + .dev = { + .platform_data = &vpu_platform_data, + }, + .resource = vpu_resources, + .num_resources = ARRAY_SIZE(vpu_resources), +}; + +static struct uio_info veu0_platform_data = { + .name = "VEU", + .version = "0", + .irq = evt2irq(0x8c0), +}; + +static struct resource veu0_resources[] = { + [0] = { + .name = "VEU(1)", + .start = 0xfe920000, + .end = 0xfe9200b7, + .flags = IORESOURCE_MEM, + }, + [1] = { + /* place holder for contiguous memory */ + }, +}; + +static struct platform_device veu0_device = { + .name = "uio_pdrv_genirq", + .id = 1, + .dev = { + .platform_data = &veu0_platform_data, + }, + .resource = veu0_resources, + .num_resources = ARRAY_SIZE(veu0_resources), +}; + +static struct uio_info veu1_platform_data = { + .name = "VEU", + .version = "0", + .irq = evt2irq(0x560), +}; + +static struct resource veu1_resources[] = { + [0] = { + .name = "VEU(2)", + .start = 0xfe924000, + .end = 0xfe9240b7, + .flags = IORESOURCE_MEM, + }, + [1] = { + /* place holder for contiguous memory */ + }, +}; + +static struct platform_device veu1_device = { + .name = "uio_pdrv_genirq", + .id = 2, + .dev = { + .platform_data = &veu1_platform_data, + }, + .resource = veu1_resources, + .num_resources = ARRAY_SIZE(veu1_resources), +}; + +static struct sh_timer_config cmt_platform_data = { + .channels_mask = 0x20, +}; + +static struct resource cmt_resources[] = { + DEFINE_RES_MEM(0x044a0000, 0x70), + DEFINE_RES_IRQ(evt2irq(0xf00)), +}; + +static struct platform_device cmt_device = { + .name = "sh-cmt-32", + .id = 0, + .dev = { + .platform_data = &cmt_platform_data, + }, + .resource = cmt_resources, + .num_resources = ARRAY_SIZE(cmt_resources), +}; + +static struct sh_timer_config tmu0_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu0_resources[] = { + DEFINE_RES_MEM(0xffd80000, 0x2c), + DEFINE_RES_IRQ(evt2irq(0x400)), + DEFINE_RES_IRQ(evt2irq(0x420)), + DEFINE_RES_IRQ(evt2irq(0x440)), +}; + +static struct platform_device tmu0_device = { + .name = "sh-tmu", + .id = 0, + .dev = { + .platform_data = &tmu0_platform_data, + }, + .resource = tmu0_resources, + .num_resources = ARRAY_SIZE(tmu0_resources), +}; + +static struct platform_device *sh7366_devices[] __initdata = { + &scif0_device, + &cmt_device, + &tmu0_device, + &iic_device, + &usb_host_device, + &vpu_device, + &veu0_device, + &veu1_device, +}; + +static int __init sh7366_devices_setup(void) +{ + platform_resource_setup_memory(&vpu_device, "vpu", 2 << 20); + platform_resource_setup_memory(&veu0_device, "veu0", 2 << 20); + platform_resource_setup_memory(&veu1_device, "veu1", 2 << 20); + + return platform_add_devices(sh7366_devices, + ARRAY_SIZE(sh7366_devices)); +} +arch_initcall(sh7366_devices_setup); + +static struct platform_device *sh7366_early_devices[] __initdata = { + &scif0_device, + &cmt_device, + &tmu0_device, +}; + +void __init plat_early_device_setup(void) +{ + early_platform_add_devices(sh7366_early_devices, + ARRAY_SIZE(sh7366_early_devices)); +} + +enum { + UNUSED=0, + ENABLED, + DISABLED, + + /* interrupt sources */ + IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7, + ICB, + DMAC0, DMAC1, DMAC2, DMAC3, + VIO_CEUI, VIO_BEUI, VIO_VEUI, VOU, + MFI, VPU, USB, + MMC_MMC1I, MMC_MMC2I, MMC_MMC3I, + DMAC4, DMAC5, DMAC_DADERR, + SCIF, SCIFA1, SCIFA2, + DENC, MSIOF, + FLCTL_FLSTEI, FLCTL_FLENDI, FLCTL_FLTREQ0I, FLCTL_FLTREQ1I, + I2C_ALI, I2C_TACKI, I2C_WAITI, I2C_DTEI, + SDHI, CMT, TSIF, SIU, + TMU0, TMU1, TMU2, + VEU2, LCDC, + + /* interrupt groups */ + + DMAC0123, VIOVOU, MMC, DMAC45, FLCTL, I2C, +}; + +static struct intc_vect vectors[] __initdata = { + INTC_VECT(IRQ0, 0x600), INTC_VECT(IRQ1, 0x620), + INTC_VECT(IRQ2, 0x640), INTC_VECT(IRQ3, 0x660), + INTC_VECT(IRQ4, 0x680), INTC_VECT(IRQ5, 0x6a0), + INTC_VECT(IRQ6, 0x6c0), INTC_VECT(IRQ7, 0x6e0), + INTC_VECT(ICB, 0x700), + INTC_VECT(DMAC0, 0x800), INTC_VECT(DMAC1, 0x820), + INTC_VECT(DMAC2, 0x840), INTC_VECT(DMAC3, 0x860), + INTC_VECT(VIO_CEUI, 0x880), INTC_VECT(VIO_BEUI, 0x8a0), + INTC_VECT(VIO_VEUI, 0x8c0), INTC_VECT(VOU, 0x8e0), + INTC_VECT(MFI, 0x900), INTC_VECT(VPU, 0x980), INTC_VECT(USB, 0xa20), + INTC_VECT(MMC_MMC1I, 0xb00), INTC_VECT(MMC_MMC2I, 0xb20), + INTC_VECT(MMC_MMC3I, 0xb40), + INTC_VECT(DMAC4, 0xb80), INTC_VECT(DMAC5, 0xba0), + INTC_VECT(DMAC_DADERR, 0xbc0), + INTC_VECT(SCIF, 0xc00), INTC_VECT(SCIFA1, 0xc20), + INTC_VECT(SCIFA2, 0xc40), + INTC_VECT(DENC, 0xc60), INTC_VECT(MSIOF, 0xc80), + INTC_VECT(FLCTL_FLSTEI, 0xd80), INTC_VECT(FLCTL_FLENDI, 0xda0), + INTC_VECT(FLCTL_FLTREQ0I, 0xdc0), INTC_VECT(FLCTL_FLTREQ1I, 0xde0), + INTC_VECT(I2C_ALI, 0xe00), INTC_VECT(I2C_TACKI, 0xe20), + INTC_VECT(I2C_WAITI, 0xe40), INTC_VECT(I2C_DTEI, 0xe60), + INTC_VECT(SDHI, 0xe80), INTC_VECT(SDHI, 0xea0), + INTC_VECT(SDHI, 0xec0), INTC_VECT(SDHI, 0xee0), + INTC_VECT(CMT, 0xf00), INTC_VECT(TSIF, 0xf20), + INTC_VECT(SIU, 0xf80), + INTC_VECT(TMU0, 0x400), INTC_VECT(TMU1, 0x420), + INTC_VECT(TMU2, 0x440), + INTC_VECT(VEU2, 0x560), INTC_VECT(LCDC, 0x580), +}; + +static struct intc_group groups[] __initdata = { + INTC_GROUP(DMAC0123, DMAC0, DMAC1, DMAC2, DMAC3), + INTC_GROUP(VIOVOU, VIO_CEUI, VIO_BEUI, VIO_VEUI, VOU), + INTC_GROUP(MMC, MMC_MMC1I, MMC_MMC2I, MMC_MMC3I), + INTC_GROUP(DMAC45, DMAC4, DMAC5, DMAC_DADERR), + INTC_GROUP(FLCTL, FLCTL_FLSTEI, FLCTL_FLENDI, + FLCTL_FLTREQ0I, FLCTL_FLTREQ1I), + INTC_GROUP(I2C, I2C_ALI, I2C_TACKI, I2C_WAITI, I2C_DTEI), +}; + +static struct intc_mask_reg mask_registers[] __initdata = { + { 0xa4080080, 0xa40800c0, 8, /* IMR0 / IMCR0 */ + { } }, + { 0xa4080084, 0xa40800c4, 8, /* IMR1 / IMCR1 */ + { VOU, VIO_VEUI, VIO_BEUI, VIO_CEUI, DMAC3, DMAC2, DMAC1, DMAC0 } }, + { 0xa4080088, 0xa40800c8, 8, /* IMR2 / IMCR2 */ + { 0, 0, 0, VPU, 0, 0, 0, MFI } }, + { 0xa408008c, 0xa40800cc, 8, /* IMR3 / IMCR3 */ + { 0, 0, 0, ICB } }, + { 0xa4080090, 0xa40800d0, 8, /* IMR4 / IMCR4 */ + { 0, TMU2, TMU1, TMU0, VEU2, 0, 0, LCDC } }, + { 0xa4080094, 0xa40800d4, 8, /* IMR5 / IMCR5 */ + { 0, DMAC_DADERR, DMAC5, DMAC4, DENC, SCIFA2, SCIFA1, SCIF } }, + { 0xa4080098, 0xa40800d8, 8, /* IMR6 / IMCR6 */ + { 0, 0, 0, 0, 0, 0, 0, MSIOF } }, + { 0xa408009c, 0xa40800dc, 8, /* IMR7 / IMCR7 */ + { I2C_DTEI, I2C_WAITI, I2C_TACKI, I2C_ALI, + FLCTL_FLTREQ1I, FLCTL_FLTREQ0I, FLCTL_FLENDI, FLCTL_FLSTEI } }, + { 0xa40800a0, 0xa40800e0, 8, /* IMR8 / IMCR8 */ + { DISABLED, ENABLED, ENABLED, ENABLED, 0, 0, 0, SIU } }, + { 0xa40800a4, 0xa40800e4, 8, /* IMR9 / IMCR9 */ + { 0, 0, 0, CMT, 0, USB, } }, + { 0xa40800a8, 0xa40800e8, 8, /* IMR10 / IMCR10 */ + { 0, MMC_MMC3I, MMC_MMC2I, MMC_MMC1I } }, + { 0xa40800ac, 0xa40800ec, 8, /* IMR11 / IMCR11 */ + { 0, 0, 0, 0, 0, 0, 0, TSIF } }, + { 0xa4140044, 0xa4140064, 8, /* INTMSK00 / INTMSKCLR00 */ + { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } }, +}; + +static struct intc_prio_reg prio_registers[] __initdata = { + { 0xa4080000, 0, 16, 4, /* IPRA */ { TMU0, TMU1, TMU2 } }, + { 0xa4080004, 0, 16, 4, /* IPRB */ { VEU2, LCDC, ICB } }, + { 0xa4080008, 0, 16, 4, /* IPRC */ { } }, + { 0xa408000c, 0, 16, 4, /* IPRD */ { } }, + { 0xa4080010, 0, 16, 4, /* IPRE */ { DMAC0123, VIOVOU, MFI, VPU } }, + { 0xa4080014, 0, 16, 4, /* IPRF */ { 0, DMAC45, USB, CMT } }, + { 0xa4080018, 0, 16, 4, /* IPRG */ { SCIF, SCIFA1, SCIFA2, DENC } }, + { 0xa408001c, 0, 16, 4, /* IPRH */ { MSIOF, 0, FLCTL, I2C } }, + { 0xa4080020, 0, 16, 4, /* IPRI */ { 0, 0, TSIF, } }, + { 0xa4080024, 0, 16, 4, /* IPRJ */ { 0, 0, SIU } }, + { 0xa4080028, 0, 16, 4, /* IPRK */ { 0, MMC, 0, SDHI } }, + { 0xa408002c, 0, 16, 4, /* IPRL */ { } }, + { 0xa4140010, 0, 32, 4, /* INTPRI00 */ + { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } }, +}; + +static struct intc_sense_reg sense_registers[] __initdata = { + { 0xa414001c, 16, 2, /* ICR1 */ + { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } }, +}; + +static struct intc_mask_reg ack_registers[] __initdata = { + { 0xa4140024, 0, 8, /* INTREQ00 */ + { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } }, +}; + +static struct intc_desc intc_desc __initdata = { + .name = "sh7366", + .force_enable = ENABLED, + .force_disable = DISABLED, + .hw = INTC_HW_DESC(vectors, groups, mask_registers, + prio_registers, sense_registers, ack_registers), +}; + +void __init plat_irq_setup(void) +{ + register_intc_controller(&intc_desc); +} + +void __init plat_mem_setup(void) +{ + /* TODO: Register Node 1 */ +} diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7722.c b/arch/sh/kernel/cpu/sh4a/setup-sh7722.c new file mode 100644 index 000000000..6b3a26e61 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7722.c @@ -0,0 +1,668 @@ +/* + * SH7722 Setup + * + * Copyright (C) 2006 - 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/mm.h> +#include <linux/platform_device.h> +#include <linux/serial.h> +#include <linux/serial_sci.h> +#include <linux/sh_dma.h> +#include <linux/sh_timer.h> +#include <linux/sh_intc.h> +#include <linux/uio_driver.h> +#include <linux/usb/m66592.h> + +#include <asm/clock.h> +#include <asm/mmzone.h> +#include <asm/siu.h> + +#include <cpu/dma-register.h> +#include <cpu/sh7722.h> +#include <cpu/serial.h> + +static const struct sh_dmae_slave_config sh7722_dmae_slaves[] = { + { + .slave_id = SHDMA_SLAVE_SCIF0_TX, + .addr = 0xffe0000c, + .chcr = DM_FIX | SM_INC | RS_ERS | TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x21, + }, { + .slave_id = SHDMA_SLAVE_SCIF0_RX, + .addr = 0xffe00014, + .chcr = DM_INC | SM_FIX | RS_ERS | TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x22, + }, { + .slave_id = SHDMA_SLAVE_SCIF1_TX, + .addr = 0xffe1000c, + .chcr = DM_FIX | SM_INC | RS_ERS | TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x25, + }, { + .slave_id = SHDMA_SLAVE_SCIF1_RX, + .addr = 0xffe10014, + .chcr = DM_INC | SM_FIX | RS_ERS | TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x26, + }, { + .slave_id = SHDMA_SLAVE_SCIF2_TX, + .addr = 0xffe2000c, + .chcr = DM_FIX | SM_INC | RS_ERS | TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x29, + }, { + .slave_id = SHDMA_SLAVE_SCIF2_RX, + .addr = 0xffe20014, + .chcr = DM_INC | SM_FIX | RS_ERS | TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x2a, + }, { + .slave_id = SHDMA_SLAVE_SIUA_TX, + .addr = 0xa454c098, + .chcr = DM_FIX | SM_INC | RS_ERS | TS_INDEX2VAL(XMIT_SZ_32BIT), + .mid_rid = 0xb1, + }, { + .slave_id = SHDMA_SLAVE_SIUA_RX, + .addr = 0xa454c090, + .chcr = DM_INC | SM_FIX | RS_ERS | TS_INDEX2VAL(XMIT_SZ_32BIT), + .mid_rid = 0xb2, + }, { + .slave_id = SHDMA_SLAVE_SIUB_TX, + .addr = 0xa454c09c, + .chcr = DM_FIX | SM_INC | RS_ERS | TS_INDEX2VAL(XMIT_SZ_32BIT), + .mid_rid = 0xb5, + }, { + .slave_id = SHDMA_SLAVE_SIUB_RX, + .addr = 0xa454c094, + .chcr = DM_INC | SM_FIX | RS_ERS | TS_INDEX2VAL(XMIT_SZ_32BIT), + .mid_rid = 0xb6, + }, { + .slave_id = SHDMA_SLAVE_SDHI0_TX, + .addr = 0x04ce0030, + .chcr = DM_FIX | SM_INC | RS_ERS | TS_INDEX2VAL(XMIT_SZ_16BIT), + .mid_rid = 0xc1, + }, { + .slave_id = SHDMA_SLAVE_SDHI0_RX, + .addr = 0x04ce0030, + .chcr = DM_INC | SM_FIX | RS_ERS | TS_INDEX2VAL(XMIT_SZ_16BIT), + .mid_rid = 0xc2, + }, +}; + +static const struct sh_dmae_channel sh7722_dmae_channels[] = { + { + .offset = 0, + .dmars = 0, + .dmars_bit = 0, + }, { + .offset = 0x10, + .dmars = 0, + .dmars_bit = 8, + }, { + .offset = 0x20, + .dmars = 4, + .dmars_bit = 0, + }, { + .offset = 0x30, + .dmars = 4, + .dmars_bit = 8, + }, { + .offset = 0x50, + .dmars = 8, + .dmars_bit = 0, + }, { + .offset = 0x60, + .dmars = 8, + .dmars_bit = 8, + } +}; + +static const unsigned int ts_shift[] = TS_SHIFT; + +static struct sh_dmae_pdata dma_platform_data = { + .slave = sh7722_dmae_slaves, + .slave_num = ARRAY_SIZE(sh7722_dmae_slaves), + .channel = sh7722_dmae_channels, + .channel_num = ARRAY_SIZE(sh7722_dmae_channels), + .ts_low_shift = CHCR_TS_LOW_SHIFT, + .ts_low_mask = CHCR_TS_LOW_MASK, + .ts_high_shift = CHCR_TS_HIGH_SHIFT, + .ts_high_mask = CHCR_TS_HIGH_MASK, + .ts_shift = ts_shift, + .ts_shift_num = ARRAY_SIZE(ts_shift), + .dmaor_init = DMAOR_INIT, +}; + +static struct resource sh7722_dmae_resources[] = { + [0] = { + /* Channel registers and DMAOR */ + .start = 0xfe008020, + .end = 0xfe00808f, + .flags = IORESOURCE_MEM, + }, + [1] = { + /* DMARSx */ + .start = 0xfe009000, + .end = 0xfe00900b, + .flags = IORESOURCE_MEM, + }, + { + .name = "error_irq", + .start = evt2irq(0xbc0), + .end = evt2irq(0xbc0), + .flags = IORESOURCE_IRQ, + }, + { + /* IRQ for channels 0-3 */ + .start = evt2irq(0x800), + .end = evt2irq(0x860), + .flags = IORESOURCE_IRQ, + }, + { + /* IRQ for channels 4-5 */ + .start = evt2irq(0xb80), + .end = evt2irq(0xba0), + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device dma_device = { + .name = "sh-dma-engine", + .id = -1, + .resource = sh7722_dmae_resources, + .num_resources = ARRAY_SIZE(sh7722_dmae_resources), + .dev = { + .platform_data = &dma_platform_data, + }, +}; + +/* Serial */ +static struct plat_sci_port scif0_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .ops = &sh7722_sci_port_ops, + .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE, +}; + +static struct resource scif0_resources[] = { + DEFINE_RES_MEM(0xffe00000, 0x100), + DEFINE_RES_IRQ(evt2irq(0xc00)), +}; + +static struct platform_device scif0_device = { + .name = "sh-sci", + .id = 0, + .resource = scif0_resources, + .num_resources = ARRAY_SIZE(scif0_resources), + .dev = { + .platform_data = &scif0_platform_data, + }, +}; + +static struct plat_sci_port scif1_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .ops = &sh7722_sci_port_ops, + .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE, +}; + +static struct resource scif1_resources[] = { + DEFINE_RES_MEM(0xffe10000, 0x100), + DEFINE_RES_IRQ(evt2irq(0xc20)), +}; + +static struct platform_device scif1_device = { + .name = "sh-sci", + .id = 1, + .resource = scif1_resources, + .num_resources = ARRAY_SIZE(scif1_resources), + .dev = { + .platform_data = &scif1_platform_data, + }, +}; + +static struct plat_sci_port scif2_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .ops = &sh7722_sci_port_ops, + .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE, +}; + +static struct resource scif2_resources[] = { + DEFINE_RES_MEM(0xffe20000, 0x100), + DEFINE_RES_IRQ(evt2irq(0xc40)), +}; + +static struct platform_device scif2_device = { + .name = "sh-sci", + .id = 2, + .resource = scif2_resources, + .num_resources = ARRAY_SIZE(scif2_resources), + .dev = { + .platform_data = &scif2_platform_data, + }, +}; + +static struct resource rtc_resources[] = { + [0] = { + .start = 0xa465fec0, + .end = 0xa465fec0 + 0x58 - 1, + .flags = IORESOURCE_IO, + }, + [1] = { + /* Period IRQ */ + .start = evt2irq(0x7a0), + .flags = IORESOURCE_IRQ, + }, + [2] = { + /* Carry IRQ */ + .start = evt2irq(0x7c0), + .flags = IORESOURCE_IRQ, + }, + [3] = { + /* Alarm IRQ */ + .start = evt2irq(0x780), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device rtc_device = { + .name = "sh-rtc", + .id = -1, + .num_resources = ARRAY_SIZE(rtc_resources), + .resource = rtc_resources, +}; + +static struct m66592_platdata usbf_platdata = { + .on_chip = 1, +}; + +static struct resource usbf_resources[] = { + [0] = { + .name = "USBF", + .start = 0x04480000, + .end = 0x044800FF, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = evt2irq(0xa20), + .end = evt2irq(0xa20), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device usbf_device = { + .name = "m66592_udc", + .id = 0, /* "usbf0" clock */ + .dev = { + .dma_mask = NULL, + .coherent_dma_mask = 0xffffffff, + .platform_data = &usbf_platdata, + }, + .num_resources = ARRAY_SIZE(usbf_resources), + .resource = usbf_resources, +}; + +static struct resource iic_resources[] = { + [0] = { + .name = "IIC", + .start = 0x04470000, + .end = 0x04470017, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = evt2irq(0xe00), + .end = evt2irq(0xe60), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device iic_device = { + .name = "i2c-sh_mobile", + .id = 0, /* "i2c0" clock */ + .num_resources = ARRAY_SIZE(iic_resources), + .resource = iic_resources, +}; + +static struct uio_info vpu_platform_data = { + .name = "VPU4", + .version = "0", + .irq = evt2irq(0x980), +}; + +static struct resource vpu_resources[] = { + [0] = { + .name = "VPU", + .start = 0xfe900000, + .end = 0xfe9022eb, + .flags = IORESOURCE_MEM, + }, + [1] = { + /* place holder for contiguous memory */ + }, +}; + +static struct platform_device vpu_device = { + .name = "uio_pdrv_genirq", + .id = 0, + .dev = { + .platform_data = &vpu_platform_data, + }, + .resource = vpu_resources, + .num_resources = ARRAY_SIZE(vpu_resources), +}; + +static struct uio_info veu_platform_data = { + .name = "VEU", + .version = "0", + .irq = evt2irq(0x8c0), +}; + +static struct resource veu_resources[] = { + [0] = { + .name = "VEU", + .start = 0xfe920000, + .end = 0xfe9200b7, + .flags = IORESOURCE_MEM, + }, + [1] = { + /* place holder for contiguous memory */ + }, +}; + +static struct platform_device veu_device = { + .name = "uio_pdrv_genirq", + .id = 1, + .dev = { + .platform_data = &veu_platform_data, + }, + .resource = veu_resources, + .num_resources = ARRAY_SIZE(veu_resources), +}; + +static struct uio_info jpu_platform_data = { + .name = "JPU", + .version = "0", + .irq = evt2irq(0x560), +}; + +static struct resource jpu_resources[] = { + [0] = { + .name = "JPU", + .start = 0xfea00000, + .end = 0xfea102d3, + .flags = IORESOURCE_MEM, + }, + [1] = { + /* place holder for contiguous memory */ + }, +}; + +static struct platform_device jpu_device = { + .name = "uio_pdrv_genirq", + .id = 2, + .dev = { + .platform_data = &jpu_platform_data, + }, + .resource = jpu_resources, + .num_resources = ARRAY_SIZE(jpu_resources), +}; + +static struct sh_timer_config cmt_platform_data = { + .channels_mask = 0x20, +}; + +static struct resource cmt_resources[] = { + DEFINE_RES_MEM(0x044a0000, 0x70), + DEFINE_RES_IRQ(evt2irq(0xf00)), +}; + +static struct platform_device cmt_device = { + .name = "sh-cmt-32", + .id = 0, + .dev = { + .platform_data = &cmt_platform_data, + }, + .resource = cmt_resources, + .num_resources = ARRAY_SIZE(cmt_resources), +}; + +static struct sh_timer_config tmu0_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu0_resources[] = { + DEFINE_RES_MEM(0xffd80000, 0x2c), + DEFINE_RES_IRQ(evt2irq(0x400)), + DEFINE_RES_IRQ(evt2irq(0x420)), + DEFINE_RES_IRQ(evt2irq(0x440)), +}; + +static struct platform_device tmu0_device = { + .name = "sh-tmu", + .id = 0, + .dev = { + .platform_data = &tmu0_platform_data, + }, + .resource = tmu0_resources, + .num_resources = ARRAY_SIZE(tmu0_resources), +}; + +static struct siu_platform siu_platform_data = { + .dma_slave_tx_a = SHDMA_SLAVE_SIUA_TX, + .dma_slave_rx_a = SHDMA_SLAVE_SIUA_RX, + .dma_slave_tx_b = SHDMA_SLAVE_SIUB_TX, + .dma_slave_rx_b = SHDMA_SLAVE_SIUB_RX, +}; + +static struct resource siu_resources[] = { + [0] = { + .start = 0xa4540000, + .end = 0xa454c10f, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = evt2irq(0xf80), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device siu_device = { + .name = "siu-pcm-audio", + .id = -1, + .dev = { + .platform_data = &siu_platform_data, + }, + .resource = siu_resources, + .num_resources = ARRAY_SIZE(siu_resources), +}; + +static struct platform_device *sh7722_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + &cmt_device, + &tmu0_device, + &rtc_device, + &usbf_device, + &iic_device, + &vpu_device, + &veu_device, + &jpu_device, + &siu_device, + &dma_device, +}; + +static int __init sh7722_devices_setup(void) +{ + platform_resource_setup_memory(&vpu_device, "vpu", 1 << 20); + platform_resource_setup_memory(&veu_device, "veu", 2 << 20); + platform_resource_setup_memory(&jpu_device, "jpu", 2 << 20); + + return platform_add_devices(sh7722_devices, + ARRAY_SIZE(sh7722_devices)); +} +arch_initcall(sh7722_devices_setup); + +static struct platform_device *sh7722_early_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + &cmt_device, + &tmu0_device, +}; + +void __init plat_early_device_setup(void) +{ + early_platform_add_devices(sh7722_early_devices, + ARRAY_SIZE(sh7722_early_devices)); +} + +enum { + UNUSED=0, + ENABLED, + DISABLED, + + /* interrupt sources */ + IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7, + HUDI, + SIM_ERI, SIM_RXI, SIM_TXI, SIM_TEI, + RTC_ATI, RTC_PRI, RTC_CUI, + DMAC0, DMAC1, DMAC2, DMAC3, + VIO_CEUI, VIO_BEUI, VIO_VEUI, VOU, + VPU, TPU, + USB_USBI0, USB_USBI1, + DMAC4, DMAC5, DMAC_DADERR, + KEYSC, + SCIF0, SCIF1, SCIF2, SIOF0, SIOF1, SIO, + FLCTL_FLSTEI, FLCTL_FLENDI, FLCTL_FLTREQ0I, FLCTL_FLTREQ1I, + I2C_ALI, I2C_TACKI, I2C_WAITI, I2C_DTEI, + CMT, TSIF, SIU, TWODG, + TMU0, TMU1, TMU2, + IRDA, JPU, LCDC, + + /* interrupt groups */ + SIM, RTC, DMAC0123, VIOVOU, USB, DMAC45, FLCTL, I2C, SDHI, +}; + +static struct intc_vect vectors[] __initdata = { + INTC_VECT(IRQ0, 0x600), INTC_VECT(IRQ1, 0x620), + INTC_VECT(IRQ2, 0x640), INTC_VECT(IRQ3, 0x660), + INTC_VECT(IRQ4, 0x680), INTC_VECT(IRQ5, 0x6a0), + INTC_VECT(IRQ6, 0x6c0), INTC_VECT(IRQ7, 0x6e0), + INTC_VECT(SIM_ERI, 0x700), INTC_VECT(SIM_RXI, 0x720), + INTC_VECT(SIM_TXI, 0x740), INTC_VECT(SIM_TEI, 0x760), + INTC_VECT(RTC_ATI, 0x780), INTC_VECT(RTC_PRI, 0x7a0), + INTC_VECT(RTC_CUI, 0x7c0), + INTC_VECT(DMAC0, 0x800), INTC_VECT(DMAC1, 0x820), + INTC_VECT(DMAC2, 0x840), INTC_VECT(DMAC3, 0x860), + INTC_VECT(VIO_CEUI, 0x880), INTC_VECT(VIO_BEUI, 0x8a0), + INTC_VECT(VIO_VEUI, 0x8c0), INTC_VECT(VOU, 0x8e0), + INTC_VECT(VPU, 0x980), INTC_VECT(TPU, 0x9a0), + INTC_VECT(USB_USBI0, 0xa20), INTC_VECT(USB_USBI1, 0xa40), + INTC_VECT(DMAC4, 0xb80), INTC_VECT(DMAC5, 0xba0), + INTC_VECT(DMAC_DADERR, 0xbc0), INTC_VECT(KEYSC, 0xbe0), + INTC_VECT(SCIF0, 0xc00), INTC_VECT(SCIF1, 0xc20), + INTC_VECT(SCIF2, 0xc40), INTC_VECT(SIOF0, 0xc80), + INTC_VECT(SIOF1, 0xca0), INTC_VECT(SIO, 0xd00), + INTC_VECT(FLCTL_FLSTEI, 0xd80), INTC_VECT(FLCTL_FLENDI, 0xda0), + INTC_VECT(FLCTL_FLTREQ0I, 0xdc0), INTC_VECT(FLCTL_FLTREQ1I, 0xde0), + INTC_VECT(I2C_ALI, 0xe00), INTC_VECT(I2C_TACKI, 0xe20), + INTC_VECT(I2C_WAITI, 0xe40), INTC_VECT(I2C_DTEI, 0xe60), + INTC_VECT(SDHI, 0xe80), INTC_VECT(SDHI, 0xea0), + INTC_VECT(SDHI, 0xec0), INTC_VECT(SDHI, 0xee0), + INTC_VECT(CMT, 0xf00), INTC_VECT(TSIF, 0xf20), + INTC_VECT(SIU, 0xf80), INTC_VECT(TWODG, 0xfa0), + INTC_VECT(TMU0, 0x400), INTC_VECT(TMU1, 0x420), + INTC_VECT(TMU2, 0x440), INTC_VECT(IRDA, 0x480), + INTC_VECT(JPU, 0x560), INTC_VECT(LCDC, 0x580), +}; + +static struct intc_group groups[] __initdata = { + INTC_GROUP(SIM, SIM_ERI, SIM_RXI, SIM_TXI, SIM_TEI), + INTC_GROUP(RTC, RTC_ATI, RTC_PRI, RTC_CUI), + INTC_GROUP(DMAC0123, DMAC0, DMAC1, DMAC2, DMAC3), + INTC_GROUP(VIOVOU, VIO_CEUI, VIO_BEUI, VIO_VEUI, VOU), + INTC_GROUP(USB, USB_USBI0, USB_USBI1), + INTC_GROUP(DMAC45, DMAC4, DMAC5, DMAC_DADERR), + INTC_GROUP(FLCTL, FLCTL_FLSTEI, FLCTL_FLENDI, + FLCTL_FLTREQ0I, FLCTL_FLTREQ1I), + INTC_GROUP(I2C, I2C_ALI, I2C_TACKI, I2C_WAITI, I2C_DTEI), +}; + +static struct intc_mask_reg mask_registers[] __initdata = { + { 0xa4080080, 0xa40800c0, 8, /* IMR0 / IMCR0 */ + { } }, + { 0xa4080084, 0xa40800c4, 8, /* IMR1 / IMCR1 */ + { VOU, VIO_VEUI, VIO_BEUI, VIO_CEUI, DMAC3, DMAC2, DMAC1, DMAC0 } }, + { 0xa4080088, 0xa40800c8, 8, /* IMR2 / IMCR2 */ + { 0, 0, 0, VPU, } }, + { 0xa408008c, 0xa40800cc, 8, /* IMR3 / IMCR3 */ + { SIM_TEI, SIM_TXI, SIM_RXI, SIM_ERI, 0, 0, 0, IRDA } }, + { 0xa4080090, 0xa40800d0, 8, /* IMR4 / IMCR4 */ + { 0, TMU2, TMU1, TMU0, JPU, 0, 0, LCDC } }, + { 0xa4080094, 0xa40800d4, 8, /* IMR5 / IMCR5 */ + { KEYSC, DMAC_DADERR, DMAC5, DMAC4, 0, SCIF2, SCIF1, SCIF0 } }, + { 0xa4080098, 0xa40800d8, 8, /* IMR6 / IMCR6 */ + { 0, 0, 0, SIO, 0, 0, SIOF1, SIOF0 } }, + { 0xa408009c, 0xa40800dc, 8, /* IMR7 / IMCR7 */ + { I2C_DTEI, I2C_WAITI, I2C_TACKI, I2C_ALI, + FLCTL_FLTREQ1I, FLCTL_FLTREQ0I, FLCTL_FLENDI, FLCTL_FLSTEI } }, + { 0xa40800a0, 0xa40800e0, 8, /* IMR8 / IMCR8 */ + { DISABLED, ENABLED, ENABLED, ENABLED, 0, 0, TWODG, SIU } }, + { 0xa40800a4, 0xa40800e4, 8, /* IMR9 / IMCR9 */ + { 0, 0, 0, CMT, 0, USB_USBI1, USB_USBI0, } }, + { 0xa40800a8, 0xa40800e8, 8, /* IMR10 / IMCR10 */ + { } }, + { 0xa40800ac, 0xa40800ec, 8, /* IMR11 / IMCR11 */ + { 0, RTC_CUI, RTC_PRI, RTC_ATI, 0, TPU, 0, TSIF } }, + { 0xa4140044, 0xa4140064, 8, /* INTMSK00 / INTMSKCLR00 */ + { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } }, +}; + +static struct intc_prio_reg prio_registers[] __initdata = { + { 0xa4080000, 0, 16, 4, /* IPRA */ { TMU0, TMU1, TMU2, IRDA } }, + { 0xa4080004, 0, 16, 4, /* IPRB */ { JPU, LCDC, SIM } }, + { 0xa4080008, 0, 16, 4, /* IPRC */ { } }, + { 0xa408000c, 0, 16, 4, /* IPRD */ { } }, + { 0xa4080010, 0, 16, 4, /* IPRE */ { DMAC0123, VIOVOU, 0, VPU } }, + { 0xa4080014, 0, 16, 4, /* IPRF */ { KEYSC, DMAC45, USB, CMT } }, + { 0xa4080018, 0, 16, 4, /* IPRG */ { SCIF0, SCIF1, SCIF2 } }, + { 0xa408001c, 0, 16, 4, /* IPRH */ { SIOF0, SIOF1, FLCTL, I2C } }, + { 0xa4080020, 0, 16, 4, /* IPRI */ { SIO, 0, TSIF, RTC } }, + { 0xa4080024, 0, 16, 4, /* IPRJ */ { 0, 0, SIU } }, + { 0xa4080028, 0, 16, 4, /* IPRK */ { 0, 0, 0, SDHI } }, + { 0xa408002c, 0, 16, 4, /* IPRL */ { TWODG, 0, TPU } }, + { 0xa4140010, 0, 32, 4, /* INTPRI00 */ + { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } }, +}; + +static struct intc_sense_reg sense_registers[] __initdata = { + { 0xa414001c, 16, 2, /* ICR1 */ + { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } }, +}; + +static struct intc_mask_reg ack_registers[] __initdata = { + { 0xa4140024, 0, 8, /* INTREQ00 */ + { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } }, +}; + +static struct intc_desc intc_desc __initdata = { + .name = "sh7722", + .force_enable = ENABLED, + .force_disable = DISABLED, + .hw = INTC_HW_DESC(vectors, groups, mask_registers, + prio_registers, sense_registers, ack_registers), +}; + +void __init plat_irq_setup(void) +{ + register_intc_controller(&intc_desc); +} + +void __init plat_mem_setup(void) +{ + /* Register the URAM space as Node 1 */ + setup_bootmem_node(1, 0x055f0000, 0x05610000); +} diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7723.c b/arch/sh/kernel/cpu/sh4a/setup-sh7723.c new file mode 100644 index 000000000..1c1b3c469 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7723.c @@ -0,0 +1,646 @@ +/* + * SH7723 Setup + * + * 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/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <linux/mm.h> +#include <linux/serial_sci.h> +#include <linux/uio_driver.h> +#include <linux/usb/r8a66597.h> +#include <linux/sh_timer.h> +#include <linux/sh_intc.h> +#include <linux/io.h> +#include <asm/clock.h> +#include <asm/mmzone.h> +#include <cpu/sh7723.h> + +/* Serial */ +static struct plat_sci_port scif0_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE, +}; + +static struct resource scif0_resources[] = { + DEFINE_RES_MEM(0xffe00000, 0x100), + DEFINE_RES_IRQ(evt2irq(0xc00)), +}; + +static struct platform_device scif0_device = { + .name = "sh-sci", + .id = 0, + .resource = scif0_resources, + .num_resources = ARRAY_SIZE(scif0_resources), + .dev = { + .platform_data = &scif0_platform_data, + }, +}; + +static struct plat_sci_port scif1_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE, +}; + +static struct resource scif1_resources[] = { + DEFINE_RES_MEM(0xffe10000, 0x100), + DEFINE_RES_IRQ(evt2irq(0xc20)), +}; + +static struct platform_device scif1_device = { + .name = "sh-sci", + .id = 1, + .resource = scif1_resources, + .num_resources = ARRAY_SIZE(scif1_resources), + .dev = { + .platform_data = &scif1_platform_data, + }, +}; + +static struct plat_sci_port scif2_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE, +}; + +static struct resource scif2_resources[] = { + DEFINE_RES_MEM(0xffe20000, 0x100), + DEFINE_RES_IRQ(evt2irq(0xc40)), +}; + +static struct platform_device scif2_device = { + .name = "sh-sci", + .id = 2, + .resource = scif2_resources, + .num_resources = ARRAY_SIZE(scif2_resources), + .dev = { + .platform_data = &scif2_platform_data, + }, +}; + +static struct plat_sci_port scif3_platform_data = { + .sampling_rate = 8, + .type = PORT_SCIFA, +}; + +static struct resource scif3_resources[] = { + DEFINE_RES_MEM(0xa4e30000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x900)), +}; + +static struct platform_device scif3_device = { + .name = "sh-sci", + .id = 3, + .resource = scif3_resources, + .num_resources = ARRAY_SIZE(scif3_resources), + .dev = { + .platform_data = &scif3_platform_data, + }, +}; + +static struct plat_sci_port scif4_platform_data = { + .sampling_rate = 8, + .type = PORT_SCIFA, +}; + +static struct resource scif4_resources[] = { + DEFINE_RES_MEM(0xa4e40000, 0x100), + DEFINE_RES_IRQ(evt2irq(0xd00)), +}; + +static struct platform_device scif4_device = { + .name = "sh-sci", + .id = 4, + .resource = scif4_resources, + .num_resources = ARRAY_SIZE(scif4_resources), + .dev = { + .platform_data = &scif4_platform_data, + }, +}; + +static struct plat_sci_port scif5_platform_data = { + .sampling_rate = 8, + .type = PORT_SCIFA, +}; + +static struct resource scif5_resources[] = { + DEFINE_RES_MEM(0xa4e50000, 0x100), + DEFINE_RES_IRQ(evt2irq(0xfa0)), +}; + +static struct platform_device scif5_device = { + .name = "sh-sci", + .id = 5, + .resource = scif5_resources, + .num_resources = ARRAY_SIZE(scif5_resources), + .dev = { + .platform_data = &scif5_platform_data, + }, +}; + +static struct uio_info vpu_platform_data = { + .name = "VPU5", + .version = "0", + .irq = evt2irq(0x980), +}; + +static struct resource vpu_resources[] = { + [0] = { + .name = "VPU", + .start = 0xfe900000, + .end = 0xfe902807, + .flags = IORESOURCE_MEM, + }, + [1] = { + /* place holder for contiguous memory */ + }, +}; + +static struct platform_device vpu_device = { + .name = "uio_pdrv_genirq", + .id = 0, + .dev = { + .platform_data = &vpu_platform_data, + }, + .resource = vpu_resources, + .num_resources = ARRAY_SIZE(vpu_resources), +}; + +static struct uio_info veu0_platform_data = { + .name = "VEU2H", + .version = "0", + .irq = evt2irq(0x8c0), +}; + +static struct resource veu0_resources[] = { + [0] = { + .name = "VEU2H0", + .start = 0xfe920000, + .end = 0xfe92027b, + .flags = IORESOURCE_MEM, + }, + [1] = { + /* place holder for contiguous memory */ + }, +}; + +static struct platform_device veu0_device = { + .name = "uio_pdrv_genirq", + .id = 1, + .dev = { + .platform_data = &veu0_platform_data, + }, + .resource = veu0_resources, + .num_resources = ARRAY_SIZE(veu0_resources), +}; + +static struct uio_info veu1_platform_data = { + .name = "VEU2H", + .version = "0", + .irq = evt2irq(0x560), +}; + +static struct resource veu1_resources[] = { + [0] = { + .name = "VEU2H1", + .start = 0xfe924000, + .end = 0xfe92427b, + .flags = IORESOURCE_MEM, + }, + [1] = { + /* place holder for contiguous memory */ + }, +}; + +static struct platform_device veu1_device = { + .name = "uio_pdrv_genirq", + .id = 2, + .dev = { + .platform_data = &veu1_platform_data, + }, + .resource = veu1_resources, + .num_resources = ARRAY_SIZE(veu1_resources), +}; + +static struct sh_timer_config cmt_platform_data = { + .channels_mask = 0x20, +}; + +static struct resource cmt_resources[] = { + DEFINE_RES_MEM(0x044a0000, 0x70), + DEFINE_RES_IRQ(evt2irq(0xf00)), +}; + +static struct platform_device cmt_device = { + .name = "sh-cmt-32", + .id = 0, + .dev = { + .platform_data = &cmt_platform_data, + }, + .resource = cmt_resources, + .num_resources = ARRAY_SIZE(cmt_resources), +}; + +static struct sh_timer_config tmu0_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu0_resources[] = { + DEFINE_RES_MEM(0xffd80000, 0x2c), + DEFINE_RES_IRQ(evt2irq(0x400)), + DEFINE_RES_IRQ(evt2irq(0x420)), + DEFINE_RES_IRQ(evt2irq(0x440)), +}; + +static struct platform_device tmu0_device = { + .name = "sh-tmu", + .id = 0, + .dev = { + .platform_data = &tmu0_platform_data, + }, + .resource = tmu0_resources, + .num_resources = ARRAY_SIZE(tmu0_resources), +}; + +static struct sh_timer_config tmu1_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu1_resources[] = { + DEFINE_RES_MEM(0xffd90000, 0x2c), + DEFINE_RES_IRQ(evt2irq(0x920)), + DEFINE_RES_IRQ(evt2irq(0x940)), + DEFINE_RES_IRQ(evt2irq(0x960)), +}; + +static struct platform_device tmu1_device = { + .name = "sh-tmu", + .id = 1, + .dev = { + .platform_data = &tmu1_platform_data, + }, + .resource = tmu1_resources, + .num_resources = ARRAY_SIZE(tmu1_resources), +}; + +static struct resource rtc_resources[] = { + [0] = { + .start = 0xa465fec0, + .end = 0xa465fec0 + 0x58 - 1, + .flags = IORESOURCE_IO, + }, + [1] = { + /* Period IRQ */ + .start = evt2irq(0xaa0), + .flags = IORESOURCE_IRQ, + }, + [2] = { + /* Carry IRQ */ + .start = evt2irq(0xac0), + .flags = IORESOURCE_IRQ, + }, + [3] = { + /* Alarm IRQ */ + .start = evt2irq(0xa80), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device rtc_device = { + .name = "sh-rtc", + .id = -1, + .num_resources = ARRAY_SIZE(rtc_resources), + .resource = rtc_resources, +}; + +static struct r8a66597_platdata r8a66597_data = { + .on_chip = 1, +}; + +static struct resource sh7723_usb_host_resources[] = { + [0] = { + .start = 0xa4d80000, + .end = 0xa4d800ff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = evt2irq(0xa20), + .end = evt2irq(0xa20), + .flags = IORESOURCE_IRQ | IRQF_TRIGGER_LOW, + }, +}; + +static struct platform_device sh7723_usb_host_device = { + .name = "r8a66597_hcd", + .id = 0, + .dev = { + .dma_mask = NULL, /* not use dma */ + .coherent_dma_mask = 0xffffffff, + .platform_data = &r8a66597_data, + }, + .num_resources = ARRAY_SIZE(sh7723_usb_host_resources), + .resource = sh7723_usb_host_resources, +}; + +static struct resource iic_resources[] = { + [0] = { + .name = "IIC", + .start = 0x04470000, + .end = 0x04470017, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = evt2irq(0xe00), + .end = evt2irq(0xe60), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device iic_device = { + .name = "i2c-sh_mobile", + .id = 0, /* "i2c0" clock */ + .num_resources = ARRAY_SIZE(iic_resources), + .resource = iic_resources, +}; + +static struct platform_device *sh7723_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + &scif3_device, + &scif4_device, + &scif5_device, + &cmt_device, + &tmu0_device, + &tmu1_device, + &rtc_device, + &iic_device, + &sh7723_usb_host_device, + &vpu_device, + &veu0_device, + &veu1_device, +}; + +static int __init sh7723_devices_setup(void) +{ + platform_resource_setup_memory(&vpu_device, "vpu", 2 << 20); + platform_resource_setup_memory(&veu0_device, "veu0", 2 << 20); + platform_resource_setup_memory(&veu1_device, "veu1", 2 << 20); + + return platform_add_devices(sh7723_devices, + ARRAY_SIZE(sh7723_devices)); +} +arch_initcall(sh7723_devices_setup); + +static struct platform_device *sh7723_early_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + &scif3_device, + &scif4_device, + &scif5_device, + &cmt_device, + &tmu0_device, + &tmu1_device, +}; + +void __init plat_early_device_setup(void) +{ + early_platform_add_devices(sh7723_early_devices, + ARRAY_SIZE(sh7723_early_devices)); +} + +#define RAMCR_CACHE_L2FC 0x0002 +#define RAMCR_CACHE_L2E 0x0001 +#define L2_CACHE_ENABLE (RAMCR_CACHE_L2E|RAMCR_CACHE_L2FC) + +void l2_cache_init(void) +{ + /* Enable L2 cache */ + __raw_writel(L2_CACHE_ENABLE, RAMCR); +} + +enum { + UNUSED=0, + ENABLED, + DISABLED, + + /* interrupt sources */ + IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7, + HUDI, + DMAC1A_DEI0,DMAC1A_DEI1,DMAC1A_DEI2,DMAC1A_DEI3, + _2DG_TRI,_2DG_INI,_2DG_CEI, + DMAC0A_DEI0,DMAC0A_DEI1,DMAC0A_DEI2,DMAC0A_DEI3, + VIO_CEUI,VIO_BEUI,VIO_VEU2HI,VIO_VOUI, + SCIFA_SCIFA0, + VPU_VPUI, + TPU_TPUI, + ADC_ADI, + USB_USI0, + RTC_ATI,RTC_PRI,RTC_CUI, + DMAC1B_DEI4,DMAC1B_DEI5,DMAC1B_DADERR, + DMAC0B_DEI4,DMAC0B_DEI5,DMAC0B_DADERR, + KEYSC_KEYI, + SCIF_SCIF0,SCIF_SCIF1,SCIF_SCIF2, + MSIOF_MSIOFI0,MSIOF_MSIOFI1, + SCIFA_SCIFA1, + FLCTL_FLSTEI,FLCTL_FLTENDI,FLCTL_FLTREQ0I,FLCTL_FLTREQ1I, + I2C_ALI,I2C_TACKI,I2C_WAITI,I2C_DTEI, + CMT_CMTI, + TSIF_TSIFI, + SIU_SIUI, + SCIFA_SCIFA2, + TMU0_TUNI0, TMU0_TUNI1, TMU0_TUNI2, + IRDA_IRDAI, + ATAPI_ATAPII, + VEU2H1_VEU2HI, + LCDC_LCDCI, + TMU1_TUNI0,TMU1_TUNI1,TMU1_TUNI2, + + /* interrupt groups */ + DMAC1A, DMAC0A, VIO, DMAC0B, FLCTL, I2C, _2DG, + SDHI1, RTC, DMAC1B, SDHI0, +}; + +static struct intc_vect vectors[] __initdata = { + INTC_VECT(IRQ0, 0x600), INTC_VECT(IRQ1, 0x620), + INTC_VECT(IRQ2, 0x640), INTC_VECT(IRQ3, 0x660), + INTC_VECT(IRQ4, 0x680), INTC_VECT(IRQ5, 0x6a0), + INTC_VECT(IRQ6, 0x6c0), INTC_VECT(IRQ7, 0x6e0), + + INTC_VECT(DMAC1A_DEI0,0x700), + INTC_VECT(DMAC1A_DEI1,0x720), + INTC_VECT(DMAC1A_DEI2,0x740), + INTC_VECT(DMAC1A_DEI3,0x760), + + INTC_VECT(_2DG_TRI, 0x780), + INTC_VECT(_2DG_INI, 0x7A0), + INTC_VECT(_2DG_CEI, 0x7C0), + + INTC_VECT(DMAC0A_DEI0,0x800), + INTC_VECT(DMAC0A_DEI1,0x820), + INTC_VECT(DMAC0A_DEI2,0x840), + INTC_VECT(DMAC0A_DEI3,0x860), + + INTC_VECT(VIO_CEUI,0x880), + INTC_VECT(VIO_BEUI,0x8A0), + INTC_VECT(VIO_VEU2HI,0x8C0), + INTC_VECT(VIO_VOUI,0x8E0), + + INTC_VECT(SCIFA_SCIFA0,0x900), + INTC_VECT(VPU_VPUI,0x980), + INTC_VECT(TPU_TPUI,0x9A0), + INTC_VECT(ADC_ADI,0x9E0), + INTC_VECT(USB_USI0,0xA20), + + INTC_VECT(RTC_ATI,0xA80), + INTC_VECT(RTC_PRI,0xAA0), + INTC_VECT(RTC_CUI,0xAC0), + + INTC_VECT(DMAC1B_DEI4,0xB00), + INTC_VECT(DMAC1B_DEI5,0xB20), + INTC_VECT(DMAC1B_DADERR,0xB40), + + INTC_VECT(DMAC0B_DEI4,0xB80), + INTC_VECT(DMAC0B_DEI5,0xBA0), + INTC_VECT(DMAC0B_DADERR,0xBC0), + + INTC_VECT(KEYSC_KEYI,0xBE0), + INTC_VECT(SCIF_SCIF0,0xC00), + INTC_VECT(SCIF_SCIF1,0xC20), + INTC_VECT(SCIF_SCIF2,0xC40), + INTC_VECT(MSIOF_MSIOFI0,0xC80), + INTC_VECT(MSIOF_MSIOFI1,0xCA0), + INTC_VECT(SCIFA_SCIFA1,0xD00), + + INTC_VECT(FLCTL_FLSTEI,0xD80), + INTC_VECT(FLCTL_FLTENDI,0xDA0), + INTC_VECT(FLCTL_FLTREQ0I,0xDC0), + INTC_VECT(FLCTL_FLTREQ1I,0xDE0), + + INTC_VECT(I2C_ALI,0xE00), + INTC_VECT(I2C_TACKI,0xE20), + INTC_VECT(I2C_WAITI,0xE40), + INTC_VECT(I2C_DTEI,0xE60), + + INTC_VECT(SDHI0, 0xE80), + INTC_VECT(SDHI0, 0xEA0), + INTC_VECT(SDHI0, 0xEC0), + + INTC_VECT(CMT_CMTI,0xF00), + INTC_VECT(TSIF_TSIFI,0xF20), + INTC_VECT(SIU_SIUI,0xF80), + INTC_VECT(SCIFA_SCIFA2,0xFA0), + + INTC_VECT(TMU0_TUNI0,0x400), + INTC_VECT(TMU0_TUNI1,0x420), + INTC_VECT(TMU0_TUNI2,0x440), + + INTC_VECT(IRDA_IRDAI,0x480), + INTC_VECT(ATAPI_ATAPII,0x4A0), + + INTC_VECT(SDHI1, 0x4E0), + INTC_VECT(SDHI1, 0x500), + INTC_VECT(SDHI1, 0x520), + + INTC_VECT(VEU2H1_VEU2HI,0x560), + INTC_VECT(LCDC_LCDCI,0x580), + + INTC_VECT(TMU1_TUNI0,0x920), + INTC_VECT(TMU1_TUNI1,0x940), + INTC_VECT(TMU1_TUNI2,0x960), + +}; + +static struct intc_group groups[] __initdata = { + INTC_GROUP(DMAC1A,DMAC1A_DEI0,DMAC1A_DEI1,DMAC1A_DEI2,DMAC1A_DEI3), + INTC_GROUP(DMAC0A,DMAC0A_DEI0,DMAC0A_DEI1,DMAC0A_DEI2,DMAC0A_DEI3), + INTC_GROUP(VIO, VIO_CEUI,VIO_BEUI,VIO_VEU2HI,VIO_VOUI), + INTC_GROUP(DMAC0B, DMAC0B_DEI4,DMAC0B_DEI5,DMAC0B_DADERR), + INTC_GROUP(FLCTL,FLCTL_FLSTEI,FLCTL_FLTENDI,FLCTL_FLTREQ0I,FLCTL_FLTREQ1I), + INTC_GROUP(I2C,I2C_ALI,I2C_TACKI,I2C_WAITI,I2C_DTEI), + INTC_GROUP(_2DG, _2DG_TRI,_2DG_INI,_2DG_CEI), + INTC_GROUP(RTC, RTC_ATI,RTC_PRI,RTC_CUI), + INTC_GROUP(DMAC1B, DMAC1B_DEI4,DMAC1B_DEI5,DMAC1B_DADERR), +}; + +static struct intc_mask_reg mask_registers[] __initdata = { + { 0xa4080080, 0xa40800c0, 8, /* IMR0 / IMCR0 */ + { 0, TMU1_TUNI2, TMU1_TUNI1, TMU1_TUNI0, + 0, ENABLED, ENABLED, ENABLED } }, + { 0xa4080084, 0xa40800c4, 8, /* IMR1 / IMCR1 */ + { VIO_VOUI, VIO_VEU2HI,VIO_BEUI,VIO_CEUI,DMAC0A_DEI3,DMAC0A_DEI2,DMAC0A_DEI1,DMAC0A_DEI0 } }, + { 0xa4080088, 0xa40800c8, 8, /* IMR2 / IMCR2 */ + { 0, 0, 0, VPU_VPUI,0,0,0,SCIFA_SCIFA0 } }, + { 0xa408008c, 0xa40800cc, 8, /* IMR3 / IMCR3 */ + { DMAC1A_DEI3,DMAC1A_DEI2,DMAC1A_DEI1,DMAC1A_DEI0,0,0,0,IRDA_IRDAI } }, + { 0xa4080090, 0xa40800d0, 8, /* IMR4 / IMCR4 */ + { 0,TMU0_TUNI2,TMU0_TUNI1,TMU0_TUNI0,VEU2H1_VEU2HI,0,0,LCDC_LCDCI } }, + { 0xa4080094, 0xa40800d4, 8, /* IMR5 / IMCR5 */ + { KEYSC_KEYI,DMAC0B_DADERR,DMAC0B_DEI5,DMAC0B_DEI4,0,SCIF_SCIF2,SCIF_SCIF1,SCIF_SCIF0 } }, + { 0xa4080098, 0xa40800d8, 8, /* IMR6 / IMCR6 */ + { 0,0,0,SCIFA_SCIFA1,ADC_ADI,0,MSIOF_MSIOFI1,MSIOF_MSIOFI0 } }, + { 0xa408009c, 0xa40800dc, 8, /* IMR7 / IMCR7 */ + { I2C_DTEI, I2C_WAITI, I2C_TACKI, I2C_ALI, + FLCTL_FLTREQ1I, FLCTL_FLTREQ0I, FLCTL_FLTENDI, FLCTL_FLSTEI } }, + { 0xa40800a0, 0xa40800e0, 8, /* IMR8 / IMCR8 */ + { 0, ENABLED, ENABLED, ENABLED, + 0, 0, SCIFA_SCIFA2, SIU_SIUI } }, + { 0xa40800a4, 0xa40800e4, 8, /* IMR9 / IMCR9 */ + { 0, 0, 0, CMT_CMTI, 0, 0, USB_USI0,0 } }, + { 0xa40800a8, 0xa40800e8, 8, /* IMR10 / IMCR10 */ + { 0, DMAC1B_DADERR,DMAC1B_DEI5,DMAC1B_DEI4,0,RTC_ATI,RTC_PRI,RTC_CUI } }, + { 0xa40800ac, 0xa40800ec, 8, /* IMR11 / IMCR11 */ + { 0,_2DG_CEI,_2DG_INI,_2DG_TRI,0,TPU_TPUI,0,TSIF_TSIFI } }, + { 0xa40800b0, 0xa40800f0, 8, /* IMR12 / IMCR12 */ + { 0,0,0,0,0,0,0,ATAPI_ATAPII } }, + { 0xa4140044, 0xa4140064, 8, /* INTMSK00 / INTMSKCLR00 */ + { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } }, +}; + +static struct intc_prio_reg prio_registers[] __initdata = { + { 0xa4080000, 0, 16, 4, /* IPRA */ { TMU0_TUNI0, TMU0_TUNI1, TMU0_TUNI2, IRDA_IRDAI } }, + { 0xa4080004, 0, 16, 4, /* IPRB */ { VEU2H1_VEU2HI, LCDC_LCDCI, DMAC1A, 0} }, + { 0xa4080008, 0, 16, 4, /* IPRC */ { TMU1_TUNI0, TMU1_TUNI1, TMU1_TUNI2, 0} }, + { 0xa408000c, 0, 16, 4, /* IPRD */ { } }, + { 0xa4080010, 0, 16, 4, /* IPRE */ { DMAC0A, VIO, SCIFA_SCIFA0, VPU_VPUI } }, + { 0xa4080014, 0, 16, 4, /* IPRF */ { KEYSC_KEYI, DMAC0B, USB_USI0, CMT_CMTI } }, + { 0xa4080018, 0, 16, 4, /* IPRG */ { SCIF_SCIF0, SCIF_SCIF1, SCIF_SCIF2,0 } }, + { 0xa408001c, 0, 16, 4, /* IPRH */ { MSIOF_MSIOFI0,MSIOF_MSIOFI1, FLCTL, I2C } }, + { 0xa4080020, 0, 16, 4, /* IPRI */ { SCIFA_SCIFA1,0,TSIF_TSIFI,_2DG } }, + { 0xa4080024, 0, 16, 4, /* IPRJ */ { ADC_ADI,0,SIU_SIUI,SDHI1 } }, + { 0xa4080028, 0, 16, 4, /* IPRK */ { RTC,DMAC1B,0,SDHI0 } }, + { 0xa408002c, 0, 16, 4, /* IPRL */ { SCIFA_SCIFA2,0,TPU_TPUI,ATAPI_ATAPII } }, + { 0xa4140010, 0, 32, 4, /* INTPRI00 */ + { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } }, +}; + +static struct intc_sense_reg sense_registers[] __initdata = { + { 0xa414001c, 16, 2, /* ICR1 */ + { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } }, +}; + +static struct intc_mask_reg ack_registers[] __initdata = { + { 0xa4140024, 0, 8, /* INTREQ00 */ + { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } }, +}; + +static struct intc_desc intc_desc __initdata = { + .name = "sh7723", + .force_enable = ENABLED, + .force_disable = DISABLED, + .hw = INTC_HW_DESC(vectors, groups, mask_registers, + prio_registers, sense_registers, ack_registers), +}; + +void __init plat_irq_setup(void) +{ + register_intc_controller(&intc_desc); +} diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7724.c b/arch/sh/kernel/cpu/sh4a/setup-sh7724.c new file mode 100644 index 000000000..c20258b18 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7724.c @@ -0,0 +1,1290 @@ +/* + * SH7724 Setup + * + * Copyright (C) 2009 Renesas Solutions Corp. + * + * Kuninori Morimoto <morimoto.kuninori@renesas.com> + * + * Based on SH7723 Setup + * 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/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <linux/mm.h> +#include <linux/serial_sci.h> +#include <linux/uio_driver.h> +#include <linux/sh_dma.h> +#include <linux/sh_timer.h> +#include <linux/sh_intc.h> +#include <linux/io.h> +#include <linux/notifier.h> + +#include <asm/suspend.h> +#include <asm/clock.h> +#include <asm/mmzone.h> + +#include <cpu/dma-register.h> +#include <cpu/sh7724.h> + +/* DMA */ +static const struct sh_dmae_slave_config sh7724_dmae_slaves[] = { + { + .slave_id = SHDMA_SLAVE_SCIF0_TX, + .addr = 0xffe0000c, + .chcr = DM_FIX | SM_INC | RS_ERS | TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x21, + }, { + .slave_id = SHDMA_SLAVE_SCIF0_RX, + .addr = 0xffe00014, + .chcr = DM_INC | SM_FIX | RS_ERS | TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x22, + }, { + .slave_id = SHDMA_SLAVE_SCIF1_TX, + .addr = 0xffe1000c, + .chcr = DM_FIX | SM_INC | RS_ERS | TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x25, + }, { + .slave_id = SHDMA_SLAVE_SCIF1_RX, + .addr = 0xffe10014, + .chcr = DM_INC | SM_FIX | RS_ERS | TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x26, + }, { + .slave_id = SHDMA_SLAVE_SCIF2_TX, + .addr = 0xffe2000c, + .chcr = DM_FIX | SM_INC | RS_ERS | TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x29, + }, { + .slave_id = SHDMA_SLAVE_SCIF2_RX, + .addr = 0xffe20014, + .chcr = DM_INC | SM_FIX | RS_ERS | TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x2a, + }, { + .slave_id = SHDMA_SLAVE_SCIF3_TX, + .addr = 0xa4e30020, + .chcr = DM_FIX | SM_INC | RS_ERS | TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x2d, + }, { + .slave_id = SHDMA_SLAVE_SCIF3_RX, + .addr = 0xa4e30024, + .chcr = DM_INC | SM_FIX | RS_ERS | TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x2e, + }, { + .slave_id = SHDMA_SLAVE_SCIF4_TX, + .addr = 0xa4e40020, + .chcr = DM_FIX | SM_INC | RS_ERS | TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x31, + }, { + .slave_id = SHDMA_SLAVE_SCIF4_RX, + .addr = 0xa4e40024, + .chcr = DM_INC | SM_FIX | RS_ERS | TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x32, + }, { + .slave_id = SHDMA_SLAVE_SCIF5_TX, + .addr = 0xa4e50020, + .chcr = DM_FIX | SM_INC | RS_ERS | TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x35, + }, { + .slave_id = SHDMA_SLAVE_SCIF5_RX, + .addr = 0xa4e50024, + .chcr = DM_INC | SM_FIX | RS_ERS | TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x36, + }, { + .slave_id = SHDMA_SLAVE_USB0D0_TX, + .addr = 0xA4D80100, + .chcr = DM_FIX | SM_INC | RS_ERS | TS_INDEX2VAL(XMIT_SZ_32BIT), + .mid_rid = 0x73, + }, { + .slave_id = SHDMA_SLAVE_USB0D0_RX, + .addr = 0xA4D80100, + .chcr = DM_INC | SM_FIX | RS_ERS | TS_INDEX2VAL(XMIT_SZ_32BIT), + .mid_rid = 0x73, + }, { + .slave_id = SHDMA_SLAVE_USB0D1_TX, + .addr = 0xA4D80120, + .chcr = DM_FIX | SM_INC | RS_ERS | TS_INDEX2VAL(XMIT_SZ_32BIT), + .mid_rid = 0x77, + }, { + .slave_id = SHDMA_SLAVE_USB0D1_RX, + .addr = 0xA4D80120, + .chcr = DM_INC | SM_FIX | RS_ERS | TS_INDEX2VAL(XMIT_SZ_32BIT), + .mid_rid = 0x77, + }, { + .slave_id = SHDMA_SLAVE_USB1D0_TX, + .addr = 0xA4D90100, + .chcr = DM_FIX | SM_INC | RS_ERS | TS_INDEX2VAL(XMIT_SZ_32BIT), + .mid_rid = 0xab, + }, { + .slave_id = SHDMA_SLAVE_USB1D0_RX, + .addr = 0xA4D90100, + .chcr = DM_INC | SM_FIX | RS_ERS | TS_INDEX2VAL(XMIT_SZ_32BIT), + .mid_rid = 0xab, + }, { + .slave_id = SHDMA_SLAVE_USB1D1_TX, + .addr = 0xA4D90120, + .chcr = DM_FIX | SM_INC | RS_ERS | TS_INDEX2VAL(XMIT_SZ_32BIT), + .mid_rid = 0xaf, + }, { + .slave_id = SHDMA_SLAVE_USB1D1_RX, + .addr = 0xA4D90120, + .chcr = DM_INC | SM_FIX | RS_ERS | TS_INDEX2VAL(XMIT_SZ_32BIT), + .mid_rid = 0xaf, + }, { + .slave_id = SHDMA_SLAVE_SDHI0_TX, + .addr = 0x04ce0030, + .chcr = DM_FIX | SM_INC | RS_ERS | TS_INDEX2VAL(XMIT_SZ_16BIT), + .mid_rid = 0xc1, + }, { + .slave_id = SHDMA_SLAVE_SDHI0_RX, + .addr = 0x04ce0030, + .chcr = DM_INC | SM_FIX | RS_ERS | TS_INDEX2VAL(XMIT_SZ_16BIT), + .mid_rid = 0xc2, + }, { + .slave_id = SHDMA_SLAVE_SDHI1_TX, + .addr = 0x04cf0030, + .chcr = DM_FIX | SM_INC | RS_ERS | TS_INDEX2VAL(XMIT_SZ_16BIT), + .mid_rid = 0xc9, + }, { + .slave_id = SHDMA_SLAVE_SDHI1_RX, + .addr = 0x04cf0030, + .chcr = DM_INC | SM_FIX | RS_ERS | TS_INDEX2VAL(XMIT_SZ_16BIT), + .mid_rid = 0xca, + }, +}; + +static const struct sh_dmae_channel sh7724_dmae_channels[] = { + { + .offset = 0, + .dmars = 0, + .dmars_bit = 0, + }, { + .offset = 0x10, + .dmars = 0, + .dmars_bit = 8, + }, { + .offset = 0x20, + .dmars = 4, + .dmars_bit = 0, + }, { + .offset = 0x30, + .dmars = 4, + .dmars_bit = 8, + }, { + .offset = 0x50, + .dmars = 8, + .dmars_bit = 0, + }, { + .offset = 0x60, + .dmars = 8, + .dmars_bit = 8, + } +}; + +static const unsigned int ts_shift[] = TS_SHIFT; + +static struct sh_dmae_pdata dma_platform_data = { + .slave = sh7724_dmae_slaves, + .slave_num = ARRAY_SIZE(sh7724_dmae_slaves), + .channel = sh7724_dmae_channels, + .channel_num = ARRAY_SIZE(sh7724_dmae_channels), + .ts_low_shift = CHCR_TS_LOW_SHIFT, + .ts_low_mask = CHCR_TS_LOW_MASK, + .ts_high_shift = CHCR_TS_HIGH_SHIFT, + .ts_high_mask = CHCR_TS_HIGH_MASK, + .ts_shift = ts_shift, + .ts_shift_num = ARRAY_SIZE(ts_shift), + .dmaor_init = DMAOR_INIT, +}; + +/* Resource order important! */ +static struct resource sh7724_dmae0_resources[] = { + { + /* Channel registers and DMAOR */ + .start = 0xfe008020, + .end = 0xfe00808f, + .flags = IORESOURCE_MEM, + }, + { + /* DMARSx */ + .start = 0xfe009000, + .end = 0xfe00900b, + .flags = IORESOURCE_MEM, + }, + { + .name = "error_irq", + .start = evt2irq(0xbc0), + .end = evt2irq(0xbc0), + .flags = IORESOURCE_IRQ, + }, + { + /* IRQ for channels 0-3 */ + .start = evt2irq(0x800), + .end = evt2irq(0x860), + .flags = IORESOURCE_IRQ, + }, + { + /* IRQ for channels 4-5 */ + .start = evt2irq(0xb80), + .end = evt2irq(0xba0), + .flags = IORESOURCE_IRQ, + }, +}; + +/* Resource order important! */ +static struct resource sh7724_dmae1_resources[] = { + { + /* Channel registers and DMAOR */ + .start = 0xfdc08020, + .end = 0xfdc0808f, + .flags = IORESOURCE_MEM, + }, + { + /* DMARSx */ + .start = 0xfdc09000, + .end = 0xfdc0900b, + .flags = IORESOURCE_MEM, + }, + { + .name = "error_irq", + .start = evt2irq(0xb40), + .end = evt2irq(0xb40), + .flags = IORESOURCE_IRQ, + }, + { + /* IRQ for channels 0-3 */ + .start = evt2irq(0x700), + .end = evt2irq(0x760), + .flags = IORESOURCE_IRQ, + }, + { + /* IRQ for channels 4-5 */ + .start = evt2irq(0xb00), + .end = evt2irq(0xb20), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device dma0_device = { + .name = "sh-dma-engine", + .id = 0, + .resource = sh7724_dmae0_resources, + .num_resources = ARRAY_SIZE(sh7724_dmae0_resources), + .dev = { + .platform_data = &dma_platform_data, + }, +}; + +static struct platform_device dma1_device = { + .name = "sh-dma-engine", + .id = 1, + .resource = sh7724_dmae1_resources, + .num_resources = ARRAY_SIZE(sh7724_dmae1_resources), + .dev = { + .platform_data = &dma_platform_data, + }, +}; + +/* Serial */ +static struct plat_sci_port scif0_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE, +}; + +static struct resource scif0_resources[] = { + DEFINE_RES_MEM(0xffe00000, 0x100), + DEFINE_RES_IRQ(evt2irq(0xc00)), +}; + +static struct platform_device scif0_device = { + .name = "sh-sci", + .id = 0, + .resource = scif0_resources, + .num_resources = ARRAY_SIZE(scif0_resources), + .dev = { + .platform_data = &scif0_platform_data, + }, +}; + +static struct plat_sci_port scif1_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE, +}; + +static struct resource scif1_resources[] = { + DEFINE_RES_MEM(0xffe10000, 0x100), + DEFINE_RES_IRQ(evt2irq(0xc20)), +}; + +static struct platform_device scif1_device = { + .name = "sh-sci", + .id = 1, + .resource = scif1_resources, + .num_resources = ARRAY_SIZE(scif1_resources), + .dev = { + .platform_data = &scif1_platform_data, + }, +}; + +static struct plat_sci_port scif2_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE, +}; + +static struct resource scif2_resources[] = { + DEFINE_RES_MEM(0xffe20000, 0x100), + DEFINE_RES_IRQ(evt2irq(0xc40)), +}; + +static struct platform_device scif2_device = { + .name = "sh-sci", + .id = 2, + .resource = scif2_resources, + .num_resources = ARRAY_SIZE(scif2_resources), + .dev = { + .platform_data = &scif2_platform_data, + }, +}; + +static struct plat_sci_port scif3_platform_data = { + .sampling_rate = 8, + .type = PORT_SCIFA, +}; + +static struct resource scif3_resources[] = { + DEFINE_RES_MEM(0xa4e30000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x900)), +}; + +static struct platform_device scif3_device = { + .name = "sh-sci", + .id = 3, + .resource = scif3_resources, + .num_resources = ARRAY_SIZE(scif3_resources), + .dev = { + .platform_data = &scif3_platform_data, + }, +}; + +static struct plat_sci_port scif4_platform_data = { + .sampling_rate = 8, + .type = PORT_SCIFA, +}; + +static struct resource scif4_resources[] = { + DEFINE_RES_MEM(0xa4e40000, 0x100), + DEFINE_RES_IRQ(evt2irq(0xd00)), +}; + +static struct platform_device scif4_device = { + .name = "sh-sci", + .id = 4, + .resource = scif4_resources, + .num_resources = ARRAY_SIZE(scif4_resources), + .dev = { + .platform_data = &scif4_platform_data, + }, +}; + +static struct plat_sci_port scif5_platform_data = { + .sampling_rate = 8, + .type = PORT_SCIFA, +}; + +static struct resource scif5_resources[] = { + DEFINE_RES_MEM(0xa4e50000, 0x100), + DEFINE_RES_IRQ(evt2irq(0xfa0)), +}; + +static struct platform_device scif5_device = { + .name = "sh-sci", + .id = 5, + .resource = scif5_resources, + .num_resources = ARRAY_SIZE(scif5_resources), + .dev = { + .platform_data = &scif5_platform_data, + }, +}; + +/* RTC */ +static struct resource rtc_resources[] = { + [0] = { + .start = 0xa465fec0, + .end = 0xa465fec0 + 0x58 - 1, + .flags = IORESOURCE_IO, + }, + [1] = { + /* Period IRQ */ + .start = evt2irq(0xaa0), + .flags = IORESOURCE_IRQ, + }, + [2] = { + /* Carry IRQ */ + .start = evt2irq(0xac0), + .flags = IORESOURCE_IRQ, + }, + [3] = { + /* Alarm IRQ */ + .start = evt2irq(0xa80), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device rtc_device = { + .name = "sh-rtc", + .id = -1, + .num_resources = ARRAY_SIZE(rtc_resources), + .resource = rtc_resources, +}; + +/* I2C0 */ +static struct resource iic0_resources[] = { + [0] = { + .name = "IIC0", + .start = 0x04470000, + .end = 0x04470018 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = evt2irq(0xe00), + .end = evt2irq(0xe60), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device iic0_device = { + .name = "i2c-sh_mobile", + .id = 0, /* "i2c0" clock */ + .num_resources = ARRAY_SIZE(iic0_resources), + .resource = iic0_resources, +}; + +/* I2C1 */ +static struct resource iic1_resources[] = { + [0] = { + .name = "IIC1", + .start = 0x04750000, + .end = 0x04750018 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = evt2irq(0xd80), + .end = evt2irq(0xde0), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device iic1_device = { + .name = "i2c-sh_mobile", + .id = 1, /* "i2c1" clock */ + .num_resources = ARRAY_SIZE(iic1_resources), + .resource = iic1_resources, +}; + +/* VPU */ +static struct uio_info vpu_platform_data = { + .name = "VPU5F", + .version = "0", + .irq = evt2irq(0x980), +}; + +static struct resource vpu_resources[] = { + [0] = { + .name = "VPU", + .start = 0xfe900000, + .end = 0xfe902807, + .flags = IORESOURCE_MEM, + }, + [1] = { + /* place holder for contiguous memory */ + }, +}; + +static struct platform_device vpu_device = { + .name = "uio_pdrv_genirq", + .id = 0, + .dev = { + .platform_data = &vpu_platform_data, + }, + .resource = vpu_resources, + .num_resources = ARRAY_SIZE(vpu_resources), +}; + +/* VEU0 */ +static struct uio_info veu0_platform_data = { + .name = "VEU3F0", + .version = "0", + .irq = evt2irq(0xc60), +}; + +static struct resource veu0_resources[] = { + [0] = { + .name = "VEU3F0", + .start = 0xfe920000, + .end = 0xfe9200cb, + .flags = IORESOURCE_MEM, + }, + [1] = { + /* place holder for contiguous memory */ + }, +}; + +static struct platform_device veu0_device = { + .name = "uio_pdrv_genirq", + .id = 1, + .dev = { + .platform_data = &veu0_platform_data, + }, + .resource = veu0_resources, + .num_resources = ARRAY_SIZE(veu0_resources), +}; + +/* VEU1 */ +static struct uio_info veu1_platform_data = { + .name = "VEU3F1", + .version = "0", + .irq = evt2irq(0x8c0), +}; + +static struct resource veu1_resources[] = { + [0] = { + .name = "VEU3F1", + .start = 0xfe924000, + .end = 0xfe9240cb, + .flags = IORESOURCE_MEM, + }, + [1] = { + /* place holder for contiguous memory */ + }, +}; + +static struct platform_device veu1_device = { + .name = "uio_pdrv_genirq", + .id = 2, + .dev = { + .platform_data = &veu1_platform_data, + }, + .resource = veu1_resources, + .num_resources = ARRAY_SIZE(veu1_resources), +}; + +/* BEU0 */ +static struct uio_info beu0_platform_data = { + .name = "BEU0", + .version = "0", + .irq = evt2irq(0x8A0), +}; + +static struct resource beu0_resources[] = { + [0] = { + .name = "BEU0", + .start = 0xfe930000, + .end = 0xfe933400, + .flags = IORESOURCE_MEM, + }, + [1] = { + /* place holder for contiguous memory */ + }, +}; + +static struct platform_device beu0_device = { + .name = "uio_pdrv_genirq", + .id = 6, + .dev = { + .platform_data = &beu0_platform_data, + }, + .resource = beu0_resources, + .num_resources = ARRAY_SIZE(beu0_resources), +}; + +/* BEU1 */ +static struct uio_info beu1_platform_data = { + .name = "BEU1", + .version = "0", + .irq = evt2irq(0xA00), +}; + +static struct resource beu1_resources[] = { + [0] = { + .name = "BEU1", + .start = 0xfe940000, + .end = 0xfe943400, + .flags = IORESOURCE_MEM, + }, + [1] = { + /* place holder for contiguous memory */ + }, +}; + +static struct platform_device beu1_device = { + .name = "uio_pdrv_genirq", + .id = 7, + .dev = { + .platform_data = &beu1_platform_data, + }, + .resource = beu1_resources, + .num_resources = ARRAY_SIZE(beu1_resources), +}; + +static struct sh_timer_config cmt_platform_data = { + .channels_mask = 0x20, +}; + +static struct resource cmt_resources[] = { + DEFINE_RES_MEM(0x044a0000, 0x70), + DEFINE_RES_IRQ(evt2irq(0xf00)), +}; + +static struct platform_device cmt_device = { + .name = "sh-cmt-32", + .id = 0, + .dev = { + .platform_data = &cmt_platform_data, + }, + .resource = cmt_resources, + .num_resources = ARRAY_SIZE(cmt_resources), +}; + +static struct sh_timer_config tmu0_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu0_resources[] = { + DEFINE_RES_MEM(0xffd80000, 0x2c), + DEFINE_RES_IRQ(evt2irq(0x400)), + DEFINE_RES_IRQ(evt2irq(0x420)), + DEFINE_RES_IRQ(evt2irq(0x440)), +}; + +static struct platform_device tmu0_device = { + .name = "sh-tmu", + .id = 0, + .dev = { + .platform_data = &tmu0_platform_data, + }, + .resource = tmu0_resources, + .num_resources = ARRAY_SIZE(tmu0_resources), +}; + +static struct sh_timer_config tmu1_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu1_resources[] = { + DEFINE_RES_MEM(0xffd90000, 0x2c), + DEFINE_RES_IRQ(evt2irq(0x920)), + DEFINE_RES_IRQ(evt2irq(0x940)), + DEFINE_RES_IRQ(evt2irq(0x960)), +}; + +static struct platform_device tmu1_device = { + .name = "sh-tmu", + .id = 1, + .dev = { + .platform_data = &tmu1_platform_data, + }, + .resource = tmu1_resources, + .num_resources = ARRAY_SIZE(tmu1_resources), +}; + +/* JPU */ +static struct uio_info jpu_platform_data = { + .name = "JPU", + .version = "0", + .irq = evt2irq(0x560), +}; + +static struct resource jpu_resources[] = { + [0] = { + .name = "JPU", + .start = 0xfe980000, + .end = 0xfe9902d3, + .flags = IORESOURCE_MEM, + }, + [1] = { + /* place holder for contiguous memory */ + }, +}; + +static struct platform_device jpu_device = { + .name = "uio_pdrv_genirq", + .id = 3, + .dev = { + .platform_data = &jpu_platform_data, + }, + .resource = jpu_resources, + .num_resources = ARRAY_SIZE(jpu_resources), +}; + +/* SPU2DSP0 */ +static struct uio_info spu0_platform_data = { + .name = "SPU2DSP0", + .version = "0", + .irq = evt2irq(0xcc0), +}; + +static struct resource spu0_resources[] = { + [0] = { + .name = "SPU2DSP0", + .start = 0xFE200000, + .end = 0xFE2FFFFF, + .flags = IORESOURCE_MEM, + }, + [1] = { + /* place holder for contiguous memory */ + }, +}; + +static struct platform_device spu0_device = { + .name = "uio_pdrv_genirq", + .id = 4, + .dev = { + .platform_data = &spu0_platform_data, + }, + .resource = spu0_resources, + .num_resources = ARRAY_SIZE(spu0_resources), +}; + +/* SPU2DSP1 */ +static struct uio_info spu1_platform_data = { + .name = "SPU2DSP1", + .version = "0", + .irq = evt2irq(0xce0), +}; + +static struct resource spu1_resources[] = { + [0] = { + .name = "SPU2DSP1", + .start = 0xFE300000, + .end = 0xFE3FFFFF, + .flags = IORESOURCE_MEM, + }, + [1] = { + /* place holder for contiguous memory */ + }, +}; + +static struct platform_device spu1_device = { + .name = "uio_pdrv_genirq", + .id = 5, + .dev = { + .platform_data = &spu1_platform_data, + }, + .resource = spu1_resources, + .num_resources = ARRAY_SIZE(spu1_resources), +}; + +static struct platform_device *sh7724_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + &scif3_device, + &scif4_device, + &scif5_device, + &cmt_device, + &tmu0_device, + &tmu1_device, + &dma0_device, + &dma1_device, + &rtc_device, + &iic0_device, + &iic1_device, + &vpu_device, + &veu0_device, + &veu1_device, + &beu0_device, + &beu1_device, + &jpu_device, + &spu0_device, + &spu1_device, +}; + +static int __init sh7724_devices_setup(void) +{ + platform_resource_setup_memory(&vpu_device, "vpu", 2 << 20); + platform_resource_setup_memory(&veu0_device, "veu0", 2 << 20); + platform_resource_setup_memory(&veu1_device, "veu1", 2 << 20); + platform_resource_setup_memory(&jpu_device, "jpu", 2 << 20); + platform_resource_setup_memory(&spu0_device, "spu0", 2 << 20); + platform_resource_setup_memory(&spu1_device, "spu1", 2 << 20); + + return platform_add_devices(sh7724_devices, + ARRAY_SIZE(sh7724_devices)); +} +arch_initcall(sh7724_devices_setup); + +static struct platform_device *sh7724_early_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + &scif3_device, + &scif4_device, + &scif5_device, + &cmt_device, + &tmu0_device, + &tmu1_device, +}; + +void __init plat_early_device_setup(void) +{ + early_platform_add_devices(sh7724_early_devices, + ARRAY_SIZE(sh7724_early_devices)); +} + +#define RAMCR_CACHE_L2FC 0x0002 +#define RAMCR_CACHE_L2E 0x0001 +#define L2_CACHE_ENABLE (RAMCR_CACHE_L2E|RAMCR_CACHE_L2FC) + +void l2_cache_init(void) +{ + /* Enable L2 cache */ + __raw_writel(L2_CACHE_ENABLE, RAMCR); +} + +enum { + UNUSED = 0, + ENABLED, + DISABLED, + + /* interrupt sources */ + IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7, + HUDI, + DMAC1A_DEI0, DMAC1A_DEI1, DMAC1A_DEI2, DMAC1A_DEI3, + _2DG_TRI, _2DG_INI, _2DG_CEI, + DMAC0A_DEI0, DMAC0A_DEI1, DMAC0A_DEI2, DMAC0A_DEI3, + VIO_CEU0, VIO_BEU0, VIO_VEU1, VIO_VOU, + SCIFA3, + VPU, + TPU, + CEU1, + BEU1, + USB0, USB1, + ATAPI, + RTC_ATI, RTC_PRI, RTC_CUI, + DMAC1B_DEI4, DMAC1B_DEI5, DMAC1B_DADERR, + DMAC0B_DEI4, DMAC0B_DEI5, DMAC0B_DADERR, + KEYSC, + SCIF_SCIF0, SCIF_SCIF1, SCIF_SCIF2, + VEU0, + MSIOF_MSIOFI0, MSIOF_MSIOFI1, + SPU_SPUI0, SPU_SPUI1, + SCIFA4, + ICB, + ETHI, + I2C1_ALI, I2C1_TACKI, I2C1_WAITI, I2C1_DTEI, + I2C0_ALI, I2C0_TACKI, I2C0_WAITI, I2C0_DTEI, + CMT, + TSIF, + FSI, + SCIFA5, + TMU0_TUNI0, TMU0_TUNI1, TMU0_TUNI2, + IRDA, + JPU, + _2DDMAC, + MMC_MMC2I, MMC_MMC3I, + LCDC, + TMU1_TUNI0, TMU1_TUNI1, TMU1_TUNI2, + + /* interrupt groups */ + DMAC1A, _2DG, DMAC0A, VIO, USB, RTC, + DMAC1B, DMAC0B, I2C0, I2C1, SDHI0, SDHI1, SPU, MMCIF, +}; + +static struct intc_vect vectors[] __initdata = { + INTC_VECT(IRQ0, 0x600), INTC_VECT(IRQ1, 0x620), + INTC_VECT(IRQ2, 0x640), INTC_VECT(IRQ3, 0x660), + INTC_VECT(IRQ4, 0x680), INTC_VECT(IRQ5, 0x6a0), + INTC_VECT(IRQ6, 0x6c0), INTC_VECT(IRQ7, 0x6e0), + + INTC_VECT(DMAC1A_DEI0, 0x700), + INTC_VECT(DMAC1A_DEI1, 0x720), + INTC_VECT(DMAC1A_DEI2, 0x740), + INTC_VECT(DMAC1A_DEI3, 0x760), + + INTC_VECT(_2DG_TRI, 0x780), + INTC_VECT(_2DG_INI, 0x7A0), + INTC_VECT(_2DG_CEI, 0x7C0), + + INTC_VECT(DMAC0A_DEI0, 0x800), + INTC_VECT(DMAC0A_DEI1, 0x820), + INTC_VECT(DMAC0A_DEI2, 0x840), + INTC_VECT(DMAC0A_DEI3, 0x860), + + INTC_VECT(VIO_CEU0, 0x880), + INTC_VECT(VIO_BEU0, 0x8A0), + INTC_VECT(VIO_VEU1, 0x8C0), + INTC_VECT(VIO_VOU, 0x8E0), + + INTC_VECT(SCIFA3, 0x900), + INTC_VECT(VPU, 0x980), + INTC_VECT(TPU, 0x9A0), + INTC_VECT(CEU1, 0x9E0), + INTC_VECT(BEU1, 0xA00), + INTC_VECT(USB0, 0xA20), + INTC_VECT(USB1, 0xA40), + INTC_VECT(ATAPI, 0xA60), + + INTC_VECT(RTC_ATI, 0xA80), + INTC_VECT(RTC_PRI, 0xAA0), + INTC_VECT(RTC_CUI, 0xAC0), + + INTC_VECT(DMAC1B_DEI4, 0xB00), + INTC_VECT(DMAC1B_DEI5, 0xB20), + INTC_VECT(DMAC1B_DADERR, 0xB40), + + INTC_VECT(DMAC0B_DEI4, 0xB80), + INTC_VECT(DMAC0B_DEI5, 0xBA0), + INTC_VECT(DMAC0B_DADERR, 0xBC0), + + INTC_VECT(KEYSC, 0xBE0), + INTC_VECT(SCIF_SCIF0, 0xC00), + INTC_VECT(SCIF_SCIF1, 0xC20), + INTC_VECT(SCIF_SCIF2, 0xC40), + INTC_VECT(VEU0, 0xC60), + INTC_VECT(MSIOF_MSIOFI0, 0xC80), + INTC_VECT(MSIOF_MSIOFI1, 0xCA0), + INTC_VECT(SPU_SPUI0, 0xCC0), + INTC_VECT(SPU_SPUI1, 0xCE0), + INTC_VECT(SCIFA4, 0xD00), + + INTC_VECT(ICB, 0xD20), + INTC_VECT(ETHI, 0xD60), + + INTC_VECT(I2C1_ALI, 0xD80), + INTC_VECT(I2C1_TACKI, 0xDA0), + INTC_VECT(I2C1_WAITI, 0xDC0), + INTC_VECT(I2C1_DTEI, 0xDE0), + + INTC_VECT(I2C0_ALI, 0xE00), + INTC_VECT(I2C0_TACKI, 0xE20), + INTC_VECT(I2C0_WAITI, 0xE40), + INTC_VECT(I2C0_DTEI, 0xE60), + + INTC_VECT(SDHI0, 0xE80), + INTC_VECT(SDHI0, 0xEA0), + INTC_VECT(SDHI0, 0xEC0), + INTC_VECT(SDHI0, 0xEE0), + + INTC_VECT(CMT, 0xF00), + INTC_VECT(TSIF, 0xF20), + INTC_VECT(FSI, 0xF80), + INTC_VECT(SCIFA5, 0xFA0), + + INTC_VECT(TMU0_TUNI0, 0x400), + INTC_VECT(TMU0_TUNI1, 0x420), + INTC_VECT(TMU0_TUNI2, 0x440), + + INTC_VECT(IRDA, 0x480), + + INTC_VECT(SDHI1, 0x4E0), + INTC_VECT(SDHI1, 0x500), + INTC_VECT(SDHI1, 0x520), + + INTC_VECT(JPU, 0x560), + INTC_VECT(_2DDMAC, 0x4A0), + + INTC_VECT(MMC_MMC2I, 0x5A0), + INTC_VECT(MMC_MMC3I, 0x5C0), + + INTC_VECT(LCDC, 0xF40), + + INTC_VECT(TMU1_TUNI0, 0x920), + INTC_VECT(TMU1_TUNI1, 0x940), + INTC_VECT(TMU1_TUNI2, 0x960), +}; + +static struct intc_group groups[] __initdata = { + INTC_GROUP(DMAC1A, DMAC1A_DEI0, DMAC1A_DEI1, DMAC1A_DEI2, DMAC1A_DEI3), + INTC_GROUP(_2DG, _2DG_TRI, _2DG_INI, _2DG_CEI), + INTC_GROUP(DMAC0A, DMAC0A_DEI0, DMAC0A_DEI1, DMAC0A_DEI2, DMAC0A_DEI3), + INTC_GROUP(VIO, VIO_CEU0, VIO_BEU0, VIO_VEU1, VIO_VOU), + INTC_GROUP(USB, USB0, USB1), + INTC_GROUP(RTC, RTC_ATI, RTC_PRI, RTC_CUI), + INTC_GROUP(DMAC1B, DMAC1B_DEI4, DMAC1B_DEI5, DMAC1B_DADERR), + INTC_GROUP(DMAC0B, DMAC0B_DEI4, DMAC0B_DEI5, DMAC0B_DADERR), + INTC_GROUP(I2C0, I2C0_ALI, I2C0_TACKI, I2C0_WAITI, I2C0_DTEI), + INTC_GROUP(I2C1, I2C1_ALI, I2C1_TACKI, I2C1_WAITI, I2C1_DTEI), + INTC_GROUP(SPU, SPU_SPUI0, SPU_SPUI1), + INTC_GROUP(MMCIF, MMC_MMC2I, MMC_MMC3I), +}; + +static struct intc_mask_reg mask_registers[] __initdata = { + { 0xa4080080, 0xa40800c0, 8, /* IMR0 / IMCR0 */ + { 0, TMU1_TUNI2, TMU1_TUNI1, TMU1_TUNI0, + 0, ENABLED, ENABLED, ENABLED } }, + { 0xa4080084, 0xa40800c4, 8, /* IMR1 / IMCR1 */ + { VIO_VOU, VIO_VEU1, VIO_BEU0, VIO_CEU0, + DMAC0A_DEI3, DMAC0A_DEI2, DMAC0A_DEI1, DMAC0A_DEI0 } }, + { 0xa4080088, 0xa40800c8, 8, /* IMR2 / IMCR2 */ + { 0, 0, 0, VPU, ATAPI, ETHI, 0, SCIFA3 } }, + { 0xa408008c, 0xa40800cc, 8, /* IMR3 / IMCR3 */ + { DMAC1A_DEI3, DMAC1A_DEI2, DMAC1A_DEI1, DMAC1A_DEI0, + SPU_SPUI1, SPU_SPUI0, BEU1, IRDA } }, + { 0xa4080090, 0xa40800d0, 8, /* IMR4 / IMCR4 */ + { 0, TMU0_TUNI2, TMU0_TUNI1, TMU0_TUNI0, + JPU, 0, 0, LCDC } }, + { 0xa4080094, 0xa40800d4, 8, /* IMR5 / IMCR5 */ + { KEYSC, DMAC0B_DADERR, DMAC0B_DEI5, DMAC0B_DEI4, + VEU0, SCIF_SCIF2, SCIF_SCIF1, SCIF_SCIF0 } }, + { 0xa4080098, 0xa40800d8, 8, /* IMR6 / IMCR6 */ + { 0, 0, ICB, SCIFA4, + CEU1, 0, MSIOF_MSIOFI1, MSIOF_MSIOFI0 } }, + { 0xa408009c, 0xa40800dc, 8, /* IMR7 / IMCR7 */ + { I2C0_DTEI, I2C0_WAITI, I2C0_TACKI, I2C0_ALI, + I2C1_DTEI, I2C1_WAITI, I2C1_TACKI, I2C1_ALI } }, + { 0xa40800a0, 0xa40800e0, 8, /* IMR8 / IMCR8 */ + { DISABLED, ENABLED, ENABLED, ENABLED, + 0, 0, SCIFA5, FSI } }, + { 0xa40800a4, 0xa40800e4, 8, /* IMR9 / IMCR9 */ + { 0, 0, 0, CMT, 0, USB1, USB0, 0 } }, + { 0xa40800a8, 0xa40800e8, 8, /* IMR10 / IMCR10 */ + { 0, DMAC1B_DADERR, DMAC1B_DEI5, DMAC1B_DEI4, + 0, RTC_CUI, RTC_PRI, RTC_ATI } }, + { 0xa40800ac, 0xa40800ec, 8, /* IMR11 / IMCR11 */ + { 0, _2DG_CEI, _2DG_INI, _2DG_TRI, + 0, TPU, 0, TSIF } }, + { 0xa40800b0, 0xa40800f0, 8, /* IMR12 / IMCR12 */ + { 0, 0, MMC_MMC3I, MMC_MMC2I, 0, 0, 0, _2DDMAC } }, + { 0xa4140044, 0xa4140064, 8, /* INTMSK00 / INTMSKCLR00 */ + { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } }, +}; + +static struct intc_prio_reg prio_registers[] __initdata = { + { 0xa4080000, 0, 16, 4, /* IPRA */ { TMU0_TUNI0, TMU0_TUNI1, + TMU0_TUNI2, IRDA } }, + { 0xa4080004, 0, 16, 4, /* IPRB */ { JPU, LCDC, DMAC1A, BEU1 } }, + { 0xa4080008, 0, 16, 4, /* IPRC */ { TMU1_TUNI0, TMU1_TUNI1, + TMU1_TUNI2, SPU } }, + { 0xa408000c, 0, 16, 4, /* IPRD */ { 0, MMCIF, 0, ATAPI } }, + { 0xa4080010, 0, 16, 4, /* IPRE */ { DMAC0A, VIO, SCIFA3, VPU } }, + { 0xa4080014, 0, 16, 4, /* IPRF */ { KEYSC, DMAC0B, USB, CMT } }, + { 0xa4080018, 0, 16, 4, /* IPRG */ { SCIF_SCIF0, SCIF_SCIF1, + SCIF_SCIF2, VEU0 } }, + { 0xa408001c, 0, 16, 4, /* IPRH */ { MSIOF_MSIOFI0, MSIOF_MSIOFI1, + I2C1, I2C0 } }, + { 0xa4080020, 0, 16, 4, /* IPRI */ { SCIFA4, ICB, TSIF, _2DG } }, + { 0xa4080024, 0, 16, 4, /* IPRJ */ { CEU1, ETHI, FSI, SDHI1 } }, + { 0xa4080028, 0, 16, 4, /* IPRK */ { RTC, DMAC1B, 0, SDHI0 } }, + { 0xa408002c, 0, 16, 4, /* IPRL */ { SCIFA5, 0, TPU, _2DDMAC } }, + { 0xa4140010, 0, 32, 4, /* INTPRI00 */ + { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } }, +}; + +static struct intc_sense_reg sense_registers[] __initdata = { + { 0xa414001c, 16, 2, /* ICR1 */ + { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } }, +}; + +static struct intc_mask_reg ack_registers[] __initdata = { + { 0xa4140024, 0, 8, /* INTREQ00 */ + { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } }, +}; + +static struct intc_desc intc_desc __initdata = { + .name = "sh7724", + .force_enable = ENABLED, + .force_disable = DISABLED, + .hw = INTC_HW_DESC(vectors, groups, mask_registers, + prio_registers, sense_registers, ack_registers), +}; + +void __init plat_irq_setup(void) +{ + register_intc_controller(&intc_desc); +} + +static struct { + /* BSC */ + unsigned long mmselr; + unsigned long cs0bcr; + unsigned long cs4bcr; + unsigned long cs5abcr; + unsigned long cs5bbcr; + unsigned long cs6abcr; + unsigned long cs6bbcr; + unsigned long cs4wcr; + unsigned long cs5awcr; + unsigned long cs5bwcr; + unsigned long cs6awcr; + unsigned long cs6bwcr; + /* INTC */ + unsigned short ipra; + unsigned short iprb; + unsigned short iprc; + unsigned short iprd; + unsigned short ipre; + unsigned short iprf; + unsigned short iprg; + unsigned short iprh; + unsigned short ipri; + unsigned short iprj; + unsigned short iprk; + unsigned short iprl; + unsigned char imr0; + unsigned char imr1; + unsigned char imr2; + unsigned char imr3; + unsigned char imr4; + unsigned char imr5; + unsigned char imr6; + unsigned char imr7; + unsigned char imr8; + unsigned char imr9; + unsigned char imr10; + unsigned char imr11; + unsigned char imr12; + /* RWDT */ + unsigned short rwtcnt; + unsigned short rwtcsr; + /* CPG */ + unsigned long irdaclk; + unsigned long spuclk; +} sh7724_rstandby_state; + +static int sh7724_pre_sleep_notifier_call(struct notifier_block *nb, + unsigned long flags, void *unused) +{ + if (!(flags & SUSP_SH_RSTANDBY)) + return NOTIFY_DONE; + + /* BCR */ + sh7724_rstandby_state.mmselr = __raw_readl(0xff800020); /* MMSELR */ + sh7724_rstandby_state.mmselr |= 0xa5a50000; + sh7724_rstandby_state.cs0bcr = __raw_readl(0xfec10004); /* CS0BCR */ + sh7724_rstandby_state.cs4bcr = __raw_readl(0xfec10010); /* CS4BCR */ + sh7724_rstandby_state.cs5abcr = __raw_readl(0xfec10014); /* CS5ABCR */ + sh7724_rstandby_state.cs5bbcr = __raw_readl(0xfec10018); /* CS5BBCR */ + sh7724_rstandby_state.cs6abcr = __raw_readl(0xfec1001c); /* CS6ABCR */ + sh7724_rstandby_state.cs6bbcr = __raw_readl(0xfec10020); /* CS6BBCR */ + sh7724_rstandby_state.cs4wcr = __raw_readl(0xfec10030); /* CS4WCR */ + sh7724_rstandby_state.cs5awcr = __raw_readl(0xfec10034); /* CS5AWCR */ + sh7724_rstandby_state.cs5bwcr = __raw_readl(0xfec10038); /* CS5BWCR */ + sh7724_rstandby_state.cs6awcr = __raw_readl(0xfec1003c); /* CS6AWCR */ + sh7724_rstandby_state.cs6bwcr = __raw_readl(0xfec10040); /* CS6BWCR */ + + /* INTC */ + sh7724_rstandby_state.ipra = __raw_readw(0xa4080000); /* IPRA */ + sh7724_rstandby_state.iprb = __raw_readw(0xa4080004); /* IPRB */ + sh7724_rstandby_state.iprc = __raw_readw(0xa4080008); /* IPRC */ + sh7724_rstandby_state.iprd = __raw_readw(0xa408000c); /* IPRD */ + sh7724_rstandby_state.ipre = __raw_readw(0xa4080010); /* IPRE */ + sh7724_rstandby_state.iprf = __raw_readw(0xa4080014); /* IPRF */ + sh7724_rstandby_state.iprg = __raw_readw(0xa4080018); /* IPRG */ + sh7724_rstandby_state.iprh = __raw_readw(0xa408001c); /* IPRH */ + sh7724_rstandby_state.ipri = __raw_readw(0xa4080020); /* IPRI */ + sh7724_rstandby_state.iprj = __raw_readw(0xa4080024); /* IPRJ */ + sh7724_rstandby_state.iprk = __raw_readw(0xa4080028); /* IPRK */ + sh7724_rstandby_state.iprl = __raw_readw(0xa408002c); /* IPRL */ + sh7724_rstandby_state.imr0 = __raw_readb(0xa4080080); /* IMR0 */ + sh7724_rstandby_state.imr1 = __raw_readb(0xa4080084); /* IMR1 */ + sh7724_rstandby_state.imr2 = __raw_readb(0xa4080088); /* IMR2 */ + sh7724_rstandby_state.imr3 = __raw_readb(0xa408008c); /* IMR3 */ + sh7724_rstandby_state.imr4 = __raw_readb(0xa4080090); /* IMR4 */ + sh7724_rstandby_state.imr5 = __raw_readb(0xa4080094); /* IMR5 */ + sh7724_rstandby_state.imr6 = __raw_readb(0xa4080098); /* IMR6 */ + sh7724_rstandby_state.imr7 = __raw_readb(0xa408009c); /* IMR7 */ + sh7724_rstandby_state.imr8 = __raw_readb(0xa40800a0); /* IMR8 */ + sh7724_rstandby_state.imr9 = __raw_readb(0xa40800a4); /* IMR9 */ + sh7724_rstandby_state.imr10 = __raw_readb(0xa40800a8); /* IMR10 */ + sh7724_rstandby_state.imr11 = __raw_readb(0xa40800ac); /* IMR11 */ + sh7724_rstandby_state.imr12 = __raw_readb(0xa40800b0); /* IMR12 */ + + /* RWDT */ + sh7724_rstandby_state.rwtcnt = __raw_readb(0xa4520000); /* RWTCNT */ + sh7724_rstandby_state.rwtcnt |= 0x5a00; + sh7724_rstandby_state.rwtcsr = __raw_readb(0xa4520004); /* RWTCSR */ + sh7724_rstandby_state.rwtcsr |= 0xa500; + __raw_writew(sh7724_rstandby_state.rwtcsr & 0x07, 0xa4520004); + + /* CPG */ + sh7724_rstandby_state.irdaclk = __raw_readl(0xa4150018); /* IRDACLKCR */ + sh7724_rstandby_state.spuclk = __raw_readl(0xa415003c); /* SPUCLKCR */ + + return NOTIFY_DONE; +} + +static int sh7724_post_sleep_notifier_call(struct notifier_block *nb, + unsigned long flags, void *unused) +{ + if (!(flags & SUSP_SH_RSTANDBY)) + return NOTIFY_DONE; + + /* BCR */ + __raw_writel(sh7724_rstandby_state.mmselr, 0xff800020); /* MMSELR */ + __raw_writel(sh7724_rstandby_state.cs0bcr, 0xfec10004); /* CS0BCR */ + __raw_writel(sh7724_rstandby_state.cs4bcr, 0xfec10010); /* CS4BCR */ + __raw_writel(sh7724_rstandby_state.cs5abcr, 0xfec10014); /* CS5ABCR */ + __raw_writel(sh7724_rstandby_state.cs5bbcr, 0xfec10018); /* CS5BBCR */ + __raw_writel(sh7724_rstandby_state.cs6abcr, 0xfec1001c); /* CS6ABCR */ + __raw_writel(sh7724_rstandby_state.cs6bbcr, 0xfec10020); /* CS6BBCR */ + __raw_writel(sh7724_rstandby_state.cs4wcr, 0xfec10030); /* CS4WCR */ + __raw_writel(sh7724_rstandby_state.cs5awcr, 0xfec10034); /* CS5AWCR */ + __raw_writel(sh7724_rstandby_state.cs5bwcr, 0xfec10038); /* CS5BWCR */ + __raw_writel(sh7724_rstandby_state.cs6awcr, 0xfec1003c); /* CS6AWCR */ + __raw_writel(sh7724_rstandby_state.cs6bwcr, 0xfec10040); /* CS6BWCR */ + + /* INTC */ + __raw_writew(sh7724_rstandby_state.ipra, 0xa4080000); /* IPRA */ + __raw_writew(sh7724_rstandby_state.iprb, 0xa4080004); /* IPRB */ + __raw_writew(sh7724_rstandby_state.iprc, 0xa4080008); /* IPRC */ + __raw_writew(sh7724_rstandby_state.iprd, 0xa408000c); /* IPRD */ + __raw_writew(sh7724_rstandby_state.ipre, 0xa4080010); /* IPRE */ + __raw_writew(sh7724_rstandby_state.iprf, 0xa4080014); /* IPRF */ + __raw_writew(sh7724_rstandby_state.iprg, 0xa4080018); /* IPRG */ + __raw_writew(sh7724_rstandby_state.iprh, 0xa408001c); /* IPRH */ + __raw_writew(sh7724_rstandby_state.ipri, 0xa4080020); /* IPRI */ + __raw_writew(sh7724_rstandby_state.iprj, 0xa4080024); /* IPRJ */ + __raw_writew(sh7724_rstandby_state.iprk, 0xa4080028); /* IPRK */ + __raw_writew(sh7724_rstandby_state.iprl, 0xa408002c); /* IPRL */ + __raw_writeb(sh7724_rstandby_state.imr0, 0xa4080080); /* IMR0 */ + __raw_writeb(sh7724_rstandby_state.imr1, 0xa4080084); /* IMR1 */ + __raw_writeb(sh7724_rstandby_state.imr2, 0xa4080088); /* IMR2 */ + __raw_writeb(sh7724_rstandby_state.imr3, 0xa408008c); /* IMR3 */ + __raw_writeb(sh7724_rstandby_state.imr4, 0xa4080090); /* IMR4 */ + __raw_writeb(sh7724_rstandby_state.imr5, 0xa4080094); /* IMR5 */ + __raw_writeb(sh7724_rstandby_state.imr6, 0xa4080098); /* IMR6 */ + __raw_writeb(sh7724_rstandby_state.imr7, 0xa408009c); /* IMR7 */ + __raw_writeb(sh7724_rstandby_state.imr8, 0xa40800a0); /* IMR8 */ + __raw_writeb(sh7724_rstandby_state.imr9, 0xa40800a4); /* IMR9 */ + __raw_writeb(sh7724_rstandby_state.imr10, 0xa40800a8); /* IMR10 */ + __raw_writeb(sh7724_rstandby_state.imr11, 0xa40800ac); /* IMR11 */ + __raw_writeb(sh7724_rstandby_state.imr12, 0xa40800b0); /* IMR12 */ + + /* RWDT */ + __raw_writew(sh7724_rstandby_state.rwtcnt, 0xa4520000); /* RWTCNT */ + __raw_writew(sh7724_rstandby_state.rwtcsr, 0xa4520004); /* RWTCSR */ + + /* CPG */ + __raw_writel(sh7724_rstandby_state.irdaclk, 0xa4150018); /* IRDACLKCR */ + __raw_writel(sh7724_rstandby_state.spuclk, 0xa415003c); /* SPUCLKCR */ + + return NOTIFY_DONE; +} + +static struct notifier_block sh7724_pre_sleep_notifier = { + .notifier_call = sh7724_pre_sleep_notifier_call, + .priority = SH_MOBILE_PRE(SH_MOBILE_SLEEP_CPU), +}; + +static struct notifier_block sh7724_post_sleep_notifier = { + .notifier_call = sh7724_post_sleep_notifier_call, + .priority = SH_MOBILE_POST(SH_MOBILE_SLEEP_CPU), +}; + +static int __init sh7724_sleep_setup(void) +{ + atomic_notifier_chain_register(&sh_mobile_pre_sleep_notifier_list, + &sh7724_pre_sleep_notifier); + + atomic_notifier_chain_register(&sh_mobile_post_sleep_notifier_list, + &sh7724_post_sleep_notifier); + return 0; +} +arch_initcall(sh7724_sleep_setup); + diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7734.c b/arch/sh/kernel/cpu/sh4a/setup-sh7734.c new file mode 100644 index 000000000..8c0c9da6b --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7734.c @@ -0,0 +1,623 @@ +/* + * arch/sh/kernel/cpu/sh4a/setup-sh7734.c + + * SH7734 Setup + * + * Copyright (C) 2011,2012 Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com> + * Copyright (C) 2011,2012 Renesas Solutions Corp. + * + * 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/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <linux/mm.h> +#include <linux/dma-mapping.h> +#include <linux/serial_sci.h> +#include <linux/sh_timer.h> +#include <linux/io.h> +#include <asm/clock.h> +#include <asm/irq.h> +#include <cpu/sh7734.h> + +/* SCIF */ +static struct plat_sci_port scif0_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH4_SCIF_BRG_REGTYPE, +}; + +static struct resource scif0_resources[] = { + DEFINE_RES_MEM(0xffe40000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x8c0)), +}; + +static struct platform_device scif0_device = { + .name = "sh-sci", + .id = 0, + .resource = scif0_resources, + .num_resources = ARRAY_SIZE(scif0_resources), + .dev = { + .platform_data = &scif0_platform_data, + }, +}; + +static struct plat_sci_port scif1_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH4_SCIF_BRG_REGTYPE, +}; + +static struct resource scif1_resources[] = { + DEFINE_RES_MEM(0xffe41000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x8e0)), +}; + +static struct platform_device scif1_device = { + .name = "sh-sci", + .id = 1, + .resource = scif1_resources, + .num_resources = ARRAY_SIZE(scif1_resources), + .dev = { + .platform_data = &scif1_platform_data, + }, +}; + +static struct plat_sci_port scif2_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH4_SCIF_BRG_REGTYPE, +}; + +static struct resource scif2_resources[] = { + DEFINE_RES_MEM(0xffe42000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x900)), +}; + +static struct platform_device scif2_device = { + .name = "sh-sci", + .id = 2, + .resource = scif2_resources, + .num_resources = ARRAY_SIZE(scif2_resources), + .dev = { + .platform_data = &scif2_platform_data, + }, +}; + +static struct plat_sci_port scif3_platform_data = { + .scscr = SCSCR_REIE | SCSCR_TOIE, + .type = PORT_SCIF, + .regtype = SCIx_SH4_SCIF_BRG_REGTYPE, +}; + +static struct resource scif3_resources[] = { + DEFINE_RES_MEM(0xffe43000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x920)), +}; + +static struct platform_device scif3_device = { + .name = "sh-sci", + .id = 3, + .resource = scif3_resources, + .num_resources = ARRAY_SIZE(scif3_resources), + .dev = { + .platform_data = &scif3_platform_data, + }, +}; + +static struct plat_sci_port scif4_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH4_SCIF_BRG_REGTYPE, +}; + +static struct resource scif4_resources[] = { + DEFINE_RES_MEM(0xffe44000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x940)), +}; + +static struct platform_device scif4_device = { + .name = "sh-sci", + .id = 4, + .resource = scif4_resources, + .num_resources = ARRAY_SIZE(scif4_resources), + .dev = { + .platform_data = &scif4_platform_data, + }, +}; + +static struct plat_sci_port scif5_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH4_SCIF_BRG_REGTYPE, +}; + +static struct resource scif5_resources[] = { + DEFINE_RES_MEM(0xffe43000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x960)), +}; + +static struct platform_device scif5_device = { + .name = "sh-sci", + .id = 5, + .resource = scif5_resources, + .num_resources = ARRAY_SIZE(scif5_resources), + .dev = { + .platform_data = &scif5_platform_data, + }, +}; + +/* RTC */ +static struct resource rtc_resources[] = { + [0] = { + .name = "rtc", + .start = 0xFFFC5000, + .end = 0xFFFC5000 + 0x26 - 1, + .flags = IORESOURCE_IO, + }, + [1] = { + .start = evt2irq(0xC00), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device rtc_device = { + .name = "sh-rtc", + .id = -1, + .num_resources = ARRAY_SIZE(rtc_resources), + .resource = rtc_resources, +}; + +/* I2C 0 */ +static struct resource i2c0_resources[] = { + [0] = { + .name = "IIC0", + .start = 0xFFC70000, + .end = 0xFFC7000A - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = evt2irq(0x860), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device i2c0_device = { + .name = "i2c-sh7734", + .id = 0, + .num_resources = ARRAY_SIZE(i2c0_resources), + .resource = i2c0_resources, +}; + +/* TMU */ +static struct sh_timer_config tmu0_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu0_resources[] = { + DEFINE_RES_MEM(0xffd80000, 0x30), + DEFINE_RES_IRQ(evt2irq(0x400)), + DEFINE_RES_IRQ(evt2irq(0x420)), + DEFINE_RES_IRQ(evt2irq(0x440)), +}; + +static struct platform_device tmu0_device = { + .name = "sh-tmu", + .id = 0, + .dev = { + .platform_data = &tmu0_platform_data, + }, + .resource = tmu0_resources, + .num_resources = ARRAY_SIZE(tmu0_resources), +}; + +static struct sh_timer_config tmu1_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu1_resources[] = { + DEFINE_RES_MEM(0xffd81000, 0x30), + DEFINE_RES_IRQ(evt2irq(0x480)), + DEFINE_RES_IRQ(evt2irq(0x4a0)), + DEFINE_RES_IRQ(evt2irq(0x4c0)), +}; + +static struct platform_device tmu1_device = { + .name = "sh-tmu", + .id = 1, + .dev = { + .platform_data = &tmu1_platform_data, + }, + .resource = tmu1_resources, + .num_resources = ARRAY_SIZE(tmu1_resources), +}; + +static struct sh_timer_config tmu2_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu2_resources[] = { + DEFINE_RES_MEM(0xffd82000, 0x30), + DEFINE_RES_IRQ(evt2irq(0x500)), + DEFINE_RES_IRQ(evt2irq(0x520)), + DEFINE_RES_IRQ(evt2irq(0x540)), +}; + +static struct platform_device tmu2_device = { + .name = "sh-tmu", + .id = 2, + .dev = { + .platform_data = &tmu2_platform_data, + }, + .resource = tmu2_resources, + .num_resources = ARRAY_SIZE(tmu2_resources), +}; + +static struct platform_device *sh7734_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + &scif3_device, + &scif4_device, + &scif5_device, + &tmu0_device, + &tmu1_device, + &tmu2_device, + &rtc_device, +}; + +static struct platform_device *sh7734_early_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + &scif3_device, + &scif4_device, + &scif5_device, + &tmu0_device, + &tmu1_device, + &tmu2_device, +}; + +void __init plat_early_device_setup(void) +{ + early_platform_add_devices(sh7734_early_devices, + ARRAY_SIZE(sh7734_early_devices)); +} + +#define GROUP 0 +enum { + UNUSED = 0, + + /* interrupt sources */ + + IRL0_LLLL, IRL0_LLLH, IRL0_LLHL, IRL0_LLHH, + IRL0_LHLL, IRL0_LHLH, IRL0_LHHL, IRL0_LHHH, + IRL0_HLLL, IRL0_HLLH, IRL0_HLHL, IRL0_HLHH, + IRL0_HHLL, IRL0_HHLH, IRL0_HHHL, + + IRQ0, IRQ1, IRQ2, IRQ3, + DU, + TMU00, TMU10, TMU20, TMU21, + TMU30, TMU40, TMU50, TMU51, + TMU60, TMU70, TMU80, + RESET_WDT, + USB, + HUDI, + SHDMAC, + SSI0, SSI1, SSI2, SSI3, + VIN0, + RGPVG, + _2DG, + MMC, + HSPI, + LBSCATA, + I2C0, + RCAN0, + MIMLB, + SCIF0, SCIF1, SCIF2, SCIF3, SCIF4, SCIF5, + LBSCDMAC0, LBSCDMAC1, LBSCDMAC2, + RCAN1, + SDHI0, SDHI1, + IEBUS, + HPBDMAC0_3, HPBDMAC4_10, HPBDMAC11_18, HPBDMAC19_22, HPBDMAC23_25_27_28, + RTC, + VIN1, + LCDC, + SRC0, SRC1, + GETHER, + SDHI2, + GPIO0_3, GPIO4_5, + STIF0, STIF1, + ADMAC, + HIF, + FLCTL, + ADC, + MTU2, + RSPI, + QSPI, + HSCIF, + VEU3F_VE3, + + /* Group */ + /* Mask */ + STIF_M, + GPIO_M, + HPBDMAC_M, + LBSCDMAC_M, + RCAN_M, + SRC_M, + SCIF_M, + LCDC_M, + _2DG_M, + VIN_M, + TMU_3_M, + TMU_0_M, + + /* Priority */ + RCAN_P, + LBSCDMAC_P, + + /* Common */ + SDHI, + SSI, + SPI, +}; + +static struct intc_vect vectors[] __initdata = { + INTC_VECT(DU, 0x3E0), + INTC_VECT(TMU00, 0x400), + INTC_VECT(TMU10, 0x420), + INTC_VECT(TMU20, 0x440), + INTC_VECT(TMU30, 0x480), + INTC_VECT(TMU40, 0x4A0), + INTC_VECT(TMU50, 0x4C0), + INTC_VECT(TMU51, 0x4E0), + INTC_VECT(TMU60, 0x500), + INTC_VECT(TMU70, 0x520), + INTC_VECT(TMU80, 0x540), + INTC_VECT(RESET_WDT, 0x560), + INTC_VECT(USB, 0x580), + INTC_VECT(HUDI, 0x600), + INTC_VECT(SHDMAC, 0x620), + INTC_VECT(SSI0, 0x6C0), + INTC_VECT(SSI1, 0x6E0), + INTC_VECT(SSI2, 0x700), + INTC_VECT(SSI3, 0x720), + INTC_VECT(VIN0, 0x740), + INTC_VECT(RGPVG, 0x760), + INTC_VECT(_2DG, 0x780), + INTC_VECT(MMC, 0x7A0), + INTC_VECT(HSPI, 0x7E0), + INTC_VECT(LBSCATA, 0x840), + INTC_VECT(I2C0, 0x860), + INTC_VECT(RCAN0, 0x880), + INTC_VECT(SCIF0, 0x8A0), + INTC_VECT(SCIF1, 0x8C0), + INTC_VECT(SCIF2, 0x900), + INTC_VECT(SCIF3, 0x920), + INTC_VECT(SCIF4, 0x940), + INTC_VECT(SCIF5, 0x960), + INTC_VECT(LBSCDMAC0, 0x9E0), + INTC_VECT(LBSCDMAC1, 0xA00), + INTC_VECT(LBSCDMAC2, 0xA20), + INTC_VECT(RCAN1, 0xA60), + INTC_VECT(SDHI0, 0xAE0), + INTC_VECT(SDHI1, 0xB00), + INTC_VECT(IEBUS, 0xB20), + INTC_VECT(HPBDMAC0_3, 0xB60), + INTC_VECT(HPBDMAC4_10, 0xB80), + INTC_VECT(HPBDMAC11_18, 0xBA0), + INTC_VECT(HPBDMAC19_22, 0xBC0), + INTC_VECT(HPBDMAC23_25_27_28, 0xBE0), + INTC_VECT(RTC, 0xC00), + INTC_VECT(VIN1, 0xC20), + INTC_VECT(LCDC, 0xC40), + INTC_VECT(SRC0, 0xC60), + INTC_VECT(SRC1, 0xC80), + INTC_VECT(GETHER, 0xCA0), + INTC_VECT(SDHI2, 0xCC0), + INTC_VECT(GPIO0_3, 0xCE0), + INTC_VECT(GPIO4_5, 0xD00), + INTC_VECT(STIF0, 0xD20), + INTC_VECT(STIF1, 0xD40), + INTC_VECT(ADMAC, 0xDA0), + INTC_VECT(HIF, 0xDC0), + INTC_VECT(FLCTL, 0xDE0), + INTC_VECT(ADC, 0xE00), + INTC_VECT(MTU2, 0xE20), + INTC_VECT(RSPI, 0xE40), + INTC_VECT(QSPI, 0xE60), + INTC_VECT(HSCIF, 0xFC0), + INTC_VECT(VEU3F_VE3, 0xF40), +}; + +static struct intc_group groups[] __initdata = { + /* Common */ + INTC_GROUP(SDHI, SDHI0, SDHI1, SDHI2), + INTC_GROUP(SPI, HSPI, RSPI, QSPI), + INTC_GROUP(SSI, SSI0, SSI1, SSI2, SSI3), + + /* Mask group */ + INTC_GROUP(STIF_M, STIF0, STIF1), /* 22 */ + INTC_GROUP(GPIO_M, GPIO0_3, GPIO4_5), /* 21 */ + INTC_GROUP(HPBDMAC_M, HPBDMAC0_3, HPBDMAC4_10, HPBDMAC11_18, + HPBDMAC19_22, HPBDMAC23_25_27_28), /* 19 */ + INTC_GROUP(LBSCDMAC_M, LBSCDMAC0, LBSCDMAC1, LBSCDMAC2), /* 18 */ + INTC_GROUP(RCAN_M, RCAN0, RCAN1, IEBUS), /* 17 */ + INTC_GROUP(SRC_M, SRC0, SRC1), /* 16 */ + INTC_GROUP(SCIF_M, SCIF0, SCIF1, SCIF2, SCIF3, SCIF4, SCIF5, + HSCIF), /* 14 */ + INTC_GROUP(LCDC_M, LCDC, MIMLB), /* 13 */ + INTC_GROUP(_2DG_M, _2DG, RGPVG), /* 12 */ + INTC_GROUP(VIN_M, VIN0, VIN1), /* 10 */ + INTC_GROUP(TMU_3_M, TMU30, TMU40, TMU50, TMU51, + TMU60, TMU60, TMU70, TMU80), /* 2 */ + INTC_GROUP(TMU_0_M, TMU00, TMU10, TMU20, TMU21), /* 1 */ + + /* Priority group*/ + INTC_GROUP(RCAN_P, RCAN0, RCAN1), /* INT2PRI5 */ + INTC_GROUP(LBSCDMAC_P, LBSCDMAC0, LBSCDMAC1), /* INT2PRI5 */ +}; + +static struct intc_mask_reg mask_registers[] __initdata = { + { 0xFF804040, 0xFF804044, 32, /* INT2MSKRG / INT2MSKCR */ + { 0, + VEU3F_VE3, + SDHI, /* SDHI 0-2 */ + ADMAC, + FLCTL, + RESET_WDT, + HIF, + ADC, + MTU2, + STIF_M, /* STIF 0,1 */ + GPIO_M, /* GPIO 0-5*/ + GETHER, + HPBDMAC_M, /* HPBDMAC 0_3 - 23_25_27_28 */ + LBSCDMAC_M, /* LBSCDMAC 0 - 2 */ + RCAN_M, /* RCAN, IEBUS */ + SRC_M, /* SRC 0,1 */ + LBSCATA, + SCIF_M, /* SCIF 0-5, HSCIF */ + LCDC_M, /* LCDC, MIMLB */ + _2DG_M, /* 2DG, RGPVG */ + SPI, /* HSPI, RSPI, QSPI */ + VIN_M, /* VIN0, 1 */ + SSI, /* SSI 0-3 */ + USB, + SHDMAC, + HUDI, + MMC, + RTC, + I2C0, /* I2C */ /* I2C 0, 1*/ + TMU_3_M, /* TMU30 - TMU80 */ + TMU_0_M, /* TMU00 - TMU21 */ + DU } }, +}; + +static struct intc_prio_reg prio_registers[] __initdata = { + { 0xFF804000, 0, 32, 8, /* INT2PRI0 */ + { DU, TMU00, TMU10, TMU20 } }, + { 0xFF804004, 0, 32, 8, /* INT2PRI1 */ + { TMU30, TMU60, RTC, SDHI } }, + { 0xFF804008, 0, 32, 8, /* INT2PRI2 */ + { HUDI, SHDMAC, USB, SSI } }, + { 0xFF80400C, 0, 32, 8, /* INT2PRI3 */ + { VIN0, SPI, _2DG, LBSCATA } }, + { 0xFF804010, 0, 32, 8, /* INT2PRI4 */ + { SCIF0, SCIF3, HSCIF, LCDC } }, + { 0xFF804014, 0, 32, 8, /* INT2PRI5 */ + { RCAN_P, LBSCDMAC_P, LBSCDMAC2, MMC } }, + { 0xFF804018, 0, 32, 8, /* INT2PRI6 */ + { HPBDMAC0_3, HPBDMAC4_10, HPBDMAC11_18, HPBDMAC19_22 } }, + { 0xFF80401C, 0, 32, 8, /* INT2PRI7 */ + { HPBDMAC23_25_27_28, I2C0, SRC0, SRC1 } }, + { 0xFF804020, 0, 32, 8, /* INT2PRI8 */ + { 0 /* ADIF */, VIN1, RESET_WDT, HIF } }, + { 0xFF804024, 0, 32, 8, /* INT2PRI9 */ + { ADMAC, FLCTL, GPIO0_3, GPIO4_5 } }, + { 0xFF804028, 0, 32, 8, /* INT2PRI10 */ + { STIF0, STIF1, VEU3F_VE3, GETHER } }, + { 0xFF80402C, 0, 32, 8, /* INT2PRI11 */ + { MTU2, RGPVG, MIMLB, IEBUS } }, +}; + +static DECLARE_INTC_DESC(intc_desc, "sh7734", vectors, groups, + mask_registers, prio_registers, NULL); + +/* Support for external interrupt pins in IRQ mode */ + +static struct intc_vect irq3210_vectors[] __initdata = { + INTC_VECT(IRQ0, 0x240), INTC_VECT(IRQ1, 0x280), + INTC_VECT(IRQ2, 0x2C0), INTC_VECT(IRQ3, 0x300), +}; + +static struct intc_sense_reg irq3210_sense_registers[] __initdata = { + { 0xFF80201C, 32, 2, /* ICR1 */ + { IRQ0, IRQ1, IRQ2, IRQ3, } }, +}; + +static struct intc_mask_reg irq3210_ack_registers[] __initdata = { + { 0xFF802024, 0, 32, /* INTREQ */ + { IRQ0, IRQ1, IRQ2, IRQ3, } }, +}; + +static struct intc_mask_reg irq3210_mask_registers[] __initdata = { + { 0xFF802044, 0xFF802064, 32, /* INTMSK0 / INTMSKCLR0 */ + { IRQ0, IRQ1, IRQ2, IRQ3, } }, +}; + +static struct intc_prio_reg irq3210_prio_registers[] __initdata = { + { 0xFF802010, 0, 32, 4, /* INTPRI */ + { IRQ0, IRQ1, IRQ2, IRQ3, } }, +}; + +static DECLARE_INTC_DESC_ACK(intc_desc_irq3210, "sh7734-irq3210", + irq3210_vectors, NULL, + irq3210_mask_registers, irq3210_prio_registers, + irq3210_sense_registers, irq3210_ack_registers); + +/* External interrupt pins in IRL mode */ + +static struct intc_vect vectors_irl3210[] __initdata = { + INTC_VECT(IRL0_LLLL, 0x200), INTC_VECT(IRL0_LLLH, 0x220), + INTC_VECT(IRL0_LLHL, 0x240), INTC_VECT(IRL0_LLHH, 0x260), + INTC_VECT(IRL0_LHLL, 0x280), INTC_VECT(IRL0_LHLH, 0x2a0), + INTC_VECT(IRL0_LHHL, 0x2c0), INTC_VECT(IRL0_LHHH, 0x2e0), + INTC_VECT(IRL0_HLLL, 0x300), INTC_VECT(IRL0_HLLH, 0x320), + INTC_VECT(IRL0_HLHL, 0x340), INTC_VECT(IRL0_HLHH, 0x360), + INTC_VECT(IRL0_HHLL, 0x380), INTC_VECT(IRL0_HHLH, 0x3a0), + INTC_VECT(IRL0_HHHL, 0x3c0), +}; + +static DECLARE_INTC_DESC(intc_desc_irl3210, "sh7734-irl3210", + vectors_irl3210, NULL, mask_registers, NULL, NULL); + +#define INTC_ICR0 0xFF802000 +#define INTC_INTMSK0 0xFF802044 +#define INTC_INTMSK1 0xFF802048 +#define INTC_INTMSKCLR0 0xFF802064 +#define INTC_INTMSKCLR1 0xFF802068 + +void __init plat_irq_setup(void) +{ + /* disable IRQ3-0 */ + __raw_writel(0xF0000000, INTC_INTMSK0); + + /* disable IRL3-0 */ + __raw_writel(0x80000000, INTC_INTMSK1); + + /* select IRL mode for IRL3-0 */ + __raw_writel(__raw_readl(INTC_ICR0) & ~0x00800000, INTC_ICR0); + + /* disable holding function, ie enable "SH-4 Mode (LVLMODE)" */ + __raw_writel(__raw_readl(INTC_ICR0) | 0x00200000, INTC_ICR0); + + register_intc_controller(&intc_desc); +} + +void __init plat_irq_setup_pins(int mode) +{ + switch (mode) { + case IRQ_MODE_IRQ3210: + /* select IRQ mode for IRL3-0 */ + __raw_writel(__raw_readl(INTC_ICR0) | 0x00800000, INTC_ICR0); + register_intc_controller(&intc_desc_irq3210); + break; + case IRQ_MODE_IRL3210: + /* enable IRL0-3 but don't provide any masking */ + __raw_writel(0x80000000, INTC_INTMSKCLR1); + __raw_writel(0xf0000000, INTC_INTMSKCLR0); + break; + case IRQ_MODE_IRL3210_MASK: + /* enable IRL0-3 and mask using cpu intc controller */ + __raw_writel(0x80000000, INTC_INTMSKCLR0); + register_intc_controller(&intc_desc_irl3210); + break; + default: + BUG(); + } +} diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7757.c b/arch/sh/kernel/cpu/sh4a/setup-sh7757.c new file mode 100644 index 000000000..a46a19b49 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7757.c @@ -0,0 +1,1244 @@ +/* + * SH7757 Setup + * + * Copyright (C) 2009, 2011 Renesas Solutions Corp. + * + * based on setup-sh7785.c : Copyright (C) 2007 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/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <linux/serial_sci.h> +#include <linux/io.h> +#include <linux/mm.h> +#include <linux/dma-mapping.h> +#include <linux/sh_timer.h> +#include <linux/sh_dma.h> +#include <linux/sh_intc.h> +#include <linux/usb/ohci_pdriver.h> +#include <cpu/dma-register.h> +#include <cpu/sh7757.h> + +static struct plat_sci_port scif2_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, +}; + +static struct resource scif2_resources[] = { + DEFINE_RES_MEM(0xfe4b0000, 0x100), /* SCIF2 */ + DEFINE_RES_IRQ(evt2irq(0x700)), +}; + +static struct platform_device scif2_device = { + .name = "sh-sci", + .id = 0, + .resource = scif2_resources, + .num_resources = ARRAY_SIZE(scif2_resources), + .dev = { + .platform_data = &scif2_platform_data, + }, +}; + +static struct plat_sci_port scif3_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, +}; + +static struct resource scif3_resources[] = { + DEFINE_RES_MEM(0xfe4c0000, 0x100), /* SCIF3 */ + DEFINE_RES_IRQ(evt2irq(0xb80)), +}; + +static struct platform_device scif3_device = { + .name = "sh-sci", + .id = 1, + .resource = scif3_resources, + .num_resources = ARRAY_SIZE(scif3_resources), + .dev = { + .platform_data = &scif3_platform_data, + }, +}; + +static struct plat_sci_port scif4_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, +}; + +static struct resource scif4_resources[] = { + DEFINE_RES_MEM(0xfe4d0000, 0x100), /* SCIF4 */ + DEFINE_RES_IRQ(evt2irq(0xf00)), +}; + +static struct platform_device scif4_device = { + .name = "sh-sci", + .id = 2, + .resource = scif4_resources, + .num_resources = ARRAY_SIZE(scif4_resources), + .dev = { + .platform_data = &scif4_platform_data, + }, +}; + +static struct sh_timer_config tmu0_platform_data = { + .channels_mask = 3, +}; + +static struct resource tmu0_resources[] = { + DEFINE_RES_MEM(0xfe430000, 0x20), + DEFINE_RES_IRQ(evt2irq(0x580)), + DEFINE_RES_IRQ(evt2irq(0x5a0)), +}; + +static struct platform_device tmu0_device = { + .name = "sh-tmu", + .id = 0, + .dev = { + .platform_data = &tmu0_platform_data, + }, + .resource = tmu0_resources, + .num_resources = ARRAY_SIZE(tmu0_resources), +}; + +static struct resource spi0_resources[] = { + [0] = { + .start = 0xfe002000, + .end = 0xfe0020ff, + .flags = IORESOURCE_MEM | IORESOURCE_MEM_32BIT, + }, + [1] = { + .start = evt2irq(0xcc0), + .flags = IORESOURCE_IRQ, + }, +}; + +/* DMA */ +static const struct sh_dmae_slave_config sh7757_dmae0_slaves[] = { + { + .slave_id = SHDMA_SLAVE_SDHI_TX, + .addr = 0x1fe50030, + .chcr = SM_INC | RS_ERS | 0x40000000 | + TS_INDEX2VAL(XMIT_SZ_16BIT), + .mid_rid = 0xc5, + }, + { + .slave_id = SHDMA_SLAVE_SDHI_RX, + .addr = 0x1fe50030, + .chcr = DM_INC | RS_ERS | 0x40000000 | + TS_INDEX2VAL(XMIT_SZ_16BIT), + .mid_rid = 0xc6, + }, + { + .slave_id = SHDMA_SLAVE_MMCIF_TX, + .addr = 0x1fcb0034, + .chcr = SM_INC | RS_ERS | 0x40000000 | + TS_INDEX2VAL(XMIT_SZ_32BIT), + .mid_rid = 0xd3, + }, + { + .slave_id = SHDMA_SLAVE_MMCIF_RX, + .addr = 0x1fcb0034, + .chcr = DM_INC | RS_ERS | 0x40000000 | + TS_INDEX2VAL(XMIT_SZ_32BIT), + .mid_rid = 0xd7, + }, +}; + +static const struct sh_dmae_slave_config sh7757_dmae1_slaves[] = { + { + .slave_id = SHDMA_SLAVE_SCIF2_TX, + .addr = 0x1f4b000c, + .chcr = SM_INC | RS_ERS | 0x40000000 | + TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x21, + }, + { + .slave_id = SHDMA_SLAVE_SCIF2_RX, + .addr = 0x1f4b0014, + .chcr = DM_INC | RS_ERS | 0x40000000 | + TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x22, + }, + { + .slave_id = SHDMA_SLAVE_SCIF3_TX, + .addr = 0x1f4c000c, + .chcr = SM_INC | RS_ERS | 0x40000000 | + TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x29, + }, + { + .slave_id = SHDMA_SLAVE_SCIF3_RX, + .addr = 0x1f4c0014, + .chcr = DM_INC | RS_ERS | 0x40000000 | + TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x2a, + }, + { + .slave_id = SHDMA_SLAVE_SCIF4_TX, + .addr = 0x1f4d000c, + .chcr = SM_INC | RS_ERS | 0x40000000 | + TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x41, + }, + { + .slave_id = SHDMA_SLAVE_SCIF4_RX, + .addr = 0x1f4d0014, + .chcr = DM_INC | RS_ERS | 0x40000000 | + TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x42, + }, + { + .slave_id = SHDMA_SLAVE_RSPI_TX, + .addr = 0xfe480004, + .chcr = SM_INC | RS_ERS | 0x40000000 | + TS_INDEX2VAL(XMIT_SZ_16BIT), + .mid_rid = 0xc1, + }, + { + .slave_id = SHDMA_SLAVE_RSPI_RX, + .addr = 0xfe480004, + .chcr = DM_INC | RS_ERS | 0x40000000 | + TS_INDEX2VAL(XMIT_SZ_16BIT), + .mid_rid = 0xc2, + }, +}; + +static const struct sh_dmae_slave_config sh7757_dmae2_slaves[] = { + { + .slave_id = SHDMA_SLAVE_RIIC0_TX, + .addr = 0x1e500012, + .chcr = SM_INC | RS_ERS | 0x40000000 | + TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x21, + }, + { + .slave_id = SHDMA_SLAVE_RIIC0_RX, + .addr = 0x1e500013, + .chcr = DM_INC | RS_ERS | 0x40000000 | + TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x22, + }, + { + .slave_id = SHDMA_SLAVE_RIIC1_TX, + .addr = 0x1e510012, + .chcr = SM_INC | RS_ERS | 0x40000000 | + TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x29, + }, + { + .slave_id = SHDMA_SLAVE_RIIC1_RX, + .addr = 0x1e510013, + .chcr = DM_INC | RS_ERS | 0x40000000 | + TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x2a, + }, + { + .slave_id = SHDMA_SLAVE_RIIC2_TX, + .addr = 0x1e520012, + .chcr = SM_INC | RS_ERS | 0x40000000 | + TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0xa1, + }, + { + .slave_id = SHDMA_SLAVE_RIIC2_RX, + .addr = 0x1e520013, + .chcr = DM_INC | RS_ERS | 0x40000000 | + TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0xa2, + }, + { + .slave_id = SHDMA_SLAVE_RIIC3_TX, + .addr = 0x1e530012, + .chcr = SM_INC | RS_ERS | 0x40000000 | + TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0xa9, + }, + { + .slave_id = SHDMA_SLAVE_RIIC3_RX, + .addr = 0x1e530013, + .chcr = DM_INC | RS_ERS | 0x40000000 | + TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0xaf, + }, + { + .slave_id = SHDMA_SLAVE_RIIC4_TX, + .addr = 0x1e540012, + .chcr = SM_INC | RS_ERS | 0x40000000 | + TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0xc5, + }, + { + .slave_id = SHDMA_SLAVE_RIIC4_RX, + .addr = 0x1e540013, + .chcr = DM_INC | RS_ERS | 0x40000000 | + TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0xc6, + }, +}; + +static const struct sh_dmae_slave_config sh7757_dmae3_slaves[] = { + { + .slave_id = SHDMA_SLAVE_RIIC5_TX, + .addr = 0x1e550012, + .chcr = SM_INC | RS_ERS | 0x40000000 | + TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x21, + }, + { + .slave_id = SHDMA_SLAVE_RIIC5_RX, + .addr = 0x1e550013, + .chcr = DM_INC | RS_ERS | 0x40000000 | + TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x22, + }, + { + .slave_id = SHDMA_SLAVE_RIIC6_TX, + .addr = 0x1e560012, + .chcr = SM_INC | RS_ERS | 0x40000000 | + TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x29, + }, + { + .slave_id = SHDMA_SLAVE_RIIC6_RX, + .addr = 0x1e560013, + .chcr = DM_INC | RS_ERS | 0x40000000 | + TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x2a, + }, + { + .slave_id = SHDMA_SLAVE_RIIC7_TX, + .addr = 0x1e570012, + .chcr = SM_INC | RS_ERS | 0x40000000 | + TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x41, + }, + { + .slave_id = SHDMA_SLAVE_RIIC7_RX, + .addr = 0x1e570013, + .chcr = DM_INC | RS_ERS | 0x40000000 | + TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x42, + }, + { + .slave_id = SHDMA_SLAVE_RIIC8_TX, + .addr = 0x1e580012, + .chcr = SM_INC | RS_ERS | 0x40000000 | + TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x45, + }, + { + .slave_id = SHDMA_SLAVE_RIIC8_RX, + .addr = 0x1e580013, + .chcr = DM_INC | RS_ERS | 0x40000000 | + TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x46, + }, + { + .slave_id = SHDMA_SLAVE_RIIC9_TX, + .addr = 0x1e590012, + .chcr = SM_INC | RS_ERS | 0x40000000 | + TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x51, + }, + { + .slave_id = SHDMA_SLAVE_RIIC9_RX, + .addr = 0x1e590013, + .chcr = DM_INC | RS_ERS | 0x40000000 | + TS_INDEX2VAL(XMIT_SZ_8BIT), + .mid_rid = 0x52, + }, +}; + +static const struct sh_dmae_channel sh7757_dmae_channels[] = { + { + .offset = 0, + .dmars = 0, + .dmars_bit = 0, + }, { + .offset = 0x10, + .dmars = 0, + .dmars_bit = 8, + }, { + .offset = 0x20, + .dmars = 4, + .dmars_bit = 0, + }, { + .offset = 0x30, + .dmars = 4, + .dmars_bit = 8, + }, { + .offset = 0x50, + .dmars = 8, + .dmars_bit = 0, + }, { + .offset = 0x60, + .dmars = 8, + .dmars_bit = 8, + } +}; + +static const unsigned int ts_shift[] = TS_SHIFT; + +static struct sh_dmae_pdata dma0_platform_data = { + .slave = sh7757_dmae0_slaves, + .slave_num = ARRAY_SIZE(sh7757_dmae0_slaves), + .channel = sh7757_dmae_channels, + .channel_num = ARRAY_SIZE(sh7757_dmae_channels), + .ts_low_shift = CHCR_TS_LOW_SHIFT, + .ts_low_mask = CHCR_TS_LOW_MASK, + .ts_high_shift = CHCR_TS_HIGH_SHIFT, + .ts_high_mask = CHCR_TS_HIGH_MASK, + .ts_shift = ts_shift, + .ts_shift_num = ARRAY_SIZE(ts_shift), + .dmaor_init = DMAOR_INIT, +}; + +static struct sh_dmae_pdata dma1_platform_data = { + .slave = sh7757_dmae1_slaves, + .slave_num = ARRAY_SIZE(sh7757_dmae1_slaves), + .channel = sh7757_dmae_channels, + .channel_num = ARRAY_SIZE(sh7757_dmae_channels), + .ts_low_shift = CHCR_TS_LOW_SHIFT, + .ts_low_mask = CHCR_TS_LOW_MASK, + .ts_high_shift = CHCR_TS_HIGH_SHIFT, + .ts_high_mask = CHCR_TS_HIGH_MASK, + .ts_shift = ts_shift, + .ts_shift_num = ARRAY_SIZE(ts_shift), + .dmaor_init = DMAOR_INIT, +}; + +static struct sh_dmae_pdata dma2_platform_data = { + .slave = sh7757_dmae2_slaves, + .slave_num = ARRAY_SIZE(sh7757_dmae2_slaves), + .channel = sh7757_dmae_channels, + .channel_num = ARRAY_SIZE(sh7757_dmae_channels), + .ts_low_shift = CHCR_TS_LOW_SHIFT, + .ts_low_mask = CHCR_TS_LOW_MASK, + .ts_high_shift = CHCR_TS_HIGH_SHIFT, + .ts_high_mask = CHCR_TS_HIGH_MASK, + .ts_shift = ts_shift, + .ts_shift_num = ARRAY_SIZE(ts_shift), + .dmaor_init = DMAOR_INIT, +}; + +static struct sh_dmae_pdata dma3_platform_data = { + .slave = sh7757_dmae3_slaves, + .slave_num = ARRAY_SIZE(sh7757_dmae3_slaves), + .channel = sh7757_dmae_channels, + .channel_num = ARRAY_SIZE(sh7757_dmae_channels), + .ts_low_shift = CHCR_TS_LOW_SHIFT, + .ts_low_mask = CHCR_TS_LOW_MASK, + .ts_high_shift = CHCR_TS_HIGH_SHIFT, + .ts_high_mask = CHCR_TS_HIGH_MASK, + .ts_shift = ts_shift, + .ts_shift_num = ARRAY_SIZE(ts_shift), + .dmaor_init = DMAOR_INIT, +}; + +/* channel 0 to 5 */ +static struct resource sh7757_dmae0_resources[] = { + [0] = { + /* Channel registers and DMAOR */ + .start = 0xff608020, + .end = 0xff60808f, + .flags = IORESOURCE_MEM, + }, + [1] = { + /* DMARSx */ + .start = 0xff609000, + .end = 0xff60900b, + .flags = IORESOURCE_MEM, + }, + { + .name = "error_irq", + .start = evt2irq(0x640), + .end = evt2irq(0x640), + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE, + }, +}; + +/* channel 6 to 11 */ +static struct resource sh7757_dmae1_resources[] = { + [0] = { + /* Channel registers and DMAOR */ + .start = 0xff618020, + .end = 0xff61808f, + .flags = IORESOURCE_MEM, + }, + [1] = { + /* DMARSx */ + .start = 0xff619000, + .end = 0xff61900b, + .flags = IORESOURCE_MEM, + }, + { + .name = "error_irq", + .start = evt2irq(0x640), + .end = evt2irq(0x640), + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE, + }, + { + /* IRQ for channels 4 */ + .start = evt2irq(0x7c0), + .end = evt2irq(0x7c0), + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE, + }, + { + /* IRQ for channels 5 */ + .start = evt2irq(0x7c0), + .end = evt2irq(0x7c0), + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE, + }, + { + /* IRQ for channels 6 */ + .start = evt2irq(0xd00), + .end = evt2irq(0xd00), + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE, + }, + { + /* IRQ for channels 7 */ + .start = evt2irq(0xd00), + .end = evt2irq(0xd00), + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE, + }, + { + /* IRQ for channels 8 */ + .start = evt2irq(0xd00), + .end = evt2irq(0xd00), + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE, + }, + { + /* IRQ for channels 9 */ + .start = evt2irq(0xd00), + .end = evt2irq(0xd00), + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE, + }, + { + /* IRQ for channels 10 */ + .start = evt2irq(0xd00), + .end = evt2irq(0xd00), + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE, + }, + { + /* IRQ for channels 11 */ + .start = evt2irq(0xd00), + .end = evt2irq(0xd00), + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE, + }, +}; + +/* channel 12 to 17 */ +static struct resource sh7757_dmae2_resources[] = { + [0] = { + /* Channel registers and DMAOR */ + .start = 0xff708020, + .end = 0xff70808f, + .flags = IORESOURCE_MEM, + }, + [1] = { + /* DMARSx */ + .start = 0xff709000, + .end = 0xff70900b, + .flags = IORESOURCE_MEM, + }, + { + .name = "error_irq", + .start = evt2irq(0x2a60), + .end = evt2irq(0x2a60), + .flags = IORESOURCE_IRQ, + }, + { + /* IRQ for channels 12 to 16 */ + .start = evt2irq(0x2400), + .end = evt2irq(0x2480), + .flags = IORESOURCE_IRQ, + }, + { + /* IRQ for channel 17 */ + .start = evt2irq(0x24e0), + .end = evt2irq(0x24e0), + .flags = IORESOURCE_IRQ, + }, +}; + +/* channel 18 to 23 */ +static struct resource sh7757_dmae3_resources[] = { + [0] = { + /* Channel registers and DMAOR */ + .start = 0xff718020, + .end = 0xff71808f, + .flags = IORESOURCE_MEM, + }, + [1] = { + /* DMARSx */ + .start = 0xff719000, + .end = 0xff71900b, + .flags = IORESOURCE_MEM, + }, + { + .name = "error_irq", + .start = evt2irq(0x2a80), + .end = evt2irq(0x2a80), + .flags = IORESOURCE_IRQ, + }, + { + /* IRQ for channels 18 to 22 */ + .start = evt2irq(0x2500), + .end = evt2irq(0x2580), + .flags = IORESOURCE_IRQ, + }, + { + /* IRQ for channel 23 */ + .start = evt2irq(0x2600), + .end = evt2irq(0x2600), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device dma0_device = { + .name = "sh-dma-engine", + .id = 0, + .resource = sh7757_dmae0_resources, + .num_resources = ARRAY_SIZE(sh7757_dmae0_resources), + .dev = { + .platform_data = &dma0_platform_data, + }, +}; + +static struct platform_device dma1_device = { + .name = "sh-dma-engine", + .id = 1, + .resource = sh7757_dmae1_resources, + .num_resources = ARRAY_SIZE(sh7757_dmae1_resources), + .dev = { + .platform_data = &dma1_platform_data, + }, +}; + +static struct platform_device dma2_device = { + .name = "sh-dma-engine", + .id = 2, + .resource = sh7757_dmae2_resources, + .num_resources = ARRAY_SIZE(sh7757_dmae2_resources), + .dev = { + .platform_data = &dma2_platform_data, + }, +}; + +static struct platform_device dma3_device = { + .name = "sh-dma-engine", + .id = 3, + .resource = sh7757_dmae3_resources, + .num_resources = ARRAY_SIZE(sh7757_dmae3_resources), + .dev = { + .platform_data = &dma3_platform_data, + }, +}; + +static struct platform_device spi0_device = { + .name = "sh_spi", + .id = 0, + .dev = { + .dma_mask = NULL, + .coherent_dma_mask = 0xffffffff, + }, + .num_resources = ARRAY_SIZE(spi0_resources), + .resource = spi0_resources, +}; + +static struct resource spi1_resources[] = { + { + .start = 0xffd8ee70, + .end = 0xffd8eeff, + .flags = IORESOURCE_MEM | IORESOURCE_MEM_8BIT, + }, + { + .start = evt2irq(0x8c0), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device spi1_device = { + .name = "sh_spi", + .id = 1, + .num_resources = ARRAY_SIZE(spi1_resources), + .resource = spi1_resources, +}; + +static struct resource rspi_resources[] = { + { + .start = 0xfe480000, + .end = 0xfe4800ff, + .flags = IORESOURCE_MEM, + }, + { + .start = evt2irq(0x1d80), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device rspi_device = { + .name = "rspi", + .id = 2, + .num_resources = ARRAY_SIZE(rspi_resources), + .resource = rspi_resources, +}; + +static struct resource usb_ehci_resources[] = { + [0] = { + .start = 0xfe4f1000, + .end = 0xfe4f10ff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = evt2irq(0x920), + .end = evt2irq(0x920), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device usb_ehci_device = { + .name = "sh_ehci", + .id = -1, + .dev = { + .dma_mask = &usb_ehci_device.dev.coherent_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, + .num_resources = ARRAY_SIZE(usb_ehci_resources), + .resource = usb_ehci_resources, +}; + +static struct resource usb_ohci_resources[] = { + [0] = { + .start = 0xfe4f1800, + .end = 0xfe4f18ff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = evt2irq(0x920), + .end = evt2irq(0x920), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct usb_ohci_pdata usb_ohci_pdata; + +static struct platform_device usb_ohci_device = { + .name = "ohci-platform", + .id = -1, + .dev = { + .dma_mask = &usb_ohci_device.dev.coherent_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &usb_ohci_pdata, + }, + .num_resources = ARRAY_SIZE(usb_ohci_resources), + .resource = usb_ohci_resources, +}; + +static struct platform_device *sh7757_devices[] __initdata = { + &scif2_device, + &scif3_device, + &scif4_device, + &tmu0_device, + &dma0_device, + &dma1_device, + &dma2_device, + &dma3_device, + &spi0_device, + &spi1_device, + &rspi_device, + &usb_ehci_device, + &usb_ohci_device, +}; + +static int __init sh7757_devices_setup(void) +{ + return platform_add_devices(sh7757_devices, + ARRAY_SIZE(sh7757_devices)); +} +arch_initcall(sh7757_devices_setup); + +static struct platform_device *sh7757_early_devices[] __initdata = { + &scif2_device, + &scif3_device, + &scif4_device, + &tmu0_device, +}; + +void __init plat_early_device_setup(void) +{ + early_platform_add_devices(sh7757_early_devices, + ARRAY_SIZE(sh7757_early_devices)); +} + +enum { + UNUSED = 0, + + /* interrupt sources */ + + IRL0_LLLL, IRL0_LLLH, IRL0_LLHL, IRL0_LLHH, + IRL0_LHLL, IRL0_LHLH, IRL0_LHHL, IRL0_LHHH, + IRL0_HLLL, IRL0_HLLH, IRL0_HLHL, IRL0_HLHH, + IRL0_HHLL, IRL0_HHLH, IRL0_HHHL, + + IRL4_LLLL, IRL4_LLLH, IRL4_LLHL, IRL4_LLHH, + IRL4_LHLL, IRL4_LHLH, IRL4_LHHL, IRL4_LHHH, + IRL4_HLLL, IRL4_HLLH, IRL4_HLHL, IRL4_HLHH, + IRL4_HHLL, IRL4_HHLH, IRL4_HHHL, + IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7, + + SDHI, DVC, + IRQ8, IRQ9, IRQ11, IRQ10, IRQ12, IRQ13, IRQ14, IRQ15, + TMU0, TMU1, TMU2, TMU2_TICPI, TMU3, TMU4, TMU5, + HUDI, + ARC4, + DMAC0_5, DMAC6_7, DMAC8_11, + SCIF0, SCIF1, SCIF2, SCIF3, SCIF4, + USB0, USB1, + JMC, + SPI0, SPI1, + TMR01, TMR23, TMR45, + FRT, + LPC, LPC5, LPC6, LPC7, LPC8, + PECI0, PECI1, PECI2, PECI3, PECI4, PECI5, + ETHERC, + ADC0, ADC1, + SIM, + IIC0_0, IIC0_1, IIC0_2, IIC0_3, + IIC1_0, IIC1_1, IIC1_2, IIC1_3, + IIC2_0, IIC2_1, IIC2_2, IIC2_3, + IIC3_0, IIC3_1, IIC3_2, IIC3_3, + IIC4_0, IIC4_1, IIC4_2, IIC4_3, + IIC5_0, IIC5_1, IIC5_2, IIC5_3, + IIC6_0, IIC6_1, IIC6_2, IIC6_3, + IIC7_0, IIC7_1, IIC7_2, IIC7_3, + IIC8_0, IIC8_1, IIC8_2, IIC8_3, + IIC9_0, IIC9_1, IIC9_2, IIC9_3, + ONFICTL, + MMC1, MMC2, + ECCU, + PCIC, + G200, + RSPI, + SGPIO, + DMINT12, DMINT13, DMINT14, DMINT15, DMINT16, DMINT17, DMINT18, DMINT19, + DMINT20, DMINT21, DMINT22, DMINT23, + DDRECC, + TSIP, + PCIE_BRIDGE, + WDT0B, WDT1B, WDT2B, WDT3B, WDT4B, WDT5B, WDT6B, WDT7B, WDT8B, + GETHER0, GETHER1, GETHER2, + PBIA, PBIB, PBIC, + DMAE2, DMAE3, + SERMUX2, SERMUX3, + + /* interrupt groups */ + + TMU012, TMU345, +}; + +static struct intc_vect vectors[] __initdata = { + INTC_VECT(SDHI, 0x480), INTC_VECT(SDHI, 0x04a0), + INTC_VECT(SDHI, 0x4c0), + INTC_VECT(DVC, 0x4e0), + INTC_VECT(IRQ8, 0x500), INTC_VECT(IRQ9, 0x520), + INTC_VECT(IRQ10, 0x540), + INTC_VECT(TMU0, 0x580), INTC_VECT(TMU1, 0x5a0), + INTC_VECT(TMU2, 0x5c0), INTC_VECT(TMU2_TICPI, 0x5e0), + INTC_VECT(HUDI, 0x600), + INTC_VECT(ARC4, 0x620), + INTC_VECT(DMAC0_5, 0x640), INTC_VECT(DMAC0_5, 0x660), + INTC_VECT(DMAC0_5, 0x680), INTC_VECT(DMAC0_5, 0x6a0), + INTC_VECT(DMAC0_5, 0x6c0), + INTC_VECT(IRQ11, 0x6e0), + INTC_VECT(SCIF2, 0x700), INTC_VECT(SCIF2, 0x720), + INTC_VECT(SCIF2, 0x740), INTC_VECT(SCIF2, 0x760), + INTC_VECT(DMAC0_5, 0x780), INTC_VECT(DMAC0_5, 0x7a0), + INTC_VECT(DMAC6_7, 0x7c0), INTC_VECT(DMAC6_7, 0x7e0), + INTC_VECT(USB0, 0x840), + INTC_VECT(IRQ12, 0x880), + INTC_VECT(JMC, 0x8a0), + INTC_VECT(SPI1, 0x8c0), + INTC_VECT(IRQ13, 0x8e0), INTC_VECT(IRQ14, 0x900), + INTC_VECT(USB1, 0x920), + INTC_VECT(TMR01, 0xa00), INTC_VECT(TMR23, 0xa20), + INTC_VECT(TMR45, 0xa40), + INTC_VECT(FRT, 0xa80), + INTC_VECT(LPC, 0xaa0), INTC_VECT(LPC, 0xac0), + INTC_VECT(LPC, 0xae0), INTC_VECT(LPC, 0xb00), + INTC_VECT(LPC, 0xb20), + INTC_VECT(SCIF0, 0xb40), INTC_VECT(SCIF1, 0xb60), + INTC_VECT(SCIF3, 0xb80), INTC_VECT(SCIF3, 0xba0), + INTC_VECT(SCIF3, 0xbc0), INTC_VECT(SCIF3, 0xbe0), + INTC_VECT(PECI0, 0xc00), INTC_VECT(PECI1, 0xc20), + INTC_VECT(PECI2, 0xc40), + INTC_VECT(IRQ15, 0xc60), + INTC_VECT(ETHERC, 0xc80), INTC_VECT(ETHERC, 0xca0), + INTC_VECT(SPI0, 0xcc0), + INTC_VECT(ADC1, 0xce0), + INTC_VECT(DMAC8_11, 0xd00), INTC_VECT(DMAC8_11, 0xd20), + INTC_VECT(DMAC8_11, 0xd40), INTC_VECT(DMAC8_11, 0xd60), + INTC_VECT(SIM, 0xd80), INTC_VECT(SIM, 0xda0), + INTC_VECT(SIM, 0xdc0), INTC_VECT(SIM, 0xde0), + INTC_VECT(TMU3, 0xe00), INTC_VECT(TMU4, 0xe20), + INTC_VECT(TMU5, 0xe40), + INTC_VECT(ADC0, 0xe60), + INTC_VECT(SCIF4, 0xf00), INTC_VECT(SCIF4, 0xf20), + INTC_VECT(SCIF4, 0xf40), INTC_VECT(SCIF4, 0xf60), + INTC_VECT(IIC0_0, 0x1400), INTC_VECT(IIC0_1, 0x1420), + INTC_VECT(IIC0_2, 0x1440), INTC_VECT(IIC0_3, 0x1460), + INTC_VECT(IIC1_0, 0x1480), INTC_VECT(IIC1_1, 0x14e0), + INTC_VECT(IIC1_2, 0x1500), INTC_VECT(IIC1_3, 0x1520), + INTC_VECT(IIC2_0, 0x1540), INTC_VECT(IIC2_1, 0x1560), + INTC_VECT(IIC2_2, 0x1580), INTC_VECT(IIC2_3, 0x1600), + INTC_VECT(IIC3_0, 0x1620), INTC_VECT(IIC3_1, 0x1640), + INTC_VECT(IIC3_2, 0x16e0), INTC_VECT(IIC3_3, 0x1700), + INTC_VECT(IIC4_0, 0x17c0), INTC_VECT(IIC4_1, 0x1800), + INTC_VECT(IIC4_2, 0x1820), INTC_VECT(IIC4_3, 0x1840), + INTC_VECT(IIC5_0, 0x1860), INTC_VECT(IIC5_1, 0x1880), + INTC_VECT(IIC5_2, 0x18a0), INTC_VECT(IIC5_3, 0x18c0), + INTC_VECT(IIC6_0, 0x18e0), INTC_VECT(IIC6_1, 0x1900), + INTC_VECT(IIC6_2, 0x1920), + INTC_VECT(ONFICTL, 0x1960), + INTC_VECT(IIC6_3, 0x1980), + INTC_VECT(IIC7_0, 0x19a0), INTC_VECT(IIC7_1, 0x1a00), + INTC_VECT(IIC7_2, 0x1a20), INTC_VECT(IIC7_3, 0x1a40), + INTC_VECT(IIC8_0, 0x1a60), INTC_VECT(IIC8_1, 0x1a80), + INTC_VECT(IIC8_2, 0x1aa0), INTC_VECT(IIC8_3, 0x1b40), + INTC_VECT(IIC9_0, 0x1b60), INTC_VECT(IIC9_1, 0x1b80), + INTC_VECT(IIC9_2, 0x1c00), INTC_VECT(IIC9_3, 0x1c20), + INTC_VECT(MMC1, 0x1c60), INTC_VECT(MMC2, 0x1c80), + INTC_VECT(ECCU, 0x1cc0), + INTC_VECT(PCIC, 0x1ce0), + INTC_VECT(G200, 0x1d00), + INTC_VECT(RSPI, 0x1d80), INTC_VECT(RSPI, 0x1da0), + INTC_VECT(RSPI, 0x1dc0), INTC_VECT(RSPI, 0x1de0), + INTC_VECT(PECI3, 0x1ec0), INTC_VECT(PECI4, 0x1ee0), + INTC_VECT(PECI5, 0x1f00), + INTC_VECT(SGPIO, 0x1f80), INTC_VECT(SGPIO, 0x1fa0), + INTC_VECT(SGPIO, 0x1fc0), + INTC_VECT(DMINT12, 0x2400), INTC_VECT(DMINT13, 0x2420), + INTC_VECT(DMINT14, 0x2440), INTC_VECT(DMINT15, 0x2460), + INTC_VECT(DMINT16, 0x2480), INTC_VECT(DMINT17, 0x24e0), + INTC_VECT(DMINT18, 0x2500), INTC_VECT(DMINT19, 0x2520), + INTC_VECT(DMINT20, 0x2540), INTC_VECT(DMINT21, 0x2560), + INTC_VECT(DMINT22, 0x2580), INTC_VECT(DMINT23, 0x2600), + INTC_VECT(DDRECC, 0x2620), + INTC_VECT(TSIP, 0x2640), + INTC_VECT(PCIE_BRIDGE, 0x27c0), + INTC_VECT(WDT0B, 0x2800), INTC_VECT(WDT1B, 0x2820), + INTC_VECT(WDT2B, 0x2840), INTC_VECT(WDT3B, 0x2860), + INTC_VECT(WDT4B, 0x2880), INTC_VECT(WDT5B, 0x28a0), + INTC_VECT(WDT6B, 0x28c0), INTC_VECT(WDT7B, 0x28e0), + INTC_VECT(WDT8B, 0x2900), + INTC_VECT(GETHER0, 0x2960), INTC_VECT(GETHER1, 0x2980), + INTC_VECT(GETHER2, 0x29a0), + INTC_VECT(PBIA, 0x2a00), INTC_VECT(PBIB, 0x2a20), + INTC_VECT(PBIC, 0x2a40), + INTC_VECT(DMAE2, 0x2a60), INTC_VECT(DMAE3, 0x2a80), + INTC_VECT(SERMUX2, 0x2aa0), INTC_VECT(SERMUX3, 0x2b40), + INTC_VECT(LPC5, 0x2b60), INTC_VECT(LPC6, 0x2b80), + INTC_VECT(LPC7, 0x2c00), INTC_VECT(LPC8, 0x2c20), +}; + +static struct intc_group groups[] __initdata = { + INTC_GROUP(TMU012, TMU0, TMU1, TMU2, TMU2_TICPI), + INTC_GROUP(TMU345, TMU3, TMU4, TMU5), +}; + +static struct intc_mask_reg mask_registers[] __initdata = { + { 0xffd00044, 0xffd00064, 32, /* INTMSK0 / INTMSKCLR0 */ + { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } }, + + { 0xffd40080, 0xffd40084, 32, /* INTMSK2 / INTMSKCLR2 */ + { IRL0_LLLL, IRL0_LLLH, IRL0_LLHL, IRL0_LLHH, + IRL0_LHLL, IRL0_LHLH, IRL0_LHHL, IRL0_LHHH, + IRL0_HLLL, IRL0_HLLH, IRL0_HLHL, IRL0_HLHH, + IRL0_HHLL, IRL0_HHLH, IRL0_HHHL, 0, + IRL4_LLLL, IRL4_LLLH, IRL4_LLHL, IRL4_LLHH, + IRL4_LHLL, IRL4_LHLH, IRL4_LHHL, IRL4_LHHH, + IRL4_HLLL, IRL4_HLLH, IRL4_HLHL, IRL4_HLHH, + IRL4_HHLL, IRL4_HHLH, IRL4_HHHL, 0, } }, + + { 0xffd40038, 0xffd4003c, 32, /* INT2MSKR / INT2MSKCR */ + { 0, 0, 0, 0, 0, 0, 0, 0, + 0, DMAC8_11, 0, PECI0, LPC, FRT, 0, TMR45, + TMR23, TMR01, 0, 0, 0, 0, 0, DMAC0_5, + HUDI, 0, 0, SCIF3, SCIF2, SDHI, TMU345, TMU012 + } }, + + { 0xffd400d0, 0xffd400d4, 32, /* INT2MSKR1 / INT2MSKCR1 */ + { IRQ15, IRQ14, IRQ13, IRQ12, IRQ11, IRQ10, SCIF4, ETHERC, + IRQ9, IRQ8, SCIF1, SCIF0, USB0, 0, 0, USB1, + ADC1, 0, DMAC6_7, ADC0, SPI0, SIM, PECI2, PECI1, + ARC4, 0, SPI1, JMC, 0, 0, 0, DVC + } }, + + { 0xffd10038, 0xffd1003c, 32, /* INT2MSKR2 / INT2MSKCR2 */ + { IIC4_1, IIC4_2, IIC5_0, ONFICTL, 0, 0, SGPIO, 0, + 0, G200, 0, IIC9_2, IIC8_2, IIC8_1, IIC8_0, IIC7_3, + IIC7_2, IIC7_1, IIC6_3, IIC0_0, IIC0_1, IIC0_2, IIC0_3, IIC3_1, + IIC2_3, 0, IIC2_1, IIC9_1, IIC3_3, IIC1_0, 0, IIC2_2 + } }, + + { 0xffd100d0, 0xffd100d4, 32, /* INT2MSKR3 / INT2MSKCR3 */ + { MMC1, IIC6_1, IIC6_0, IIC5_1, IIC3_2, IIC2_0, PECI5, MMC2, + IIC1_3, IIC1_2, IIC9_0, IIC8_3, IIC4_3, IIC7_0, 0, IIC6_2, + PCIC, 0, IIC4_0, 0, ECCU, RSPI, 0, IIC9_3, + IIC3_0, 0, IIC5_3, IIC5_2, 0, 0, 0, IIC1_1 + } }, + + { 0xffd20038, 0xffd2003c, 32, /* INT2MSKR4 / INT2MSKCR4 */ + { WDT0B, WDT1B, WDT3B, GETHER0, 0, 0, 0, 0, + 0, 0, 0, LPC7, SERMUX2, DMAE3, DMAE2, PBIC, + PBIB, PBIA, GETHER1, DMINT12, DMINT13, DMINT14, DMINT15, TSIP, + DMINT23, 0, DMINT21, LPC6, 0, DMINT16, 0, DMINT22 + } }, + + { 0xffd200d0, 0xffd200d4, 32, /* INT2MSKR5 / INT2MSKCR5 */ + { 0, WDT8B, WDT7B, WDT4B, 0, DMINT20, 0, 0, + DMINT19, DMINT18, LPC5, SERMUX3, WDT2B, GETHER2, 0, 0, + 0, 0, PCIE_BRIDGE, 0, 0, 0, 0, LPC8, + DDRECC, 0, WDT6B, WDT5B, 0, 0, 0, DMINT17 + } }, +}; + +#define INTPRI 0xffd00010 +#define INT2PRI0 0xffd40000 +#define INT2PRI1 0xffd40004 +#define INT2PRI2 0xffd40008 +#define INT2PRI3 0xffd4000c +#define INT2PRI4 0xffd40010 +#define INT2PRI5 0xffd40014 +#define INT2PRI6 0xffd40018 +#define INT2PRI7 0xffd4001c +#define INT2PRI8 0xffd400a0 +#define INT2PRI9 0xffd400a4 +#define INT2PRI10 0xffd400a8 +#define INT2PRI11 0xffd400ac +#define INT2PRI12 0xffd400b0 +#define INT2PRI13 0xffd400b4 +#define INT2PRI14 0xffd400b8 +#define INT2PRI15 0xffd400bc +#define INT2PRI16 0xffd10000 +#define INT2PRI17 0xffd10004 +#define INT2PRI18 0xffd10008 +#define INT2PRI19 0xffd1000c +#define INT2PRI20 0xffd10010 +#define INT2PRI21 0xffd10014 +#define INT2PRI22 0xffd10018 +#define INT2PRI23 0xffd1001c +#define INT2PRI24 0xffd100a0 +#define INT2PRI25 0xffd100a4 +#define INT2PRI26 0xffd100a8 +#define INT2PRI27 0xffd100ac +#define INT2PRI28 0xffd100b0 +#define INT2PRI29 0xffd100b4 +#define INT2PRI30 0xffd100b8 +#define INT2PRI31 0xffd100bc +#define INT2PRI32 0xffd20000 +#define INT2PRI33 0xffd20004 +#define INT2PRI34 0xffd20008 +#define INT2PRI35 0xffd2000c +#define INT2PRI36 0xffd20010 +#define INT2PRI37 0xffd20014 +#define INT2PRI38 0xffd20018 +#define INT2PRI39 0xffd2001c +#define INT2PRI40 0xffd200a0 +#define INT2PRI41 0xffd200a4 +#define INT2PRI42 0xffd200a8 +#define INT2PRI43 0xffd200ac +#define INT2PRI44 0xffd200b0 +#define INT2PRI45 0xffd200b4 +#define INT2PRI46 0xffd200b8 +#define INT2PRI47 0xffd200bc + +static struct intc_prio_reg prio_registers[] __initdata = { + { INTPRI, 0, 32, 4, { IRQ0, IRQ1, IRQ2, IRQ3, + IRQ4, IRQ5, IRQ6, IRQ7 } }, + + { INT2PRI0, 0, 32, 8, { TMU0, TMU1, TMU2, TMU2_TICPI } }, + { INT2PRI1, 0, 32, 8, { TMU3, TMU4, TMU5, SDHI } }, + { INT2PRI2, 0, 32, 8, { SCIF2, SCIF3, 0, IRQ8 } }, + { INT2PRI3, 0, 32, 8, { HUDI, DMAC0_5, ADC0, IRQ9 } }, + { INT2PRI4, 0, 32, 8, { IRQ10, 0, TMR01, TMR23 } }, + { INT2PRI5, 0, 32, 8, { TMR45, 0, FRT, LPC } }, + { INT2PRI6, 0, 32, 8, { PECI0, ETHERC, DMAC8_11, 0 } }, + { INT2PRI7, 0, 32, 8, { SCIF4, 0, IRQ11, IRQ12 } }, + { INT2PRI8, 0, 32, 8, { 0, 0, 0, DVC } }, + { INT2PRI9, 0, 32, 8, { ARC4, 0, SPI1, JMC } }, + { INT2PRI10, 0, 32, 8, { SPI0, SIM, PECI2, PECI1 } }, + { INT2PRI11, 0, 32, 8, { ADC1, IRQ13, DMAC6_7, IRQ14 } }, + { INT2PRI12, 0, 32, 8, { USB0, 0, IRQ15, USB1 } }, + { INT2PRI13, 0, 32, 8, { 0, 0, SCIF1, SCIF0 } }, + + { INT2PRI16, 0, 32, 8, { IIC2_2, 0, 0, 0 } }, + { INT2PRI17, 0, 32, 8, { 0, 0, 0, IIC1_0 } }, + { INT2PRI18, 0, 32, 8, { IIC3_3, IIC9_1, IIC2_1, IIC1_2 } }, + { INT2PRI19, 0, 32, 8, { IIC2_3, IIC3_1, 0, IIC1_3 } }, + { INT2PRI20, 0, 32, 8, { IIC2_0, IIC6_3, IIC7_1, IIC7_2 } }, + { INT2PRI21, 0, 32, 8, { IIC7_3, IIC8_0, IIC8_1, IIC8_2 } }, + { INT2PRI22, 0, 32, 8, { IIC9_2, MMC2, G200, 0 } }, + { INT2PRI23, 0, 32, 8, { PECI5, SGPIO, IIC3_2, IIC5_1 } }, + { INT2PRI24, 0, 32, 8, { PECI4, PECI3, 0, IIC1_1 } }, + { INT2PRI25, 0, 32, 8, { IIC3_0, 0, IIC5_3, IIC5_2 } }, + { INT2PRI26, 0, 32, 8, { ECCU, RSPI, 0, IIC9_3 } }, + { INT2PRI27, 0, 32, 8, { PCIC, IIC6_0, IIC4_0, IIC6_1 } }, + { INT2PRI28, 0, 32, 8, { IIC4_3, IIC7_0, MMC1, IIC6_2 } }, + { INT2PRI29, 0, 32, 8, { 0, 0, IIC9_0, IIC8_3 } }, + { INT2PRI30, 0, 32, 8, { IIC4_1, IIC4_2, IIC5_0, ONFICTL } }, + { INT2PRI31, 0, 32, 8, { IIC0_0, IIC0_1, IIC0_2, IIC0_3 } }, + { INT2PRI32, 0, 32, 8, { DMINT22, 0, 0, 0 } }, + { INT2PRI33, 0, 32, 8, { 0, 0, 0, DMINT16 } }, + { INT2PRI34, 0, 32, 8, { 0, LPC6, DMINT21, DMINT18 } }, + { INT2PRI35, 0, 32, 8, { DMINT23, TSIP, 0, DMINT19 } }, + { INT2PRI36, 0, 32, 8, { DMINT20, GETHER1, PBIA, PBIB } }, + { INT2PRI37, 0, 32, 8, { PBIC, DMAE2, DMAE3, SERMUX2 } }, + { INT2PRI38, 0, 32, 8, { LPC7, 0, 0, 0 } }, + { INT2PRI39, 0, 32, 8, { 0, 0, 0, WDT4B } }, + { INT2PRI40, 0, 32, 8, { 0, 0, 0, DMINT17 } }, + { INT2PRI41, 0, 32, 8, { DDRECC, 0, WDT6B, WDT5B } }, + { INT2PRI42, 0, 32, 8, { 0, 0, 0, LPC8 } }, + { INT2PRI43, 0, 32, 8, { 0, WDT7B, PCIE_BRIDGE, WDT8B } }, + { INT2PRI44, 0, 32, 8, { WDT2B, GETHER2, 0, 0 } }, + { INT2PRI45, 0, 32, 8, { 0, 0, LPC5, SERMUX3 } }, + { INT2PRI46, 0, 32, 8, { WDT0B, WDT1B, WDT3B, GETHER0 } }, + { INT2PRI47, 0, 32, 8, { DMINT12, DMINT13, DMINT14, DMINT15 } }, +}; + +static struct intc_sense_reg sense_registers_irq8to15[] __initdata = { + { 0xffd100f8, 32, 2, /* ICR2 */ { IRQ15, IRQ14, IRQ13, IRQ12, + IRQ11, IRQ10, IRQ9, IRQ8 } }, +}; + +static DECLARE_INTC_DESC(intc_desc, "sh7757", vectors, groups, + mask_registers, prio_registers, + sense_registers_irq8to15); + +/* Support for external interrupt pins in IRQ mode */ +static struct intc_vect vectors_irq0123[] __initdata = { + INTC_VECT(IRQ0, 0x200), INTC_VECT(IRQ1, 0x240), + INTC_VECT(IRQ2, 0x280), INTC_VECT(IRQ3, 0x2c0), +}; + +static struct intc_vect vectors_irq4567[] __initdata = { + INTC_VECT(IRQ4, 0x300), INTC_VECT(IRQ5, 0x340), + INTC_VECT(IRQ6, 0x380), INTC_VECT(IRQ7, 0x3c0), +}; + +static struct intc_sense_reg sense_registers[] __initdata = { + { 0xffd0001c, 32, 2, /* ICR1 */ { IRQ0, IRQ1, IRQ2, IRQ3, + IRQ4, IRQ5, IRQ6, IRQ7 } }, +}; + +static struct intc_mask_reg ack_registers[] __initdata = { + { 0xffd00024, 0, 32, /* INTREQ */ + { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } }, +}; + +static DECLARE_INTC_DESC_ACK(intc_desc_irq0123, "sh7757-irq0123", + vectors_irq0123, NULL, mask_registers, + prio_registers, sense_registers, ack_registers); + +static DECLARE_INTC_DESC_ACK(intc_desc_irq4567, "sh7757-irq4567", + vectors_irq4567, NULL, mask_registers, + prio_registers, sense_registers, ack_registers); + +/* External interrupt pins in IRL mode */ +static struct intc_vect vectors_irl0123[] __initdata = { + INTC_VECT(IRL0_LLLL, 0x200), INTC_VECT(IRL0_LLLH, 0x220), + INTC_VECT(IRL0_LLHL, 0x240), INTC_VECT(IRL0_LLHH, 0x260), + INTC_VECT(IRL0_LHLL, 0x280), INTC_VECT(IRL0_LHLH, 0x2a0), + INTC_VECT(IRL0_LHHL, 0x2c0), INTC_VECT(IRL0_LHHH, 0x2e0), + INTC_VECT(IRL0_HLLL, 0x300), INTC_VECT(IRL0_HLLH, 0x320), + INTC_VECT(IRL0_HLHL, 0x340), INTC_VECT(IRL0_HLHH, 0x360), + INTC_VECT(IRL0_HHLL, 0x380), INTC_VECT(IRL0_HHLH, 0x3a0), + INTC_VECT(IRL0_HHHL, 0x3c0), +}; + +static struct intc_vect vectors_irl4567[] __initdata = { + INTC_VECT(IRL4_LLLL, 0x200), INTC_VECT(IRL4_LLLH, 0x220), + INTC_VECT(IRL4_LLHL, 0x240), INTC_VECT(IRL4_LLHH, 0x260), + INTC_VECT(IRL4_LHLL, 0x280), INTC_VECT(IRL4_LHLH, 0x2a0), + INTC_VECT(IRL4_LHHL, 0x2c0), INTC_VECT(IRL4_LHHH, 0x2e0), + INTC_VECT(IRL4_HLLL, 0x300), INTC_VECT(IRL4_HLLH, 0x320), + INTC_VECT(IRL4_HLHL, 0x340), INTC_VECT(IRL4_HLHH, 0x360), + INTC_VECT(IRL4_HHLL, 0x380), INTC_VECT(IRL4_HHLH, 0x3a0), + INTC_VECT(IRL4_HHHL, 0x3c0), +}; + +static DECLARE_INTC_DESC(intc_desc_irl0123, "sh7757-irl0123", vectors_irl0123, + NULL, mask_registers, NULL, NULL); + +static DECLARE_INTC_DESC(intc_desc_irl4567, "sh7757-irl4567", vectors_irl4567, + NULL, mask_registers, NULL, NULL); + +#define INTC_ICR0 0xffd00000 +#define INTC_INTMSK0 0xffd00044 +#define INTC_INTMSK1 0xffd00048 +#define INTC_INTMSK2 0xffd40080 +#define INTC_INTMSKCLR1 0xffd00068 +#define INTC_INTMSKCLR2 0xffd40084 + +void __init plat_irq_setup(void) +{ + /* disable IRQ3-0 + IRQ7-4 */ + __raw_writel(0xff000000, INTC_INTMSK0); + + /* disable IRL3-0 + IRL7-4 */ + __raw_writel(0xc0000000, INTC_INTMSK1); + __raw_writel(0xfffefffe, INTC_INTMSK2); + + /* select IRL mode for IRL3-0 + IRL7-4 */ + __raw_writel(__raw_readl(INTC_ICR0) & ~0x00c00000, INTC_ICR0); + + /* disable holding function, ie enable "SH-4 Mode" */ + __raw_writel(__raw_readl(INTC_ICR0) | 0x00200000, INTC_ICR0); + + register_intc_controller(&intc_desc); +} + +void __init plat_irq_setup_pins(int mode) +{ + switch (mode) { + case IRQ_MODE_IRQ7654: + /* select IRQ mode for IRL7-4 */ + __raw_writel(__raw_readl(INTC_ICR0) | 0x00400000, INTC_ICR0); + register_intc_controller(&intc_desc_irq4567); + break; + case IRQ_MODE_IRQ3210: + /* select IRQ mode for IRL3-0 */ + __raw_writel(__raw_readl(INTC_ICR0) | 0x00800000, INTC_ICR0); + register_intc_controller(&intc_desc_irq0123); + break; + case IRQ_MODE_IRL7654: + /* enable IRL7-4 but don't provide any masking */ + __raw_writel(0x40000000, INTC_INTMSKCLR1); + __raw_writel(0x0000fffe, INTC_INTMSKCLR2); + break; + case IRQ_MODE_IRL3210: + /* enable IRL0-3 but don't provide any masking */ + __raw_writel(0x80000000, INTC_INTMSKCLR1); + __raw_writel(0xfffe0000, INTC_INTMSKCLR2); + break; + case IRQ_MODE_IRL7654_MASK: + /* enable IRL7-4 and mask using cpu intc controller */ + __raw_writel(0x40000000, INTC_INTMSKCLR1); + register_intc_controller(&intc_desc_irl4567); + break; + case IRQ_MODE_IRL3210_MASK: + /* enable IRL0-3 and mask using cpu intc controller */ + __raw_writel(0x80000000, INTC_INTMSKCLR1); + register_intc_controller(&intc_desc_irl0123); + break; + default: + BUG(); + } +} + +void __init plat_mem_setup(void) +{ +} diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7763.c b/arch/sh/kernel/cpu/sh4a/setup-sh7763.c new file mode 100644 index 000000000..40e6cda91 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7763.c @@ -0,0 +1,457 @@ +/* + * SH7763 Setup + * + * Copyright (C) 2006 Paul Mundt + * Copyright (C) 2007 Yoshihiro Shimoda + * Copyright (C) 2008, 2009 Nobuhiro Iwamatsu + * + * 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/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <linux/sh_timer.h> +#include <linux/sh_intc.h> +#include <linux/io.h> +#include <linux/serial_sci.h> +#include <linux/usb/ohci_pdriver.h> + +static struct plat_sci_port scif0_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif0_resources[] = { + DEFINE_RES_MEM(0xffe00000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x700)), +}; + +static struct platform_device scif0_device = { + .name = "sh-sci", + .id = 0, + .resource = scif0_resources, + .num_resources = ARRAY_SIZE(scif0_resources), + .dev = { + .platform_data = &scif0_platform_data, + }, +}; + +static struct plat_sci_port scif1_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif1_resources[] = { + DEFINE_RES_MEM(0xffe08000, 0x100), + DEFINE_RES_IRQ(evt2irq(0xb80)), +}; + +static struct platform_device scif1_device = { + .name = "sh-sci", + .id = 1, + .resource = scif1_resources, + .num_resources = ARRAY_SIZE(scif1_resources), + .dev = { + .platform_data = &scif1_platform_data, + }, +}; + +static struct plat_sci_port scif2_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif2_resources[] = { + DEFINE_RES_MEM(0xffe10000, 0x100), + DEFINE_RES_IRQ(evt2irq(0xf00)), +}; + +static struct platform_device scif2_device = { + .name = "sh-sci", + .id = 2, + .resource = scif2_resources, + .num_resources = ARRAY_SIZE(scif2_resources), + .dev = { + .platform_data = &scif2_platform_data, + }, +}; + +static struct resource rtc_resources[] = { + [0] = { + .start = 0xffe80000, + .end = 0xffe80000 + 0x58 - 1, + .flags = IORESOURCE_IO, + }, + [1] = { + /* Shared Period/Carry/Alarm IRQ */ + .start = evt2irq(0x480), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device rtc_device = { + .name = "sh-rtc", + .id = -1, + .num_resources = ARRAY_SIZE(rtc_resources), + .resource = rtc_resources, +}; + +static struct resource usb_ohci_resources[] = { + [0] = { + .start = 0xffec8000, + .end = 0xffec80ff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = evt2irq(0xc60), + .end = evt2irq(0xc60), + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 usb_ohci_dma_mask = 0xffffffffUL; + +static struct usb_ohci_pdata usb_ohci_pdata; + +static struct platform_device usb_ohci_device = { + .name = "ohci-platform", + .id = -1, + .dev = { + .dma_mask = &usb_ohci_dma_mask, + .coherent_dma_mask = 0xffffffff, + .platform_data = &usb_ohci_pdata, + }, + .num_resources = ARRAY_SIZE(usb_ohci_resources), + .resource = usb_ohci_resources, +}; + +static struct resource usbf_resources[] = { + [0] = { + .start = 0xffec0000, + .end = 0xffec00ff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = evt2irq(0xc80), + .end = evt2irq(0xc80), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device usbf_device = { + .name = "sh_udc", + .id = -1, + .dev = { + .dma_mask = NULL, + .coherent_dma_mask = 0xffffffff, + }, + .num_resources = ARRAY_SIZE(usbf_resources), + .resource = usbf_resources, +}; + +static struct sh_timer_config tmu0_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu0_resources[] = { + DEFINE_RES_MEM(0xffd80000, 0x30), + DEFINE_RES_IRQ(evt2irq(0x580)), + DEFINE_RES_IRQ(evt2irq(0x5a0)), + DEFINE_RES_IRQ(evt2irq(0x5c0)), +}; + +static struct platform_device tmu0_device = { + .name = "sh-tmu", + .id = 0, + .dev = { + .platform_data = &tmu0_platform_data, + }, + .resource = tmu0_resources, + .num_resources = ARRAY_SIZE(tmu0_resources), +}; + +static struct sh_timer_config tmu1_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu1_resources[] = { + DEFINE_RES_MEM(0xffd88000, 0x2c), + DEFINE_RES_IRQ(evt2irq(0xe00)), + DEFINE_RES_IRQ(evt2irq(0xe20)), + DEFINE_RES_IRQ(evt2irq(0xe40)), +}; + +static struct platform_device tmu1_device = { + .name = "sh-tmu", + .id = 1, + .dev = { + .platform_data = &tmu1_platform_data, + }, + .resource = tmu1_resources, + .num_resources = ARRAY_SIZE(tmu1_resources), +}; + +static struct platform_device *sh7763_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + &tmu0_device, + &tmu1_device, + &rtc_device, + &usb_ohci_device, + &usbf_device, +}; + +static int __init sh7763_devices_setup(void) +{ + return platform_add_devices(sh7763_devices, + ARRAY_SIZE(sh7763_devices)); +} +arch_initcall(sh7763_devices_setup); + +static struct platform_device *sh7763_early_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + &tmu0_device, + &tmu1_device, +}; + +void __init plat_early_device_setup(void) +{ + early_platform_add_devices(sh7763_early_devices, + ARRAY_SIZE(sh7763_early_devices)); +} + +enum { + UNUSED = 0, + + /* interrupt sources */ + + IRL_LLLL, IRL_LLLH, IRL_LLHL, IRL_LLHH, + IRL_LHLL, IRL_LHLH, IRL_LHHL, IRL_LHHH, + IRL_HLLL, IRL_HLLH, IRL_HLHL, IRL_HLHH, + IRL_HHLL, IRL_HHLH, IRL_HHHL, + + IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7, + RTC, WDT, TMU0, TMU1, TMU2, TMU2_TICPI, + HUDI, LCDC, DMAC, SCIF0, IIC0, IIC1, CMT, GETHER, HAC, + PCISERR, PCIINTA, PCIINTB, PCIINTC, PCIINTD, PCIC5, + STIF0, STIF1, SCIF1, SIOF0, SIOF1, SIOF2, + USBH, USBF, TPU, PCC, MMCIF, SIM, + TMU3, TMU4, TMU5, ADC, SSI0, SSI1, SSI2, SSI3, + SCIF2, GPIO, + + /* interrupt groups */ + + TMU012, TMU345, +}; + +static struct intc_vect vectors[] __initdata = { + INTC_VECT(RTC, 0x480), INTC_VECT(RTC, 0x4a0), + INTC_VECT(RTC, 0x4c0), + INTC_VECT(WDT, 0x560), INTC_VECT(TMU0, 0x580), + INTC_VECT(TMU1, 0x5a0), INTC_VECT(TMU2, 0x5c0), + INTC_VECT(TMU2_TICPI, 0x5e0), INTC_VECT(HUDI, 0x600), + INTC_VECT(LCDC, 0x620), + INTC_VECT(DMAC, 0x640), INTC_VECT(DMAC, 0x660), + INTC_VECT(DMAC, 0x680), INTC_VECT(DMAC, 0x6a0), + INTC_VECT(DMAC, 0x6c0), + INTC_VECT(SCIF0, 0x700), INTC_VECT(SCIF0, 0x720), + INTC_VECT(SCIF0, 0x740), INTC_VECT(SCIF0, 0x760), + INTC_VECT(DMAC, 0x780), INTC_VECT(DMAC, 0x7a0), + INTC_VECT(IIC0, 0x8A0), INTC_VECT(IIC1, 0x8C0), + INTC_VECT(CMT, 0x900), INTC_VECT(GETHER, 0x920), + INTC_VECT(GETHER, 0x940), INTC_VECT(GETHER, 0x960), + INTC_VECT(HAC, 0x980), + INTC_VECT(PCISERR, 0xa00), INTC_VECT(PCIINTA, 0xa20), + INTC_VECT(PCIINTB, 0xa40), INTC_VECT(PCIINTC, 0xa60), + INTC_VECT(PCIINTD, 0xa80), INTC_VECT(PCIC5, 0xaa0), + INTC_VECT(PCIC5, 0xac0), INTC_VECT(PCIC5, 0xae0), + INTC_VECT(PCIC5, 0xb00), INTC_VECT(PCIC5, 0xb20), + INTC_VECT(STIF0, 0xb40), INTC_VECT(STIF1, 0xb60), + INTC_VECT(SCIF1, 0xb80), INTC_VECT(SCIF1, 0xba0), + INTC_VECT(SCIF1, 0xbc0), INTC_VECT(SCIF1, 0xbe0), + INTC_VECT(SIOF0, 0xc00), INTC_VECT(SIOF1, 0xc20), + INTC_VECT(USBH, 0xc60), INTC_VECT(USBF, 0xc80), + INTC_VECT(USBF, 0xca0), + INTC_VECT(TPU, 0xcc0), INTC_VECT(PCC, 0xce0), + INTC_VECT(MMCIF, 0xd00), INTC_VECT(MMCIF, 0xd20), + INTC_VECT(MMCIF, 0xd40), INTC_VECT(MMCIF, 0xd60), + INTC_VECT(SIM, 0xd80), INTC_VECT(SIM, 0xda0), + INTC_VECT(SIM, 0xdc0), INTC_VECT(SIM, 0xde0), + INTC_VECT(TMU3, 0xe00), INTC_VECT(TMU4, 0xe20), + INTC_VECT(TMU5, 0xe40), INTC_VECT(ADC, 0xe60), + INTC_VECT(SSI0, 0xe80), INTC_VECT(SSI1, 0xea0), + INTC_VECT(SSI2, 0xec0), INTC_VECT(SSI3, 0xee0), + INTC_VECT(SCIF2, 0xf00), INTC_VECT(SCIF2, 0xf20), + INTC_VECT(SCIF2, 0xf40), INTC_VECT(SCIF2, 0xf60), + INTC_VECT(GPIO, 0xf80), INTC_VECT(GPIO, 0xfa0), + INTC_VECT(GPIO, 0xfc0), INTC_VECT(GPIO, 0xfe0), +}; + +static struct intc_group groups[] __initdata = { + INTC_GROUP(TMU012, TMU0, TMU1, TMU2, TMU2_TICPI), + INTC_GROUP(TMU345, TMU3, TMU4, TMU5), +}; + +static struct intc_mask_reg mask_registers[] __initdata = { + { 0xffd40038, 0xffd4003c, 32, /* INT2MSKR / INT2MSKCR */ + { 0, 0, 0, 0, 0, 0, GPIO, 0, + SSI0, MMCIF, 0, SIOF0, PCIC5, PCIINTD, PCIINTC, PCIINTB, + PCIINTA, PCISERR, HAC, CMT, 0, 0, 0, DMAC, + HUDI, 0, WDT, SCIF1, SCIF0, RTC, TMU345, TMU012 } }, + { 0xffd400d0, 0xffd400d4, 32, /* INT2MSKR1 / INT2MSKCR1 */ + { 0, 0, 0, 0, 0, 0, SCIF2, USBF, + 0, 0, STIF1, STIF0, 0, 0, USBH, GETHER, + PCC, 0, 0, ADC, TPU, SIM, SIOF2, SIOF1, + LCDC, 0, IIC1, IIC0, SSI3, SSI2, SSI1, 0 } }, +}; + +static struct intc_prio_reg prio_registers[] __initdata = { + { 0xffd40000, 0, 32, 8, /* INT2PRI0 */ { TMU0, TMU1, + TMU2, TMU2_TICPI } }, + { 0xffd40004, 0, 32, 8, /* INT2PRI1 */ { TMU3, TMU4, TMU5, RTC } }, + { 0xffd40008, 0, 32, 8, /* INT2PRI2 */ { SCIF0, SCIF1, WDT } }, + { 0xffd4000c, 0, 32, 8, /* INT2PRI3 */ { HUDI, DMAC, ADC } }, + { 0xffd40010, 0, 32, 8, /* INT2PRI4 */ { CMT, HAC, + PCISERR, PCIINTA } }, + { 0xffd40014, 0, 32, 8, /* INT2PRI5 */ { PCIINTB, PCIINTC, + PCIINTD, PCIC5 } }, + { 0xffd40018, 0, 32, 8, /* INT2PRI6 */ { SIOF0, USBF, MMCIF, SSI0 } }, + { 0xffd4001c, 0, 32, 8, /* INT2PRI7 */ { SCIF2, GPIO } }, + { 0xffd400a0, 0, 32, 8, /* INT2PRI8 */ { SSI3, SSI2, SSI1, 0 } }, + { 0xffd400a4, 0, 32, 8, /* INT2PRI9 */ { LCDC, 0, IIC1, IIC0 } }, + { 0xffd400a8, 0, 32, 8, /* INT2PRI10 */ { TPU, SIM, SIOF2, SIOF1 } }, + { 0xffd400ac, 0, 32, 8, /* INT2PRI11 */ { PCC } }, + { 0xffd400b0, 0, 32, 8, /* INT2PRI12 */ { 0, 0, USBH, GETHER } }, + { 0xffd400b4, 0, 32, 8, /* INT2PRI13 */ { 0, 0, STIF1, STIF0 } }, +}; + +static DECLARE_INTC_DESC(intc_desc, "sh7763", vectors, groups, + mask_registers, prio_registers, NULL); + +/* Support for external interrupt pins in IRQ mode */ +static struct intc_vect irq_vectors[] __initdata = { + INTC_VECT(IRQ0, 0x240), INTC_VECT(IRQ1, 0x280), + INTC_VECT(IRQ2, 0x2c0), INTC_VECT(IRQ3, 0x300), + INTC_VECT(IRQ4, 0x340), INTC_VECT(IRQ5, 0x380), + INTC_VECT(IRQ6, 0x3c0), INTC_VECT(IRQ7, 0x200), +}; + +static struct intc_mask_reg irq_mask_registers[] __initdata = { + { 0xffd00044, 0xffd00064, 32, /* INTMSK0 / INTMSKCLR0 */ + { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } }, +}; + +static struct intc_prio_reg irq_prio_registers[] __initdata = { + { 0xffd00010, 0, 32, 4, /* INTPRI */ { IRQ0, IRQ1, IRQ2, IRQ3, + IRQ4, IRQ5, IRQ6, IRQ7 } }, +}; + +static struct intc_sense_reg irq_sense_registers[] __initdata = { + { 0xffd0001c, 32, 2, /* ICR1 */ { IRQ0, IRQ1, IRQ2, IRQ3, + IRQ4, IRQ5, IRQ6, IRQ7 } }, +}; + +static struct intc_mask_reg irq_ack_registers[] __initdata = { + { 0xffd00024, 0, 32, /* INTREQ */ + { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } }, +}; + +static DECLARE_INTC_DESC_ACK(intc_irq_desc, "sh7763-irq", irq_vectors, + NULL, irq_mask_registers, irq_prio_registers, + irq_sense_registers, irq_ack_registers); + + +/* External interrupt pins in IRL mode */ +static struct intc_vect irl_vectors[] __initdata = { + INTC_VECT(IRL_LLLL, 0x200), INTC_VECT(IRL_LLLH, 0x220), + INTC_VECT(IRL_LLHL, 0x240), INTC_VECT(IRL_LLHH, 0x260), + INTC_VECT(IRL_LHLL, 0x280), INTC_VECT(IRL_LHLH, 0x2a0), + INTC_VECT(IRL_LHHL, 0x2c0), INTC_VECT(IRL_LHHH, 0x2e0), + INTC_VECT(IRL_HLLL, 0x300), INTC_VECT(IRL_HLLH, 0x320), + INTC_VECT(IRL_HLHL, 0x340), INTC_VECT(IRL_HLHH, 0x360), + INTC_VECT(IRL_HHLL, 0x380), INTC_VECT(IRL_HHLH, 0x3a0), + INTC_VECT(IRL_HHHL, 0x3c0), +}; + +static struct intc_mask_reg irl3210_mask_registers[] __initdata = { + { 0xffd40080, 0xffd40084, 32, /* INTMSK2 / INTMSKCLR2 */ + { IRL_LLLL, IRL_LLLH, IRL_LLHL, IRL_LLHH, + IRL_LHLL, IRL_LHLH, IRL_LHHL, IRL_LHHH, + IRL_HLLL, IRL_HLLH, IRL_HLHL, IRL_HLHH, + IRL_HHLL, IRL_HHLH, IRL_HHHL, } }, +}; + +static struct intc_mask_reg irl7654_mask_registers[] __initdata = { + { 0xffd40080, 0xffd40084, 32, /* INTMSK2 / INTMSKCLR2 */ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + IRL_LLLL, IRL_LLLH, IRL_LLHL, IRL_LLHH, + IRL_LHLL, IRL_LHLH, IRL_LHHL, IRL_LHHH, + IRL_HLLL, IRL_HLLH, IRL_HLHL, IRL_HLHH, + IRL_HHLL, IRL_HHLH, IRL_HHHL, } }, +}; + +static DECLARE_INTC_DESC(intc_irl7654_desc, "sh7763-irl7654", irl_vectors, + NULL, irl7654_mask_registers, NULL, NULL); + +static DECLARE_INTC_DESC(intc_irl3210_desc, "sh7763-irl3210", irl_vectors, + NULL, irl3210_mask_registers, NULL, NULL); + +#define INTC_ICR0 0xffd00000 +#define INTC_INTMSK0 0xffd00044 +#define INTC_INTMSK1 0xffd00048 +#define INTC_INTMSK2 0xffd40080 +#define INTC_INTMSKCLR1 0xffd00068 +#define INTC_INTMSKCLR2 0xffd40084 + +void __init plat_irq_setup(void) +{ + /* disable IRQ7-0 */ + __raw_writel(0xff000000, INTC_INTMSK0); + + /* disable IRL3-0 + IRL7-4 */ + __raw_writel(0xc0000000, INTC_INTMSK1); + __raw_writel(0xfffefffe, INTC_INTMSK2); + + register_intc_controller(&intc_desc); +} + +void __init plat_irq_setup_pins(int mode) +{ + switch (mode) { + case IRQ_MODE_IRQ: + /* select IRQ mode for IRL3-0 + IRL7-4 */ + __raw_writel(__raw_readl(INTC_ICR0) | 0x00c00000, INTC_ICR0); + register_intc_controller(&intc_irq_desc); + break; + case IRQ_MODE_IRL7654: + /* enable IRL7-4 but don't provide any masking */ + __raw_writel(0x40000000, INTC_INTMSKCLR1); + __raw_writel(0x0000fffe, INTC_INTMSKCLR2); + break; + case IRQ_MODE_IRL3210: + /* enable IRL0-3 but don't provide any masking */ + __raw_writel(0x80000000, INTC_INTMSKCLR1); + __raw_writel(0xfffe0000, INTC_INTMSKCLR2); + break; + case IRQ_MODE_IRL7654_MASK: + /* enable IRL7-4 and mask using cpu intc controller */ + __raw_writel(0x40000000, INTC_INTMSKCLR1); + register_intc_controller(&intc_irl7654_desc); + break; + case IRQ_MODE_IRL3210_MASK: + /* enable IRL0-3 and mask using cpu intc controller */ + __raw_writel(0x80000000, INTC_INTMSKCLR1); + register_intc_controller(&intc_irl3210_desc); + break; + default: + BUG(); + } +} diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7770.c b/arch/sh/kernel/cpu/sh4a/setup-sh7770.c new file mode 100644 index 000000000..82e3bdf2e --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7770.c @@ -0,0 +1,573 @@ +/* + * SH7770 Setup + * + * Copyright (C) 2006 - 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/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <linux/serial_sci.h> +#include <linux/sh_timer.h> +#include <linux/sh_intc.h> +#include <linux/io.h> + +static struct plat_sci_port scif0_platform_data = { + .scscr = SCSCR_REIE | SCSCR_TOIE, + .type = PORT_SCIF, +}; + +static struct resource scif0_resources[] = { + DEFINE_RES_MEM(0xff923000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x9a0)), +}; + +static struct platform_device scif0_device = { + .name = "sh-sci", + .id = 0, + .resource = scif0_resources, + .num_resources = ARRAY_SIZE(scif0_resources), + .dev = { + .platform_data = &scif0_platform_data, + }, +}; + +static struct plat_sci_port scif1_platform_data = { + .scscr = SCSCR_REIE | SCSCR_TOIE, + .type = PORT_SCIF, +}; + +static struct resource scif1_resources[] = { + DEFINE_RES_MEM(0xff924000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x9c0)), +}; + +static struct platform_device scif1_device = { + .name = "sh-sci", + .id = 1, + .resource = scif1_resources, + .num_resources = ARRAY_SIZE(scif1_resources), + .dev = { + .platform_data = &scif1_platform_data, + }, +}; + +static struct plat_sci_port scif2_platform_data = { + .scscr = SCSCR_REIE | SCSCR_TOIE, + .type = PORT_SCIF, +}; + +static struct resource scif2_resources[] = { + DEFINE_RES_MEM(0xff925000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x9e0)), +}; + +static struct platform_device scif2_device = { + .name = "sh-sci", + .id = 2, + .resource = scif2_resources, + .num_resources = ARRAY_SIZE(scif2_resources), + .dev = { + .platform_data = &scif2_platform_data, + }, +}; + +static struct plat_sci_port scif3_platform_data = { + .scscr = SCSCR_REIE | SCSCR_TOIE, + .type = PORT_SCIF, +}; + +static struct resource scif3_resources[] = { + DEFINE_RES_MEM(0xff926000, 0x100), + DEFINE_RES_IRQ(evt2irq(0xa00)), +}; + +static struct platform_device scif3_device = { + .name = "sh-sci", + .id = 3, + .resource = scif3_resources, + .num_resources = ARRAY_SIZE(scif3_resources), + .dev = { + .platform_data = &scif3_platform_data, + }, +}; + +static struct plat_sci_port scif4_platform_data = { + .scscr = SCSCR_REIE | SCSCR_TOIE, + .type = PORT_SCIF, +}; + +static struct resource scif4_resources[] = { + DEFINE_RES_MEM(0xff927000, 0x100), + DEFINE_RES_IRQ(evt2irq(0xa20)), +}; + +static struct platform_device scif4_device = { + .name = "sh-sci", + .id = 4, + .resource = scif4_resources, + .num_resources = ARRAY_SIZE(scif4_resources), + .dev = { + .platform_data = &scif4_platform_data, + }, +}; + +static struct plat_sci_port scif5_platform_data = { + .scscr = SCSCR_REIE | SCSCR_TOIE, + .type = PORT_SCIF, +}; + +static struct resource scif5_resources[] = { + DEFINE_RES_MEM(0xff928000, 0x100), + DEFINE_RES_IRQ(evt2irq(0xa40)), +}; + +static struct platform_device scif5_device = { + .name = "sh-sci", + .id = 5, + .resource = scif5_resources, + .num_resources = ARRAY_SIZE(scif5_resources), + .dev = { + .platform_data = &scif5_platform_data, + }, +}; + +static struct plat_sci_port scif6_platform_data = { + .scscr = SCSCR_REIE | SCSCR_TOIE, + .type = PORT_SCIF, +}; + +static struct resource scif6_resources[] = { + DEFINE_RES_MEM(0xff929000, 0x100), + DEFINE_RES_IRQ(evt2irq(0xa60)), +}; + +static struct platform_device scif6_device = { + .name = "sh-sci", + .id = 6, + .resource = scif6_resources, + .num_resources = ARRAY_SIZE(scif6_resources), + .dev = { + .platform_data = &scif6_platform_data, + }, +}; + +static struct plat_sci_port scif7_platform_data = { + .scscr = SCSCR_REIE | SCSCR_TOIE, + .type = PORT_SCIF, +}; + +static struct resource scif7_resources[] = { + DEFINE_RES_MEM(0xff92a000, 0x100), + DEFINE_RES_IRQ(evt2irq(0xa80)), +}; + +static struct platform_device scif7_device = { + .name = "sh-sci", + .id = 7, + .resource = scif7_resources, + .num_resources = ARRAY_SIZE(scif7_resources), + .dev = { + .platform_data = &scif7_platform_data, + }, +}; + +static struct plat_sci_port scif8_platform_data = { + .scscr = SCSCR_REIE | SCSCR_TOIE, + .type = PORT_SCIF, +}; + +static struct resource scif8_resources[] = { + DEFINE_RES_MEM(0xff92b000, 0x100), + DEFINE_RES_IRQ(evt2irq(0xaa0)), +}; + +static struct platform_device scif8_device = { + .name = "sh-sci", + .id = 8, + .resource = scif8_resources, + .num_resources = ARRAY_SIZE(scif8_resources), + .dev = { + .platform_data = &scif8_platform_data, + }, +}; + +static struct plat_sci_port scif9_platform_data = { + .scscr = SCSCR_REIE | SCSCR_TOIE, + .type = PORT_SCIF, +}; + +static struct resource scif9_resources[] = { + DEFINE_RES_MEM(0xff92c000, 0x100), + DEFINE_RES_IRQ(evt2irq(0xac0)), +}; + +static struct platform_device scif9_device = { + .name = "sh-sci", + .id = 9, + .resource = scif9_resources, + .num_resources = ARRAY_SIZE(scif9_resources), + .dev = { + .platform_data = &scif9_platform_data, + }, +}; + +static struct sh_timer_config tmu0_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu0_resources[] = { + DEFINE_RES_MEM(0xffd80000, 0x30), + DEFINE_RES_IRQ(evt2irq(0x400)), + DEFINE_RES_IRQ(evt2irq(0x420)), + DEFINE_RES_IRQ(evt2irq(0x440)), +}; + +static struct platform_device tmu0_device = { + .name = "sh-tmu", + .id = 0, + .dev = { + .platform_data = &tmu0_platform_data, + }, + .resource = tmu0_resources, + .num_resources = ARRAY_SIZE(tmu0_resources), +}; + +static struct sh_timer_config tmu1_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu1_resources[] = { + DEFINE_RES_MEM(0xffd81000, 0x30), + DEFINE_RES_IRQ(evt2irq(0x460)), + DEFINE_RES_IRQ(evt2irq(0x480)), + DEFINE_RES_IRQ(evt2irq(0x4a0)), +}; + +static struct platform_device tmu1_device = { + .name = "sh-tmu", + .id = 1, + .dev = { + .platform_data = &tmu1_platform_data, + }, + .resource = tmu1_resources, + .num_resources = ARRAY_SIZE(tmu1_resources), +}; + +static struct sh_timer_config tmu2_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu2_resources[] = { + DEFINE_RES_MEM(0xffd82000, 0x2c), + DEFINE_RES_IRQ(evt2irq(0x4c0)), + DEFINE_RES_IRQ(evt2irq(0x4e0)), + DEFINE_RES_IRQ(evt2irq(0x500)), +}; + +static struct platform_device tmu2_device = { + .name = "sh-tmu", + .id = 2, + .dev = { + .platform_data = &tmu2_platform_data, + }, + .resource = tmu2_resources, + .num_resources = ARRAY_SIZE(tmu2_resources), +}; + +static struct platform_device *sh7770_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + &scif3_device, + &scif4_device, + &scif5_device, + &scif6_device, + &scif7_device, + &scif8_device, + &scif9_device, + &tmu0_device, + &tmu1_device, + &tmu2_device, +}; + +static int __init sh7770_devices_setup(void) +{ + return platform_add_devices(sh7770_devices, + ARRAY_SIZE(sh7770_devices)); +} +arch_initcall(sh7770_devices_setup); + +static struct platform_device *sh7770_early_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + &scif3_device, + &scif4_device, + &scif5_device, + &scif6_device, + &scif7_device, + &scif8_device, + &scif9_device, + &tmu0_device, + &tmu1_device, + &tmu2_device, +}; + +void __init plat_early_device_setup(void) +{ + early_platform_add_devices(sh7770_early_devices, + ARRAY_SIZE(sh7770_early_devices)); +} + +enum { + UNUSED = 0, + + /* interrupt sources */ + IRL_LLLL, IRL_LLLH, IRL_LLHL, IRL_LLHH, + IRL_LHLL, IRL_LHLH, IRL_LHHL, IRL_LHHH, + IRL_HLLL, IRL_HLLH, IRL_HLHL, IRL_HLHH, + IRL_HHLL, IRL_HHLH, IRL_HHHL, + + IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, + + GPIO, + TMU0, TMU1, TMU2, TMU2_TICPI, + TMU3, TMU4, TMU5, TMU5_TICPI, + TMU6, TMU7, TMU8, + HAC, IPI, SPDIF, HUDI, I2C, + DMAC0_DMINT0, DMAC0_DMINT1, DMAC0_DMINT2, + I2S0, I2S1, I2S2, I2S3, + SRC_RX, SRC_TX, SRC_SPDIF, + DU, VIDEO_IN, REMOTE, YUV, USB, ATAPI, CAN, GPS, GFX2D, + GFX3D_MBX, GFX3D_DMAC, + EXBUS_ATA, + SPI0, SPI1, + SCIF089, SCIF1234, SCIF567, + ADC, + BBDMAC_0_3, BBDMAC_4_7, BBDMAC_8_10, BBDMAC_11_14, + BBDMAC_15_18, BBDMAC_19_22, BBDMAC_23_26, BBDMAC_27, + BBDMAC_28, BBDMAC_29, BBDMAC_30, BBDMAC_31, + + /* interrupt groups */ + TMU, DMAC, I2S, SRC, GFX3D, SPI, SCIF, BBDMAC, +}; + +static struct intc_vect vectors[] __initdata = { + INTC_VECT(GPIO, 0x3e0), + INTC_VECT(TMU0, 0x400), INTC_VECT(TMU1, 0x420), + INTC_VECT(TMU2, 0x440), INTC_VECT(TMU2_TICPI, 0x460), + INTC_VECT(TMU3, 0x480), INTC_VECT(TMU4, 0x4a0), + INTC_VECT(TMU5, 0x4c0), INTC_VECT(TMU5_TICPI, 0x4e0), + INTC_VECT(TMU6, 0x500), INTC_VECT(TMU7, 0x520), + INTC_VECT(TMU8, 0x540), + INTC_VECT(HAC, 0x580), INTC_VECT(IPI, 0x5c0), + INTC_VECT(SPDIF, 0x5e0), + INTC_VECT(HUDI, 0x600), INTC_VECT(I2C, 0x620), + INTC_VECT(DMAC0_DMINT0, 0x640), INTC_VECT(DMAC0_DMINT1, 0x660), + INTC_VECT(DMAC0_DMINT2, 0x680), + INTC_VECT(I2S0, 0x6a0), INTC_VECT(I2S1, 0x6c0), + INTC_VECT(I2S2, 0x6e0), INTC_VECT(I2S3, 0x700), + INTC_VECT(SRC_RX, 0x720), INTC_VECT(SRC_TX, 0x740), + INTC_VECT(SRC_SPDIF, 0x760), + INTC_VECT(DU, 0x780), INTC_VECT(VIDEO_IN, 0x7a0), + INTC_VECT(REMOTE, 0x7c0), INTC_VECT(YUV, 0x7e0), + INTC_VECT(USB, 0x840), INTC_VECT(ATAPI, 0x860), + INTC_VECT(CAN, 0x880), INTC_VECT(GPS, 0x8a0), + INTC_VECT(GFX2D, 0x8c0), + INTC_VECT(GFX3D_MBX, 0x900), INTC_VECT(GFX3D_DMAC, 0x920), + INTC_VECT(EXBUS_ATA, 0x940), + INTC_VECT(SPI0, 0x960), INTC_VECT(SPI1, 0x980), + INTC_VECT(SCIF089, 0x9a0), INTC_VECT(SCIF1234, 0x9c0), + INTC_VECT(SCIF1234, 0x9e0), INTC_VECT(SCIF1234, 0xa00), + INTC_VECT(SCIF1234, 0xa20), INTC_VECT(SCIF567, 0xa40), + INTC_VECT(SCIF567, 0xa60), INTC_VECT(SCIF567, 0xa80), + INTC_VECT(SCIF089, 0xaa0), INTC_VECT(SCIF089, 0xac0), + INTC_VECT(ADC, 0xb20), + INTC_VECT(BBDMAC_0_3, 0xba0), INTC_VECT(BBDMAC_0_3, 0xbc0), + INTC_VECT(BBDMAC_0_3, 0xbe0), INTC_VECT(BBDMAC_0_3, 0xc00), + INTC_VECT(BBDMAC_4_7, 0xc20), INTC_VECT(BBDMAC_4_7, 0xc40), + INTC_VECT(BBDMAC_4_7, 0xc60), INTC_VECT(BBDMAC_4_7, 0xc80), + INTC_VECT(BBDMAC_8_10, 0xca0), INTC_VECT(BBDMAC_8_10, 0xcc0), + INTC_VECT(BBDMAC_8_10, 0xce0), INTC_VECT(BBDMAC_11_14, 0xd00), + INTC_VECT(BBDMAC_11_14, 0xd20), INTC_VECT(BBDMAC_11_14, 0xd40), + INTC_VECT(BBDMAC_11_14, 0xd60), INTC_VECT(BBDMAC_15_18, 0xd80), + INTC_VECT(BBDMAC_15_18, 0xda0), INTC_VECT(BBDMAC_15_18, 0xdc0), + INTC_VECT(BBDMAC_15_18, 0xde0), INTC_VECT(BBDMAC_19_22, 0xe00), + INTC_VECT(BBDMAC_19_22, 0xe20), INTC_VECT(BBDMAC_19_22, 0xe40), + INTC_VECT(BBDMAC_19_22, 0xe60), INTC_VECT(BBDMAC_23_26, 0xe80), + INTC_VECT(BBDMAC_23_26, 0xea0), INTC_VECT(BBDMAC_23_26, 0xec0), + INTC_VECT(BBDMAC_23_26, 0xee0), INTC_VECT(BBDMAC_27, 0xf00), + INTC_VECT(BBDMAC_28, 0xf20), INTC_VECT(BBDMAC_29, 0xf40), + INTC_VECT(BBDMAC_30, 0xf60), INTC_VECT(BBDMAC_31, 0xf80), +}; + +static struct intc_group groups[] __initdata = { + INTC_GROUP(TMU, TMU0, TMU1, TMU2, TMU2_TICPI, TMU3, TMU4, TMU5, + TMU5_TICPI, TMU6, TMU7, TMU8), + INTC_GROUP(DMAC, DMAC0_DMINT0, DMAC0_DMINT1, DMAC0_DMINT2), + INTC_GROUP(I2S, I2S0, I2S1, I2S2, I2S3), + INTC_GROUP(SRC, SRC_RX, SRC_TX, SRC_SPDIF), + INTC_GROUP(GFX3D, GFX3D_MBX, GFX3D_DMAC), + INTC_GROUP(SPI, SPI0, SPI1), + INTC_GROUP(SCIF, SCIF089, SCIF1234, SCIF567), + INTC_GROUP(BBDMAC, + BBDMAC_0_3, BBDMAC_4_7, BBDMAC_8_10, BBDMAC_11_14, + BBDMAC_15_18, BBDMAC_19_22, BBDMAC_23_26, BBDMAC_27, + BBDMAC_28, BBDMAC_29, BBDMAC_30, BBDMAC_31), +}; + +static struct intc_mask_reg mask_registers[] __initdata = { + { 0xffe00040, 0xffe00044, 32, /* INT2MSKR / INT2MSKCR */ + { 0, BBDMAC, ADC, SCIF, SPI, EXBUS_ATA, GFX3D, GFX2D, + GPS, CAN, ATAPI, USB, YUV, REMOTE, VIDEO_IN, DU, SRC, I2S, + DMAC, I2C, HUDI, SPDIF, IPI, HAC, TMU, GPIO } }, +}; + +static struct intc_prio_reg prio_registers[] __initdata = { + { 0xffe00000, 0, 32, 8, /* INT2PRI0 */ { GPIO, TMU0, 0, HAC } }, + { 0xffe00004, 0, 32, 8, /* INT2PRI1 */ { IPI, SPDIF, HUDI, I2C } }, + { 0xffe00008, 0, 32, 8, /* INT2PRI2 */ { DMAC, I2S, SRC, DU } }, + { 0xffe0000c, 0, 32, 8, /* INT2PRI3 */ { VIDEO_IN, REMOTE, YUV, USB } }, + { 0xffe00010, 0, 32, 8, /* INT2PRI4 */ { ATAPI, CAN, GPS, GFX2D } }, + { 0xffe00014, 0, 32, 8, /* INT2PRI5 */ { 0, GFX3D, EXBUS_ATA, SPI } }, + { 0xffe00018, 0, 32, 8, /* INT2PRI6 */ { SCIF1234, SCIF567, SCIF089 } }, + { 0xffe0001c, 0, 32, 8, /* INT2PRI7 */ { ADC, 0, 0, BBDMAC_0_3 } }, + { 0xffe00020, 0, 32, 8, /* INT2PRI8 */ + { BBDMAC_4_7, BBDMAC_8_10, BBDMAC_11_14, BBDMAC_15_18 } }, + { 0xffe00024, 0, 32, 8, /* INT2PRI9 */ + { BBDMAC_19_22, BBDMAC_23_26, BBDMAC_27, BBDMAC_28 } }, + { 0xffe00028, 0, 32, 8, /* INT2PRI10 */ + { BBDMAC_29, BBDMAC_30, BBDMAC_31 } }, + { 0xffe0002c, 0, 32, 8, /* INT2PRI11 */ + { TMU1, TMU2, TMU2_TICPI, TMU3 } }, + { 0xffe00030, 0, 32, 8, /* INT2PRI12 */ + { TMU4, TMU5, TMU5_TICPI, TMU6 } }, + { 0xffe00034, 0, 32, 8, /* INT2PRI13 */ + { TMU7, TMU8 } }, +}; + +static DECLARE_INTC_DESC(intc_desc, "sh7770", vectors, groups, + mask_registers, prio_registers, NULL); + +/* Support for external interrupt pins in IRQ mode */ +static struct intc_vect irq_vectors[] __initdata = { + INTC_VECT(IRQ0, 0x240), INTC_VECT(IRQ1, 0x280), + INTC_VECT(IRQ2, 0x2c0), INTC_VECT(IRQ3, 0x300), + INTC_VECT(IRQ4, 0x340), INTC_VECT(IRQ5, 0x380), +}; + +static struct intc_mask_reg irq_mask_registers[] __initdata = { + { 0xffd00044, 0xffd00064, 32, /* INTMSK0 / INTMSKCLR0 */ + { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, } }, +}; + +static struct intc_prio_reg irq_prio_registers[] __initdata = { + { 0xffd00010, 0, 32, 4, /* INTPRI */ { IRQ0, IRQ1, IRQ2, IRQ3, + IRQ4, IRQ5, } }, +}; + +static struct intc_sense_reg irq_sense_registers[] __initdata = { + { 0xffd0001c, 32, 2, /* ICR1 */ { IRQ0, IRQ1, IRQ2, IRQ3, + IRQ4, IRQ5, } }, +}; + +static DECLARE_INTC_DESC(intc_irq_desc, "sh7770-irq", irq_vectors, + NULL, irq_mask_registers, irq_prio_registers, + irq_sense_registers); + +/* External interrupt pins in IRL mode */ +static struct intc_vect irl_vectors[] __initdata = { + INTC_VECT(IRL_LLLL, 0x200), INTC_VECT(IRL_LLLH, 0x220), + INTC_VECT(IRL_LLHL, 0x240), INTC_VECT(IRL_LLHH, 0x260), + INTC_VECT(IRL_LHLL, 0x280), INTC_VECT(IRL_LHLH, 0x2a0), + INTC_VECT(IRL_LHHL, 0x2c0), INTC_VECT(IRL_LHHH, 0x2e0), + INTC_VECT(IRL_HLLL, 0x300), INTC_VECT(IRL_HLLH, 0x320), + INTC_VECT(IRL_HLHL, 0x340), INTC_VECT(IRL_HLHH, 0x360), + INTC_VECT(IRL_HHLL, 0x380), INTC_VECT(IRL_HHLH, 0x3a0), + INTC_VECT(IRL_HHHL, 0x3c0), +}; + +static struct intc_mask_reg irl3210_mask_registers[] __initdata = { + { 0xffd40080, 0xffd40084, 32, /* INTMSK2 / INTMSKCLR2 */ + { IRL_LLLL, IRL_LLLH, IRL_LLHL, IRL_LLHH, + IRL_LHLL, IRL_LHLH, IRL_LHHL, IRL_LHHH, + IRL_HLLL, IRL_HLLH, IRL_HLHL, IRL_HLHH, + IRL_HHLL, IRL_HHLH, IRL_HHHL, } }, +}; + +static struct intc_mask_reg irl7654_mask_registers[] __initdata = { + { 0xffd40080, 0xffd40084, 32, /* INTMSK2 / INTMSKCLR2 */ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + IRL_LLLL, IRL_LLLH, IRL_LLHL, IRL_LLHH, + IRL_LHLL, IRL_LHLH, IRL_LHHL, IRL_LHHH, + IRL_HLLL, IRL_HLLH, IRL_HLHL, IRL_HLHH, + IRL_HHLL, IRL_HHLH, IRL_HHHL, } }, +}; + +static DECLARE_INTC_DESC(intc_irl7654_desc, "sh7780-irl7654", irl_vectors, + NULL, irl7654_mask_registers, NULL, NULL); + +static DECLARE_INTC_DESC(intc_irl3210_desc, "sh7780-irl3210", irl_vectors, + NULL, irl3210_mask_registers, NULL, NULL); + +#define INTC_ICR0 0xffd00000 +#define INTC_INTMSK0 0xffd00044 +#define INTC_INTMSK1 0xffd00048 +#define INTC_INTMSK2 0xffd40080 +#define INTC_INTMSKCLR1 0xffd00068 +#define INTC_INTMSKCLR2 0xffd40084 + +void __init plat_irq_setup(void) +{ + /* disable IRQ7-0 */ + __raw_writel(0xff000000, INTC_INTMSK0); + + /* disable IRL3-0 + IRL7-4 */ + __raw_writel(0xc0000000, INTC_INTMSK1); + __raw_writel(0xfffefffe, INTC_INTMSK2); + + /* select IRL mode for IRL3-0 + IRL7-4 */ + __raw_writel(__raw_readl(INTC_ICR0) & ~0x00c00000, INTC_ICR0); + + /* disable holding function, ie enable "SH-4 Mode" */ + __raw_writel(__raw_readl(INTC_ICR0) | 0x00200000, INTC_ICR0); + + register_intc_controller(&intc_desc); +} + +void __init plat_irq_setup_pins(int mode) +{ + switch (mode) { + case IRQ_MODE_IRQ: + /* select IRQ mode for IRL3-0 + IRL7-4 */ + __raw_writel(__raw_readl(INTC_ICR0) | 0x00c00000, INTC_ICR0); + register_intc_controller(&intc_irq_desc); + break; + case IRQ_MODE_IRL7654: + /* enable IRL7-4 but don't provide any masking */ + __raw_writel(0x40000000, INTC_INTMSKCLR1); + __raw_writel(0x0000fffe, INTC_INTMSKCLR2); + break; + case IRQ_MODE_IRL3210: + /* enable IRL0-3 but don't provide any masking */ + __raw_writel(0x80000000, INTC_INTMSKCLR1); + __raw_writel(0xfffe0000, INTC_INTMSKCLR2); + break; + case IRQ_MODE_IRL7654_MASK: + /* enable IRL7-4 and mask using cpu intc controller */ + __raw_writel(0x40000000, INTC_INTMSKCLR1); + register_intc_controller(&intc_irl7654_desc); + break; + case IRQ_MODE_IRL3210_MASK: + /* enable IRL0-3 and mask using cpu intc controller */ + __raw_writel(0x80000000, INTC_INTMSKCLR1); + register_intc_controller(&intc_irl3210_desc); + break; + default: + BUG(); + } +} diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7780.c b/arch/sh/kernel/cpu/sh4a/setup-sh7780.c new file mode 100644 index 000000000..d90ff67a4 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7780.c @@ -0,0 +1,507 @@ +/* + * SH7780 Setup + * + * Copyright (C) 2006 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/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <linux/io.h> +#include <linux/serial_sci.h> +#include <linux/sh_dma.h> +#include <linux/sh_timer.h> +#include <linux/sh_intc.h> +#include <cpu/dma-register.h> + +static struct plat_sci_port scif0_platform_data = { + .scscr = SCSCR_REIE | SCSCR_CKE1, + .type = PORT_SCIF, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif0_resources[] = { + DEFINE_RES_MEM(0xffe00000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x700)), +}; + +static struct platform_device scif0_device = { + .name = "sh-sci", + .id = 0, + .resource = scif0_resources, + .num_resources = ARRAY_SIZE(scif0_resources), + .dev = { + .platform_data = &scif0_platform_data, + }, +}; + +static struct plat_sci_port scif1_platform_data = { + .scscr = SCSCR_REIE | SCSCR_CKE1, + .type = PORT_SCIF, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif1_resources[] = { + DEFINE_RES_MEM(0xffe10000, 0x100), + DEFINE_RES_IRQ(evt2irq(0xb80)), +}; + +static struct platform_device scif1_device = { + .name = "sh-sci", + .id = 1, + .resource = scif1_resources, + .num_resources = ARRAY_SIZE(scif1_resources), + .dev = { + .platform_data = &scif1_platform_data, + }, +}; + +static struct sh_timer_config tmu0_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu0_resources[] = { + DEFINE_RES_MEM(0xffd80000, 0x30), + DEFINE_RES_IRQ(evt2irq(0x580)), + DEFINE_RES_IRQ(evt2irq(0x5a0)), + DEFINE_RES_IRQ(evt2irq(0x5c0)), +}; + +static struct platform_device tmu0_device = { + .name = "sh-tmu", + .id = 0, + .dev = { + .platform_data = &tmu0_platform_data, + }, + .resource = tmu0_resources, + .num_resources = ARRAY_SIZE(tmu0_resources), +}; + +static struct sh_timer_config tmu1_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu1_resources[] = { + DEFINE_RES_MEM(0xffdc0000, 0x2c), + DEFINE_RES_IRQ(evt2irq(0xe00)), + DEFINE_RES_IRQ(evt2irq(0xe20)), + DEFINE_RES_IRQ(evt2irq(0xe40)), +}; + +static struct platform_device tmu1_device = { + .name = "sh-tmu", + .id = 1, + .dev = { + .platform_data = &tmu1_platform_data, + }, + .resource = tmu1_resources, + .num_resources = ARRAY_SIZE(tmu1_resources), +}; + +static struct resource rtc_resources[] = { + [0] = { + .start = 0xffe80000, + .end = 0xffe80000 + 0x58 - 1, + .flags = IORESOURCE_IO, + }, + [1] = { + /* Shared Period/Carry/Alarm IRQ */ + .start = evt2irq(0x480), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device rtc_device = { + .name = "sh-rtc", + .id = -1, + .num_resources = ARRAY_SIZE(rtc_resources), + .resource = rtc_resources, +}; + +/* DMA */ +static const struct sh_dmae_channel sh7780_dmae0_channels[] = { + { + .offset = 0, + .dmars = 0, + .dmars_bit = 0, + }, { + .offset = 0x10, + .dmars = 0, + .dmars_bit = 8, + }, { + .offset = 0x20, + .dmars = 4, + .dmars_bit = 0, + }, { + .offset = 0x30, + .dmars = 4, + .dmars_bit = 8, + }, { + .offset = 0x50, + .dmars = 8, + .dmars_bit = 0, + }, { + .offset = 0x60, + .dmars = 8, + .dmars_bit = 8, + } +}; + +static const struct sh_dmae_channel sh7780_dmae1_channels[] = { + { + .offset = 0, + }, { + .offset = 0x10, + }, { + .offset = 0x20, + }, { + .offset = 0x30, + }, { + .offset = 0x50, + }, { + .offset = 0x60, + } +}; + +static const unsigned int ts_shift[] = TS_SHIFT; + +static struct sh_dmae_pdata dma0_platform_data = { + .channel = sh7780_dmae0_channels, + .channel_num = ARRAY_SIZE(sh7780_dmae0_channels), + .ts_low_shift = CHCR_TS_LOW_SHIFT, + .ts_low_mask = CHCR_TS_LOW_MASK, + .ts_high_shift = CHCR_TS_HIGH_SHIFT, + .ts_high_mask = CHCR_TS_HIGH_MASK, + .ts_shift = ts_shift, + .ts_shift_num = ARRAY_SIZE(ts_shift), + .dmaor_init = DMAOR_INIT, +}; + +static struct sh_dmae_pdata dma1_platform_data = { + .channel = sh7780_dmae1_channels, + .channel_num = ARRAY_SIZE(sh7780_dmae1_channels), + .ts_low_shift = CHCR_TS_LOW_SHIFT, + .ts_low_mask = CHCR_TS_LOW_MASK, + .ts_high_shift = CHCR_TS_HIGH_SHIFT, + .ts_high_mask = CHCR_TS_HIGH_MASK, + .ts_shift = ts_shift, + .ts_shift_num = ARRAY_SIZE(ts_shift), + .dmaor_init = DMAOR_INIT, +}; + +static struct resource sh7780_dmae0_resources[] = { + [0] = { + /* Channel registers and DMAOR */ + .start = 0xfc808020, + .end = 0xfc80808f, + .flags = IORESOURCE_MEM, + }, + [1] = { + /* DMARSx */ + .start = 0xfc809000, + .end = 0xfc80900b, + .flags = IORESOURCE_MEM, + }, + { + /* + * Real DMA error vector is 0x6c0, and channel + * vectors are 0x640-0x6a0, 0x780-0x7a0 + */ + .name = "error_irq", + .start = evt2irq(0x640), + .end = evt2irq(0x640), + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE, + }, +}; + +static struct resource sh7780_dmae1_resources[] = { + [0] = { + /* Channel registers and DMAOR */ + .start = 0xfc818020, + .end = 0xfc81808f, + .flags = IORESOURCE_MEM, + }, + /* DMAC1 has no DMARS */ + { + /* + * Real DMA error vector is 0x6c0, and channel + * vectors are 0x7c0-0x7e0, 0xd80-0xde0 + */ + .name = "error_irq", + .start = evt2irq(0x7c0), + .end = evt2irq(0x7c0), + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE, + }, +}; + +static struct platform_device dma0_device = { + .name = "sh-dma-engine", + .id = 0, + .resource = sh7780_dmae0_resources, + .num_resources = ARRAY_SIZE(sh7780_dmae0_resources), + .dev = { + .platform_data = &dma0_platform_data, + }, +}; + +static struct platform_device dma1_device = { + .name = "sh-dma-engine", + .id = 1, + .resource = sh7780_dmae1_resources, + .num_resources = ARRAY_SIZE(sh7780_dmae1_resources), + .dev = { + .platform_data = &dma1_platform_data, + }, +}; + +static struct platform_device *sh7780_devices[] __initdata = { + &scif0_device, + &scif1_device, + &tmu0_device, + &tmu1_device, + &rtc_device, + &dma0_device, + &dma1_device, +}; + +static int __init sh7780_devices_setup(void) +{ + return platform_add_devices(sh7780_devices, + ARRAY_SIZE(sh7780_devices)); +} +arch_initcall(sh7780_devices_setup); + +static struct platform_device *sh7780_early_devices[] __initdata = { + &scif0_device, + &scif1_device, + &tmu0_device, + &tmu1_device, +}; + +void __init plat_early_device_setup(void) +{ + if (mach_is_sh2007()) { + scif0_platform_data.scscr &= ~SCSCR_CKE1; + scif1_platform_data.scscr &= ~SCSCR_CKE1; + } + + early_platform_add_devices(sh7780_early_devices, + ARRAY_SIZE(sh7780_early_devices)); +} + +enum { + UNUSED = 0, + + /* interrupt sources */ + + IRL_LLLL, IRL_LLLH, IRL_LLHL, IRL_LLHH, + IRL_LHLL, IRL_LHLH, IRL_LHHL, IRL_LHHH, + IRL_HLLL, IRL_HLLH, IRL_HLHL, IRL_HLHH, + IRL_HHLL, IRL_HHLH, IRL_HHHL, + + IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7, + RTC, WDT, TMU0, TMU1, TMU2, TMU2_TICPI, + HUDI, DMAC0, SCIF0, DMAC1, CMT, HAC, + PCISERR, PCIINTA, PCIINTB, PCIINTC, PCIINTD, PCIC5, + SCIF1, SIOF, HSPI, MMCIF, TMU3, TMU4, TMU5, SSI, FLCTL, GPIO, + + /* interrupt groups */ + + TMU012, TMU345, +}; + +static struct intc_vect vectors[] __initdata = { + INTC_VECT(RTC, 0x480), INTC_VECT(RTC, 0x4a0), + INTC_VECT(RTC, 0x4c0), + INTC_VECT(WDT, 0x560), + INTC_VECT(TMU0, 0x580), INTC_VECT(TMU1, 0x5a0), + INTC_VECT(TMU2, 0x5c0), INTC_VECT(TMU2_TICPI, 0x5e0), + INTC_VECT(HUDI, 0x600), + INTC_VECT(DMAC0, 0x640), INTC_VECT(DMAC0, 0x660), + INTC_VECT(DMAC0, 0x680), INTC_VECT(DMAC0, 0x6a0), + INTC_VECT(DMAC0, 0x6c0), + INTC_VECT(SCIF0, 0x700), INTC_VECT(SCIF0, 0x720), + INTC_VECT(SCIF0, 0x740), INTC_VECT(SCIF0, 0x760), + INTC_VECT(DMAC0, 0x780), INTC_VECT(DMAC0, 0x7a0), + INTC_VECT(DMAC1, 0x7c0), INTC_VECT(DMAC1, 0x7e0), + INTC_VECT(CMT, 0x900), INTC_VECT(HAC, 0x980), + INTC_VECT(PCISERR, 0xa00), INTC_VECT(PCIINTA, 0xa20), + INTC_VECT(PCIINTB, 0xa40), INTC_VECT(PCIINTC, 0xa60), + INTC_VECT(PCIINTD, 0xa80), INTC_VECT(PCIC5, 0xaa0), + INTC_VECT(PCIC5, 0xac0), INTC_VECT(PCIC5, 0xae0), + INTC_VECT(PCIC5, 0xb00), INTC_VECT(PCIC5, 0xb20), + INTC_VECT(SCIF1, 0xb80), INTC_VECT(SCIF1, 0xba0), + INTC_VECT(SCIF1, 0xbc0), INTC_VECT(SCIF1, 0xbe0), + INTC_VECT(SIOF, 0xc00), INTC_VECT(HSPI, 0xc80), + INTC_VECT(MMCIF, 0xd00), INTC_VECT(MMCIF, 0xd20), + INTC_VECT(MMCIF, 0xd40), INTC_VECT(MMCIF, 0xd60), + INTC_VECT(DMAC1, 0xd80), INTC_VECT(DMAC1, 0xda0), + INTC_VECT(DMAC1, 0xdc0), INTC_VECT(DMAC1, 0xde0), + INTC_VECT(TMU3, 0xe00), INTC_VECT(TMU4, 0xe20), + INTC_VECT(TMU5, 0xe40), + INTC_VECT(SSI, 0xe80), + INTC_VECT(FLCTL, 0xf00), INTC_VECT(FLCTL, 0xf20), + INTC_VECT(FLCTL, 0xf40), INTC_VECT(FLCTL, 0xf60), + INTC_VECT(GPIO, 0xf80), INTC_VECT(GPIO, 0xfa0), + INTC_VECT(GPIO, 0xfc0), INTC_VECT(GPIO, 0xfe0), +}; + +static struct intc_group groups[] __initdata = { + INTC_GROUP(TMU012, TMU0, TMU1, TMU2, TMU2_TICPI), + INTC_GROUP(TMU345, TMU3, TMU4, TMU5), +}; + +static struct intc_mask_reg mask_registers[] __initdata = { + { 0xffd40038, 0xffd4003c, 32, /* INT2MSKR / INT2MSKCR */ + { 0, 0, 0, 0, 0, 0, GPIO, FLCTL, + SSI, MMCIF, HSPI, SIOF, PCIC5, PCIINTD, PCIINTC, PCIINTB, + PCIINTA, PCISERR, HAC, CMT, 0, 0, DMAC1, DMAC0, + HUDI, 0, WDT, SCIF1, SCIF0, RTC, TMU345, TMU012 } }, +}; + +static struct intc_prio_reg prio_registers[] __initdata = { + { 0xffd40000, 0, 32, 8, /* INT2PRI0 */ { TMU0, TMU1, + TMU2, TMU2_TICPI } }, + { 0xffd40004, 0, 32, 8, /* INT2PRI1 */ { TMU3, TMU4, TMU5, RTC } }, + { 0xffd40008, 0, 32, 8, /* INT2PRI2 */ { SCIF0, SCIF1, WDT } }, + { 0xffd4000c, 0, 32, 8, /* INT2PRI3 */ { HUDI, DMAC0, DMAC1 } }, + { 0xffd40010, 0, 32, 8, /* INT2PRI4 */ { CMT, HAC, + PCISERR, PCIINTA, } }, + { 0xffd40014, 0, 32, 8, /* INT2PRI5 */ { PCIINTB, PCIINTC, + PCIINTD, PCIC5 } }, + { 0xffd40018, 0, 32, 8, /* INT2PRI6 */ { SIOF, HSPI, MMCIF, SSI } }, + { 0xffd4001c, 0, 32, 8, /* INT2PRI7 */ { FLCTL, GPIO } }, +}; + +static DECLARE_INTC_DESC(intc_desc, "sh7780", vectors, groups, + mask_registers, prio_registers, NULL); + +/* Support for external interrupt pins in IRQ mode */ + +static struct intc_vect irq_vectors[] __initdata = { + INTC_VECT(IRQ0, 0x240), INTC_VECT(IRQ1, 0x280), + INTC_VECT(IRQ2, 0x2c0), INTC_VECT(IRQ3, 0x300), + INTC_VECT(IRQ4, 0x340), INTC_VECT(IRQ5, 0x380), + INTC_VECT(IRQ6, 0x3c0), INTC_VECT(IRQ7, 0x200), +}; + +static struct intc_mask_reg irq_mask_registers[] __initdata = { + { 0xffd00044, 0xffd00064, 32, /* INTMSK0 / INTMSKCLR0 */ + { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } }, +}; + +static struct intc_prio_reg irq_prio_registers[] __initdata = { + { 0xffd00010, 0, 32, 4, /* INTPRI */ { IRQ0, IRQ1, IRQ2, IRQ3, + IRQ4, IRQ5, IRQ6, IRQ7 } }, +}; + +static struct intc_sense_reg irq_sense_registers[] __initdata = { + { 0xffd0001c, 32, 2, /* ICR1 */ { IRQ0, IRQ1, IRQ2, IRQ3, + IRQ4, IRQ5, IRQ6, IRQ7 } }, +}; + +static struct intc_mask_reg irq_ack_registers[] __initdata = { + { 0xffd00024, 0, 32, /* INTREQ */ + { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } }, +}; + +static DECLARE_INTC_DESC_ACK(intc_irq_desc, "sh7780-irq", irq_vectors, + NULL, irq_mask_registers, irq_prio_registers, + irq_sense_registers, irq_ack_registers); + +/* External interrupt pins in IRL mode */ + +static struct intc_vect irl_vectors[] __initdata = { + INTC_VECT(IRL_LLLL, 0x200), INTC_VECT(IRL_LLLH, 0x220), + INTC_VECT(IRL_LLHL, 0x240), INTC_VECT(IRL_LLHH, 0x260), + INTC_VECT(IRL_LHLL, 0x280), INTC_VECT(IRL_LHLH, 0x2a0), + INTC_VECT(IRL_LHHL, 0x2c0), INTC_VECT(IRL_LHHH, 0x2e0), + INTC_VECT(IRL_HLLL, 0x300), INTC_VECT(IRL_HLLH, 0x320), + INTC_VECT(IRL_HLHL, 0x340), INTC_VECT(IRL_HLHH, 0x360), + INTC_VECT(IRL_HHLL, 0x380), INTC_VECT(IRL_HHLH, 0x3a0), + INTC_VECT(IRL_HHHL, 0x3c0), +}; + +static struct intc_mask_reg irl3210_mask_registers[] __initdata = { + { 0xffd40080, 0xffd40084, 32, /* INTMSK2 / INTMSKCLR2 */ + { IRL_LLLL, IRL_LLLH, IRL_LLHL, IRL_LLHH, + IRL_LHLL, IRL_LHLH, IRL_LHHL, IRL_LHHH, + IRL_HLLL, IRL_HLLH, IRL_HLHL, IRL_HLHH, + IRL_HHLL, IRL_HHLH, IRL_HHHL, } }, +}; + +static struct intc_mask_reg irl7654_mask_registers[] __initdata = { + { 0xffd40080, 0xffd40084, 32, /* INTMSK2 / INTMSKCLR2 */ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + IRL_LLLL, IRL_LLLH, IRL_LLHL, IRL_LLHH, + IRL_LHLL, IRL_LHLH, IRL_LHHL, IRL_LHHH, + IRL_HLLL, IRL_HLLH, IRL_HLHL, IRL_HLHH, + IRL_HHLL, IRL_HHLH, IRL_HHHL, } }, +}; + +static DECLARE_INTC_DESC(intc_irl7654_desc, "sh7780-irl7654", irl_vectors, + NULL, irl7654_mask_registers, NULL, NULL); + +static DECLARE_INTC_DESC(intc_irl3210_desc, "sh7780-irl3210", irl_vectors, + NULL, irl3210_mask_registers, NULL, NULL); + +#define INTC_ICR0 0xffd00000 +#define INTC_INTMSK0 0xffd00044 +#define INTC_INTMSK1 0xffd00048 +#define INTC_INTMSK2 0xffd40080 +#define INTC_INTMSKCLR1 0xffd00068 +#define INTC_INTMSKCLR2 0xffd40084 + +void __init plat_irq_setup(void) +{ + /* disable IRQ7-0 */ + __raw_writel(0xff000000, INTC_INTMSK0); + + /* disable IRL3-0 + IRL7-4 */ + __raw_writel(0xc0000000, INTC_INTMSK1); + __raw_writel(0xfffefffe, INTC_INTMSK2); + + /* select IRL mode for IRL3-0 + IRL7-4 */ + __raw_writel(__raw_readl(INTC_ICR0) & ~0x00c00000, INTC_ICR0); + + /* disable holding function, ie enable "SH-4 Mode" */ + __raw_writel(__raw_readl(INTC_ICR0) | 0x00200000, INTC_ICR0); + + register_intc_controller(&intc_desc); +} + +void __init plat_irq_setup_pins(int mode) +{ + switch (mode) { + case IRQ_MODE_IRQ: + /* select IRQ mode for IRL3-0 + IRL7-4 */ + __raw_writel(__raw_readl(INTC_ICR0) | 0x00c00000, INTC_ICR0); + register_intc_controller(&intc_irq_desc); + break; + case IRQ_MODE_IRL7654: + /* enable IRL7-4 but don't provide any masking */ + __raw_writel(0x40000000, INTC_INTMSKCLR1); + __raw_writel(0x0000fffe, INTC_INTMSKCLR2); + break; + case IRQ_MODE_IRL3210: + /* enable IRL0-3 but don't provide any masking */ + __raw_writel(0x80000000, INTC_INTMSKCLR1); + __raw_writel(0xfffe0000, INTC_INTMSKCLR2); + break; + case IRQ_MODE_IRL7654_MASK: + /* enable IRL7-4 and mask using cpu intc controller */ + __raw_writel(0x40000000, INTC_INTMSKCLR1); + register_intc_controller(&intc_irl7654_desc); + break; + case IRQ_MODE_IRL3210_MASK: + /* enable IRL0-3 and mask using cpu intc controller */ + __raw_writel(0x80000000, INTC_INTMSKCLR1); + register_intc_controller(&intc_irl3210_desc); + break; + default: + BUG(); + } +} diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7785.c b/arch/sh/kernel/cpu/sh4a/setup-sh7785.c new file mode 100644 index 000000000..b0d6f82f2 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7785.c @@ -0,0 +1,610 @@ +/* + * SH7785 Setup + * + * Copyright (C) 2007 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/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <linux/serial_sci.h> +#include <linux/io.h> +#include <linux/mm.h> +#include <linux/sh_dma.h> +#include <linux/sh_timer.h> +#include <linux/sh_intc.h> +#include <asm/mmzone.h> +#include <cpu/dma-register.h> + +static struct plat_sci_port scif0_platform_data = { + .scscr = SCSCR_REIE | SCSCR_CKE1, + .type = PORT_SCIF, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif0_resources[] = { + DEFINE_RES_MEM(0xffea0000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x700)), +}; + +static struct platform_device scif0_device = { + .name = "sh-sci", + .id = 0, + .resource = scif0_resources, + .num_resources = ARRAY_SIZE(scif0_resources), + .dev = { + .platform_data = &scif0_platform_data, + }, +}; + +static struct plat_sci_port scif1_platform_data = { + .scscr = SCSCR_REIE | SCSCR_CKE1, + .type = PORT_SCIF, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif1_resources[] = { + DEFINE_RES_MEM(0xffeb0000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x780)), +}; + +static struct platform_device scif1_device = { + .name = "sh-sci", + .id = 1, + .resource = scif1_resources, + .num_resources = ARRAY_SIZE(scif1_resources), + .dev = { + .platform_data = &scif1_platform_data, + }, +}; + +static struct plat_sci_port scif2_platform_data = { + .scscr = SCSCR_REIE | SCSCR_CKE1, + .type = PORT_SCIF, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif2_resources[] = { + DEFINE_RES_MEM(0xffec0000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x980)), +}; + +static struct platform_device scif2_device = { + .name = "sh-sci", + .id = 2, + .resource = scif2_resources, + .num_resources = ARRAY_SIZE(scif2_resources), + .dev = { + .platform_data = &scif2_platform_data, + }, +}; + +static struct plat_sci_port scif3_platform_data = { + .scscr = SCSCR_REIE | SCSCR_CKE1, + .type = PORT_SCIF, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif3_resources[] = { + DEFINE_RES_MEM(0xffed0000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x9a0)), +}; + +static struct platform_device scif3_device = { + .name = "sh-sci", + .id = 3, + .resource = scif3_resources, + .num_resources = ARRAY_SIZE(scif3_resources), + .dev = { + .platform_data = &scif3_platform_data, + }, +}; + +static struct plat_sci_port scif4_platform_data = { + .scscr = SCSCR_REIE | SCSCR_CKE1, + .type = PORT_SCIF, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif4_resources[] = { + DEFINE_RES_MEM(0xffee0000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x9c0)), +}; + +static struct platform_device scif4_device = { + .name = "sh-sci", + .id = 4, + .resource = scif4_resources, + .num_resources = ARRAY_SIZE(scif4_resources), + .dev = { + .platform_data = &scif4_platform_data, + }, +}; + +static struct plat_sci_port scif5_platform_data = { + .scscr = SCSCR_REIE | SCSCR_CKE1, + .type = PORT_SCIF, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif5_resources[] = { + DEFINE_RES_MEM(0xffef0000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x9e0)), +}; + +static struct platform_device scif5_device = { + .name = "sh-sci", + .id = 5, + .resource = scif5_resources, + .num_resources = ARRAY_SIZE(scif5_resources), + .dev = { + .platform_data = &scif5_platform_data, + }, +}; + +static struct sh_timer_config tmu0_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu0_resources[] = { + DEFINE_RES_MEM(0xffd80000, 0x30), + DEFINE_RES_IRQ(evt2irq(0x580)), + DEFINE_RES_IRQ(evt2irq(0x5a0)), + DEFINE_RES_IRQ(evt2irq(0x5c0)), +}; + +static struct platform_device tmu0_device = { + .name = "sh-tmu", + .id = 0, + .dev = { + .platform_data = &tmu0_platform_data, + }, + .resource = tmu0_resources, + .num_resources = ARRAY_SIZE(tmu0_resources), +}; + +static struct sh_timer_config tmu1_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu1_resources[] = { + DEFINE_RES_MEM(0xffdc0000, 0x2c), + DEFINE_RES_IRQ(evt2irq(0xe00)), + DEFINE_RES_IRQ(evt2irq(0xe20)), + DEFINE_RES_IRQ(evt2irq(0xe40)), +}; + +static struct platform_device tmu1_device = { + .name = "sh-tmu", + .id = 1, + .dev = { + .platform_data = &tmu1_platform_data, + }, + .resource = tmu1_resources, + .num_resources = ARRAY_SIZE(tmu1_resources), +}; + +/* DMA */ +static const struct sh_dmae_channel sh7785_dmae0_channels[] = { + { + .offset = 0, + .dmars = 0, + .dmars_bit = 0, + }, { + .offset = 0x10, + .dmars = 0, + .dmars_bit = 8, + }, { + .offset = 0x20, + .dmars = 4, + .dmars_bit = 0, + }, { + .offset = 0x30, + .dmars = 4, + .dmars_bit = 8, + }, { + .offset = 0x50, + .dmars = 8, + .dmars_bit = 0, + }, { + .offset = 0x60, + .dmars = 8, + .dmars_bit = 8, + } +}; + +static const struct sh_dmae_channel sh7785_dmae1_channels[] = { + { + .offset = 0, + }, { + .offset = 0x10, + }, { + .offset = 0x20, + }, { + .offset = 0x30, + }, { + .offset = 0x50, + }, { + .offset = 0x60, + } +}; + +static const unsigned int ts_shift[] = TS_SHIFT; + +static struct sh_dmae_pdata dma0_platform_data = { + .channel = sh7785_dmae0_channels, + .channel_num = ARRAY_SIZE(sh7785_dmae0_channels), + .ts_low_shift = CHCR_TS_LOW_SHIFT, + .ts_low_mask = CHCR_TS_LOW_MASK, + .ts_high_shift = CHCR_TS_HIGH_SHIFT, + .ts_high_mask = CHCR_TS_HIGH_MASK, + .ts_shift = ts_shift, + .ts_shift_num = ARRAY_SIZE(ts_shift), + .dmaor_init = DMAOR_INIT, +}; + +static struct sh_dmae_pdata dma1_platform_data = { + .channel = sh7785_dmae1_channels, + .channel_num = ARRAY_SIZE(sh7785_dmae1_channels), + .ts_low_shift = CHCR_TS_LOW_SHIFT, + .ts_low_mask = CHCR_TS_LOW_MASK, + .ts_high_shift = CHCR_TS_HIGH_SHIFT, + .ts_high_mask = CHCR_TS_HIGH_MASK, + .ts_shift = ts_shift, + .ts_shift_num = ARRAY_SIZE(ts_shift), + .dmaor_init = DMAOR_INIT, +}; + +static struct resource sh7785_dmae0_resources[] = { + [0] = { + /* Channel registers and DMAOR */ + .start = 0xfc808020, + .end = 0xfc80808f, + .flags = IORESOURCE_MEM, + }, + [1] = { + /* DMARSx */ + .start = 0xfc809000, + .end = 0xfc80900b, + .flags = IORESOURCE_MEM, + }, + { + /* + * Real DMA error vector is 0x6e0, and channel + * vectors are 0x620-0x6c0 + */ + .name = "error_irq", + .start = evt2irq(0x620), + .end = evt2irq(0x620), + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE, + }, +}; + +static struct resource sh7785_dmae1_resources[] = { + [0] = { + /* Channel registers and DMAOR */ + .start = 0xfcc08020, + .end = 0xfcc0808f, + .flags = IORESOURCE_MEM, + }, + /* DMAC1 has no DMARS */ + { + /* + * Real DMA error vector is 0x940, and channel + * vectors are 0x880-0x920 + */ + .name = "error_irq", + .start = evt2irq(0x880), + .end = evt2irq(0x880), + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE, + }, +}; + +static struct platform_device dma0_device = { + .name = "sh-dma-engine", + .id = 0, + .resource = sh7785_dmae0_resources, + .num_resources = ARRAY_SIZE(sh7785_dmae0_resources), + .dev = { + .platform_data = &dma0_platform_data, + }, +}; + +static struct platform_device dma1_device = { + .name = "sh-dma-engine", + .id = 1, + .resource = sh7785_dmae1_resources, + .num_resources = ARRAY_SIZE(sh7785_dmae1_resources), + .dev = { + .platform_data = &dma1_platform_data, + }, +}; + +static struct platform_device *sh7785_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + &scif3_device, + &scif4_device, + &scif5_device, + &tmu0_device, + &tmu1_device, + &dma0_device, + &dma1_device, +}; + +static int __init sh7785_devices_setup(void) +{ + return platform_add_devices(sh7785_devices, + ARRAY_SIZE(sh7785_devices)); +} +arch_initcall(sh7785_devices_setup); + +static struct platform_device *sh7785_early_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + &scif3_device, + &scif4_device, + &scif5_device, + &tmu0_device, + &tmu1_device, +}; + +void __init plat_early_device_setup(void) +{ + early_platform_add_devices(sh7785_early_devices, + ARRAY_SIZE(sh7785_early_devices)); +} + +enum { + UNUSED = 0, + + /* interrupt sources */ + + IRL0_LLLL, IRL0_LLLH, IRL0_LLHL, IRL0_LLHH, + IRL0_LHLL, IRL0_LHLH, IRL0_LHHL, IRL0_LHHH, + IRL0_HLLL, IRL0_HLLH, IRL0_HLHL, IRL0_HLHH, + IRL0_HHLL, IRL0_HHLH, IRL0_HHHL, + + IRL4_LLLL, IRL4_LLLH, IRL4_LLHL, IRL4_LLHH, + IRL4_LHLL, IRL4_LHLH, IRL4_LHHL, IRL4_LHHH, + IRL4_HLLL, IRL4_HLLH, IRL4_HLHL, IRL4_HLHH, + IRL4_HHLL, IRL4_HHLH, IRL4_HHHL, + + IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7, + WDT, TMU0, TMU1, TMU2, TMU2_TICPI, + HUDI, DMAC0, SCIF0, SCIF1, DMAC1, HSPI, + SCIF2, SCIF3, SCIF4, SCIF5, + PCISERR, PCIINTA, PCIINTB, PCIINTC, PCIINTD, PCIC5, + SIOF, MMCIF, DU, GDTA, + TMU3, TMU4, TMU5, + SSI0, SSI1, + HAC0, HAC1, + FLCTL, GPIO, + + /* interrupt groups */ + + TMU012, TMU345 +}; + +static struct intc_vect vectors[] __initdata = { + INTC_VECT(WDT, 0x560), + INTC_VECT(TMU0, 0x580), INTC_VECT(TMU1, 0x5a0), + INTC_VECT(TMU2, 0x5c0), INTC_VECT(TMU2_TICPI, 0x5e0), + INTC_VECT(HUDI, 0x600), + INTC_VECT(DMAC0, 0x620), INTC_VECT(DMAC0, 0x640), + INTC_VECT(DMAC0, 0x660), INTC_VECT(DMAC0, 0x680), + INTC_VECT(DMAC0, 0x6a0), INTC_VECT(DMAC0, 0x6c0), + INTC_VECT(DMAC0, 0x6e0), + INTC_VECT(SCIF0, 0x700), INTC_VECT(SCIF0, 0x720), + INTC_VECT(SCIF0, 0x740), INTC_VECT(SCIF0, 0x760), + INTC_VECT(SCIF1, 0x780), INTC_VECT(SCIF1, 0x7a0), + INTC_VECT(SCIF1, 0x7c0), INTC_VECT(SCIF1, 0x7e0), + INTC_VECT(DMAC1, 0x880), INTC_VECT(DMAC1, 0x8a0), + INTC_VECT(DMAC1, 0x8c0), INTC_VECT(DMAC1, 0x8e0), + INTC_VECT(DMAC1, 0x900), INTC_VECT(DMAC1, 0x920), + INTC_VECT(DMAC1, 0x940), + INTC_VECT(HSPI, 0x960), + INTC_VECT(SCIF2, 0x980), INTC_VECT(SCIF3, 0x9a0), + INTC_VECT(SCIF4, 0x9c0), INTC_VECT(SCIF5, 0x9e0), + INTC_VECT(PCISERR, 0xa00), INTC_VECT(PCIINTA, 0xa20), + INTC_VECT(PCIINTB, 0xa40), INTC_VECT(PCIINTC, 0xa60), + INTC_VECT(PCIINTD, 0xa80), INTC_VECT(PCIC5, 0xaa0), + INTC_VECT(PCIC5, 0xac0), INTC_VECT(PCIC5, 0xae0), + INTC_VECT(PCIC5, 0xb00), INTC_VECT(PCIC5, 0xb20), + INTC_VECT(SIOF, 0xc00), + INTC_VECT(MMCIF, 0xd00), INTC_VECT(MMCIF, 0xd20), + INTC_VECT(MMCIF, 0xd40), INTC_VECT(MMCIF, 0xd60), + INTC_VECT(DU, 0xd80), + INTC_VECT(GDTA, 0xda0), INTC_VECT(GDTA, 0xdc0), + INTC_VECT(GDTA, 0xde0), + INTC_VECT(TMU3, 0xe00), INTC_VECT(TMU4, 0xe20), + INTC_VECT(TMU5, 0xe40), + INTC_VECT(SSI0, 0xe80), INTC_VECT(SSI1, 0xea0), + INTC_VECT(HAC0, 0xec0), INTC_VECT(HAC1, 0xee0), + INTC_VECT(FLCTL, 0xf00), INTC_VECT(FLCTL, 0xf20), + INTC_VECT(FLCTL, 0xf40), INTC_VECT(FLCTL, 0xf60), + INTC_VECT(GPIO, 0xf80), INTC_VECT(GPIO, 0xfa0), + INTC_VECT(GPIO, 0xfc0), INTC_VECT(GPIO, 0xfe0), +}; + +static struct intc_group groups[] __initdata = { + INTC_GROUP(TMU012, TMU0, TMU1, TMU2, TMU2_TICPI), + INTC_GROUP(TMU345, TMU3, TMU4, TMU5), +}; + +static struct intc_mask_reg mask_registers[] __initdata = { + { 0xffd00044, 0xffd00064, 32, /* INTMSK0 / INTMSKCLR0 */ + { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } }, + + { 0xffd40080, 0xffd40084, 32, /* INTMSK2 / INTMSKCLR2 */ + { IRL0_LLLL, IRL0_LLLH, IRL0_LLHL, IRL0_LLHH, + IRL0_LHLL, IRL0_LHLH, IRL0_LHHL, IRL0_LHHH, + IRL0_HLLL, IRL0_HLLH, IRL0_HLHL, IRL0_HLHH, + IRL0_HHLL, IRL0_HHLH, IRL0_HHHL, 0, + IRL4_LLLL, IRL4_LLLH, IRL4_LLHL, IRL4_LLHH, + IRL4_LHLL, IRL4_LHLH, IRL4_LHHL, IRL4_LHHH, + IRL4_HLLL, IRL4_HLLH, IRL4_HLHL, IRL4_HLHH, + IRL4_HHLL, IRL4_HHLH, IRL4_HHHL, 0, } }, + + { 0xffd40038, 0xffd4003c, 32, /* INT2MSKR / INT2MSKCR */ + { 0, 0, 0, GDTA, DU, SSI0, SSI1, GPIO, + FLCTL, MMCIF, HSPI, SIOF, PCIC5, PCIINTD, PCIINTC, PCIINTB, + PCIINTA, PCISERR, HAC1, HAC0, DMAC1, DMAC0, HUDI, WDT, + SCIF5, SCIF4, SCIF3, SCIF2, SCIF1, SCIF0, TMU345, TMU012 } }, +}; + +static struct intc_prio_reg prio_registers[] __initdata = { + { 0xffd00010, 0, 32, 4, /* INTPRI */ { IRQ0, IRQ1, IRQ2, IRQ3, + IRQ4, IRQ5, IRQ6, IRQ7 } }, + { 0xffd40000, 0, 32, 8, /* INT2PRI0 */ { TMU0, TMU1, + TMU2, TMU2_TICPI } }, + { 0xffd40004, 0, 32, 8, /* INT2PRI1 */ { TMU3, TMU4, TMU5, } }, + { 0xffd40008, 0, 32, 8, /* INT2PRI2 */ { SCIF0, SCIF1, + SCIF2, SCIF3 } }, + { 0xffd4000c, 0, 32, 8, /* INT2PRI3 */ { SCIF4, SCIF5, WDT, } }, + { 0xffd40010, 0, 32, 8, /* INT2PRI4 */ { HUDI, DMAC0, DMAC1, } }, + { 0xffd40014, 0, 32, 8, /* INT2PRI5 */ { HAC0, HAC1, + PCISERR, PCIINTA } }, + { 0xffd40018, 0, 32, 8, /* INT2PRI6 */ { PCIINTB, PCIINTC, + PCIINTD, PCIC5 } }, + { 0xffd4001c, 0, 32, 8, /* INT2PRI7 */ { SIOF, HSPI, MMCIF, } }, + { 0xffd40020, 0, 32, 8, /* INT2PRI8 */ { FLCTL, GPIO, SSI0, SSI1, } }, + { 0xffd40024, 0, 32, 8, /* INT2PRI9 */ { DU, GDTA, } }, +}; + +static DECLARE_INTC_DESC(intc_desc, "sh7785", vectors, groups, + mask_registers, prio_registers, NULL); + +/* Support for external interrupt pins in IRQ mode */ + +static struct intc_vect vectors_irq0123[] __initdata = { + INTC_VECT(IRQ0, 0x240), INTC_VECT(IRQ1, 0x280), + INTC_VECT(IRQ2, 0x2c0), INTC_VECT(IRQ3, 0x300), +}; + +static struct intc_vect vectors_irq4567[] __initdata = { + INTC_VECT(IRQ4, 0x340), INTC_VECT(IRQ5, 0x380), + INTC_VECT(IRQ6, 0x3c0), INTC_VECT(IRQ7, 0x200), +}; + +static struct intc_sense_reg sense_registers[] __initdata = { + { 0xffd0001c, 32, 2, /* ICR1 */ { IRQ0, IRQ1, IRQ2, IRQ3, + IRQ4, IRQ5, IRQ6, IRQ7 } }, +}; + +static struct intc_mask_reg ack_registers[] __initdata = { + { 0xffd00024, 0, 32, /* INTREQ */ + { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } }, +}; + +static DECLARE_INTC_DESC_ACK(intc_desc_irq0123, "sh7785-irq0123", + vectors_irq0123, NULL, mask_registers, + prio_registers, sense_registers, ack_registers); + +static DECLARE_INTC_DESC_ACK(intc_desc_irq4567, "sh7785-irq4567", + vectors_irq4567, NULL, mask_registers, + prio_registers, sense_registers, ack_registers); + +/* External interrupt pins in IRL mode */ + +static struct intc_vect vectors_irl0123[] __initdata = { + INTC_VECT(IRL0_LLLL, 0x200), INTC_VECT(IRL0_LLLH, 0x220), + INTC_VECT(IRL0_LLHL, 0x240), INTC_VECT(IRL0_LLHH, 0x260), + INTC_VECT(IRL0_LHLL, 0x280), INTC_VECT(IRL0_LHLH, 0x2a0), + INTC_VECT(IRL0_LHHL, 0x2c0), INTC_VECT(IRL0_LHHH, 0x2e0), + INTC_VECT(IRL0_HLLL, 0x300), INTC_VECT(IRL0_HLLH, 0x320), + INTC_VECT(IRL0_HLHL, 0x340), INTC_VECT(IRL0_HLHH, 0x360), + INTC_VECT(IRL0_HHLL, 0x380), INTC_VECT(IRL0_HHLH, 0x3a0), + INTC_VECT(IRL0_HHHL, 0x3c0), +}; + +static struct intc_vect vectors_irl4567[] __initdata = { + INTC_VECT(IRL4_LLLL, 0xb00), INTC_VECT(IRL4_LLLH, 0xb20), + INTC_VECT(IRL4_LLHL, 0xb40), INTC_VECT(IRL4_LLHH, 0xb60), + INTC_VECT(IRL4_LHLL, 0xb80), INTC_VECT(IRL4_LHLH, 0xba0), + INTC_VECT(IRL4_LHHL, 0xbc0), INTC_VECT(IRL4_LHHH, 0xbe0), + INTC_VECT(IRL4_HLLL, 0xc00), INTC_VECT(IRL4_HLLH, 0xc20), + INTC_VECT(IRL4_HLHL, 0xc40), INTC_VECT(IRL4_HLHH, 0xc60), + INTC_VECT(IRL4_HHLL, 0xc80), INTC_VECT(IRL4_HHLH, 0xca0), + INTC_VECT(IRL4_HHHL, 0xcc0), +}; + +static DECLARE_INTC_DESC(intc_desc_irl0123, "sh7785-irl0123", vectors_irl0123, + NULL, mask_registers, NULL, NULL); + +static DECLARE_INTC_DESC(intc_desc_irl4567, "sh7785-irl4567", vectors_irl4567, + NULL, mask_registers, NULL, NULL); + +#define INTC_ICR0 0xffd00000 +#define INTC_INTMSK0 0xffd00044 +#define INTC_INTMSK1 0xffd00048 +#define INTC_INTMSK2 0xffd40080 +#define INTC_INTMSKCLR1 0xffd00068 +#define INTC_INTMSKCLR2 0xffd40084 + +void __init plat_irq_setup(void) +{ + /* disable IRQ3-0 + IRQ7-4 */ + __raw_writel(0xff000000, INTC_INTMSK0); + + /* disable IRL3-0 + IRL7-4 */ + __raw_writel(0xc0000000, INTC_INTMSK1); + __raw_writel(0xfffefffe, INTC_INTMSK2); + + /* select IRL mode for IRL3-0 + IRL7-4 */ + __raw_writel(__raw_readl(INTC_ICR0) & ~0x00c00000, INTC_ICR0); + + /* disable holding function, ie enable "SH-4 Mode" */ + __raw_writel(__raw_readl(INTC_ICR0) | 0x00200000, INTC_ICR0); + + register_intc_controller(&intc_desc); +} + +void __init plat_irq_setup_pins(int mode) +{ + switch (mode) { + case IRQ_MODE_IRQ7654: + /* select IRQ mode for IRL7-4 */ + __raw_writel(__raw_readl(INTC_ICR0) | 0x00400000, INTC_ICR0); + register_intc_controller(&intc_desc_irq4567); + break; + case IRQ_MODE_IRQ3210: + /* select IRQ mode for IRL3-0 */ + __raw_writel(__raw_readl(INTC_ICR0) | 0x00800000, INTC_ICR0); + register_intc_controller(&intc_desc_irq0123); + break; + case IRQ_MODE_IRL7654: + /* enable IRL7-4 but don't provide any masking */ + __raw_writel(0x40000000, INTC_INTMSKCLR1); + __raw_writel(0x0000fffe, INTC_INTMSKCLR2); + break; + case IRQ_MODE_IRL3210: + /* enable IRL0-3 but don't provide any masking */ + __raw_writel(0x80000000, INTC_INTMSKCLR1); + __raw_writel(0xfffe0000, INTC_INTMSKCLR2); + break; + case IRQ_MODE_IRL7654_MASK: + /* enable IRL7-4 and mask using cpu intc controller */ + __raw_writel(0x40000000, INTC_INTMSKCLR1); + register_intc_controller(&intc_desc_irl4567); + break; + case IRQ_MODE_IRL3210_MASK: + /* enable IRL0-3 and mask using cpu intc controller */ + __raw_writel(0x80000000, INTC_INTMSKCLR1); + register_intc_controller(&intc_desc_irl0123); + break; + default: + BUG(); + } +} + +void __init plat_mem_setup(void) +{ + /* Register the URAM space as Node 1 */ + setup_bootmem_node(1, 0xe55f0000, 0xe5610000); +} diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7786.c b/arch/sh/kernel/cpu/sh4a/setup-sh7786.c new file mode 100644 index 000000000..17aac38a6 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7786.c @@ -0,0 +1,842 @@ +/* + * SH7786 Setup + * + * Copyright (C) 2009 - 2011 Renesas Solutions Corp. + * Kuninori Morimoto <morimoto.kuninori@renesas.com> + * Paul Mundt <paul.mundt@renesas.com> + * + * Based on SH7785 Setup + * + * Copyright (C) 2007 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/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <linux/serial_sci.h> +#include <linux/io.h> +#include <linux/mm.h> +#include <linux/dma-mapping.h> +#include <linux/sh_timer.h> +#include <linux/sh_dma.h> +#include <linux/sh_intc.h> +#include <linux/usb/ohci_pdriver.h> +#include <cpu/dma-register.h> +#include <asm/mmzone.h> + +static struct plat_sci_port scif0_platform_data = { + .scscr = SCSCR_REIE | SCSCR_CKE1, + .type = PORT_SCIF, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif0_resources[] = { + DEFINE_RES_MEM(0xffea0000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x700)), + DEFINE_RES_IRQ(evt2irq(0x720)), + DEFINE_RES_IRQ(evt2irq(0x760)), + DEFINE_RES_IRQ(evt2irq(0x740)), +}; + +static struct platform_device scif0_device = { + .name = "sh-sci", + .id = 0, + .resource = scif0_resources, + .num_resources = ARRAY_SIZE(scif0_resources), + .dev = { + .platform_data = &scif0_platform_data, + }, +}; + +/* + * The rest of these all have multiplexed IRQs + */ +static struct plat_sci_port scif1_platform_data = { + .scscr = SCSCR_REIE | SCSCR_CKE1, + .type = PORT_SCIF, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif1_resources[] = { + DEFINE_RES_MEM(0xffeb0000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x780)), +}; + +static struct resource scif1_demux_resources[] = { + DEFINE_RES_MEM(0xffeb0000, 0x100), + /* Placeholders, see sh7786_devices_setup() */ + DEFINE_RES_IRQ(0), + DEFINE_RES_IRQ(0), + DEFINE_RES_IRQ(0), + DEFINE_RES_IRQ(0), +}; + +static struct platform_device scif1_device = { + .name = "sh-sci", + .id = 1, + .resource = scif1_resources, + .num_resources = ARRAY_SIZE(scif1_resources), + .dev = { + .platform_data = &scif1_platform_data, + }, +}; + +static struct plat_sci_port scif2_platform_data = { + .scscr = SCSCR_REIE | SCSCR_CKE1, + .type = PORT_SCIF, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif2_resources[] = { + DEFINE_RES_MEM(0xffec0000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x840)), +}; + +static struct platform_device scif2_device = { + .name = "sh-sci", + .id = 2, + .resource = scif2_resources, + .num_resources = ARRAY_SIZE(scif2_resources), + .dev = { + .platform_data = &scif2_platform_data, + }, +}; + +static struct plat_sci_port scif3_platform_data = { + .scscr = SCSCR_REIE | SCSCR_CKE1, + .type = PORT_SCIF, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif3_resources[] = { + DEFINE_RES_MEM(0xffed0000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x860)), +}; + +static struct platform_device scif3_device = { + .name = "sh-sci", + .id = 3, + .resource = scif3_resources, + .num_resources = ARRAY_SIZE(scif3_resources), + .dev = { + .platform_data = &scif3_platform_data, + }, +}; + +static struct plat_sci_port scif4_platform_data = { + .scscr = SCSCR_REIE | SCSCR_CKE1, + .type = PORT_SCIF, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif4_resources[] = { + DEFINE_RES_MEM(0xffee0000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x880)), +}; + +static struct platform_device scif4_device = { + .name = "sh-sci", + .id = 4, + .resource = scif4_resources, + .num_resources = ARRAY_SIZE(scif4_resources), + .dev = { + .platform_data = &scif4_platform_data, + }, +}; + +static struct plat_sci_port scif5_platform_data = { + .scscr = SCSCR_REIE | SCSCR_CKE1, + .type = PORT_SCIF, + .regtype = SCIx_SH4_SCIF_FIFODATA_REGTYPE, +}; + +static struct resource scif5_resources[] = { + DEFINE_RES_MEM(0xffef0000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x8a0)), +}; + +static struct platform_device scif5_device = { + .name = "sh-sci", + .id = 5, + .resource = scif5_resources, + .num_resources = ARRAY_SIZE(scif5_resources), + .dev = { + .platform_data = &scif5_platform_data, + }, +}; + +static struct sh_timer_config tmu0_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu0_resources[] = { + DEFINE_RES_MEM(0xffd80000, 0x30), + DEFINE_RES_IRQ(evt2irq(0x400)), + DEFINE_RES_IRQ(evt2irq(0x420)), + DEFINE_RES_IRQ(evt2irq(0x440)), +}; + +static struct platform_device tmu0_device = { + .name = "sh-tmu", + .id = 0, + .dev = { + .platform_data = &tmu0_platform_data, + }, + .resource = tmu0_resources, + .num_resources = ARRAY_SIZE(tmu0_resources), +}; + +static struct sh_timer_config tmu1_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu1_resources[] = { + DEFINE_RES_MEM(0xffda0000, 0x2c), + DEFINE_RES_IRQ(evt2irq(0x480)), + DEFINE_RES_IRQ(evt2irq(0x4a0)), + DEFINE_RES_IRQ(evt2irq(0x4c0)), +}; + +static struct platform_device tmu1_device = { + .name = "sh-tmu", + .id = 1, + .dev = { + .platform_data = &tmu1_platform_data, + }, + .resource = tmu1_resources, + .num_resources = ARRAY_SIZE(tmu1_resources), +}; + +static struct sh_timer_config tmu2_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu2_resources[] = { + DEFINE_RES_MEM(0xffdc0000, 0x2c), + DEFINE_RES_IRQ(evt2irq(0x7a0)), + DEFINE_RES_IRQ(evt2irq(0x7a0)), + DEFINE_RES_IRQ(evt2irq(0x7a0)), +}; + +static struct platform_device tmu2_device = { + .name = "sh-tmu", + .id = 2, + .dev = { + .platform_data = &tmu2_platform_data, + }, + .resource = tmu2_resources, + .num_resources = ARRAY_SIZE(tmu2_resources), +}; + +static struct sh_timer_config tmu3_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu3_resources[] = { + DEFINE_RES_MEM(0xffde0000, 0x2c), + DEFINE_RES_IRQ(evt2irq(0x7c0)), + DEFINE_RES_IRQ(evt2irq(0x7c0)), + DEFINE_RES_IRQ(evt2irq(0x7c0)), +}; + +static struct platform_device tmu3_device = { + .name = "sh-tmu", + .id = 3, + .dev = { + .platform_data = &tmu3_platform_data, + }, + .resource = tmu3_resources, + .num_resources = ARRAY_SIZE(tmu3_resources), +}; + +static const struct sh_dmae_channel dmac0_channels[] = { + { + .offset = 0, + .dmars = 0, + .dmars_bit = 0, + }, { + .offset = 0x10, + .dmars = 0, + .dmars_bit = 8, + }, { + .offset = 0x20, + .dmars = 4, + .dmars_bit = 0, + }, { + .offset = 0x30, + .dmars = 4, + .dmars_bit = 8, + }, { + .offset = 0x50, + .dmars = 8, + .dmars_bit = 0, + }, { + .offset = 0x60, + .dmars = 8, + .dmars_bit = 8, + } +}; + +static const unsigned int ts_shift[] = TS_SHIFT; + +static struct sh_dmae_pdata dma0_platform_data = { + .channel = dmac0_channels, + .channel_num = ARRAY_SIZE(dmac0_channels), + .ts_low_shift = CHCR_TS_LOW_SHIFT, + .ts_low_mask = CHCR_TS_LOW_MASK, + .ts_high_shift = CHCR_TS_HIGH_SHIFT, + .ts_high_mask = CHCR_TS_HIGH_MASK, + .ts_shift = ts_shift, + .ts_shift_num = ARRAY_SIZE(ts_shift), + .dmaor_init = DMAOR_INIT, +}; + +/* Resource order important! */ +static struct resource dmac0_resources[] = { + { + /* Channel registers and DMAOR */ + .start = 0xfe008020, + .end = 0xfe00808f, + .flags = IORESOURCE_MEM, + }, { + /* DMARSx */ + .start = 0xfe009000, + .end = 0xfe00900b, + .flags = IORESOURCE_MEM, + }, { + .name = "error_irq", + .start = evt2irq(0x5c0), + .end = evt2irq(0x5c0), + .flags = IORESOURCE_IRQ, + }, { + /* IRQ for channels 0-5 */ + .start = evt2irq(0x500), + .end = evt2irq(0x5a0), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device dma0_device = { + .name = "sh-dma-engine", + .id = 0, + .resource = dmac0_resources, + .num_resources = ARRAY_SIZE(dmac0_resources), + .dev = { + .platform_data = &dma0_platform_data, + }, +}; + +#define USB_EHCI_START 0xffe70000 +#define USB_OHCI_START 0xffe70400 + +static struct resource usb_ehci_resources[] = { + [0] = { + .start = USB_EHCI_START, + .end = USB_EHCI_START + 0x3ff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = evt2irq(0xba0), + .end = evt2irq(0xba0), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device usb_ehci_device = { + .name = "sh_ehci", + .id = -1, + .dev = { + .dma_mask = &usb_ehci_device.dev.coherent_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, + .num_resources = ARRAY_SIZE(usb_ehci_resources), + .resource = usb_ehci_resources, +}; + +static struct resource usb_ohci_resources[] = { + [0] = { + .start = USB_OHCI_START, + .end = USB_OHCI_START + 0x3ff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = evt2irq(0xba0), + .end = evt2irq(0xba0), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct usb_ohci_pdata usb_ohci_pdata; + +static struct platform_device usb_ohci_device = { + .name = "ohci-platform", + .id = -1, + .dev = { + .dma_mask = &usb_ohci_device.dev.coherent_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &usb_ohci_pdata, + }, + .num_resources = ARRAY_SIZE(usb_ohci_resources), + .resource = usb_ohci_resources, +}; + +static struct platform_device *sh7786_early_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + &scif3_device, + &scif4_device, + &scif5_device, + &tmu0_device, + &tmu1_device, + &tmu2_device, +}; + +static struct platform_device *sh7786_devices[] __initdata = { + &dma0_device, + &usb_ehci_device, + &usb_ohci_device, +}; + +/* + * Please call this function if your platform board + * use external clock for USB + * */ +#define USBCTL0 0xffe70858 +#define CLOCK_MODE_MASK 0xffffff7f +#define EXT_CLOCK_MODE 0x00000080 + +void __init sh7786_usb_use_exclock(void) +{ + u32 val = __raw_readl(USBCTL0) & CLOCK_MODE_MASK; + __raw_writel(val | EXT_CLOCK_MODE, USBCTL0); +} + +#define USBINITREG1 0xffe70094 +#define USBINITREG2 0xffe7009c +#define USBINITVAL1 0x00ff0040 +#define USBINITVAL2 0x00000001 + +#define USBPCTL1 0xffe70804 +#define USBST 0xffe70808 +#define PHY_ENB 0x00000001 +#define PLL_ENB 0x00000002 +#define PHY_RST 0x00000004 +#define ACT_PLL_STATUS 0xc0000000 + +static void __init sh7786_usb_setup(void) +{ + int i = 1000000; + + /* + * USB initial settings + * + * The following settings are necessary + * for using the USB modules. + * + * see "USB Initial Settings" for detail + */ + __raw_writel(USBINITVAL1, USBINITREG1); + __raw_writel(USBINITVAL2, USBINITREG2); + + /* + * Set the PHY and PLL enable bit + */ + __raw_writel(PHY_ENB | PLL_ENB, USBPCTL1); + while (i--) { + if (ACT_PLL_STATUS == (__raw_readl(USBST) & ACT_PLL_STATUS)) { + /* Set the PHY RST bit */ + __raw_writel(PHY_ENB | PLL_ENB | PHY_RST, USBPCTL1); + printk(KERN_INFO "sh7786 usb setup done\n"); + break; + } + cpu_relax(); + } +} + +enum { + UNUSED = 0, + + /* interrupt sources */ + IRL0_LLLL, IRL0_LLLH, IRL0_LLHL, IRL0_LLHH, + IRL0_LHLL, IRL0_LHLH, IRL0_LHHL, IRL0_LHHH, + IRL0_HLLL, IRL0_HLLH, IRL0_HLHL, IRL0_HLHH, + IRL0_HHLL, IRL0_HHLH, IRL0_HHHL, + + IRL4_LLLL, IRL4_LLLH, IRL4_LLHL, IRL4_LLHH, + IRL4_LHLL, IRL4_LHLH, IRL4_LHHL, IRL4_LHHH, + IRL4_HLLL, IRL4_HLLH, IRL4_HLHL, IRL4_HLHH, + IRL4_HHLL, IRL4_HHLH, IRL4_HHHL, + + IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7, + WDT, + TMU0_0, TMU0_1, TMU0_2, TMU0_3, + TMU1_0, TMU1_1, TMU1_2, + DMAC0_0, DMAC0_1, DMAC0_2, DMAC0_3, DMAC0_4, DMAC0_5, DMAC0_6, + HUDI1, HUDI0, + DMAC1_0, DMAC1_1, DMAC1_2, DMAC1_3, + HPB_0, HPB_1, HPB_2, + SCIF0_0, SCIF0_1, SCIF0_2, SCIF0_3, + SCIF1, + TMU2, TMU3, + SCIF2, SCIF3, SCIF4, SCIF5, + Eth_0, Eth_1, + PCIeC0_0, PCIeC0_1, PCIeC0_2, + PCIeC1_0, PCIeC1_1, PCIeC1_2, + USB, + I2C0, I2C1, + DU, + SSI0, SSI1, SSI2, SSI3, + PCIeC2_0, PCIeC2_1, PCIeC2_2, + HAC0, HAC1, + FLCTL, + HSPI, + GPIO0, GPIO1, + Thermal, + INTICI0, INTICI1, INTICI2, INTICI3, + INTICI4, INTICI5, INTICI6, INTICI7, + + /* Muxed sub-events */ + TXI1, BRI1, RXI1, ERI1, +}; + +static struct intc_vect sh7786_vectors[] __initdata = { + INTC_VECT(WDT, 0x3e0), + INTC_VECT(TMU0_0, 0x400), INTC_VECT(TMU0_1, 0x420), + INTC_VECT(TMU0_2, 0x440), INTC_VECT(TMU0_3, 0x460), + INTC_VECT(TMU1_0, 0x480), INTC_VECT(TMU1_1, 0x4a0), + INTC_VECT(TMU1_2, 0x4c0), + INTC_VECT(DMAC0_0, 0x500), INTC_VECT(DMAC0_1, 0x520), + INTC_VECT(DMAC0_2, 0x540), INTC_VECT(DMAC0_3, 0x560), + INTC_VECT(DMAC0_4, 0x580), INTC_VECT(DMAC0_5, 0x5a0), + INTC_VECT(DMAC0_6, 0x5c0), + INTC_VECT(HUDI1, 0x5e0), INTC_VECT(HUDI0, 0x600), + INTC_VECT(DMAC1_0, 0x620), INTC_VECT(DMAC1_1, 0x640), + INTC_VECT(DMAC1_2, 0x660), INTC_VECT(DMAC1_3, 0x680), + INTC_VECT(HPB_0, 0x6a0), INTC_VECT(HPB_1, 0x6c0), + INTC_VECT(HPB_2, 0x6e0), + INTC_VECT(SCIF0_0, 0x700), INTC_VECT(SCIF0_1, 0x720), + INTC_VECT(SCIF0_2, 0x740), INTC_VECT(SCIF0_3, 0x760), + INTC_VECT(SCIF1, 0x780), + INTC_VECT(TMU2, 0x7a0), INTC_VECT(TMU3, 0x7c0), + INTC_VECT(SCIF2, 0x840), INTC_VECT(SCIF3, 0x860), + INTC_VECT(SCIF4, 0x880), INTC_VECT(SCIF5, 0x8a0), + INTC_VECT(Eth_0, 0x8c0), INTC_VECT(Eth_1, 0x8e0), + INTC_VECT(PCIeC0_0, 0xae0), INTC_VECT(PCIeC0_1, 0xb00), + INTC_VECT(PCIeC0_2, 0xb20), + INTC_VECT(PCIeC1_0, 0xb40), INTC_VECT(PCIeC1_1, 0xb60), + INTC_VECT(PCIeC1_2, 0xb80), + INTC_VECT(USB, 0xba0), + INTC_VECT(I2C0, 0xcc0), INTC_VECT(I2C1, 0xce0), + INTC_VECT(DU, 0xd00), + INTC_VECT(SSI0, 0xd20), INTC_VECT(SSI1, 0xd40), + INTC_VECT(SSI2, 0xd60), INTC_VECT(SSI3, 0xd80), + INTC_VECT(PCIeC2_0, 0xda0), INTC_VECT(PCIeC2_1, 0xdc0), + INTC_VECT(PCIeC2_2, 0xde0), + INTC_VECT(HAC0, 0xe00), INTC_VECT(HAC1, 0xe20), + INTC_VECT(FLCTL, 0xe40), + INTC_VECT(HSPI, 0xe80), + INTC_VECT(GPIO0, 0xea0), INTC_VECT(GPIO1, 0xec0), + INTC_VECT(Thermal, 0xee0), + INTC_VECT(INTICI0, 0xf00), INTC_VECT(INTICI1, 0xf20), + INTC_VECT(INTICI2, 0xf40), INTC_VECT(INTICI3, 0xf60), + INTC_VECT(INTICI4, 0xf80), INTC_VECT(INTICI5, 0xfa0), + INTC_VECT(INTICI6, 0xfc0), INTC_VECT(INTICI7, 0xfe0), +}; + +#define CnINTMSK0 0xfe410030 +#define CnINTMSK1 0xfe410040 +#define CnINTMSKCLR0 0xfe410050 +#define CnINTMSKCLR1 0xfe410060 +#define CnINT2MSKR0 0xfe410a20 +#define CnINT2MSKR1 0xfe410a24 +#define CnINT2MSKR2 0xfe410a28 +#define CnINT2MSKR3 0xfe410a2c +#define CnINT2MSKCR0 0xfe410a30 +#define CnINT2MSKCR1 0xfe410a34 +#define CnINT2MSKCR2 0xfe410a38 +#define CnINT2MSKCR3 0xfe410a3c +#define INTMSK2 0xfe410068 +#define INTMSKCLR2 0xfe41006c + +#define INTDISTCR0 0xfe4100b0 +#define INTDISTCR1 0xfe4100b4 +#define INT2DISTCR0 0xfe410900 +#define INT2DISTCR1 0xfe410904 +#define INT2DISTCR2 0xfe410908 +#define INT2DISTCR3 0xfe41090c + +static struct intc_mask_reg sh7786_mask_registers[] __initdata = { + { CnINTMSK0, CnINTMSKCLR0, 32, + { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 }, + INTC_SMP_BALANCING(INTDISTCR0) }, + { INTMSK2, INTMSKCLR2, 32, + { IRL0_LLLL, IRL0_LLLH, IRL0_LLHL, IRL0_LLHH, + IRL0_LHLL, IRL0_LHLH, IRL0_LHHL, IRL0_LHHH, + IRL0_HLLL, IRL0_HLLH, IRL0_HLHL, IRL0_HLHH, + IRL0_HHLL, IRL0_HHLH, IRL0_HHHL, 0, + IRL4_LLLL, IRL4_LLLH, IRL4_LLHL, IRL4_LLHH, + IRL4_LHLL, IRL4_LHLH, IRL4_LHHL, IRL4_LHHH, + IRL4_HLLL, IRL4_HLLH, IRL4_HLHL, IRL4_HLHH, + IRL4_HHLL, IRL4_HHLH, IRL4_HHHL, 0, } }, + { CnINT2MSKR0, CnINT2MSKCR0 , 32, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, WDT }, + INTC_SMP_BALANCING(INT2DISTCR0) }, + { CnINT2MSKR1, CnINT2MSKCR1, 32, + { TMU0_0, TMU0_1, TMU0_2, TMU0_3, TMU1_0, TMU1_1, TMU1_2, 0, + DMAC0_0, DMAC0_1, DMAC0_2, DMAC0_3, DMAC0_4, DMAC0_5, DMAC0_6, + HUDI1, HUDI0, + DMAC1_0, DMAC1_1, DMAC1_2, DMAC1_3, + HPB_0, HPB_1, HPB_2, + SCIF0_0, SCIF0_1, SCIF0_2, SCIF0_3, + SCIF1, + TMU2, TMU3, 0, }, INTC_SMP_BALANCING(INT2DISTCR1) }, + { CnINT2MSKR2, CnINT2MSKCR2, 32, + { 0, 0, SCIF2, SCIF3, SCIF4, SCIF5, + Eth_0, Eth_1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + PCIeC0_0, PCIeC0_1, PCIeC0_2, + PCIeC1_0, PCIeC1_1, PCIeC1_2, + USB, 0, 0 }, INTC_SMP_BALANCING(INT2DISTCR2) }, + { CnINT2MSKR3, CnINT2MSKCR3, 32, + { 0, 0, 0, 0, 0, 0, + I2C0, I2C1, + DU, SSI0, SSI1, SSI2, SSI3, + PCIeC2_0, PCIeC2_1, PCIeC2_2, + HAC0, HAC1, + FLCTL, 0, + HSPI, GPIO0, GPIO1, Thermal, + 0, 0, 0, 0, 0, 0, 0, 0 }, INTC_SMP_BALANCING(INT2DISTCR3) }, +}; + +static struct intc_prio_reg sh7786_prio_registers[] __initdata = { + { 0xfe410010, 0, 32, 4, /* INTPRI */ { IRQ0, IRQ1, IRQ2, IRQ3, + IRQ4, IRQ5, IRQ6, IRQ7 } }, + { 0xfe410800, 0, 32, 8, /* INT2PRI0 */ { 0, 0, 0, WDT } }, + { 0xfe410804, 0, 32, 8, /* INT2PRI1 */ { TMU0_0, TMU0_1, + TMU0_2, TMU0_3 } }, + { 0xfe410808, 0, 32, 8, /* INT2PRI2 */ { TMU1_0, TMU1_1, + TMU1_2, 0 } }, + { 0xfe41080c, 0, 32, 8, /* INT2PRI3 */ { DMAC0_0, DMAC0_1, + DMAC0_2, DMAC0_3 } }, + { 0xfe410810, 0, 32, 8, /* INT2PRI4 */ { DMAC0_4, DMAC0_5, + DMAC0_6, HUDI1 } }, + { 0xfe410814, 0, 32, 8, /* INT2PRI5 */ { HUDI0, DMAC1_0, + DMAC1_1, DMAC1_2 } }, + { 0xfe410818, 0, 32, 8, /* INT2PRI6 */ { DMAC1_3, HPB_0, + HPB_1, HPB_2 } }, + { 0xfe41081c, 0, 32, 8, /* INT2PRI7 */ { SCIF0_0, SCIF0_1, + SCIF0_2, SCIF0_3 } }, + { 0xfe410820, 0, 32, 8, /* INT2PRI8 */ { SCIF1, TMU2, TMU3, 0 } }, + { 0xfe410824, 0, 32, 8, /* INT2PRI9 */ { 0, 0, SCIF2, SCIF3 } }, + { 0xfe410828, 0, 32, 8, /* INT2PRI10 */ { SCIF4, SCIF5, + Eth_0, Eth_1 } }, + { 0xfe41082c, 0, 32, 8, /* INT2PRI11 */ { 0, 0, 0, 0 } }, + { 0xfe410830, 0, 32, 8, /* INT2PRI12 */ { 0, 0, 0, 0 } }, + { 0xfe410834, 0, 32, 8, /* INT2PRI13 */ { 0, 0, 0, 0 } }, + { 0xfe410838, 0, 32, 8, /* INT2PRI14 */ { 0, 0, 0, PCIeC0_0 } }, + { 0xfe41083c, 0, 32, 8, /* INT2PRI15 */ { PCIeC0_1, PCIeC0_2, + PCIeC1_0, PCIeC1_1 } }, + { 0xfe410840, 0, 32, 8, /* INT2PRI16 */ { PCIeC1_2, USB, 0, 0 } }, + { 0xfe410844, 0, 32, 8, /* INT2PRI17 */ { 0, 0, 0, 0 } }, + { 0xfe410848, 0, 32, 8, /* INT2PRI18 */ { 0, 0, I2C0, I2C1 } }, + { 0xfe41084c, 0, 32, 8, /* INT2PRI19 */ { DU, SSI0, SSI1, SSI2 } }, + { 0xfe410850, 0, 32, 8, /* INT2PRI20 */ { SSI3, PCIeC2_0, + PCIeC2_1, PCIeC2_2 } }, + { 0xfe410854, 0, 32, 8, /* INT2PRI21 */ { HAC0, HAC1, FLCTL, 0 } }, + { 0xfe410858, 0, 32, 8, /* INT2PRI22 */ { HSPI, GPIO0, + GPIO1, Thermal } }, + { 0xfe41085c, 0, 32, 8, /* INT2PRI23 */ { 0, 0, 0, 0 } }, + { 0xfe410860, 0, 32, 8, /* INT2PRI24 */ { 0, 0, 0, 0 } }, + { 0xfe410090, 0xfe4100a0, 32, 4, /* CnICIPRI / CnICIPRICLR */ + { INTICI7, INTICI6, INTICI5, INTICI4, + INTICI3, INTICI2, INTICI1, INTICI0 }, INTC_SMP(4, 2) }, +}; + +static struct intc_subgroup sh7786_subgroups[] __initdata = { + { 0xfe410c20, 32, SCIF1, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, TXI1, BRI1, RXI1, ERI1 } }, +}; + +static struct intc_desc sh7786_intc_desc __initdata = { + .name = "sh7786", + .hw = { + .vectors = sh7786_vectors, + .nr_vectors = ARRAY_SIZE(sh7786_vectors), + .mask_regs = sh7786_mask_registers, + .nr_mask_regs = ARRAY_SIZE(sh7786_mask_registers), + .subgroups = sh7786_subgroups, + .nr_subgroups = ARRAY_SIZE(sh7786_subgroups), + .prio_regs = sh7786_prio_registers, + .nr_prio_regs = ARRAY_SIZE(sh7786_prio_registers), + }, +}; + +/* Support for external interrupt pins in IRQ mode */ +static struct intc_vect vectors_irq0123[] __initdata = { + INTC_VECT(IRQ0, 0x200), INTC_VECT(IRQ1, 0x240), + INTC_VECT(IRQ2, 0x280), INTC_VECT(IRQ3, 0x2c0), +}; + +static struct intc_vect vectors_irq4567[] __initdata = { + INTC_VECT(IRQ4, 0x300), INTC_VECT(IRQ5, 0x340), + INTC_VECT(IRQ6, 0x380), INTC_VECT(IRQ7, 0x3c0), +}; + +static struct intc_sense_reg sh7786_sense_registers[] __initdata = { + { 0xfe41001c, 32, 2, /* ICR1 */ { IRQ0, IRQ1, IRQ2, IRQ3, + IRQ4, IRQ5, IRQ6, IRQ7 } }, +}; + +static struct intc_mask_reg sh7786_ack_registers[] __initdata = { + { 0xfe410024, 0, 32, /* INTREQ */ + { IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7 } }, +}; + +static DECLARE_INTC_DESC_ACK(intc_desc_irq0123, "sh7786-irq0123", + vectors_irq0123, NULL, sh7786_mask_registers, + sh7786_prio_registers, sh7786_sense_registers, + sh7786_ack_registers); + +static DECLARE_INTC_DESC_ACK(intc_desc_irq4567, "sh7786-irq4567", + vectors_irq4567, NULL, sh7786_mask_registers, + sh7786_prio_registers, sh7786_sense_registers, + sh7786_ack_registers); + +/* External interrupt pins in IRL mode */ + +static struct intc_vect vectors_irl0123[] __initdata = { + INTC_VECT(IRL0_LLLL, 0x200), INTC_VECT(IRL0_LLLH, 0x220), + INTC_VECT(IRL0_LLHL, 0x240), INTC_VECT(IRL0_LLHH, 0x260), + INTC_VECT(IRL0_LHLL, 0x280), INTC_VECT(IRL0_LHLH, 0x2a0), + INTC_VECT(IRL0_LHHL, 0x2c0), INTC_VECT(IRL0_LHHH, 0x2e0), + INTC_VECT(IRL0_HLLL, 0x300), INTC_VECT(IRL0_HLLH, 0x320), + INTC_VECT(IRL0_HLHL, 0x340), INTC_VECT(IRL0_HLHH, 0x360), + INTC_VECT(IRL0_HHLL, 0x380), INTC_VECT(IRL0_HHLH, 0x3a0), + INTC_VECT(IRL0_HHHL, 0x3c0), +}; + +static struct intc_vect vectors_irl4567[] __initdata = { + INTC_VECT(IRL4_LLLL, 0x900), INTC_VECT(IRL4_LLLH, 0x920), + INTC_VECT(IRL4_LLHL, 0x940), INTC_VECT(IRL4_LLHH, 0x960), + INTC_VECT(IRL4_LHLL, 0x980), INTC_VECT(IRL4_LHLH, 0x9a0), + INTC_VECT(IRL4_LHHL, 0x9c0), INTC_VECT(IRL4_LHHH, 0x9e0), + INTC_VECT(IRL4_HLLL, 0xa00), INTC_VECT(IRL4_HLLH, 0xa20), + INTC_VECT(IRL4_HLHL, 0xa40), INTC_VECT(IRL4_HLHH, 0xa60), + INTC_VECT(IRL4_HHLL, 0xa80), INTC_VECT(IRL4_HHLH, 0xaa0), + INTC_VECT(IRL4_HHHL, 0xac0), +}; + +static DECLARE_INTC_DESC(intc_desc_irl0123, "sh7786-irl0123", vectors_irl0123, + NULL, sh7786_mask_registers, NULL, NULL); + +static DECLARE_INTC_DESC(intc_desc_irl4567, "sh7786-irl4567", vectors_irl4567, + NULL, sh7786_mask_registers, NULL, NULL); + +#define INTC_ICR0 0xfe410000 +#define INTC_INTMSK0 CnINTMSK0 +#define INTC_INTMSK1 CnINTMSK1 +#define INTC_INTMSK2 INTMSK2 +#define INTC_INTMSKCLR1 CnINTMSKCLR1 +#define INTC_INTMSKCLR2 INTMSKCLR2 + +void __init plat_irq_setup(void) +{ + /* disable IRQ3-0 + IRQ7-4 */ + __raw_writel(0xff000000, INTC_INTMSK0); + + /* disable IRL3-0 + IRL7-4 */ + __raw_writel(0xc0000000, INTC_INTMSK1); + __raw_writel(0xfffefffe, INTC_INTMSK2); + + /* select IRL mode for IRL3-0 + IRL7-4 */ + __raw_writel(__raw_readl(INTC_ICR0) & ~0x00c00000, INTC_ICR0); + + register_intc_controller(&sh7786_intc_desc); +} + +void __init plat_irq_setup_pins(int mode) +{ + switch (mode) { + case IRQ_MODE_IRQ7654: + /* select IRQ mode for IRL7-4 */ + __raw_writel(__raw_readl(INTC_ICR0) | 0x00400000, INTC_ICR0); + register_intc_controller(&intc_desc_irq4567); + break; + case IRQ_MODE_IRQ3210: + /* select IRQ mode for IRL3-0 */ + __raw_writel(__raw_readl(INTC_ICR0) | 0x00800000, INTC_ICR0); + register_intc_controller(&intc_desc_irq0123); + break; + case IRQ_MODE_IRL7654: + /* enable IRL7-4 but don't provide any masking */ + __raw_writel(0x40000000, INTC_INTMSKCLR1); + __raw_writel(0x0000fffe, INTC_INTMSKCLR2); + break; + case IRQ_MODE_IRL3210: + /* enable IRL0-3 but don't provide any masking */ + __raw_writel(0x80000000, INTC_INTMSKCLR1); + __raw_writel(0xfffe0000, INTC_INTMSKCLR2); + break; + case IRQ_MODE_IRL7654_MASK: + /* enable IRL7-4 and mask using cpu intc controller */ + __raw_writel(0x40000000, INTC_INTMSKCLR1); + register_intc_controller(&intc_desc_irl4567); + break; + case IRQ_MODE_IRL3210_MASK: + /* enable IRL0-3 and mask using cpu intc controller */ + __raw_writel(0x80000000, INTC_INTMSKCLR1); + register_intc_controller(&intc_desc_irl0123); + break; + default: + BUG(); + } +} + +void __init plat_mem_setup(void) +{ +} + +static int __init sh7786_devices_setup(void) +{ + int ret, irq; + + sh7786_usb_setup(); + + /* + * De-mux SCIF1 IRQs if possible + */ + irq = intc_irq_lookup(sh7786_intc_desc.name, TXI1); + if (irq > 0) { + scif1_demux_resources[1].start = + intc_irq_lookup(sh7786_intc_desc.name, ERI1); + scif1_demux_resources[2].start = + intc_irq_lookup(sh7786_intc_desc.name, RXI1); + scif1_demux_resources[3].start = irq; + scif1_demux_resources[4].start = + intc_irq_lookup(sh7786_intc_desc.name, BRI1); + + scif1_device.resource = scif1_demux_resources; + scif1_device.num_resources = ARRAY_SIZE(scif1_demux_resources); + } + + ret = platform_add_devices(sh7786_early_devices, + ARRAY_SIZE(sh7786_early_devices)); + if (unlikely(ret != 0)) + return ret; + + return platform_add_devices(sh7786_devices, + ARRAY_SIZE(sh7786_devices)); +} +arch_initcall(sh7786_devices_setup); + +void __init plat_early_device_setup(void) +{ + early_platform_add_devices(sh7786_early_devices, + ARRAY_SIZE(sh7786_early_devices)); +} diff --git a/arch/sh/kernel/cpu/sh4a/setup-shx3.c b/arch/sh/kernel/cpu/sh4a/setup-shx3.c new file mode 100644 index 000000000..ee14d92d8 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/setup-shx3.c @@ -0,0 +1,398 @@ +/* + * SH-X3 Prototype Setup + * + * Copyright (C) 2007 - 2010 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/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <linux/serial_sci.h> +#include <linux/io.h> +#include <linux/gpio.h> +#include <linux/sh_timer.h> +#include <linux/sh_intc.h> +#include <cpu/shx3.h> +#include <asm/mmzone.h> + +/* + * This intentionally only registers SCIF ports 0, 1, and 3. SCIF 2 + * INTEVT values overlap with the FPU EXPEVT ones, requiring special + * demuxing in the exception dispatch path. + * + * As this overlap is something that never should have made it in to + * silicon in the first place, we just refuse to deal with the port at + * all rather than adding infrastructure to hack around it. + */ +static struct plat_sci_port scif0_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, +}; + +static struct resource scif0_resources[] = { + DEFINE_RES_MEM(0xffc30000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x700)), + DEFINE_RES_IRQ(evt2irq(0x720)), + DEFINE_RES_IRQ(evt2irq(0x760)), + DEFINE_RES_IRQ(evt2irq(0x740)), +}; + +static struct platform_device scif0_device = { + .name = "sh-sci", + .id = 0, + .resource = scif0_resources, + .num_resources = ARRAY_SIZE(scif0_resources), + .dev = { + .platform_data = &scif0_platform_data, + }, +}; + +static struct plat_sci_port scif1_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, +}; + +static struct resource scif1_resources[] = { + DEFINE_RES_MEM(0xffc40000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x780)), + DEFINE_RES_IRQ(evt2irq(0x7a0)), + DEFINE_RES_IRQ(evt2irq(0x7e0)), + DEFINE_RES_IRQ(evt2irq(0x7c0)), +}; + +static struct platform_device scif1_device = { + .name = "sh-sci", + .id = 1, + .resource = scif1_resources, + .num_resources = ARRAY_SIZE(scif1_resources), + .dev = { + .platform_data = &scif1_platform_data, + }, +}; + +static struct plat_sci_port scif2_platform_data = { + .scscr = SCSCR_REIE, + .type = PORT_SCIF, +}; + +static struct resource scif2_resources[] = { + DEFINE_RES_MEM(0xffc60000, 0x100), + DEFINE_RES_IRQ(evt2irq(0x880)), + DEFINE_RES_IRQ(evt2irq(0x8a0)), + DEFINE_RES_IRQ(evt2irq(0x8e0)), + DEFINE_RES_IRQ(evt2irq(0x8c0)), +}; + +static struct platform_device scif2_device = { + .name = "sh-sci", + .id = 2, + .resource = scif2_resources, + .num_resources = ARRAY_SIZE(scif2_resources), + .dev = { + .platform_data = &scif2_platform_data, + }, +}; + +static struct sh_timer_config tmu0_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu0_resources[] = { + DEFINE_RES_MEM(0xffc10000, 0x30), + DEFINE_RES_IRQ(evt2irq(0x400)), + DEFINE_RES_IRQ(evt2irq(0x420)), + DEFINE_RES_IRQ(evt2irq(0x440)), +}; + +static struct platform_device tmu0_device = { + .name = "sh-tmu", + .id = 0, + .dev = { + .platform_data = &tmu0_platform_data, + }, + .resource = tmu0_resources, + .num_resources = ARRAY_SIZE(tmu0_resources), +}; + +static struct sh_timer_config tmu1_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu1_resources[] = { + DEFINE_RES_MEM(0xffc20000, 0x2c), + DEFINE_RES_IRQ(evt2irq(0x460)), + DEFINE_RES_IRQ(evt2irq(0x480)), + DEFINE_RES_IRQ(evt2irq(0x4a0)), +}; + +static struct platform_device tmu1_device = { + .name = "sh-tmu", + .id = 1, + .dev = { + .platform_data = &tmu1_platform_data, + }, + .resource = tmu1_resources, + .num_resources = ARRAY_SIZE(tmu1_resources), +}; + +static struct platform_device *shx3_early_devices[] __initdata = { + &scif0_device, + &scif1_device, + &scif2_device, + &tmu0_device, + &tmu1_device, +}; + +static int __init shx3_devices_setup(void) +{ + return platform_add_devices(shx3_early_devices, + ARRAY_SIZE(shx3_early_devices)); +} +arch_initcall(shx3_devices_setup); + +void __init plat_early_device_setup(void) +{ + early_platform_add_devices(shx3_early_devices, + ARRAY_SIZE(shx3_early_devices)); +} + +enum { + UNUSED = 0, + + /* interrupt sources */ + IRL_LLLL, IRL_LLLH, IRL_LLHL, IRL_LLHH, + IRL_LHLL, IRL_LHLH, IRL_LHHL, IRL_LHHH, + IRL_HLLL, IRL_HLLH, IRL_HLHL, IRL_HLHH, + IRL_HHLL, IRL_HHLH, IRL_HHHL, + IRQ0, IRQ1, IRQ2, IRQ3, + HUDII, + TMU0, TMU1, TMU2, TMU3, TMU4, TMU5, + PCII0, PCII1, PCII2, PCII3, PCII4, + PCII5, PCII6, PCII7, PCII8, PCII9, + SCIF0_ERI, SCIF0_RXI, SCIF0_BRI, SCIF0_TXI, + SCIF1_ERI, SCIF1_RXI, SCIF1_BRI, SCIF1_TXI, + SCIF2_ERI, SCIF2_RXI, SCIF2_BRI, SCIF2_TXI, + SCIF3_ERI, SCIF3_RXI, SCIF3_BRI, SCIF3_TXI, + DMAC0_DMINT0, DMAC0_DMINT1, DMAC0_DMINT2, DMAC0_DMINT3, + DMAC0_DMINT4, DMAC0_DMINT5, DMAC0_DMAE, + DU, + DMAC1_DMINT6, DMAC1_DMINT7, DMAC1_DMINT8, DMAC1_DMINT9, + DMAC1_DMINT10, DMAC1_DMINT11, DMAC1_DMAE, + IIC, VIN0, VIN1, VCORE0, ATAPI, + DTU0, DTU1, DTU2, DTU3, + FE0, FE1, + GPIO0, GPIO1, GPIO2, GPIO3, + PAM, IRM, + INTICI0, INTICI1, INTICI2, INTICI3, + INTICI4, INTICI5, INTICI6, INTICI7, + + /* interrupt groups */ + IRL, PCII56789, SCIF0, SCIF1, SCIF2, SCIF3, + DMAC0, DMAC1, +}; + +static struct intc_vect vectors[] __initdata = { + INTC_VECT(HUDII, 0x3e0), + INTC_VECT(TMU0, 0x400), INTC_VECT(TMU1, 0x420), + INTC_VECT(TMU2, 0x440), INTC_VECT(TMU3, 0x460), + INTC_VECT(TMU4, 0x480), INTC_VECT(TMU5, 0x4a0), + INTC_VECT(PCII0, 0x500), INTC_VECT(PCII1, 0x520), + INTC_VECT(PCII2, 0x540), INTC_VECT(PCII3, 0x560), + INTC_VECT(PCII4, 0x580), INTC_VECT(PCII5, 0x5a0), + INTC_VECT(PCII6, 0x5c0), INTC_VECT(PCII7, 0x5e0), + INTC_VECT(PCII8, 0x600), INTC_VECT(PCII9, 0x620), + INTC_VECT(SCIF0_ERI, 0x700), INTC_VECT(SCIF0_RXI, 0x720), + INTC_VECT(SCIF0_BRI, 0x740), INTC_VECT(SCIF0_TXI, 0x760), + INTC_VECT(SCIF1_ERI, 0x780), INTC_VECT(SCIF1_RXI, 0x7a0), + INTC_VECT(SCIF1_BRI, 0x7c0), INTC_VECT(SCIF1_TXI, 0x7e0), + INTC_VECT(SCIF3_ERI, 0x880), INTC_VECT(SCIF3_RXI, 0x8a0), + INTC_VECT(SCIF3_BRI, 0x8c0), INTC_VECT(SCIF3_TXI, 0x8e0), + INTC_VECT(DMAC0_DMINT0, 0x900), INTC_VECT(DMAC0_DMINT1, 0x920), + INTC_VECT(DMAC0_DMINT2, 0x940), INTC_VECT(DMAC0_DMINT3, 0x960), + INTC_VECT(DMAC0_DMINT4, 0x980), INTC_VECT(DMAC0_DMINT5, 0x9a0), + INTC_VECT(DMAC0_DMAE, 0x9c0), + INTC_VECT(DU, 0x9e0), + INTC_VECT(DMAC1_DMINT6, 0xa00), INTC_VECT(DMAC1_DMINT7, 0xa20), + INTC_VECT(DMAC1_DMINT8, 0xa40), INTC_VECT(DMAC1_DMINT9, 0xa60), + INTC_VECT(DMAC1_DMINT10, 0xa80), INTC_VECT(DMAC1_DMINT11, 0xaa0), + INTC_VECT(DMAC1_DMAE, 0xac0), + INTC_VECT(IIC, 0xae0), + INTC_VECT(VIN0, 0xb00), INTC_VECT(VIN1, 0xb20), + INTC_VECT(VCORE0, 0xb00), INTC_VECT(ATAPI, 0xb60), + INTC_VECT(DTU0, 0xc00), INTC_VECT(DTU0, 0xc20), + INTC_VECT(DTU0, 0xc40), + INTC_VECT(DTU1, 0xc60), INTC_VECT(DTU1, 0xc80), + INTC_VECT(DTU1, 0xca0), + INTC_VECT(DTU2, 0xcc0), INTC_VECT(DTU2, 0xce0), + INTC_VECT(DTU2, 0xd00), + INTC_VECT(DTU3, 0xd20), INTC_VECT(DTU3, 0xd40), + INTC_VECT(DTU3, 0xd60), + INTC_VECT(FE0, 0xe00), INTC_VECT(FE1, 0xe20), + INTC_VECT(GPIO0, 0xe40), INTC_VECT(GPIO1, 0xe60), + INTC_VECT(GPIO2, 0xe80), INTC_VECT(GPIO3, 0xea0), + INTC_VECT(PAM, 0xec0), INTC_VECT(IRM, 0xee0), + INTC_VECT(INTICI0, 0xf00), INTC_VECT(INTICI1, 0xf20), + INTC_VECT(INTICI2, 0xf40), INTC_VECT(INTICI3, 0xf60), + INTC_VECT(INTICI4, 0xf80), INTC_VECT(INTICI5, 0xfa0), + INTC_VECT(INTICI6, 0xfc0), INTC_VECT(INTICI7, 0xfe0), +}; + +static struct intc_group groups[] __initdata = { + INTC_GROUP(IRL, IRL_LLLL, IRL_LLLH, IRL_LLHL, IRL_LLHH, + IRL_LHLL, IRL_LHLH, IRL_LHHL, IRL_LHHH, + IRL_HLLL, IRL_HLLH, IRL_HLHL, IRL_HLHH, + IRL_HHLL, IRL_HHLH, IRL_HHHL), + INTC_GROUP(PCII56789, PCII5, PCII6, PCII7, PCII8, PCII9), + INTC_GROUP(SCIF0, SCIF0_ERI, SCIF0_RXI, SCIF0_BRI, SCIF0_TXI), + INTC_GROUP(SCIF1, SCIF1_ERI, SCIF1_RXI, SCIF1_BRI, SCIF1_TXI), + INTC_GROUP(SCIF3, SCIF3_ERI, SCIF3_RXI, SCIF3_BRI, SCIF3_TXI), + INTC_GROUP(DMAC0, DMAC0_DMINT0, DMAC0_DMINT1, DMAC0_DMINT2, + DMAC0_DMINT3, DMAC0_DMINT4, DMAC0_DMINT5, DMAC0_DMAE), + INTC_GROUP(DMAC1, DMAC1_DMINT6, DMAC1_DMINT7, DMAC1_DMINT8, + DMAC1_DMINT9, DMAC1_DMINT10, DMAC1_DMINT11), +}; + +#define INT2DISTCR0 0xfe4108a0 +#define INT2DISTCR1 0xfe4108a4 +#define INT2DISTCR2 0xfe4108a8 + +static struct intc_mask_reg mask_registers[] __initdata = { + { 0xfe410030, 0xfe410050, 32, /* CnINTMSK0 / CnINTMSKCLR0 */ + { IRQ0, IRQ1, IRQ2, IRQ3 } }, + { 0xfe410040, 0xfe410060, 32, /* CnINTMSK1 / CnINTMSKCLR1 */ + { IRL } }, + { 0xfe410820, 0xfe410850, 32, /* CnINT2MSK0 / CnINT2MSKCLR0 */ + { FE1, FE0, 0, ATAPI, VCORE0, VIN1, VIN0, IIC, + DU, GPIO3, GPIO2, GPIO1, GPIO0, PAM, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* HUDI bits ignored */ + 0, TMU5, TMU4, TMU3, TMU2, TMU1, TMU0, 0, }, + INTC_SMP_BALANCING(INT2DISTCR0) }, + { 0xfe410830, 0xfe410860, 32, /* CnINT2MSK1 / CnINT2MSKCLR1 */ + { 0, 0, 0, 0, DTU3, DTU2, DTU1, DTU0, /* IRM bits ignored */ + PCII9, PCII8, PCII7, PCII6, PCII5, PCII4, PCII3, PCII2, + PCII1, PCII0, DMAC1_DMAE, DMAC1_DMINT11, + DMAC1_DMINT10, DMAC1_DMINT9, DMAC1_DMINT8, DMAC1_DMINT7, + DMAC1_DMINT6, DMAC0_DMAE, DMAC0_DMINT5, DMAC0_DMINT4, + DMAC0_DMINT3, DMAC0_DMINT2, DMAC0_DMINT1, DMAC0_DMINT0 }, + INTC_SMP_BALANCING(INT2DISTCR1) }, + { 0xfe410840, 0xfe410870, 32, /* CnINT2MSK2 / CnINT2MSKCLR2 */ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + SCIF3_TXI, SCIF3_BRI, SCIF3_RXI, SCIF3_ERI, + SCIF2_TXI, SCIF2_BRI, SCIF2_RXI, SCIF2_ERI, + SCIF1_TXI, SCIF1_BRI, SCIF1_RXI, SCIF1_ERI, + SCIF0_TXI, SCIF0_BRI, SCIF0_RXI, SCIF0_ERI }, + INTC_SMP_BALANCING(INT2DISTCR2) }, +}; + +static struct intc_prio_reg prio_registers[] __initdata = { + { 0xfe410010, 0, 32, 4, /* INTPRI */ { IRQ0, IRQ1, IRQ2, IRQ3 } }, + + { 0xfe410800, 0, 32, 4, /* INT2PRI0 */ { 0, HUDII, TMU5, TMU4, + TMU3, TMU2, TMU1, TMU0 } }, + { 0xfe410804, 0, 32, 4, /* INT2PRI1 */ { DTU3, DTU2, DTU1, DTU0, + SCIF3, SCIF2, + SCIF1, SCIF0 } }, + { 0xfe410808, 0, 32, 4, /* INT2PRI2 */ { DMAC1, DMAC0, + PCII56789, PCII4, + PCII3, PCII2, + PCII1, PCII0 } }, + { 0xfe41080c, 0, 32, 4, /* INT2PRI3 */ { FE1, FE0, ATAPI, VCORE0, + VIN1, VIN0, IIC, DU} }, + { 0xfe410810, 0, 32, 4, /* INT2PRI4 */ { 0, 0, PAM, GPIO3, + GPIO2, GPIO1, GPIO0, IRM } }, + { 0xfe410090, 0xfe4100a0, 32, 4, /* CnICIPRI / CnICIPRICLR */ + { INTICI7, INTICI6, INTICI5, INTICI4, + INTICI3, INTICI2, INTICI1, INTICI0 }, INTC_SMP(4, 4) }, +}; + +static DECLARE_INTC_DESC(intc_desc, "shx3", vectors, groups, + mask_registers, prio_registers, NULL); + +/* Support for external interrupt pins in IRQ mode */ +static struct intc_vect vectors_irq[] __initdata = { + INTC_VECT(IRQ0, 0x240), INTC_VECT(IRQ1, 0x280), + INTC_VECT(IRQ2, 0x2c0), INTC_VECT(IRQ3, 0x300), +}; + +static struct intc_sense_reg sense_registers[] __initdata = { + { 0xfe41001c, 32, 2, /* ICR1 */ { IRQ0, IRQ1, IRQ2, IRQ3 } }, +}; + +static DECLARE_INTC_DESC(intc_desc_irq, "shx3-irq", vectors_irq, groups, + mask_registers, prio_registers, sense_registers); + +/* External interrupt pins in IRL mode */ +static struct intc_vect vectors_irl[] __initdata = { + INTC_VECT(IRL_LLLL, 0x200), INTC_VECT(IRL_LLLH, 0x220), + INTC_VECT(IRL_LLHL, 0x240), INTC_VECT(IRL_LLHH, 0x260), + INTC_VECT(IRL_LHLL, 0x280), INTC_VECT(IRL_LHLH, 0x2a0), + INTC_VECT(IRL_LHHL, 0x2c0), INTC_VECT(IRL_LHHH, 0x2e0), + INTC_VECT(IRL_HLLL, 0x300), INTC_VECT(IRL_HLLH, 0x320), + INTC_VECT(IRL_HLHL, 0x340), INTC_VECT(IRL_HLHH, 0x360), + INTC_VECT(IRL_HHLL, 0x380), INTC_VECT(IRL_HHLH, 0x3a0), + INTC_VECT(IRL_HHHL, 0x3c0), +}; + +static DECLARE_INTC_DESC(intc_desc_irl, "shx3-irl", vectors_irl, groups, + mask_registers, prio_registers, NULL); + +void __init plat_irq_setup_pins(int mode) +{ + int ret = 0; + + switch (mode) { + case IRQ_MODE_IRQ: + ret |= gpio_request(GPIO_FN_IRQ3, intc_desc_irq.name); + ret |= gpio_request(GPIO_FN_IRQ2, intc_desc_irq.name); + ret |= gpio_request(GPIO_FN_IRQ1, intc_desc_irq.name); + ret |= gpio_request(GPIO_FN_IRQ0, intc_desc_irq.name); + + if (unlikely(ret)) { + pr_err("Failed to set IRQ mode\n"); + return; + } + + register_intc_controller(&intc_desc_irq); + break; + case IRQ_MODE_IRL3210: + ret |= gpio_request(GPIO_FN_IRL3, intc_desc_irl.name); + ret |= gpio_request(GPIO_FN_IRL2, intc_desc_irl.name); + ret |= gpio_request(GPIO_FN_IRL1, intc_desc_irl.name); + ret |= gpio_request(GPIO_FN_IRL0, intc_desc_irl.name); + + if (unlikely(ret)) { + pr_err("Failed to set IRL mode\n"); + return; + } + + register_intc_controller(&intc_desc_irl); + break; + default: + BUG(); + } +} + +void __init plat_irq_setup(void) +{ + register_intc_controller(&intc_desc); +} + +void __init plat_mem_setup(void) +{ + unsigned int nid = 1; + + /* Register CPU#0 URAM space as Node 1 */ + setup_bootmem_node(nid++, 0x145f0000, 0x14610000); /* CPU0 */ + +#if 0 + /* XXX: Not yet.. */ + setup_bootmem_node(nid++, 0x14df0000, 0x14e10000); /* CPU1 */ + setup_bootmem_node(nid++, 0x155f0000, 0x15610000); /* CPU2 */ + setup_bootmem_node(nid++, 0x15df0000, 0x15e10000); /* CPU3 */ +#endif + + setup_bootmem_node(nid++, 0x16000000, 0x16020000); /* CSM */ +} diff --git a/arch/sh/kernel/cpu/sh4a/smp-shx3.c b/arch/sh/kernel/cpu/sh4a/smp-shx3.c new file mode 100644 index 000000000..c1f66c35e --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/smp-shx3.c @@ -0,0 +1,149 @@ +/* + * SH-X3 SMP + * + * Copyright (C) 2007 - 2010 Paul Mundt + * Copyright (C) 2007 Magnus Damm + * + * 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/cpumask.h> +#include <linux/smp.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/cpu.h> +#include <asm/sections.h> + +#define STBCR_REG(phys_id) (0xfe400004 | (phys_id << 12)) +#define RESET_REG(phys_id) (0xfe400008 | (phys_id << 12)) + +#define STBCR_MSTP 0x00000001 +#define STBCR_RESET 0x00000002 +#define STBCR_SLEEP 0x00000004 +#define STBCR_LTSLP 0x80000000 + +static irqreturn_t ipi_interrupt_handler(int irq, void *arg) +{ + unsigned int message = (unsigned int)(long)arg; + unsigned int cpu = hard_smp_processor_id(); + unsigned int offs = 4 * cpu; + unsigned int x; + + x = __raw_readl(0xfe410070 + offs); /* C0INITICI..CnINTICI */ + x &= (1 << (message << 2)); + __raw_writel(x, 0xfe410080 + offs); /* C0INTICICLR..CnINTICICLR */ + + smp_message_recv(message); + + return IRQ_HANDLED; +} + +static void shx3_smp_setup(void) +{ + unsigned int cpu = 0; + int i, num; + + init_cpu_possible(cpumask_of(cpu)); + + /* Enable light sleep for the boot CPU */ + __raw_writel(__raw_readl(STBCR_REG(cpu)) | STBCR_LTSLP, STBCR_REG(cpu)); + + __cpu_number_map[0] = 0; + __cpu_logical_map[0] = 0; + + /* + * Do this stupidly for now.. we don't have an easy way to probe + * for the total number of cores. + */ + for (i = 1, num = 0; i < NR_CPUS; i++) { + set_cpu_possible(i, true); + __cpu_number_map[i] = ++num; + __cpu_logical_map[num] = i; + } + + printk(KERN_INFO "Detected %i available secondary CPU(s)\n", num); +} + +static void shx3_prepare_cpus(unsigned int max_cpus) +{ + int i; + + BUILD_BUG_ON(SMP_MSG_NR >= 8); + + for (i = 0; i < SMP_MSG_NR; i++) + if (request_irq(104 + i, ipi_interrupt_handler, + IRQF_PERCPU, "IPI", (void *)(long)i)) + pr_err("Failed to request irq %d\n", i); + + for (i = 0; i < max_cpus; i++) + set_cpu_present(i, true); +} + +static void shx3_start_cpu(unsigned int cpu, unsigned long entry_point) +{ + if (__in_29bit_mode()) + __raw_writel(entry_point, RESET_REG(cpu)); + else + __raw_writel(virt_to_phys(entry_point), RESET_REG(cpu)); + + if (!(__raw_readl(STBCR_REG(cpu)) & STBCR_MSTP)) + __raw_writel(STBCR_MSTP, STBCR_REG(cpu)); + + while (!(__raw_readl(STBCR_REG(cpu)) & STBCR_MSTP)) + cpu_relax(); + + /* Start up secondary processor by sending a reset */ + __raw_writel(STBCR_RESET | STBCR_LTSLP, STBCR_REG(cpu)); +} + +static unsigned int shx3_smp_processor_id(void) +{ + return __raw_readl(0xff000048); /* CPIDR */ +} + +static void shx3_send_ipi(unsigned int cpu, unsigned int message) +{ + unsigned long addr = 0xfe410070 + (cpu * 4); + + BUG_ON(cpu >= 4); + + __raw_writel(1 << (message << 2), addr); /* C0INTICI..CnINTICI */ +} + +static void shx3_update_boot_vector(unsigned int cpu) +{ + __raw_writel(STBCR_MSTP, STBCR_REG(cpu)); + while (!(__raw_readl(STBCR_REG(cpu)) & STBCR_MSTP)) + cpu_relax(); + __raw_writel(STBCR_RESET, STBCR_REG(cpu)); +} + +static int shx3_cpu_prepare(unsigned int cpu) +{ + shx3_update_boot_vector(cpu); + return 0; +} + +static int register_shx3_cpu_notifier(void) +{ + cpuhp_setup_state_nocalls(CPUHP_SH_SH3X_PREPARE, "sh/shx3:prepare", + shx3_cpu_prepare, NULL); + return 0; +} +late_initcall(register_shx3_cpu_notifier); + +struct plat_smp_ops shx3_smp_ops = { + .smp_setup = shx3_smp_setup, + .prepare_cpus = shx3_prepare_cpus, + .start_cpu = shx3_start_cpu, + .smp_processor_id = shx3_smp_processor_id, + .send_ipi = shx3_send_ipi, + .cpu_die = native_cpu_die, + .cpu_disable = native_cpu_disable, + .play_dead = native_play_dead, +}; diff --git a/arch/sh/kernel/cpu/sh4a/ubc.c b/arch/sh/kernel/cpu/sh4a/ubc.c new file mode 100644 index 000000000..efb2745bc --- /dev/null +++ b/arch/sh/kernel/cpu/sh4a/ubc.c @@ -0,0 +1,133 @@ +/* + * arch/sh/kernel/cpu/sh4a/ubc.c + * + * On-chip UBC support for SH-4A CPUs. + * + * Copyright (C) 2009 - 2010 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/err.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <asm/hw_breakpoint.h> + +#define UBC_CBR(idx) (0xff200000 + (0x20 * idx)) +#define UBC_CRR(idx) (0xff200004 + (0x20 * idx)) +#define UBC_CAR(idx) (0xff200008 + (0x20 * idx)) +#define UBC_CAMR(idx) (0xff20000c + (0x20 * idx)) + +#define UBC_CCMFR 0xff200600 +#define UBC_CBCR 0xff200620 + +/* CRR */ +#define UBC_CRR_PCB (1 << 1) +#define UBC_CRR_BIE (1 << 0) + +/* CBR */ +#define UBC_CBR_CE (1 << 0) + +static struct sh_ubc sh4a_ubc; + +static void sh4a_ubc_enable(struct arch_hw_breakpoint *info, int idx) +{ + __raw_writel(UBC_CBR_CE | info->len | info->type, UBC_CBR(idx)); + __raw_writel(info->address, UBC_CAR(idx)); +} + +static void sh4a_ubc_disable(struct arch_hw_breakpoint *info, int idx) +{ + __raw_writel(0, UBC_CBR(idx)); + __raw_writel(0, UBC_CAR(idx)); +} + +static void sh4a_ubc_enable_all(unsigned long mask) +{ + int i; + + for (i = 0; i < sh4a_ubc.num_events; i++) + if (mask & (1 << i)) + __raw_writel(__raw_readl(UBC_CBR(i)) | UBC_CBR_CE, + UBC_CBR(i)); +} + +static void sh4a_ubc_disable_all(void) +{ + int i; + + for (i = 0; i < sh4a_ubc.num_events; i++) + __raw_writel(__raw_readl(UBC_CBR(i)) & ~UBC_CBR_CE, + UBC_CBR(i)); +} + +static unsigned long sh4a_ubc_active_mask(void) +{ + unsigned long active = 0; + int i; + + for (i = 0; i < sh4a_ubc.num_events; i++) + if (__raw_readl(UBC_CBR(i)) & UBC_CBR_CE) + active |= (1 << i); + + return active; +} + +static unsigned long sh4a_ubc_triggered_mask(void) +{ + return __raw_readl(UBC_CCMFR); +} + +static void sh4a_ubc_clear_triggered_mask(unsigned long mask) +{ + __raw_writel(__raw_readl(UBC_CCMFR) & ~mask, UBC_CCMFR); +} + +static struct sh_ubc sh4a_ubc = { + .name = "SH-4A", + .num_events = 2, + .trap_nr = 0x1e0, + .enable = sh4a_ubc_enable, + .disable = sh4a_ubc_disable, + .enable_all = sh4a_ubc_enable_all, + .disable_all = sh4a_ubc_disable_all, + .active_mask = sh4a_ubc_active_mask, + .triggered_mask = sh4a_ubc_triggered_mask, + .clear_triggered_mask = sh4a_ubc_clear_triggered_mask, +}; + +static int __init sh4a_ubc_init(void) +{ + struct clk *ubc_iclk = clk_get(NULL, "ubc0"); + int i; + + /* + * The UBC MSTP bit is optional, as not all platforms will have + * it. Just ignore it if we can't find it. + */ + if (IS_ERR(ubc_iclk)) + ubc_iclk = NULL; + + clk_enable(ubc_iclk); + + __raw_writel(0, UBC_CBCR); + + for (i = 0; i < sh4a_ubc.num_events; i++) { + __raw_writel(0, UBC_CAMR(i)); + __raw_writel(0, UBC_CBR(i)); + + __raw_writel(UBC_CRR_BIE | UBC_CRR_PCB, UBC_CRR(i)); + + /* dummy read for write posting */ + (void)__raw_readl(UBC_CRR(i)); + } + + clk_disable(ubc_iclk); + + sh4a_ubc.clk = ubc_iclk; + + return register_sh_ubc(&sh4a_ubc); +} +arch_initcall(sh4a_ubc_init); diff --git a/arch/sh/kernel/cpu/sh5/Makefile b/arch/sh/kernel/cpu/sh5/Makefile new file mode 100644 index 000000000..97d23ec30 --- /dev/null +++ b/arch/sh/kernel/cpu/sh5/Makefile @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the Linux/SuperH SH-5 backends. +# +obj-y := entry.o probe.o switchto.o + +obj-$(CONFIG_SH_FPU) += fpu.o +obj-$(CONFIG_KALLSYMS) += unwind.o + +# CPU subtype setup +obj-$(CONFIG_CPU_SH5) += setup-sh5.o + +# Primary on-chip clocks (common) +clock-$(CONFIG_CPU_SH5) := clock-sh5.o + +obj-y += $(clock-y) diff --git a/arch/sh/kernel/cpu/sh5/clock-sh5.c b/arch/sh/kernel/cpu/sh5/clock-sh5.c new file mode 100644 index 000000000..c48b93d4c --- /dev/null +++ b/arch/sh/kernel/cpu/sh5/clock-sh5.c @@ -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 = (__raw_readl(cprc_base + 0x00) >> 6) & 0x0007; + clk->rate *= ifc_table[idx]; +} + +static struct sh_clk_ops sh5_master_clk_ops = { + .init = master_clk_init, +}; + +static unsigned long module_clk_recalc(struct clk *clk) +{ + int idx = (__raw_readw(cprc_base) >> 12) & 0x0007; + return clk->parent->rate / ifc_table[idx]; +} + +static struct sh_clk_ops sh5_module_clk_ops = { + .recalc = module_clk_recalc, +}; + +static unsigned long bus_clk_recalc(struct clk *clk) +{ + int idx = (__raw_readw(cprc_base) >> 3) & 0x0007; + return clk->parent->rate / ifc_table[idx]; +} + +static struct sh_clk_ops sh5_bus_clk_ops = { + .recalc = bus_clk_recalc, +}; + +static unsigned long cpu_clk_recalc(struct clk *clk) +{ + int idx = (__raw_readw(cprc_base) & 0x0007); + return clk->parent->rate / ifc_table[idx]; +} + +static struct sh_clk_ops sh5_cpu_clk_ops = { + .recalc = cpu_clk_recalc, +}; + +static struct sh_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 sh_clk_ops **ops, int idx) +{ + cprc_base = (unsigned long)ioremap_nocache(CPRC_BASE, 1024); + BUG_ON(!cprc_base); + + if (idx < ARRAY_SIZE(sh5_clk_ops)) + *ops = sh5_clk_ops[idx]; +} diff --git a/arch/sh/kernel/cpu/sh5/entry.S b/arch/sh/kernel/cpu/sh5/entry.S new file mode 100644 index 000000000..0c8d0377d --- /dev/null +++ b/arch/sh/kernel/cpu/sh5/entry.S @@ -0,0 +1,2003 @@ +/* + * arch/sh/kernel/cpu/sh5/entry.S + * + * Copyright (C) 2000, 2001 Paolo Alberelli + * Copyright (C) 2004 - 2008 Paul Mundt + * Copyright (C) 2003, 2004 Richard Curnow + * + * 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/errno.h> +#include <linux/init.h> +#include <linux/sys.h> +#include <cpu/registers.h> +#include <asm/processor.h> +#include <asm/unistd.h> +#include <asm/thread_info.h> +#include <asm/asm-offsets.h> + +/* + * SR fields. + */ +#define SR_ASID_MASK 0x00ff0000 +#define SR_FD_MASK 0x00008000 +#define SR_SS 0x08000000 +#define SR_BL 0x10000000 +#define SR_MD 0x40000000 + +/* + * Event code. + */ +#define EVENT_INTERRUPT 0 +#define EVENT_FAULT_TLB 1 +#define EVENT_FAULT_NOT_TLB 2 +#define EVENT_DEBUG 3 + +/* EXPEVT values */ +#define RESET_CAUSE 0x20 +#define DEBUGSS_CAUSE 0x980 + +/* + * Frame layout. Quad index. + */ +#define FRAME_T(x) FRAME_TBASE+(x*8) +#define FRAME_R(x) FRAME_RBASE+(x*8) +#define FRAME_S(x) FRAME_SBASE+(x*8) +#define FSPC 0 +#define FSSR 1 +#define FSYSCALL_ID 2 + +/* Arrange the save frame to be a multiple of 32 bytes long */ +#define FRAME_SBASE 0 +#define FRAME_RBASE (FRAME_SBASE+(3*8)) /* SYSCALL_ID - SSR - SPC */ +#define FRAME_TBASE (FRAME_RBASE+(63*8)) /* r0 - r62 */ +#define FRAME_PBASE (FRAME_TBASE+(8*8)) /* tr0 -tr7 */ +#define FRAME_SIZE (FRAME_PBASE+(2*8)) /* pad0-pad1 */ + +#define FP_FRAME_SIZE FP_FRAME_BASE+(33*8) /* dr0 - dr31 + fpscr */ +#define FP_FRAME_BASE 0 + +#define SAVED_R2 0*8 +#define SAVED_R3 1*8 +#define SAVED_R4 2*8 +#define SAVED_R5 3*8 +#define SAVED_R18 4*8 +#define SAVED_R6 5*8 +#define SAVED_TR0 6*8 + +/* These are the registers saved in the TLB path that aren't saved in the first + level of the normal one. */ +#define TLB_SAVED_R25 7*8 +#define TLB_SAVED_TR1 8*8 +#define TLB_SAVED_TR2 9*8 +#define TLB_SAVED_TR3 10*8 +#define TLB_SAVED_TR4 11*8 +/* Save R0/R1 : PT-migrating compiler currently dishounours -ffixed-r0 and -ffixed-r1 causing + breakage otherwise. */ +#define TLB_SAVED_R0 12*8 +#define TLB_SAVED_R1 13*8 + +#define CLI() \ + getcon SR, r6; \ + ori r6, 0xf0, r6; \ + putcon r6, SR; + +#define STI() \ + getcon SR, r6; \ + andi r6, ~0xf0, r6; \ + putcon r6, SR; + +#ifdef CONFIG_PREEMPT +# define preempt_stop() CLI() +#else +# define preempt_stop() +# define resume_kernel restore_all +#endif + + .section .data, "aw" + +#define FAST_TLBMISS_STACK_CACHELINES 4 +#define FAST_TLBMISS_STACK_QUADWORDS (4*FAST_TLBMISS_STACK_CACHELINES) + +/* Register back-up area for all exceptions */ + .balign 32 + /* Allow for 16 quadwords to be pushed by fast tlbmiss handling + * register saves etc. */ + .fill FAST_TLBMISS_STACK_QUADWORDS, 8, 0x0 +/* This is 32 byte aligned by construction */ +/* Register back-up area for all exceptions */ +reg_save_area: + .quad 0 + .quad 0 + .quad 0 + .quad 0 + + .quad 0 + .quad 0 + .quad 0 + .quad 0 + + .quad 0 + .quad 0 + .quad 0 + .quad 0 + + .quad 0 + .quad 0 + +/* Save area for RESVEC exceptions. We cannot use reg_save_area because of + * reentrancy. Note this area may be accessed via physical address. + * Align so this fits a whole single cache line, for ease of purging. + */ + .balign 32,0,32 +resvec_save_area: + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .balign 32,0,32 + +/* Jump table of 3rd level handlers */ +trap_jtable: + .long do_exception_error /* 0x000 */ + .long do_exception_error /* 0x020 */ +#ifdef CONFIG_MMU + .long tlb_miss_load /* 0x040 */ + .long tlb_miss_store /* 0x060 */ +#else + .long do_exception_error + .long do_exception_error +#endif + ! ARTIFICIAL pseudo-EXPEVT setting + .long do_debug_interrupt /* 0x080 */ +#ifdef CONFIG_MMU + .long tlb_miss_load /* 0x0A0 */ + .long tlb_miss_store /* 0x0C0 */ +#else + .long do_exception_error + .long do_exception_error +#endif + .long do_address_error_load /* 0x0E0 */ + .long do_address_error_store /* 0x100 */ +#ifdef CONFIG_SH_FPU + .long do_fpu_error /* 0x120 */ +#else + .long do_exception_error /* 0x120 */ +#endif + .long do_exception_error /* 0x140 */ + .long system_call /* 0x160 */ + .long do_reserved_inst /* 0x180 */ + .long do_illegal_slot_inst /* 0x1A0 */ + .long do_exception_error /* 0x1C0 - NMI */ + .long do_exception_error /* 0x1E0 */ + .rept 15 + .long do_IRQ /* 0x200 - 0x3C0 */ + .endr + .long do_exception_error /* 0x3E0 */ + .rept 32 + .long do_IRQ /* 0x400 - 0x7E0 */ + .endr + .long fpu_error_or_IRQA /* 0x800 */ + .long fpu_error_or_IRQB /* 0x820 */ + .long do_IRQ /* 0x840 */ + .long do_IRQ /* 0x860 */ + .rept 6 + .long do_exception_error /* 0x880 - 0x920 */ + .endr + .long breakpoint_trap_handler /* 0x940 */ + .long do_exception_error /* 0x960 */ + .long do_single_step /* 0x980 */ + + .rept 3 + .long do_exception_error /* 0x9A0 - 0x9E0 */ + .endr + .long do_IRQ /* 0xA00 */ + .long do_IRQ /* 0xA20 */ +#ifdef CONFIG_MMU + .long itlb_miss_or_IRQ /* 0xA40 */ +#else + .long do_IRQ +#endif + .long do_IRQ /* 0xA60 */ + .long do_IRQ /* 0xA80 */ +#ifdef CONFIG_MMU + .long itlb_miss_or_IRQ /* 0xAA0 */ +#else + .long do_IRQ +#endif + .long do_exception_error /* 0xAC0 */ + .long do_address_error_exec /* 0xAE0 */ + .rept 8 + .long do_exception_error /* 0xB00 - 0xBE0 */ + .endr + .rept 18 + .long do_IRQ /* 0xC00 - 0xE20 */ + .endr + + .section .text64, "ax" + +/* + * --- Exception/Interrupt/Event Handling Section + */ + +/* + * VBR and RESVEC blocks. + * + * First level handler for VBR-based exceptions. + * + * To avoid waste of space, align to the maximum text block size. + * This is assumed to be at most 128 bytes or 32 instructions. + * DO NOT EXCEED 32 instructions on the first level handlers ! + * + * Also note that RESVEC is contained within the VBR block + * where the room left (1KB - TEXT_SIZE) allows placing + * the RESVEC block (at most 512B + TEXT_SIZE). + * + * So first (and only) level handler for RESVEC-based exceptions. + * + * Where the fault/interrupt is handled (not_a_tlb_miss, tlb_miss + * and interrupt) we are a lot tight with register space until + * saving onto the stack frame, which is done in handle_exception(). + * + */ + +#define TEXT_SIZE 128 +#define BLOCK_SIZE 1664 /* Dynamic check, 13*128 */ + + .balign TEXT_SIZE +LVBR_block: + .space 256, 0 /* Power-on class handler, */ + /* not required here */ +not_a_tlb_miss: + synco /* TAKum03020 (but probably a good idea anyway.) */ + /* Save original stack pointer into KCR1 */ + putcon SP, KCR1 + + /* Save other original registers into reg_save_area */ + movi reg_save_area, SP + st.q SP, SAVED_R2, r2 + st.q SP, SAVED_R3, r3 + st.q SP, SAVED_R4, r4 + st.q SP, SAVED_R5, r5 + st.q SP, SAVED_R6, r6 + st.q SP, SAVED_R18, r18 + gettr tr0, r3 + st.q SP, SAVED_TR0, r3 + + /* Set args for Non-debug, Not a TLB miss class handler */ + getcon EXPEVT, r2 + movi ret_from_exception, r3 + ori r3, 1, r3 + movi EVENT_FAULT_NOT_TLB, r4 + or SP, ZERO, r5 + getcon KCR1, SP + pta handle_exception, tr0 + blink tr0, ZERO + + .balign 256 + ! VBR+0x200 + nop + .balign 256 + ! VBR+0x300 + nop + .balign 256 + /* + * Instead of the natural .balign 1024 place RESVEC here + * respecting the final 1KB alignment. + */ + .balign TEXT_SIZE + /* + * Instead of '.space 1024-TEXT_SIZE' place the RESVEC + * block making sure the final alignment is correct. + */ +#ifdef CONFIG_MMU +tlb_miss: + synco /* TAKum03020 (but probably a good idea anyway.) */ + putcon SP, KCR1 + movi reg_save_area, SP + /* SP is guaranteed 32-byte aligned. */ + st.q SP, TLB_SAVED_R0 , r0 + st.q SP, TLB_SAVED_R1 , r1 + st.q SP, SAVED_R2 , r2 + st.q SP, SAVED_R3 , r3 + st.q SP, SAVED_R4 , r4 + st.q SP, SAVED_R5 , r5 + st.q SP, SAVED_R6 , r6 + st.q SP, SAVED_R18, r18 + + /* Save R25 for safety; as/ld may want to use it to achieve the call to + * the code in mm/tlbmiss.c */ + st.q SP, TLB_SAVED_R25, r25 + gettr tr0, r2 + gettr tr1, r3 + gettr tr2, r4 + gettr tr3, r5 + gettr tr4, r18 + st.q SP, SAVED_TR0 , r2 + st.q SP, TLB_SAVED_TR1 , r3 + st.q SP, TLB_SAVED_TR2 , r4 + st.q SP, TLB_SAVED_TR3 , r5 + st.q SP, TLB_SAVED_TR4 , r18 + + pt do_fast_page_fault, tr0 + getcon SSR, r2 + getcon EXPEVT, r3 + getcon TEA, r4 + shlri r2, 30, r2 + andi r2, 1, r2 /* r2 = SSR.MD */ + blink tr0, LINK + + pt fixup_to_invoke_general_handler, tr1 + + /* If the fast path handler fixed the fault, just drop through quickly + to the restore code right away to return to the excepting context. + */ + bnei/u r2, 0, tr1 + +fast_tlb_miss_restore: + ld.q SP, SAVED_TR0, r2 + ld.q SP, TLB_SAVED_TR1, r3 + ld.q SP, TLB_SAVED_TR2, r4 + + ld.q SP, TLB_SAVED_TR3, r5 + ld.q SP, TLB_SAVED_TR4, r18 + + ptabs r2, tr0 + ptabs r3, tr1 + ptabs r4, tr2 + ptabs r5, tr3 + ptabs r18, tr4 + + ld.q SP, TLB_SAVED_R0, r0 + ld.q SP, TLB_SAVED_R1, r1 + ld.q SP, SAVED_R2, r2 + ld.q SP, SAVED_R3, r3 + ld.q SP, SAVED_R4, r4 + ld.q SP, SAVED_R5, r5 + ld.q SP, SAVED_R6, r6 + ld.q SP, SAVED_R18, r18 + ld.q SP, TLB_SAVED_R25, r25 + + getcon KCR1, SP + rte + nop /* for safety, in case the code is run on sh5-101 cut1.x */ + +fixup_to_invoke_general_handler: + + /* OK, new method. Restore stuff that's not expected to get saved into + the 'first-level' reg save area, then just fall through to setting + up the registers and calling the second-level handler. */ + + /* 2nd level expects r2,3,4,5,6,18,tr0 to be saved. So we must restore + r25,tr1-4 and save r6 to get into the right state. */ + + ld.q SP, TLB_SAVED_TR1, r3 + ld.q SP, TLB_SAVED_TR2, r4 + ld.q SP, TLB_SAVED_TR3, r5 + ld.q SP, TLB_SAVED_TR4, r18 + ld.q SP, TLB_SAVED_R25, r25 + + ld.q SP, TLB_SAVED_R0, r0 + ld.q SP, TLB_SAVED_R1, r1 + + ptabs/u r3, tr1 + ptabs/u r4, tr2 + ptabs/u r5, tr3 + ptabs/u r18, tr4 + + /* Set args for Non-debug, TLB miss class handler */ + getcon EXPEVT, r2 + movi ret_from_exception, r3 + ori r3, 1, r3 + movi EVENT_FAULT_TLB, r4 + or SP, ZERO, r5 + getcon KCR1, SP + pta handle_exception, tr0 + blink tr0, ZERO +#else /* CONFIG_MMU */ + .balign 256 +#endif + +/* NB TAKE GREAT CARE HERE TO ENSURE THAT THE INTERRUPT CODE + DOES END UP AT VBR+0x600 */ + nop + nop + nop + nop + nop + nop + + .balign 256 + /* VBR + 0x600 */ + +interrupt: + synco /* TAKum03020 (but probably a good idea anyway.) */ + /* Save original stack pointer into KCR1 */ + putcon SP, KCR1 + + /* Save other original registers into reg_save_area */ + movi reg_save_area, SP + st.q SP, SAVED_R2, r2 + st.q SP, SAVED_R3, r3 + st.q SP, SAVED_R4, r4 + st.q SP, SAVED_R5, r5 + st.q SP, SAVED_R6, r6 + st.q SP, SAVED_R18, r18 + gettr tr0, r3 + st.q SP, SAVED_TR0, r3 + + /* Set args for interrupt class handler */ + getcon INTEVT, r2 + movi ret_from_irq, r3 + ori r3, 1, r3 + movi EVENT_INTERRUPT, r4 + or SP, ZERO, r5 + getcon KCR1, SP + pta handle_exception, tr0 + blink tr0, ZERO + .balign TEXT_SIZE /* let's waste the bare minimum */ + +LVBR_block_end: /* Marker. Used for total checking */ + + .balign 256 +LRESVEC_block: + /* Panic handler. Called with MMU off. Possible causes/actions: + * - Reset: Jump to program start. + * - Single Step: Turn off Single Step & return. + * - Others: Call panic handler, passing PC as arg. + * (this may need to be extended...) + */ +reset_or_panic: + synco /* TAKum03020 (but probably a good idea anyway.) */ + putcon SP, DCR + /* First save r0-1 and tr0, as we need to use these */ + movi resvec_save_area-CONFIG_PAGE_OFFSET, SP + st.q SP, 0, r0 + st.q SP, 8, r1 + gettr tr0, r0 + st.q SP, 32, r0 + + /* Check cause */ + getcon EXPEVT, r0 + movi RESET_CAUSE, r1 + sub r1, r0, r1 /* r1=0 if reset */ + movi _stext-CONFIG_PAGE_OFFSET, r0 + ori r0, 1, r0 + ptabs r0, tr0 + beqi r1, 0, tr0 /* Jump to start address if reset */ + + getcon EXPEVT, r0 + movi DEBUGSS_CAUSE, r1 + sub r1, r0, r1 /* r1=0 if single step */ + pta single_step_panic, tr0 + beqi r1, 0, tr0 /* jump if single step */ + + /* Now jump to where we save the registers. */ + movi panic_stash_regs-CONFIG_PAGE_OFFSET, r1 + ptabs r1, tr0 + blink tr0, r63 + +single_step_panic: + /* We are in a handler with Single Step set. We need to resume the + * handler, by turning on MMU & turning off Single Step. */ + getcon SSR, r0 + movi SR_MMU, r1 + or r0, r1, r0 + movi ~SR_SS, r1 + and r0, r1, r0 + putcon r0, SSR + /* Restore EXPEVT, as the rte won't do this */ + getcon PEXPEVT, r0 + putcon r0, EXPEVT + /* Restore regs */ + ld.q SP, 32, r0 + ptabs r0, tr0 + ld.q SP, 0, r0 + ld.q SP, 8, r1 + getcon DCR, SP + synco + rte + + + .balign 256 +debug_exception: + synco /* TAKum03020 (but probably a good idea anyway.) */ + /* + * Single step/software_break_point first level handler. + * Called with MMU off, so the first thing we do is enable it + * by doing an rte with appropriate SSR. + */ + putcon SP, DCR + /* Save SSR & SPC, together with R0 & R1, as we need to use 2 regs. */ + movi resvec_save_area-CONFIG_PAGE_OFFSET, SP + + /* With the MMU off, we are bypassing the cache, so purge any + * data that will be made stale by the following stores. + */ + ocbp SP, 0 + synco + + st.q SP, 0, r0 + st.q SP, 8, r1 + getcon SPC, r0 + st.q SP, 16, r0 + getcon SSR, r0 + st.q SP, 24, r0 + + /* Enable MMU, block exceptions, set priv mode, disable single step */ + movi SR_MMU | SR_BL | SR_MD, r1 + or r0, r1, r0 + movi ~SR_SS, r1 + and r0, r1, r0 + putcon r0, SSR + /* Force control to debug_exception_2 when rte is executed */ + movi debug_exeception_2, r0 + ori r0, 1, r0 /* force SHmedia, just in case */ + putcon r0, SPC + getcon DCR, SP + synco + rte +debug_exeception_2: + /* Restore saved regs */ + putcon SP, KCR1 + movi resvec_save_area, SP + ld.q SP, 24, r0 + putcon r0, SSR + ld.q SP, 16, r0 + putcon r0, SPC + ld.q SP, 0, r0 + ld.q SP, 8, r1 + + /* Save other original registers into reg_save_area */ + movi reg_save_area, SP + st.q SP, SAVED_R2, r2 + st.q SP, SAVED_R3, r3 + st.q SP, SAVED_R4, r4 + st.q SP, SAVED_R5, r5 + st.q SP, SAVED_R6, r6 + st.q SP, SAVED_R18, r18 + gettr tr0, r3 + st.q SP, SAVED_TR0, r3 + + /* Set args for debug class handler */ + getcon EXPEVT, r2 + movi ret_from_exception, r3 + ori r3, 1, r3 + movi EVENT_DEBUG, r4 + or SP, ZERO, r5 + getcon KCR1, SP + pta handle_exception, tr0 + blink tr0, ZERO + + .balign 256 +debug_interrupt: + /* !!! WE COME HERE IN REAL MODE !!! */ + /* Hook-up debug interrupt to allow various debugging options to be + * hooked into its handler. */ + /* Save original stack pointer into KCR1 */ + synco + putcon SP, KCR1 + movi resvec_save_area-CONFIG_PAGE_OFFSET, SP + ocbp SP, 0 + ocbp SP, 32 + synco + + /* Save other original registers into reg_save_area thru real addresses */ + st.q SP, SAVED_R2, r2 + st.q SP, SAVED_R3, r3 + st.q SP, SAVED_R4, r4 + st.q SP, SAVED_R5, r5 + st.q SP, SAVED_R6, r6 + st.q SP, SAVED_R18, r18 + gettr tr0, r3 + st.q SP, SAVED_TR0, r3 + + /* move (spc,ssr)->(pspc,pssr). The rte will shift + them back again, so that they look like the originals + as far as the real handler code is concerned. */ + getcon spc, r6 + putcon r6, pspc + getcon ssr, r6 + putcon r6, pssr + + ! construct useful SR for handle_exception + movi 3, r6 + shlli r6, 30, r6 + getcon sr, r18 + or r18, r6, r6 + putcon r6, ssr + + ! SSR is now the current SR with the MD and MMU bits set + ! i.e. the rte will switch back to priv mode and put + ! the mmu back on + + ! construct spc + movi handle_exception, r18 + ori r18, 1, r18 ! for safety (do we need this?) + putcon r18, spc + + /* Set args for Non-debug, Not a TLB miss class handler */ + + ! EXPEVT==0x80 is unused, so 'steal' this value to put the + ! debug interrupt handler in the vectoring table + movi 0x80, r2 + movi ret_from_exception, r3 + ori r3, 1, r3 + movi EVENT_FAULT_NOT_TLB, r4 + + or SP, ZERO, r5 + movi CONFIG_PAGE_OFFSET, r6 + add r6, r5, r5 + getcon KCR1, SP + + synco ! for safety + rte ! -> handle_exception, switch back to priv mode again + +LRESVEC_block_end: /* Marker. Unused. */ + + .balign TEXT_SIZE + +/* + * Second level handler for VBR-based exceptions. Pre-handler. + * In common to all stack-frame sensitive handlers. + * + * Inputs: + * (KCR0) Current [current task union] + * (KCR1) Original SP + * (r2) INTEVT/EXPEVT + * (r3) appropriate return address + * (r4) Event (0 = interrupt, 1 = TLB miss fault, 2 = Not TLB miss fault, 3=debug) + * (r5) Pointer to reg_save_area + * (SP) Original SP + * + * Available registers: + * (r6) + * (r18) + * (tr0) + * + */ +handle_exception: + /* Common 2nd level handler. */ + + /* First thing we need an appropriate stack pointer */ + getcon SSR, r6 + shlri r6, 30, r6 + andi r6, 1, r6 + pta stack_ok, tr0 + bne r6, ZERO, tr0 /* Original stack pointer is fine */ + + /* Set stack pointer for user fault */ + getcon KCR0, SP + movi THREAD_SIZE, r6 /* Point to the end */ + add SP, r6, SP + +stack_ok: + +/* DEBUG : check for underflow/overflow of the kernel stack */ + pta no_underflow, tr0 + getcon KCR0, r6 + movi 1024, r18 + add r6, r18, r6 + bge SP, r6, tr0 ! ? below 1k from bottom of stack : danger zone + +/* Just panic to cause a crash. */ +bad_sp: + ld.b r63, 0, r6 + nop + +no_underflow: + pta bad_sp, tr0 + getcon kcr0, r6 + movi THREAD_SIZE, r18 + add r18, r6, r6 + bgt SP, r6, tr0 ! sp above the stack + + /* Make some room for the BASIC frame. */ + movi -(FRAME_SIZE), r6 + add SP, r6, SP + +/* Could do this with no stalling if we had another spare register, but the + code below will be OK. */ + ld.q r5, SAVED_R2, r6 + ld.q r5, SAVED_R3, r18 + st.q SP, FRAME_R(2), r6 + ld.q r5, SAVED_R4, r6 + st.q SP, FRAME_R(3), r18 + ld.q r5, SAVED_R5, r18 + st.q SP, FRAME_R(4), r6 + ld.q r5, SAVED_R6, r6 + st.q SP, FRAME_R(5), r18 + ld.q r5, SAVED_R18, r18 + st.q SP, FRAME_R(6), r6 + ld.q r5, SAVED_TR0, r6 + st.q SP, FRAME_R(18), r18 + st.q SP, FRAME_T(0), r6 + + /* Keep old SP around */ + getcon KCR1, r6 + + /* Save the rest of the general purpose registers */ + st.q SP, FRAME_R(0), r0 + st.q SP, FRAME_R(1), r1 + st.q SP, FRAME_R(7), r7 + st.q SP, FRAME_R(8), r8 + st.q SP, FRAME_R(9), r9 + st.q SP, FRAME_R(10), r10 + st.q SP, FRAME_R(11), r11 + st.q SP, FRAME_R(12), r12 + st.q SP, FRAME_R(13), r13 + st.q SP, FRAME_R(14), r14 + + /* SP is somewhere else */ + st.q SP, FRAME_R(15), r6 + + st.q SP, FRAME_R(16), r16 + st.q SP, FRAME_R(17), r17 + /* r18 is saved earlier. */ + st.q SP, FRAME_R(19), r19 + st.q SP, FRAME_R(20), r20 + st.q SP, FRAME_R(21), r21 + st.q SP, FRAME_R(22), r22 + st.q SP, FRAME_R(23), r23 + st.q SP, FRAME_R(24), r24 + st.q SP, FRAME_R(25), r25 + st.q SP, FRAME_R(26), r26 + st.q SP, FRAME_R(27), r27 + st.q SP, FRAME_R(28), r28 + st.q SP, FRAME_R(29), r29 + st.q SP, FRAME_R(30), r30 + st.q SP, FRAME_R(31), r31 + st.q SP, FRAME_R(32), r32 + st.q SP, FRAME_R(33), r33 + st.q SP, FRAME_R(34), r34 + st.q SP, FRAME_R(35), r35 + st.q SP, FRAME_R(36), r36 + st.q SP, FRAME_R(37), r37 + st.q SP, FRAME_R(38), r38 + st.q SP, FRAME_R(39), r39 + st.q SP, FRAME_R(40), r40 + st.q SP, FRAME_R(41), r41 + st.q SP, FRAME_R(42), r42 + st.q SP, FRAME_R(43), r43 + st.q SP, FRAME_R(44), r44 + st.q SP, FRAME_R(45), r45 + st.q SP, FRAME_R(46), r46 + st.q SP, FRAME_R(47), r47 + st.q SP, FRAME_R(48), r48 + st.q SP, FRAME_R(49), r49 + st.q SP, FRAME_R(50), r50 + st.q SP, FRAME_R(51), r51 + st.q SP, FRAME_R(52), r52 + st.q SP, FRAME_R(53), r53 + st.q SP, FRAME_R(54), r54 + st.q SP, FRAME_R(55), r55 + st.q SP, FRAME_R(56), r56 + st.q SP, FRAME_R(57), r57 + st.q SP, FRAME_R(58), r58 + st.q SP, FRAME_R(59), r59 + st.q SP, FRAME_R(60), r60 + st.q SP, FRAME_R(61), r61 + st.q SP, FRAME_R(62), r62 + + /* + * Save the S* registers. + */ + getcon SSR, r61 + st.q SP, FRAME_S(FSSR), r61 + getcon SPC, r62 + st.q SP, FRAME_S(FSPC), r62 + movi -1, r62 /* Reset syscall_nr */ + st.q SP, FRAME_S(FSYSCALL_ID), r62 + + /* Save the rest of the target registers */ + gettr tr1, r6 + st.q SP, FRAME_T(1), r6 + gettr tr2, r6 + st.q SP, FRAME_T(2), r6 + gettr tr3, r6 + st.q SP, FRAME_T(3), r6 + gettr tr4, r6 + st.q SP, FRAME_T(4), r6 + gettr tr5, r6 + st.q SP, FRAME_T(5), r6 + gettr tr6, r6 + st.q SP, FRAME_T(6), r6 + gettr tr7, r6 + st.q SP, FRAME_T(7), r6 + + ! setup FP so that unwinder can wind back through nested kernel mode + ! exceptions + add SP, ZERO, r14 + + /* For syscall and debug race condition, get TRA now */ + getcon TRA, r5 + + /* We are in a safe position to turn SR.BL off, but set IMASK=0xf + * Also set FD, to catch FPU usage in the kernel. + * + * benedict.gaster@superh.com 29/07/2002 + * + * On all SH5-101 revisions it is unsafe to raise the IMASK and at the + * same time change BL from 1->0, as any pending interrupt of a level + * higher than he previous value of IMASK will leak through and be + * taken unexpectedly. + * + * To avoid this we raise the IMASK and then issue another PUTCON to + * enable interrupts. + */ + getcon SR, r6 + movi SR_IMASK | SR_FD, r7 + or r6, r7, r6 + putcon r6, SR + movi SR_UNBLOCK_EXC, r7 + and r6, r7, r6 + putcon r6, SR + + + /* Now call the appropriate 3rd level handler */ + or r3, ZERO, LINK + movi trap_jtable, r3 + shlri r2, 3, r2 + ldx.l r2, r3, r3 + shlri r2, 2, r2 + ptabs r3, tr0 + or SP, ZERO, r3 + blink tr0, ZERO + +/* + * Second level handler for VBR-based exceptions. Post-handlers. + * + * Post-handlers for interrupts (ret_from_irq), exceptions + * (ret_from_exception) and common reentrance doors (restore_all + * to get back to the original context, ret_from_syscall loop to + * check kernel exiting). + * + * ret_with_reschedule and work_notifysig are an inner lables of + * the ret_from_syscall loop. + * + * In common to all stack-frame sensitive handlers. + * + * Inputs: + * (SP) struct pt_regs *, original register's frame pointer (basic) + * + */ + .global ret_from_irq +ret_from_irq: + ld.q SP, FRAME_S(FSSR), r6 + shlri r6, 30, r6 + andi r6, 1, r6 + pta resume_kernel, tr0 + bne r6, ZERO, tr0 /* no further checks */ + STI() + pta ret_with_reschedule, tr0 + blink tr0, ZERO /* Do not check softirqs */ + + .global ret_from_exception +ret_from_exception: + preempt_stop() + + ld.q SP, FRAME_S(FSSR), r6 + shlri r6, 30, r6 + andi r6, 1, r6 + pta resume_kernel, tr0 + bne r6, ZERO, tr0 /* no further checks */ + + /* Check softirqs */ + +#ifdef CONFIG_PREEMPT + pta ret_from_syscall, tr0 + blink tr0, ZERO + +resume_kernel: + CLI() + + pta restore_all, tr0 + + getcon KCR0, r6 + ld.l r6, TI_PRE_COUNT, r7 + beq/u r7, ZERO, tr0 + +need_resched: + ld.l r6, TI_FLAGS, r7 + movi (1 << TIF_NEED_RESCHED), r8 + and r8, r7, r8 + bne r8, ZERO, tr0 + + getcon SR, r7 + andi r7, 0xf0, r7 + bne r7, ZERO, tr0 + + movi preempt_schedule_irq, r7 + ori r7, 1, r7 + ptabs r7, tr1 + blink tr1, LINK + + pta need_resched, tr1 + blink tr1, ZERO +#endif + + .global ret_from_syscall +ret_from_syscall: + +ret_with_reschedule: + getcon KCR0, r6 ! r6 contains current_thread_info + ld.l r6, TI_FLAGS, r7 ! r7 contains current_thread_info->flags + + movi _TIF_NEED_RESCHED, r8 + and r8, r7, r8 + pta work_resched, tr0 + bne r8, ZERO, tr0 + + pta restore_all, tr1 + + movi (_TIF_SIGPENDING|_TIF_NOTIFY_RESUME), r8 + and r8, r7, r8 + pta work_notifysig, tr0 + bne r8, ZERO, tr0 + + blink tr1, ZERO + +work_resched: + pta ret_from_syscall, tr0 + gettr tr0, LINK + movi schedule, r6 + ptabs r6, tr0 + blink tr0, ZERO /* Call schedule(), return on top */ + +work_notifysig: + gettr tr1, LINK + + movi do_notify_resume, r6 + ptabs r6, tr0 + or SP, ZERO, r2 + or r7, ZERO, r3 + blink tr0, LINK /* Call do_notify_resume(regs, current_thread_info->flags), return here */ + +restore_all: + /* Do prefetches */ + + ld.q SP, FRAME_T(0), r6 + ld.q SP, FRAME_T(1), r7 + ld.q SP, FRAME_T(2), r8 + ld.q SP, FRAME_T(3), r9 + ptabs r6, tr0 + ptabs r7, tr1 + ptabs r8, tr2 + ptabs r9, tr3 + ld.q SP, FRAME_T(4), r6 + ld.q SP, FRAME_T(5), r7 + ld.q SP, FRAME_T(6), r8 + ld.q SP, FRAME_T(7), r9 + ptabs r6, tr4 + ptabs r7, tr5 + ptabs r8, tr6 + ptabs r9, tr7 + + ld.q SP, FRAME_R(0), r0 + ld.q SP, FRAME_R(1), r1 + ld.q SP, FRAME_R(2), r2 + ld.q SP, FRAME_R(3), r3 + ld.q SP, FRAME_R(4), r4 + ld.q SP, FRAME_R(5), r5 + ld.q SP, FRAME_R(6), r6 + ld.q SP, FRAME_R(7), r7 + ld.q SP, FRAME_R(8), r8 + ld.q SP, FRAME_R(9), r9 + ld.q SP, FRAME_R(10), r10 + ld.q SP, FRAME_R(11), r11 + ld.q SP, FRAME_R(12), r12 + ld.q SP, FRAME_R(13), r13 + ld.q SP, FRAME_R(14), r14 + + ld.q SP, FRAME_R(16), r16 + ld.q SP, FRAME_R(17), r17 + ld.q SP, FRAME_R(18), r18 + ld.q SP, FRAME_R(19), r19 + ld.q SP, FRAME_R(20), r20 + ld.q SP, FRAME_R(21), r21 + ld.q SP, FRAME_R(22), r22 + ld.q SP, FRAME_R(23), r23 + ld.q SP, FRAME_R(24), r24 + ld.q SP, FRAME_R(25), r25 + ld.q SP, FRAME_R(26), r26 + ld.q SP, FRAME_R(27), r27 + ld.q SP, FRAME_R(28), r28 + ld.q SP, FRAME_R(29), r29 + ld.q SP, FRAME_R(30), r30 + ld.q SP, FRAME_R(31), r31 + ld.q SP, FRAME_R(32), r32 + ld.q SP, FRAME_R(33), r33 + ld.q SP, FRAME_R(34), r34 + ld.q SP, FRAME_R(35), r35 + ld.q SP, FRAME_R(36), r36 + ld.q SP, FRAME_R(37), r37 + ld.q SP, FRAME_R(38), r38 + ld.q SP, FRAME_R(39), r39 + ld.q SP, FRAME_R(40), r40 + ld.q SP, FRAME_R(41), r41 + ld.q SP, FRAME_R(42), r42 + ld.q SP, FRAME_R(43), r43 + ld.q SP, FRAME_R(44), r44 + ld.q SP, FRAME_R(45), r45 + ld.q SP, FRAME_R(46), r46 + ld.q SP, FRAME_R(47), r47 + ld.q SP, FRAME_R(48), r48 + ld.q SP, FRAME_R(49), r49 + ld.q SP, FRAME_R(50), r50 + ld.q SP, FRAME_R(51), r51 + ld.q SP, FRAME_R(52), r52 + ld.q SP, FRAME_R(53), r53 + ld.q SP, FRAME_R(54), r54 + ld.q SP, FRAME_R(55), r55 + ld.q SP, FRAME_R(56), r56 + ld.q SP, FRAME_R(57), r57 + ld.q SP, FRAME_R(58), r58 + + getcon SR, r59 + movi SR_BLOCK_EXC, r60 + or r59, r60, r59 + putcon r59, SR /* SR.BL = 1, keep nesting out */ + ld.q SP, FRAME_S(FSSR), r61 + ld.q SP, FRAME_S(FSPC), r62 + movi SR_ASID_MASK, r60 + and r59, r60, r59 + andc r61, r60, r61 /* Clear out older ASID */ + or r59, r61, r61 /* Retain current ASID */ + putcon r61, SSR + putcon r62, SPC + + /* Ignore FSYSCALL_ID */ + + ld.q SP, FRAME_R(59), r59 + ld.q SP, FRAME_R(60), r60 + ld.q SP, FRAME_R(61), r61 + ld.q SP, FRAME_R(62), r62 + + /* Last touch */ + ld.q SP, FRAME_R(15), SP + rte + nop + +/* + * Third level handlers for VBR-based exceptions. Adapting args to + * and/or deflecting to fourth level handlers. + * + * Fourth level handlers interface. + * Most are C-coded handlers directly pointed by the trap_jtable. + * (Third = Fourth level) + * Inputs: + * (r2) fault/interrupt code, entry number (e.g. NMI = 14, + * IRL0-3 (0000) = 16, RTLBMISS = 2, SYSCALL = 11, etc ...) + * (r3) struct pt_regs *, original register's frame pointer + * (r4) Event (0 = interrupt, 1 = TLB miss fault, 2 = Not TLB miss fault) + * (r5) TRA control register (for syscall/debug benefit only) + * (LINK) return address + * (SP) = r3 + * + * Kernel TLB fault handlers will get a slightly different interface. + * (r2) struct pt_regs *, original register's frame pointer + * (r3) page fault error code (see asm/thread_info.h) + * (r4) Effective Address of fault + * (LINK) return address + * (SP) = r2 + * + * fpu_error_or_IRQ? is a helper to deflect to the right cause. + * + */ +#ifdef CONFIG_MMU +tlb_miss_load: + or SP, ZERO, r2 + or ZERO, ZERO, r3 /* Read */ + getcon TEA, r4 + pta call_do_page_fault, tr0 + beq ZERO, ZERO, tr0 + +tlb_miss_store: + or SP, ZERO, r2 + movi FAULT_CODE_WRITE, r3 /* Write */ + getcon TEA, r4 + pta call_do_page_fault, tr0 + beq ZERO, ZERO, tr0 + +itlb_miss_or_IRQ: + pta its_IRQ, tr0 + beqi/u r4, EVENT_INTERRUPT, tr0 + + /* ITLB miss */ + or SP, ZERO, r2 + movi FAULT_CODE_ITLB, r3 + getcon TEA, r4 + /* Fall through */ + +call_do_page_fault: + movi do_page_fault, r6 + ptabs r6, tr0 + blink tr0, ZERO +#endif /* CONFIG_MMU */ + +fpu_error_or_IRQA: + pta its_IRQ, tr0 + beqi/l r4, EVENT_INTERRUPT, tr0 +#ifdef CONFIG_SH_FPU + movi fpu_state_restore_trap_handler, r6 +#else + movi do_exception_error, r6 +#endif + ptabs r6, tr0 + blink tr0, ZERO + +fpu_error_or_IRQB: + pta its_IRQ, tr0 + beqi/l r4, EVENT_INTERRUPT, tr0 +#ifdef CONFIG_SH_FPU + movi fpu_state_restore_trap_handler, r6 +#else + movi do_exception_error, r6 +#endif + ptabs r6, tr0 + blink tr0, ZERO + +its_IRQ: + movi do_IRQ, r6 + ptabs r6, tr0 + blink tr0, ZERO + +/* + * system_call/unknown_trap third level handler: + * + * Inputs: + * (r2) fault/interrupt code, entry number (TRAP = 11) + * (r3) struct pt_regs *, original register's frame pointer + * (r4) Not used. Event (0=interrupt, 1=TLB miss fault, 2=Not TLB miss fault) + * (r5) TRA Control Reg (0x00xyzzzz: x=1 SYSCALL, y = #args, z=nr) + * (SP) = r3 + * (LINK) return address: ret_from_exception + * (*r3) Syscall parms: SC#, arg0, arg1, ..., arg5 in order (Saved r2/r7) + * + * Outputs: + * (*r3) Syscall reply (Saved r2) + * (LINK) In case of syscall only it can be scrapped. + * Common second level post handler will be ret_from_syscall. + * Common (non-trace) exit point to that is syscall_ret (saving + * result to r2). Common bad exit point is syscall_bad (returning + * ENOSYS then saved to r2). + * + */ + +unknown_trap: + /* Unknown Trap or User Trace */ + movi do_unknown_trapa, r6 + ptabs r6, tr0 + ld.q r3, FRAME_R(9), r2 /* r2 = #arg << 16 | syscall # */ + andi r2, 0x1ff, r2 /* r2 = syscall # */ + blink tr0, LINK + + pta syscall_ret, tr0 + blink tr0, ZERO + + /* New syscall implementation*/ +system_call: + pta unknown_trap, tr0 + or r5, ZERO, r4 /* TRA (=r5) -> r4 */ + shlri r4, 20, r4 + bnei r4, 1, tr0 /* unknown_trap if not 0x1yzzzz */ + + /* It's a system call */ + st.q r3, FRAME_S(FSYSCALL_ID), r5 /* ID (0x1yzzzz) -> stack */ + andi r5, 0x1ff, r5 /* syscall # -> r5 */ + + STI() + + pta syscall_allowed, tr0 + movi NR_syscalls - 1, r4 /* Last valid */ + bgeu/l r4, r5, tr0 + +syscall_bad: + /* Return ENOSYS ! */ + movi -(ENOSYS), r2 /* Fall-through */ + + .global syscall_ret +syscall_ret: + st.q SP, FRAME_R(9), r2 /* Expecting SP back to BASIC frame */ + ld.q SP, FRAME_S(FSPC), r2 + addi r2, 4, r2 /* Move PC, being pre-execution event */ + st.q SP, FRAME_S(FSPC), r2 + pta ret_from_syscall, tr0 + blink tr0, ZERO + + +/* A different return path for ret_from_fork, because we now need + * to call schedule_tail with the later kernels. Because prev is + * loaded into r2 by switch_to() means we can just call it straight away + */ + +.global ret_from_fork +ret_from_fork: + + movi schedule_tail,r5 + ori r5, 1, r5 + ptabs r5, tr0 + blink tr0, LINK + + ld.q SP, FRAME_S(FSPC), r2 + addi r2, 4, r2 /* Move PC, being pre-execution event */ + st.q SP, FRAME_S(FSPC), r2 + pta ret_from_syscall, tr0 + blink tr0, ZERO + +.global ret_from_kernel_thread +ret_from_kernel_thread: + + movi schedule_tail,r5 + ori r5, 1, r5 + ptabs r5, tr0 + blink tr0, LINK + + ld.q SP, FRAME_R(2), r2 + ld.q SP, FRAME_R(3), r3 + ptabs r3, tr0 + blink tr0, LINK + + ld.q SP, FRAME_S(FSPC), r2 + addi r2, 4, r2 /* Move PC, being pre-execution event */ + st.q SP, FRAME_S(FSPC), r2 + pta ret_from_syscall, tr0 + blink tr0, ZERO + +syscall_allowed: + /* Use LINK to deflect the exit point, default is syscall_ret */ + pta syscall_ret, tr0 + gettr tr0, LINK + pta syscall_notrace, tr0 + + getcon KCR0, r2 + ld.l r2, TI_FLAGS, r4 + movi _TIF_WORK_SYSCALL_MASK, r6 + and r6, r4, r6 + beq/l r6, ZERO, tr0 + + /* Trace it by calling syscall_trace before and after */ + movi do_syscall_trace_enter, r4 + or SP, ZERO, r2 + ptabs r4, tr0 + blink tr0, LINK + + /* Save the retval */ + st.q SP, FRAME_R(2), r2 + + /* Reload syscall number as r5 is trashed by do_syscall_trace_enter */ + ld.q SP, FRAME_S(FSYSCALL_ID), r5 + andi r5, 0x1ff, r5 + + pta syscall_ret_trace, tr0 + gettr tr0, LINK + +syscall_notrace: + /* Now point to the appropriate 4th level syscall handler */ + movi sys_call_table, r4 + shlli r5, 2, r5 + ldx.l r4, r5, r5 + ptabs r5, tr0 + + /* Prepare original args */ + ld.q SP, FRAME_R(2), r2 + ld.q SP, FRAME_R(3), r3 + ld.q SP, FRAME_R(4), r4 + ld.q SP, FRAME_R(5), r5 + ld.q SP, FRAME_R(6), r6 + ld.q SP, FRAME_R(7), r7 + + /* And now the trick for those syscalls requiring regs * ! */ + or SP, ZERO, r8 + + /* Call it */ + blink tr0, ZERO /* LINK is already properly set */ + +syscall_ret_trace: + /* We get back here only if under trace */ + st.q SP, FRAME_R(9), r2 /* Save return value */ + + movi do_syscall_trace_leave, LINK + or SP, ZERO, r2 + ptabs LINK, tr0 + blink tr0, LINK + + /* This needs to be done after any syscall tracing */ + ld.q SP, FRAME_S(FSPC), r2 + addi r2, 4, r2 /* Move PC, being pre-execution event */ + st.q SP, FRAME_S(FSPC), r2 + + pta ret_from_syscall, tr0 + blink tr0, ZERO /* Resume normal return sequence */ + +/* + * --- Switch to running under a particular ASID and return the previous ASID value + * --- The caller is assumed to have done a cli before calling this. + * + * Input r2 : new ASID + * Output r2 : old ASID + */ + + .global switch_and_save_asid +switch_and_save_asid: + getcon sr, r0 + movi 255, r4 + shlli r4, 16, r4 /* r4 = mask to select ASID */ + and r0, r4, r3 /* r3 = shifted old ASID */ + andi r2, 255, r2 /* mask down new ASID */ + shlli r2, 16, r2 /* align new ASID against SR.ASID */ + andc r0, r4, r0 /* efface old ASID from SR */ + or r0, r2, r0 /* insert the new ASID */ + putcon r0, ssr + movi 1f, r0 + putcon r0, spc + rte + nop +1: + ptabs LINK, tr0 + shlri r3, 16, r2 /* r2 = old ASID */ + blink tr0, r63 + + .global route_to_panic_handler +route_to_panic_handler: + /* Switch to real mode, goto panic_handler, don't return. Useful for + last-chance debugging, e.g. if no output wants to go to the console. + */ + + movi panic_handler - CONFIG_PAGE_OFFSET, r1 + ptabs r1, tr0 + pta 1f, tr1 + gettr tr1, r0 + putcon r0, spc + getcon sr, r0 + movi 1, r1 + shlli r1, 31, r1 + andc r0, r1, r0 + putcon r0, ssr + rte + nop +1: /* Now in real mode */ + blink tr0, r63 + nop + + .global peek_real_address_q +peek_real_address_q: + /* Two args: + r2 : real mode address to peek + r2(out) : result quadword + + This is provided as a cheapskate way of manipulating device + registers for debugging (to avoid the need to ioremap the debug + module, and to avoid the need to ioremap the watchpoint + controller in a way that identity maps sufficient bits to avoid the + SH5-101 cut2 silicon defect). + + This code is not performance critical + */ + + add.l r2, r63, r2 /* sign extend address */ + getcon sr, r0 /* r0 = saved original SR */ + movi 1, r1 + shlli r1, 28, r1 + or r0, r1, r1 /* r0 with block bit set */ + putcon r1, sr /* now in critical section */ + movi 1, r36 + shlli r36, 31, r36 + andc r1, r36, r1 /* turn sr.mmu off in real mode section */ + + putcon r1, ssr + movi .peek0 - CONFIG_PAGE_OFFSET, r36 /* real mode target address */ + movi 1f, r37 /* virtual mode return addr */ + putcon r36, spc + + synco + rte + nop + +.peek0: /* come here in real mode, don't touch caches!! + still in critical section (sr.bl==1) */ + putcon r0, ssr + putcon r37, spc + /* Here's the actual peek. If the address is bad, all bets are now off + * what will happen (handlers invoked in real-mode = bad news) */ + ld.q r2, 0, r2 + synco + rte /* Back to virtual mode */ + nop + +1: + ptabs LINK, tr0 + blink tr0, r63 + + .global poke_real_address_q +poke_real_address_q: + /* Two args: + r2 : real mode address to poke + r3 : quadword value to write. + + This is provided as a cheapskate way of manipulating device + registers for debugging (to avoid the need to ioremap the debug + module, and to avoid the need to ioremap the watchpoint + controller in a way that identity maps sufficient bits to avoid the + SH5-101 cut2 silicon defect). + + This code is not performance critical + */ + + add.l r2, r63, r2 /* sign extend address */ + getcon sr, r0 /* r0 = saved original SR */ + movi 1, r1 + shlli r1, 28, r1 + or r0, r1, r1 /* r0 with block bit set */ + putcon r1, sr /* now in critical section */ + movi 1, r36 + shlli r36, 31, r36 + andc r1, r36, r1 /* turn sr.mmu off in real mode section */ + + putcon r1, ssr + movi .poke0-CONFIG_PAGE_OFFSET, r36 /* real mode target address */ + movi 1f, r37 /* virtual mode return addr */ + putcon r36, spc + + synco + rte + nop + +.poke0: /* come here in real mode, don't touch caches!! + still in critical section (sr.bl==1) */ + putcon r0, ssr + putcon r37, spc + /* Here's the actual poke. If the address is bad, all bets are now off + * what will happen (handlers invoked in real-mode = bad news) */ + st.q r2, 0, r3 + synco + rte /* Back to virtual mode */ + nop + +1: + ptabs LINK, tr0 + blink tr0, r63 + +#ifdef CONFIG_MMU +/* + * --- User Access Handling Section + */ + +/* + * User Access support. It all moved to non inlined Assembler + * functions in here. + * + * __kernel_size_t __copy_user(void *__to, const void *__from, + * __kernel_size_t __n) + * + * Inputs: + * (r2) target address + * (r3) source address + * (r4) size in bytes + * + * Ouputs: + * (*r2) target data + * (r2) non-copied bytes + * + * If a fault occurs on the user pointer, bail out early and return the + * number of bytes not copied in r2. + * Strategy : for large blocks, call a real memcpy function which can + * move >1 byte at a time using unaligned ld/st instructions, and can + * manipulate the cache using prefetch + alloco to improve the speed + * further. If a fault occurs in that function, just revert to the + * byte-by-byte approach used for small blocks; this is rare so the + * performance hit for that case does not matter. + * + * For small blocks it's not worth the overhead of setting up and calling + * the memcpy routine; do the copy a byte at a time. + * + */ + .global __copy_user +__copy_user: + pta __copy_user_byte_by_byte, tr1 + movi 16, r0 ! this value is a best guess, should tune it by benchmarking + bge/u r0, r4, tr1 + pta copy_user_memcpy, tr0 + addi SP, -32, SP + /* Save arguments in case we have to fix-up unhandled page fault */ + st.q SP, 0, r2 + st.q SP, 8, r3 + st.q SP, 16, r4 + st.q SP, 24, r35 ! r35 is callee-save + /* Save LINK in a register to reduce RTS time later (otherwise + ld SP,*,LINK;ptabs LINK;trn;blink trn,r63 becomes a critical path) */ + ori LINK, 0, r35 + blink tr0, LINK + + /* Copy completed normally if we get back here */ + ptabs r35, tr0 + ld.q SP, 24, r35 + /* don't restore r2-r4, pointless */ + /* set result=r2 to zero as the copy must have succeeded. */ + or r63, r63, r2 + addi SP, 32, SP + blink tr0, r63 ! RTS + + .global __copy_user_fixup +__copy_user_fixup: + /* Restore stack frame */ + ori r35, 0, LINK + ld.q SP, 24, r35 + ld.q SP, 16, r4 + ld.q SP, 8, r3 + ld.q SP, 0, r2 + addi SP, 32, SP + /* Fall through to original code, in the 'same' state we entered with */ + +/* The slow byte-by-byte method is used if the fast copy traps due to a bad + user address. In that rare case, the speed drop can be tolerated. */ +__copy_user_byte_by_byte: + pta ___copy_user_exit, tr1 + pta ___copy_user1, tr0 + beq/u r4, r63, tr1 /* early exit for zero length copy */ + sub r2, r3, r0 + addi r0, -1, r0 + +___copy_user1: + ld.b r3, 0, r5 /* Fault address 1 */ + + /* Could rewrite this to use just 1 add, but the second comes 'free' + due to load latency */ + addi r3, 1, r3 + addi r4, -1, r4 /* No real fixup required */ +___copy_user2: + stx.b r3, r0, r5 /* Fault address 2 */ + bne r4, ZERO, tr0 + +___copy_user_exit: + or r4, ZERO, r2 + ptabs LINK, tr0 + blink tr0, ZERO + +/* + * __kernel_size_t __clear_user(void *addr, __kernel_size_t size) + * + * Inputs: + * (r2) target address + * (r3) size in bytes + * + * Ouputs: + * (*r2) zero-ed target data + * (r2) non-zero-ed bytes + */ + .global __clear_user +__clear_user: + pta ___clear_user_exit, tr1 + pta ___clear_user1, tr0 + beq/u r3, r63, tr1 + +___clear_user1: + st.b r2, 0, ZERO /* Fault address */ + addi r2, 1, r2 + addi r3, -1, r3 /* No real fixup required */ + bne r3, ZERO, tr0 + +___clear_user_exit: + or r3, ZERO, r2 + ptabs LINK, tr0 + blink tr0, ZERO + +#endif /* CONFIG_MMU */ + +/* + * extern long __get_user_asm_?(void *val, long addr) + * + * Inputs: + * (r2) dest address + * (r3) source address (in User Space) + * + * Ouputs: + * (r2) -EFAULT (faulting) + * 0 (not faulting) + */ + .global __get_user_asm_b +__get_user_asm_b: + or r2, ZERO, r4 + movi -(EFAULT), r2 /* r2 = reply, no real fixup */ + +___get_user_asm_b1: + ld.b r3, 0, r5 /* r5 = data */ + st.b r4, 0, r5 + or ZERO, ZERO, r2 + +___get_user_asm_b_exit: + ptabs LINK, tr0 + blink tr0, ZERO + + + .global __get_user_asm_w +__get_user_asm_w: + or r2, ZERO, r4 + movi -(EFAULT), r2 /* r2 = reply, no real fixup */ + +___get_user_asm_w1: + ld.w r3, 0, r5 /* r5 = data */ + st.w r4, 0, r5 + or ZERO, ZERO, r2 + +___get_user_asm_w_exit: + ptabs LINK, tr0 + blink tr0, ZERO + + + .global __get_user_asm_l +__get_user_asm_l: + or r2, ZERO, r4 + movi -(EFAULT), r2 /* r2 = reply, no real fixup */ + +___get_user_asm_l1: + ld.l r3, 0, r5 /* r5 = data */ + st.l r4, 0, r5 + or ZERO, ZERO, r2 + +___get_user_asm_l_exit: + ptabs LINK, tr0 + blink tr0, ZERO + + + .global __get_user_asm_q +__get_user_asm_q: + or r2, ZERO, r4 + movi -(EFAULT), r2 /* r2 = reply, no real fixup */ + +___get_user_asm_q1: + ld.q r3, 0, r5 /* r5 = data */ + st.q r4, 0, r5 + or ZERO, ZERO, r2 + +___get_user_asm_q_exit: + ptabs LINK, tr0 + blink tr0, ZERO + +/* + * extern long __put_user_asm_?(void *pval, long addr) + * + * Inputs: + * (r2) kernel pointer to value + * (r3) dest address (in User Space) + * + * Ouputs: + * (r2) -EFAULT (faulting) + * 0 (not faulting) + */ + .global __put_user_asm_b +__put_user_asm_b: + ld.b r2, 0, r4 /* r4 = data */ + movi -(EFAULT), r2 /* r2 = reply, no real fixup */ + +___put_user_asm_b1: + st.b r3, 0, r4 + or ZERO, ZERO, r2 + +___put_user_asm_b_exit: + ptabs LINK, tr0 + blink tr0, ZERO + + + .global __put_user_asm_w +__put_user_asm_w: + ld.w r2, 0, r4 /* r4 = data */ + movi -(EFAULT), r2 /* r2 = reply, no real fixup */ + +___put_user_asm_w1: + st.w r3, 0, r4 + or ZERO, ZERO, r2 + +___put_user_asm_w_exit: + ptabs LINK, tr0 + blink tr0, ZERO + + + .global __put_user_asm_l +__put_user_asm_l: + ld.l r2, 0, r4 /* r4 = data */ + movi -(EFAULT), r2 /* r2 = reply, no real fixup */ + +___put_user_asm_l1: + st.l r3, 0, r4 + or ZERO, ZERO, r2 + +___put_user_asm_l_exit: + ptabs LINK, tr0 + blink tr0, ZERO + + + .global __put_user_asm_q +__put_user_asm_q: + ld.q r2, 0, r4 /* r4 = data */ + movi -(EFAULT), r2 /* r2 = reply, no real fixup */ + +___put_user_asm_q1: + st.q r3, 0, r4 + or ZERO, ZERO, r2 + +___put_user_asm_q_exit: + ptabs LINK, tr0 + blink tr0, ZERO + +panic_stash_regs: + /* The idea is : when we get an unhandled panic, we dump the registers + to a known memory location, the just sit in a tight loop. + This allows the human to look at the memory region through the GDB + session (assuming the debug module's SHwy initiator isn't locked up + or anything), to hopefully analyze the cause of the panic. */ + + /* On entry, former r15 (SP) is in DCR + former r0 is at resvec_saved_area + 0 + former r1 is at resvec_saved_area + 8 + former tr0 is at resvec_saved_area + 32 + DCR is the only register whose value is lost altogether. + */ + + movi 0xffffffff80000000, r0 ! phy of dump area + ld.q SP, 0x000, r1 ! former r0 + st.q r0, 0x000, r1 + ld.q SP, 0x008, r1 ! former r1 + st.q r0, 0x008, r1 + st.q r0, 0x010, r2 + st.q r0, 0x018, r3 + st.q r0, 0x020, r4 + st.q r0, 0x028, r5 + st.q r0, 0x030, r6 + st.q r0, 0x038, r7 + st.q r0, 0x040, r8 + st.q r0, 0x048, r9 + st.q r0, 0x050, r10 + st.q r0, 0x058, r11 + st.q r0, 0x060, r12 + st.q r0, 0x068, r13 + st.q r0, 0x070, r14 + getcon dcr, r14 + st.q r0, 0x078, r14 + st.q r0, 0x080, r16 + st.q r0, 0x088, r17 + st.q r0, 0x090, r18 + st.q r0, 0x098, r19 + st.q r0, 0x0a0, r20 + st.q r0, 0x0a8, r21 + st.q r0, 0x0b0, r22 + st.q r0, 0x0b8, r23 + st.q r0, 0x0c0, r24 + st.q r0, 0x0c8, r25 + st.q r0, 0x0d0, r26 + st.q r0, 0x0d8, r27 + st.q r0, 0x0e0, r28 + st.q r0, 0x0e8, r29 + st.q r0, 0x0f0, r30 + st.q r0, 0x0f8, r31 + st.q r0, 0x100, r32 + st.q r0, 0x108, r33 + st.q r0, 0x110, r34 + st.q r0, 0x118, r35 + st.q r0, 0x120, r36 + st.q r0, 0x128, r37 + st.q r0, 0x130, r38 + st.q r0, 0x138, r39 + st.q r0, 0x140, r40 + st.q r0, 0x148, r41 + st.q r0, 0x150, r42 + st.q r0, 0x158, r43 + st.q r0, 0x160, r44 + st.q r0, 0x168, r45 + st.q r0, 0x170, r46 + st.q r0, 0x178, r47 + st.q r0, 0x180, r48 + st.q r0, 0x188, r49 + st.q r0, 0x190, r50 + st.q r0, 0x198, r51 + st.q r0, 0x1a0, r52 + st.q r0, 0x1a8, r53 + st.q r0, 0x1b0, r54 + st.q r0, 0x1b8, r55 + st.q r0, 0x1c0, r56 + st.q r0, 0x1c8, r57 + st.q r0, 0x1d0, r58 + st.q r0, 0x1d8, r59 + st.q r0, 0x1e0, r60 + st.q r0, 0x1e8, r61 + st.q r0, 0x1f0, r62 + st.q r0, 0x1f8, r63 ! bogus, but for consistency's sake... + + ld.q SP, 0x020, r1 ! former tr0 + st.q r0, 0x200, r1 + gettr tr1, r1 + st.q r0, 0x208, r1 + gettr tr2, r1 + st.q r0, 0x210, r1 + gettr tr3, r1 + st.q r0, 0x218, r1 + gettr tr4, r1 + st.q r0, 0x220, r1 + gettr tr5, r1 + st.q r0, 0x228, r1 + gettr tr6, r1 + st.q r0, 0x230, r1 + gettr tr7, r1 + st.q r0, 0x238, r1 + + getcon sr, r1 + getcon ssr, r2 + getcon pssr, r3 + getcon spc, r4 + getcon pspc, r5 + getcon intevt, r6 + getcon expevt, r7 + getcon pexpevt, r8 + getcon tra, r9 + getcon tea, r10 + getcon kcr0, r11 + getcon kcr1, r12 + getcon vbr, r13 + getcon resvec, r14 + + st.q r0, 0x240, r1 + st.q r0, 0x248, r2 + st.q r0, 0x250, r3 + st.q r0, 0x258, r4 + st.q r0, 0x260, r5 + st.q r0, 0x268, r6 + st.q r0, 0x270, r7 + st.q r0, 0x278, r8 + st.q r0, 0x280, r9 + st.q r0, 0x288, r10 + st.q r0, 0x290, r11 + st.q r0, 0x298, r12 + st.q r0, 0x2a0, r13 + st.q r0, 0x2a8, r14 + + getcon SPC,r2 + getcon SSR,r3 + getcon EXPEVT,r4 + /* Prepare to jump to C - physical address */ + movi panic_handler-CONFIG_PAGE_OFFSET, r1 + ori r1, 1, r1 + ptabs r1, tr0 + getcon DCR, SP + blink tr0, ZERO + nop + nop + nop + nop + + + + +/* + * --- Signal Handling Section + */ + +/* + * extern long long _sa_default_rt_restorer + * extern long long _sa_default_restorer + * + * or, better, + * + * extern void _sa_default_rt_restorer(void) + * extern void _sa_default_restorer(void) + * + * Code prototypes to do a sys_rt_sigreturn() or sys_sysreturn() + * from user space. Copied into user space by signal management. + * Both must be quad aligned and 2 quad long (4 instructions). + * + */ + .balign 8 + .global sa_default_rt_restorer +sa_default_rt_restorer: + movi 0x10, r9 + shori __NR_rt_sigreturn, r9 + trapa r9 + nop + + .balign 8 + .global sa_default_restorer +sa_default_restorer: + movi 0x10, r9 + shori __NR_sigreturn, r9 + trapa r9 + nop + +/* + * --- __ex_table Section + */ + +/* + * User Access Exception Table. + */ + .section __ex_table, "a" + + .global asm_uaccess_start /* Just a marker */ +asm_uaccess_start: + +#ifdef CONFIG_MMU + .long ___copy_user1, ___copy_user_exit + .long ___copy_user2, ___copy_user_exit + .long ___clear_user1, ___clear_user_exit +#endif + .long ___get_user_asm_b1, ___get_user_asm_b_exit + .long ___get_user_asm_w1, ___get_user_asm_w_exit + .long ___get_user_asm_l1, ___get_user_asm_l_exit + .long ___get_user_asm_q1, ___get_user_asm_q_exit + .long ___put_user_asm_b1, ___put_user_asm_b_exit + .long ___put_user_asm_w1, ___put_user_asm_w_exit + .long ___put_user_asm_l1, ___put_user_asm_l_exit + .long ___put_user_asm_q1, ___put_user_asm_q_exit + + .global asm_uaccess_end /* Just a marker */ +asm_uaccess_end: + + + + +/* + * --- .init.text Section + */ + + __INIT + +/* + * void trap_init (void) + * + */ + .global trap_init +trap_init: + addi SP, -24, SP /* Room to save r28/r29/r30 */ + st.q SP, 0, r28 + st.q SP, 8, r29 + st.q SP, 16, r30 + + /* Set VBR and RESVEC */ + movi LVBR_block, r19 + andi r19, -4, r19 /* reset MMUOFF + reserved */ + /* For RESVEC exceptions we force the MMU off, which means we need the + physical address. */ + movi LRESVEC_block-CONFIG_PAGE_OFFSET, r20 + andi r20, -4, r20 /* reset reserved */ + ori r20, 1, r20 /* set MMUOFF */ + putcon r19, VBR + putcon r20, RESVEC + + /* Sanity check */ + movi LVBR_block_end, r21 + andi r21, -4, r21 + movi BLOCK_SIZE, r29 /* r29 = expected size */ + or r19, ZERO, r30 + add r19, r29, r19 + + /* + * Ugly, but better loop forever now than crash afterwards. + * We should print a message, but if we touch LVBR or + * LRESVEC blocks we should not be surprised if we get stuck + * in trap_init(). + */ + pta trap_init_loop, tr1 + gettr tr1, r28 /* r28 = trap_init_loop */ + sub r21, r30, r30 /* r30 = actual size */ + + /* + * VBR/RESVEC handlers overlap by being bigger than + * allowed. Very bad. Just loop forever. + * (r28) panic/loop address + * (r29) expected size + * (r30) actual size + */ +trap_init_loop: + bne r19, r21, tr1 + + /* Now that exception vectors are set up reset SR.BL */ + getcon SR, r22 + movi SR_UNBLOCK_EXC, r23 + and r22, r23, r22 + putcon r22, SR + + addi SP, 24, SP + ptabs LINK, tr0 + blink tr0, ZERO + diff --git a/arch/sh/kernel/cpu/sh5/fpu.c b/arch/sh/kernel/cpu/sh5/fpu.c new file mode 100644 index 000000000..9f8713aa7 --- /dev/null +++ b/arch/sh/kernel/cpu/sh5/fpu.c @@ -0,0 +1,111 @@ +/* + * arch/sh/kernel/cpu/sh5/fpu.c + * + * Copyright (C) 2001 Manuela Cirronis, Paolo Alberelli + * Copyright (C) 2002 STMicroelectronics Limited + * Author : Stuart Menefy + * + * Started from SH4 version: + * Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka + * + * 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/sched.h> +#include <linux/signal.h> +#include <asm/processor.h> + +void save_fpu(struct task_struct *tsk) +{ + asm volatile("fst.p %0, (0*8), fp0\n\t" + "fst.p %0, (1*8), fp2\n\t" + "fst.p %0, (2*8), fp4\n\t" + "fst.p %0, (3*8), fp6\n\t" + "fst.p %0, (4*8), fp8\n\t" + "fst.p %0, (5*8), fp10\n\t" + "fst.p %0, (6*8), fp12\n\t" + "fst.p %0, (7*8), fp14\n\t" + "fst.p %0, (8*8), fp16\n\t" + "fst.p %0, (9*8), fp18\n\t" + "fst.p %0, (10*8), fp20\n\t" + "fst.p %0, (11*8), fp22\n\t" + "fst.p %0, (12*8), fp24\n\t" + "fst.p %0, (13*8), fp26\n\t" + "fst.p %0, (14*8), fp28\n\t" + "fst.p %0, (15*8), fp30\n\t" + "fst.p %0, (16*8), fp32\n\t" + "fst.p %0, (17*8), fp34\n\t" + "fst.p %0, (18*8), fp36\n\t" + "fst.p %0, (19*8), fp38\n\t" + "fst.p %0, (20*8), fp40\n\t" + "fst.p %0, (21*8), fp42\n\t" + "fst.p %0, (22*8), fp44\n\t" + "fst.p %0, (23*8), fp46\n\t" + "fst.p %0, (24*8), fp48\n\t" + "fst.p %0, (25*8), fp50\n\t" + "fst.p %0, (26*8), fp52\n\t" + "fst.p %0, (27*8), fp54\n\t" + "fst.p %0, (28*8), fp56\n\t" + "fst.p %0, (29*8), fp58\n\t" + "fst.p %0, (30*8), fp60\n\t" + "fst.p %0, (31*8), fp62\n\t" + + "fgetscr fr63\n\t" + "fst.s %0, (32*8), fr63\n\t" + : /* no output */ + : "r" (&tsk->thread.xstate->hardfpu) + : "memory"); +} + +void restore_fpu(struct task_struct *tsk) +{ + asm volatile("fld.p %0, (0*8), fp0\n\t" + "fld.p %0, (1*8), fp2\n\t" + "fld.p %0, (2*8), fp4\n\t" + "fld.p %0, (3*8), fp6\n\t" + "fld.p %0, (4*8), fp8\n\t" + "fld.p %0, (5*8), fp10\n\t" + "fld.p %0, (6*8), fp12\n\t" + "fld.p %0, (7*8), fp14\n\t" + "fld.p %0, (8*8), fp16\n\t" + "fld.p %0, (9*8), fp18\n\t" + "fld.p %0, (10*8), fp20\n\t" + "fld.p %0, (11*8), fp22\n\t" + "fld.p %0, (12*8), fp24\n\t" + "fld.p %0, (13*8), fp26\n\t" + "fld.p %0, (14*8), fp28\n\t" + "fld.p %0, (15*8), fp30\n\t" + "fld.p %0, (16*8), fp32\n\t" + "fld.p %0, (17*8), fp34\n\t" + "fld.p %0, (18*8), fp36\n\t" + "fld.p %0, (19*8), fp38\n\t" + "fld.p %0, (20*8), fp40\n\t" + "fld.p %0, (21*8), fp42\n\t" + "fld.p %0, (22*8), fp44\n\t" + "fld.p %0, (23*8), fp46\n\t" + "fld.p %0, (24*8), fp48\n\t" + "fld.p %0, (25*8), fp50\n\t" + "fld.p %0, (26*8), fp52\n\t" + "fld.p %0, (27*8), fp54\n\t" + "fld.p %0, (28*8), fp56\n\t" + "fld.p %0, (29*8), fp58\n\t" + "fld.p %0, (30*8), fp60\n\t" + + "fld.s %0, (32*8), fr63\n\t" + "fputscr fr63\n\t" + + "fld.p %0, (31*8), fp62\n\t" + : /* no output */ + : "r" (&tsk->thread.xstate->hardfpu) + : "memory"); +} + +asmlinkage void do_fpu_error(unsigned long ex, struct pt_regs *regs) +{ + struct task_struct *tsk = current; + + regs->pc += 4; + + force_sig(SIGFPE, tsk); +} diff --git a/arch/sh/kernel/cpu/sh5/probe.c b/arch/sh/kernel/cpu/sh5/probe.c new file mode 100644 index 000000000..eca427c2f --- /dev/null +++ b/arch/sh/kernel/cpu/sh5/probe.c @@ -0,0 +1,75 @@ +/* + * arch/sh/kernel/cpu/sh5/probe.c + * + * CPU Subtype Probing for SH-5. + * + * Copyright (C) 2000, 2001 Paolo Alberelli + * Copyright (C) 2003 - 2007 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/io.h> +#include <linux/string.h> +#include <asm/processor.h> +#include <asm/cache.h> +#include <asm/tlb.h> + +void cpu_probe(void) +{ + unsigned long long cir; + + /* + * Do peeks in real mode to avoid having to set up a mapping for + * the WPC registers. On SH5-101 cut2, such a mapping would be + * exposed to an address translation erratum which would make it + * hard to set up correctly. + */ + cir = peek_real_address_q(0x0d000008); + if ((cir & 0xffff) == 0x5103) + boot_cpu_data.type = CPU_SH5_103; + else if (((cir >> 32) & 0xffff) == 0x51e2) + /* CPU.VCR aliased at CIR address on SH5-101 */ + boot_cpu_data.type = CPU_SH5_101; + + boot_cpu_data.family = CPU_FAMILY_SH5; + + /* + * First, setup some sane values for the I-cache. + */ + boot_cpu_data.icache.ways = 4; + boot_cpu_data.icache.sets = 256; + boot_cpu_data.icache.linesz = L1_CACHE_BYTES; + boot_cpu_data.icache.way_incr = (1 << 13); + boot_cpu_data.icache.entry_shift = 5; + boot_cpu_data.icache.way_size = boot_cpu_data.icache.sets * + boot_cpu_data.icache.linesz; + boot_cpu_data.icache.entry_mask = 0x1fe0; + boot_cpu_data.icache.flags = 0; + + /* + * Next, setup some sane values for the D-cache. + * + * On the SH5, these are pretty consistent with the I-cache settings, + * so we just copy over the existing definitions.. these can be fixed + * up later, especially if we add runtime CPU probing. + * + * Though in the meantime it saves us from having to duplicate all of + * the above definitions.. + */ + boot_cpu_data.dcache = boot_cpu_data.icache; + + /* + * Setup any cache-related flags here + */ +#if defined(CONFIG_CACHE_WRITETHROUGH) + set_bit(SH_CACHE_MODE_WT, &(boot_cpu_data.dcache.flags)); +#elif defined(CONFIG_CACHE_WRITEBACK) + set_bit(SH_CACHE_MODE_WB, &(boot_cpu_data.dcache.flags)); +#endif + + /* Setup some I/D TLB defaults */ + sh64_tlb_init(); +} diff --git a/arch/sh/kernel/cpu/sh5/setup-sh5.c b/arch/sh/kernel/cpu/sh5/setup-sh5.c new file mode 100644 index 000000000..084a9cc99 --- /dev/null +++ b/arch/sh/kernel/cpu/sh5/setup-sh5.c @@ -0,0 +1,123 @@ +/* + * SH5-101/SH5-103 CPU Setup + * + * 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/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <linux/serial_sci.h> +#include <linux/io.h> +#include <linux/mm.h> +#include <linux/sh_timer.h> +#include <asm/addrspace.h> + +static struct plat_sci_port scif0_platform_data = { + .flags = UPF_IOREMAP, + .scscr = SCSCR_REIE, + .type = PORT_SCIF, +}; + +static struct resource scif0_resources[] = { + DEFINE_RES_MEM(PHYS_PERIPHERAL_BLOCK + 0x01030000, 0x100), + DEFINE_RES_IRQ(39), + DEFINE_RES_IRQ(40), + DEFINE_RES_IRQ(42), +}; + +static struct platform_device scif0_device = { + .name = "sh-sci", + .id = 0, + .resource = scif0_resources, + .num_resources = ARRAY_SIZE(scif0_resources), + .dev = { + .platform_data = &scif0_platform_data, + }, +}; + +static struct resource rtc_resources[] = { + [0] = { + .start = PHYS_PERIPHERAL_BLOCK + 0x01040000, + .end = PHYS_PERIPHERAL_BLOCK + 0x01040000 + 0x58 - 1, + .flags = IORESOURCE_IO, + }, + [1] = { + /* Period IRQ */ + .start = IRQ_PRI, + .flags = IORESOURCE_IRQ, + }, + [2] = { + /* Carry IRQ */ + .start = IRQ_CUI, + .flags = IORESOURCE_IRQ, + }, + [3] = { + /* Alarm IRQ */ + .start = IRQ_ATI, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device rtc_device = { + .name = "sh-rtc", + .id = -1, + .num_resources = ARRAY_SIZE(rtc_resources), + .resource = rtc_resources, +}; + +#define TMU_BLOCK_OFF 0x01020000 +#define TMU_BASE PHYS_PERIPHERAL_BLOCK + TMU_BLOCK_OFF + +static struct sh_timer_config tmu0_platform_data = { + .channels_mask = 7, +}; + +static struct resource tmu0_resources[] = { + DEFINE_RES_MEM(TMU_BASE, 0x30), + DEFINE_RES_IRQ(IRQ_TUNI0), + DEFINE_RES_IRQ(IRQ_TUNI1), + DEFINE_RES_IRQ(IRQ_TUNI2), +}; + +static struct platform_device tmu0_device = { + .name = "sh-tmu", + .id = 0, + .dev = { + .platform_data = &tmu0_platform_data, + }, + .resource = tmu0_resources, + .num_resources = ARRAY_SIZE(tmu0_resources), +}; + +static struct platform_device *sh5_early_devices[] __initdata = { + &scif0_device, + &tmu0_device, +}; + +static struct platform_device *sh5_devices[] __initdata = { + &rtc_device, +}; + +static int __init sh5_devices_setup(void) +{ + int ret; + + ret = platform_add_devices(sh5_early_devices, + ARRAY_SIZE(sh5_early_devices)); + if (unlikely(ret != 0)) + return ret; + + return platform_add_devices(sh5_devices, + ARRAY_SIZE(sh5_devices)); +} +arch_initcall(sh5_devices_setup); + +void __init plat_early_device_setup(void) +{ + early_platform_add_devices(sh5_early_devices, + ARRAY_SIZE(sh5_early_devices)); +} diff --git a/arch/sh/kernel/cpu/sh5/switchto.S b/arch/sh/kernel/cpu/sh5/switchto.S new file mode 100644 index 000000000..45c351b0f --- /dev/null +++ b/arch/sh/kernel/cpu/sh5/switchto.S @@ -0,0 +1,198 @@ +/* + * arch/sh/kernel/cpu/sh5/switchto.S + * + * sh64 context switch + * + * Copyright (C) 2004 Richard Curnow + * + * 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. +*/ + + .section .text..SHmedia32,"ax" + .little + + .balign 32 + + .type sh64_switch_to,@function + .global sh64_switch_to + .global __sh64_switch_to_end +sh64_switch_to: + +/* Incoming args + r2 - prev + r3 - &prev->thread + r4 - next + r5 - &next->thread + + Outgoing results + r2 - last (=prev) : this just stays in r2 throughout + + Want to create a full (struct pt_regs) on the stack to allow backtracing + functions to work. However, we only need to populate the callee-save + register slots in this structure; since we're a function our ancestors must + have themselves preserved all caller saved state in the stack. This saves + some wasted effort since we won't need to look at the values. + + In particular, all caller-save registers are immediately available for + scratch use. + +*/ + +#define FRAME_SIZE (76*8 + 8) + + movi FRAME_SIZE, r0 + sub.l r15, r0, r15 + ! Do normal-style register save to support backtrace + + st.l r15, 0, r18 ! save link reg + st.l r15, 4, r14 ! save fp + add.l r15, r63, r14 ! setup frame pointer + + ! hopefully this looks normal to the backtrace now. + + addi.l r15, 8, r1 ! base of pt_regs + addi.l r1, 24, r0 ! base of pt_regs.regs + addi.l r0, (63*8), r8 ! base of pt_regs.trregs + + /* Note : to be fixed? + struct pt_regs is really designed for holding the state on entry + to an exception, i.e. pc,sr,regs etc. However, for the context + switch state, some of this is not required. But the unwinder takes + struct pt_regs * as an arg so we have to build this structure + to allow unwinding switched tasks in show_state() */ + + st.q r0, ( 9*8), r9 + st.q r0, (10*8), r10 + st.q r0, (11*8), r11 + st.q r0, (12*8), r12 + st.q r0, (13*8), r13 + st.q r0, (14*8), r14 ! for unwind, want to look as though we took a trap at + ! the point where the process is left in suspended animation, i.e. current + ! fp here, not the saved one. + st.q r0, (16*8), r16 + + st.q r0, (24*8), r24 + st.q r0, (25*8), r25 + st.q r0, (26*8), r26 + st.q r0, (27*8), r27 + st.q r0, (28*8), r28 + st.q r0, (29*8), r29 + st.q r0, (30*8), r30 + st.q r0, (31*8), r31 + st.q r0, (32*8), r32 + st.q r0, (33*8), r33 + st.q r0, (34*8), r34 + st.q r0, (35*8), r35 + + st.q r0, (44*8), r44 + st.q r0, (45*8), r45 + st.q r0, (46*8), r46 + st.q r0, (47*8), r47 + st.q r0, (48*8), r48 + st.q r0, (49*8), r49 + st.q r0, (50*8), r50 + st.q r0, (51*8), r51 + st.q r0, (52*8), r52 + st.q r0, (53*8), r53 + st.q r0, (54*8), r54 + st.q r0, (55*8), r55 + st.q r0, (56*8), r56 + st.q r0, (57*8), r57 + st.q r0, (58*8), r58 + st.q r0, (59*8), r59 + + ! do this early as pta->gettr has no pipeline forwarding (=> 5 cycle latency) + ! Use a local label to avoid creating a symbol that will confuse the ! + ! backtrace + pta .Lsave_pc, tr0 + + gettr tr5, r45 + gettr tr6, r46 + gettr tr7, r47 + st.q r8, (5*8), r45 + st.q r8, (6*8), r46 + st.q r8, (7*8), r47 + + ! Now switch context + gettr tr0, r9 + st.l r3, 0, r15 ! prev->thread.sp + st.l r3, 8, r1 ! prev->thread.kregs + st.l r3, 4, r9 ! prev->thread.pc + st.q r1, 0, r9 ! save prev->thread.pc into pt_regs->pc + + ! Load PC for next task (init value or save_pc later) + ld.l r5, 4, r18 ! next->thread.pc + ! Switch stacks + ld.l r5, 0, r15 ! next->thread.sp + ptabs r18, tr0 + + ! Update current + ld.l r4, 4, r9 ! next->thread_info (2nd element of next task_struct) + putcon r9, kcr0 ! current = next->thread_info + + ! go to save_pc for a reschedule, or the initial thread.pc for a new process + blink tr0, r63 + + ! Restore (when we come back to a previously saved task) +.Lsave_pc: + addi.l r15, 32, r0 ! r0 = next's regs + addi.l r0, (63*8), r8 ! r8 = next's tr_regs + + ld.q r8, (5*8), r45 + ld.q r8, (6*8), r46 + ld.q r8, (7*8), r47 + ptabs r45, tr5 + ptabs r46, tr6 + ptabs r47, tr7 + + ld.q r0, ( 9*8), r9 + ld.q r0, (10*8), r10 + ld.q r0, (11*8), r11 + ld.q r0, (12*8), r12 + ld.q r0, (13*8), r13 + ld.q r0, (14*8), r14 + ld.q r0, (16*8), r16 + + ld.q r0, (24*8), r24 + ld.q r0, (25*8), r25 + ld.q r0, (26*8), r26 + ld.q r0, (27*8), r27 + ld.q r0, (28*8), r28 + ld.q r0, (29*8), r29 + ld.q r0, (30*8), r30 + ld.q r0, (31*8), r31 + ld.q r0, (32*8), r32 + ld.q r0, (33*8), r33 + ld.q r0, (34*8), r34 + ld.q r0, (35*8), r35 + + ld.q r0, (44*8), r44 + ld.q r0, (45*8), r45 + ld.q r0, (46*8), r46 + ld.q r0, (47*8), r47 + ld.q r0, (48*8), r48 + ld.q r0, (49*8), r49 + ld.q r0, (50*8), r50 + ld.q r0, (51*8), r51 + ld.q r0, (52*8), r52 + ld.q r0, (53*8), r53 + ld.q r0, (54*8), r54 + ld.q r0, (55*8), r55 + ld.q r0, (56*8), r56 + ld.q r0, (57*8), r57 + ld.q r0, (58*8), r58 + ld.q r0, (59*8), r59 + + ! epilogue + ld.l r15, 0, r18 + ld.l r15, 4, r14 + ptabs r18, tr0 + movi FRAME_SIZE, r0 + add r15, r0, r15 + blink tr0, r63 +__sh64_switch_to_end: +.LFE1: + .size sh64_switch_to,.LFE1-sh64_switch_to + diff --git a/arch/sh/kernel/cpu/sh5/unwind.c b/arch/sh/kernel/cpu/sh5/unwind.c new file mode 100644 index 000000000..3a4fed406 --- /dev/null +++ b/arch/sh/kernel/cpu/sh5/unwind.c @@ -0,0 +1,345 @@ +/* + * arch/sh/kernel/cpu/sh5/unwind.c + * + * Copyright (C) 2004 Paul Mundt + * Copyright (C) 2004 Richard Curnow + * + * 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/kallsyms.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <asm/page.h> +#include <asm/ptrace.h> +#include <asm/processor.h> +#include <asm/io.h> +#include <asm/unwinder.h> +#include <asm/stacktrace.h> + +static u8 regcache[63]; + +/* + * Finding the previous stack frame isn't horribly straightforward as it is + * on some other platforms. In the sh64 case, we don't have "linked" stack + * frames, so we need to do a bit of work to determine the previous frame, + * and in turn, the previous r14/r18 pair. + * + * There are generally a few cases which determine where we can find out + * the r14/r18 values. In the general case, this can be determined by poking + * around the prologue of the symbol PC is in (note that we absolutely must + * have frame pointer support as well as the kernel symbol table mapped, + * otherwise we can't even get this far). + * + * In other cases, such as the interrupt/exception path, we can poke around + * the sp/fp. + * + * Notably, this entire approach is somewhat error prone, and in the event + * that the previous frame cannot be determined, that's all we can do. + * Either way, this still leaves us with a more correct backtrace then what + * we would be able to come up with by walking the stack (which is garbage + * for anything beyond the first frame). + * -- PFM. + */ +static int lookup_prev_stack_frame(unsigned long fp, unsigned long pc, + unsigned long *pprev_fp, unsigned long *pprev_pc, + struct pt_regs *regs) +{ + const char *sym; + char namebuf[128]; + unsigned long offset; + unsigned long prologue = 0; + unsigned long fp_displacement = 0; + unsigned long fp_prev = 0; + unsigned long offset_r14 = 0, offset_r18 = 0; + int i, found_prologue_end = 0; + + sym = kallsyms_lookup(pc, NULL, &offset, NULL, namebuf); + if (!sym) + return -EINVAL; + + prologue = pc - offset; + if (!prologue) + return -EINVAL; + + /* Validate fp, to avoid risk of dereferencing a bad pointer later. + Assume 128Mb since that's the amount of RAM on a Cayman. Modify + when there is an SH-5 board with more. */ + if ((fp < (unsigned long) phys_to_virt(__MEMORY_START)) || + (fp >= (unsigned long)(phys_to_virt(__MEMORY_START)) + 128*1024*1024) || + ((fp & 7) != 0)) { + return -EINVAL; + } + + /* + * Depth to walk, depth is completely arbitrary. + */ + for (i = 0; i < 100; i++, prologue += sizeof(unsigned long)) { + unsigned long op; + u8 major, minor; + u8 src, dest, disp; + + op = *(unsigned long *)prologue; + + major = (op >> 26) & 0x3f; + src = (op >> 20) & 0x3f; + minor = (op >> 16) & 0xf; + disp = (op >> 10) & 0x3f; + dest = (op >> 4) & 0x3f; + + /* + * Stack frame creation happens in a number of ways.. in the + * general case when the stack frame is less than 511 bytes, + * it's generally created by an addi or addi.l: + * + * addi/addi.l r15, -FRAME_SIZE, r15 + * + * in the event that the frame size is bigger than this, it's + * typically created using a movi/sub pair as follows: + * + * movi FRAME_SIZE, rX + * sub r15, rX, r15 + */ + + switch (major) { + case (0x00 >> 2): + switch (minor) { + case 0x8: /* add.l */ + case 0x9: /* add */ + /* Look for r15, r63, r14 */ + if (src == 15 && disp == 63 && dest == 14) + found_prologue_end = 1; + + break; + case 0xa: /* sub.l */ + case 0xb: /* sub */ + if (src != 15 || dest != 15) + continue; + + fp_displacement -= regcache[disp]; + fp_prev = fp - fp_displacement; + break; + } + break; + case (0xa8 >> 2): /* st.l */ + if (src != 15) + continue; + + switch (dest) { + case 14: + if (offset_r14 || fp_displacement == 0) + continue; + + offset_r14 = (u64)(((((s64)op >> 10) & 0x3ff) << 54) >> 54); + offset_r14 *= sizeof(unsigned long); + offset_r14 += fp_displacement; + break; + case 18: + if (offset_r18 || fp_displacement == 0) + continue; + + offset_r18 = (u64)(((((s64)op >> 10) & 0x3ff) << 54) >> 54); + offset_r18 *= sizeof(unsigned long); + offset_r18 += fp_displacement; + break; + } + + break; + case (0xcc >> 2): /* movi */ + if (dest >= 63) { + printk(KERN_NOTICE "%s: Invalid dest reg %d " + "specified in movi handler. Failed " + "opcode was 0x%lx: ", __func__, + dest, op); + + continue; + } + + /* Sign extend */ + regcache[dest] = + sign_extend64((((u64)op >> 10) & 0xffff), 9); + break; + case (0xd0 >> 2): /* addi */ + case (0xd4 >> 2): /* addi.l */ + /* Look for r15, -FRAME_SIZE, r15 */ + if (src != 15 || dest != 15) + continue; + + /* Sign extended frame size.. */ + fp_displacement += + (u64)(((((s64)op >> 10) & 0x3ff) << 54) >> 54); + fp_prev = fp - fp_displacement; + break; + } + + if (found_prologue_end && offset_r14 && (offset_r18 || *pprev_pc) && fp_prev) + break; + } + + if (offset_r14 == 0 || fp_prev == 0) { + if (!offset_r14) + pr_debug("Unable to find r14 offset\n"); + if (!fp_prev) + pr_debug("Unable to find previous fp\n"); + + return -EINVAL; + } + + /* For innermost leaf function, there might not be a offset_r18 */ + if (!*pprev_pc && (offset_r18 == 0)) + return -EINVAL; + + *pprev_fp = *(unsigned long *)(fp_prev + offset_r14); + + if (offset_r18) + *pprev_pc = *(unsigned long *)(fp_prev + offset_r18); + + *pprev_pc &= ~1; + + return 0; +} + +/* + * Don't put this on the stack since we'll want to call in to + * sh64_unwinder_dump() when we're close to underflowing the stack + * anyway. + */ +static struct pt_regs here_regs; + +extern const char syscall_ret; +extern const char ret_from_syscall; +extern const char ret_from_exception; +extern const char ret_from_irq; + +static void sh64_unwind_inner(const struct stacktrace_ops *ops, + void *data, struct pt_regs *regs); + +static inline void unwind_nested(const struct stacktrace_ops *ops, void *data, + unsigned long pc, unsigned long fp) +{ + if ((fp >= __MEMORY_START) && + ((fp & 7) == 0)) + sh64_unwind_inner(ops, data, (struct pt_regs *)fp); +} + +static void sh64_unwind_inner(const struct stacktrace_ops *ops, + void *data, struct pt_regs *regs) +{ + unsigned long pc, fp; + int ofs = 0; + int first_pass; + + pc = regs->pc & ~1; + fp = regs->regs[14]; + + first_pass = 1; + for (;;) { + int cond; + unsigned long next_fp, next_pc; + + if (pc == ((unsigned long)&syscall_ret & ~1)) { + printk("SYSCALL\n"); + unwind_nested(ops, data, pc, fp); + return; + } + + if (pc == ((unsigned long)&ret_from_syscall & ~1)) { + printk("SYSCALL (PREEMPTED)\n"); + unwind_nested(ops, data, pc, fp); + return; + } + + /* In this case, the PC is discovered by lookup_prev_stack_frame but + it has 4 taken off it to look like the 'caller' */ + if (pc == ((unsigned long)&ret_from_exception & ~1)) { + printk("EXCEPTION\n"); + unwind_nested(ops, data, pc, fp); + return; + } + + if (pc == ((unsigned long)&ret_from_irq & ~1)) { + printk("IRQ\n"); + unwind_nested(ops, data, pc, fp); + return; + } + + cond = ((pc >= __MEMORY_START) && (fp >= __MEMORY_START) && + ((pc & 3) == 0) && ((fp & 7) == 0)); + + pc -= ofs; + + ops->address(data, pc, 1); + + if (first_pass) { + /* If the innermost frame is a leaf function, it's + * possible that r18 is never saved out to the stack. + */ + next_pc = regs->regs[18]; + } else { + next_pc = 0; + } + + if (lookup_prev_stack_frame(fp, pc, &next_fp, &next_pc, regs) == 0) { + ofs = sizeof(unsigned long); + pc = next_pc & ~1; + fp = next_fp; + } else { + printk("Unable to lookup previous stack frame\n"); + break; + } + first_pass = 0; + } + + printk("\n"); +} + +static void sh64_unwinder_dump(struct task_struct *task, + struct pt_regs *regs, + unsigned long *sp, + const struct stacktrace_ops *ops, + void *data) +{ + if (!regs) { + /* + * Fetch current regs if we have no other saved state to back + * trace from. + */ + regs = &here_regs; + + __asm__ __volatile__ ("ori r14, 0, %0" : "=r" (regs->regs[14])); + __asm__ __volatile__ ("ori r15, 0, %0" : "=r" (regs->regs[15])); + __asm__ __volatile__ ("ori r18, 0, %0" : "=r" (regs->regs[18])); + + __asm__ __volatile__ ("gettr tr0, %0" : "=r" (regs->tregs[0])); + __asm__ __volatile__ ("gettr tr1, %0" : "=r" (regs->tregs[1])); + __asm__ __volatile__ ("gettr tr2, %0" : "=r" (regs->tregs[2])); + __asm__ __volatile__ ("gettr tr3, %0" : "=r" (regs->tregs[3])); + __asm__ __volatile__ ("gettr tr4, %0" : "=r" (regs->tregs[4])); + __asm__ __volatile__ ("gettr tr5, %0" : "=r" (regs->tregs[5])); + __asm__ __volatile__ ("gettr tr6, %0" : "=r" (regs->tregs[6])); + __asm__ __volatile__ ("gettr tr7, %0" : "=r" (regs->tregs[7])); + + __asm__ __volatile__ ( + "pta 0f, tr0\n\t" + "blink tr0, %0\n\t" + "0: nop" + : "=r" (regs->pc) + ); + } + + sh64_unwind_inner(ops, data, regs); +} + +static struct unwinder sh64_unwinder = { + .name = "sh64-unwinder", + .dump = sh64_unwinder_dump, + .rating = 150, +}; + +static int __init sh64_unwinder_init(void) +{ + return unwinder_register(&sh64_unwinder); +} +early_initcall(sh64_unwinder_init); diff --git a/arch/sh/kernel/cpu/shmobile/Makefile b/arch/sh/kernel/cpu/shmobile/Makefile new file mode 100644 index 000000000..e8a5111e8 --- /dev/null +++ b/arch/sh/kernel/cpu/shmobile/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the Linux/SuperH SH-Mobile backends. +# + +# Power Management & Sleep mode +obj-$(CONFIG_PM) += pm.o sleep.o +obj-$(CONFIG_CPU_IDLE) += cpuidle.o diff --git a/arch/sh/kernel/cpu/shmobile/cpuidle.c b/arch/sh/kernel/cpu/shmobile/cpuidle.c new file mode 100644 index 000000000..c32e66079 --- /dev/null +++ b/arch/sh/kernel/cpu/shmobile/cpuidle.c @@ -0,0 +1,98 @@ +/* + * arch/sh/kernel/cpu/shmobile/cpuidle.c + * + * Cpuidle support code for SuperH Mobile + * + * Copyright (C) 2009 Magnus Damm + * + * 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/io.h> +#include <linux/suspend.h> +#include <linux/cpuidle.h> +#include <linux/export.h> +#include <asm/suspend.h> +#include <linux/uaccess.h> + +static unsigned long cpuidle_mode[] = { + SUSP_SH_SLEEP, /* regular sleep mode */ + SUSP_SH_SLEEP | SUSP_SH_SF, /* sleep mode + self refresh */ + SUSP_SH_STANDBY | SUSP_SH_SF, /* software standby mode + self refresh */ +}; + +static int cpuidle_sleep_enter(struct cpuidle_device *dev, + struct cpuidle_driver *drv, + int index) +{ + unsigned long allowed_mode = SUSP_SH_SLEEP; + int requested_state = index; + int allowed_state; + int k; + + /* convert allowed mode to allowed state */ + for (k = ARRAY_SIZE(cpuidle_mode) - 1; k > 0; k--) + if (cpuidle_mode[k] == allowed_mode) + break; + + allowed_state = k; + + /* take the following into account for sleep mode selection: + * - allowed_state: best mode allowed by hardware (clock deps) + * - requested_state: best mode allowed by software (latencies) + */ + k = min_t(int, allowed_state, requested_state); + + sh_mobile_call_standby(cpuidle_mode[k]); + + return k; +} + +static struct cpuidle_driver cpuidle_driver = { + .name = "sh_idle", + .owner = THIS_MODULE, + .states = { + { + .exit_latency = 1, + .target_residency = 1 * 2, + .power_usage = 3, + .enter = cpuidle_sleep_enter, + .name = "C1", + .desc = "SuperH Sleep Mode", + }, + { + .exit_latency = 100, + .target_residency = 1 * 2, + .power_usage = 1, + .enter = cpuidle_sleep_enter, + .name = "C2", + .desc = "SuperH Sleep Mode [SF]", + .disabled = true, + }, + { + .exit_latency = 2300, + .target_residency = 1 * 2, + .power_usage = 1, + .enter = cpuidle_sleep_enter, + .name = "C3", + .desc = "SuperH Mobile Standby Mode [SF]", + .disabled = true, + }, + }, + .safe_state_index = 0, + .state_count = 3, +}; + +int __init sh_mobile_setup_cpuidle(void) +{ + if (sh_mobile_sleep_supported & SUSP_SH_SF) + cpuidle_driver.states[1].disabled = false; + + if (sh_mobile_sleep_supported & SUSP_SH_STANDBY) + cpuidle_driver.states[2].disabled = false; + + return cpuidle_register(&cpuidle_driver, NULL); +} diff --git a/arch/sh/kernel/cpu/shmobile/pm.c b/arch/sh/kernel/cpu/shmobile/pm.c new file mode 100644 index 000000000..fba2be5d7 --- /dev/null +++ b/arch/sh/kernel/cpu/shmobile/pm.c @@ -0,0 +1,156 @@ +/* + * arch/sh/kernel/cpu/shmobile/pm.c + * + * Power management support code for SuperH Mobile + * + * Copyright (C) 2009 Magnus Damm + * + * 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/io.h> +#include <linux/suspend.h> +#include <asm/suspend.h> +#include <linux/uaccess.h> +#include <asm/cacheflush.h> +#include <asm/bl_bit.h> + +/* + * Notifier lists for pre/post sleep notification + */ +ATOMIC_NOTIFIER_HEAD(sh_mobile_pre_sleep_notifier_list); +ATOMIC_NOTIFIER_HEAD(sh_mobile_post_sleep_notifier_list); + +/* + * Sleep modes available on SuperH Mobile: + * + * Sleep mode is just plain "sleep" instruction + * Sleep Self-Refresh mode is above plus RAM put in Self-Refresh + * Standby Self-Refresh mode is above plus stopped clocks + */ +#define SUSP_MODE_SLEEP (SUSP_SH_SLEEP) +#define SUSP_MODE_SLEEP_SF (SUSP_SH_SLEEP | SUSP_SH_SF) +#define SUSP_MODE_STANDBY_SF (SUSP_SH_STANDBY | SUSP_SH_SF) +#define SUSP_MODE_RSTANDBY_SF \ + (SUSP_SH_RSTANDBY | SUSP_SH_MMU | SUSP_SH_REGS | SUSP_SH_SF) + /* + * U-standby mode is unsupported since it needs bootloader hacks + */ + +#ifdef CONFIG_CPU_SUBTYPE_SH7724 +#define RAM_BASE 0xfd800000 /* RSMEM */ +#else +#define RAM_BASE 0xe5200000 /* ILRAM */ +#endif + +void sh_mobile_call_standby(unsigned long mode) +{ + void *onchip_mem = (void *)RAM_BASE; + struct sh_sleep_data *sdp = onchip_mem; + void (*standby_onchip_mem)(unsigned long, unsigned long); + + /* code located directly after data structure */ + standby_onchip_mem = (void *)(sdp + 1); + + atomic_notifier_call_chain(&sh_mobile_pre_sleep_notifier_list, + mode, NULL); + + /* flush the caches if MMU flag is set */ + if (mode & SUSP_SH_MMU) + flush_cache_all(); + + /* Let assembly snippet in on-chip memory handle the rest */ + standby_onchip_mem(mode, RAM_BASE); + + atomic_notifier_call_chain(&sh_mobile_post_sleep_notifier_list, + mode, NULL); +} + +extern char sh_mobile_sleep_enter_start; +extern char sh_mobile_sleep_enter_end; + +extern char sh_mobile_sleep_resume_start; +extern char sh_mobile_sleep_resume_end; + +unsigned long sh_mobile_sleep_supported = SUSP_SH_SLEEP; + +void sh_mobile_register_self_refresh(unsigned long flags, + void *pre_start, void *pre_end, + void *post_start, void *post_end) +{ + void *onchip_mem = (void *)RAM_BASE; + void *vp; + struct sh_sleep_data *sdp; + int n; + + /* part 0: data area */ + sdp = onchip_mem; + sdp->addr.stbcr = 0xa4150020; /* STBCR */ + sdp->addr.bar = 0xa4150040; /* BAR */ + sdp->addr.pteh = 0xff000000; /* PTEH */ + sdp->addr.ptel = 0xff000004; /* PTEL */ + sdp->addr.ttb = 0xff000008; /* TTB */ + sdp->addr.tea = 0xff00000c; /* TEA */ + sdp->addr.mmucr = 0xff000010; /* MMUCR */ + sdp->addr.ptea = 0xff000034; /* PTEA */ + sdp->addr.pascr = 0xff000070; /* PASCR */ + sdp->addr.irmcr = 0xff000078; /* IRMCR */ + sdp->addr.ccr = 0xff00001c; /* CCR */ + sdp->addr.ramcr = 0xff000074; /* RAMCR */ + vp = sdp + 1; + + /* part 1: common code to enter sleep mode */ + n = &sh_mobile_sleep_enter_end - &sh_mobile_sleep_enter_start; + memcpy(vp, &sh_mobile_sleep_enter_start, n); + vp += roundup(n, 4); + + /* part 2: board specific code to enter self-refresh mode */ + n = pre_end - pre_start; + memcpy(vp, pre_start, n); + sdp->sf_pre = (unsigned long)vp; + vp += roundup(n, 4); + + /* part 3: board specific code to resume from self-refresh mode */ + n = post_end - post_start; + memcpy(vp, post_start, n); + sdp->sf_post = (unsigned long)vp; + vp += roundup(n, 4); + + /* part 4: common code to resume from sleep mode */ + WARN_ON(vp > (onchip_mem + 0x600)); + vp = onchip_mem + 0x600; /* located at interrupt vector */ + n = &sh_mobile_sleep_resume_end - &sh_mobile_sleep_resume_start; + memcpy(vp, &sh_mobile_sleep_resume_start, n); + sdp->resume = (unsigned long)vp; + + sh_mobile_sleep_supported |= flags; +} + +static int sh_pm_enter(suspend_state_t state) +{ + if (!(sh_mobile_sleep_supported & SUSP_MODE_STANDBY_SF)) + return -ENXIO; + + local_irq_disable(); + set_bl_bit(); + sh_mobile_call_standby(SUSP_MODE_STANDBY_SF); + local_irq_disable(); + clear_bl_bit(); + return 0; +} + +static const struct platform_suspend_ops sh_pm_ops = { + .enter = sh_pm_enter, + .valid = suspend_valid_only_mem, +}; + +static int __init sh_pm_init(void) +{ + suspend_set_ops(&sh_pm_ops); + return sh_mobile_setup_cpuidle(); +} + +late_initcall(sh_pm_init); diff --git a/arch/sh/kernel/cpu/shmobile/sleep.S b/arch/sh/kernel/cpu/shmobile/sleep.S new file mode 100644 index 000000000..e6aac65f5 --- /dev/null +++ b/arch/sh/kernel/cpu/shmobile/sleep.S @@ -0,0 +1,405 @@ +/* + * arch/sh/kernel/cpu/sh4a/sleep-sh_mobile.S + * + * Sleep mode and Standby modes support for SuperH Mobile + * + * Copyright (C) 2009 Magnus Damm + * + * 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/sys.h> +#include <linux/errno.h> +#include <linux/linkage.h> +#include <asm/asm-offsets.h> +#include <asm/suspend.h> + +/* + * Kernel mode register usage, see entry.S: + * k0 scratch + * k1 scratch + */ +#define k0 r0 +#define k1 r1 + +/* manage self-refresh and enter standby mode. must be self-contained. + * this code will be copied to on-chip memory and executed from there. + */ + .balign 4 +ENTRY(sh_mobile_sleep_enter_start) + + /* save mode flags */ + mov.l r4, @(SH_SLEEP_MODE, r5) + + /* save original vbr */ + stc vbr, r0 + mov.l r0, @(SH_SLEEP_VBR, r5) + + /* point vbr to our on-chip memory page */ + ldc r5, vbr + + /* save return address */ + sts pr, r0 + mov.l r0, @(SH_SLEEP_SPC, r5) + + /* save sr */ + stc sr, r0 + mov.l r0, @(SH_SLEEP_SR, r5) + + /* save general purpose registers to stack if needed */ + mov.l @(SH_SLEEP_MODE, r5), r0 + tst #SUSP_SH_REGS, r0 + bt skip_regs_save + + sts.l pr, @-r15 + mov.l r14, @-r15 + mov.l r13, @-r15 + mov.l r12, @-r15 + mov.l r11, @-r15 + mov.l r10, @-r15 + mov.l r9, @-r15 + mov.l r8, @-r15 + + /* make sure bank0 is selected, save low registers */ + mov.l rb_bit, r9 + not r9, r9 + bsr set_sr + mov #0, r10 + + bsr save_low_regs + nop + + /* switch to bank 1, save low registers */ + mov.l rb_bit, r10 + bsr set_sr + mov #-1, r9 + + bsr save_low_regs + nop + + /* switch back to bank 0 */ + mov.l rb_bit, r9 + not r9, r9 + bsr set_sr + mov #0, r10 + +skip_regs_save: + + /* save sp, also set to internal ram */ + mov.l r15, @(SH_SLEEP_SP, r5) + mov r5, r15 + + /* save stbcr */ + bsr save_register + mov #SH_SLEEP_REG_STBCR, r0 + + /* save mmu and cache context if needed */ + mov.l @(SH_SLEEP_MODE, r5), r0 + tst #SUSP_SH_MMU, r0 + bt skip_mmu_save_disable + + /* save mmu state */ + bsr save_register + mov #SH_SLEEP_REG_PTEH, r0 + + bsr save_register + mov #SH_SLEEP_REG_PTEL, r0 + + bsr save_register + mov #SH_SLEEP_REG_TTB, r0 + + bsr save_register + mov #SH_SLEEP_REG_TEA, r0 + + bsr save_register + mov #SH_SLEEP_REG_MMUCR, r0 + + bsr save_register + mov #SH_SLEEP_REG_PTEA, r0 + + bsr save_register + mov #SH_SLEEP_REG_PASCR, r0 + + bsr save_register + mov #SH_SLEEP_REG_IRMCR, r0 + + /* invalidate TLBs and disable the MMU */ + bsr get_register + mov #SH_SLEEP_REG_MMUCR, r0 + mov #4, r1 + mov.l r1, @r0 + icbi @r0 + + /* save cache registers and disable caches */ + bsr save_register + mov #SH_SLEEP_REG_CCR, r0 + + bsr save_register + mov #SH_SLEEP_REG_RAMCR, r0 + + bsr get_register + mov #SH_SLEEP_REG_CCR, r0 + mov #0, r1 + mov.l r1, @r0 + icbi @r0 + +skip_mmu_save_disable: + /* call self-refresh entering code if needed */ + mov.l @(SH_SLEEP_MODE, r5), r0 + tst #SUSP_SH_SF, r0 + bt skip_set_sf + + mov.l @(SH_SLEEP_SF_PRE, r5), r0 + jsr @r0 + nop + +skip_set_sf: + mov.l @(SH_SLEEP_MODE, r5), r0 + tst #SUSP_SH_STANDBY, r0 + bt test_rstandby + + /* set mode to "software standby mode" */ + bra do_sleep + mov #0x80, r1 + +test_rstandby: + tst #SUSP_SH_RSTANDBY, r0 + bt test_ustandby + + /* setup BAR register */ + bsr get_register + mov #SH_SLEEP_REG_BAR, r0 + mov.l @(SH_SLEEP_RESUME, r5), r1 + mov.l r1, @r0 + + /* set mode to "r-standby mode" */ + bra do_sleep + mov #0x20, r1 + +test_ustandby: + tst #SUSP_SH_USTANDBY, r0 + bt force_sleep + + /* set mode to "u-standby mode" */ + bra do_sleep + mov #0x10, r1 + +force_sleep: + + /* set mode to "sleep mode" */ + mov #0x00, r1 + +do_sleep: + /* setup and enter selected standby mode */ + bsr get_register + mov #SH_SLEEP_REG_STBCR, r0 + mov.l r1, @r0 +again: + sleep + bra again + nop + +save_register: + add #SH_SLEEP_BASE_ADDR, r0 + mov.l @(r0, r5), r1 + add #-SH_SLEEP_BASE_ADDR, r0 + mov.l @r1, r1 + add #SH_SLEEP_BASE_DATA, r0 + mov.l r1, @(r0, r5) + add #-SH_SLEEP_BASE_DATA, r0 + rts + nop + +get_register: + add #SH_SLEEP_BASE_ADDR, r0 + mov.l @(r0, r5), r0 + rts + nop + +set_sr: + stc sr, r8 + and r9, r8 + or r10, r8 + ldc r8, sr + rts + nop + +save_low_regs: + mov.l r7, @-r15 + mov.l r6, @-r15 + mov.l r5, @-r15 + mov.l r4, @-r15 + mov.l r3, @-r15 + mov.l r2, @-r15 + mov.l r1, @-r15 + rts + mov.l r0, @-r15 + + .balign 4 +rb_bit: .long 0x20000000 ! RB=1 + +ENTRY(sh_mobile_sleep_enter_end) + + .balign 4 +ENTRY(sh_mobile_sleep_resume_start) + + /* figure out start address */ + bsr 0f + nop +0: + sts pr, k1 + mov.l 1f, k0 + and k0, k1 + + /* store pointer to data area in VBR */ + ldc k1, vbr + + /* setup sr with saved sr */ + mov.l @(SH_SLEEP_SR, k1), k0 + ldc k0, sr + + /* now: user register set! */ + stc vbr, r5 + + /* setup spc with return address to c code */ + mov.l @(SH_SLEEP_SPC, r5), r0 + ldc r0, spc + + /* restore vbr */ + mov.l @(SH_SLEEP_VBR, r5), r0 + ldc r0, vbr + + /* setup ssr with saved sr */ + mov.l @(SH_SLEEP_SR, r5), r0 + ldc r0, ssr + + /* restore sp */ + mov.l @(SH_SLEEP_SP, r5), r15 + + /* restore sleep mode register */ + bsr restore_register + mov #SH_SLEEP_REG_STBCR, r0 + + /* call self-refresh resume code if needed */ + mov.l @(SH_SLEEP_MODE, r5), r0 + tst #SUSP_SH_SF, r0 + bt skip_restore_sf + + mov.l @(SH_SLEEP_SF_POST, r5), r0 + jsr @r0 + nop + +skip_restore_sf: + /* restore mmu and cache state if needed */ + mov.l @(SH_SLEEP_MODE, r5), r0 + tst #SUSP_SH_MMU, r0 + bt skip_restore_mmu + + /* restore mmu state */ + bsr restore_register + mov #SH_SLEEP_REG_PTEH, r0 + + bsr restore_register + mov #SH_SLEEP_REG_PTEL, r0 + + bsr restore_register + mov #SH_SLEEP_REG_TTB, r0 + + bsr restore_register + mov #SH_SLEEP_REG_TEA, r0 + + bsr restore_register + mov #SH_SLEEP_REG_PTEA, r0 + + bsr restore_register + mov #SH_SLEEP_REG_PASCR, r0 + + bsr restore_register + mov #SH_SLEEP_REG_IRMCR, r0 + + bsr restore_register + mov #SH_SLEEP_REG_MMUCR, r0 + icbi @r0 + + /* restore cache settings */ + bsr restore_register + mov #SH_SLEEP_REG_RAMCR, r0 + icbi @r0 + + bsr restore_register + mov #SH_SLEEP_REG_CCR, r0 + icbi @r0 + +skip_restore_mmu: + + /* restore general purpose registers if needed */ + mov.l @(SH_SLEEP_MODE, r5), r0 + tst #SUSP_SH_REGS, r0 + bt skip_restore_regs + + /* switch to bank 1, restore low registers */ + mov.l _rb_bit, r10 + bsr _set_sr + mov #-1, r9 + + bsr restore_low_regs + nop + + /* switch to bank0, restore low registers */ + mov.l _rb_bit, r9 + not r9, r9 + bsr _set_sr + mov #0, r10 + + bsr restore_low_regs + nop + + /* restore the rest of the registers */ + mov.l @r15+, r8 + mov.l @r15+, r9 + mov.l @r15+, r10 + mov.l @r15+, r11 + mov.l @r15+, r12 + mov.l @r15+, r13 + mov.l @r15+, r14 + lds.l @r15+, pr + +skip_restore_regs: + rte + nop + +restore_register: + add #SH_SLEEP_BASE_DATA, r0 + mov.l @(r0, r5), r1 + add #-SH_SLEEP_BASE_DATA, r0 + add #SH_SLEEP_BASE_ADDR, r0 + mov.l @(r0, r5), r0 + mov.l r1, @r0 + rts + nop + +_set_sr: + stc sr, r8 + and r9, r8 + or r10, r8 + ldc r8, sr + rts + nop + +restore_low_regs: + mov.l @r15+, r0 + mov.l @r15+, r1 + mov.l @r15+, r2 + mov.l @r15+, r3 + mov.l @r15+, r4 + mov.l @r15+, r5 + mov.l @r15+, r6 + rts + mov.l @r15+, r7 + + .balign 4 +_rb_bit: .long 0x20000000 ! RB=1 +1: .long ~0x7ff +ENTRY(sh_mobile_sleep_resume_end) |