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!
Цей коміт міститься в:
Linus Torvalds
2005-04-16 15:20:36 -07:00
коміт 1da177e4c3
17291 змінених файлів з 6718755 додано та 0 видалено

8
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)

222
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);

257
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);

455
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) {
int irq = cascade->get_irq_number(giuint_irq);
ack_giuint_irq(pin);
if (irq >= 0)
do_IRQ(irq, regs);
end_giuint_irq(pin);
} else {
do_IRQ(giuint_irq, regs);
}
enable_irq(GIUINT_CASCADE_IRQ);
}
static int __init vr41xx_giu_init(void)
{
int i;
switch (current_cpu_data.cputype) {
case CPU_VR4111:
case CPU_VR4121:
giu_base = GIUIOSELL_TYPE1;
break;
case CPU_VR4122:
case CPU_VR4131:
case CPU_VR4133:
giu_base = GIUIOSELL_TYPE2;
break;
default:
printk(KERN_ERR "GIU: Unexpected CPU of NEC VR4100 series\n");
return -EINVAL;
}
for (i = 0; i < GIUINT_NR_IRQS; i++) {
if (i < GIUINT_HIGH_OFFSET)
clear_giuint(GIUINTENL, (uint16_t)1 << i);
else
clear_giuint(GIUINTENH, (uint16_t)1 << (i - GIUINT_HIGH_OFFSET));
giuint_cascade[i].flag = GIUINT_NO_CASCADE;
giuint_cascade[i].get_irq_number = no_irq_number;
}
return 0;
}
early_initcall(vr41xx_giu_init);

757
arch/mips/vr41xx/common/icu.c Звичайний файл
Переглянути файл

