summaryrefslogtreecommitdiffstats
path: root/arch/sh/boards/mach-cayman
diff options
context:
space:
mode:
Diffstat (limited to 'arch/sh/boards/mach-cayman')
-rw-r--r--arch/sh/boards/mach-cayman/Makefile5
-rw-r--r--arch/sh/boards/mach-cayman/irq.c197
-rw-r--r--arch/sh/boards/mach-cayman/led.c51
-rw-r--r--arch/sh/boards/mach-cayman/setup.c187
4 files changed, 440 insertions, 0 deletions
diff --git a/arch/sh/boards/mach-cayman/Makefile b/arch/sh/boards/mach-cayman/Makefile
new file mode 100644
index 000000000000..489a8f867368
--- /dev/null
+++ b/arch/sh/boards/mach-cayman/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Hitachi Cayman specific parts of the kernel
+#
+obj-y := setup.o irq.o
+obj-$(CONFIG_HEARTBEAT) += led.o
diff --git a/arch/sh/boards/mach-cayman/irq.c b/arch/sh/boards/mach-cayman/irq.c
new file mode 100644
index 000000000000..ceb37ae92c70
--- /dev/null
+++ b/arch/sh/boards/mach-cayman/irq.c
@@ -0,0 +1,197 @@
+/*
+ * arch/sh/mach-cayman/irq.c - SH-5 Cayman Interrupt Support
+ *
+ * This file handles the board specific parts of the Cayman interrupt system
+ *
+ * Copyright (C) 2002 Stuart Menefy
+ *
+ * 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/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/signal.h>
+#include <cpu/irq.h>
+#include <asm/page.h>
+
+/* Setup for the SMSC FDC37C935 / LAN91C100FD */
+#define SMSC_IRQ IRQ_IRL1
+
+/* Setup for PCI Bus 2, which transmits interrupts via the EPLD */
+#define PCI2_IRQ IRQ_IRL3
+
+unsigned long epld_virt;
+
+#define EPLD_BASE 0x04002000
+#define EPLD_STATUS_BASE (epld_virt + 0x10)
+#define EPLD_MASK_BASE (epld_virt + 0x20)
+
+/* Note the SMSC SuperIO chip and SMSC LAN chip interrupts are all muxed onto
+ the same SH-5 interrupt */
+
+static irqreturn_t cayman_interrupt_smsc(int irq, void *dev_id)
+{
+ printk(KERN_INFO "CAYMAN: spurious SMSC interrupt\n");
+ return IRQ_NONE;
+}
+
+static irqreturn_t cayman_interrupt_pci2(int irq, void *dev_id)
+{
+ printk(KERN_INFO "CAYMAN: spurious PCI interrupt, IRQ %d\n", irq);
+ return IRQ_NONE;
+}
+
+static struct irqaction cayman_action_smsc = {
+ .name = "Cayman SMSC Mux",
+ .handler = cayman_interrupt_smsc,
+ .flags = IRQF_DISABLED,
+};
+
+static struct irqaction cayman_action_pci2 = {
+ .name = "Cayman PCI2 Mux",
+ .handler = cayman_interrupt_pci2,
+ .flags = IRQF_DISABLED,
+};
+
+static void enable_cayman_irq(unsigned int irq)
+{
+ unsigned long flags;
+ unsigned long mask;
+ unsigned int reg;
+ unsigned char bit;
+
+ irq -= START_EXT_IRQS;
+ reg = EPLD_MASK_BASE + ((irq / 8) << 2);
+ bit = 1<<(irq % 8);
+ local_irq_save(flags);
+ mask = ctrl_inl(reg);
+ mask |= bit;
+ ctrl_outl(mask, reg);
+ local_irq_restore(flags);
+}
+
+void disable_cayman_irq(unsigned int irq)
+{
+ unsigned long flags;
+ unsigned long mask;
+ unsigned int reg;
+ unsigned char bit;
+
+ irq -= START_EXT_IRQS;
+ reg = EPLD_MASK_BASE + ((irq / 8) << 2);
+ bit = 1<<(irq % 8);
+ local_irq_save(flags);
+ mask = ctrl_inl(reg);
+ mask &= ~bit;
+ ctrl_outl(mask, reg);
+ local_irq_restore(flags);
+}
+
+static void ack_cayman_irq(unsigned int irq)
+{
+ disable_cayman_irq(irq);
+}
+
+static void end_cayman_irq(unsigned int irq)
+{
+ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+ enable_cayman_irq(irq);
+}
+
+static unsigned int startup_cayman_irq(unsigned int irq)
+{
+ enable_cayman_irq(irq);
+ return 0; /* never anything pending */
+}
+
+static void shutdown_cayman_irq(unsigned int irq)
+{
+ disable_cayman_irq(irq);
+}
+
+struct hw_interrupt_type cayman_irq_type = {
+ .typename = "Cayman-IRQ",
+ .startup = startup_cayman_irq,
+ .shutdown = shutdown_cayman_irq,
+ .enable = enable_cayman_irq,
+ .disable = disable_cayman_irq,
+ .ack = ack_cayman_irq,
+ .end = end_cayman_irq,
+};
+
+int cayman_irq_demux(int evt)
+{
+ int irq = intc_evt_to_irq[evt];
+
+ if (irq == SMSC_IRQ) {
+ unsigned long status;
+ int i;
+
+ status = ctrl_inl(EPLD_STATUS_BASE) &
+ ctrl_inl(EPLD_MASK_BASE) & 0xff;
+ if (status == 0) {
+ irq = -1;
+ } else {
+ for (i=0; i<8; i++) {
+ if (status & (1<<i))
+ break;
+ }
+ irq = START_EXT_IRQS + i;
+ }
+ }
+
+ if (irq == PCI2_IRQ) {
+ unsigned long status;
+ int i;
+
+ status = ctrl_inl(EPLD_STATUS_BASE + 3 * sizeof(u32)) &
+ ctrl_inl(EPLD_MASK_BASE + 3 * sizeof(u32)) & 0xff;
+ if (status == 0) {
+ irq = -1;
+ } else {
+ for (i=0; i<8; i++) {
+ if (status & (1<<i))
+ break;
+ }
+ irq = START_EXT_IRQS + (3 * 8) + i;
+ }
+ }
+
+ return irq;
+}
+
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_SYSCTL)
+int cayman_irq_describe(char* p, int irq)
+{
+ if (irq < NR_INTC_IRQS) {
+ return intc_irq_describe(p, irq);
+ } else if (irq < NR_INTC_IRQS + 8) {
+ return sprintf(p, "(SMSC %d)", irq - NR_INTC_IRQS);
+ } else if ((irq >= NR_INTC_IRQS + 24) && (irq < NR_INTC_IRQS + 32)) {
+ return sprintf(p, "(PCI2 %d)", irq - (NR_INTC_IRQS + 24));
+ }
+
+ return 0;
+}
+#endif
+
+void init_cayman_irq(void)
+{
+ int i;
+
+ epld_virt = onchip_remap(EPLD_BASE, 1024, "EPLD");
+ if (!epld_virt) {
+ printk(KERN_ERR "Cayman IRQ: Unable to remap EPLD\n");
+ return;
+ }
+
+ for (i=0; i<NR_EXT_IRQS; i++) {
+ irq_desc[START_EXT_IRQS + i].chip = &cayman_irq_type;
+ }
+
+ /* Setup the SMSC interrupt */
+ setup_irq(SMSC_IRQ, &cayman_action_smsc);
+ setup_irq(PCI2_IRQ, &cayman_action_pci2);
+}
diff --git a/arch/sh/boards/mach-cayman/led.c b/arch/sh/boards/mach-cayman/led.c
new file mode 100644
index 000000000000..a808eac4ecd6
--- /dev/null
+++ b/arch/sh/boards/mach-cayman/led.c
@@ -0,0 +1,51 @@
+/*
+ * arch/sh/boards/cayman/led.c
+ *
+ * Copyright (C) 2002 Stuart Menefy <stuart.menefy@st.com>
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License. See linux/COPYING for more information.
+ *
+ * Flash the LEDs
+ */
+#include <asm/io.h>
+
+/*
+** It is supposed these functions to be used for a low level
+** debugging (via Cayman LEDs), hence to be available as soon
+** as possible.
+** Unfortunately Cayman LEDs relies on Cayman EPLD to be mapped
+** (this happen when IRQ are initialized... quite late).
+** These triky dependencies should be removed. Temporary, it
+** may be enough to NOP until EPLD is mapped.
+*/
+
+extern unsigned long epld_virt;
+
+#define LED_ADDR (epld_virt + 0x008)
+#define HDSP2534_ADDR (epld_virt + 0x100)
+
+void mach_led(int position, int value)
+{
+ if (!epld_virt)
+ return;
+
+ if (value)
+ ctrl_outl(0, LED_ADDR);
+ else
+ ctrl_outl(1, LED_ADDR);
+
+}
+
+void mach_alphanum(int position, unsigned char value)
+{
+ if (!epld_virt)
+ return;
+
+ ctrl_outb(value, HDSP2534_ADDR + 0xe0 + (position << 2));
+}
+
+void mach_alphanum_brightness(int setting)
+{
+ ctrl_outb(setting & 7, HDSP2534_ADDR + 0xc0);
+}
diff --git a/arch/sh/boards/mach-cayman/setup.c b/arch/sh/boards/mach-cayman/setup.c
new file mode 100644
index 000000000000..e7f9cc5f2ff1
--- /dev/null
+++ b/arch/sh/boards/mach-cayman/setup.c
@@ -0,0 +1,187 @@
+/*
+ * arch/sh/mach-cayman/setup.c
+ *
+ * SH5 Cayman support
+ *
+ * Copyright (C) 2002 David J. Mckay & Benedict Gaster
+ * Copyright (C) 2003 - 2007 Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <cpu/irq.h>
+
+/*
+ * Platform Dependent Interrupt Priorities.
+ */
+
+/* Using defaults defined in irq.h */
+#define RES NO_PRIORITY /* Disabled */
+#define IR0 IRL0_PRIORITY /* IRLs */
+#define IR1 IRL1_PRIORITY
+#define IR2 IRL2_PRIORITY
+#define IR3 IRL3_PRIORITY
+#define PCA INTA_PRIORITY /* PCI Ints */
+#define PCB INTB_PRIORITY
+#define PCC INTC_PRIORITY
+#define PCD INTD_PRIORITY
+#define SER TOP_PRIORITY
+#define ERR TOP_PRIORITY
+#define PW0 TOP_PRIORITY
+#define PW1 TOP_PRIORITY
+#define PW2 TOP_PRIORITY
+#define PW3 TOP_PRIORITY
+#define DM0 NO_PRIORITY /* DMA Ints */
+#define DM1 NO_PRIORITY
+#define DM2 NO_PRIORITY
+#define DM3 NO_PRIORITY
+#define DAE NO_PRIORITY
+#define TU0 TIMER_PRIORITY /* TMU Ints */
+#define TU1 NO_PRIORITY
+#define TU2 NO_PRIORITY
+#define TI2 NO_PRIORITY
+#define ATI NO_PRIORITY /* RTC Ints */
+#define PRI NO_PRIORITY
+#define CUI RTC_PRIORITY
+#define ERI SCIF_PRIORITY /* SCIF Ints */
+#define RXI SCIF_PRIORITY
+#define BRI SCIF_PRIORITY
+#define TXI SCIF_PRIORITY
+#define ITI TOP_PRIORITY /* WDT Ints */
+
+/* Setup for the SMSC FDC37C935 */
+#define SMSC_SUPERIO_BASE 0x04000000
+#define SMSC_CONFIG_PORT_ADDR 0x3f0
+#define SMSC_INDEX_PORT_ADDR SMSC_CONFIG_PORT_ADDR
+#define SMSC_DATA_PORT_ADDR 0x3f1
+
+#define SMSC_ENTER_CONFIG_KEY 0x55
+#define SMSC_EXIT_CONFIG_KEY 0xaa
+
+#define SMCS_LOGICAL_DEV_INDEX 0x07
+#define SMSC_DEVICE_ID_INDEX 0x20
+#define SMSC_DEVICE_REV_INDEX 0x21
+#define SMSC_ACTIVATE_INDEX 0x30
+#define SMSC_PRIMARY_BASE_INDEX 0x60
+#define SMSC_SECONDARY_BASE_INDEX 0x62
+#define SMSC_PRIMARY_INT_INDEX 0x70
+#define SMSC_SECONDARY_INT_INDEX 0x72
+
+#define SMSC_IDE1_DEVICE 1
+#define SMSC_KEYBOARD_DEVICE 7
+#define SMSC_CONFIG_REGISTERS 8
+
+#define SMSC_SUPERIO_READ_INDEXED(index) ({ \
+ outb((index), SMSC_INDEX_PORT_ADDR); \
+ inb(SMSC_DATA_PORT_ADDR); })
+#define SMSC_SUPERIO_WRITE_INDEXED(val, index) ({ \
+ outb((index), SMSC_INDEX_PORT_ADDR); \
+ outb((val), SMSC_DATA_PORT_ADDR); })
+
+#define IDE1_PRIMARY_BASE 0x01f0
+#define IDE1_SECONDARY_BASE 0x03f6
+
+unsigned long smsc_superio_virt;
+
+int platform_int_priority[NR_INTC_IRQS] = {
+ IR0, IR1, IR2, IR3, PCA, PCB, PCC, PCD, /* IRQ 0- 7 */
+ RES, RES, RES, RES, SER, ERR, PW3, PW2, /* IRQ 8-15 */
+ PW1, PW0, DM0, DM1, DM2, DM3, DAE, RES, /* IRQ 16-23 */
+ RES, RES, RES, RES, RES, RES, RES, RES, /* IRQ 24-31 */
+ TU0, TU1, TU2, TI2, ATI, PRI, CUI, ERI, /* IRQ 32-39 */
+ RXI, BRI, TXI, RES, RES, RES, RES, RES, /* IRQ 40-47 */
+ RES, RES, RES, RES, RES, RES, RES, RES, /* IRQ 48-55 */
+ RES, RES, RES, RES, RES, RES, RES, ITI, /* IRQ 56-63 */
+};
+
+static int __init smsc_superio_setup(void)
+{
+ unsigned char devid, devrev;
+
+ smsc_superio_virt = onchip_remap(SMSC_SUPERIO_BASE, 1024, "SMSC SuperIO");
+ if (!smsc_superio_virt) {
+ panic("Unable to remap SMSC SuperIO\n");
+ }
+
+ /* Initially the chip is in run state */
+ /* Put it into configuration state */
+ outb(SMSC_ENTER_CONFIG_KEY, SMSC_CONFIG_PORT_ADDR);
+ outb(SMSC_ENTER_CONFIG_KEY, SMSC_CONFIG_PORT_ADDR);
+
+ /* Read device ID info */
+ devid = SMSC_SUPERIO_READ_INDEXED(SMSC_DEVICE_ID_INDEX);
+ devrev = SMSC_SUPERIO_READ_INDEXED(SMSC_DEVICE_REV_INDEX);
+ printk("SMSC SuperIO devid %02x rev %02x\n", devid, devrev);
+
+ /* Select the keyboard device */
+ SMSC_SUPERIO_WRITE_INDEXED(SMSC_KEYBOARD_DEVICE, SMCS_LOGICAL_DEV_INDEX);
+
+ /* enable it */
+ SMSC_SUPERIO_WRITE_INDEXED(1, SMSC_ACTIVATE_INDEX);
+
+ /* Select the interrupts */
+ /* On a PC keyboard is IRQ1, mouse is IRQ12 */
+ SMSC_SUPERIO_WRITE_INDEXED(1, SMSC_PRIMARY_INT_INDEX);
+ SMSC_SUPERIO_WRITE_INDEXED(12, SMSC_SECONDARY_INT_INDEX);
+
+#ifdef CONFIG_IDE
+ /*
+ * Only IDE1 exists on the Cayman
+ */
+
+ /* Power it on */
+ SMSC_SUPERIO_WRITE_INDEXED(1 << SMSC_IDE1_DEVICE, 0x22);
+
+ SMSC_SUPERIO_WRITE_INDEXED(SMSC_IDE1_DEVICE, SMCS_LOGICAL_DEV_INDEX);
+ SMSC_SUPERIO_WRITE_INDEXED(1, SMSC_ACTIVATE_INDEX);
+
+ SMSC_SUPERIO_WRITE_INDEXED(IDE1_PRIMARY_BASE >> 8,
+ SMSC_PRIMARY_BASE_INDEX + 0);
+ SMSC_SUPERIO_WRITE_INDEXED(IDE1_PRIMARY_BASE & 0xff,
+ SMSC_PRIMARY_BASE_INDEX + 1);
+
+ SMSC_SUPERIO_WRITE_INDEXED(IDE1_SECONDARY_BASE >> 8,
+ SMSC_SECONDARY_BASE_INDEX + 0);
+ SMSC_SUPERIO_WRITE_INDEXED(IDE1_SECONDARY_BASE & 0xff,
+ SMSC_SECONDARY_BASE_INDEX + 1);
+
+ SMSC_SUPERIO_WRITE_INDEXED(14, SMSC_PRIMARY_INT_INDEX);
+
+ SMSC_SUPERIO_WRITE_INDEXED(SMSC_CONFIG_REGISTERS,
+ SMCS_LOGICAL_DEV_INDEX);
+
+ SMSC_SUPERIO_WRITE_INDEXED(0x00, 0xc2); /* GP42 = nIDE1_OE */
+ SMSC_SUPERIO_WRITE_INDEXED(0x01, 0xc5); /* GP45 = IDE1_IRQ */
+ SMSC_SUPERIO_WRITE_INDEXED(0x00, 0xc6); /* GP46 = nIOROP */
+ SMSC_SUPERIO_WRITE_INDEXED(0x00, 0xc7); /* GP47 = nIOWOP */
+#endif
+
+ /* Exit the configuration state */
+ outb(SMSC_EXIT_CONFIG_KEY, SMSC_CONFIG_PORT_ADDR);
+
+ return 0;
+}
+__initcall(smsc_superio_setup);
+
+static void __iomem *cayman_ioport_map(unsigned long port, unsigned int len)
+{
+ if (port < 0x400) {
+ extern unsigned long smsc_superio_virt;
+ return (void __iomem *)((port << 2) | smsc_superio_virt);
+ }
+
+ return (void __iomem *)port;
+}
+
+extern void init_cayman_irq(void);
+
+static struct sh_machine_vector mv_cayman __initmv = {
+ .mv_name = "Hitachi Cayman",
+ .mv_nr_irqs = 64,
+ .mv_ioport_map = cayman_ioport_map,
+ .mv_init_irq = init_cayman_irq,
+};