diff options
Diffstat (limited to 'arch/mips/vr41xx/common')
-rw-r--r-- | arch/mips/vr41xx/common/Makefile | 5 | ||||
-rw-r--r-- | arch/mips/vr41xx/common/bcu.c | 223 | ||||
-rw-r--r-- | arch/mips/vr41xx/common/cmu.c | 257 | ||||
-rw-r--r-- | arch/mips/vr41xx/common/giu.c | 123 | ||||
-rw-r--r-- | arch/mips/vr41xx/common/icu.c | 729 | ||||
-rw-r--r-- | arch/mips/vr41xx/common/init.c | 77 | ||||
-rw-r--r-- | arch/mips/vr41xx/common/irq.c | 124 | ||||
-rw-r--r-- | arch/mips/vr41xx/common/pmu.c | 136 | ||||
-rw-r--r-- | arch/mips/vr41xx/common/rtc.c | 118 | ||||
-rw-r--r-- | arch/mips/vr41xx/common/siu.c | 155 | ||||
-rw-r--r-- | arch/mips/vr41xx/common/type.c | 24 |
11 files changed, 1971 insertions, 0 deletions
diff --git a/arch/mips/vr41xx/common/Makefile b/arch/mips/vr41xx/common/Makefile new file mode 100644 index 000000000..d0d84ec8d --- /dev/null +++ b/arch/mips/vr41xx/common/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for common code of the NEC VR4100 series. +# + +obj-y += bcu.o cmu.o giu.o icu.o init.o irq.o pmu.o rtc.o siu.o type.o diff --git a/arch/mips/vr41xx/common/bcu.c b/arch/mips/vr41xx/common/bcu.c new file mode 100644 index 000000000..829067222 --- /dev/null +++ b/arch/mips/vr41xx/common/bcu.c @@ -0,0 +1,223 @@ +/* + * bcu.c, Bus Control Unit routines for the NEC VR4100 series. + * + * Copyright (C) 2002 MontaVista Software Inc. + * Author: Yoichi Yuasa <source@mvista.com> + * Copyright (C) 2003-2005 Yoichi Yuasa <yuasa@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. <source@mvista.com> + * - New creation, NEC VR4122 and VR4131 are supported. + * - Added support for NEC VR4111 and VR4121. + * + * Yoichi Yuasa <yuasa@linux-mips.org> + * - Added support for NEC VR4133. + */ +#include <linux/export.h> +#include <linux/kernel.h> +#include <linux/smp.h> +#include <linux/types.h> + +#include <asm/cpu-type.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_type()) { + 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_type()) { + 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_type()) { + 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_type()) { + 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 000000000..1534b354d --- /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 <source@mvista.com> + * Copyright (C) 2003-2005 Yoichi Yuasa <yuasa@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. <source@mvista.com> + * - New creation, NEC VR4122 and VR4131 are supported. + * - Added support for NEC VR4111 and VR4121. + * + * Yoichi Yuasa <yuasa@linux-mips.org> + * - Added support for NEC VR4133. + */ +#include <linux/export.h> +#include <linux/init.h> +#include <linux/ioport.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 DEFINE_SPINLOCK(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_type() == CPU_VR4111 || + current_cpu_type() == 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_type() == CPU_VR4111 || + current_cpu_type() == 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_type() == CPU_VR4111 || + current_cpu_type() == 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_type()) { + 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_type() == 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 000000000..32cc8d66b --- /dev/null +++ b/arch/mips/vr41xx/common/giu.c @@ -0,0 +1,123 @@ +/* + * NEC VR4100 series GIU platform device. + * + * Copyright (C) 2007 Yoichi Yuasa <yuasa@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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/smp.h> +#include <linux/ioport.h> +#include <linux/platform_device.h> + +#include <asm/cpu.h> +#include <asm/vr41xx/giu.h> +#include <asm/vr41xx/irq.h> + +static struct resource giu_50pins_pullupdown_resource[] __initdata = { + { + .start = 0x0b000100, + .end = 0x0b00011f, + .flags = IORESOURCE_MEM, + }, + { + .start = 0x0b0002e0, + .end = 0x0b0002e3, + .flags = IORESOURCE_MEM, + }, + { + .start = GIUINT_IRQ, + .end = GIUINT_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource giu_36pins_resource[] __initdata = { + { + .start = 0x0f000140, + .end = 0x0f00015f, + .flags = IORESOURCE_MEM, + }, + { + .start = GIUINT_IRQ, + .end = GIUINT_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource giu_48pins_resource[] __initdata = { + { + .start = 0x0f000140, + .end = 0x0f000167, + .flags = IORESOURCE_MEM, + }, + { + .start = GIUINT_IRQ, + .end = GIUINT_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static int __init vr41xx_giu_add(void) +{ + struct platform_device *pdev; + struct resource *res; + unsigned int num; + int retval; + + pdev = platform_device_alloc("GIU", -1); + if (!pdev) + return -ENOMEM; + + switch (current_cpu_type()) { + case CPU_VR4111: + case CPU_VR4121: + pdev->id = GPIO_50PINS_PULLUPDOWN; + res = giu_50pins_pullupdown_resource; + num = ARRAY_SIZE(giu_50pins_pullupdown_resource); + break; + case CPU_VR4122: + case CPU_VR4131: + pdev->id = GPIO_36PINS; + res = giu_36pins_resource; + num = ARRAY_SIZE(giu_36pins_resource); + break; + case CPU_VR4133: + pdev->id = GPIO_48PINS_EDGE_SELECT; + res = giu_48pins_resource; + num = ARRAY_SIZE(giu_48pins_resource); + break; + default: + retval = -ENODEV; + goto err_free_device; + } + + retval = platform_device_add_resources(pdev, res, num); + if (retval) + goto err_free_device; + + retval = platform_device_add(pdev); + if (retval) + goto err_free_device; + + return 0; + +err_free_device: + platform_device_put(pdev); + + return retval; +} +device_initcall(vr41xx_giu_add); diff --git a/arch/mips/vr41xx/common/icu.c b/arch/mips/vr41xx/common/icu.c new file mode 100644 index 000000000..745b7b436 --- /dev/null +++ b/arch/mips/vr41xx/common/icu.c @@ -0,0 +1,729 @@ +/* + * icu.c, Interrupt Control Unit routines for the NEC VR4100 series. + * + * Copyright (C) 2001-2002 MontaVista Software Inc. + * Author: Yoichi Yuasa <source@mvista.com> + * Copyright (C) 2003-2006 Yoichi Yuasa <yuasa@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. <source@mvista.com> + * - New creation, NEC VR4122 and VR4131 are supported. + * - Added support for NEC VR4111 and VR4121. + * + * Yoichi Yuasa <yuasa@linux-mips.org> + * - Coped with INTASSIGN of NEC VR4133. + */ +#include <linux/errno.h> +#include <linux/export.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/irq.h> +#include <linux/smp.h> +#include <linux/types.h> + +#include <asm/cpu.h> +#include <asm/io.h> +#include <asm/vr41xx/irq.h> +#include <asm/vr41xx/vr41xx.h> + +static void __iomem *icu1_base; +static void __iomem *icu2_base; + +static unsigned char sysint1_assign[16] = { + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char sysint2_assign[16] = { + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +#define ICU1_TYPE1_BASE 0x0b000080UL +#define ICU2_TYPE1_BASE 0x0b000200UL + +#define ICU1_TYPE2_BASE 0x0f000080UL +#define ICU2_TYPE2_BASE 0x0f0000a0UL + +#define ICU1_SIZE 0x20 +#define ICU2_SIZE 0x1c + +#define SYSINT1REG 0x00 +#define PIUINTREG 0x02 +#define INTASSIGN0 0x04 +#define INTASSIGN1 0x06 +#define GIUINTLREG 0x08 +#define DSIUINTREG 0x0a +#define MSYSINT1REG 0x0c +#define MPIUINTREG 0x0e +#define MAIUINTREG 0x10 +#define MKIUINTREG 0x12 +#define MMACINTREG 0x12 +#define MGIUINTLREG 0x14 +#define MDSIUINTREG 0x16 +#define NMIREG 0x18 +#define SOFTREG 0x1a +#define INTASSIGN2 0x1c +#define INTASSIGN3 0x1e + +#define SYSINT2REG 0x00 +#define GIUINTHREG 0x02 +#define FIRINTREG 0x04 +#define MSYSINT2REG 0x06 +#define MGIUINTHREG 0x08 +#define MFIRINTREG 0x0a +#define PCIINTREG 0x0c + #define PCIINT0 0x0001 +#define SCUINTREG 0x0e + #define SCUINT0 0x0001 +#define CSIINTREG 0x10 +#define MPCIINTREG 0x12 +#define MSCUINTREG 0x14 +#define MCSIINTREG 0x16 +#define BCUINTREG 0x18 + #define BCUINTR 0x0001 +#define MBCUINTREG 0x1a + +#define SYSINT1_IRQ_TO_PIN(x) ((x) - SYSINT1_IRQ_BASE) /* Pin 0-15 */ +#define SYSINT2_IRQ_TO_PIN(x) ((x) - SYSINT2_IRQ_BASE) /* Pin 0-15 */ + +#define INT_TO_IRQ(x) ((x) + 2) /* Int0-4 -> IRQ2-6 */ + +#define icu1_read(offset) readw(icu1_base + (offset)) +#define icu1_write(offset, value) writew((value), icu1_base + (offset)) + +#define icu2_read(offset) readw(icu2_base + (offset)) +#define icu2_write(offset, value) writew((value), icu2_base + (offset)) + +#define INTASSIGN_MAX 4 +#define INTASSIGN_MASK 0x0007 + +static inline uint16_t icu1_set(uint8_t offset, uint16_t set) +{ + uint16_t data; + + data = icu1_read(offset); + data |= set; + icu1_write(offset, data); + + return data; +} + +static inline uint16_t icu1_clear(uint8_t offset, uint16_t clear) +{ + uint16_t data; + + data = icu1_read(offset); + data &= ~clear; + icu1_write(offset, data); + + return data; +} + +static inline uint16_t icu2_set(uint8_t offset, uint16_t set) +{ + uint16_t data; + + data = icu2_read(offset); + data |= set; + icu2_write(offset, data); + + return data; +} + +static inline uint16_t icu2_clear(uint8_t offset, uint16_t clear) +{ + uint16_t data; + + data = icu2_read(offset); + data &= ~clear; + icu2_write(offset, data); + + return data; +} + +void vr41xx_enable_piuint(uint16_t mask) +{ + struct irq_desc *desc = irq_to_desc(PIU_IRQ); + unsigned long flags; + + if (current_cpu_type() == CPU_VR4111 || + current_cpu_type() == CPU_VR4121) { + raw_spin_lock_irqsave(&desc->lock, flags); + icu1_set(MPIUINTREG, mask); + raw_spin_unlock_irqrestore(&desc->lock, flags); + } +} + +EXPORT_SYMBOL(vr41xx_enable_piuint); + +void vr41xx_disable_piuint(uint16_t mask) +{ + struct irq_desc *desc = irq_to_desc(PIU_IRQ); + unsigned long flags; + + if (current_cpu_type() == CPU_VR4111 || + current_cpu_type() == CPU_VR4121) { + raw_spin_lock_irqsave(&desc->lock, flags); + icu1_clear(MPIUINTREG, mask); + raw_spin_unlock_irqrestore(&desc->lock, flags); + } +} + +EXPORT_SYMBOL(vr41xx_disable_piuint); + +void vr41xx_enable_aiuint(uint16_t mask) +{ + struct irq_desc *desc = irq_to_desc(AIU_IRQ); + unsigned long flags; + + if (current_cpu_type() == CPU_VR4111 || + current_cpu_type() == CPU_VR4121) { + raw_spin_lock_irqsave(&desc->lock, flags); + icu1_set(MAIUINTREG, mask); + raw_spin_unlock_irqrestore(&desc->lock, flags); + } +} + +EXPORT_SYMBOL(vr41xx_enable_aiuint); + +void vr41xx_disable_aiuint(uint16_t mask) +{ + struct irq_desc *desc = irq_to_desc(AIU_IRQ); + unsigned long flags; + + if (current_cpu_type() == CPU_VR4111 || + current_cpu_type() == CPU_VR4121) { + raw_spin_lock_irqsave(&desc->lock, flags); + icu1_clear(MAIUINTREG, mask); + raw_spin_unlock_irqrestore(&desc->lock, flags); + } +} + +EXPORT_SYMBOL(vr41xx_disable_aiuint); + +void vr41xx_enable_kiuint(uint16_t mask) +{ + struct irq_desc *desc = irq_to_desc(KIU_IRQ); + unsigned long flags; + + if (current_cpu_type() == CPU_VR4111 || + current_cpu_type() == CPU_VR4121) { + raw_spin_lock_irqsave(&desc->lock, flags); + icu1_set(MKIUINTREG, mask); + raw_spin_unlock_irqrestore(&desc->lock, flags); + } +} + +EXPORT_SYMBOL(vr41xx_enable_kiuint); + +void vr41xx_disable_kiuint(uint16_t mask) +{ + struct irq_desc *desc = irq_to_desc(KIU_IRQ); + unsigned long flags; + + if (current_cpu_type() == CPU_VR4111 || + current_cpu_type() == CPU_VR4121) { + raw_spin_lock_irqsave(&desc->lock, flags); + icu1_clear(MKIUINTREG, mask); + raw_spin_unlock_irqrestore(&desc->lock, flags); + } +} + +EXPORT_SYMBOL(vr41xx_disable_kiuint); + +void vr41xx_enable_macint(uint16_t mask) +{ + struct irq_desc *desc = irq_to_desc(ETHERNET_IRQ); + unsigned long flags; + + raw_spin_lock_irqsave(&desc->lock, flags); + icu1_set(MMACINTREG, mask); + raw_spin_unlock_irqrestore(&desc->lock, flags); +} + +EXPORT_SYMBOL(vr41xx_enable_macint); + +void vr41xx_disable_macint(uint16_t mask) +{ + struct irq_desc *desc = irq_to_desc(ETHERNET_IRQ); + unsigned long flags; + + raw_spin_lock_irqsave(&desc->lock, flags); + icu1_clear(MMACINTREG, mask); + raw_spin_unlock_irqrestore(&desc->lock, flags); +} + +EXPORT_SYMBOL(vr41xx_disable_macint); + +void vr41xx_enable_dsiuint(uint16_t mask) +{ + struct irq_desc *desc = irq_to_desc(DSIU_IRQ); + unsigned long flags; + + raw_spin_lock_irqsave(&desc->lock, flags); + icu1_set(MDSIUINTREG, mask); + raw_spin_unlock_irqrestore(&desc->lock, flags); +} + +EXPORT_SYMBOL(vr41xx_enable_dsiuint); + +void vr41xx_disable_dsiuint(uint16_t mask) +{ + struct irq_desc *desc = irq_to_desc(DSIU_IRQ); + unsigned long flags; + + raw_spin_lock_irqsave(&desc->lock, flags); + icu1_clear(MDSIUINTREG, mask); + raw_spin_unlock_irqrestore(&desc->lock, flags); +} + +EXPORT_SYMBOL(vr41xx_disable_dsiuint); + +void vr41xx_enable_firint(uint16_t mask) +{ + struct irq_desc *desc = irq_to_desc(FIR_IRQ); + unsigned long flags; + + raw_spin_lock_irqsave(&desc->lock, flags); + icu2_set(MFIRINTREG, mask); + raw_spin_unlock_irqrestore(&desc->lock, flags); +} + +EXPORT_SYMBOL(vr41xx_enable_firint); + +void vr41xx_disable_firint(uint16_t mask) +{ + struct irq_desc *desc = irq_to_desc(FIR_IRQ); + unsigned long flags; + + raw_spin_lock_irqsave(&desc->lock, flags); + icu2_clear(MFIRINTREG, mask); + raw_spin_unlock_irqrestore(&desc->lock, flags); +} + +EXPORT_SYMBOL(vr41xx_disable_firint); + +void vr41xx_enable_pciint(void) +{ + struct irq_desc *desc = irq_to_desc(PCI_IRQ); + unsigned long flags; + + if (current_cpu_type() == CPU_VR4122 || + current_cpu_type() == CPU_VR4131 || + current_cpu_type() == CPU_VR4133) { + raw_spin_lock_irqsave(&desc->lock, flags); + icu2_write(MPCIINTREG, PCIINT0); + raw_spin_unlock_irqrestore(&desc->lock, flags); + } +} + +EXPORT_SYMBOL(vr41xx_enable_pciint); + +void vr41xx_disable_pciint(void) +{ + struct irq_desc *desc = irq_to_desc(PCI_IRQ); + unsigned long flags; + + if (current_cpu_type() == CPU_VR4122 || + current_cpu_type() == CPU_VR4131 || + current_cpu_type() == CPU_VR4133) { + raw_spin_lock_irqsave(&desc->lock, flags); + icu2_write(MPCIINTREG, 0); + raw_spin_unlock_irqrestore(&desc->lock, flags); + } +} + +EXPORT_SYMBOL(vr41xx_disable_pciint); + +void vr41xx_enable_scuint(void) +{ + struct irq_desc *desc = irq_to_desc(SCU_IRQ); + unsigned long flags; + + if (current_cpu_type() == CPU_VR4122 || + current_cpu_type() == CPU_VR4131 || + current_cpu_type() == CPU_VR4133) { + raw_spin_lock_irqsave(&desc->lock, flags); + icu2_write(MSCUINTREG, SCUINT0); + raw_spin_unlock_irqrestore(&desc->lock, flags); + } +} + +EXPORT_SYMBOL(vr41xx_enable_scuint); + +void vr41xx_disable_scuint(void) +{ + struct irq_desc *desc = irq_to_desc(SCU_IRQ); + unsigned long flags; + + if (current_cpu_type() == CPU_VR4122 || + current_cpu_type() == CPU_VR4131 || + current_cpu_type() == CPU_VR4133) { + raw_spin_lock_irqsave(&desc->lock, flags); + icu2_write(MSCUINTREG, 0); + raw_spin_unlock_irqrestore(&desc->lock, flags); + } +} + +EXPORT_SYMBOL(vr41xx_disable_scuint); + +void vr41xx_enable_csiint(uint16_t mask) +{ + struct irq_desc *desc = irq_to_desc(CSI_IRQ); + unsigned long flags; + + if (current_cpu_type() == CPU_VR4122 || + current_cpu_type() == CPU_VR4131 || + current_cpu_type() == CPU_VR4133) { + raw_spin_lock_irqsave(&desc->lock, flags); + icu2_set(MCSIINTREG, mask); + raw_spin_unlock_irqrestore(&desc->lock, flags); + } +} + +EXPORT_SYMBOL(vr41xx_enable_csiint); + +void vr41xx_disable_csiint(uint16_t mask) +{ + struct irq_desc *desc = irq_to_desc(CSI_IRQ); + unsigned long flags; + + if (current_cpu_type() == CPU_VR4122 || + current_cpu_type() == CPU_VR4131 || + current_cpu_type() == CPU_VR4133) { + raw_spin_lock_irqsave(&desc->lock, flags); + icu2_clear(MCSIINTREG, mask); + raw_spin_unlock_irqrestore(&desc->lock, flags); + } +} + +EXPORT_SYMBOL(vr41xx_disable_csiint); + +void vr41xx_enable_bcuint(void) +{ + struct irq_desc *desc = irq_to_desc(BCU_IRQ); + unsigned long flags; + + if (current_cpu_type() == CPU_VR4122 || + current_cpu_type() == CPU_VR4131 || + current_cpu_type() == CPU_VR4133) { + raw_spin_lock_irqsave(&desc->lock, flags); + icu2_write(MBCUINTREG, BCUINTR); + raw_spin_unlock_irqrestore(&desc->lock, flags); + } +} + +EXPORT_SYMBOL(vr41xx_enable_bcuint); + +void vr41xx_disable_bcuint(void) +{ + struct irq_desc *desc = irq_to_desc(BCU_IRQ); + unsigned long flags; + + if (current_cpu_type() == CPU_VR4122 || + current_cpu_type() == CPU_VR4131 || + current_cpu_type() == CPU_VR4133) { + raw_spin_lock_irqsave(&desc->lock, flags); + icu2_write(MBCUINTREG, 0); + raw_spin_unlock_irqrestore(&desc->lock, flags); + } +} + +EXPORT_SYMBOL(vr41xx_disable_bcuint); + +static void disable_sysint1_irq(struct irq_data *d) +{ + icu1_clear(MSYSINT1REG, 1 << SYSINT1_IRQ_TO_PIN(d->irq)); +} + +static void enable_sysint1_irq(struct irq_data *d) +{ + icu1_set(MSYSINT1REG, 1 << SYSINT1_IRQ_TO_PIN(d->irq)); +} + +static struct irq_chip sysint1_irq_type = { + .name = "SYSINT1", + .irq_mask = disable_sysint1_irq, + .irq_unmask = enable_sysint1_irq, +}; + +static void disable_sysint2_irq(struct irq_data *d) +{ + icu2_clear(MSYSINT2REG, 1 << SYSINT2_IRQ_TO_PIN(d->irq)); +} + +static void enable_sysint2_irq(struct irq_data *d) +{ + icu2_set(MSYSINT2REG, 1 << SYSINT2_IRQ_TO_PIN(d->irq)); +} + +static struct irq_chip sysint2_irq_type = { + .name = "SYSINT2", + .irq_mask = disable_sysint2_irq, + .irq_unmask = enable_sysint2_irq, +}; + +static inline int set_sysint1_assign(unsigned int irq, unsigned char assign) +{ + struct irq_desc *desc = irq_to_desc(irq); + uint16_t intassign0, intassign1; + unsigned int pin; + + pin = SYSINT1_IRQ_TO_PIN(irq); + + raw_spin_lock_irq(&desc->lock); + + intassign0 = icu1_read(INTASSIGN0); + intassign1 = icu1_read(INTASSIGN1); + + switch (pin) { + case 0: + intassign0 &= ~INTASSIGN_MASK; + intassign0 |= (uint16_t)assign; + break; + case 1: + intassign0 &= ~(INTASSIGN_MASK << 3); + intassign0 |= (uint16_t)assign << 3; + break; + case 2: + intassign0 &= ~(INTASSIGN_MASK << 6); + intassign0 |= (uint16_t)assign << 6; + break; + case 3: + intassign0 &= ~(INTASSIGN_MASK << 9); + intassign0 |= (uint16_t)assign << 9; + break; + case 8: + intassign0 &= ~(INTASSIGN_MASK << 12); + intassign0 |= (uint16_t)assign << 12; + break; + case 9: + intassign1 &= ~INTASSIGN_MASK; + intassign1 |= (uint16_t)assign; + break; + case 11: + intassign1 &= ~(INTASSIGN_MASK << 6); + intassign1 |= (uint16_t)assign << 6; + break; + case 12: + intassign1 &= ~(INTASSIGN_MASK << 9); + intassign1 |= (uint16_t)assign << 9; + break; + default: + raw_spin_unlock_irq(&desc->lock); + return -EINVAL; + } + + sysint1_assign[pin] = assign; + icu1_write(INTASSIGN0, intassign0); + icu1_write(INTASSIGN1, intassign1); + + raw_spin_unlock_irq(&desc->lock); + + return 0; +} + +static inline int set_sysint2_assign(unsigned int irq, unsigned char assign) +{ + struct irq_desc *desc = irq_to_desc(irq); + uint16_t intassign2, intassign3; + unsigned int pin; + + pin = SYSINT2_IRQ_TO_PIN(irq); + + raw_spin_lock_irq(&desc->lock); + + intassign2 = icu1_read(INTASSIGN2); + intassign3 = icu1_read(INTASSIGN3); + + switch (pin) { + case 0: + intassign2 &= ~INTASSIGN_MASK; + intassign2 |= (uint16_t)assign; + break; + case 1: + intassign2 &= ~(INTASSIGN_MASK << 3); + intassign2 |= (uint16_t)assign << 3; + break; + case 3: + intassign2 &= ~(INTASSIGN_MASK << 6); + intassign2 |= (uint16_t)assign << 6; + break; + case 4: + intassign2 &= ~(INTASSIGN_MASK << 9); + intassign2 |= (uint16_t)assign << 9; + break; + case 5: + intassign2 &= ~(INTASSIGN_MASK << 12); + intassign2 |= (uint16_t)assign << 12; + break; + case 6: + intassign3 &= ~INTASSIGN_MASK; + intassign3 |= (uint16_t)assign; + break; + case 7: + intassign3 &= ~(INTASSIGN_MASK << 3); + intassign3 |= (uint16_t)assign << 3; + break; + case 8: + intassign3 &= ~(INTASSIGN_MASK << 6); + intassign3 |= (uint16_t)assign << 6; + break; + case 9: + intassign3 &= ~(INTASSIGN_MASK << 9); + intassign3 |= (uint16_t)assign << 9; + break; + case 10: + intassign3 &= ~(INTASSIGN_MASK << 12); + intassign3 |= (uint16_t)assign << 12; + break; + default: + raw_spin_unlock_irq(&desc->lock); + return -EINVAL; + } + + sysint2_assign[pin] = assign; + icu1_write(INTASSIGN2, intassign2); + icu1_write(INTASSIGN3, intassign3); + + raw_spin_unlock_irq(&desc->lock); + + return 0; +} + +int vr41xx_set_intassign(unsigned int irq, unsigned char intassign) +{ + int retval = -EINVAL; + + if (current_cpu_type() != CPU_VR4133) + return -EINVAL; + + if (intassign > INTASSIGN_MAX) + return -EINVAL; + + if (irq >= SYSINT1_IRQ_BASE && irq <= SYSINT1_IRQ_LAST) + retval = set_sysint1_assign(irq, intassign); + else if (irq >= SYSINT2_IRQ_BASE && irq <= SYSINT2_IRQ_LAST) + retval = set_sysint2_assign(irq, intassign); + + return retval; +} + +EXPORT_SYMBOL(vr41xx_set_intassign); + +static int icu_get_irq(unsigned int irq) +{ + uint16_t pend1, pend2; + uint16_t mask1, mask2; + int i; + + pend1 = icu1_read(SYSINT1REG); + mask1 = icu1_read(MSYSINT1REG); + + pend2 = icu2_read(SYSINT2REG); + mask2 = icu2_read(MSYSINT2REG); + + mask1 &= pend1; + mask2 &= pend2; + + if (mask1) { + for (i = 0; i < 16; i++) { + if (irq == INT_TO_IRQ(sysint1_assign[i]) && (mask1 & (1 << i))) + return SYSINT1_IRQ(i); + } + } + + if (mask2) { + for (i = 0; i < 16; i++) { + if (irq == INT_TO_IRQ(sysint2_assign[i]) && (mask2 & (1 << i))) + return SYSINT2_IRQ(i); + } + } + + printk(KERN_ERR "spurious ICU interrupt: %04x,%04x\n", pend1, pend2); + + atomic_inc(&irq_err_count); + + return -1; +} + +static int __init vr41xx_icu_init(void) +{ + unsigned long icu1_start, icu2_start; + int i; + + switch (current_cpu_type()) { + case CPU_VR4111: + case CPU_VR4121: + icu1_start = ICU1_TYPE1_BASE; + icu2_start = ICU2_TYPE1_BASE; + break; + case CPU_VR4122: + case CPU_VR4131: + case CPU_VR4133: + icu1_start = ICU1_TYPE2_BASE; + icu2_start = ICU2_TYPE2_BASE; + break; + default: + printk(KERN_ERR "ICU: Unexpected CPU of NEC VR4100 series\n"); + return -ENODEV; + } + + if (request_mem_region(icu1_start, ICU1_SIZE, "ICU") == NULL) + return -EBUSY; + + if (request_mem_region(icu2_start, ICU2_SIZE, "ICU") == NULL) { + release_mem_region(icu1_start, ICU1_SIZE); + return -EBUSY; + } + + icu1_base = ioremap(icu1_start, ICU1_SIZE); + if (icu1_base == NULL) { + release_mem_region(icu1_start, ICU1_SIZE); + release_mem_region(icu2_start, ICU2_SIZE); + return -ENOMEM; + } + + icu2_base = ioremap(icu2_start, ICU2_SIZE); + if (icu2_base == NULL) { + iounmap(icu1_base); + release_mem_region(icu1_start, ICU1_SIZE); + release_mem_region(icu2_start, ICU2_SIZE); + return -ENOMEM; + } + + icu1_write(MSYSINT1REG, 0); + icu1_write(MGIUINTLREG, 0xffff); + + icu2_write(MSYSINT2REG, 0); + icu2_write(MGIUINTHREG, 0xffff); + + for (i = SYSINT1_IRQ_BASE; i <= SYSINT1_IRQ_LAST; i++) + irq_set_chip_and_handler(i, &sysint1_irq_type, + handle_level_irq); + + for (i = SYSINT2_IRQ_BASE; i <= SYSINT2_IRQ_LAST; i++) + irq_set_chip_and_handler(i, &sysint2_irq_type, + handle_level_irq); + + cascade_irq(INT0_IRQ, icu_get_irq); + cascade_irq(INT1_IRQ, icu_get_irq); + cascade_irq(INT2_IRQ, icu_get_irq); + cascade_irq(INT3_IRQ, icu_get_irq); + cascade_irq(INT4_IRQ, icu_get_irq); + + return 0; +} + +core_initcall(vr41xx_icu_init); diff --git a/arch/mips/vr41xx/common/init.c b/arch/mips/vr41xx/common/init.c new file mode 100644 index 000000000..23916321c --- /dev/null +++ b/arch/mips/vr41xx/common/init.c @@ -0,0 +1,77 @@ +/* + * init.c, Common initialization routines for NEC VR4100 series. + * + * Copyright (C) 2003-2009 Yoichi Yuasa <yuasa@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 + */ +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/irq.h> +#include <linux/string.h> + +#include <asm/bootinfo.h> +#include <asm/time.h> +#include <asm/vr41xx/irq.h> +#include <asm/vr41xx/vr41xx.h> + +#define IO_MEM_RESOURCE_START 0UL +#define IO_MEM_RESOURCE_END 0x1fffffffUL + +static void __init iomem_resource_init(void) +{ + iomem_resource.start = IO_MEM_RESOURCE_START; + iomem_resource.end = IO_MEM_RESOURCE_END; +} + +void __init plat_time_init(void) +{ + unsigned long tclock; + + vr41xx_calculate_clock_frequency(); + + tclock = vr41xx_get_tclock_frequency(); + if (current_cpu_data.processor_id == PRID_VR4131_REV2_0 || + current_cpu_data.processor_id == PRID_VR4131_REV2_1) + mips_hpt_frequency = tclock / 2; + else + mips_hpt_frequency = tclock / 4; +} + +void __init plat_mem_setup(void) +{ + iomem_resource_init(); + + vr41xx_siu_setup(); +} + +void __init prom_init(void) +{ + int argc, i; + char **argv; + + argc = fw_arg0; + argv = (char **)fw_arg1; + + for (i = 1; i < argc; i++) { + strlcat(arcs_cmdline, argv[i], COMMAND_LINE_SIZE); + if (i < (argc - 1)) + strlcat(arcs_cmdline, " ", COMMAND_LINE_SIZE); + } +} + +void __init prom_free_prom_memory(void) +{ +} diff --git a/arch/mips/vr41xx/common/irq.c b/arch/mips/vr41xx/common/irq.c new file mode 100644 index 000000000..28211f3ee --- /dev/null +++ b/arch/mips/vr41xx/common/irq.c @@ -0,0 +1,124 @@ +/* + * Interrupt handing routines for NEC VR4100 series. + * + * Copyright (C) 2005-2007 Yoichi Yuasa <yuasa@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 + */ +#include <linux/export.h> +#include <linux/interrupt.h> +#include <linux/irq.h> + +#include <asm/irq_cpu.h> +#include <asm/vr41xx/irq.h> + +typedef struct irq_cascade { + int (*get_irq)(unsigned int); +} irq_cascade_t; + +static irq_cascade_t irq_cascade[NR_IRQS] __cacheline_aligned; + +static struct irqaction cascade_irqaction = { + .handler = no_action, + .name = "cascade", + .flags = IRQF_NO_THREAD, +}; + +int cascade_irq(unsigned int irq, int (*get_irq)(unsigned int)) +{ + int retval = 0; + + if (irq >= NR_IRQS) + return -EINVAL; + + if (irq_cascade[irq].get_irq != NULL) + free_irq(irq, NULL); + + irq_cascade[irq].get_irq = get_irq; + + if (get_irq != NULL) { + retval = setup_irq(irq, &cascade_irqaction); + if (retval < 0) + irq_cascade[irq].get_irq = NULL; + } + + return retval; +} + +EXPORT_SYMBOL_GPL(cascade_irq); + +static void irq_dispatch(unsigned int irq) +{ + irq_cascade_t *cascade; + + if (irq >= NR_IRQS) { + atomic_inc(&irq_err_count); + return; + } + + cascade = irq_cascade + irq; + if (cascade->get_irq != NULL) { + struct irq_desc *desc = irq_to_desc(irq); + struct irq_data *idata = irq_desc_get_irq_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + int ret; + + if (chip->irq_mask_ack) + chip->irq_mask_ack(idata); + else { + chip->irq_mask(idata); + chip->irq_ack(idata); + } + ret = cascade->get_irq(irq); + irq = ret; + if (ret < 0) + atomic_inc(&irq_err_count); + else + irq_dispatch(irq); + if (!irqd_irq_disabled(idata) && chip->irq_unmask) + chip->irq_unmask(idata); + } else + do_IRQ(irq); +} + +asmlinkage void plat_irq_dispatch(void) +{ + unsigned int pending = read_c0_cause() & read_c0_status() & ST0_IM; + + if (pending & CAUSEF_IP7) + do_IRQ(TIMER_IRQ); + else if (pending & 0x7800) { + if (pending & CAUSEF_IP3) + irq_dispatch(INT1_IRQ); + else if (pending & CAUSEF_IP4) + irq_dispatch(INT2_IRQ); + else if (pending & CAUSEF_IP5) + irq_dispatch(INT3_IRQ); + else if (pending & CAUSEF_IP6) + irq_dispatch(INT4_IRQ); + } else if (pending & CAUSEF_IP2) + irq_dispatch(INT0_IRQ); + else if (pending & CAUSEF_IP0) + do_IRQ(MIPS_SOFTINT0_IRQ); + else if (pending & CAUSEF_IP1) + do_IRQ(MIPS_SOFTINT1_IRQ); + else + spurious_interrupt(); +} + +void __init arch_init_irq(void) +{ + mips_cpu_irq_init(); +} diff --git a/arch/mips/vr41xx/common/pmu.c b/arch/mips/vr41xx/common/pmu.c new file mode 100644 index 000000000..16e684b59 --- /dev/null +++ b/arch/mips/vr41xx/common/pmu.c @@ -0,0 +1,136 @@ +/* + * pmu.c, Power Management Unit routines for NEC VR4100 series. + * + * Copyright (C) 2003-2007 Yoichi Yuasa <yuasa@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 + */ +#include <linux/cpu.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/pm.h> +#include <linux/sched.h> +#include <linux/types.h> + +#include <asm/cacheflush.h> +#include <asm/cpu.h> +#include <asm/idle.h> +#include <asm/io.h> +#include <asm/processor.h> +#include <asm/reboot.h> + +#define PMU_TYPE1_BASE 0x0b0000a0UL +#define PMU_TYPE1_SIZE 0x0eUL + +#define PMU_TYPE2_BASE 0x0f0000c0UL +#define PMU_TYPE2_SIZE 0x10UL + +#define PMUCNT2REG 0x06 + #define SOFTRST 0x0010 + +static void __iomem *pmu_base; + +#define pmu_read(offset) readw(pmu_base + (offset)) +#define pmu_write(offset, value) writew((value), pmu_base + (offset)) + +static void __cpuidle vr41xx_cpu_wait(void) +{ + local_irq_disable(); + if (!need_resched()) + /* + * "standby" sets IE bit of the CP0_STATUS to 1. + */ + __asm__("standby;\n"); + else + local_irq_enable(); +} + +static inline void software_reset(void) +{ + uint16_t pmucnt2; + + switch (current_cpu_type()) { + case CPU_VR4122: + case CPU_VR4131: + case CPU_VR4133: + pmucnt2 = pmu_read(PMUCNT2REG); + pmucnt2 |= SOFTRST; + pmu_write(PMUCNT2REG, pmucnt2); + break; + default: + set_c0_status(ST0_BEV | ST0_ERL); + change_c0_config(CONF_CM_CMASK, CONF_CM_UNCACHED); + __flush_cache_all(); + write_c0_wired(0); + __asm__("jr %0"::"r"(0xbfc00000)); + break; + } +} + +static void vr41xx_restart(char *command) +{ + local_irq_disable(); + software_reset(); + while (1) ; +} + +static void vr41xx_halt(void) +{ + local_irq_disable(); + printk(KERN_NOTICE "\nYou can turn off the power supply\n"); + __asm__("hibernate;\n"); +} + +static int __init vr41xx_pmu_init(void) +{ + unsigned long start, size; + + switch (current_cpu_type()) { + case CPU_VR4111: + case CPU_VR4121: + start = PMU_TYPE1_BASE; + size = PMU_TYPE1_SIZE; + break; + case CPU_VR4122: + case CPU_VR4131: + case CPU_VR4133: + start = PMU_TYPE2_BASE; + size = PMU_TYPE2_SIZE; + break; + default: + printk("Unexpected CPU of NEC VR4100 series\n"); + return -ENODEV; + } + + if (request_mem_region(start, size, "PMU") == NULL) + return -EBUSY; + + pmu_base = ioremap(start, size); + if (pmu_base == NULL) { + release_mem_region(start, size); + return -EBUSY; + } + + cpu_wait = vr41xx_cpu_wait; + _machine_restart = vr41xx_restart; + _machine_halt = vr41xx_halt; + pm_power_off = vr41xx_halt; + + return 0; +} + +core_initcall(vr41xx_pmu_init); diff --git a/arch/mips/vr41xx/common/rtc.c b/arch/mips/vr41xx/common/rtc.c new file mode 100644 index 000000000..c1e3d2009 --- /dev/null +++ b/arch/mips/vr41xx/common/rtc.c @@ -0,0 +1,118 @@ +/* + * NEC VR4100 series RTC platform device. + * + * Copyright (C) 2007 Yoichi Yuasa <yuasa@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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/smp.h> +#include <linux/ioport.h> +#include <linux/platform_device.h> + +#include <asm/cpu.h> +#include <asm/vr41xx/irq.h> + +static struct resource rtc_type1_resource[] __initdata = { + { + .start = 0x0b0000c0, + .end = 0x0b0000df, + .flags = IORESOURCE_MEM, + }, + { + .start = 0x0b0001c0, + .end = 0x0b0001df, + .flags = IORESOURCE_MEM, + }, + { + .start = ELAPSEDTIME_IRQ, + .end = ELAPSEDTIME_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = RTCLONG1_IRQ, + .end = RTCLONG1_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource rtc_type2_resource[] __initdata = { + { + .start = 0x0f000100, + .end = 0x0f00011f, + .flags = IORESOURCE_MEM, + }, + { + .start = 0x0f000120, + .end = 0x0f00013f, + .flags = IORESOURCE_MEM, + }, + { + .start = ELAPSEDTIME_IRQ, + .end = ELAPSEDTIME_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = RTCLONG1_IRQ, + .end = RTCLONG1_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static int __init vr41xx_rtc_add(void) +{ + struct platform_device *pdev; + struct resource *res; + unsigned int num; + int retval; + + pdev = platform_device_alloc("RTC", -1); + if (!pdev) + return -ENOMEM; + + switch (current_cpu_type()) { + case CPU_VR4111: + case CPU_VR4121: + res = rtc_type1_resource; + num = ARRAY_SIZE(rtc_type1_resource); + break; + case CPU_VR4122: + case CPU_VR4131: + case CPU_VR4133: + res = rtc_type2_resource; + num = ARRAY_SIZE(rtc_type2_resource); + break; + default: + retval = -ENODEV; + goto err_free_device; + } + + retval = platform_device_add_resources(pdev, res, num); + if (retval) + goto err_free_device; + + retval = platform_device_add(pdev); + if (retval) + goto err_free_device; + + return 0; + +err_free_device: + platform_device_put(pdev); + + return retval; +} +device_initcall(vr41xx_rtc_add); diff --git a/arch/mips/vr41xx/common/siu.c b/arch/mips/vr41xx/common/siu.c new file mode 100644 index 000000000..bbd45d255 --- /dev/null +++ b/arch/mips/vr41xx/common/siu.c @@ -0,0 +1,155 @@ +/* + * NEC VR4100 series SIU platform device. + * + * Copyright (C) 2007-2008 Yoichi Yuasa <yuasa@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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/platform_device.h> +#include <linux/serial_core.h> +#include <linux/irq.h> + +#include <asm/cpu.h> +#include <asm/vr41xx/siu.h> + +static unsigned int siu_type1_ports[SIU_PORTS_MAX] __initdata = { + PORT_VR41XX_SIU, + PORT_UNKNOWN, +}; + +static struct resource siu_type1_resource[] __initdata = { + { + .start = 0x0c000000, + .end = 0x0c00000a, + .flags = IORESOURCE_MEM, + }, + { + .start = SIU_IRQ, + .end = SIU_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static unsigned int siu_type2_ports[SIU_PORTS_MAX] __initdata = { + PORT_VR41XX_SIU, + PORT_VR41XX_DSIU, +}; + +static struct resource siu_type2_resource[] __initdata = { + { + .start = 0x0f000800, + .end = 0x0f00080a, + .flags = IORESOURCE_MEM, + }, + { + .start = 0x0f000820, + .end = 0x0f000829, + .flags = IORESOURCE_MEM, + }, + { + .start = SIU_IRQ, + .end = SIU_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = DSIU_IRQ, + .end = DSIU_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static int __init vr41xx_siu_add(void) +{ + struct platform_device *pdev; + struct resource *res; + unsigned int num; + int retval; + + pdev = platform_device_alloc("SIU", -1); + if (!pdev) + return -ENOMEM; + + switch (current_cpu_type()) { + case CPU_VR4111: + case CPU_VR4121: + pdev->dev.platform_data = siu_type1_ports; + res = siu_type1_resource; + num = ARRAY_SIZE(siu_type1_resource); + break; + case CPU_VR4122: + case CPU_VR4131: + case CPU_VR4133: + pdev->dev.platform_data = siu_type2_ports; + res = siu_type2_resource; + num = ARRAY_SIZE(siu_type2_resource); + break; + default: + retval = -ENODEV; + goto err_free_device; + } + + retval = platform_device_add_resources(pdev, res, num); + if (retval) + goto err_free_device; + + retval = platform_device_add(pdev); + if (retval) + goto err_free_device; + + return 0; + +err_free_device: + platform_device_put(pdev); + + return retval; +} +device_initcall(vr41xx_siu_add); + +void __init vr41xx_siu_setup(void) +{ + struct uart_port port; + struct resource *res; + unsigned int *type; + int i; + + switch (current_cpu_type()) { + case CPU_VR4111: + case CPU_VR4121: + type = siu_type1_ports; + res = siu_type1_resource; + break; + case CPU_VR4122: + case CPU_VR4131: + case CPU_VR4133: + type = siu_type2_ports; + res = siu_type2_resource; + break; + default: + return; + } + + for (i = 0; i < SIU_PORTS_MAX; i++) { + port.line = i; + port.type = type[i]; + if (port.type == PORT_UNKNOWN) + break; + port.mapbase = res[i].start; + port.membase = (unsigned char __iomem *)KSEG1ADDR(res[i].start); + vr41xx_siu_early_setup(&port); + } +} diff --git a/arch/mips/vr41xx/common/type.c b/arch/mips/vr41xx/common/type.c new file mode 100644 index 000000000..45836a92b --- /dev/null +++ b/arch/mips/vr41xx/common/type.c @@ -0,0 +1,24 @@ +/* + * type.c, System type for NEC VR4100 series. + * + * Copyright (C) 2005 Yoichi Yuasa <yuasa@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 + */ + +const char *get_system_type(void) +{ + return "NEC VR4100 series"; +} |