@@ -0,0 +1,757 @@
/*
* icu.c, Interrupt Control Unit routines for the NEC VR4100 series.
*
* Copyright (C) 2001-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 VR4122 and VR4131 are supported.
* - Added support for NEC VR4111 and VR4121.
*
* Yoichi Yuasa <yuasa@hh.iij4u.or.jp>
* - Coped with INTASSIGN of NEC VR4133.
*/
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/smp.h>
#include <linux/types.h>
#include <asm/cpu.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/irq_cpu.h>
#include <asm/vr41xx/vr41xx.h>
extern asmlinkage void vr41xx_handle_interrupt(void);
extern void init_vr41xx_giuint_irq(void);
extern void giuint_irq_dispatch(struct pt_regs *regs);
static uint32_t icu1_base;
static uint32_t icu2_base;
static struct irqaction icu_cascade = {
.handler = no_action,
.mask = CPU_MASK_NONE,
.name = "cascade",
};
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, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
#define SYSINT1REG_TYPE1 KSEG1ADDR(0x0b000080)
#define SYSINT2REG_TYPE1 KSEG1ADDR(0x0b000200)
#define SYSINT1REG_TYPE2 KSEG1ADDR(0x0f000080)
#define SYSINT2REG_TYPE2 KSEG1ADDR(0x0f0000a0)
#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 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 read_icu1(offset) readw(icu1_base + (offset))
#define write_icu1(val, offset) writew((val), icu1_base + (offset))
#define read_icu2(offset) readw(icu2_base + (offset))
#define write_icu2(val, offset) writew((val), icu2_base + (offset))
#define INTASSIGN_MAX 4
#define INTASSIGN_MASK 0x0007
static inline uint16_t set_icu1(uint8_t offset, uint16_t set)
{
uint16_t res;
res = read_icu1(offset);
res |= set;
write_icu1(res, offset);
return res;
}
static inline uint16_t clear_icu1(uint8_t offset, uint16_t clear)
{
uint16_t res;
res = read_icu1(offset);
res &= ~clear;
write_icu1(res, offset);
return res;
}
static inline uint16_t set_icu2(uint8_t offset, uint16_t set)
{
uint16_t res;
res = read_icu2(offset);
res |= set;
write_icu2(res, offset);
return res;
}
static inline uint16_t clear_icu2(uint8_t offset, uint16_t clear)
{
uint16_t res;
res = read_icu2(offset);
res &= ~clear;
write_icu2(res, offset);
return res;
}
/*=======================================================================*/
void vr41xx_enable_piuint(uint16_t mask)
{
irq_desc_t *desc = irq_desc + PIU_IRQ;
unsigned long flags;
if (current_cpu_data.cputype == CPU_VR4111 ||
current_cpu_data.cputype == CPU_VR4121) {
spin_lock_irqsave(&desc->lock, flags);
set_icu1(MPIUINTREG, mask);
spin_unlock_irqrestore(&desc->lock, flags);
}
}
EXPORT_SYMBOL(vr41xx_enable_piuint);
void vr41xx_disable_piuint(uint16_t mask)
{
irq_desc_t *desc = irq_desc + PIU_IRQ;
unsigned long flags;
if (current_cpu_data.cputype == CPU_VR4111 ||
current_cpu_data.cputype == CPU_VR4121) {
spin_lock_irqsave(&desc->lock, flags);
clear_icu1(MPIUINTREG, mask);
spin_unlock_irqrestore(&desc->lock, flags);
}
}
EXPORT_SYMBOL(vr41xx_disable_piuint);
void vr41xx_enable_aiuint(uint16_t mask)
{
irq_desc_t *desc = irq_desc + AIU_IRQ;
unsigned long flags;
if (current_cpu_data.cputype == CPU_VR4111 ||
current_cpu_data.cputype == CPU_VR4121) {
spin_lock_irqsave(&desc->lock, flags);
set_icu1(MAIUINTREG, mask);
spin_unlock_irqrestore(&desc->lock, flags);
}
}
EXPORT_SYMBOL(vr41xx_enable_aiuint);
void vr41xx_disable_aiuint(uint16_t mask)
{
irq_desc_t *desc = irq_desc + AIU_IRQ;
unsigned long flags;
if (current_cpu_data.cputype == CPU_VR4111 ||
current_cpu_data.cputype == CPU_VR4121) {
spin_lock_irqsave(&desc->lock, flags);
clear_icu1(MAIUINTREG, mask);
spin_unlock_irqrestore(&desc->lock, flags);
}
}
EXPORT_SYMBOL(vr41xx_disable_aiuint);
void vr41xx_enable_kiuint(uint16_t mask)
{
irq_desc_t *desc = irq_desc + KIU_IRQ;
unsigned long flags;
if (current_cpu_data.cputype == CPU_VR4111 ||
current_cpu_data.cputype == CPU_VR4121) {
spin_lock_irqsave(&desc->lock, flags);
set_icu1(MKIUINTREG, mask);
spin_unlock_irqrestore(&desc->lock, flags);
}
}
EXPORT_SYMBOL(vr41xx_enable_kiuint);
void vr41xx_disable_kiuint(uint16_t mask)
{
irq_desc_t *desc = irq_desc + KIU_IRQ;
unsigned long flags;
if (current_cpu_data.cputype == CPU_VR4111 ||
current_cpu_data.cputype == CPU_VR4121) {
spin_lock_irqsave(&desc->lock, flags);
clear_icu1(MKIUINTREG, mask);
spin_unlock_irqrestore(&desc->lock, flags);
}
}
EXPORT_SYMBOL(vr41xx_disable_kiuint);
void vr41xx_enable_dsiuint(uint16_t mask)
{
irq_desc_t *desc = irq_desc + DSIU_IRQ;
unsigned long flags;
spin_lock_irqsave(&desc->lock, flags);
set_icu1(MDSIUINTREG, mask);
spin_unlock_irqrestore(&desc->lock, flags);
}
EXPORT_SYMBOL(vr41xx_enable_dsiuint);
void vr41xx_disable_dsiuint(uint16_t mask)
{
irq_desc_t *desc = irq_desc + DSIU_IRQ;
unsigned long flags;
spin_lock_irqsave(&desc->lock, flags);
clear_icu1(MDSIUINTREG, mask);
spin_unlock_irqrestore(&desc->lock, flags);
}
EXPORT_SYMBOL(vr41xx_disable_dsiuint);
void vr41xx_enable_firint(uint16_t mask)
{
irq_desc_t *desc = irq_desc + FIR_IRQ;
unsigned long flags;
spin_lock_irqsave(&desc->lock, flags);
set_icu2(MFIRINTREG, mask);
spin_unlock_irqrestore(&desc->lock, flags);
}
EXPORT_SYMBOL(vr41xx_enable_firint);
void vr41xx_disable_firint(uint16_t mask)
{
irq_desc_t *desc = irq_desc + FIR_IRQ;
unsigned long flags;
spin_lock_irqsave(&desc->lock, flags);
clear_icu2(MFIRINTREG, mask);
spin_unlock_irqrestore(&desc->lock, flags);
}
EXPORT_SYMBOL(vr41xx_disable_firint);
void vr41xx_enable_pciint(void)
{
irq_desc_t *desc = irq_desc + PCI_IRQ;
unsigned long flags;
if (current_cpu_data.cputype == CPU_VR4122 ||
current_cpu_data.cputype == CPU_VR4131 ||
current_cpu_data.cputype == CPU_VR4133) {
spin_lock_irqsave(&desc->lock, flags);
write_icu2(PCIINT0, MPCIINTREG);
spin_unlock_irqrestore(&desc->lock, flags);
}
}
EXPORT_SYMBOL(vr41xx_enable_pciint);
void vr41xx_disable_pciint(void)
{
irq_desc_t *desc = irq_desc + PCI_IRQ;
unsigned long flags;
if (current_cpu_data.cputype == CPU_VR4122 ||
current_cpu_data.cputype == CPU_VR4131 ||
current_cpu_data.cputype == CPU_VR4133) {
spin_lock_irqsave(&desc->lock, flags);
write_icu2(0, MPCIINTREG);
spin_unlock_irqrestore(&desc->lock, flags);
}
}
EXPORT_SYMBOL(vr41xx_disable_pciint);
void vr41xx_enable_scuint(void)
{
irq_desc_t *desc = irq_desc + SCU_IRQ;
unsigned long flags;
if (current_cpu_data.cputype == CPU_VR4122 ||
current_cpu_data.cputype == CPU_VR4131 ||
current_cpu_data.cputype == CPU_VR4133) {
spin_lock_irqsave(&desc->lock, flags);
write_icu2(SCUINT0, MSCUINTREG);
spin_unlock_irqrestore(&desc->lock, flags);
}
}
EXPORT_SYMBOL(vr41xx_enable_scuint);
void vr41xx_disable_scuint(void)
{
irq_desc_t *desc = irq_desc + SCU_IRQ;
unsigned long flags;
if (current_cpu_data.cputype == CPU_VR4122 ||
current_cpu_data.cputype == CPU_VR4131 ||
current_cpu_data.cputype == CPU_VR4133) {
spin_lock_irqsave(&desc->lock, flags);
write_icu2(0, MSCUINTREG);
spin_unlock_irqrestore(&desc->lock, flags);
}
}
EXPORT_SYMBOL(vr41xx_disable_scuint);
void vr41xx_enable_csiint(uint16_t mask)
{
irq_desc_t *desc = irq_desc + CSI_IRQ;
unsigned long flags;
if (current_cpu_data.cputype == CPU_VR4122 ||
current_cpu_data.cputype == CPU_VR4131 ||
current_cpu_data.cputype == CPU_VR4133) {
spin_lock_irqsave(&desc->lock, flags);
set_icu2(MCSIINTREG, mask);
spin_unlock_irqrestore(&desc->lock, flags);
}
}
EXPORT_SYMBOL(vr41xx_enable_csiint);
void vr41xx_disable_csiint(uint16_t mask)
{
irq_desc_t *desc = irq_desc + CSI_IRQ;
unsigned long flags;
if (current_cpu_data.cputype == CPU_VR4122 ||
current_cpu_data.cputype == CPU_VR4131 ||
current_cpu_data.cputype == CPU_VR4133) {
spin_lock_irqsave(&desc->lock, flags);
clear_icu2(MCSIINTREG, mask);
spin_unlock_irqrestore(&desc->lock, flags);
}
}
EXPORT_SYMBOL(vr41xx_disable_csiint);
void vr41xx_enable_bcuint(void)
{
irq_desc_t *desc = irq_desc + BCU_IRQ;
unsigned long flags;
if (current_cpu_data.cputype == CPU_VR4122 ||
current_cpu_data.cputype == CPU_VR4131 ||
current_cpu_data.cputype == CPU_VR4133) {
spin_lock_irqsave(&desc->lock, flags);
write_icu2(BCUINTR, MBCUINTREG);
spin_unlock_irqrestore(&desc->lock, flags);
}
}
EXPORT_SYMBOL(vr41xx_enable_bcuint);
void vr41xx_disable_bcuint(void)
{
irq_desc_t *desc = irq_desc + BCU_IRQ;
unsigned long flags;
if (current_cpu_data.cputype == CPU_VR4122 ||
current_cpu_data.cputype == CPU_VR4131 ||
current_cpu_data.cputype == CPU_VR4133) {
spin_lock_irqsave(&desc->lock, flags);
write_icu2(0, MBCUINTREG);
spin_unlock_irqrestore(&desc->lock, flags);
}
}
EXPORT_SYMBOL(vr41xx_disable_bcuint);
/*=======================================================================*/
static unsigned int startup_sysint1_irq(unsigned int irq)
{
set_icu1(MSYSINT1REG, (uint16_t)1 << SYSINT1_IRQ_TO_PIN(irq));
return 0; /* never anything pending */
}
static void shutdown_sysint1_irq(unsigned int irq)
{
clear_icu1(MSYSINT1REG, (uint16_t)1 << SYSINT1_IRQ_TO_PIN(irq));
}
static void enable_sysint1_irq(unsigned int irq)
{
set_icu1(MSYSINT1REG, (uint16_t)1 << SYSINT1_IRQ_TO_PIN(irq));
}
#define disable_sysint1_irq shutdown_sysint1_irq
#define ack_sysint1_irq shutdown_sysint1_irq
static void end_sysint1_irq(unsigned int irq)
{
if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
set_icu1(MSYSINT1REG, (uint16_t)1 << SYSINT1_IRQ_TO_PIN(irq));
}
static struct hw_interrupt_type sysint1_irq_type = {
.typename = "SYSINT1",
.startup = startup_sysint1_irq,
.shutdown = shutdown_sysint1_irq,
.enable = enable_sysint1_irq,
.disable = disable_sysint1_irq,
.ack = ack_sysint1_irq,
.end = end_sysint1_irq,
};
/*=======================================================================*/
static unsigned int startup_sysint2_irq(unsigned int irq)
{
set_icu2(MSYSINT2REG, (uint16_t)1 << SYSINT2_IRQ_TO_PIN(irq));
return 0; /* never anything pending */
}
static void shutdown_sysint2_irq(unsigned int irq)
{
clear_icu2(MSYSINT2REG, (uint16_t)1 << SYSINT2_IRQ_TO_PIN(irq));
}
static void enable_sysint2_irq(unsigned int irq)
{
set_icu2(MSYSINT2REG, (uint16_t)1 << SYSINT2_IRQ_TO_PIN(irq));
}
#define disable_sysint2_irq shutdown_sysint2_irq
#define ack_sysint2_irq shutdown_sysint2_irq
static void end_sysint2_irq(unsigned int irq)
{
if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
set_icu2(MSYSINT2REG, (uint16_t)1 << SYSINT2_IRQ_TO_PIN(irq));
}
static struct hw_interrupt_type sysint2_irq_type = {
.typename = "SYSINT2",
.startup = startup_sysint2_irq,
.shutdown = shutdown_sysint2_irq,
.enable = enable_sysint2_irq,
.disable = disable_sysint2_irq,
.ack = ack_sysint2_irq,
.end = end_sysint2_irq,
};
/*=======================================================================*/
static inline int set_sysint1_assign(unsigned int irq, unsigned char assign)
{
irq_desc_t *desc = irq_desc + irq;
uint16_t intassign0, intassign1;
unsigned int pin;
pin = SYSINT1_IRQ_TO_PIN(irq);
spin_lock_irq(&desc->lock);
intassign0 = read_icu1(INTASSIGN0);
intassign1 = read_icu1(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:
return -EINVAL;
}
sysint1_assign[pin] = assign;
write_icu1(intassign0, INTASSIGN0);
write_icu1(intassign1, INTASSIGN1);
spin_unlock_irq(&desc->lock);
return 0;
}
static inline int set_sysint2_assign(unsigned int irq, unsigned char assign)
{
irq_desc_t *desc = irq_desc + irq;
uint16_t intassign2, intassign3;
unsigned int pin;
pin = SYSINT2_IRQ_TO_PIN(irq);
spin_lock_irq(&desc->lock);
intassign2 = read_icu1(INTASSIGN2);
intassign3 = read_icu1(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:
return -EINVAL;
}
sysint2_assign[pin] = assign;
write_icu1(intassign2, INTASSIGN2);
write_icu1(intassign3, INTASSIGN3);
spin_unlock_irq(&desc->lock);
return 0;
}
int vr41xx_set_intassign(unsigned int irq, unsigned char intassign)
{
int retval = -EINVAL;
if (current_cpu_data.cputype != 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);
/*=======================================================================*/
asmlinkage void irq_dispatch(unsigned char intnum, struct pt_regs *regs)
{
uint16_t pend1, pend2;
uint16_t mask1, mask2;
int i;
pend1 = read_icu1(SYSINT1REG);
mask1 = read_icu1(MSYSINT1REG);
pend2 = read_icu2(SYSINT2REG);
mask2 = read_icu2(MSYSINT2REG);
mask1 &= pend1;
mask2 &= pend2;
if (mask1) {
for (i = 0; i < 16; i++) {
if (intnum == sysint1_assign[i] &&
(mask1 & ((uint16_t)1 << i))) {
if (i == 8)
giuint_irq_dispatch(regs);
else
do_IRQ(SYSINT1_IRQ(i), regs);
return;
}
}
}
if (mask2) {
for (i = 0; i < 16; i++) {
if (intnum == sysint2_assign[i] &&
(mask2 & ((uint16_t)1 << i))) {
do_IRQ(SYSINT2_IRQ(i), regs);
return;
}
}
}
printk(KERN_ERR "spurious ICU interrupt: %04x,%04x\n", pend1, pend2);
atomic_inc(&irq_err_count);
}
/*=======================================================================*/
static int __init vr41xx_icu_init(void)
{
switch (current_cpu_data.cputype) {
case CPU_VR4111:
case CPU_VR4121:
icu1_base = SYSINT1REG_TYPE1;
icu2_base = SYSINT2REG_TYPE1;
break;
case CPU_VR4122:
case CPU_VR4131:
case CPU_VR4133:
icu1_base = SYSINT1REG_TYPE2;
icu2_base = SYSINT2REG_TYPE2;
break;
default:
printk(KERN_ERR "ICU: Unexpected CPU of NEC VR4100 series\n");
return -EINVAL;
}
write_icu1(0, MSYSINT1REG);
write_icu1(0xffff, MGIUINTLREG);
write_icu2(0, MSYSINT2REG);
write_icu2(0xffff, MGIUINTHREG);
return 0;
}
early_initcall(vr41xx_icu_init);
/*=======================================================================*/
static inline void init_vr41xx_icu_irq(void)
{
int i;
for (i = SYSINT1_IRQ_BASE; i <= SYSINT1_IRQ_LAST; i++)
irq_desc[i].handler = &sysint1_irq_type;
for (i = SYSINT2_IRQ_BASE; i <= SYSINT2_IRQ_LAST; i++)
irq_desc[i].handler = &sysint2_irq_type;
setup_irq(INT0_CASCADE_IRQ, &icu_cascade);
setup_irq(INT1_CASCADE_IRQ, &icu_cascade);
setup_irq(INT2_CASCADE_IRQ, &icu_cascade);
setup_irq(INT3_CASCADE_IRQ, &icu_cascade);
setup_irq(INT4_CASCADE_IRQ, &icu_cascade);
}
void __init arch_init_irq(void)
{
mips_cpu_irq_init(MIPS_CPU_IRQ_BASE);
init_vr41xx_icu_irq();
init_vr41xx_giuint_irq();
set_except_vector(0, vr41xx_handle_interrupt);
}

85
arch/mips/vr41xx/common/init.c Звичайний файл
Переглянути файл

@@ -0,0 +1,85 @@
/*
* init.c, Common initialization routines for NEC VR4100 series.
*
* 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
*/
#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/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;
}
static void __init setup_timer_frequency(void)
{
unsigned long tclock;
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;
}
static void __init setup_timer_irq(struct irqaction *irq)
{
setup_irq(TIMER_IRQ, irq);
}
static void __init timer_init(void)
{
board_time_init = setup_timer_frequency;
board_timer_setup = setup_timer_irq;
}
void __init prom_init(void)
{
int argc, i;
char **argv;
argc = fw_arg0;
argv = (char **)fw_arg1;
for (i = 1; i < argc; i++) {
strcat(arcs_cmdline, argv[i]);
if (i < (argc - 1))
strcat(arcs_cmdline, " ");
}
vr41xx_calculate_clock_frequency();
timer_init();
iomem_resource_init();
}
unsigned long __init prom_free_prom_memory (void)
{
return 0UL;
}

