summaryrefslogtreecommitdiff
path: root/arch/sh/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'arch/sh/kernel')
-rw-r--r--arch/sh/kernel/Makefile4
-rw-r--r--arch/sh/kernel/cpu/Makefile9
-rw-r--r--arch/sh/kernel/cpu/bus.c2
-rw-r--r--arch/sh/kernel/cpu/clock.c287
-rw-r--r--arch/sh/kernel/cpu/irq/Makefile7
-rw-r--r--arch/sh/kernel/cpu/irq/imask.c (renamed from arch/sh/kernel/cpu/irq_imask.c)16
-rw-r--r--arch/sh/kernel/cpu/irq/intc2.c284
-rw-r--r--arch/sh/kernel/cpu/irq/ipr.c (renamed from arch/sh/kernel/cpu/irq_ipr.c)217
-rw-r--r--arch/sh/kernel/cpu/irq/pint.c169
-rw-r--r--arch/sh/kernel/cpu/sh3/Makefile7
-rw-r--r--arch/sh/kernel/cpu/sh3/clock-sh3.c89
-rw-r--r--arch/sh/kernel/cpu/sh3/clock-sh7300.c78
-rw-r--r--arch/sh/kernel/cpu/sh3/clock-sh7705.c84
-rw-r--r--arch/sh/kernel/cpu/sh3/clock-sh7709.c96
-rw-r--r--arch/sh/kernel/cpu/sh4/Makefile11
-rw-r--r--arch/sh/kernel/cpu/sh4/clock-sh4-202.c179
-rw-r--r--arch/sh/kernel/cpu/sh4/clock-sh4.c80
-rw-r--r--arch/sh/kernel/cpu/sh4/clock-sh73180.c81
-rw-r--r--arch/sh/kernel/cpu/sh4/clock-sh7770.c73
-rw-r--r--arch/sh/kernel/cpu/sh4/clock-sh7780.c126
-rw-r--r--arch/sh/kernel/cpu/sh4/irq_intc2.c222
-rw-r--r--arch/sh/kernel/io.c41
-rw-r--r--arch/sh/kernel/io_generic.c192
-rw-r--r--arch/sh/kernel/irq.c64
-rw-r--r--arch/sh/kernel/machine_kexec.c112
-rw-r--r--arch/sh/kernel/process.c10
-rw-r--r--arch/sh/kernel/relocate_kernel.S102
-rw-r--r--arch/sh/kernel/time.c518
-rw-r--r--arch/sh/kernel/timers/Makefile8
-rw-r--r--arch/sh/kernel/timers/timer-tmu.c229
-rw-r--r--arch/sh/kernel/timers/timer.c50
31 files changed, 2387 insertions, 1060 deletions
diff --git a/arch/sh/kernel/Makefile b/arch/sh/kernel/Makefile
index 8b81969..7a86eeb 100644
--- a/arch/sh/kernel/Makefile
+++ b/arch/sh/kernel/Makefile
@@ -17,6 +17,4 @@ obj-$(CONFIG_SH_KGDB) += kgdb_stub.o kgdb_jmp.o
obj-$(CONFIG_SH_CPU_FREQ) += cpufreq.o
obj-$(CONFIG_MODULES) += module.o
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
-
-USE_STANDARD_AS_RULE := true
-
+obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
diff --git a/arch/sh/kernel/cpu/Makefile b/arch/sh/kernel/cpu/Makefile
index cd43714..5bfc33b 100644
--- a/arch/sh/kernel/cpu/Makefile
+++ b/arch/sh/kernel/cpu/Makefile
@@ -2,15 +2,12 @@
# Makefile for the Linux/SuperH CPU-specifc backends.
#
-obj-y := irq_ipr.o irq_imask.o init.o bus.o
+obj-y += irq/ init.o bus.o clock.o
obj-$(CONFIG_CPU_SH2) += sh2/
obj-$(CONFIG_CPU_SH3) += sh3/
obj-$(CONFIG_CPU_SH4) += sh4/
-obj-$(CONFIG_SH_RTC) += rtc.o
+obj-$(CONFIG_SH_RTC) += rtc.o
obj-$(CONFIG_UBC_WAKEUP) += ubc.o
-obj-$(CONFIG_SH_ADC) += adc.o
-
-USE_STANDARD_AS_RULE := true
-
+obj-$(CONFIG_SH_ADC) += adc.o
diff --git a/arch/sh/kernel/cpu/bus.c b/arch/sh/kernel/cpu/bus.c
index 3278d23..fc6c4bd 100644
--- a/arch/sh/kernel/cpu/bus.c
+++ b/arch/sh/kernel/cpu/bus.c
@@ -109,6 +109,8 @@ int sh_device_register(struct sh_dev *dev)
/* This is needed for USB OHCI to work */
if (dev->dma_mask)
dev->dev.dma_mask = dev->dma_mask;
+ if (dev->coherent_dma_mask)
+ dev->dev.coherent_dma_mask = dev->coherent_dma_mask;
snprintf(dev->dev.bus_id, BUS_ID_SIZE, "%s%u",
dev->name, dev->dev_id);
diff --git a/arch/sh/kernel/cpu/clock.c b/arch/sh/kernel/cpu/clock.c
new file mode 100644
index 0000000..989e7fd
--- /dev/null
+++ b/arch/sh/kernel/cpu/clock.c
@@ -0,0 +1,287 @@
+/*
+ * arch/sh/kernel/cpu/clock.c - SuperH clock framework
+ *
+ * Copyright (C) 2005 Paul Mundt
+ *
+ * This clock framework is derived from the OMAP version by:
+ *
+ * Copyright (C) 2004 Nokia Corporation
+ * Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/kref.h>
+#include <linux/seq_file.h>
+#include <linux/err.h>
+#include <asm/clock.h>
+#include <asm/timer.h>
+
+static LIST_HEAD(clock_list);
+static DEFINE_SPINLOCK(clock_lock);
+static DECLARE_MUTEX(clock_list_sem);
+
+/*
+ * Each subtype is expected to define the init routines for these clocks,
+ * as each subtype (or processor family) will have these clocks at the
+ * very least. These are all provided through the CPG, which even some of
+ * the more quirky parts (such as ST40, SH4-202, etc.) still have.
+ *
+ * The processor-specific code is expected to register any additional
+ * clock sources that are of interest.
+ */
+static struct clk master_clk = {
+ .name = "master_clk",
+ .flags = CLK_ALWAYS_ENABLED | CLK_RATE_PROPAGATES,
+#ifdef CONFIG_SH_PCLK_FREQ_BOOL
+ .rate = CONFIG_SH_PCLK_FREQ,
+#endif
+};
+
+static struct clk module_clk = {
+ .name = "module_clk",
+ .parent = &master_clk,
+ .flags = CLK_ALWAYS_ENABLED | CLK_RATE_PROPAGATES,
+};
+
+static struct clk bus_clk = {
+ .name = "bus_clk",
+ .parent = &master_clk,
+ .flags = CLK_ALWAYS_ENABLED | CLK_RATE_PROPAGATES,
+};
+
+static struct clk cpu_clk = {
+ .name = "cpu_clk",
+ .parent = &master_clk,
+ .flags = CLK_ALWAYS_ENABLED,
+};
+
+/*
+ * The ordering of these clocks matters, do not change it.
+ */
+static struct clk *onchip_clocks[] = {
+ &master_clk,
+ &module_clk,
+ &bus_clk,
+ &cpu_clk,
+};
+
+static void propagate_rate(struct clk *clk)
+{
+ struct clk *clkp;
+
+ list_for_each_entry(clkp, &clock_list, node) {
+ if (likely(clkp->parent != clk))
+ continue;
+ if (likely(clkp->ops && clkp->ops->recalc))
+ clkp->ops->recalc(clkp);
+ }
+}
+
+int __clk_enable(struct clk *clk)
+{
+ /*
+ * See if this is the first time we're enabling the clock, some
+ * clocks that are always enabled still require "special"
+ * initialization. This is especially true if the clock mode
+ * changes and the clock needs to hunt for the proper set of
+ * divisors to use before it can effectively recalc.
+ */
+ if (unlikely(atomic_read(&clk->kref.refcount) == 1))
+ if (clk->ops && clk->ops->init)
+ clk->ops->init(clk);
+
+ if (clk->flags & CLK_ALWAYS_ENABLED)
+ return 0;
+
+ if (likely(clk->ops && clk->ops->enable))
+ clk->ops->enable(clk);
+
+ kref_get(&clk->kref);
+ return 0;
+}
+
+int clk_enable(struct clk *clk)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&clock_lock, flags);
+ ret = __clk_enable(clk);
+ spin_unlock_irqrestore(&clock_lock, flags);
+
+ return ret;
+}
+
+static void clk_kref_release(struct kref *kref)
+{
+ /* Nothing to do */
+}
+
+void __clk_disable(struct clk *clk)
+{
+ if (clk->flags & CLK_ALWAYS_ENABLED)
+ return;
+
+ kref_put(&clk->kref, clk_kref_release);
+}
+
+void clk_disable(struct clk *clk)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&clock_lock, flags);
+ __clk_disable(clk);
+ spin_unlock_irqrestore(&clock_lock, flags);
+}
+
+int clk_register(struct clk *clk)
+{
+ down(&clock_list_sem);
+
+ list_add(&clk->node, &clock_list);
+ kref_init(&clk->kref);
+
+ up(&clock_list_sem);
+
+ return 0;
+}
+
+void clk_unregister(struct clk *clk)
+{
+ down(&clock_list_sem);
+ list_del(&clk->node);
+ up(&clock_list_sem);
+}
+
+inline unsigned long clk_get_rate(struct clk *clk)
+{
+ return clk->rate;
+}
+
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ int ret = -EOPNOTSUPP;
+
+ if (likely(clk->ops && clk->ops->set_rate)) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&clock_lock, flags);
+ ret = clk->ops->set_rate(clk, rate);
+ spin_unlock_irqrestore(&clock_lock, flags);
+ }
+
+ if (unlikely(clk->flags & CLK_RATE_PROPAGATES))
+ propagate_rate(clk);
+
+ return ret;
+}
+
+void clk_recalc_rate(struct clk *clk)
+{
+ if (likely(clk->ops && clk->ops->recalc)) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&clock_lock, flags);
+ clk->ops->recalc(clk);
+ spin_unlock_irqrestore(&clock_lock, flags);
+ }
+
+ if (unlikely(clk->flags & CLK_RATE_PROPAGATES))
+ propagate_rate(clk);
+}
+
+struct clk *clk_get(const char *id)
+{
+ struct clk *p, *clk = ERR_PTR(-ENOENT);
+
+ down(&clock_list_sem);
+ list_for_each_entry(p, &clock_list, node) {
+ if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) {
+ clk = p;
+ break;
+ }
+ }
+ up(&clock_list_sem);
+
+ return clk;
+}
+
+void clk_put(struct clk *clk)
+{
+ if (clk && !IS_ERR(clk))
+ module_put(clk->owner);
+}
+
+void __init __attribute__ ((weak))
+arch_init_clk_ops(struct clk_ops **ops, int type)
+{
+}
+
+int __init clk_init(void)
+{
+ int i, ret = 0;
+
+ if (unlikely(!master_clk.rate))
+ /*
+ * NOTE: This will break if the default divisor has been
+ * changed.
+ *
+ * No one should be changing the default on us however,
+ * expect that a sane value for CONFIG_SH_PCLK_FREQ will
+ * be defined in the event of a different divisor.
+ */
+ master_clk.rate = get_timer_frequency() * 4;
+
+ for (i = 0; i < ARRAY_SIZE(onchip_clocks); i++) {
+ struct clk *clk = onchip_clocks[i];
+
+ arch_init_clk_ops(&clk->ops, i);
+ ret |= clk_register(clk);
+ clk_enable(clk);
+ }
+
+ /* Kick the child clocks.. */
+ propagate_rate(&master_clk);
+ propagate_rate(&bus_clk);
+
+ return ret;
+}
+
+int show_clocks(struct seq_file *m)
+{
+ struct clk *clk;
+
+ list_for_each_entry_reverse(clk, &clock_list, node) {
+ unsigned long rate = clk_get_rate(clk);
+
+ /*
+ * Don't bother listing dummy clocks with no ancestry
+ * that only support enable and disable ops.
+ */
+ if (unlikely(!rate && !clk->parent))
+ continue;
+
+ seq_printf(m, "%-12s\t: %ld.%02ldMHz\n", clk->name,
+ rate / 1000000, (rate % 1000000) / 10000);
+ }
+
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(clk_register);
+EXPORT_SYMBOL_GPL(clk_unregister);
+EXPORT_SYMBOL_GPL(clk_get);
+EXPORT_SYMBOL_GPL(clk_put);
+EXPORT_SYMBOL_GPL(clk_enable);
+EXPORT_SYMBOL_GPL(clk_disable);
+EXPORT_SYMBOL_GPL(__clk_enable);
+EXPORT_SYMBOL_GPL(__clk_disable);
+EXPORT_SYMBOL_GPL(clk_get_rate);
+EXPORT_SYMBOL_GPL(clk_set_rate);
+EXPORT_SYMBOL_GPL(clk_recalc_rate);
diff --git a/arch/sh/kernel/cpu/irq/Makefile b/arch/sh/kernel/cpu/irq/Makefile
new file mode 100644
index 0000000..e3cccea
--- /dev/null
+++ b/arch/sh/kernel/cpu/irq/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the Linux/SuperH CPU-specifc IRQ handlers.
+#
+obj-y += ipr.o imask.o
+
+obj-$(CONFIG_CPU_HAS_PINT_IRQ) += pint.o
+obj-$(CONFIG_CPU_HAS_INTC2_IRQ) += intc2.o
diff --git a/arch/sh/kernel/cpu/irq_imask.c b/arch/sh/kernel/cpu/irq/imask.c
index a963d00..baed9a5 100644
--- a/arch/sh/kernel/cpu/irq_imask.c
+++ b/arch/sh/kernel/cpu/irq/imask.c
@@ -1,16 +1,12 @@
-/* $Id: irq_imask.c,v 1.1.2.1 2002/11/17 10:53:43 mrbrown Exp $
- *
- * linux/arch/sh/kernel/irq_imask.c
+/*
+ * 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>
@@ -19,13 +15,11 @@
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/bitops.h>
-
-#include <asm/system.h>
-#include <asm/irq.h>
-
#include <linux/spinlock.h>
#include <linux/cache.h>
#include <linux/irq.h>
+#include <asm/system.h>
+#include <asm/irq.h>
/* Bitmap of IRQ masked */
static unsigned long imask_mask = 0x7fff;
@@ -40,7 +34,7 @@ static void end_imask_irq(unsigned int irq);
#define IMASK_PRIORITY 15
static unsigned int startup_imask_irq(unsigned int irq)
-{
+{
/* Nothing to do */
return 0; /* never anything pending */
}
diff --git a/arch/sh/kernel/cpu/irq/intc2.c b/arch/sh/kernel/cpu/irq/intc2.c
new file mode 100644
index 0000000..06e8afa
--- /dev/null
+++ b/arch/sh/kernel/cpu/irq/intc2.c
@@ -0,0 +1,284 @@
+/*
+ * Interrupt handling for INTC2-based IRQ.
+ *
+ * Copyright (C) 2001 David J. Mckay (david.mckay@st.com)
+ * Copyright (C) 2005, 2006 Paul Mundt (lethal@linux-sh.org)
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License. See linux/COPYING for more information.
+ *
+ * These are the "new Hitachi style" interrupts, as present on the
+ * Hitachi 7751, the STM ST40 STB1, SH7760, and SH7780.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/machvec.h>
+
+struct intc2_data {
+ unsigned char msk_offset;
+ unsigned char msk_shift;
+
+ int (*clear_irq) (int);
+};
+
+static struct intc2_data intc2_data[NR_INTC2_IRQS];
+
+static void enable_intc2_irq(unsigned int irq);
+static void disable_intc2_irq(unsigned int irq);
+
+/* shutdown is same as "disable" */
+#define shutdown_intc2_irq disable_intc2_irq
+
+static void mask_and_ack_intc2(unsigned int);
+static void end_intc2_irq(unsigned int irq);
+
+static unsigned int startup_intc2_irq(unsigned int irq)
+{
+ enable_intc2_irq(irq);
+ return 0; /* never anything pending */
+}
+
+static struct hw_interrupt_type intc2_irq_type = {
+ .typename = "INTC2-IRQ",
+ .startup = startup_intc2_irq,
+ .shutdown = shutdown_intc2_irq,
+ .enable = enable_intc2_irq,
+ .disable = disable_intc2_irq,
+ .ack = mask_and_ack_intc2,
+ .end = end_intc2_irq
+};
+
+static void disable_intc2_irq(unsigned int irq)
+{
+ int irq_offset = irq - INTC2_FIRST_IRQ;
+ int msk_shift, msk_offset;
+
+ /* Sanity check */
+ if (unlikely(irq_offset < 0 || irq_offset >= NR_INTC2_IRQS))
+ return;
+
+ msk_shift = intc2_data[irq_offset].msk_shift;
+ msk_offset = intc2_data[irq_offset].msk_offset;
+
+ ctrl_outl(1 << msk_shift,
+ INTC2_BASE + INTC2_INTMSK_OFFSET + msk_offset);
+}
+
+static void enable_intc2_irq(unsigned int irq)
+{
+ int irq_offset = irq - INTC2_FIRST_IRQ;
+ int msk_shift, msk_offset;
+
+ /* Sanity check */
+ if (unlikely(irq_offset < 0 || irq_offset >= NR_INTC2_IRQS))
+ return;
+
+ msk_shift = intc2_data[irq_offset].msk_shift;
+ msk_offset = intc2_data[irq_offset].msk_offset;
+
+ ctrl_outl(1 << msk_shift,
+ INTC2_BASE + INTC2_INTMSKCLR_OFFSET + msk_offset);
+}
+
+static void mask_and_ack_intc2(unsigned int irq)
+{
+ disable_intc2_irq(irq);
+}
+
+static void end_intc2_irq(unsigned int irq)
+{
+ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+ enable_intc2_irq(irq);
+
+ if (unlikely(intc2_data[irq - INTC2_FIRST_IRQ].clear_irq))
+ intc2_data[irq - INTC2_FIRST_IRQ].clear_irq(irq);
+}
+
+/*
+ * Setup an INTC2 style interrupt.
+ * NOTE: Unlike IPR interrupts, parameters are not shifted by this code,
+ * allowing the use of the numbers straight out of the datasheet.
+ * For example:
+ * PIO1 which is INTPRI00[19,16] and INTMSK00[13]
+ * would be: ^ ^ ^ ^
+ * | | | |
+ * make_intc2_irq(84, 0, 16, 0, 13);
+ */
+void make_intc2_irq(unsigned int irq,
+ unsigned int ipr_offset, unsigned int ipr_shift,
+ unsigned int msk_offset, unsigned int msk_shift,
+ unsigned int priority)
+{
+ int irq_offset = irq - INTC2_FIRST_IRQ;
+ unsigned int flags;
+ unsigned long ipr;
+
+ if (unlikely(irq_offset < 0 || irq_offset >= NR_INTC2_IRQS))
+ return;
+
+ disable_irq_nosync(irq);
+
+ /* Fill the data we need */
+ intc2_data[irq_offset].msk_offset = msk_offset;
+ intc2_data[irq_offset].msk_shift = msk_shift;
+ intc2_data[irq_offset].clear_irq = NULL;
+
+ /* Set the priority level */
+ local_irq_save(flags);
+
+ ipr = ctrl_inl(INTC2_BASE + INTC2_INTPRI_OFFSET + ipr_offset);
+ ipr &= ~(0xf << ipr_shift);
+ ipr |= priority << ipr_shift;
+ ctrl_outl(ipr, INTC2_BASE + INTC2_INTPRI_OFFSET + ipr_offset);
+
+ local_irq_restore(flags);
+
+ irq_desc[irq].handler = &intc2_irq_type;
+
+ disable_intc2_irq(irq);
+}
+
+static struct intc2_init {
+ unsigned short irq;
+ unsigned char ipr_offset, ipr_shift;
+ unsigned char msk_offset, msk_shift;
+ unsigned char priority;
+} intc2_init_data[] __initdata = {
+#if defined(CONFIG_CPU_SUBTYPE_ST40)
+ {64, 0, 0, 0, 0, 13}, /* PCI serr */
+ {65, 0, 4, 0, 1, 13}, /* PCI err */
+ {66, 0, 4, 0, 2, 13}, /* PCI ad */
+ {67, 0, 4, 0, 3, 13}, /* PCI pwd down */
+ {72, 0, 8, 0, 5, 13}, /* DMAC INT0 */
+ {73, 0, 8, 0, 6, 13}, /* DMAC INT1 */
+ {74, 0, 8, 0, 7, 13}, /* DMAC INT2 */
+ {75, 0, 8, 0, 8, 13}, /* DMAC INT3 */
+ {76, 0, 8, 0, 9, 13}, /* DMAC INT4 */
+ {78, 0, 8, 0, 11, 13}, /* DMAC ERR */
+ {80, 0, 12, 0, 12, 13}, /* PIO0 */
+ {84, 0, 16, 0, 13, 13}, /* PIO1 */
+ {88, 0, 20, 0, 14, 13}, /* PIO2 */
+ {112, 4, 0, 4, 0, 13}, /* Mailbox */
+ #ifdef CONFIG_CPU_SUBTYPE_ST40GX1
+ {116, 4, 4, 4, 4, 13}, /* SSC0 */
+ {120, 4, 8, 4, 8, 13}, /* IR Blaster */
+ {124, 4, 12, 4, 12, 13}, /* USB host */
+ {128, 4, 16, 4, 16, 13}, /* Video processor BLITTER */
+ {132, 4, 20, 4, 20, 13}, /* UART0 */
+ {134, 4, 20, 4, 22, 13}, /* UART2 */
+ {136, 4, 24, 4, 24, 13}, /* IO_PIO0 */
+ {140, 4, 28, 4, 28, 13}, /* EMPI */
+ {144, 8, 0, 8, 0, 13}, /* MAFE */
+ {148, 8, 4, 8, 4, 13}, /* PWM */
+ {152, 8, 8, 8, 8, 13}, /* SSC1 */
+ {156, 8, 12, 8, 12, 13}, /* IO_PIO1 */
+ {160, 8, 16, 8, 16, 13}, /* USB target */
+ {164, 8, 20, 8, 20, 13}, /* UART1 */
+ {168, 8, 24, 8, 24, 13}, /* Teletext */
+ {172, 8, 28, 8, 28, 13}, /* VideoSync VTG */
+ {173, 8, 28, 8, 29, 13}, /* VideoSync DVP0 */
+ {174, 8, 28, 8, 30, 13}, /* VideoSync DVP1 */
+#endif
+#elif defined(CONFIG_CPU_SUBTYPE_SH7760)
+/*
+ * SH7760 INTC2-Style interrupts, vectors IRQ48-111 INTEVT 0x800-0xFE0
+ */
+ /* INTPRIO0 | INTMSK0 */
+ {48, 0, 28, 0, 31, 3}, /* IRQ 4 */
+ {49, 0, 24, 0, 30, 3}, /* IRQ 3 */
+ {50, 0, 20, 0, 29, 3}, /* IRQ 2 */
+ {51, 0, 16, 0, 28, 3}, /* IRQ 1 */
+ /* 52-55 (INTEVT 0x880-0x8E0) unused/reserved */
+ /* INTPRIO4 | INTMSK0 */
+ {56, 4, 28, 0, 25, 3}, /* HCAN2_CHAN0 */
+ {57, 4, 24, 0, 24, 3}, /* HCAN2_CHAN1 */
+ {58, 4, 20, 0, 23, 3}, /* I2S_CHAN0 */
+ {59, 4, 16, 0, 22, 3}, /* I2S_CHAN1 */
+ {60, 4, 12, 0, 21, 3}, /* AC97_CHAN0 */
+ {61, 4, 8, 0, 20, 3}, /* AC97_CHAN1 */
+ {62, 4, 4, 0, 19, 3}, /* I2C_CHAN0 */
+ {63, 4, 0, 0, 18, 3}, /* I2C_CHAN1 */
+ /* INTPRIO8 | INTMSK0 */
+ {52, 8, 16, 0, 11, 3}, /* SCIF0_ERI_IRQ */
+ {53, 8, 16, 0, 10, 3}, /* SCIF0_RXI_IRQ */
+ {54, 8, 16, 0, 9, 3}, /* SCIF0_BRI_IRQ */
+ {55, 8, 16, 0, 8, 3}, /* SCIF0_TXI_IRQ */
+ {64, 8, 28, 0, 17, 3}, /* USBHI_IRQ */
+ {65, 8, 24, 0, 16, 3}, /* LCDC */
+ /* 66, 67 unused */
+ {68, 8, 20, 0, 14, 13}, /* DMABRGI0_IRQ */
+ {69, 8, 20, 0, 13, 13}, /* DMABRGI1_IRQ */
+ {70, 8, 20, 0, 12, 13}, /* DMABRGI2_IRQ */
+ /* 71 unused */
+ {72, 8, 12, 0, 7, 3}, /* SCIF1_ERI_IRQ */
+ {73, 8, 12, 0, 6, 3}, /* SCIF1_RXI_IRQ */
+ {74, 8, 12, 0, 5, 3}, /* SCIF1_BRI_IRQ */
+ {75, 8, 12, 0, 4, 3}, /* SCIF1_TXI_IRQ */
+ {76, 8, 8, 0, 3, 3}, /* SCIF2_ERI_IRQ */
+ {77, 8, 8, 0, 2, 3}, /* SCIF2_RXI_IRQ */
+ {78, 8, 8, 0, 1, 3}, /* SCIF2_BRI_IRQ */
+ {79, 8, 8, 0, 0, 3}, /* SCIF2_TXI_IRQ */
+ /* | INTMSK4 */
+ {80, 8, 4, 4, 23, 3}, /* SIM_ERI */
+ {81, 8, 4, 4, 22, 3}, /* SIM_RXI */
+ {82, 8, 4, 4, 21, 3}, /* SIM_TXI */
+ {83, 8, 4, 4, 20, 3}, /* SIM_TEI */
+ {84, 8, 0, 4, 19, 3}, /* HSPII */
+ /* INTPRIOC | INTMSK4 */
+ /* 85-87 unused/reserved */
+ {88, 12, 20, 4, 18, 3}, /* MMCI0 */
+ {89, 12, 20, 4, 17, 3}, /* MMCI1 */
+ {90, 12, 20, 4, 16, 3}, /* MMCI2 */
+ {91, 12, 20, 4, 15, 3}, /* MMCI3 */
+ {92, 12, 12, 4, 6, 3}, /* MFI (unsure, bug? in my 7760 manual*/
+ /* 93-107 reserved/undocumented */
+ {108,12, 4, 4, 1, 3}, /* ADC */
+ {109,12, 0, 4, 0, 3}, /* CMTI */
+ /* 110-111 reserved/unused */
+#elif defined(CONFIG_CPU_SUBTYPE_SH7780)
+ { TIMER_IRQ, 0, 24, 0, INTC_TMU0_MSK, 2},
+#ifdef CONFIG_SH_RTC
+ { RTC_IRQ, 4, 0, 0, INTC_RTC_MSK, TIMER_PRIORITY },
+#endif
+ { SCIF0_ERI_IRQ, 8, 24, 0, INTC_SCIF0_MSK, SCIF0_PRIORITY },
+ { SCIF0_RXI_IRQ, 8, 24, 0, INTC_SCIF0_MSK, SCIF0_PRIORITY },
+ { SCIF0_BRI_IRQ, 8, 24, 0, INTC_SCIF0_MSK, SCIF0_PRIORITY },
+ { SCIF0_TXI_IRQ, 8, 24, 0, INTC_SCIF0_MSK, SCIF0_PRIORITY },
+
+ { SCIF1_ERI_IRQ, 8, 16, 0, INTC_SCIF1_MSK, SCIF1_PRIORITY },
+ { SCIF1_RXI_IRQ, 8, 16, 0, INTC_SCIF1_MSK, SCIF1_PRIORITY },
+ { SCIF1_BRI_IRQ, 8, 16, 0, INTC_SCIF1_MSK, SCIF1_PRIORITY },
+ { SCIF1_TXI_IRQ, 8, 16, 0, INTC_SCIF1_MSK, SCIF1_PRIORITY },
+
+ { PCIC0_IRQ, 0x10, 8, 0, INTC_PCIC0_MSK, PCIC0_PRIORITY },
+ { PCIC1_IRQ, 0x10, 0, 0, INTC_PCIC1_MSK, PCIC1_PRIORITY },
+ { PCIC2_IRQ, 0x14, 24, 0, INTC_PCIC2_MSK, PCIC2_PRIORITY },
+ { PCIC3_IRQ, 0x14, 16, 0, INTC_PCIC3_MSK, PCIC3_PRIORITY },
+ { PCIC4_IRQ, 0x14, 8, 0, INTC_PCIC4_MSK, PCIC4_PRIORITY },
+#endif
+};
+
+void __init init_IRQ_intc2(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(intc2_init_data); i++) {
+ struct intc2_init *p = intc2_init_data + i;
+ make_intc2_irq(p->irq, p->ipr_offset, p->ipr_shift,
+ p-> msk_offset, p->msk_shift, p->priority);
+ }
+}
+
+/* Adds a termination callback to the interrupt */
+void intc2_add_clear_irq(int irq, int (*fn)(int))
+{
+ if (unlikely(irq < INTC2_FIRST_IRQ))
+ return;
+
+ intc2_data[irq - INTC2_FIRST_IRQ].clear_irq = fn;
+}
+
diff --git a/arch/sh/kernel/cpu/irq_ipr.c b/arch/sh/kernel/cpu/irq/ipr.c
index 71f9209..fdbd718 100644
--- a/arch/sh/kernel/cpu/irq_ipr.c
+++ b/arch/sh/kernel/cpu/irq/ipr.c
@@ -1,6 +1,5 @@
-/* $Id: irq_ipr.c,v 1.1.2.1 2002/11/17 10:53:43 mrbrown Exp $
- *
- * linux/arch/sh/kernel/irq_ipr.c
+/*
+ * arch/sh/kernel/cpu/irq/ipr.c
*
* Copyright (C) 1999 Niibe Yutaka & Takeshi Yaegashi
* Copyright (C) 2000 Kazumoto Kojima
@@ -109,7 +108,8 @@ static void end_ipr_irq(unsigned int irq)
enable_ipr_irq(irq);
}
-void make_ipr_irq(unsigned int irq, unsigned int addr, int pos, int priority)
+void make_ipr_irq(unsigned int irq, unsigned int addr, int pos,
+ int priority, int maskpos)
{
disable_irq_nosync(irq);
ipr_data[irq].addr = addr;
@@ -120,126 +120,47 @@ void make_ipr_irq(unsigned int irq, unsigned int addr, int pos, int priority)
disable_ipr_irq(irq);
}
-#if defined(CONFIG_CPU_SUBTYPE_SH7705) || \
- defined(CONFIG_CPU_SUBTYPE_SH7707) || \
- defined(CONFIG_CPU_SUBTYPE_SH7709)
-static unsigned char pint_map[256];
-static unsigned long portcr_mask = 0;
-
-static void enable_pint_irq(unsigned int irq);
-static void disable_pint_irq(unsigned int irq);
-
-/* shutdown is same as "disable" */
-#define shutdown_pint_irq disable_pint_irq
-
-static void mask_and_ack_pint(unsigned int);
-static void end_pint_irq(unsigned int irq);
-
-static unsigned int startup_pint_irq(unsigned int irq)
-{
- enable_pint_irq(irq);
- return 0; /* never anything pending */
-}
-
-static struct hw_interrupt_type pint_irq_type = {
- .typename = "PINT-IRQ",
- .startup = startup_pint_irq,
- .shutdown = shutdown_pint_irq,
- .enable = enable_pint_irq,
- .disable = disable_pint_irq,
- .ack = mask_and_ack_pint,
- .end = end_pint_irq
-};
-
-static void disable_pint_irq(unsigned int irq)
-{
- unsigned long val, flags;
-
- local_irq_save(flags);
- val = ctrl_inw(INTC_INTER);
- val &= ~(1 << (irq - PINT_IRQ_BASE));
- ctrl_outw(val, INTC_INTER); /* disable PINTn */
- portcr_mask &= ~(3 << (irq - PINT_IRQ_BASE)*2);
- local_irq_restore(flags);
-}
-
-static void enable_pint_irq(unsigned int irq)
-{
- unsigned long val, flags;
-
- local_irq_save(flags);
- val = ctrl_inw(INTC_INTER);
- val |= 1 << (irq - PINT_IRQ_BASE);
- ctrl_outw(val, INTC_INTER); /* enable PINTn */
- portcr_mask |= 3 << (irq - PINT_IRQ_BASE)*2;
- local_irq_restore(flags);
-}
-
-static void mask_and_ack_pint(unsigned int irq)
-{
- disable_pint_irq(irq);
-}
-
-static void end_pint_irq(unsigned int irq)
-{
- if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
- enable_pint_irq(irq);
-}
-
-void make_pint_irq(unsigned int irq)
-{
- disable_irq_nosync(irq);
- irq_desc[irq].handler = &pint_irq_type;
- disable_pint_irq(irq);
-}
-#endif
-
void __init init_IRQ(void)
{
-#if defined(CONFIG_CPU_SUBTYPE_SH7705) || \
- defined(CONFIG_CPU_SUBTYPE_SH7707) || \
- defined(CONFIG_CPU_SUBTYPE_SH7709)
- int i;
-#endif
-
- make_ipr_irq(TIMER_IRQ, TIMER_IPR_ADDR, TIMER_IPR_POS, TIMER_PRIORITY);
- make_ipr_irq(TIMER1_IRQ, TIMER1_IPR_ADDR, TIMER1_IPR_POS, TIMER1_PRIORITY);
+#ifndef CONFIG_CPU_SUBTYPE_SH7780
+ make_ipr_irq(TIMER_IRQ, TIMER_IPR_ADDR, TIMER_IPR_POS, TIMER_PRIORITY, 0);
+ make_ipr_irq(TIMER1_IRQ, TIMER1_IPR_ADDR, TIMER1_IPR_POS, TIMER1_PRIORITY, 0);
#if defined(CONFIG_SH_RTC)
- make_ipr_irq(RTC_IRQ, RTC_IPR_ADDR, RTC_IPR_POS, RTC_PRIORITY);
+ make_ipr_irq(RTC_IRQ, RTC_IPR_ADDR, RTC_IPR_POS, RTC_PRIORITY, 0);
#endif
#ifdef SCI_ERI_IRQ
- make_ipr_irq(SCI_ERI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY);
- make_ipr_irq(SCI_RXI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY);
- make_ipr_irq(SCI_TXI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY);
+ make_ipr_irq(SCI_ERI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY, 0);
+ make_ipr_irq(SCI_RXI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY, 0);
+ make_ipr_irq(SCI_TXI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY, 0);
#endif
#ifdef SCIF1_ERI_IRQ
- make_ipr_irq(SCIF1_ERI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY);
- make_ipr_irq(SCIF1_RXI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY);
- make_ipr_irq(SCIF1_BRI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY);
- make_ipr_irq(SCIF1_TXI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY);
+ make_ipr_irq(SCIF1_ERI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY, 0);
+ make_ipr_irq(SCIF1_RXI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY, 0);
+ make_ipr_irq(SCIF1_BRI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY, 0);
+ make_ipr_irq(SCIF1_TXI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY, 0);
#endif
#if defined(CONFIG_CPU_SUBTYPE_SH7300)
- make_ipr_irq(SCIF0_IRQ, SCIF0_IPR_ADDR, SCIF0_IPR_POS, SCIF0_PRIORITY);
- make_ipr_irq(DMTE2_IRQ, DMA1_IPR_ADDR, DMA1_IPR_POS, DMA1_PRIORITY);
- make_ipr_irq(DMTE3_IRQ, DMA1_IPR_ADDR, DMA1_IPR_POS, DMA1_PRIORITY);
- make_ipr_irq(VIO_IRQ, VIO_IPR_ADDR, VIO_IPR_POS, VIO_PRIORITY);
+ make_ipr_irq(SCIF0_IRQ, SCIF0_IPR_ADDR, SCIF0_IPR_POS, SCIF0_PRIORITY, 0);
+ make_ipr_irq(DMTE2_IRQ, DMA1_IPR_ADDR, DMA1_IPR_POS, DMA1_PRIORITY, 0);
+ make_ipr_irq(DMTE3_IRQ, DMA1_IPR_ADDR, DMA1_IPR_POS, DMA1_PRIORITY, 0);
+ make_ipr_irq(VIO_IRQ, VIO_IPR_ADDR, VIO_IPR_POS, VIO_PRIORITY, 0);
#endif
#ifdef SCIF_ERI_IRQ
- make_ipr_irq(SCIF_ERI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY);
- make_ipr_irq(SCIF_RXI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY);
- make_ipr_irq(SCIF_BRI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY);
- make_ipr_irq(SCIF_TXI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY);
+ make_ipr_irq(SCIF_ERI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY, 0);
+ make_ipr_irq(SCIF_RXI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY, 0);
+ make_ipr_irq(SCIF_BRI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY, 0);
+ make_ipr_irq(SCIF_TXI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY, 0);
#endif
#ifdef IRDA_ERI_IRQ
- make_ipr_irq(IRDA_ERI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY);
- make_ipr_irq(IRDA_RXI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY);
- make_ipr_irq(IRDA_BRI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY);
- make_ipr_irq(IRDA_TXI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY);
+ make_ipr_irq(IRDA_ERI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY, 0);
+ make_ipr_irq(IRDA_RXI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY, 0);
+ make_ipr_irq(IRDA_BRI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY, 0);
+ make_ipr_irq(IRDA_TXI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY, 0);
#endif
#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) || \
@@ -254,86 +175,32 @@ void __init init_IRQ(void)
* You should set corresponding bits of PFC to "00"
* to enable these interrupts.
*/
- make_ipr_irq(IRQ0_IRQ, IRQ0_IPR_ADDR, IRQ0_IPR_POS, IRQ0_PRIORITY);
- make_ipr_irq(IRQ1_IRQ, IRQ1_IPR_ADDR, IRQ1_IPR_POS, IRQ1_PRIORITY);
- make_ipr_irq(IRQ2_IRQ, IRQ2_IPR_ADDR, IRQ2_IPR_POS, IRQ2_PRIORITY);
- make_ipr_irq(IRQ3_IRQ, IRQ3_IPR_ADDR, IRQ3_IPR_POS, IRQ3_PRIORITY);
- make_ipr_irq(IRQ4_IRQ, IRQ4_IPR_ADDR, IRQ4_IPR_POS, IRQ4_PRIORITY);
- make_ipr_irq(IRQ5_IRQ, IRQ5_IPR_ADDR, IRQ5_IPR_POS, IRQ5_PRIORITY);
-#if !defined(CONFIG_CPU_SUBTYPE_SH7300)
- make_ipr_irq(PINT0_IRQ, PINT0_IPR_ADDR, PINT0_IPR_POS, PINT0_PRIORITY);
- make_ipr_irq(PINT8_IRQ, PINT8_IPR_ADDR, PINT8_IPR_POS, PINT8_PRIORITY);
- enable_ipr_irq(PINT0_IRQ);
- enable_ipr_irq(PINT8_IRQ);
+ make_ipr_irq(IRQ0_IRQ, IRQ0_IPR_ADDR, IRQ0_IPR_POS, IRQ0_PRIORITY, 0);
+ make_ipr_irq(IRQ1_IRQ, IRQ1_IPR_ADDR, IRQ1_IPR_POS, IRQ1_PRIORITY, 0);
+ make_ipr_irq(IRQ2_IRQ, IRQ2_IPR_ADDR, IRQ2_IPR_POS, IRQ2_PRIORITY, 0);
+ make_ipr_irq(IRQ3_IRQ, IRQ3_IPR_ADDR, IRQ3_IPR_POS, IRQ3_PRIORITY, 0);
+ make_ipr_irq(IRQ4_IRQ, IRQ4_IPR_ADDR, IRQ4_IPR_POS, IRQ4_PRIORITY, 0);
+ make_ipr_irq(IRQ5_IRQ, IRQ5_IPR_ADDR, IRQ5_IPR_POS, IRQ5_PRIORITY, 0);
+#endif
+#endif
- for(i = 0; i < 16; i++)
- make_pint_irq(PINT_IRQ_BASE + i);
- for(i = 0; i < 256; i++)
- {
- if(i & 1) pint_map[i] = 0;
- else if(i & 2) pint_map[i] = 1;
- else if(i & 4) pint_map[i] = 2;
- else if(i & 8) pint_map[i] = 3;
- else if(i & 0x10) pint_map[i] = 4;
- else if(i & 0x20) pint_map[i] = 5;
- else if(i & 0x40) pint_map[i] = 6;
- else if(i & 0x80) pint_map[i] = 7;
- }
-#endif /* !CONFIG_CPU_SUBTYPE_SH7300 */
-#endif /* CONFIG_CPU_SUBTYPE_SH7707 || CONFIG_CPU_SUBTYPE_SH7709 || CONFIG_CPU_SUBTYPE_SH7300*/
+#ifdef CONFIG_CPU_HAS_PINT_IRQ
+ init_IRQ_pint();
+#endif
-#ifdef CONFIG_CPU_SUBTYPE_ST40
+#ifdef CONFIG_CPU_HAS_INTC2_IRQ
init_IRQ_intc2();
#endif
-
/* Perform the machine specific initialisation */
- if (sh_mv.mv_init_irq != NULL) {
+ if (sh_mv.mv_init_irq != NULL)
sh_mv.mv_init_irq();
- }
}
-#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) || \
- defined(CONFIG_CPU_SUBTYPE_SH7300) || defined(CONFIG_CPU_SUBTYPE_SH7705)
+
+#if !defined(CONFIG_CPU_HAS_PINT_IRQ)
int ipr_irq_demux(int irq)
{
-#if !defined(CONFIG_CPU_SUBTYPE_SH7300)
- unsigned long creg, dreg, d, sav;
-
- if(irq == PINT0_IRQ)
- {
-#if defined(CONFIG_CPU_SUBTYPE_SH7707)
- creg = PORT_PACR;
- dreg = PORT_PADR;
-#else
- creg = PORT_PCCR;
- dreg = PORT_PCDR;
-#endif
- sav = ctrl_inw(creg);
- ctrl_outw(sav | portcr_mask, creg);
- d = (~ctrl_inb(dreg) ^ ctrl_inw(INTC_ICR2)) & ctrl_inw(INTC_INTER) & 0xff;
- ctrl_outw(sav, creg);
- if(d == 0) return irq;
- return PINT_IRQ_BASE + pint_map[d];
- }
- else if(irq == PINT8_IRQ)
- {
-#if defined(CONFIG_CPU_SUBTYPE_SH7707)
- creg = PORT_PBCR;
- dreg = PORT_PBDR;
-#else
- creg = PORT_PFCR;
- dreg = PORT_PFDR;
-#endif
- sav = ctrl_inw(creg);
- ctrl_outw(sav | (portcr_mask >> 16), creg);
- d = (~ctrl_inb(dreg) ^ (ctrl_inw(INTC_ICR2) >> 8)) & (ctrl_inw(INTC_INTER) >> 8) & 0xff;
- ctrl_outw(sav, creg);
- if(d == 0) return irq;
- return PINT_IRQ_BASE + 8 + pint_map[d];
- }
-#endif
return irq;
}
#endif
EXPORT_SYMBOL(make_ipr_irq);
-
diff --git a/arch/sh/kernel/cpu/irq/pint.c b/arch/sh/kernel/cpu/irq/pint.c
new file mode 100644
index 0000000..95d6024
--- /dev/null
+++ b/arch/sh/kernel/cpu/irq/pint.c
@@ -0,0 +1,169 @@
+/*
+ * arch/sh/kernel/cpu/irq/pint.c - Interrupt handling for PINT-based IRQs.
+ *
+ * Copyright (C) 1999 Niibe Yutaka & Takeshi Yaegashi
+ * Copyright (C) 2000 Kazumoto Kojima
+ * Copyright (C) 2003 Takashi Kusuda <kusuda-takashi@hitachi-ul.co.jp>
+ *
+ * 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/config.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/machvec.h>
+
+static unsigned char pint_map[256];
+static unsigned long portcr_mask;
+
+static void enable_pint_irq(unsigned int irq);
+static void disable_pint_irq(unsigned int irq);
+
+/* shutdown is same as "disable" */
+#define shutdown_pint_irq disable_pint_irq
+
+static void mask_and_ack_pint(unsigned int);
+static void end_pint_irq(unsigned int irq);
+
+static unsigned int startup_pint_irq(unsigned int irq)
+{
+ enable_pint_irq(irq);
+ return 0; /* never anything pending */
+}
+
+static struct hw_interrupt_type pint_irq_type = {
+ .typename = "PINT-IRQ",
+ .startup = startup_pint_irq,
+ .shutdown = shutdown_pint_irq,
+ .enable = enable_pint_irq,
+ .disable = disable_pint_irq,
+ .ack = mask_and_ack_pint,
+ .end = end_pint_irq
+};
+
+static void disable_pint_irq(unsigned int irq)
+{
+ unsigned long val, flags;
+
+ local_irq_save(flags);
+ val = ctrl_inw(INTC_INTER);
+ val &= ~(1 << (irq - PINT_IRQ_BASE));
+ ctrl_outw(val, INTC_INTER); /* disable PINTn */
+ portcr_mask &= ~(3 << (irq - PINT_IRQ_BASE)*2);
+ local_irq_restore(flags);
+}
+
+static void enable_pint_irq(unsigned int irq)
+{
+ unsigned long val, flags;
+
+ local_irq_save(flags);
+ val = ctrl_inw(INTC_INTER);
+ val |= 1 << (irq - PINT_IRQ_BASE);
+ ctrl_outw(val, INTC_INTER); /* enable PINTn */
+ portcr_mask |= 3 << (irq - PINT_IRQ_BASE)*2;
+ local_irq_restore(flags);
+}
+
+static void mask_and_ack_pint(unsigned int irq)
+{
+ disable_pint_irq(irq);
+}
+
+static void end_pint_irq(unsigned int irq)
+{
+ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+ enable_pint_irq(irq);
+}
+
+void make_pint_irq(unsigned int irq)
+{
+ disable_irq_nosync(irq);
+ irq_desc[irq].handler = &pint_irq_type;
+ disable_pint_irq(irq);
+}
+
+void __init init_IRQ_pint(void)
+{
+ int i;
+
+ make_ipr_irq(PINT0_IRQ, PINT0_IPR_ADDR, PINT0_IPR_POS, PINT0_PRIORITY);
+ make_ipr_irq(PINT8_IRQ, PINT8_IPR_ADDR, PINT8_IPR_POS, PINT8_PRIORITY);
+
+ enable_irq(PINT0_IRQ);
+ enable_irq(PINT8_IRQ);
+
+ for(i = 0; i < 16; i++)
+ make_pint_irq(PINT_IRQ_BASE + i);
+
+ for(i = 0; i < 256; i++) {
+ if (i & 1)
+ pint_map[i] = 0;
+ else if (i & 2)
+ pint_map[i] = 1;
+ else if (i & 4)
+ pint_map[i] = 2;
+ else if (i & 8)
+ pint_map[i] = 3;
+ else if (i & 0x10)
+ pint_map[i] = 4;
+ else if (i & 0x20)
+ pint_map[i] = 5;
+ else if (i & 0x40)
+ pint_map[i] = 6;
+ else if (i & 0x80)
+ pint_map[i] = 7;
+ }
+}
+
+int ipr_irq_demux(int irq)
+{
+ unsigned long creg, dreg, d, sav;
+
+ if (irq == PINT0_IRQ) {
+#if defined(CONFIG_CPU_SUBTYPE_SH7707)
+ creg = PORT_PACR;
+ dreg = PORT_PADR;
+#else
+ creg = PORT_PCCR;
+ dreg = PORT_PCDR;
+#endif
+ sav = ctrl_inw(creg);
+ ctrl_outw(sav | portcr_mask, creg);
+ d = (~ctrl_inb(dreg) ^ ctrl_inw(INTC_ICR2)) &
+ ctrl_inw(INTC_INTER) & 0xff;
+ ctrl_outw(sav, creg);
+
+ if (d == 0)
+ return irq;
+
+ return PINT_IRQ_BASE + pint_map[d];
+ } else if (irq == PINT8_IRQ) {
+#if defined(CONFIG_CPU_SUBTYPE_SH7707)
+ creg = PORT_PBCR;
+ dreg = PORT_PBDR;
+#else
+ creg = PORT_PFCR;
+ dreg = PORT_PFDR;
+#endif
+ sav = ctrl_inw(creg);
+ ctrl_outw(sav | (portcr_mask >> 16), creg);
+ d = (~ctrl_inb(dreg) ^ (ctrl_inw(INTC_ICR2) >> 8)) &
+ (ctrl_inw(INTC_INTER) >> 8) & 0xff;
+ ctrl_outw(sav, creg);
+
+ if (d == 0)
+ return irq;
+
+ return PINT_IRQ_BASE + 8 + pint_map[d];
+ }
+
+ return irq;
+}
+
diff --git a/arch/sh/kernel/cpu/sh3/Makefile b/arch/sh/kernel/cpu/sh3/Makefile
index a64532e..b54dbb9 100644
--- a/arch/sh/kernel/cpu/sh3/Makefile
+++ b/arch/sh/kernel/cpu/sh3/Makefile
@@ -4,3 +4,10 @@
obj-y := ex.o probe.o
+clock-$(CONFIG_CPU_SH3) := clock-sh3.o
+clock-$(CONFIG_CPU_SUBTYPE_SH7300) := clock-sh7300.o
+clock-$(CONFIG_CPU_SUBTYPE_SH7705) := clock-sh7705.o
+clock-$(CONFIG_CPU_SUBTYPE_SH7709) := clock-sh7709.o
+
+obj-y += $(clock-y)
+
diff --git a/arch/sh/kernel/cpu/sh3/clock-sh3.c b/arch/sh/kernel/cpu/sh3/clock-sh3.c
new file mode 100644
index 0000000..c3c9459
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh3/clock-sh3.c
@@ -0,0 +1,89 @@
+/*
+ * arch/sh/kernel/cpu/sh3/clock-sh3.c
+ *
+ * Generic SH-3 support for the clock framework
+ *
+ * Copyright (C) 2005 Paul Mundt
+ *
+ * FRQCR parsing hacked out of arch/sh/kernel/time.c
+ *
+ * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka
+ * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
+ * Copyright (C) 2002, 2003, 2004 Paul Mundt
+ * Copyright (C) 2002 M. R. Brown <mrbrown@linux-sh.org>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <asm/clock.h>
+#include <asm/freq.h>
+#include <asm/io.h>
+
+static int stc_multipliers[] = { 1, 2, 3, 4, 6, 1, 1, 1 };
+static int ifc_divisors[] = { 1, 2, 3, 4, 1, 1, 1, 1 };
+static int pfc_divisors[] = { 1, 2, 3, 4, 6, 1, 1, 1 };
+
+static void master_clk_init(struct clk *clk)
+{
+ int frqcr = ctrl_inw(FRQCR);
+ int idx = ((frqcr & 0x2000) >> 11) | (frqcr & 0x0003);
+
+ clk->rate *= pfc_divisors[idx];
+}
+
+static struct clk_ops sh3_master_clk_ops = {
+ .init = master_clk_init,
+};
+
+static void module_clk_recalc(struct clk *clk)
+{
+ int frqcr = ctrl_inw(FRQCR);
+ int idx = ((frqcr & 0x2000) >> 11) | (frqcr & 0x0003);
+
+ clk->rate = clk->parent->rate / pfc_divisors[idx];
+}
+
+static struct clk_ops sh3_module_clk_ops = {
+ .recalc = module_clk_recalc,
+};
+
+static void bus_clk_recalc(struct clk *clk)
+{
+ int frqcr = ctrl_inw(FRQCR);
+ int idx = ((frqcr & 0x8000) >> 13) | ((frqcr & 0x0030) >> 4);
+
+ clk->rate = clk->parent->rate / stc_multipliers[idx];
+}
+
+static struct clk_ops sh3_bus_clk_ops = {
+ .recalc = bus_clk_recalc,
+};
+
+static void cpu_clk_recalc(struct clk *clk)
+{
+ int frqcr = ctrl_inw(FRQCR);
+ int idx = ((frqcr & 0x4000) >> 12) | ((frqcr & 0x000c) >> 2);
+
+ clk->rate = clk->parent->rate / ifc_divisors[idx];
+}
+
+static struct clk_ops sh3_cpu_clk_ops = {
+ .recalc = cpu_clk_recalc,
+};
+
+static struct clk_ops *sh3_clk_ops[] = {
+ &sh3_master_clk_ops,
+ &sh3_module_clk_ops,
+ &sh3_bus_clk_ops,
+ &sh3_cpu_clk_ops,
+};
+
+void __init arch_init_clk_ops(struct clk_ops **ops, int idx)
+{
+ if (idx < ARRAY_SIZE(sh3_clk_ops))
+ *ops = sh3_clk_ops[idx];
+}
+
diff --git a/arch/sh/kernel/cpu/sh3/clock-sh7300.c b/arch/sh/kernel/cpu/sh3/clock-sh7300.c
new file mode 100644
index 0000000..e804174
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh3/clock-sh7300.c
@@ -0,0 +1,78 @@
+/*
+ * arch/sh/kernel/cpu/sh3/clock-sh7300.c
+ *
+ * SH7300 support for the clock framework
+ *
+ * Copyright (C) 2005 Paul Mundt
+ *
+ * FRQCR parsing hacked out of arch/sh/kernel/time.c
+ *
+ * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka
+ * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
+ * Copyright (C) 2002, 2003, 2004 Paul Mundt
+ * Copyright (C) 2002 M. R. Brown <mrbrown@linux-sh.org>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <asm/clock.h>
+#include <asm/freq.h>
+#include <asm/io.h>
+
+static int md_table[] = { 1, 2, 3, 4, 6, 8, 12 };
+
+static void master_clk_init(struct clk *clk)
+{
+ clk->rate *= md_table[ctrl_inw(FRQCR) & 0x0007];
+}
+
+static struct clk_ops sh7300_master_clk_ops = {
+ .init = master_clk_init,
+};
+
+static void module_clk_recalc(struct clk *clk)
+{
+ int idx = (ctrl_inw(FRQCR) & 0x0007);
+ clk->rate = clk->parent->rate / md_table[idx];
+}
+
+static struct clk_ops sh7300_module_clk_ops = {
+ .recalc = module_clk_recalc,
+};
+
+static void bus_clk_recalc(struct clk *clk)
+{
+ int idx = (ctrl_inw(FRQCR) & 0x0700) >> 8;
+ clk->rate = clk->parent->rate / md_table[idx];
+}
+
+static struct clk_ops sh7300_bus_clk_ops = {
+ .recalc = bus_clk_recalc,
+};
+
+static void cpu_clk_recalc(struct clk *clk)
+{
+ int idx = (ctrl_inw(FRQCR) & 0x0070) >> 4;
+ clk->rate = clk->parent->rate / md_table[idx];
+}
+
+static struct clk_ops sh7300_cpu_clk_ops = {
+ .recalc = cpu_clk_recalc,
+};
+
+static struct clk_ops *sh7300_clk_ops[] = {
+ &sh7300_master_clk_ops,
+ &sh7300_module_clk_ops,
+ &sh7300_bus_clk_ops,
+ &sh7300_cpu_clk_ops,
+};
+
+void __init arch_init_clk_ops(struct clk_ops **ops, int idx)
+{
+ if (idx < ARRAY_SIZE(sh7300_clk_ops))
+ *ops = sh7300_clk_ops[idx];
+}
+
diff --git a/arch/sh/kernel/cpu/sh3/clock-sh7705.c b/arch/sh/kernel/cpu/sh3/clock-sh7705.c
new file mode 100644
index 0000000..dfdbf32
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh3/clock-sh7705.c
@@ -0,0 +1,84 @@
+/*
+ * arch/sh/kernel/cpu/sh3/clock-sh7705.c
+ *
+ * SH7705 support for the clock framework
+ *
+ * Copyright (C) 2005 Paul Mundt
+ *
+ * FRQCR parsing hacked out of arch/sh/kernel/time.c
+ *
+ * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka
+ * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
+ * Copyright (C) 2002, 2003, 2004 Paul Mundt
+ * Copyright (C) 2002 M. R. Brown <mrbrown@linux-sh.org>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <asm/clock.h>
+#include <asm/freq.h>
+#include <asm/io.h>
+
+/*
+ * SH7705 uses the same divisors as the generic SH-3 case, it's just the
+ * FRQCR layout that is a bit different..
+ */
+static int stc_multipliers[] = { 1, 2, 3, 4, 6, 1, 1, 1 };
+static int ifc_divisors[] = { 1, 2, 3, 4, 1, 1, 1, 1 };
+static int pfc_divisors[] = { 1, 2, 3, 4, 6, 1, 1, 1 };
+
+static void master_clk_init(struct clk *clk)
+{
+ clk->rate *= pfc_divisors[ctrl_inw(FRQCR) & 0x0003];
+}
+
+static struct clk_ops sh7705_master_clk_ops = {
+ .init = master_clk_init,
+};
+
+static void module_clk_recalc(struct clk *clk)
+{
+ int idx = ctrl_inw(FRQCR) & 0x0003;
+ clk->rate = clk->parent->rate / pfc_divisors[idx];
+}
+
+static struct clk_ops sh7705_module_clk_ops = {
+ .recalc = module_clk_recalc,
+};
+
+static void bus_clk_recalc(struct clk *clk)
+{
+ int idx = (ctrl_inw(FRQCR) & 0x0300) >> 8;
+ clk->rate = clk->parent->rate / stc_multipliers[idx];
+}
+
+static struct clk_ops sh7705_bus_clk_ops = {
+ .recalc = bus_clk_recalc,
+};
+
+static void cpu_clk_recalc(struct clk *clk)
+{
+ int idx = (ctrl_inw(FRQCR) & 0x0030) >> 4;
+ clk->rate = clk->parent->rate / ifc_divisors[idx];
+}
+
+static struct clk_ops sh7705_cpu_clk_ops = {
+ .recalc = cpu_clk_recalc,
+};
+
+static struct clk_ops *sh7705_clk_ops[] = {
+ &sh7705_master_clk_ops,
+ &sh7705_module_clk_ops,
+ &sh7705_bus_clk_ops,
+ &sh7705_cpu_clk_ops,
+};
+
+void __init arch_init_clk_ops(struct clk_ops **ops, int idx)
+{
+ if (idx < ARRAY_SIZE(sh7705_clk_ops))
+ *ops = sh7705_clk_ops[idx];
+}
+
diff --git a/arch/sh/kernel/cpu/sh3/clock-sh7709.c b/arch/sh/kernel/cpu/sh3/clock-sh7709.c
new file mode 100644
index 0000000..10461a7
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh3/clock-sh7709.c
@@ -0,0 +1,96 @@
+/*
+ * arch/sh/kernel/cpu/sh3/clock-sh7709.c
+ *
+ * SH7709 support for the clock framework
+ *
+ * Copyright (C) 2005 Andriy Skulysh
+ *
+ * Based on arch/sh/kernel/cpu/sh3/clock-sh7705.c
+ * Copyright (C) 2005 Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <asm/clock.h>
+#include <asm/freq.h>
+#include <asm/io.h>
+
+static int stc_multipliers[] = { 1, 2, 4, 8, 3, 6, 1, 1 };
+static int ifc_divisors[] = { 1, 2, 4, 1, 3, 1, 1, 1 };
+static int pfc_divisors[] = { 1, 2, 4, 1, 3, 6, 1, 1 };
+
+static void set_bus_parent(struct clk *clk)
+{
+ struct clk *bus_clk = clk_get("bus_clk");
+ clk->parent = bus_clk;
+ clk_put(bus_clk);
+}
+
+static void master_clk_init(struct clk *clk)
+{
+ int frqcr = ctrl_inw(FRQCR);
+ int idx = ((frqcr & 0x2000) >> 11) | (frqcr & 0x0003);
+
+ clk->rate *= pfc_divisors[idx];
+}
+
+static struct clk_ops sh7709_master_clk_ops = {
+ .init = master_clk_init,
+};
+
+static void module_clk_recalc(struct clk *clk)
+{
+ int frqcr = ctrl_inw(FRQCR);
+ int idx = ((frqcr & 0x2000) >> 11) | (frqcr & 0x0003);
+
+ clk->rate = clk->parent->rate / pfc_divisors[idx];
+}
+
+static struct clk_ops sh7709_module_clk_ops = {
+#ifdef CLOCK_MODE_0_1_2_7
+ .init = set_bus_parent,
+#endif
+ .recalc = module_clk_recalc,
+};
+
+static void bus_clk_recalc(struct clk *clk)
+{
+ int frqcr = ctrl_inw(FRQCR);
+ int idx = (frqcr & 0x0080) ?
+ ((frqcr & 0x8000) >> 13) | ((frqcr & 0x0030) >> 4) : 1;
+
+ clk->rate = clk->parent->rate * stc_multipliers[idx];
+}
+
+static struct clk_ops sh7709_bus_clk_ops = {
+ .recalc = bus_clk_recalc,
+};
+
+static void cpu_clk_recalc(struct clk *clk)
+{
+ int frqcr = ctrl_inw(FRQCR);
+ int idx = ((frqcr & 0x4000) >> 12) | ((frqcr & 0x000c) >> 2);
+
+ clk->rate = clk->parent->rate / ifc_divisors[idx];
+}
+
+static struct clk_ops sh7709_cpu_clk_ops = {
+ .init = set_bus_parent,
+ .recalc = cpu_clk_recalc,
+};
+
+static struct clk_ops *sh7709_clk_ops[] = {
+ &sh7709_master_clk_ops,
+ &sh7709_module_clk_ops,
+ &sh7709_bus_clk_ops,
+ &sh7709_cpu_clk_ops,
+};
+
+void __init arch_init_clk_ops(struct clk_ops **ops, int idx)
+{
+ if (idx < ARRAY_SIZE(sh7709_clk_ops))
+ *ops = sh7709_clk_ops[idx];
+}
diff --git a/arch/sh/kernel/cpu/sh4/Makefile b/arch/sh/kernel/cpu/sh4/Makefile
index ead1071..3d5cafc 100644
--- a/arch/sh/kernel/cpu/sh4/Makefile
+++ b/arch/sh/kernel/cpu/sh4/Makefile
@@ -5,6 +5,15 @@
obj-y := ex.o probe.o
obj-$(CONFIG_SH_FPU) += fpu.o
-obj-$(CONFIG_CPU_SUBTYPE_ST40STB1) += irq_intc2.o
obj-$(CONFIG_SH_STORE_QUEUES) += sq.o
+# Primary on-chip clocks (common)
+clock-$(CONFIG_CPU_SH4) := clock-sh4.o
+clock-$(CONFIG_CPU_SUBTYPE_SH73180) := clock-sh73180.o
+clock-$(CONFIG_CPU_SUBTYPE_SH7770) := clock-sh7770.o
+clock-$(CONFIG_CPU_SUBTYPE_SH7780) := clock-sh7780.o
+
+# Additional clocks by subtype
+clock-$(CONFIG_CPU_SUBTYPE_SH4_202) += clock-sh4-202.o
+
+obj-y += $(clock-y)
diff --git a/arch/sh/kernel/cpu/sh4/clock-sh4-202.c b/arch/sh/kernel/cpu/sh4/clock-sh4-202.c
new file mode 100644
index 0000000..bfdf5fe
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh4/clock-sh4-202.c
@@ -0,0 +1,179 @@
+/*
+ * arch/sh/kernel/cpu/sh4/clock-sh4-202.c
+ *
+ * Additional SH4-202 support for the clock framework
+ *
+ * Copyright (C) 2005 Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <asm/clock.h>
+#include <asm/freq.h>
+#include <asm/io.h>
+
+#define CPG2_FRQCR3 0xfe0a0018
+
+static int frqcr3_divisors[] = { 1, 2, 3, 4, 6, 8, 16 };
+static int frqcr3_values[] = { 0, 1, 2, 3, 4, 5, 6 };
+
+static void emi_clk_recalc(struct clk *clk)
+{
+ int idx = ctrl_inl(CPG2_FRQCR3) & 0x0007;
+ clk->rate = clk->parent->rate / frqcr3_divisors[idx];
+}
+
+static inline int frqcr3_lookup(struct clk *clk, unsigned long rate)
+{
+ int divisor = clk->parent->rate / rate;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(frqcr3_divisors); i++)
+ if (frqcr3_divisors[i] == divisor)
+ return frqcr3_values[i];
+
+ /* Safe fallback */
+ return 5;
+}
+
+static struct clk_ops sh4202_emi_clk_ops = {
+ .recalc = emi_clk_recalc,
+};
+
+static struct clk sh4202_emi_clk = {
+ .name = "emi_clk",
+ .flags = CLK_ALWAYS_ENABLED,
+ .ops = &sh4202_emi_clk_ops,
+};
+
+static void femi_clk_recalc(struct clk *clk)
+{
+ int idx = (ctrl_inl(CPG2_FRQCR3) >> 3) & 0x0007;
+ clk->rate = clk->parent->rate / frqcr3_divisors[idx];
+}
+
+static struct clk_ops sh4202_femi_clk_ops = {
+ .recalc = femi_clk_recalc,
+};
+
+static struct clk sh4202_femi_clk = {
+ .name = "femi_clk",
+ .flags = CLK_ALWAYS_ENABLED,
+ .ops = &sh4202_femi_clk_ops,
+};
+
+static void shoc_clk_init(struct clk *clk)
+{
+ int i;
+
+ /*
+ * For some reason, the shoc_clk seems to be set to some really
+ * insane value at boot (values outside of the allowable frequency
+ * range for instance). We deal with this by scaling it back down
+ * to something sensible just in case.
+ *
+ * Start scaling from the high end down until we find something
+ * that passes rate verification..
+ */
+ for (i = 0; i < ARRAY_SIZE(frqcr3_divisors); i++) {
+ int divisor = frqcr3_divisors[i];
+
+ if (clk->ops->set_rate(clk, clk->parent->rate / divisor) == 0)
+ break;
+ }
+
+ WARN_ON(i == ARRAY_SIZE(frqcr3_divisors)); /* Undefined clock */
+}
+
+static void shoc_clk_recalc(struct clk *clk)
+{
+ int idx = (ctrl_inl(CPG2_FRQCR3) >> 6) & 0x0007;
+ clk->rate = clk->parent->rate / frqcr3_divisors[idx];
+}
+
+static int shoc_clk_verify_rate(struct clk *clk, unsigned long rate)
+{
+ struct clk *bclk = clk_get("bus_clk");
+ unsigned long bclk_rate = clk_get_rate(bclk);
+
+ clk_put(bclk);
+
+ if (rate > bclk_rate)
+ return 1;
+ if (rate > 66000000)
+ return 1;
+
+ return 0;
+}
+
+static int shoc_clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned long frqcr3;
+ unsigned int tmp;
+
+ /* Make sure we have something sensible to switch to */
+ if (shoc_clk_verify_rate(clk, rate) != 0)
+ return -EINVAL;
+
+ tmp = frqcr3_lookup(clk, rate);
+
+ frqcr3 = ctrl_inl(CPG2_FRQCR3);
+ frqcr3 &= ~(0x0007 << 6);
+ frqcr3 |= tmp << 6;
+ ctrl_outl(frqcr3, CPG2_FRQCR3);
+
+ clk->rate = clk->parent->rate / frqcr3_divisors[tmp];
+
+ return 0;
+}
+
+static struct clk_ops sh4202_shoc_clk_ops = {
+ .init = shoc_clk_init,
+ .recalc = shoc_clk_recalc,
+ .set_rate = shoc_clk_set_rate,
+};
+
+static struct clk sh4202_shoc_clk = {
+ .name = "shoc_clk",
+ .flags = CLK_ALWAYS_ENABLED,
+ .ops = &sh4202_shoc_clk_ops,
+};
+
+static struct clk *sh4202_onchip_clocks[] = {
+ &sh4202_emi_clk,
+ &sh4202_femi_clk,
+ &sh4202_shoc_clk,
+};
+
+static int __init sh4202_clk_init(void)
+{
+ struct clk *clk = clk_get("master_clk");
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(sh4202_onchip_clocks); i++) {
+ struct clk *clkp = sh4202_onchip_clocks[i];
+
+ clkp->parent = clk;
+ clk_register(clkp);
+ clk_enable(clkp);
+ }
+
+ /*
+ * Now that we have the rest of the clocks registered, we need to
+ * force the parent clock to propagate so that these clocks will
+ * automatically figure out their rate. We cheat by handing the
+ * parent clock its current rate and forcing child propagation.
+ */
+ clk_set_rate(clk, clk_get_rate(clk));
+
+ clk_put(clk);
+
+ return 0;
+}
+
+arch_initcall(sh4202_clk_init);
+
diff --git a/arch/sh/kernel/cpu/sh4/clock-sh4.c b/arch/sh/kernel/cpu/sh4/clock-sh4.c
new file mode 100644
index 0000000..dca9f87
--- /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[ctrl_inw(FRQCR) & 0x0007];
+}
+
+static struct clk_ops sh4_master_clk_ops = {
+ .init = master_clk_init,
+};
+
+static void module_clk_recalc(struct clk *clk)
+{
+ int idx = (ctrl_inw(FRQCR) & 0x0007);
+ clk->rate = clk->parent->rate / pfc_divisors[idx];
+}
+
+static struct clk_ops sh4_module_clk_ops = {
+ .recalc = module_clk_recalc,
+};
+
+static void bus_clk_recalc(struct clk *clk)
+{
+ int idx = (ctrl_inw(FRQCR) >> 3) & 0x0007;
+ clk->rate = clk->parent->rate / bfc_divisors[idx];
+}
+
+static struct clk_ops sh4_bus_clk_ops = {
+ .recalc = bus_clk_recalc,
+};
+
+static void cpu_clk_recalc(struct clk *clk)
+{
+ int idx = (ctrl_inw(FRQCR) >> 6) & 0x0007;
+ clk->rate = clk->parent->rate / ifc_divisors[idx];
+}
+
+static struct clk_ops sh4_cpu_clk_ops = {
+ .recalc = cpu_clk_recalc,
+};
+
+static struct clk_ops *sh4_clk_ops[] = {
+ &sh4_master_clk_ops,
+ &sh4_module_clk_ops,
+ &sh4_bus_clk_ops,
+ &sh4_cpu_clk_ops,
+};
+
+void __init arch_init_clk_ops(struct clk_ops **ops, int idx)
+{
+ if (idx < ARRAY_SIZE(sh4_clk_ops))
+ *ops = sh4_clk_ops[idx];
+}
+
diff --git a/arch/sh/kernel/cpu/sh4/clock-sh73180.c b/arch/sh/kernel/cpu/sh4/clock-sh73180.c
new file mode 100644
index 0000000..2fa5cb2
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh4/clock-sh73180.c
@@ -0,0 +1,81 @@
+/*
+ * arch/sh/kernel/cpu/sh4/clock-sh73180.c
+ *
+ * SH73180 support for the clock framework
+ *
+ * Copyright (C) 2005 Paul Mundt
+ *
+ * FRQCR parsing hacked out of arch/sh/kernel/time.c
+ *
+ * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka
+ * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
+ * Copyright (C) 2002, 2003, 2004 Paul Mundt
+ * Copyright (C) 2002 M. R. Brown <mrbrown@linux-sh.org>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <asm/clock.h>
+#include <asm/freq.h>
+#include <asm/io.h>
+
+/*
+ * SH73180 uses a common set of divisors, so this is quite simple..
+ */
+static int divisors[] = { 1, 2, 3, 4, 6, 8, 12, 16 };
+
+static void master_clk_init(struct clk *clk)
+{
+ clk->rate *= divisors[ctrl_inl(FRQCR) & 0x0007];
+}
+
+static struct clk_ops sh73180_master_clk_ops = {
+ .init = master_clk_init,
+};
+
+static void module_clk_recalc(struct clk *clk)
+{
+ int idx = (ctrl_inl(FRQCR) & 0x0007);
+ clk->rate = clk->parent->rate / divisors[idx];
+}
+
+static struct clk_ops sh73180_module_clk_ops = {
+ .recalc = module_clk_recalc,
+};
+
+static void bus_clk_recalc(struct clk *clk)
+{
+ int idx = (ctrl_inl(FRQCR) >> 12) & 0x0007;
+ clk->rate = clk->parent->rate / divisors[idx];
+}
+
+static struct clk_ops sh73180_bus_clk_ops = {
+ .recalc = bus_clk_recalc,
+};
+
+static void cpu_clk_recalc(struct clk *clk)
+{
+ int idx = (ctrl_inl(FRQCR) >> 20) & 0x0007;
+ clk->rate = clk->parent->rate / divisors[idx];
+}
+
+static struct clk_ops sh73180_cpu_clk_ops = {
+ .recalc = cpu_clk_recalc,
+};
+
+static struct clk_ops *sh73180_clk_ops[] = {
+ &sh73180_master_clk_ops,
+ &sh73180_module_clk_ops,
+ &sh73180_bus_clk_ops,
+ &sh73180_cpu_clk_ops,
+};
+
+void __init arch_init_clk_ops(struct clk_ops **ops, int idx)
+{
+ if (idx < ARRAY_SIZE(sh73180_clk_ops))
+ *ops = sh73180_clk_ops[idx];
+}
+
diff --git a/arch/sh/kernel/cpu/sh4/clock-sh7770.c b/arch/sh/kernel/cpu/sh4/clock-sh7770.c
new file mode 100644
index 0000000..c8694ba
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh4/clock-sh7770.c
@@ -0,0 +1,73 @@
+/*
+ * arch/sh/kernel/cpu/sh4/clock-sh7770.c
+ *
+ * SH7770 support for the clock framework
+ *
+ * Copyright (C) 2005 Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <asm/clock.h>
+#include <asm/freq.h>
+#include <asm/io.h>
+
+static int ifc_divisors[] = { 1, 1, 1, 1, 1, 1, 1, 1 };
+static int bfc_divisors[] = { 1, 1, 1, 1, 1, 8,12, 1 };
+static int pfc_divisors[] = { 1, 8, 1,10,12,16, 1, 1 };
+
+static void master_clk_init(struct clk *clk)
+{
+ clk->rate *= pfc_divisors[(ctrl_inl(FRQCR) >> 28) & 0x000f];
+}
+
+static struct clk_ops sh7770_master_clk_ops = {
+ .init = master_clk_init,
+};
+
+static void module_clk_recalc(struct clk *clk)
+{
+ int idx = ((ctrl_inl(FRQCR) >> 28) & 0x000f);
+ clk->rate = clk->parent->rate / pfc_divisors[idx];
+}
+
+static struct clk_ops sh7770_module_clk_ops = {
+ .recalc = module_clk_recalc,
+};
+
+static void bus_clk_recalc(struct clk *clk)
+{
+ int idx = (ctrl_inl(FRQCR) & 0x000f);
+ clk->rate = clk->parent->rate / bfc_divisors[idx];
+}
+
+static struct clk_ops sh7770_bus_clk_ops = {
+ .recalc = bus_clk_recalc,
+};
+
+static void cpu_clk_recalc(struct clk *clk)
+{
+ int idx = ((ctrl_inl(FRQCR) >> 24) & 0x000f);
+ clk->rate = clk->parent->rate / ifc_divisors[idx];
+}
+
+static struct clk_ops sh7770_cpu_clk_ops = {
+ .recalc = cpu_clk_recalc,
+};
+
+static struct clk_ops *sh7770_clk_ops[] = {
+ &sh7770_master_clk_ops,
+ &sh7770_module_clk_ops,
+ &sh7770_bus_clk_ops,
+ &sh7770_cpu_clk_ops,
+};
+
+void __init arch_init_clk_ops(struct clk_ops **ops, int idx)
+{
+ if (idx < ARRAY_SIZE(sh7770_clk_ops))
+ *ops = sh7770_clk_ops[idx];
+}
+
diff --git a/arch/sh/kernel/cpu/sh4/clock-sh7780.c b/arch/sh/kernel/cpu/sh4/clock-sh7780.c
new file mode 100644
index 0000000..93ad367
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh4/clock-sh7780.c
@@ -0,0 +1,126 @@
+/*
+ * arch/sh/kernel/cpu/sh4/clock-sh7780.c
+ *
+ * SH7780 support for the clock framework
+ *
+ * Copyright (C) 2005 Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <asm/clock.h>
+#include <asm/freq.h>
+#include <asm/io.h>
+
+static int ifc_divisors[] = { 2, 4 };
+static int bfc_divisors[] = { 1, 1, 1, 8, 12, 16, 24, 1 };
+static int pfc_divisors[] = { 1, 24, 24, 1 };
+static int cfc_divisors[] = { 1, 1, 4, 1, 6, 1, 1, 1 };
+
+static void master_clk_init(struct clk *clk)
+{
+ clk->rate *= pfc_divisors[ctrl_inl(FRQCR) & 0x0003];
+}
+
+static struct clk_ops sh7780_master_clk_ops = {
+ .init = master_clk_init,
+};
+
+static void module_clk_recalc(struct clk *clk)
+{
+ int idx = (ctrl_inl(FRQCR) & 0x0003);
+ clk->rate = clk->parent->rate / pfc_divisors[idx];
+}
+
+static struct clk_ops sh7780_module_clk_ops = {
+ .recalc = module_clk_recalc,
+};
+
+static void bus_clk_recalc(struct clk *clk)
+{
+ int idx = ((ctrl_inl(FRQCR) >> 16) & 0x0007);
+ clk->rate = clk->parent->rate / bfc_divisors[idx];
+}
+
+static struct clk_ops sh7780_bus_clk_ops = {
+ .recalc = bus_clk_recalc,
+};
+
+static void cpu_clk_recalc(struct clk *clk)
+{
+ int idx = ((ctrl_inl(FRQCR) >> 24) & 0x0001);
+ clk->rate = clk->parent->rate / ifc_divisors[idx];
+}
+
+static struct clk_ops sh7780_cpu_clk_ops = {
+ .recalc = cpu_clk_recalc,
+};
+
+static struct clk_ops *sh7780_clk_ops[] = {
+ &sh7780_master_clk_ops,
+ &sh7780_module_clk_ops,
+ &sh7780_bus_clk_ops,
+ &sh7780_cpu_clk_ops,
+};
+
+void __init arch_init_clk_ops(struct clk_ops **ops, int idx)
+{
+ if (idx < ARRAY_SIZE(sh7780_clk_ops))
+ *ops = sh7780_clk_ops[idx];
+}
+
+static void shyway_clk_recalc(struct clk *clk)
+{
+ int idx = ((ctrl_inl(FRQCR) >> 20) & 0x0007);
+ clk->rate = clk->parent->rate / cfc_divisors[idx];
+}
+
+static struct clk_ops sh7780_shyway_clk_ops = {
+ .recalc = shyway_clk_recalc,
+};
+
+static struct clk sh7780_shyway_clk = {
+ .name = "shyway_clk",
+ .flags = CLK_ALWAYS_ENABLED,
+ .ops = &sh7780_shyway_clk_ops,
+};
+
+/*
+ * Additional SH7780-specific on-chip clocks that aren't already part of the
+ * clock framework
+ */
+static struct clk *sh7780_onchip_clocks[] = {
+ &sh7780_shyway_clk,
+};
+
+static int __init sh7780_clk_init(void)
+{
+ struct clk *clk = clk_get("master_clk");
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(sh7780_onchip_clocks); i++) {
+ struct clk *clkp = sh7780_onchip_clocks[i];
+
+ clkp->parent = clk;
+ clk_register(clkp);
+ clk_enable(clkp);
+ }
+
+ /*
+ * Now that we have the rest of the clocks registered, we need to
+ * force the parent clock to propagate so that these clocks will
+ * automatically figure out their rate. We cheat by handing the
+ * parent clock its current rate and forcing child propagation.
+ */
+ clk_set_rate(clk, clk_get_rate(clk));
+
+ clk_put(clk);
+
+ return 0;
+}
+
+arch_initcall(sh7780_clk_init);
+
diff --git a/arch/sh/kernel/cpu/sh4/irq_intc2.c b/arch/sh/kernel/cpu/sh4/irq_intc2.c
deleted file mode 100644
index f6b16ba..0000000
--- a/arch/sh/kernel/cpu/sh4/irq_intc2.c
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * linux/arch/sh/kernel/irq_intc2.c
- *
- * Copyright (C) 2001 David J. Mckay (david.mckay@st.com)
- *
- * May be copied or modified under the terms of the GNU General Public
- * License. See linux/COPYING for more information.
- *
- * Interrupt handling for INTC2-based IRQ.
- *
- * These are the "new Hitachi style" interrupts, as present on the
- * Hitachi 7751 and the STM ST40 STB1.
- */
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/irq.h>
-
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/machvec.h>
-
-
-struct intc2_data {
- unsigned char msk_offset;
- unsigned char msk_shift;
-#ifdef CONFIG_CPU_SUBTYPE_ST40
- int (*clear_irq) (int);
-#endif
-};
-
-
-static struct intc2_data intc2_data[NR_INTC2_IRQS];
-
-static void enable_intc2_irq(unsigned int irq);
-static void disable_intc2_irq(unsigned int irq);
-
-/* shutdown is same as "disable" */
-#define shutdown_intc2_irq disable_intc2_irq
-
-static void mask_and_ack_intc2(unsigned int);
-static void end_intc2_irq(unsigned int irq);
-
-static unsigned int startup_intc2_irq(unsigned int irq)
-{
- enable_intc2_irq(irq);
- return 0; /* never anything pending */
-}
-
-static struct hw_interrupt_type intc2_irq_type = {
- .typename = "INTC2-IRQ",
- .startup = startup_intc2_irq,
- .shutdown = shutdown_intc2_irq,
- .enable = enable_intc2_irq,
- .disable = disable_intc2_irq,
- .ack = mask_and_ack_intc2,
- .end = end_intc2_irq
-};
-
-static void disable_intc2_irq(unsigned int irq)
-{
- int irq_offset = irq - INTC2_FIRST_IRQ;
- int msk_shift, msk_offset;
-
- // Sanity check
- if((irq_offset<0) || (irq_offset>=NR_INTC2_IRQS))
- return;
-
- msk_shift = intc2_data[irq_offset].msk_shift;
- msk_offset = intc2_data[irq_offset].msk_offset;
-
- ctrl_outl(1<<msk_shift,
- INTC2_BASE+INTC2_INTMSK_OFFSET+msk_offset);
-}
-
-static void enable_intc2_irq(unsigned int irq)
-{
- int irq_offset = irq - INTC2_FIRST_IRQ;
- int msk_shift, msk_offset;
-
- /* Sanity check */
- if((irq_offset<0) || (irq_offset>=NR_INTC2_IRQS))
- return;
-
- msk_shift = intc2_data[irq_offset].msk_shift;
- msk_offset = intc2_data[irq_offset].msk_offset;
-
- ctrl_outl(1<<msk_shift,
- INTC2_BASE+INTC2_INTMSKCLR_OFFSET+msk_offset);
-}
-
-static void mask_and_ack_intc2(unsigned int irq)
-{
- disable_intc2_irq(irq);
-}
-
-static void end_intc2_irq(unsigned int irq)
-{
- if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
- enable_intc2_irq(irq);
-
-#ifdef CONFIG_CPU_SUBTYPE_ST40
- if (intc2_data[irq - INTC2_FIRST_IRQ].clear_irq)
- intc2_data[irq - INTC2_FIRST_IRQ].clear_irq (irq);
-#endif
-}
-
-/*
- * Setup an INTC2 style interrupt.
- * NOTE: Unlike IPR interrupts, parameters are not shifted by this code,
- * allowing the use of the numbers straight out of the datasheet.
- * For example:
- * PIO1 which is INTPRI00[19,16] and INTMSK00[13]
- * would be: ^ ^ ^ ^
- * | | | |
- * make_intc2_irq(84, 0, 16, 0, 13);
- */
-void make_intc2_irq(unsigned int irq,
- unsigned int ipr_offset, unsigned int ipr_shift,
- unsigned int msk_offset, unsigned int msk_shift,
- unsigned int priority)
-{
- int irq_offset = irq - INTC2_FIRST_IRQ;
- unsigned int flags;
- unsigned long ipr;
-
- if((irq_offset<0) || (irq_offset>=NR_INTC2_IRQS))
- return;
-
- disable_irq_nosync(irq);
-
- /* Fill the data we need */
- intc2_data[irq_offset].msk_offset = msk_offset;
- intc2_data[irq_offset].msk_shift = msk_shift;
-#ifdef CONFIG_CPU_SUBTYPE_ST40
- intc2_data[irq_offset].clear_irq = NULL;
-#endif
-
- /* Set the priority level */
- local_irq_save(flags);
-
- ipr=ctrl_inl(INTC2_BASE+INTC2_INTPRI_OFFSET+ipr_offset);
- ipr&=~(0xf<<ipr_shift);
- ipr|=(priority)<<ipr_shift;
- ctrl_outl(ipr, INTC2_BASE+INTC2_INTPRI_OFFSET+ipr_offset);
-
- local_irq_restore(flags);
-
- irq_desc[irq].handler=&intc2_irq_type;
-
- disable_intc2_irq(irq);
-}
-
-#ifdef CONFIG_CPU_SUBTYPE_ST40
-
-struct intc2_init {
- unsigned short irq;
- unsigned char ipr_offset, ipr_shift;
- unsigned char msk_offset, msk_shift;
-};
-
-static struct intc2_init intc2_init_data[] __initdata = {
- {64, 0, 0, 0, 0}, /* PCI serr */
- {65, 0, 4, 0, 1}, /* PCI err */
- {66, 0, 4, 0, 2}, /* PCI ad */
- {67, 0, 4, 0, 3}, /* PCI pwd down */
- {72, 0, 8, 0, 5}, /* DMAC INT0 */
- {73, 0, 8, 0, 6}, /* DMAC INT1 */
- {74, 0, 8, 0, 7}, /* DMAC INT2 */
- {75, 0, 8, 0, 8}, /* DMAC INT3 */
- {76, 0, 8, 0, 9}, /* DMAC INT4 */
- {78, 0, 8, 0, 11}, /* DMAC ERR */
- {80, 0, 12, 0, 12}, /* PIO0 */
- {84, 0, 16, 0, 13}, /* PIO1 */
- {88, 0, 20, 0, 14}, /* PIO2 */
- {112, 4, 0, 4, 0}, /* Mailbox */
-#ifdef CONFIG_CPU_SUBTYPE_ST40GX1
- {116, 4, 4, 4, 4}, /* SSC0 */
- {120, 4, 8, 4, 8}, /* IR Blaster */
- {124, 4, 12, 4, 12}, /* USB host */
- {128, 4, 16, 4, 16}, /* Video processor BLITTER */
- {132, 4, 20, 4, 20}, /* UART0 */
- {134, 4, 20, 4, 22}, /* UART2 */
- {136, 4, 24, 4, 24}, /* IO_PIO0 */
- {140, 4, 28, 4, 28}, /* EMPI */
- {144, 8, 0, 8, 0}, /* MAFE */
- {148, 8, 4, 8, 4}, /* PWM */
- {152, 8, 8, 8, 8}, /* SSC1 */
- {156, 8, 12, 8, 12}, /* IO_PIO1 */
- {160, 8, 16, 8, 16}, /* USB target */
- {164, 8, 20, 8, 20}, /* UART1 */
- {168, 8, 24, 8, 24}, /* Teletext */
- {172, 8, 28, 8, 28}, /* VideoSync VTG */
- {173, 8, 28, 8, 29}, /* VideoSync DVP0 */
- {174, 8, 28, 8, 30}, /* VideoSync DVP1 */
-#endif
-};
-
-void __init init_IRQ_intc2(void)
-{
- struct intc2_init *p;
-
- printk(KERN_ALERT "init_IRQ_intc2\n");
-
- for (p = intc2_init_data;
- p<intc2_init_data+ARRAY_SIZE(intc2_init_data);
- p++) {
- make_intc2_irq(p->irq, p->ipr_offset, p->ipr_shift,
- p-> msk_offset, p->msk_shift, 13);
- }
-}
-
-/* Adds a termination callback to the interrupt */
-void intc2_add_clear_irq(int irq, int (*fn)(int))
-{
- if (irq < INTC2_FIRST_IRQ)
- return;
-
- intc2_data[irq - INTC2_FIRST_IRQ].clear_irq = fn;
-}
-
-#endif /* CONFIG_CPU_SUBTYPE_ST40 */
diff --git a/arch/sh/kernel/io.c b/arch/sh/kernel/io.c
index d9932f2..71c9fde 100644
--- a/arch/sh/kernel/io.c
+++ b/arch/sh/kernel/io.c
@@ -2,58 +2,73 @@
* linux/arch/sh/kernel/io.c
*
* Copyright (C) 2000 Stuart Menefy
+ * Copyright (C) 2005 Paul Mundt
*
* Provide real functions which expand to whatever the header file defined.
* Also definitions of machine independent IO functions.
+ *
+ * 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 <asm/io.h>
#include <linux/module.h>
+#include <asm/machvec.h>
+#include <asm/io.h>
/*
* Copy data from IO memory space to "real" memory space.
* This needs to be optimized.
*/
-void memcpy_fromio(void * to, unsigned long from, unsigned long count)
+void memcpy_fromio(void *to, volatile void __iomem *from, unsigned long count)
{
char *p = to;
while (count) {
count--;
- *p = readb(from);
+ *p = readb((void __iomem *)from);
p++;
from++;
}
}
-
+EXPORT_SYMBOL(memcpy_fromio);
+
/*
* Copy data from "real" memory space to IO memory space.
* This needs to be optimized.
*/
-void memcpy_toio(unsigned long to, const void * from, unsigned long count)
+void memcpy_toio(volatile void __iomem *to, const void *from, unsigned long count)
{
const char *p = from;
while (count) {
count--;
- writeb(*p, to);
+ writeb(*p, (void __iomem *)to);
p++;
to++;
}
}
-
+EXPORT_SYMBOL(memcpy_toio);
+
/*
* "memset" on IO memory space.
* This needs to be optimized.
*/
-void memset_io(unsigned long dst, int c, unsigned long count)
+void memset_io(volatile void __iomem *dst, int c, unsigned long count)
{
while (count) {
count--;
- writeb(c, dst);
+ writeb(c, (void __iomem *)dst);
dst++;
}
}
-
-EXPORT_SYMBOL(memcpy_fromio);
-EXPORT_SYMBOL(memcpy_toio);
EXPORT_SYMBOL(memset_io);
+void __iomem *ioport_map(unsigned long port, unsigned int nr)
+{
+ return sh_mv.mv_ioport_map(port, nr);
+}
+EXPORT_SYMBOL(ioport_map);
+
+void ioport_unmap(void __iomem *addr)
+{
+ sh_mv.mv_ioport_unmap(addr);
+}
+EXPORT_SYMBOL(ioport_unmap);
diff --git a/arch/sh/kernel/io_generic.c b/arch/sh/kernel/io_generic.c
index a911b01..28ec748 100644
--- a/arch/sh/kernel/io_generic.c
+++ b/arch/sh/kernel/io_generic.c
@@ -3,6 +3,7 @@
* linux/arch/sh/kernel/io_generic.c
*
* Copyright (C) 2000 Niibe Yutaka
+ * Copyright (C) 2005 Paul Mundt
*
* Generic I/O routine. These can be used where a machine specific version
* is not required.
@@ -10,21 +11,20 @@
* 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/module.h>
#include <asm/io.h>
#include <asm/machvec.h>
-#include <linux/module.h>
-#if defined(CONFIG_CPU_SH3)
+#ifdef CONFIG_CPU_SH3
+/* SH3 has a PCMCIA bug that needs a dummy read from area 6 for a
+ * workaround. */
/* I'm not sure SH7709 has this kind of bug */
-#define SH3_PCMCIA_BUG_WORKAROUND 1
-#define DUMMY_READ_AREA6 0xba000000
+#define dummy_read() ctrl_inb(0xba000000)
+#else
+#define dummy_read()
#endif
-#define PORT2ADDR(x) (sh_mv.mv_isa_port2addr(x))
-
unsigned long generic_io_base;
static inline void delay(void)
@@ -32,40 +32,40 @@ static inline void delay(void)
ctrl_inw(0xa0000000);
}
-unsigned char generic_inb(unsigned long port)
+u8 generic_inb(unsigned long port)
{
- return *(volatile unsigned char*)PORT2ADDR(port);
+ return ctrl_inb((unsigned long __force)ioport_map(port, 1));
}
-unsigned short generic_inw(unsigned long port)
+u16 generic_inw(unsigned long port)
{
- return *(volatile unsigned short*)PORT2ADDR(port);
+ return ctrl_inw((unsigned long __force)ioport_map(port, 2));
}
-unsigned int generic_inl(unsigned long port)
+u32 generic_inl(unsigned long port)
{
- return *(volatile unsigned long*)PORT2ADDR(port);
+ return ctrl_inl((unsigned long __force)ioport_map(port, 4));
}
-unsigned char generic_inb_p(unsigned long port)
+u8 generic_inb_p(unsigned long port)
{
- unsigned long v = *(volatile unsigned char*)PORT2ADDR(port);
+ unsigned long v = generic_inb(port);
delay();
return v;
}
-unsigned short generic_inw_p(unsigned long port)
+u16 generic_inw_p(unsigned long port)
{
- unsigned long v = *(volatile unsigned short*)PORT2ADDR(port);
+ unsigned long v = generic_inw(port);
delay();
return v;
}
-unsigned int generic_inl_p(unsigned long port)
+u32 generic_inl_p(unsigned long port)
{
- unsigned long v = *(volatile unsigned long*)PORT2ADDR(port);
+ unsigned long v = generic_inl(port);
delay();
return v;
@@ -77,75 +77,70 @@ unsigned int generic_inl_p(unsigned long port)
* convert the port address to real address once.
*/
-void generic_insb(unsigned long port, void *buffer, unsigned long count)
+void generic_insb(unsigned long port, void *dst, unsigned long count)
{
- volatile unsigned char *port_addr;
- unsigned char *buf=buffer;
-
- port_addr = (volatile unsigned char *)PORT2ADDR(port);
+ volatile u8 *port_addr;
+ u8 *buf = dst;
- while(count--)
- *buf++ = *port_addr;
+ port_addr = (volatile u8 *)ioport_map(port, 1);
+ while (count--)
+ *buf++ = *port_addr;
}
-void generic_insw(unsigned long port, void *buffer, unsigned long count)
+void generic_insw(unsigned long port, void *dst, unsigned long count)
{
- volatile unsigned short *port_addr;
- unsigned short *buf=buffer;
+ volatile u16 *port_addr;
+ u16 *buf = dst;
- port_addr = (volatile unsigned short *)PORT2ADDR(port);
+ port_addr = (volatile u16 *)ioport_map(port, 2);
+ while (count--)
+ *buf++ = *port_addr;
- while(count--)
- *buf++ = *port_addr;
-#ifdef SH3_PCMCIA_BUG_WORKAROUND
- ctrl_inb (DUMMY_READ_AREA6);
-#endif
+ dummy_read();
}
-void generic_insl(unsigned long port, void *buffer, unsigned long count)
+void generic_insl(unsigned long port, void *dst, unsigned long count)
{
- volatile unsigned long *port_addr;
- unsigned long *buf=buffer;
+ volatile u32 *port_addr;
+ u32 *buf = dst;
- port_addr = (volatile unsigned long *)PORT2ADDR(port);
+ port_addr = (volatile u32 *)ioport_map(port, 4);
+ while (count--)
+ *buf++ = *port_addr;
- while(count--)
- *buf++ = *port_addr;
-#ifdef SH3_PCMCIA_BUG_WORKAROUND
- ctrl_inb (DUMMY_READ_AREA6);
-#endif
+ dummy_read();
}
-void generic_outb(unsigned char b, unsigned long port)
+void generic_outb(u8 b, unsigned long port)
{
- *(volatile unsigned char*)PORT2ADDR(port) = b;
+ ctrl_outb(b, (unsigned long __force)ioport_map(port, 1));
}
-void generic_outw(unsigned short b, unsigned long port)
+void generic_outw(u16 b, unsigned long port)
{
- *(volatile unsigned short*)PORT2ADDR(port) = b;
+ ctrl_outw(b, (unsigned long __force)ioport_map(port, 2));
}
-void generic_outl(unsigned int b, unsigned long port)
+void generic_outl(u32 b, unsigned long port)
{
- *(volatile unsigned long*)PORT2ADDR(port) = b;
+ ctrl_outl(b, (unsigned long __force)ioport_map(port, 4));
}
-void generic_outb_p(unsigned char b, unsigned long port)
+void generic_outb_p(u8 b, unsigned long port)
{
- *(volatile unsigned char*)PORT2ADDR(port) = b;
+ generic_outb(b, port);
delay();
}
-void generic_outw_p(unsigned short b, unsigned long port)
+void generic_outw_p(u16 b, unsigned long port)
{
- *(volatile unsigned short*)PORT2ADDR(port) = b;
+ generic_outw(b, port);
delay();
}
-void generic_outl_p(unsigned int b, unsigned long port)
+void generic_outl_p(u32 b, unsigned long port)
{
- *(volatile unsigned long*)PORT2ADDR(port) = b;
+ generic_outl(b, port);
delay();
}
@@ -154,90 +149,77 @@ void generic_outl_p(unsigned int b, unsigned long port)
* address. However as the port address doesn't change we only need to
* convert the port address to real address once.
*/
-
-void generic_outsb(unsigned long port, const void *buffer, unsigned long count)
+void generic_outsb(unsigned long port, const void *src, unsigned long count)
{
- volatile unsigned char *port_addr;
- const unsigned char *buf=buffer;
+ volatile u8 *port_addr;
+ const u8 *buf = src;
- port_addr = (volatile unsigned char *)PORT2ADDR(port);
+ port_addr = (volatile u8 __force *)ioport_map(port, 1);
- while(count--)
- *port_addr = *buf++;
+ while (count--)
+ *port_addr = *buf++;
}
-void generic_outsw(unsigned long port, const void *buffer, unsigned long count)
+void generic_outsw(unsigned long port, const void *src, unsigned long count)
{
- volatile unsigned short *port_addr;
- const unsigned short *buf=buffer;
+ volatile u16 *port_addr;
+ const u16 *buf = src;
- port_addr = (volatile unsigned short *)PORT2ADDR(port);
+ port_addr = (volatile u16 __force *)ioport_map(port, 2);
- while(count--)
- *port_addr = *buf++;
+ while (count--)
+ *port_addr = *buf++;
-#ifdef SH3_PCMCIA_BUG_WORKAROUND
- ctrl_inb (DUMMY_READ_AREA6);
-#endif
+ dummy_read();
}
-void generic_outsl(unsigned long port, const void *buffer, unsigned long count)
+void generic_outsl(unsigned long port, const void *src, unsigned long count)
{
- volatile unsigned long *port_addr;
- const unsigned long *buf=buffer;
+ volatile u32 *port_addr;
+ const u32 *buf = src;
- port_addr = (volatile unsigned long *)PORT2ADDR(port);
+ port_addr = (volatile u32 __force *)ioport_map(port, 4);
+ while (count--)
+ *port_addr = *buf++;
- while(count--)
- *port_addr = *buf++;
-
-#ifdef SH3_PCMCIA_BUG_WORKAROUND
- ctrl_inb (DUMMY_READ_AREA6);
-#endif
-}
-
-unsigned char generic_readb(unsigned long addr)
-{
- return *(volatile unsigned char*)addr;
+ dummy_read();
}
-unsigned short generic_readw(unsigned long addr)
+u8 generic_readb(void __iomem *addr)
{
- return *(volatile unsigned short*)addr;
+ return ctrl_inb((unsigned long __force)addr);
}
-unsigned int generic_readl(unsigned long addr)
+u16 generic_readw(void __iomem *addr)
{
- return *(volatile unsigned long*)addr;
+ return ctrl_inw((unsigned long __force)addr);
}
-void generic_writeb(unsigned char b, unsigned long addr)
+u32 generic_readl(void __iomem *addr)
{
- *(volatile unsigned char*)addr = b;
+ return ctrl_inl((unsigned long __force)addr);
}
-void generic_writew(unsigned short b, unsigned long addr)
+void generic_writeb(u8 b, void __iomem *addr)
{
- *(volatile unsigned short*)addr = b;
+ ctrl_outb(b, (unsigned long __force)addr);
}
-void generic_writel(unsigned int b, unsigned long addr)
+void generic_writew(u16 b, void __iomem *addr)
{
- *(volatile unsigned long*)addr = b;
+ ctrl_outw(b, (unsigned long __force)addr);
}
-void * generic_ioremap(unsigned long offset, unsigned long size)
+void generic_writel(u32 b, void __iomem *addr)
{
- return (void *) P2SEGADDR(offset);
+ ctrl_outl(b, (unsigned long __force)addr);
}
-EXPORT_SYMBOL(generic_ioremap);
-void generic_iounmap(void *addr)
+void __iomem *generic_ioport_map(unsigned long addr, unsigned int size)
{
+ return (void __iomem *)(addr + generic_io_base);
}
-EXPORT_SYMBOL(generic_iounmap);
-unsigned long generic_isa_port2addr(unsigned long offset)
+void generic_ioport_unmap(void __iomem *addr)
{
- return offset + generic_io_base;
}
diff --git a/arch/sh/kernel/irq.c b/arch/sh/kernel/irq.c
index 54c1712..6883c00 100644
--- a/arch/sh/kernel/irq.c
+++ b/arch/sh/kernel/irq.c
@@ -8,38 +8,13 @@
* SuperH version: Copyright (C) 1999 Niibe Yutaka
*/
-/*
- * IRQs are in fact implemented a bit like signal handlers for the kernel.
- * Naturally it's not a 1:1 relation, but there are similarities.
- */
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/ptrace.h>
-#include <linux/errno.h>
-#include <linux/kernel_stat.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/ioport.h>
+#include <linux/irq.h>
#include <linux/interrupt.h>
-#include <linux/timex.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
-#include <linux/random.h>
-#include <linux/smp.h>
-#include <linux/smp_lock.h>
-#include <linux/init.h>
+#include <linux/kernel_stat.h>
#include <linux/seq_file.h>
-#include <linux/kallsyms.h>
-#include <linux/bitops.h>
-
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/pgalloc.h>
-#include <asm/delay.h>
#include <asm/irq.h>
-#include <linux/irq.h>
-
+#include <asm/processor.h>
+#include <asm/cpu/mmu_context.h>
/*
* 'what should we do if we get a hw irq event on an illegal vector'.
@@ -66,7 +41,7 @@ int show_interrupts(struct seq_file *p, void *v)
seq_putc(p, '\n');
}
- if (i < ACTUAL_NR_IRQS) {
+ if (i < NR_IRQS) {
spin_lock_irqsave(&irq_desc[i].lock, flags);
action = irq_desc[i].action;
if (!action)
@@ -86,19 +61,32 @@ unlock:
}
#endif
+
asmlinkage int do_IRQ(unsigned long r4, unsigned long r5,
unsigned long r6, unsigned long r7,
struct pt_regs regs)
-{
- int irq;
+{
+ int irq = r4;
irq_enter();
- asm volatile("stc r2_bank, %0\n\t"
- "shlr2 %0\n\t"
- "shlr2 %0\n\t"
- "shlr %0\n\t"
- "add #-16, %0\n\t"
- :"=z" (irq));
+
+#ifdef CONFIG_CPU_HAS_INTEVT
+ __asm__ __volatile__ (
+#ifdef CONFIG_CPU_HAS_SR_RB
+ "stc r2_bank, %0\n\t"
+#else
+ "mov.l @%1, %0\n\t"
+#endif
+ "shlr2 %0\n\t"
+ "shlr2 %0\n\t"
+ "shlr %0\n\t"
+ "add #-16, %0\n\t"
+ : "=z" (irq), "=r" (r4)
+ : "1" (INTEVT)
+ : "memory"
+ );
+#endif
+
irq = irq_demux(irq);
__do_IRQ(irq, &regs);
irq_exit();
diff --git a/arch/sh/kernel/machine_kexec.c b/arch/sh/kernel/machine_kexec.c
new file mode 100644
index 0000000..4354652
--- /dev/null
+++ b/arch/sh/kernel/machine_kexec.c
@@ -0,0 +1,112 @@
+/*
+ * machine_kexec.c - handle transition of Linux booting another kernel
+ * Copyright (C) 2002-2003 Eric Biederman <ebiederm@xmission.com>
+ *
+ * GameCube/ppc32 port Copyright (C) 2004 Albert Herranz
+ * LANDISK/sh4 supported by kogiidena
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#include <linux/mm.h>
+#include <linux/kexec.h>
+#include <linux/delay.h>
+#include <linux/reboot.h>
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+#include <asm/mmu_context.h>
+#include <asm/io.h>
+#include <asm/cacheflush.h>
+
+typedef NORET_TYPE void (*relocate_new_kernel_t)(
+ unsigned long indirection_page,
+ unsigned long reboot_code_buffer,
+ unsigned long start_address,
+ unsigned long vbr_reg) ATTRIB_NORET;
+
+const extern unsigned char relocate_new_kernel[];
+const extern unsigned int relocate_new_kernel_size;
+extern void *gdb_vbr_vector;
+
+/*
+ * Provide a dummy crash_notes definition while crash dump arrives to ppc.
+ * This prevents breakage of crash_notes attribute in kernel/ksysfs.c.
+ */
+void *crash_notes = NULL;
+
+void machine_shutdown(void)
+{
+}
+
+void machine_crash_shutdown(struct pt_regs *regs)
+{
+}
+
+/*
+ * Do what every setup is needed on image and the
+ * reboot code buffer to allow us to avoid allocations
+ * later.
+ */
+int machine_kexec_prepare(struct kimage *image)
+{
+ return 0;
+}
+
+void machine_kexec_cleanup(struct kimage *image)
+{
+}
+
+static void kexec_info(struct kimage *image)
+{
+ int i;
+ printk("kexec information\n");
+ for (i = 0; i < image->nr_segments; i++) {
+ printk(" segment[%d]: 0x%08x - 0x%08x (0x%08x)\n",
+ i,
+ (unsigned int)image->segment[i].mem,
+ (unsigned int)image->segment[i].mem + image->segment[i].memsz,
+ (unsigned int)image->segment[i].memsz);
+ }
+ printk(" start : 0x%08x\n\n", (unsigned int)image->start);
+}
+
+
+/*
+ * Do not allocate memory (or fail in any way) in machine_kexec().
+ * We are past the point of no return, committed to rebooting now.
+ */
+NORET_TYPE void machine_kexec(struct kimage *image)
+{
+
+ unsigned long page_list;
+ unsigned long reboot_code_buffer;
+ unsigned long vbr_reg;
+ relocate_new_kernel_t rnk;
+
+#if defined(CONFIG_SH_STANDARD_BIOS)
+ vbr_reg = ((unsigned long )gdb_vbr_vector) - 0x100;
+#else
+ vbr_reg = 0x80000000; // dummy
+#endif
+ /* Interrupts aren't acceptable while we reboot */
+ local_irq_disable();
+
+ page_list = image->head;
+
+ /* we need both effective and real address here */
+ reboot_code_buffer =
+ (unsigned long)page_address(image->control_code_page);
+
+ /* copy our kernel relocation code to the control code page */
+ memcpy((void *)reboot_code_buffer, relocate_new_kernel,
+ relocate_new_kernel_size);
+
+ kexec_info(image);
+ flush_cache_all();
+
+ /* now call it */
+ rnk = (relocate_new_kernel_t) reboot_code_buffer;
+ (*rnk)(page_list, reboot_code_buffer, image->start, vbr_reg);
+}
+
diff --git a/arch/sh/kernel/process.c b/arch/sh/kernel/process.c
index aac15e4..a4dc2b5 100644
--- a/arch/sh/kernel/process.c
+++ b/arch/sh/kernel/process.c
@@ -71,6 +71,16 @@ void cpu_idle(void)
void machine_restart(char * __unused)
{
+
+#ifdef CONFIG_KEXEC
+ struct kimage *image;
+ image = xchg(&kexec_image, 0);
+ if (image) {
+ machine_shutdown();
+ machine_kexec(image);
+ }
+#endif
+
/* SR.BL=1 and invoke address error to let CPU reset (manual reset) */
asm volatile("ldc %0, sr\n\t"
"mov.l @%1, %0" : : "r" (0x10000000), "r" (0x80000001));
diff --git a/arch/sh/kernel/relocate_kernel.S b/arch/sh/kernel/relocate_kernel.S
new file mode 100644
index 0000000..b0695cf
--- /dev/null
+++ b/arch/sh/kernel/relocate_kernel.S
@@ -0,0 +1,102 @@
+/*
+ * relocate_kernel.S - put the kernel image in place to boot
+ * 2005.9.17 kogiidena@eggplant.ddo.jp
+ *
+ * LANDISK/sh4 is supported. Maybe, SH archtecture works well.
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#include <linux/config.h>
+#include <linux/linkage.h>
+
+#define PAGE_SIZE 4096 /* must be same value as in <asm/page.h> */
+
+
+ .globl relocate_new_kernel
+relocate_new_kernel:
+ /* r4 = indirection_page */
+ /* r5 = reboot_code_buffer */
+ /* r6 = start_address */
+ /* r7 = vbr_reg */
+
+ mov.l 10f,r8 /* 4096 */
+ mov.l 11f,r9 /* 0xa0000000 */
+
+ /* stack setting */
+ add r8,r5
+ mov r5,r15
+
+ bra 1f
+ mov r4,r0 /* cmd = indirection_page */
+0:
+ mov.l @r4+,r0 /* cmd = *ind++ */
+
+1: /* addr = (cmd | 0xa0000000) & 0xfffffff0 */
+ mov r0,r2
+ or r9,r2
+ mov #-16,r1
+ and r1,r2
+
+ /* if(cmd & IND_DESTINATION) dst = addr */
+ tst #1,r0
+ bt 2f
+ bra 0b
+ mov r2,r5
+
+2: /* else if(cmd & IND_INDIRECTION) ind = addr */
+ tst #2,r0
+ bt 3f
+ bra 0b
+ mov r2,r4
+
+3: /* else if(cmd & IND_DONE) goto 6 */
+ tst #4,r0
+ bt 4f
+ bra 6f
+ nop
+
+4: /* else if(cmd & IND_SOURCE) memcpy(dst,addr,PAGE_SIZE) */
+ tst #8,r0
+ bt 0b
+
+ mov r8,r3
+ shlr2 r3
+ shlr2 r3
+5:
+ 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
+ add #4,r5
+ bf 5b
+
+ bra 0b
+ nop
+6:
+#ifdef CONFIG_SH_STANDARD_BIOS
+ ldc r7, vbr
+#endif
+ jmp @r6
+ nop
+
+ .align 2
+10:
+ .long PAGE_SIZE
+11:
+ .long 0xa0000000
+
+relocate_new_kernel_end:
+
+ .globl relocate_new_kernel_size
+relocate_new_kernel_size:
+ .long relocate_new_kernel_end - relocate_new_kernel
diff --git a/arch/sh/kernel/time.c b/arch/sh/kernel/time.c
index 671b876..314a275 100644
--- a/arch/sh/kernel/time.c
+++ b/arch/sh/kernel/time.c
@@ -3,7 +3,7 @@
*
* 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, 2003, 2004, 2005 Paul Mundt
* Copyright (C) 2002 M. R. Brown <mrbrown@linux-sh.org>
*
* Some code taken from i386 version.
@@ -11,50 +11,21 @@
*/
#include <linux/config.h>
-#include <linux/errno.h>
-#include <linux/module.h>
-#include <linux/sched.h>
#include <linux/kernel.h>
-#include <linux/param.h>
-#include <linux/string.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/time.h>
-#include <linux/delay.h>
+#include <linux/module.h>
#include <linux/init.h>
-#include <linux/smp.h>
#include <linux/profile.h>
-
-#include <asm/processor.h>
-#include <asm/uaccess.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/delay.h>
-#include <asm/machvec.h>
+#include <asm/clock.h>
#include <asm/rtc.h>
-#include <asm/freq.h>
-#include <asm/cpu/timer.h>
-#ifdef CONFIG_SH_KGDB
+#include <asm/timer.h>
#include <asm/kgdb.h>
-#endif
-
-#include <linux/timex.h>
-#include <linux/irq.h>
-
-#define TMU_TOCR_INIT 0x00
-#define TMU0_TCR_INIT 0x0020
-#define TMU_TSTR_INIT 1
-
-#define TMU0_TCR_CALIB 0x0000
-
-#ifdef CONFIG_CPU_SUBTYPE_ST40STB1
-#define CLOCKGEN_MEMCLKCR 0xbb040038
-#define MEMCLKCR_RATIO_MASK 0x7
-#endif /* CONFIG_CPU_SUBTYPE_ST40STB1 */
extern unsigned long wall_jiffies;
-#define TICK_SIZE (tick_nsec / 1000)
-DEFINE_SPINLOCK(tmu0_lock);
+struct sys_timer *sys_timer;
+
+/* Move this somewhere more sensible.. */
+DEFINE_SPINLOCK(rtc_lock);
+EXPORT_SYMBOL(rtc_lock);
/* XXX: Can we initialize this in a routine somewhere? Dreamcast doesn't want
* these routines anywhere... */
@@ -66,98 +37,14 @@ void (*rtc_get_time)(struct timespec *);
int (*rtc_set_time)(const time_t);
#endif
-#if defined(CONFIG_CPU_SUBTYPE_SH7300)
-static int md_table[] = { 1, 2, 3, 4, 6, 8, 12 };
-#endif
-#if defined(CONFIG_CPU_SH3)
-static int stc_multipliers[] = { 1, 2, 3, 4, 6, 1, 1, 1 };
-static int stc_values[] = { 0, 1, 4, 2, 5, 0, 0, 0 };
-#define bfc_divisors stc_multipliers
-#define bfc_values stc_values
-static int ifc_divisors[] = { 1, 2, 3, 4, 1, 1, 1, 1 };
-static int ifc_values[] = { 0, 1, 4, 2, 0, 0, 0, 0 };
-static int pfc_divisors[] = { 1, 2, 3, 4, 6, 1, 1, 1 };
-static int pfc_values[] = { 0, 1, 4, 2, 5, 0, 0, 0 };
-#elif defined(CONFIG_CPU_SH4)
-#if defined(CONFIG_CPU_SUBTYPE_SH73180)
-static int ifc_divisors[] = { 1, 2, 3, 4, 6, 8, 12, 16 };
-static int ifc_values[] = { 0, 1, 2, 3, 4, 5, 6, 7 };
-#define bfc_divisors ifc_divisors /* Same */
-#define bfc_values ifc_values
-#define pfc_divisors ifc_divisors /* Same */
-#define pfc_values ifc_values
-#else
-static int ifc_divisors[] = { 1, 2, 3, 4, 6, 8, 1, 1 };
-static int ifc_values[] = { 0, 1, 2, 3, 0, 4, 0, 5 };
-#define bfc_divisors ifc_divisors /* Same */
-#define bfc_values ifc_values
-static int pfc_divisors[] = { 2, 3, 4, 6, 8, 2, 2, 2 };
-static int pfc_values[] = { 0, 0, 1, 2, 0, 3, 0, 4 };
-#endif
-#else
-#error "Unknown ifc/bfc/pfc/stc values for this processor"
-#endif
-
/*
* Scheduler clock - returns current time in nanosec units.
*/
-unsigned long long sched_clock(void)
+unsigned long long __attribute__ ((weak)) sched_clock(void)
{
return (unsigned long long)jiffies * (1000000000 / HZ);
}
-static unsigned long do_gettimeoffset(void)
-{
- int count;
- unsigned long flags;
-
- static int count_p = 0x7fffffff; /* for the first call after boot */
- static unsigned long jiffies_p = 0;
-
- /*
- * cache volatile jiffies temporarily; we have IRQs turned off.
- */
- unsigned long jiffies_t;
-
- spin_lock_irqsave(&tmu0_lock, flags);
- /* timer count may underflow right here */
- count = ctrl_inl(TMU0_TCNT); /* read the latched count */
-
- jiffies_t = jiffies;
-
- /*
- * avoiding timer inconsistencies (they are rare, but they happen)...
- * there is one kind of problem that must be avoided here:
- * 1. the timer counter underflows
- */
-
- if( jiffies_t == jiffies_p ) {
- if( count > count_p ) {
- /* the nutcase */
-
- if(ctrl_inw(TMU0_TCR) & 0x100) { /* Check UNF bit */
- /*
- * We cannot detect lost timer interrupts ...
- * well, that's why we call them lost, don't we? :)
- * [hmm, on the Pentium and Alpha we can ... sort of]
- */
- count -= LATCH;
- } else {
- printk("do_slow_gettimeoffset(): hardware timer problem?\n");
- }
- }
- } else
- jiffies_p = jiffies_t;
-
- count_p = count;
- spin_unlock_irqrestore(&tmu0_lock, flags);
-
- count = ((LATCH-1) - count) * TICK_SIZE;
- count = (count + LATCH/2) / LATCH;
-
- return count;
-}
-
void do_gettimeofday(struct timeval *tv)
{
unsigned long seq;
@@ -166,7 +53,7 @@ void do_gettimeofday(struct timeval *tv)
do {
seq = read_seqbegin(&xtime_lock);
- usec = do_gettimeoffset();
+ usec = get_timer_offset();
lost = jiffies - wall_jiffies;
if (lost)
@@ -202,7 +89,7 @@ int do_settimeofday(struct timespec *tv)
* wall time. Discover what correction gettimeofday() would have
* made, and then undo it!
*/
- nsec -= 1000 * (do_gettimeoffset() +
+ nsec -= 1000 * (get_timer_offset() +
(jiffies - wall_jiffies) * (1000000 / HZ));
wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec);
@@ -224,10 +111,10 @@ EXPORT_SYMBOL(do_settimeofday);
static long last_rtc_update;
/*
- * timer_interrupt() needs to keep up the real-time clock,
+ * handle_timer_tick() needs to keep up the real-time clock,
* as well as call the "do_timer()" routine every clocktick
*/
-static inline void do_timer_interrupt(int irq, struct pt_regs *regs)
+void handle_timer_tick(struct pt_regs *regs)
{
do_timer(regs);
#ifndef CONFIG_SMP
@@ -252,337 +139,35 @@ static inline void do_timer_interrupt(int irq, struct pt_regs *regs)
if (rtc_set_time(xtime.tv_sec) == 0)
last_rtc_update = xtime.tv_sec;
else
- last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
+ /* do it again in 60s */
+ last_rtc_update = xtime.tv_sec - 600;
}
}
-/*
- * This is the same as the above, except we _also_ save the current
- * Time Stamp Counter value at the time of the timer interrupt, so that
- * we later on can estimate the time of day more exactly.
- */
-static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
- unsigned long timer_status;
-
- /* Clear UNF bit */
- timer_status = ctrl_inw(TMU0_TCR);
- timer_status &= ~0x100;
- ctrl_outw(timer_status, TMU0_TCR);
-
- /*
- * Here we are in the timer irq handler. We just have irqs locally
- * disabled but we don't know if the timer_bh is running on the other
- * CPU. We need to avoid to SMP race with it. NOTE: we don' t need
- * the irq version of write_lock because as just said we have irq
- * locally disabled. -arca
- */
- write_seqlock(&xtime_lock);
- do_timer_interrupt(irq, regs);
- write_sequnlock(&xtime_lock);
-
- return IRQ_HANDLED;
-}
-
-/*
- * Hah! We'll see if this works (switching from usecs to nsecs).
- */
-static unsigned int __init get_timer_frequency(void)
-{
- u32 freq;
- struct timespec ts1, ts2;
- unsigned long diff_nsec;
- unsigned long factor;
-
- /* Setup the timer: We don't want to generate interrupts, just
- * have it count down at its natural rate.
- */
- ctrl_outb(0, TMU_TSTR);
-#if !defined(CONFIG_CPU_SUBTYPE_SH7300)
- ctrl_outb(TMU_TOCR_INIT, TMU_TOCR);
-#endif
- ctrl_outw(TMU0_TCR_CALIB, TMU0_TCR);
- ctrl_outl(0xffffffff, TMU0_TCOR);
- ctrl_outl(0xffffffff, TMU0_TCNT);
-
- rtc_get_time(&ts2);
-
- do {
- rtc_get_time(&ts1);
- } while (ts1.tv_nsec == ts2.tv_nsec && ts1.tv_sec == ts2.tv_sec);
-
- /* actually start the timer */
- ctrl_outb(TMU_TSTR_INIT, TMU_TSTR);
-
- do {
- rtc_get_time(&ts2);
- } while (ts1.tv_nsec == ts2.tv_nsec && ts1.tv_sec == ts2.tv_sec);
-
- freq = 0xffffffff - ctrl_inl(TMU0_TCNT);
- if (ts2.tv_nsec < ts1.tv_nsec) {
- ts2.tv_nsec += 1000000000;
- ts2.tv_sec--;
- }
-
- diff_nsec = (ts2.tv_sec - ts1.tv_sec) * 1000000000 + (ts2.tv_nsec - ts1.tv_nsec);
-
- /* this should work well if the RTC has a precision of n Hz, where
- * n is an integer. I don't think we have to worry about the other
- * cases. */
- factor = (1000000000 + diff_nsec/2) / diff_nsec;
-
- if (factor * diff_nsec > 1100000000 ||
- factor * diff_nsec < 900000000)
- panic("weird RTC (diff_nsec %ld)", diff_nsec);
-
- return freq * factor;
-}
-
-void (*board_time_init)(void);
-void (*board_timer_setup)(struct irqaction *irq);
-
-static unsigned int sh_pclk_freq __initdata = CONFIG_SH_PCLK_FREQ;
-
-static int __init sh_pclk_setup(char *str)
-{
- unsigned int freq;
-
- if (get_option(&str, &freq))
- sh_pclk_freq = freq;
-
- return 1;
-}
-__setup("sh_pclk=", sh_pclk_setup);
-
-static struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, CPU_MASK_NONE, "timer", NULL, NULL};
-
-void get_current_frequency_divisors(unsigned int *ifc, unsigned int *bfc, unsigned int *pfc)
-{
- unsigned int frqcr = ctrl_inw(FRQCR);
-
-#if defined(CONFIG_CPU_SH3)
-#if defined(CONFIG_CPU_SUBTYPE_SH7300)
- *ifc = md_table[((frqcr & 0x0070) >> 4)];
- *bfc = md_table[((frqcr & 0x0700) >> 8)];
- *pfc = md_table[frqcr & 0x0007];
-#elif defined(CONFIG_CPU_SUBTYPE_SH7705)
- *bfc = stc_multipliers[(frqcr & 0x0300) >> 8];
- *ifc = ifc_divisors[(frqcr & 0x0030) >> 4];
- *pfc = pfc_divisors[frqcr & 0x0003];
-#else
- unsigned int tmp;
-
- tmp = (frqcr & 0x8000) >> 13;
- tmp |= (frqcr & 0x0030) >> 4;
- *bfc = stc_multipliers[tmp];
- tmp = (frqcr & 0x4000) >> 12;
- tmp |= (frqcr & 0x000c) >> 2;
- *ifc = ifc_divisors[tmp];
- tmp = (frqcr & 0x2000) >> 11;
- tmp |= frqcr & 0x0003;
- *pfc = pfc_divisors[tmp];
-#endif
-#elif defined(CONFIG_CPU_SH4)
-#if defined(CONFIG_CPU_SUBTYPE_SH73180)
- *ifc = ifc_divisors[(frqcr>> 20) & 0x0007];
- *bfc = bfc_divisors[(frqcr>> 12) & 0x0007];
- *pfc = pfc_divisors[frqcr & 0x0007];
-#else
- *ifc = ifc_divisors[(frqcr >> 6) & 0x0007];
- *bfc = bfc_divisors[(frqcr >> 3) & 0x0007];
- *pfc = pfc_divisors[frqcr & 0x0007];
-#endif
-#endif
-}
-
-/*
- * This bit of ugliness builds up accessor routines to get at both
- * the divisors and the physical values.
- */
-#define _FREQ_TABLE(x) \
- unsigned int get_##x##_divisor(unsigned int value) \
- { return x##_divisors[value]; } \
- \
- unsigned int get_##x##_value(unsigned int divisor) \
- { return x##_values[(divisor - 1)]; }
-
-_FREQ_TABLE(ifc);
-_FREQ_TABLE(bfc);
-_FREQ_TABLE(pfc);
-
-#ifdef CONFIG_CPU_SUBTYPE_ST40STB1
-
-/*
- * The ST40 divisors are totally different so we set the cpu data
- * clocks using a different algorithm
- *
- * I've just plugged this from the 2.4 code
- * - Alex Bennee <kernel-hacker@bennee.com>
- */
-#define CCN_PVR_CHIP_SHIFT 24
-#define CCN_PVR_CHIP_MASK 0xff
-#define CCN_PVR_CHIP_ST40STB1 0x4
-
-
-struct frqcr_data {
- unsigned short frqcr;
-
- struct {
- unsigned char multiplier;
- unsigned char divisor;
- } factor[3];
-};
-
-static struct frqcr_data st40_frqcr_table[] = {
- { 0x000, {{1,1}, {1,1}, {1,2}}},
- { 0x002, {{1,1}, {1,1}, {1,4}}},
- { 0x004, {{1,1}, {1,1}, {1,8}}},
- { 0x008, {{1,1}, {1,2}, {1,2}}},
- { 0x00A, {{1,1}, {1,2}, {1,4}}},
- { 0x00C, {{1,1}, {1,2}, {1,8}}},
- { 0x011, {{1,1}, {2,3}, {1,6}}},
- { 0x013, {{1,1}, {2,3}, {1,3}}},
- { 0x01A, {{1,1}, {1,2}, {1,4}}},
- { 0x01C, {{1,1}, {1,2}, {1,8}}},
- { 0x023, {{1,1}, {2,3}, {1,3}}},
- { 0x02C, {{1,1}, {1,2}, {1,8}}},
- { 0x048, {{1,2}, {1,2}, {1,4}}},
- { 0x04A, {{1,2}, {1,2}, {1,6}}},
- { 0x04C, {{1,2}, {1,2}, {1,8}}},
- { 0x05A, {{1,2}, {1,3}, {1,6}}},
- { 0x05C, {{1,2}, {1,3}, {1,6}}},
- { 0x063, {{1,2}, {1,4}, {1,4}}},
- { 0x06C, {{1,2}, {1,4}, {1,8}}},
- { 0x091, {{1,3}, {1,3}, {1,6}}},
- { 0x093, {{1,3}, {1,3}, {1,6}}},
- { 0x0A3, {{1,3}, {1,6}, {1,6}}},
- { 0x0DA, {{1,4}, {1,4}, {1,8}}},
- { 0x0DC, {{1,4}, {1,4}, {1,8}}},
- { 0x0EC, {{1,4}, {1,8}, {1,8}}},
- { 0x123, {{1,4}, {1,4}, {1,8}}},
- { 0x16C, {{1,4}, {1,8}, {1,8}}},
+static struct sysdev_class timer_sysclass = {
+ set_kset_name("timer"),
};
-struct memclk_data {
- unsigned char multiplier;
- unsigned char divisor;
-};
-
-static struct memclk_data st40_memclk_table[8] = {
- {1,1}, // 000
- {1,2}, // 001
- {1,3}, // 010
- {2,3}, // 011
- {1,4}, // 100
- {1,6}, // 101
- {1,8}, // 110
- {1,8} // 111
-};
-
-static void st40_specific_time_init(unsigned int module_clock, unsigned short frqcr)
+static int __init timer_init_sysfs(void)
{
- unsigned int cpu_clock, master_clock, bus_clock, memory_clock;
- struct frqcr_data *d;
- int a;
- unsigned long memclkcr;
- struct memclk_data *e;
+ int ret = sysdev_class_register(&timer_sysclass);
+ if (ret != 0)
+ return ret;
- for (a = 0; a < ARRAY_SIZE(st40_frqcr_table); a++) {
- d = &st40_frqcr_table[a];
-
- if (d->frqcr == (frqcr & 0x1ff))
- break;
- }
+ sys_timer->dev.cls = &timer_sysclass;
+ return sysdev_register(&sys_timer->dev);
+}
- if (a == ARRAY_SIZE(st40_frqcr_table)) {
- d = st40_frqcr_table;
+device_initcall(timer_init_sysfs);
- printk("ERROR: Unrecognised FRQCR value (0x%x), "
- "using default multipliers\n", frqcr);
- }
-
- memclkcr = ctrl_inl(CLOCKGEN_MEMCLKCR);
- e = &st40_memclk_table[memclkcr & MEMCLKCR_RATIO_MASK];
-
- printk(KERN_INFO "Clock multipliers: CPU: %d/%d Bus: %d/%d "
- "Mem: %d/%d Periph: %d/%d\n",
- d->factor[0].multiplier, d->factor[0].divisor,
- d->factor[1].multiplier, d->factor[1].divisor,
- e->multiplier, e->divisor,
- d->factor[2].multiplier, d->factor[2].divisor);
-
- master_clock = module_clock * d->factor[2].divisor
- / d->factor[2].multiplier;
- bus_clock = master_clock * d->factor[1].multiplier
- / d->factor[1].divisor;
- memory_clock = master_clock * e->multiplier
- / e->divisor;
- cpu_clock = master_clock * d->factor[0].multiplier
- / d->factor[0].divisor;
-
- current_cpu_data.cpu_clock = cpu_clock;
- current_cpu_data.master_clock = master_clock;
- current_cpu_data.bus_clock = bus_clock;
- current_cpu_data.memory_clock = memory_clock;
- current_cpu_data.module_clock = module_clock;
-}
-#endif
+void (*board_time_init)(void);
void __init time_init(void)
{
- unsigned int timer_freq = 0;
- unsigned int ifc, pfc, bfc;
- unsigned long interval;
-#ifdef CONFIG_CPU_SUBTYPE_ST40STB1
- unsigned long pvr;
- unsigned short frqcr;
-#endif
-
if (board_time_init)
board_time_init();
- /*
- * If we don't have an RTC (such as with the SH7300), don't attempt to
- * probe the timer frequency. Rely on an either hardcoded peripheral
- * clock value, or on the sh_pclk command line option. Note that we
- * still need to have CONFIG_SH_PCLK_FREQ set in order for things like
- * CLOCK_TICK_RATE to be sane.
- */
- current_cpu_data.module_clock = sh_pclk_freq;
-
-#ifdef CONFIG_SH_PCLK_CALC
- /* XXX: Switch this over to a more generic test. */
- {
- unsigned int freq;
-
- /*
- * If we've specified a peripheral clock frequency, and we have
- * an RTC, compare it against the autodetected value. Complain
- * if there's a mismatch.
- */
- timer_freq = get_timer_frequency();
- freq = timer_freq * 4;
-
- if (sh_pclk_freq && (sh_pclk_freq/100*99 > freq || sh_pclk_freq/100*101 < freq)) {
- printk(KERN_NOTICE "Calculated peripheral clock value "
- "%d differs from sh_pclk value %d, fixing..\n",
- freq, sh_pclk_freq);
- current_cpu_data.module_clock = freq;
- }
- }
-#endif
-
-#ifdef CONFIG_CPU_SUBTYPE_ST40STB1
- /* XXX: Update ST40 code to use board_time_init() */
- pvr = ctrl_inl(CCN_PVR);
- frqcr = ctrl_inw(FRQCR);
- printk("time.c ST40 Probe: PVR %08lx, FRQCR %04hx\n", pvr, frqcr);
-
- if (((pvr >> CCN_PVR_CHIP_SHIFT) & CCN_PVR_CHIP_MASK) == CCN_PVR_CHIP_ST40STB1)
- st40_specific_time_init(current_cpu_data.module_clock, frqcr);
- else
-#endif
- get_current_frequency_divisors(&ifc, &bfc, &pfc);
+ clk_init();
if (rtc_get_time) {
rtc_get_time(&xtime);
@@ -594,51 +179,12 @@ void __init time_init(void)
set_normalized_timespec(&wall_to_monotonic,
-xtime.tv_sec, -xtime.tv_nsec);
- if (board_timer_setup) {
- board_timer_setup(&irq0);
- } else {
- setup_irq(TIMER_IRQ, &irq0);
- }
-
/*
- * for ST40 chips the current_cpu_data should already be set
- * so not having valid pfc/bfc/ifc shouldn't be a problem
+ * Find the timer to use as the system timer, it will be
+ * initialized for us.
*/
- if (!current_cpu_data.master_clock)
- current_cpu_data.master_clock = current_cpu_data.module_clock * pfc;
- if (!current_cpu_data.bus_clock)
- current_cpu_data.bus_clock = current_cpu_data.master_clock / bfc;
- if (!current_cpu_data.cpu_clock)
- current_cpu_data.cpu_clock = current_cpu_data.master_clock / ifc;
-
- printk("CPU clock: %d.%02dMHz\n",
- (current_cpu_data.cpu_clock / 1000000),
- (current_cpu_data.cpu_clock % 1000000)/10000);
- printk("Bus clock: %d.%02dMHz\n",
- (current_cpu_data.bus_clock / 1000000),
- (current_cpu_data.bus_clock % 1000000)/10000);
-#ifdef CONFIG_CPU_SUBTYPE_ST40STB1
- printk("Memory clock: %d.%02dMHz\n",
- (current_cpu_data.memory_clock / 1000000),
- (current_cpu_data.memory_clock % 1000000)/10000);
-#endif
- printk("Module clock: %d.%02dMHz\n",
- (current_cpu_data.module_clock / 1000000),
- (current_cpu_data.module_clock % 1000000)/10000);
-
- interval = (current_cpu_data.module_clock/4 + HZ/2) / HZ;
-
- printk("Interval = %ld\n", interval);
-
- /* Start TMU0 */
- ctrl_outb(0, TMU_TSTR);
-#if !defined(CONFIG_CPU_SUBTYPE_SH7300)
- ctrl_outb(TMU_TOCR_INIT, TMU_TOCR);
-#endif
- ctrl_outw(TMU0_TCR_INIT, TMU0_TCR);
- ctrl_outl(interval, TMU0_TCOR);
- ctrl_outl(interval, TMU0_TCNT);
- ctrl_outb(TMU_TSTR_INIT, TMU_TSTR);
+ sys_timer = get_sys_timer();
+ printk(KERN_INFO "Using %s for system timer\n", sys_timer->name);
#if defined(CONFIG_SH_KGDB)
/*
diff --git a/arch/sh/kernel/timers/Makefile b/arch/sh/kernel/timers/Makefile
new file mode 100644
index 0000000..151a6a3
--- /dev/null
+++ b/arch/sh/kernel/timers/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the various Linux/SuperH timers
+#
+
+obj-y := timer.o
+
+obj-$(CONFIG_SH_TMU) += timer-tmu.o
+
diff --git a/arch/sh/kernel/timers/timer-tmu.c b/arch/sh/kernel/timers/timer-tmu.c
new file mode 100644
index 0000000..96a64cb
--- /dev/null
+++ b/arch/sh/kernel/timers/timer-tmu.c
@@ -0,0 +1,229 @@
+/*
+ * arch/sh/kernel/timers/timer-tmu.c - TMU Timer Support
+ *
+ * Copyright (C) 2005 Paul Mundt
+ *
+ * TMU handling code 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 <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/seqlock.h>
+#include <asm/timer.h>
+#include <asm/rtc.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/clock.h>
+
+#define TMU_TOCR_INIT 0x00
+#define TMU0_TCR_INIT 0x0020
+#define TMU_TSTR_INIT 1
+
+#define TMU0_TCR_CALIB 0x0000
+
+static DEFINE_SPINLOCK(tmu0_lock);
+
+static unsigned long tmu_timer_get_offset(void)
+{
+ int count;
+ unsigned long flags;
+
+ static int count_p = 0x7fffffff; /* for the first call after boot */
+ static unsigned long jiffies_p = 0;
+
+ /*
+ * cache volatile jiffies temporarily; we have IRQs turned off.
+ */
+ unsigned long jiffies_t;
+
+ spin_lock_irqsave(&tmu0_lock, flags);
+ /* timer count may underflow right here */
+ count = ctrl_inl(TMU0_TCNT); /* read the latched count */
+
+ jiffies_t = jiffies;
+
+ /*
+ * avoiding timer inconsistencies (they are rare, but they happen)...
+ * there is one kind of problem that must be avoided here:
+ * 1. the timer counter underflows
+ */
+
+ if (jiffies_t == jiffies_p) {
+ if (count > count_p) {
+ /* the nutcase */
+ if (ctrl_inw(TMU0_TCR) & 0x100) { /* Check UNF bit */
+ count -= LATCH;
+ } else {
+ printk("%s (): hardware timer problem?\n",
+ __FUNCTION__);
+ }
+ }
+ } else
+ jiffies_p = jiffies_t;
+
+ count_p = count;
+ spin_unlock_irqrestore(&tmu0_lock, flags);
+
+ count = ((LATCH-1) - count) * TICK_SIZE;
+ count = (count + LATCH/2) / LATCH;
+
+ return count;
+}
+
+static irqreturn_t tmu_timer_interrupt(int irq, void *dev_id,
+ struct pt_regs *regs)
+{
+ unsigned long timer_status;
+
+ /* Clear UNF bit */
+ timer_status = ctrl_inw(TMU0_TCR);
+ timer_status &= ~0x100;
+ ctrl_outw(timer_status, TMU0_TCR);
+
+ /*
+ * Here we are in the timer irq handler. We just have irqs locally
+ * disabled but we don't know if the timer_bh is running on the other
+ * CPU. We need to avoid to SMP race with it. NOTE: we don' t need
+ * the irq version of write_lock because as just said we have irq
+ * locally disabled. -arca
+ */
+ write_seqlock(&xtime_lock);
+ handle_timer_tick(regs);
+ write_sequnlock(&xtime_lock);
+
+ return IRQ_HANDLED;
+}
+
+static struct irqaction tmu_irq = {
+ .name = "timer",
+ .handler = tmu_timer_interrupt,
+ .flags = SA_INTERRUPT,
+ .mask = CPU_MASK_NONE,
+};
+
+/*
+ * Hah! We'll see if this works (switching from usecs to nsecs).
+ */
+static unsigned long tmu_timer_get_frequency(void)
+{
+ u32 freq;
+ struct timespec ts1, ts2;
+ unsigned long diff_nsec;
+ unsigned long factor;
+
+ /* Setup the timer: We don't want to generate interrupts, just
+ * have it count down at its natural rate.
+ */
+ ctrl_outb(0, TMU_TSTR);
+#if !defined(CONFIG_CPU_SUBTYPE_SH7300) && !defined(CONFIG_CPU_SUBTYPE_SH7760)
+ ctrl_outb(TMU_TOCR_INIT, TMU_TOCR);
+#endif
+ ctrl_outw(TMU0_TCR_CALIB, TMU0_TCR);
+ ctrl_outl(0xffffffff, TMU0_TCOR);
+ ctrl_outl(0xffffffff, TMU0_TCNT);
+
+ rtc_get_time(&ts2);
+
+ do {
+ rtc_get_time(&ts1);
+ } while (ts1.tv_nsec == ts2.tv_nsec && ts1.tv_sec == ts2.tv_sec);
+
+ /* actually start the timer */
+ ctrl_outb(TMU_TSTR_INIT, TMU_TSTR);
+
+ do {
+ rtc_get_time(&ts2);
+ } while (ts1.tv_nsec == ts2.tv_nsec && ts1.tv_sec == ts2.tv_sec);
+
+ freq = 0xffffffff - ctrl_inl(TMU0_TCNT);
+ if (ts2.tv_nsec < ts1.tv_nsec) {
+ ts2.tv_nsec += 1000000000;
+ ts2.tv_sec--;
+ }
+
+ diff_nsec = (ts2.tv_sec - ts1.tv_sec) * 1000000000 + (ts2.tv_nsec - ts1.tv_nsec);
+
+ /* this should work well if the RTC has a precision of n Hz, where
+ * n is an integer. I don't think we have to worry about the other
+ * cases. */
+ factor = (1000000000 + diff_nsec/2) / diff_nsec;
+
+ if (factor * diff_nsec > 1100000000 ||
+ factor * diff_nsec < 900000000)
+ panic("weird RTC (diff_nsec %ld)", diff_nsec);
+
+ return freq * factor;
+}
+
+static void tmu_clk_init(struct clk *clk)
+{
+ u8 divisor = TMU0_TCR_INIT & 0x7;
+ ctrl_outw(TMU0_TCR_INIT, TMU0_TCR);
+ clk->rate = clk->parent->rate / (4 << (divisor << 1));
+}
+
+static void tmu_clk_recalc(struct clk *clk)
+{
+ u8 divisor = ctrl_inw(TMU0_TCR) & 0x7;
+ clk->rate = clk->parent->rate / (4 << (divisor << 1));
+}
+
+static struct clk_ops tmu_clk_ops = {
+ .init = tmu_clk_init,
+ .recalc = tmu_clk_recalc,
+};
+
+static struct clk tmu0_clk = {
+ .name = "tmu0_clk",
+ .ops = &tmu_clk_ops,
+};
+
+static int tmu_timer_init(void)
+{
+ unsigned long interval;
+
+ setup_irq(TIMER_IRQ, &tmu_irq);
+
+ tmu0_clk.parent = clk_get("module_clk");
+
+ /* Start TMU0 */
+ ctrl_outb(0, TMU_TSTR);
+#if !defined(CONFIG_CPU_SUBTYPE_SH7300) && !defined(CONFIG_CPU_SUBTYPE_SH7760)
+ ctrl_outb(TMU_TOCR_INIT, TMU_TOCR);
+#endif
+
+ clk_register(&tmu0_clk);
+ clk_enable(&tmu0_clk);
+
+ interval = (clk_get_rate(&tmu0_clk) + HZ / 2) / HZ;
+ printk(KERN_INFO "Interval = %ld\n", interval);
+
+ ctrl_outl(interval, TMU0_TCOR);
+ ctrl_outl(interval, TMU0_TCNT);
+
+ ctrl_outb(TMU_TSTR_INIT, TMU_TSTR);
+
+ return 0;
+}
+
+struct sys_timer_ops tmu_timer_ops = {
+ .init = tmu_timer_init,
+ .get_frequency = tmu_timer_get_frequency,
+ .get_offset = tmu_timer_get_offset,
+};
+
+struct sys_timer tmu_timer = {
+ .name = "tmu",
+ .ops = &tmu_timer_ops,
+};
+
diff --git a/arch/sh/kernel/timers/timer.c b/arch/sh/kernel/timers/timer.c
new file mode 100644
index 0000000..dc1f631
--- /dev/null
+++ b/arch/sh/kernel/timers/timer.c
@@ -0,0 +1,50 @@
+/*
+ * arch/sh/kernel/timers/timer.c - Common timer code
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <asm/timer.h>
+
+static struct sys_timer *sys_timers[] __initdata = {
+#ifdef CONFIG_SH_TMU
+ &tmu_timer,
+#endif
+ NULL,
+};
+
+static char timer_override[10] __initdata;
+static int __init timer_setup(char *str)
+{
+ if (str)
+ strlcpy(timer_override, str, sizeof(timer_override));
+ return 1;
+}
+__setup("timer=", timer_setup);
+
+struct sys_timer *get_sys_timer(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(sys_timers); i++) {
+ struct sys_timer *t = sys_timers[i];
+
+ if (unlikely(!t))
+ break;
+ if (unlikely(timer_override[0]))
+ if ((strcmp(timer_override, t->name) != 0))
+ continue;
+ if (likely(t->ops->init() == 0))
+ return t;
+ }
+
+ return NULL;
+}
+