diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/mips/vr41xx |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/mips/vr41xx')
26 files changed, 3315 insertions, 0 deletions
diff --git a/arch/mips/vr41xx/casio-e55/Makefile b/arch/mips/vr41xx/casio-e55/Makefile new file mode 100644 index 00000000000..d4c03cc8eb0 --- /dev/null +++ b/arch/mips/vr41xx/casio-e55/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the CASIO CASSIOPEIA E-55/65 specific parts of the kernel +# + +obj-y += setup.o diff --git a/arch/mips/vr41xx/casio-e55/setup.c b/arch/mips/vr41xx/casio-e55/setup.c new file mode 100644 index 00000000000..aa8605ab76f --- /dev/null +++ b/arch/mips/vr41xx/casio-e55/setup.c @@ -0,0 +1,40 @@ +/* + * setup.c, Setup for the CASIO CASSIOPEIA E-11/15/55/65. + * + * Copyright (C) 2002-2005 Yoichi Yuasa <yuasa@hh.iij4u.or.jp> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <linux/init.h> +#include <linux/ioport.h> + +#include <asm/io.h> +#include <asm/vr41xx/e55.h> + +const char *get_system_type(void) +{ + return "CASIO CASSIOPEIA E-11/15/55/65"; +} + +static int __init casio_e55_setup(void) +{ + set_io_port_base(IO_PORT_BASE); + ioport_resource.start = IO_PORT_RESOURCE_START; + ioport_resource.end = IO_PORT_RESOURCE_END; + + return 0; +} + +arch_initcall(casio_e55_setup); diff --git a/arch/mips/vr41xx/common/Makefile b/arch/mips/vr41xx/common/Makefile new file mode 100644 index 00000000000..92c11e9bbb3 --- /dev/null +++ b/arch/mips/vr41xx/common/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for common code of the NEC VR4100 series. +# + +obj-y += bcu.o cmu.o giu.o icu.o init.o int-handler.o pmu.o +obj-$(CONFIG_VRC4173) += vrc4173.o + +EXTRA_AFLAGS := $(CFLAGS) diff --git a/arch/mips/vr41xx/common/bcu.c b/arch/mips/vr41xx/common/bcu.c new file mode 100644 index 00000000000..cdfa4273a1c --- /dev/null +++ b/arch/mips/vr41xx/common/bcu.c @@ -0,0 +1,222 @@ +/* + * bcu.c, Bus Control Unit routines for the NEC VR4100 series. + * + * Copyright (C) 2002 MontaVista Software Inc. + * Author: Yoichi Yuasa <yyuasa@mvista.com, or source@mvista.com> + * Copyright (C) 2003-2005 Yoichi Yuasa <yuasa@hh.iij4u.or.jp> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* + * Changes: + * MontaVista Software Inc. <yyuasa@mvista.com> or <source@mvista.com> + * - New creation, NEC VR4122 and VR4131 are supported. + * - Added support for NEC VR4111 and VR4121. + * + * Yoichi Yuasa <yuasa@hh.iij4u.or.jp> + * - Added support for NEC VR4133. + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/smp.h> +#include <linux/types.h> + +#include <asm/cpu.h> +#include <asm/io.h> + +#define CLKSPEEDREG_TYPE1 (void __iomem *)KSEG1ADDR(0x0b000014) +#define CLKSPEEDREG_TYPE2 (void __iomem *)KSEG1ADDR(0x0f000014) + #define CLKSP(x) ((x) & 0x001f) + #define CLKSP_VR4133(x) ((x) & 0x0007) + + #define DIV2B 0x8000 + #define DIV3B 0x4000 + #define DIV4B 0x2000 + + #define DIVT(x) (((x) & 0xf000) >> 12) + #define DIVVT(x) (((x) & 0x0f00) >> 8) + + #define TDIVMODE(x) (2 << (((x) & 0x1000) >> 12)) + #define VTDIVMODE(x) (((x) & 0x0700) >> 8) + +static unsigned long vr41xx_vtclock; +static unsigned long vr41xx_tclock; + +unsigned long vr41xx_get_vtclock_frequency(void) +{ + return vr41xx_vtclock; +} + +EXPORT_SYMBOL_GPL(vr41xx_get_vtclock_frequency); + +unsigned long vr41xx_get_tclock_frequency(void) +{ + return vr41xx_tclock; +} + +EXPORT_SYMBOL_GPL(vr41xx_get_tclock_frequency); + +static inline uint16_t read_clkspeed(void) +{ + switch (current_cpu_data.cputype) { + case CPU_VR4111: + case CPU_VR4121: return readw(CLKSPEEDREG_TYPE1); + case CPU_VR4122: + case CPU_VR4131: + case CPU_VR4133: return readw(CLKSPEEDREG_TYPE2); + default: + printk(KERN_INFO "Unexpected CPU of NEC VR4100 series\n"); + break; + } + + return 0; +} + +static inline unsigned long calculate_pclock(uint16_t clkspeed) +{ + unsigned long pclock = 0; + + switch (current_cpu_data.cputype) { + case CPU_VR4111: + case CPU_VR4121: + pclock = 18432000 * 64; + pclock /= CLKSP(clkspeed); + break; + case CPU_VR4122: + pclock = 18432000 * 98; + pclock /= CLKSP(clkspeed); + break; + case CPU_VR4131: + pclock = 18432000 * 108; + pclock /= CLKSP(clkspeed); + break; + case CPU_VR4133: + switch (CLKSP_VR4133(clkspeed)) { + case 0: + pclock = 133000000; + break; + case 1: + pclock = 149000000; + break; + case 2: + pclock = 165900000; + break; + case 3: + pclock = 199100000; + break; + case 4: + pclock = 265900000; + break; + default: + printk(KERN_INFO "Unknown PClock speed for NEC VR4133\n"); + break; + } + break; + default: + printk(KERN_INFO "Unexpected CPU of NEC VR4100 series\n"); + break; + } + + printk(KERN_INFO "PClock: %ldHz\n", pclock); + + return pclock; +} + +static inline unsigned long calculate_vtclock(uint16_t clkspeed, unsigned long pclock) +{ + unsigned long vtclock = 0; + + switch (current_cpu_data.cputype) { + case CPU_VR4111: + /* The NEC VR4111 doesn't have the VTClock. */ + break; + case CPU_VR4121: + vtclock = pclock; + /* DIVVT == 9 Divide by 1.5 . VTClock = (PClock * 6) / 9 */ + if (DIVVT(clkspeed) == 9) + vtclock = pclock * 6; + /* DIVVT == 10 Divide by 2.5 . VTClock = (PClock * 4) / 10 */ + else if (DIVVT(clkspeed) == 10) + vtclock = pclock * 4; + vtclock /= DIVVT(clkspeed); + printk(KERN_INFO "VTClock: %ldHz\n", vtclock); + break; + case CPU_VR4122: + if(VTDIVMODE(clkspeed) == 7) + vtclock = pclock / 1; + else if(VTDIVMODE(clkspeed) == 1) + vtclock = pclock / 2; + else + vtclock = pclock / VTDIVMODE(clkspeed); + printk(KERN_INFO "VTClock: %ldHz\n", vtclock); + break; + case CPU_VR4131: + case CPU_VR4133: + vtclock = pclock / VTDIVMODE(clkspeed); + printk(KERN_INFO "VTClock: %ldHz\n", vtclock); + break; + default: + printk(KERN_INFO "Unexpected CPU of NEC VR4100 series\n"); + break; + } + + return vtclock; +} + +static inline unsigned long calculate_tclock(uint16_t clkspeed, unsigned long pclock, + unsigned long vtclock) +{ + unsigned long tclock = 0; + + switch (current_cpu_data.cputype) { + case CPU_VR4111: + if (!(clkspeed & DIV2B)) + tclock = pclock / 2; + else if (!(clkspeed & DIV3B)) + tclock = pclock / 3; + else if (!(clkspeed & DIV4B)) + tclock = pclock / 4; + break; + case CPU_VR4121: + tclock = pclock / DIVT(clkspeed); + break; + case CPU_VR4122: + case CPU_VR4131: + case CPU_VR4133: + tclock = vtclock / TDIVMODE(clkspeed); + break; + default: + printk(KERN_INFO "Unexpected CPU of NEC VR4100 series\n"); + break; + } + + printk(KERN_INFO "TClock: %ldHz\n", tclock); + + return tclock; +} + +void vr41xx_calculate_clock_frequency(void) +{ + unsigned long pclock; + uint16_t clkspeed; + + clkspeed = read_clkspeed(); + + pclock = calculate_pclock(clkspeed); + vr41xx_vtclock = calculate_vtclock(clkspeed, pclock); + vr41xx_tclock = calculate_tclock(clkspeed, pclock, vr41xx_vtclock); +} + +EXPORT_SYMBOL_GPL(vr41xx_calculate_clock_frequency); diff --git a/arch/mips/vr41xx/common/cmu.c b/arch/mips/vr41xx/common/cmu.c new file mode 100644 index 00000000000..fcd3cb8cdd9 --- /dev/null +++ b/arch/mips/vr41xx/common/cmu.c @@ -0,0 +1,257 @@ +/* + * cmu.c, Clock Mask Unit routines for the NEC VR4100 series. + * + * Copyright (C) 2001-2002 MontaVista Software Inc. + * Author: Yoichi Yuasa <yyuasa@mvista.com or source@mvista.com> + * Copuright (C) 2003-2005 Yoichi Yuasa <yuasa@hh.iij4u.or.jp> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* + * Changes: + * MontaVista Software Inc. <yyuasa@mvista.com> or <source@mvista.com> + * - New creation, NEC VR4122 and VR4131 are supported. + * - Added support for NEC VR4111 and VR4121. + * + * Yoichi Yuasa <yuasa@hh.iij4u.or.jp> + * - Added support for NEC VR4133. + */ +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/module.h> +#include <linux/smp.h> +#include <linux/spinlock.h> +#include <linux/types.h> + +#include <asm/cpu.h> +#include <asm/io.h> +#include <asm/vr41xx/vr41xx.h> + +#define CMU_TYPE1_BASE 0x0b000060UL +#define CMU_TYPE1_SIZE 0x4 + +#define CMU_TYPE2_BASE 0x0f000060UL +#define CMU_TYPE2_SIZE 0x4 + +#define CMU_TYPE3_BASE 0x0f000060UL +#define CMU_TYPE3_SIZE 0x8 + +#define CMUCLKMSK 0x0 + #define MSKPIU 0x0001 + #define MSKSIU 0x0002 + #define MSKAIU 0x0004 + #define MSKKIU 0x0008 + #define MSKFIR 0x0010 + #define MSKDSIU 0x0820 + #define MSKCSI 0x0040 + #define MSKPCIU 0x0080 + #define MSKSSIU 0x0100 + #define MSKSHSP 0x0200 + #define MSKFFIR 0x0400 + #define MSKSCSI 0x1000 + #define MSKPPCIU 0x2000 +#define CMUCLKMSK2 0x4 + #define MSKCEU 0x0001 + #define MSKMAC0 0x0002 + #define MSKMAC1 0x0004 + +static void __iomem *cmu_base; +static uint16_t cmuclkmsk, cmuclkmsk2; +static spinlock_t cmu_lock; + +#define cmu_read(offset) readw(cmu_base + (offset)) +#define cmu_write(offset, value) writew((value), cmu_base + (offset)) + +void vr41xx_supply_clock(vr41xx_clock_t clock) +{ + spin_lock_irq(&cmu_lock); + + switch (clock) { + case PIU_CLOCK: + cmuclkmsk |= MSKPIU; + break; + case SIU_CLOCK: + cmuclkmsk |= MSKSIU | MSKSSIU; + break; + case AIU_CLOCK: + cmuclkmsk |= MSKAIU; + break; + case KIU_CLOCK: + cmuclkmsk |= MSKKIU; + break; + case FIR_CLOCK: + cmuclkmsk |= MSKFIR | MSKFFIR; + break; + case DSIU_CLOCK: + if (current_cpu_data.cputype == CPU_VR4111 || + current_cpu_data.cputype == CPU_VR4121) + cmuclkmsk |= MSKDSIU; + else + cmuclkmsk |= MSKSIU | MSKDSIU; + break; + case CSI_CLOCK: + cmuclkmsk |= MSKCSI | MSKSCSI; + break; + case PCIU_CLOCK: + cmuclkmsk |= MSKPCIU; + break; + case HSP_CLOCK: + cmuclkmsk |= MSKSHSP; + break; + case PCI_CLOCK: + cmuclkmsk |= MSKPPCIU; + break; + case CEU_CLOCK: + cmuclkmsk2 |= MSKCEU; + break; + case ETHER0_CLOCK: + cmuclkmsk2 |= MSKMAC0; + break; + case ETHER1_CLOCK: + cmuclkmsk2 |= MSKMAC1; + break; + default: + break; + } + + if (clock == CEU_CLOCK || clock == ETHER0_CLOCK || + clock == ETHER1_CLOCK) + cmu_write(CMUCLKMSK2, cmuclkmsk2); + else + cmu_write(CMUCLKMSK, cmuclkmsk); + + spin_unlock_irq(&cmu_lock); +} + +EXPORT_SYMBOL_GPL(vr41xx_supply_clock); + +void vr41xx_mask_clock(vr41xx_clock_t clock) +{ + spin_lock_irq(&cmu_lock); + + switch (clock) { + case PIU_CLOCK: + cmuclkmsk &= ~MSKPIU; + break; + case SIU_CLOCK: + if (current_cpu_data.cputype == CPU_VR4111 || + current_cpu_data.cputype == CPU_VR4121) { + cmuclkmsk &= ~(MSKSIU | MSKSSIU); + } else { + if (cmuclkmsk & MSKDSIU) + cmuclkmsk &= ~MSKSSIU; + else + cmuclkmsk &= ~(MSKSIU | MSKSSIU); + } + break; + case AIU_CLOCK: + cmuclkmsk &= ~MSKAIU; + break; + case KIU_CLOCK: + cmuclkmsk &= ~MSKKIU; + break; + case FIR_CLOCK: + cmuclkmsk &= ~(MSKFIR | MSKFFIR); + break; + case DSIU_CLOCK: + if (current_cpu_data.cputype == CPU_VR4111 || + current_cpu_data.cputype == CPU_VR4121) { + cmuclkmsk &= ~MSKDSIU; + } else { + if (cmuclkmsk & MSKSSIU) + cmuclkmsk &= ~MSKDSIU; + else + cmuclkmsk &= ~(MSKSIU | MSKDSIU); + } + break; + case CSI_CLOCK: + cmuclkmsk &= ~(MSKCSI | MSKSCSI); + break; + case PCIU_CLOCK: + cmuclkmsk &= ~MSKPCIU; + break; + case HSP_CLOCK: + cmuclkmsk &= ~MSKSHSP; + break; + case PCI_CLOCK: + cmuclkmsk &= ~MSKPPCIU; + break; + case CEU_CLOCK: + cmuclkmsk2 &= ~MSKCEU; + break; + case ETHER0_CLOCK: + cmuclkmsk2 &= ~MSKMAC0; + break; + case ETHER1_CLOCK: + cmuclkmsk2 &= ~MSKMAC1; + break; + default: + break; + } + + if (clock == CEU_CLOCK || clock == ETHER0_CLOCK || + clock == ETHER1_CLOCK) + cmu_write(CMUCLKMSK2, cmuclkmsk2); + else + cmu_write(CMUCLKMSK, cmuclkmsk); + + spin_unlock_irq(&cmu_lock); +} + +EXPORT_SYMBOL_GPL(vr41xx_mask_clock); + +static int __init vr41xx_cmu_init(void) +{ + unsigned long start, size; + + switch (current_cpu_data.cputype) { + case CPU_VR4111: + case CPU_VR4121: + start = CMU_TYPE1_BASE; + size = CMU_TYPE1_SIZE; + break; + case CPU_VR4122: + case CPU_VR4131: + start = CMU_TYPE2_BASE; + size = CMU_TYPE2_SIZE; + break; + case CPU_VR4133: + start = CMU_TYPE3_BASE; + size = CMU_TYPE3_SIZE; + break; + default: + panic("Unexpected CPU of NEC VR4100 series"); + break; + } + + if (request_mem_region(start, size, "CMU") == NULL) + return -EBUSY; + + cmu_base = ioremap(start, size); + if (cmu_base == NULL) { + release_mem_region(start, size); + return -EBUSY; + } + + cmuclkmsk = cmu_read(CMUCLKMSK); + if (current_cpu_data.cputype == CPU_VR4133) + cmuclkmsk2 = cmu_read(CMUCLKMSK2); + + spin_lock_init(&cmu_lock); + + return 0; +} + +core_initcall(vr41xx_cmu_init); diff --git a/arch/mips/vr41xx/common/giu.c b/arch/mips/vr41xx/common/giu.c new file mode 100644 index 00000000000..9c6b21a79e8 --- /dev/null +++ b/arch/mips/vr41xx/common/giu.c @@ -0,0 +1,455 @@ +/* + * giu.c, General-purpose I/O Unit Interrupt routines for NEC VR4100 series. + * + * Copyright (C) 2002 MontaVista Software Inc. + * Author: Yoichi Yuasa <yyuasa@mvista.com or source@mvista.com> + * Copyright (C) 2003-2004 Yoichi Yuasa <yuasa@hh.iij4u.or.jp> + * Copyright (C) 2005 Ralf Baechle (ralf@linux-mips.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* + * Changes: + * MontaVista Software Inc. <yyuasa@mvista.com> or <source@mvista.com> + * - New creation, NEC VR4111, VR4121, VR4122 and VR4131 are supported. + * + * Yoichi Yuasa <yuasa@hh.iij4u.or.jp> + * - Added support for NEC VR4133. + * - Removed board_irq_init. + */ +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/smp.h> +#include <linux/types.h> + +#include <asm/cpu.h> +#include <asm/io.h> +#include <asm/vr41xx/vr41xx.h> + +#define GIUIOSELL_TYPE1 KSEG1ADDR(0x0b000100) +#define GIUIOSELL_TYPE2 KSEG1ADDR(0x0f000140) + +#define GIUIOSELL 0x00 +#define GIUIOSELH 0x02 +#define GIUINTSTATL 0x08 +#define GIUINTSTATH 0x0a +#define GIUINTENL 0x0c +#define GIUINTENH 0x0e +#define GIUINTTYPL 0x10 +#define GIUINTTYPH 0x12 +#define GIUINTALSELL 0x14 +#define GIUINTALSELH 0x16 +#define GIUINTHTSELL 0x18 +#define GIUINTHTSELH 0x1a +#define GIUFEDGEINHL 0x20 +#define GIUFEDGEINHH 0x22 +#define GIUREDGEINHL 0x24 +#define GIUREDGEINHH 0x26 + +static uint32_t giu_base; + +static struct irqaction giu_cascade = { + .handler = no_action, + .mask = CPU_MASK_NONE, + .name = "cascade", +}; + +#define read_giuint(offset) readw(giu_base + (offset)) +#define write_giuint(val, offset) writew((val), giu_base + (offset)) + +#define GIUINT_HIGH_OFFSET 16 + +static inline uint16_t set_giuint(uint8_t offset, uint16_t set) +{ + uint16_t res; + + res = read_giuint(offset); + res |= set; + write_giuint(res, offset); + + return res; +} + +static inline uint16_t clear_giuint(uint8_t offset, uint16_t clear) +{ + uint16_t res; + + res = read_giuint(offset); + res &= ~clear; + write_giuint(res, offset); + + return res; +} + +static unsigned int startup_giuint_low_irq(unsigned int irq) +{ + unsigned int pin; + + pin = GIU_IRQ_TO_PIN(irq); + write_giuint((uint16_t)1 << pin, GIUINTSTATL); + set_giuint(GIUINTENL, (uint16_t)1 << pin); + + return 0; +} + +static void shutdown_giuint_low_irq(unsigned int irq) +{ + clear_giuint(GIUINTENL, (uint16_t)1 << GIU_IRQ_TO_PIN(irq)); +} + +static void enable_giuint_low_irq(unsigned int irq) +{ + set_giuint(GIUINTENL, (uint16_t)1 << GIU_IRQ_TO_PIN(irq)); +} + +#define disable_giuint_low_irq shutdown_giuint_low_irq + +static void ack_giuint_low_irq(unsigned int irq) +{ + unsigned int pin; + + pin = GIU_IRQ_TO_PIN(irq); + clear_giuint(GIUINTENL, (uint16_t)1 << pin); + write_giuint((uint16_t)1 << pin, GIUINTSTATL); +} + +static void end_giuint_low_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) + set_giuint(GIUINTENL, (uint16_t)1 << GIU_IRQ_TO_PIN(irq)); +} + +static struct hw_interrupt_type giuint_low_irq_type = { + .typename = "GIUINTL", + .startup = startup_giuint_low_irq, + .shutdown = shutdown_giuint_low_irq, + .enable = enable_giuint_low_irq, + .disable = disable_giuint_low_irq, + .ack = ack_giuint_low_irq, + .end = end_giuint_low_irq, +}; + +static unsigned int startup_giuint_high_irq(unsigned int irq) +{ + unsigned int pin; + + pin = GIU_IRQ_TO_PIN(irq - GIUINT_HIGH_OFFSET); + write_giuint((uint16_t)1 << pin, GIUINTSTATH); + set_giuint(GIUINTENH, (uint16_t)1 << pin); + + return 0; +} + +static void shutdown_giuint_high_irq(unsigned int irq) +{ + clear_giuint(GIUINTENH, (uint16_t)1 << GIU_IRQ_TO_PIN(irq - GIUINT_HIGH_OFFSET)); +} + +static void enable_giuint_high_irq(unsigned int irq) +{ + set_giuint(GIUINTENH, (uint16_t)1 << GIU_IRQ_TO_PIN(irq - GIUINT_HIGH_OFFSET)); +} + +#define disable_giuint_high_irq shutdown_giuint_high_irq + +static void ack_giuint_high_irq(unsigned int irq) +{ + unsigned int pin; + + pin = GIU_IRQ_TO_PIN(irq - GIUINT_HIGH_OFFSET); + clear_giuint(GIUINTENH, (uint16_t)1 << pin); + write_giuint((uint16_t)1 << pin, GIUINTSTATH); +} + +static void end_giuint_high_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) + set_giuint(GIUINTENH, (uint16_t)1 << GIU_IRQ_TO_PIN(irq - GIUINT_HIGH_OFFSET)); +} + +static struct hw_interrupt_type giuint_high_irq_type = { + .typename = "GIUINTH", + .startup = startup_giuint_high_irq, + .shutdown = shutdown_giuint_high_irq, + .enable = enable_giuint_high_irq, + .disable = disable_giuint_high_irq, + .ack = ack_giuint_high_irq, + .end = end_giuint_high_irq, +}; + +void __init init_vr41xx_giuint_irq(void) +{ + int i; + + for (i = GIU_IRQ_BASE; i <= GIU_IRQ_LAST; i++) { + if (i < (GIU_IRQ_BASE + GIUINT_HIGH_OFFSET)) + irq_desc[i].handler = &giuint_low_irq_type; + else + irq_desc[i].handler = &giuint_high_irq_type; + } + + setup_irq(GIUINT_CASCADE_IRQ, &giu_cascade); +} + +void vr41xx_set_irq_trigger(int pin, int trigger, int hold) +{ + uint16_t mask; + + if (pin < GIUINT_HIGH_OFFSET) { + mask = (uint16_t)1 << pin; + if (trigger != TRIGGER_LEVEL) { + set_giuint(GIUINTTYPL, mask); + if (hold == SIGNAL_HOLD) + set_giuint(GIUINTHTSELL, mask); + else + clear_giuint(GIUINTHTSELL, mask); + if (current_cpu_data.cputype == CPU_VR4133) { + switch (trigger) { + case TRIGGER_EDGE_FALLING: + set_giuint(GIUFEDGEINHL, mask); + clear_giuint(GIUREDGEINHL, mask); + break; + case TRIGGER_EDGE_RISING: + clear_giuint(GIUFEDGEINHL, mask); + set_giuint(GIUREDGEINHL, mask); + break; + default: + set_giuint(GIUFEDGEINHL, mask); + set_giuint(GIUREDGEINHL, mask); + break; + } + } + } else { + clear_giuint(GIUINTTYPL, mask); + clear_giuint(GIUINTHTSELL, mask); + } + write_giuint(mask, GIUINTSTATL); + } else { + mask = (uint16_t)1 << (pin - GIUINT_HIGH_OFFSET); + if (trigger != TRIGGER_LEVEL) { + set_giuint(GIUINTTYPH, mask); + if (hold == SIGNAL_HOLD) + set_giuint(GIUINTHTSELH, mask); + else + clear_giuint(GIUINTHTSELH, mask); + if (current_cpu_data.cputype == CPU_VR4133) { + switch (trigger) { + case TRIGGER_EDGE_FALLING: + set_giuint(GIUFEDGEINHH, mask); + clear_giuint(GIUREDGEINHH, mask); + break; + case TRIGGER_EDGE_RISING: + clear_giuint(GIUFEDGEINHH, mask); + set_giuint(GIUREDGEINHH, mask); + break; + default: + set_giuint(GIUFEDGEINHH, mask); + set_giuint(GIUREDGEINHH, mask); + break; + } + } + } else { + clear_giuint(GIUINTTYPH, mask); + clear_giuint(GIUINTHTSELH, mask); + } + write_giuint(mask, GIUINTSTATH); + } +} + +EXPORT_SYMBOL(vr41xx_set_irq_trigger); + +void vr41xx_set_irq_level(int pin, int level) +{ + uint16_t mask; + + if (pin < GIUINT_HIGH_OFFSET) { + mask = (uint16_t)1 << pin; + if (level == LEVEL_HIGH) + set_giuint(GIUINTALSELL, mask); + else + clear_giuint(GIUINTALSELL, mask); + write_giuint(mask, GIUINTSTATL); + } else { + mask = (uint16_t)1 << (pin - GIUINT_HIGH_OFFSET); + if (level == LEVEL_HIGH) + set_giuint(GIUINTALSELH, mask); + else + clear_giuint(GIUINTALSELH, mask); + write_giuint(mask, GIUINTSTATH); + } +} + +EXPORT_SYMBOL(vr41xx_set_irq_level); + +#define GIUINT_NR_IRQS 32 + +enum { + GIUINT_NO_CASCADE, + GIUINT_CASCADE +}; + +struct vr41xx_giuint_cascade { + unsigned int flag; + int (*get_irq_number)(int irq); +}; + +static struct vr41xx_giuint_cascade giuint_cascade[GIUINT_NR_IRQS]; + +static int no_irq_number(int irq) +{ + return -EINVAL; +} + +int vr41xx_cascade_irq(unsigned int irq, int (*get_irq_number)(int irq)) +{ + unsigned int pin; + int retval; + + if (irq < GIU_IRQ(0) || irq > GIU_IRQ(31)) + return -EINVAL; + + if(!get_irq_number) + return -EINVAL; + + pin = GIU_IRQ_TO_PIN(irq); + giuint_cascade[pin].flag = GIUINT_CASCADE; + giuint_cascade[pin].get_irq_number = get_irq_number; + + retval = setup_irq(irq, &giu_cascade); + if (retval != 0) { + giuint_cascade[pin].flag = GIUINT_NO_CASCADE; + giuint_cascade[pin].get_irq_number = no_irq_number; + } + + return retval; +} + +EXPORT_SYMBOL(vr41xx_cascade_irq); + +static inline int get_irq_pin_number(void) +{ + uint16_t pendl, pendh, maskl, maskh; + int i; + + pendl = read_giuint(GIUINTSTATL); + pendh = read_giuint(GIUINTSTATH); + maskl = read_giuint(GIUINTENL); + maskh = read_giuint(GIUINTENH); + + maskl &= pendl; + maskh &= pendh; + + if (maskl) { + for (i = 0; i < 16; i++) { + if (maskl & ((uint16_t)1 << i)) + return i; + } + } else if (maskh) { + for (i = 0; i < 16; i++) { + if (maskh & ((uint16_t)1 << i)) + return i + GIUINT_HIGH_OFFSET; + } + } + + printk(KERN_ERR "spurious GIU interrupt: %04x(%04x),%04x(%04x)\n", + maskl, pendl, maskh, pendh); + + atomic_inc(&irq_err_count); + + return -1; +} + +static inline void ack_giuint_irq(int pin) +{ + if (pin < GIUINT_HIGH_OFFSET) { + clear_giuint(GIUINTENL, (uint16_t)1 << pin); + write_giuint((uint16_t)1 << pin, GIUINTSTATL); + } else { + pin -= GIUINT_HIGH_OFFSET; + clear_giuint(GIUINTENH, (uint16_t)1 << pin); + write_giuint((uint16_t)1 << pin, GIUINTSTATH); + } +} + +static inline void end_giuint_irq(int pin) +{ + if (pin < GIUINT_HIGH_OFFSET) + set_giuint(GIUINTENL, (uint16_t)1 << pin); + else + set_giuint(GIUINTENH, (uint16_t)1 << (pin - GIUINT_HIGH_OFFSET)); +} + +void giuint_irq_dispatch(struct pt_regs *regs) +{ + struct vr41xx_giuint_cascade *cascade; + unsigned int giuint_irq; + int pin; + + pin = get_irq_pin_number(); + if (pin < 0) + return; + + disable_irq(GIUINT_CASCADE_IRQ); + + cascade = &giuint_cascade[pin]; + giuint_irq = GIU_IRQ(pin); + if (cascade->flag == GIUINT_CASCADE) { |