114
arch/mips/vr41xx/common/int-handler.S Звичайний файл
Переглянути файл

@@ -0,0 +1,114 @@
/*
* FILE NAME
* arch/mips/vr41xx/common/int-handler.S
*
* BRIEF MODULE DESCRIPTION
* Interrupt dispatcher for the NEC VR4100 series.
*
* Author: Yoichi Yuasa
* yyuasa@mvista.com or source@mvista.com
*
* Copyright 2001 MontaVista Software Inc.
*
* 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* 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.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* Changes:
* MontaVista Software Inc. <yyuasa@mvista.com> or <source@mvista.com>
* - New creation, NEC VR4100 series are supported.
*
* Yoichi Yuasa <yuasa@hh.iij4u.or.jp>
* - Coped with INTASSIGN of NEC VR4133.
*/
#include <asm/asm.h>
#include <asm/regdef.h>
#include <asm/mipsregs.h>
#include <asm/stackframe.h>
.text
.set noreorder
.align 5
NESTED(vr41xx_handle_interrupt, PT_SIZE, ra)
.set noat
SAVE_ALL
CLI
.set at
.set noreorder
/*
* Get the pending interrupts
*/
mfc0 t0, CP0_CAUSE
mfc0 t1, CP0_STATUS
andi t0, 0xff00
and t0, t0, t1
andi t1, t0, CAUSEF_IP7 # MIPS timer interrupt
bnez t1, handle_irq
li a0, 7
andi t1, t0, 0x7800 # check for Int1-4
beqz t1, 1f
andi t1, t0, CAUSEF_IP3 # check for Int1
bnez t1, handle_int
li a0, 1
andi t1, t0, CAUSEF_IP4 # check for Int2
bnez t1, handle_int
li a0, 2
andi t1, t0, CAUSEF_IP5 # check for Int3
bnez t1, handle_int
li a0, 3
andi t1, t0, CAUSEF_IP6 # check for Int4
bnez t1, handle_int
li a0, 4
1:
andi t1, t0, CAUSEF_IP2 # check for Int0
bnez t1, handle_int
li a0, 0
andi t1, t0, CAUSEF_IP0 # check for IP0
bnez t1, handle_irq
li a0, 0
andi t1, t0, CAUSEF_IP1 # check for IP1
bnez t1, handle_irq
li a0, 1
j spurious_interrupt
nop
handle_int:
jal irq_dispatch
move a1, sp
j ret_from_irq
nop
handle_irq:
jal do_IRQ
move a1, sp
j ret_from_irq
END(vr41xx_handle_interrupt)

