summaryrefslogtreecommitdiffstats
path: root/arch/sh/kernel/cpu
diff options
context:
space:
mode:
Diffstat (limited to 'arch/sh/kernel/cpu')
-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
20 files changed, 1702 insertions, 415 deletions
diff --git a/arch/sh/kernel/cpu/Makefile b/arch/sh/kernel/cpu/Makefile
index cd43714df61a..5bfc33bec5d0 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 3278d234bb1b..fc6c4bd40c65 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 000000000000..989e7fdd524d
--- /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 000000000000..e3cccea15e1d
--- /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 a963d00a971e..baed9a550d39 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 000000000000..06e8afab32e4
--- /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 71f92096132b..fdbd718ae5c6 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 000000000000..95d6024fe1ae
--- /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 a64532e4dc63..b54dbb9a0c86 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 000000000000..c3c945958baf
--- /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 000000000000..e804174b9625
--- /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 000000000000..dfdbf3277fd7
--- /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 000000000000..10461a745e5f
--- /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 ead1071eac73..3d5cafc71ae3 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 000000000000..bfdf5fe8d948
--- /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 000000000000..dca9f87a12d6
--- /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 000000000000..2fa5cb2ae68d
--- /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 000000000000..c8694bac6477
--- /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 000000000000..93ad367342c9
--- /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 f6b16ba01932..000000000000
--- 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 */