81
arch/mips/vr41xx/common/pmu.c Звичайний файл
Переглянути файл

@@ -0,0 +1,81 @@
/*
* pmu.c, Power Management Unit routines for NEC VR4100 series.
*
* Copyright (C) 2003-2004 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/kernel.h>
#include <linux/smp.h>
#include <linux/types.h>
#include <asm/cpu.h>
#include <asm/io.h>
#include <asm/reboot.h>
#include <asm/system.h>
#define PMUCNT2REG KSEG1ADDR(0x0f0000c6)
#define SOFTRST 0x0010
static inline void software_reset(void)
{
uint16_t val;
switch (current_cpu_data.cputype) {
case CPU_VR4122:
case CPU_VR4131:
case CPU_VR4133:
val = readw(PMUCNT2REG);
val |= SOFTRST;
writew(val, PMUCNT2REG);
break;
default:
break;
}
}
static void vr41xx_restart(char *command)
{
local_irq_disable();
software_reset();
printk(KERN_NOTICE "\nYou can reset your system\n");
while (1) ;
}
static void vr41xx_halt(void)
{
local_irq_disable();
printk(KERN_NOTICE "\nYou can turn off the power supply\n");
while (1) ;
}
static void vr41xx_power_off(void)
{
local_irq_disable();
printk(KERN_NOTICE "\nYou can turn off the power supply\n");
while (1) ;
}
static int __init vr41xx_pmu_init(void)
{
_machine_restart = vr41xx_restart;
_machine_halt = vr41xx_halt;
_machine_power_off = vr41xx_power_off;
return 0;
}
early_initcall(vr41xx_pmu_init);

581
arch/mips/vr41xx/common/vrc4173.c Звичайний файл
Переглянути файл

@@ -0,0 +1,581 @@
/*
* vrc4173.c, NEC VRC4173 base driver for NEC VR4122/VR4131.
*
* Copyright (C) 2001-2003 MontaVista Software Inc.
* Author: Yoichi Yuasa <yyuasa@mvista.com, or source@mvista.com>
* Copyright (C) 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
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/pci.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <asm/vr41xx/vr41xx.h>
#include <asm/vr41xx/vrc4173.h>
MODULE_DESCRIPTION("NEC VRC4173 base driver for NEC VR4122/4131");
MODULE_AUTHOR("Yoichi Yuasa <yyuasa@mvista.com>");
MODULE_LICENSE("GPL");
#define VRC4173_CMUCLKMSK 0x040
#define MSKPIU 0x0001
#define MSKKIU 0x0002
#define MSKAIU 0x0004
#define MSKPS2CH1 0x0008
#define MSKPS2CH2 0x0010
#define MSKUSB 0x0020
#define MSKCARD1 0x0040
#define MSKCARD2 0x0080
#define MSKAC97 0x0100
#define MSK48MUSB 0x0400
#define MSK48MPIN 0x0800
#define MSK48MOSC 0x1000
#define VRC4173_CMUSRST 0x042
#define USBRST 0x0001
#define CARD1RST 0x0002
#define CARD2RST 0x0004
#define AC97RST 0x0008
#define VRC4173_SYSINT1REG 0x060
#define VRC4173_MSYSINT1REG 0x06c
#define VRC4173_MPIUINTREG 0x06e
#define VRC4173_MAIUINTREG 0x070
#define VRC4173_MKIUINTREG 0x072
#define VRC4173_SELECTREG 0x09e
#define SEL3 0x0008
#define SEL2 0x0004
#define SEL1 0x0002
#define SEL0 0x0001
static struct pci_device_id vrc4173_id_table[] __devinitdata = {
{ .vendor = PCI_VENDOR_ID_NEC,
.device = PCI_DEVICE_ID_NEC_VRC4173,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID, },
{ .vendor = 0, },
};
unsigned long vrc4173_io_offset = 0;
EXPORT_SYMBOL(vrc4173_io_offset);
static int vrc4173_initialized;
static uint16_t vrc4173_cmuclkmsk;
static uint16_t vrc4173_selectreg;
static spinlock_t vrc4173_cmu_lock;
static spinlock_t vrc4173_giu_lock;
static inline void set_cmusrst(uint16_t val)
{
uint16_t cmusrst;
cmusrst = vrc4173_inw(VRC4173_CMUSRST);
cmusrst |= val;
vrc4173_outw(cmusrst, VRC4173_CMUSRST);
}
static inline void clear_cmusrst(uint16_t val)
{
uint16_t cmusrst;
cmusrst = vrc4173_inw(VRC4173_CMUSRST);
cmusrst &= ~val;
vrc4173_outw(cmusrst, VRC4173_CMUSRST);
}
void vrc4173_supply_clock(vrc4173_clock_t clock)
{
if (vrc4173_initialized) {
spin_lock_irq(&vrc4173_cmu_lock);
switch (clock) {
case VRC4173_PIU_CLOCK:
vrc4173_cmuclkmsk |= MSKPIU;
break;
case VRC4173_KIU_CLOCK:
vrc4173_cmuclkmsk |= MSKKIU;
break;
case VRC4173_AIU_CLOCK:
vrc4173_cmuclkmsk |= MSKAIU;
break;
case VRC4173_PS2_CH1_CLOCK:
vrc4173_cmuclkmsk |= MSKPS2CH1;
break;
case VRC4173_PS2_CH2_CLOCK:
vrc4173_cmuclkmsk |= MSKPS2CH2;
break;
case VRC4173_USBU_PCI_CLOCK:
set_cmusrst(USBRST);
vrc4173_cmuclkmsk |= MSKUSB;
break;
case VRC4173_CARDU1_PCI_CLOCK:
set_cmusrst(CARD1RST);
vrc4173_cmuclkmsk |= MSKCARD1;
break;
case VRC4173_CARDU2_PCI_CLOCK:
set_cmusrst(CARD2RST);
vrc4173_cmuclkmsk |= MSKCARD2;
break;
case VRC4173_AC97U_PCI_CLOCK:
set_cmusrst(AC97RST);
vrc4173_cmuclkmsk |= MSKAC97;
break;
case VRC4173_USBU_48MHz_CLOCK:
set_cmusrst(USBRST);
vrc4173_cmuclkmsk |= MSK48MUSB;
break;
case VRC4173_EXT_48MHz_CLOCK:
if (vrc4173_cmuclkmsk & MSK48MOSC)
vrc4173_cmuclkmsk |= MSK48MPIN;
else
printk(KERN_WARNING
"vrc4173_supply_clock: "
"Please supply VRC4173_48MHz_CLOCK first "
"rather than VRC4173_EXT_48MHz_CLOCK.\n");
break;
case VRC4173_48MHz_CLOCK:
vrc4173_cmuclkmsk |= MSK48MOSC;
break;
default:
printk(KERN_WARNING
"vrc4173_supply_clock: Invalid CLOCK value %u\n", clock);
break;
}
vrc4173_outw(vrc4173_cmuclkmsk, VRC4173_CMUCLKMSK);
switch (clock) {
case VRC4173_USBU_PCI_CLOCK:
case VRC4173_USBU_48MHz_CLOCK:
clear_cmusrst(USBRST);
break;
case VRC4173_CARDU1_PCI_CLOCK:
clear_cmusrst(CARD1RST);
break;
case VRC4173_CARDU2_PCI_CLOCK:
clear_cmusrst(CARD2RST);
break;
case VRC4173_AC97U_PCI_CLOCK:
clear_cmusrst(AC97RST);
break;
default:
break;
}
spin_unlock_irq(&vrc4173_cmu_lock);
}
}
EXPORT_SYMBOL(vrc4173_supply_clock);
void vrc4173_mask_clock(vrc4173_clock_t clock)
{
if (vrc4173_initialized) {
spin_lock_irq(&vrc4173_cmu_lock);
switch (clock) {
case VRC4173_PIU_CLOCK:
vrc4173_cmuclkmsk &= ~MSKPIU;
break;
case VRC4173_KIU_CLOCK:
vrc4173_cmuclkmsk &= ~MSKKIU;
break;
case VRC4173_AIU_CLOCK:
vrc4173_cmuclkmsk &= ~MSKAIU;
break;
case VRC4173_PS2_CH1_CLOCK:
vrc4173_cmuclkmsk &= ~MSKPS2CH1;
break;
case VRC4173_PS2_CH2_CLOCK:
vrc4173_cmuclkmsk &= ~MSKPS2CH2;
break;
case VRC4173_USBU_PCI_CLOCK:
set_cmusrst(USBRST);
vrc4173_cmuclkmsk &= ~MSKUSB;
break;
case VRC4173_CARDU1_PCI_CLOCK:
set_cmusrst(CARD1RST);
vrc4173_cmuclkmsk &= ~MSKCARD1;
break;
case VRC4173_CARDU2_PCI_CLOCK:
set_cmusrst(CARD2RST);
vrc4173_cmuclkmsk &= ~MSKCARD2;
break;
case VRC4173_AC97U_PCI_CLOCK:
set_cmusrst(AC97RST);
vrc4173_cmuclkmsk &= ~MSKAC97;
break;
case VRC4173_USBU_48MHz_CLOCK:
set_cmusrst(USBRST);
vrc4173_cmuclkmsk &= ~MSK48MUSB;
break;
case VRC4173_EXT_48MHz_CLOCK:
vrc4173_cmuclkmsk &= ~MSK48MPIN;
break;
case VRC4173_48MHz_CLOCK:
vrc4173_cmuclkmsk &= ~MSK48MOSC;
break;
default:
printk(KERN_WARNING "vrc4173_mask_clock: Invalid CLOCK value %u\n", clock);
break;
}
vrc4173_outw(vrc4173_cmuclkmsk, VRC4173_CMUCLKMSK);
switch (clock) {
case VRC4173_USBU_PCI_CLOCK:
case VRC4173_USBU_48MHz_CLOCK:
clear_cmusrst(USBRST);
break;
case VRC4173_CARDU1_PCI_CLOCK:
clear_cmusrst(CARD1RST);
break;
case VRC4173_CARDU2_PCI_CLOCK:
clear_cmusrst(CARD2RST);
break;
case VRC4173_AC97U_PCI_CLOCK:
clear_cmusrst(AC97RST);
break;
default:
break;
}
spin_unlock_irq(&vrc4173_cmu_lock);
}
}
EXPORT_SYMBOL(vrc4173_mask_clock);
static inline void vrc4173_cmu_init(void)
{
vrc4173_cmuclkmsk = vrc4173_inw(VRC4173_CMUCLKMSK);
spin_lock_init(&vrc4173_cmu_lock);
}
void vrc4173_select_function(vrc4173_function_t function)
{
if (vrc4173_initialized) {
spin_lock_irq(&vrc4173_giu_lock);
switch(function) {
case PS2_CHANNEL1:
vrc4173_selectreg |= SEL2;
break;
case PS2_CHANNEL2:
vrc4173_selectreg |= SEL1;
break;
case TOUCHPANEL:
vrc4173_selectreg &= SEL2 | SEL1 | SEL0;
break;
case KEYBOARD_8SCANLINES:
vrc4173_selectreg &= SEL3 | SEL2 | SEL1;
break;
case KEYBOARD_10SCANLINES:
vrc4173_selectreg &= SEL3 | SEL2;
break;
case KEYBOARD_12SCANLINES:
vrc4173_selectreg &= SEL3;
break;
case GPIO_0_15PINS:
vrc4173_selectreg |= SEL0;
break;
case GPIO_16_20PINS:
vrc4173_selectreg |= SEL3;
break;
}
vrc4173_outw(vrc4173_selectreg, VRC4173_SELECTREG);
spin_unlock_irq(&vrc4173_giu_lock);
}
}
EXPORT_SYMBOL(vrc4173_select_function);
static inline void vrc4173_giu_init(void)
{
vrc4173_selectreg = vrc4173_inw(VRC4173_SELECTREG);
spin_lock_init(&vrc4173_giu_lock);
}
void vrc4173_enable_piuint(uint16_t mask)
{
irq_desc_t *desc = irq_desc + VRC4173_PIU_IRQ;
unsigned long flags;
uint16_t val;
spin_lock_irqsave(&desc->lock, flags);
val = vrc4173_inw(VRC4173_MPIUINTREG);
val |= mask;
vrc4173_outw(val, VRC4173_MPIUINTREG);
spin_unlock_irqrestore(&desc->lock, flags);
}
EXPORT_SYMBOL(vrc4173_enable_piuint);
void vrc4173_disable_piuint(uint16_t mask)
{
irq_desc_t *desc = irq_desc + VRC4173_PIU_IRQ;
unsigned long flags;
uint16_t val;
spin_lock_irqsave(&desc->lock, flags);
val = vrc4173_inw(VRC4173_MPIUINTREG);
val &= ~mask;
vrc4173_outw(val, VRC4173_MPIUINTREG);
spin_unlock_irqrestore(&desc->lock, flags);
}
EXPORT_SYMBOL(vrc4173_disable_piuint);
void vrc4173_enable_aiuint(uint16_t mask)
{
irq_desc_t *desc = irq_desc + VRC4173_AIU_IRQ;
unsigned long flags;
uint16_t val;
spin_lock_irqsave(&desc->lock, flags);
val = vrc4173_inw(VRC4173_MAIUINTREG);
val |= mask;
vrc4173_outw(val, VRC4173_MAIUINTREG);
spin_unlock_irqrestore(&desc->lock, flags);
}
EXPORT_SYMBOL(vrc4173_enable_aiuint);
void vrc4173_disable_aiuint(uint16_t mask)
{
irq_desc_t *desc = irq_desc + VRC4173_AIU_IRQ;
unsigned long flags;
uint16_t val;
spin_lock_irqsave(&desc->lock, flags);
val = vrc4173_inw(VRC4173_MAIUINTREG);
val &= ~mask;
vrc4173_outw(val, VRC4173_MAIUINTREG);
spin_unlock_irqrestore(&desc->lock, flags);
}
EXPORT_SYMBOL(vrc4173_disable_aiuint);
void vrc4173_enable_kiuint(uint16_t mask)
{
irq_desc_t *desc = irq_desc + VRC4173_KIU_IRQ;
unsigned long flags;
uint16_t val;
spin_lock_irqsave(&desc->lock, flags);
val = vrc4173_inw(VRC4173_MKIUINTREG);
val |= mask;
vrc4173_outw(val, VRC4173_MKIUINTREG);
spin_unlock_irqrestore(&desc->lock, flags);
}
EXPORT_SYMBOL(vrc4173_enable_kiuint);
void vrc4173_disable_kiuint(uint16_t mask)
{
irq_desc_t *desc = irq_desc + VRC4173_KIU_IRQ;
unsigned long flags;
uint16_t val;
spin_lock_irqsave(&desc->lock, flags);
val = vrc4173_inw(VRC4173_MKIUINTREG);
val &= ~mask;
vrc4173_outw(val, VRC4173_MKIUINTREG);
spin_unlock_irqrestore(&desc->lock, flags);
}
EXPORT_SYMBOL(vrc4173_disable_kiuint);
static void enable_vrc4173_irq(unsigned int irq)
{
uint16_t val;
val = vrc4173_inw(VRC4173_MSYSINT1REG);
val |= (uint16_t)1 << (irq - VRC4173_IRQ_BASE);
vrc4173_outw(val, VRC4173_MSYSINT1REG);
}
static void disable_vrc4173_irq(unsigned int irq)
{
uint16_t val;
val = vrc4173_inw(VRC4173_MSYSINT1REG);
val &= ~((uint16_t)1 << (irq - VRC4173_IRQ_BASE));
vrc4173_outw(val, VRC4173_MSYSINT1REG);
}
static unsigned int startup_vrc4173_irq(unsigned int irq)
{
enable_vrc4173_irq(irq);
return 0; /* never anything pending */
}
#define shutdown_vrc4173_irq disable_vrc4173_irq
#define ack_vrc4173_irq disable_vrc4173_irq
static void end_vrc4173_irq(unsigned int irq)
{
if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
enable_vrc4173_irq(irq);
}
static struct hw_interrupt_type vrc4173_irq_type = {
.typename = "VRC4173",
.startup = startup_vrc4173_irq,
.shutdown = shutdown_vrc4173_irq,
.enable = enable_vrc4173_irq,
.disable = disable_vrc4173_irq,
.ack = ack_vrc4173_irq,
.end = end_vrc4173_irq,
};
static int vrc4173_get_irq_number(int irq)
{
uint16_t status, mask;
int i;
status = vrc4173_inw(VRC4173_SYSINT1REG);
mask = vrc4173_inw(VRC4173_MSYSINT1REG);
status &= mask;
if (status) {
for (i = 0; i < 16; i++)
if (status & (0x0001 << i))
return VRC4173_IRQ(i);
}
return -EINVAL;
}
static inline int vrc4173_icu_init(int cascade_irq)
{
int i;
if (cascade_irq < GIU_IRQ(0) || cascade_irq > GIU_IRQ(15))
return -EINVAL;
vrc4173_outw(0, VRC4173_MSYSINT1REG);
vr41xx_set_irq_trigger(GIU_IRQ_TO_PIN(cascade_irq), TRIGGER_LEVEL, SIGNAL_THROUGH);
vr41xx_set_irq_level(GIU_IRQ_TO_PIN(cascade_irq), LEVEL_LOW);
for (i = VRC4173_IRQ_BASE; i <= VRC4173_IRQ_LAST; i++)
irq_desc[i].handler = &vrc4173_irq_type;
return 0;
}
static int __devinit vrc4173_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
unsigned long start, flags;
int err;
err = pci_enable_device(dev);
if (err < 0) {
printk(KERN_ERR "vrc4173: Failed to enable PCI device, aborting\n");
return err;
}
pci_set_master(dev);
start = pci_resource_start(dev, 0);
if (start == 0) {
printk(KERN_ERR "vrc4173:No such PCI I/O resource, aborting\n");
return -ENXIO;
}
flags = pci_resource_flags(dev, 0);
if ((flags & IORESOURCE_IO) == 0) {
printk(KERN_ERR "vrc4173: No such PCI I/O resource, aborting\n");
return -ENXIO;
}
err = pci_request_regions(dev, "NEC VRC4173");
if (err < 0) {
printk(KERN_ERR "vrc4173: PCI resources are busy, aborting\n");
return err;
}
set_vrc4173_io_offset(start);
vrc4173_cmu_init();
vrc4173_giu_init();
err = vrc4173_icu_init(dev->irq);
if (err < 0) {
printk(KERN_ERR "vrc4173: Invalid IRQ %d, aborting\n", dev->irq);
return err;
}
err = vr41xx_cascade_irq(dev->irq, vrc4173_get_irq_number);
if (err < 0) {
printk(KERN_ERR "vrc4173: IRQ resource %d is busy, aborting\n", dev->irq);
return err;
}
printk(KERN_INFO
"NEC VRC4173 at 0x%#08lx, IRQ is cascaded to %d\n", start, dev->irq);
return 0;
}
static void vrc4173_remove(struct pci_dev *dev)
{
free_irq(dev->irq, NULL);
pci_release_regions(dev);
}
static struct pci_driver vrc4173_driver = {
.name = "NEC VRC4173",
.probe = vrc4173_probe,
.remove = vrc4173_remove,
.id_table = vrc4173_id_table,
};
static int __devinit vrc4173_init(void)
{
int err;
err = pci_module_init(&vrc4173_driver);
if (err < 0)
return err;
vrc4173_initialized = 1;
return 0;
}
static void __devexit vrc4173_exit(void)
{
vrc4173_initialized = 0;
pci_unregister_driver(&vrc4173_driver);
}
module_init(vrc4173_init);
module_exit(vrc4173_exit);