|
From: James S. <jsi...@us...> - 2001-08-24 19:01:29
|
Update of /cvsroot/linux-mips/linux/arch/mips64/sgi-ip32
In directory usw-pr-cvs1:/tmp/cvs-serv19814
Added Files:
Makefile crime.c ip32-berr.c ip32-irq-glue.S ip32-irq.c
ip32-pci-dma.c ip32-pci.c ip32-rtc.c ip32-setup.c ip32-timer.c
Log Message:
Added sgi ip32 platform.
--- NEW FILE: Makefile ---
#
# Makefile for the SGI specific kernel interface routines
# under Linux.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (ie not a .c file).
#
# Note 2! The CFLAGS definitions are now in the main makefile...
.S.s:
$(CPP) $(CFLAGS) $< -o $*.s
.S.o:
$(CC) $(CFLAGS) -c $< -o $*.o
O_TARGET := ip32-kern.a
all: ip32-kern.a ip32-irq-glue.o
obj-y += ip32-irq.o ip32-rtc.o ip32-setup.o ip32-irq-glue.o \
ip32-berr.o ip32-timer.o crime.o
ifdef CONFIG_PCI
obj-y += ip32-pci.o ip32-pci-dma.o
endif
include $(TOPDIR)/Rules.make
--- NEW FILE: crime.c ---
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2001 Keith M Wesolowski
*/
#include <linux/types.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <asm/ip32/crime.h>
#include <asm/ptrace.h>
#include <asm/promlib.h>
void __init crime_init (void)
{
u64 id = crime_read_64 (CRIME_ID);
u64 rev = id & CRIME_ID_REV;
id = (id & CRIME_ID_IDBITS) >> 4;
printk ("CRIME id %1lx rev %ld detected at %016lx\n", id, rev,
(unsigned long) CRIME_BASE);
}
/* XXX Like on Sun, these give us various useful information to printk. */
void crime_memerr_intr (unsigned int irq, void *dev_id, struct pt_regs *regs)
{
u64 memerr = crime_read_64 (CRIME_MEM_ERROR_STAT);
u64 addr = crime_read_64 (CRIME_MEM_ERROR_ADDR);
memerr &= CRIME_MEM_ERROR_STAT_MASK;
printk ("CRIME memory error at physaddr 0x%08lx status %08lx\n",
addr << 2, memerr);
crime_write_64 (CRIME_MEM_ERROR_STAT, 0);
}
void crime_cpuerr_intr (unsigned int irq, void *dev_id, struct pt_regs *regs)
{
u64 cpuerr = crime_read_64 (CRIME_CPU_ERROR_STAT);
u64 addr = crime_read_64 (CRIME_CPU_ERROR_ADDR);
cpuerr &= CRIME_CPU_ERROR_MASK;
addr <<= 2UL;
printk ("CRIME CPU interface error detected at %09lx status %08lx\n",
addr, cpuerr);
crime_write_64 (CRIME_CPU_ERROR_STAT, 0);
}
--- NEW FILE: ip32-berr.c ---
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 1994, 1995, 1996, 1999, 2000 by Ralf Baechle
* Copyright (C) 1999, 2000 by Silicon Graphics
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <asm/uaccess.h>
#include <asm/paccess.h>
#include <asm/addrspace.h>
#include <asm/ptrace.h>
/* XXX I have no idea what this does --kmw */
extern asmlinkage void handle_ibe(void);
extern asmlinkage void handle_dbe(void);
extern const struct exception_table_entry __start___dbe_table[];
extern const struct exception_table_entry __stop___dbe_table[];
static inline unsigned long
search_one_table(const struct exception_table_entry *first,
const struct exception_table_entry *last,
unsigned long value)
{
while (first <= last) {
const struct exception_table_entry *mid;
long diff;
mid = (last - first) / 2 + first;
diff = mid->insn - value;
if (diff == 0)
return mid->nextinsn;
else if (diff < 0)
first = mid+1;
else
last = mid-1;
}
return 0;
}
static inline unsigned long
search_dbe_table(unsigned long addr)
{
unsigned long ret;
/* There is only the kernel to search. */
ret = search_one_table(__start___dbe_table, __stop___dbe_table-1, addr);
if (ret) return ret;
return 0;
}
void do_ibe(struct pt_regs *regs)
{
printk("Got ibe at 0x%lx\n", regs->cp0_epc);
show_regs(regs);
dump_tlb_addr(regs->cp0_epc);
force_sig(SIGBUS, current);
while(1);
}
void do_dbe(struct pt_regs *regs)
{
unsigned long fixup;
fixup = search_dbe_table(regs->cp0_epc);
if (fixup) {
long new_epc;
new_epc = fixup_exception(dpf_reg, fixup, regs->cp0_epc);
regs->cp0_epc = new_epc;
return;
}
printk("Got dbe at 0x%lx\n", regs->cp0_epc);
show_regs(regs);
dump_tlb_all();
while(1);
force_sig(SIGBUS, current);
}
void __init
bus_error_init(void)
{
int dummy;
set_except_vector(6, handle_ibe);
set_except_vector(7, handle_dbe);
/* At this time nothing uses the DBE protection mechanism on the
O2, so this here is needed to make the kernel link. */
get_dbe(dummy, (int *)KSEG0);
}
--- NEW FILE: ip32-irq-glue.S ---
/*
* Low level interrupt handler for the SGI O2 aka IP32 aka Moosehead
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2000 Harald Koerfgen
* Copyright (C) 2001 Keith M Wesolowski
*/
#include <asm/asm.h>
#include <asm/regdef.h>
#include <asm/mipsregs.h>
#include <asm/stackframe.h>
#include <asm/addrspace.h>
#include <asm/ip32/ip32_ints.h>
.text
.set noreorder
.set noat
.align 5
NESTED(ip32_handle_int, PT_SIZE, ra)
.set noat
SAVE_ALL
CLI # TEST: interrupts should be off
.set at
.set noreorder
mfc0 s0,CP0_CAUSE
andi t1, s0, IE_IRQ0
bnez t1, handle_irq0
andi t1, s0, IE_IRQ1
bnez t1, handle_irq1
andi t1, s0, IE_IRQ2
bnez t1, handle_irq2
andi t1, s0, IE_IRQ3
bnez t1, handle_irq3
andi t1, s0, IE_IRQ4
bnez t1, handle_irq4
andi t1, s0, IE_IRQ5
bnez t1, handle_irq5
nop
/* Either someone has triggered the "software interrupts"
* or we lost an interrupt somehow. Ignore it.
*/
j ret_from_irq
nop
handle_irq0:
jal ip32_irq0
move a0, sp
j ret_from_irq
nop
handle_irq1:
jal ip32_irq1
move a0, sp
j ret_from_irq
nop
handle_irq2:
jal ip32_irq2
move a0, sp
j ret_from_irq
nop
handle_irq3:
jal ip32_irq3
move a0, sp
j ret_from_irq
nop
handle_irq4:
jal ip32_irq4
move a0, sp
j ret_from_irq
nop
handle_irq5:
jal ip32_irq5
move a0, sp
j ret_from_irq
nop
END(ip32_handle_int)
--- NEW FILE: ip32-irq.c ---
/*
* Code to handle IP32 IRQs
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2000 Harald Koerfgen
* Copyright (C) 2001 Keith M Wesolowski
*/
#include <linux/init.h>
#include <linux/kernel_stat.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/bitops.h>
#include <asm/bitops.h>
#include <asm/mipsregs.h>
#include <asm/system.h>
#include <asm/ip32/ip32_ints.h>
#include <asm/ip32/crime.h>
#include <asm/ip32/mace.h>
#include <asm/signal.h>
#undef DEBUG_IRQ
#ifdef DEBUG_IRQ
#define DBG(x...) printk(x)
#else
#define DBG(x...)
#endif
/* O2 irq map
*
* IP0 -> software (ignored)
* IP1 -> software (ignored)
* IP2 -> (irq0) C crime 1.1 all interrupts; crime 1.5 ???
* IP3 -> (irq1) X unknown
* IP4 -> (irq2) X unknown
* IP5 -> (irq3) X unknown
* IP6 -> (irq4) X unknown
* IP7 -> (irq5) 0 CPU count/compare timer (system timer)
*
* crime: (C)
*
* CRIME_INT_STAT 31:0:
*
* 0 -> 1 Video in 1
* 1 -> 2 Video in 2
* 2 -> 3 Video out
* 3 -> 4 Mace ethernet
* 4 -> S SuperIO sub-interrupt
* 5 -> M Miscellaneous sub-interrupt
* 6 -> A Audio sub-interrupt
* 7 -> 8 PCI bridge errors
* 8 -> 9 PCI SCSI aic7xxx 0
* 9 -> 10 PCI SCSI aic7xxx 1
* 10 -> 11 PCI slot 0
* 11 -> 12 unused (PCI slot 1)
* 12 -> 13 unused (PCI slot 2)
* 13 -> 14 unused (PCI shared 0)
* 14 -> 15 unused (PCI shared 1)
* 15 -> 16 unused (PCI shared 2)
* 16 -> 17 GBE0 (E)
* 17 -> 18 GBE1 (E)
* 18 -> 19 GBE2 (E)
* 19 -> 20 GBE3 (E)
* 20 -> 21 CPU errors
* 21 -> 22 Memory errors
* 22 -> 23 RE empty edge (E)
* 23 -> 24 RE full edge (E)
* 24 -> 25 RE idle edge (E)
* 25 -> 26 RE empty level
* 26 -> 27 RE full level
* 27 -> 28 RE idle level
* 28 -> 29 unused (software 0) (E)
* 29 -> 30 unused (software 1) (E)
* 30 -> 31 unused (software 2) - crime 1.5 CPU SysCorError (E)
* 31 -> 32 VICE
*
* S, M, A: Use the MACE ISA interrupt register
* MACE_ISA_INT_STAT 31:0
*
* 0-7 -> 33-40 Audio
* 8 -> 41 RTC
* 9 -> 42 Keyboard
* 10 -> X Keyboard polled
* 11 -> 44 Mouse
* 12 -> X Mouse polled
* 13-15 -> 46-48 Count/compare timers
* 16-19 -> 49-52 Parallel (16 E)
* 20-25 -> 53-58 Serial 1 (22 E)
* 26-31 -> 59-64 Serial 2 (28 E)
*
* Note that this means IRQs 5-7, 43, and 45 do not exist. This is a
* different IRQ map than IRIX uses, but that's OK as Linux irq handling
* is quite different anyway.
*/
/* Some initial interrupts to set up */
extern void crime_memerr_intr (unsigned int irq, void *dev_id,
struct pt_regs *regs);
extern void crime_cpuerr_intr (unsigned int irq, void *dev_id,
struct pt_regs *regs);
struct irqaction memerr_irq = { crime_memerr_intr, SA_INTERRUPT,
0, "CRIME memory error", NULL,
NULL };
struct irqaction cpuerr_irq = { crime_cpuerr_intr, SA_INTERRUPT,
0, "CRIME CPU error", NULL,
NULL };
unsigned long spurious_count = 0;
extern void ip32_handle_int (void);
extern void do_IRQ (unsigned int irq, struct pt_regs *regs);
/* For interrupts wired from a single device to the CPU. Only the clock
* uses this it seems, which is IRQ 0 and IP7.
*/
static void enable_cpu_irq (unsigned int irq)
{
set_cp0_status (STATUSF_IP7);
}
static unsigned int startup_cpu_irq (unsigned int irq) {
enable_cpu_irq (irq);
return 0;
}
static void disable_cpu_irq (unsigned int irq)
{
clear_cp0_status (STATUSF_IP7);
}
static void end_cpu_irq (unsigned int irq)
{
if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
enable_cpu_irq (irq);
}
#define shutdown_cpu_irq disable_cpu_irq
#define mask_and_ack_cpu_irq disable_cpu_irq
static struct hw_interrupt_type ip32_cpu_interrupt = {
"IP32 CPU",
startup_cpu_irq,
shutdown_cpu_irq,
enable_cpu_irq,
disable_cpu_irq,
mask_and_ack_cpu_irq,
end_cpu_irq,
NULL
};
/*
* This is for pure CRIME interrupts - ie not MACE. The advantage?
* We get to split the register in half and do faster lookups.
*/
static void enable_crime_irq (unsigned int irq)
{
u64 crime_mask;
unsigned long flags;
save_and_cli (flags);
crime_mask = crime_read_64 (CRIME_INT_MASK);
crime_mask |= 1 << (irq - 1);
crime_write_64 (CRIME_INT_MASK, crime_mask);
restore_flags (flags);
}
static unsigned int startup_crime_irq (unsigned int irq)
{
enable_crime_irq (irq);
return 0; /* This is probably not right; we could have pending irqs */
}
static void disable_crime_irq (unsigned int irq)
{
u64 crime_mask;
unsigned long flags;
save_and_cli (flags);
crime_mask = crime_read_64 (CRIME_INT_MASK);
crime_mask &= ~(1 << (irq - 1));
crime_write_64 (CRIME_INT_MASK, crime_mask);
restore_flags (flags);
}
static void mask_and_ack_crime_irq (unsigned int irq)
{
u64 crime_mask;
unsigned long flags;
/* Edge triggered interrupts must be cleared. */
if ((irq <= CRIME_GBE0_IRQ && irq >= CRIME_GBE3_IRQ)
|| (irq <= CRIME_RE_EMPTY_E_IRQ && irq >= CRIME_RE_IDLE_E_IRQ)
|| (irq <= CRIME_SOFT0_IRQ && irq >= CRIME_SOFT2_IRQ)) {
save_and_cli (flags);
crime_mask = crime_read_64 (CRIME_HARD_INT);
crime_mask &= ~(1 << (irq - 1));
crime_write_64 (CRIME_HARD_INT, crime_mask);
restore_flags (flags);
}
disable_crime_irq (irq);
}
static void end_crime_irq (unsigned int irq)
{
if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
enable_crime_irq (irq);
}
#define shutdown_crime_irq disable_crime_irq
static struct hw_interrupt_type ip32_crime_interrupt = {
"IP32 CRIME",
startup_crime_irq,
shutdown_crime_irq,
enable_crime_irq,
disable_crime_irq,
mask_and_ack_crime_irq,
end_crime_irq,
NULL
};
/* This is for MACE PCI interrupts. We can decrease bus traffic by masking
* as close to the source as possible. This also means we can take the
* next chunk of the CRIME register in one piece.
*/
static void enable_macepci_irq (unsigned int irq)
{
u32 mace_mask;
u64 crime_mask;
unsigned long flags;
save_and_cli (flags);
mace_mask = mace_read_32 (MACEPCI_CONTROL);
mace_mask |= MACEPCI_CONTROL_INT (irq - 9);
mace_write_32 (MACEPCI_CONTROL, mace_mask);
/* In case the CRIME interrupt isn't enabled, we must enable it;
* however, we never disable interrupts at that level.
*/
crime_mask = crime_read_64 (CRIME_INT_MASK);
crime_mask |= 1 << (irq - 1);
crime_write_64 (CRIME_INT_MASK, crime_mask);
restore_flags (flags);
}
static unsigned int startup_macepci_irq (unsigned int irq) {
enable_macepci_irq (irq);
return 0; /* XXX */
}
static void disable_macepci_irq (unsigned int irq)
{
u32 mace_mask;
unsigned long flags;
save_and_cli (flags);
mace_mask = mace_read_32 (MACEPCI_CONTROL);
mace_mask &= ~MACEPCI_CONTROL_INT (irq - 9);
mace_write_32 (MACEPCI_CONTROL, mace_mask);
restore_flags (flags);
}
static void end_macepci_irq (unsigned int irq)
{
if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
enable_macepci_irq (irq);
}
#define shutdown_macepci_irq disable_macepci_irq
#define mask_and_ack_macepci_irq disable_macepci_irq
static struct hw_interrupt_type ip32_macepci_interrupt = {
"IP32 MACE PCI",
startup_macepci_irq,
shutdown_macepci_irq,
enable_macepci_irq,
disable_macepci_irq,
mask_and_ack_macepci_irq,
end_macepci_irq,
NULL
};
/* This is used for MACE ISA interrupts. That means bits 4-6 in the
* CRIME register.
*/
static void enable_maceisa_irq (unsigned int irq)
{
u64 crime_mask;
u32 mace_mask;
unsigned int crime_int = 0;
unsigned long flags;
DBG ("maceisa enable: %u\n", irq);
switch (irq) {
case MACEISA_AUDIO_SW_IRQ ... MACEISA_AUDIO3_MERR_IRQ:
crime_int = MACE_AUDIO_INT;
break;
case MACEISA_RTC_IRQ ... MACEISA_TIMER2_IRQ:
crime_int = MACE_MISC_INT;
break;
case MACEISA_PARALLEL_IRQ ... MACEISA_SERIAL2_RDMAOR_IRQ:
crime_int = MACE_SUPERIO_INT;
break;
}
DBG ("crime_int %016lx enabled\n", crime_int);
save_and_cli (flags);
crime_mask = crime_read_64 (CRIME_INT_MASK);
crime_mask |= crime_int;
crime_write_64 (CRIME_INT_MASK, crime_mask);
mace_mask = mace_read_32 (MACEISA_INT_MASK);
mace_mask |= 1 << (irq - 33);
mace_write_32 (MACEISA_INT_MASK, mace_mask);
restore_flags (flags);
}
static unsigned int startup_maceisa_irq (unsigned int irq) {
enable_maceisa_irq (irq);
return 0;
}
static void disable_maceisa_irq (unsigned int irq)
{
u32 mace_mask;
unsigned long flags;
save_and_cli (flags);
mace_mask = mace_read_32 (MACEISA_INT_MASK);
mace_mask &= ~(1 << (irq - 33));
mace_write_32 (MACEISA_INT_MASK, mace_mask);
restore_flags (flags);
}
static void mask_and_ack_maceisa_irq (unsigned int irq)
{
u32 mace_mask;
unsigned long flags;
switch (irq) {
case MACEISA_PARALLEL_IRQ:
case MACEISA_SERIAL1_TDMAPR_IRQ:
case MACEISA_SERIAL2_TDMAPR_IRQ:
save_and_cli (flags);
mace_mask = mace_read_32 (MACEISA_INT_STAT);
mace_mask &= ~(1 << (irq - 33));
mace_write_32 (MACEISA_INT_STAT, mace_mask);
restore_flags (flags);
break;
}
disable_maceisa_irq (irq);
}
static void end_maceisa_irq (unsigned irq)
{
if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
enable_maceisa_irq (irq);
}
#define shutdown_maceisa_irq disable_maceisa_irq
static struct hw_interrupt_type ip32_maceisa_interrupt = {
"IP32 MACE ISA",
startup_maceisa_irq,
shutdown_maceisa_irq,
enable_maceisa_irq,
disable_maceisa_irq,
mask_and_ack_maceisa_irq,
end_maceisa_irq,
NULL
};
/* This is used for regular non-ISA, non-PCI MACE interrupts. That means
* bits 0-3 and 7 in the CRIME register.
*/
static void enable_mace_irq (unsigned int irq)
{
u64 crime_mask;
unsigned long flags;
save_and_cli (flags);
crime_mask = crime_read_64 (CRIME_INT_MASK);
crime_mask |= 1 << (irq - 1);
crime_write_64 (CRIME_INT_MASK, crime_mask);
restore_flags (flags);
}
static unsigned int startup_mace_irq (unsigned int irq)
{
enable_mace_irq (irq);
return 0;
}
static void disable_mace_irq (unsigned int irq)
{
u64 crime_mask;
unsigned long flags;
save_and_cli (flags);
crime_mask = crime_read_64 (CRIME_INT_MASK);
crime_mask &= ~(1 << (irq - 1));
crime_write_64 (CRIME_INT_MASK, crime_mask);
restore_flags (flags);
}
static void end_mace_irq (unsigned int irq)
{
if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
enable_mace_irq (irq);
}
#define shutdown_mace_irq disable_mace_irq
#define mask_and_ack_mace_irq disable_mace_irq
static struct hw_interrupt_type ip32_mace_interrupt = {
"IP32 MACE",
startup_mace_irq,
shutdown_mace_irq,
enable_mace_irq,
disable_mace_irq,
mask_and_ack_mace_irq,
end_mace_irq,
NULL
};
static void ip32_unknown_interrupt (struct pt_regs *regs)
{
u64 crime;
u32 mace;
printk ("Unknown interrupt occurred!\n");
printk ("cp0_status: %08x\tcp0_cause: %08x\n",
read_32bit_cp0_register (CP0_STATUS),
read_32bit_cp0_register (CP0_CAUSE));
crime = crime_read_64 (CRIME_INT_MASK);
printk ("CRIME interrupt mask: %016lx\n", crime);
crime = crime_read_64 (CRIME_INT_STAT);
printk ("CRIME interrupt status: %016lx\n", crime);
crime = crime_read_64 (CRIME_HARD_INT);
printk ("CRIME hardware interrupt register: %016lx\n", crime);
mace = mace_read_32 (MACEISA_INT_MASK);
printk ("MACE ISA interrupt mask: %08x\n", mace);
mace = mace_read_32 (MACEISA_INT_STAT);
printk ("MACE ISA interrupt status: %08x\n", mace);
mace = mace_read_32 (MACEPCI_CONTROL);
printk ("MACE PCI control register: %08x\n", mace);
printk ("Register dump:\n");
show_regs (regs);
printk ("Please mail this report to lin...@os...\n");
printk ("Spinning...");
while (1) ;
}
void __init ip32_irq_init(void)
{
unsigned int irq;
extern void init_generic_irq (void);
/* Install our interrupt handler, then clear and disable all
* CRIME and MACE interrupts.
*/
crime_write_64 (CRIME_INT_MASK, 0);
crime_write_64 (CRIME_HARD_INT, 0);
crime_write_64 (CRIME_SOFT_INT, 0);
mace_write_32 (MACEISA_INT_STAT, 0);
mace_write_32 (MACEISA_INT_MASK, 0);
set_except_vector(0, ip32_handle_int);
init_generic_irq ();
for (irq = 0; irq <= IP32_IRQ_MAX; irq++) {
hw_irq_controller *controller;
if (irq == CLOCK_IRQ)
controller = &ip32_cpu_interrupt;
else if (irq <= MACE_PCI_BRIDGE_IRQ && irq >= MACE_VID_IN1_IRQ)
controller = &ip32_mace_interrupt;
else if (irq <= MACEPCI_SHARED2_IRQ && irq >= MACEPCI_SCSI0_IRQ)
controller = &ip32_macepci_interrupt;
else if (irq <= CRIME_VICE_IRQ && irq >= CRIME_GBE0_IRQ)
controller = &ip32_crime_interrupt;
else
controller = &ip32_maceisa_interrupt;
irq_desc[irq].status = IRQ_DISABLED;
irq_desc[irq].action = 0;
irq_desc[irq].depth = 0;
irq_desc[irq].handler = controller;
}
setup_irq (CRIME_MEMERR_IRQ, &memerr_irq);
setup_irq (CRIME_CPUERR_IRQ, &cpuerr_irq);
}
/* CRIME 1.1 appears to deliver all interrupts to this one pin. */
void ip32_irq0 (struct pt_regs *regs)
{
u64 crime_int = crime_read_64 (CRIME_INT_STAT);
int irq = 0;
if (crime_int & CRIME_MACE_INT_MASK) {
crime_int &= CRIME_MACE_INT_MASK;
irq = ffs (crime_int);
} else if (crime_int & CRIME_MACEISA_INT_MASK) {
u32 mace_int;
mace_int = mace_read_32 (MACEISA_INT_STAT);
if (mace_int == 0)
irq = 0;
else
irq = ffs (mace_int) + 32;
} else if (crime_int & CRIME_MACEPCI_INT_MASK) {
crime_int &= CRIME_MACEPCI_INT_MASK;
crime_int >>= 8;
irq = ffs (crime_int) + 8;
} else if (crime_int & 0xffff0000) {
crime_int >>= 16;
irq = ffs (crime_int) + 16;
}
if (irq == 0)
ip32_unknown_interrupt (regs);
DBG ("*irq %u*\n", irq);
do_IRQ (irq, regs);
}
void ip32_irq1 (struct pt_regs *regs)
{
ip32_unknown_interrupt (regs);
}
void ip32_irq2 (struct pt_regs *regs)
{
ip32_unknown_interrupt (regs);
}
void ip32_irq3 (struct pt_regs *regs)
{
ip32_unknown_interrupt (regs);
}
void ip32_irq4 (struct pt_regs *regs)
{
ip32_unknown_interrupt (regs);
}
void ip32_irq5 (struct pt_regs *regs)
{
do_IRQ (CLOCK_IRQ, regs);
}
--- NEW FILE: ip32-pci-dma.c ---
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2000 Ani Joshi <aj...@un...>
* Copyright (C) 2000 Ralf Baechle <ra...@gn...>
* swiped from i386, and cloned for MIPS by Geert, polished by Ralf.
*/
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/pci.h>
#include <asm/io.h>
#include <asm/addrspace.h>
#include <asm/ip32/mace.h>
unsigned long bus_to_baddr[256] = { MACEPCI_SWAPPED_VIEW, 0, };
void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size,
dma_addr_t * dma_handle)
{
void *ret;
int gfp = GFP_ATOMIC;
if (hwdev == NULL || hwdev->dma_mask != 0xffffffff)
gfp |= GFP_DMA;
ret = (void *) __get_free_pages(gfp, get_order(size));
if (ret != NULL) {
memset(ret, 0, size);
dma_cache_wback_inv((unsigned long) ret, size);
*dma_handle = (bus_to_baddr[0] | __pa (ret));
}
return ret;
}
void pci_free_consistent(struct pci_dev *hwdev, size_t size,
void *vaddr, dma_addr_t dma_handle)
{
free_pages((unsigned long) vaddr, get_order(size));
}
--- NEW FILE: ip32-pci.c ---
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2000, 2001 Keith M Wesolowski
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/types.h>
#include <asm/pci.h>
#include <asm/ip32/mace.h>
#include <asm/ip32/crime.h>
#include <asm/ip32/ip32_ints.h>
#include <linux/delay.h>
#undef DEBUG_MACE_PCI
/*
* O2 has up to 5 PCI devices connected into the MACE bridge. The device
* map looks like this:
*
* 0 aic7xxx 0
* 1 aic7xxx 1
* 2 expansion slot
* 3 N/C
* 4 N/C
*/
#define chkslot(dev) \
do { \
if ((dev)->bus->number > 0 || PCI_SLOT ((dev)->devfn) < 1 \
|| PCI_SLOT ((dev)->devfn) > 3) \
return PCIBIOS_DEVICE_NOT_FOUND; \
} while (0)
#define mkaddr(dev, where) \
((((dev)->devfn & 0xffUL) << 8) | ((where) & 0xfcUL))
void macepci_error (int irq, void *dev, struct pt_regs *regs);
static int macepci_read_config_byte (struct pci_dev *dev, int where,
u8 *val)
{
*val = 0xff;
chkslot (dev);
mace_write_32 (MACEPCI_CONFIG_ADDR, mkaddr (dev, where));
*val = mace_read_8 (MACEPCI_CONFIG_DATA + ((where & 3UL) ^ 3UL));
return PCIBIOS_SUCCESSFUL;
}
static int macepci_read_config_word (struct pci_dev *dev, int where,
u16 *val)
{
*val = 0xffff;
chkslot (dev);
if (where & 1)
return PCIBIOS_BAD_REGISTER_NUMBER;
mace_write_32 (MACEPCI_CONFIG_ADDR, mkaddr (dev, where));
*val = mace_read_16 (MACEPCI_CONFIG_DATA + ((where & 2UL) ^ 2UL));
return PCIBIOS_SUCCESSFUL;
}
static int macepci_read_config_dword (struct pci_dev *dev, int where,
u32 *val)
{
*val = 0xffffffff;
chkslot (dev);
if (where & 3)
return PCIBIOS_BAD_REGISTER_NUMBER;
mace_write_32 (MACEPCI_CONFIG_ADDR, mkaddr (dev, where));
*val = mace_read_32 (MACEPCI_CONFIG_DATA);
return PCIBIOS_SUCCESSFUL;
}
static int macepci_write_config_byte (struct pci_dev *dev, int where,
u8 val)
{
chkslot (dev);
mace_write_32 (MACEPCI_CONFIG_ADDR, mkaddr (dev, where));
mace_write_8 (MACEPCI_CONFIG_DATA + ((where & 3UL) ^ 3UL), val);
return PCIBIOS_SUCCESSFUL;
}
static int macepci_write_config_word (struct pci_dev *dev, int where,
u16 val)
{
chkslot (dev);
if (where & 1)
return PCIBIOS_BAD_REGISTER_NUMBER;
mace_write_32 (MACEPCI_CONFIG_ADDR, mkaddr (dev, where));
mace_write_16 (MACEPCI_CONFIG_DATA + ((where & 2UL) ^ 2UL), val);
return PCIBIOS_SUCCESSFUL;
}
static int macepci_write_config_dword (struct pci_dev *dev, int where,
u32 val)
{
chkslot (dev);
if (where & 3)
return PCIBIOS_BAD_REGISTER_NUMBER;
mace_write_32 (MACEPCI_CONFIG_ADDR, mkaddr (dev, where));
mace_write_32 (MACEPCI_CONFIG_DATA, val);
return PCIBIOS_SUCCESSFUL;
}
static struct pci_ops macepci_ops = {
macepci_read_config_byte,
macepci_read_config_word,
macepci_read_config_dword,
macepci_write_config_byte,
macepci_write_config_word,
macepci_write_config_dword
};
struct pci_fixup pcibios_fixups[] = { { 0 } };
void __init pcibios_init (void)
{
struct pci_dev *dev;
u32 start, size;
u16 cmd;
u32 base_io = 0x3000; /* The first i/o address to assign after SCSI */
u32 base_mem = 0x80100000; /* Likewise */
u32 rev = mace_read_32 (MACEPCI_REV);
int i;
printk ("MACE: PCI rev %d detected at %016lx\n", rev,
(u64) MACE_BASE + MACE_PCI);
/* These are *bus* addresses */
ioport_resource.start = 0;
ioport_resource.end = 0xffffffffUL;
iomem_resource.start = 0x80000000UL;
iomem_resource.end = 0xffffffffUL;
/* Clear any outstanding errors and enable interrupts */
mace_write_32 (MACEPCI_ERROR_ADDR, 0);
mace_write_32 (MACEPCI_ERROR_FLAGS, 0);
mace_write_32 (MACEPCI_CONTROL, 0xff008500);
crime_write_64 (CRIME_HARD_INT, 0UL);
crime_write_64 (CRIME_SOFT_INT, 0UL);
crime_write_64 (CRIME_INT_STAT, 0x000000000000ff00UL);
if (request_irq (MACE_PCI_BRIDGE_IRQ, macepci_error, 0,
"MACE PCI error", NULL))
panic ("PCI bridge can't get interrupt; can't happen.\n");
pci_scan_bus (0, &macepci_ops, NULL);
#ifdef DEBUG_MACE_PCI
pci_for_each_dev (dev) {
printk ("Device: %d/%d/%d ARCS-assigned bus resource map\n",
dev->bus->number, PCI_SLOT (dev->devfn),
PCI_FUNC (dev->devfn));
for (i=0; i < DEVICE_COUNT_RESOURCE; i++) {
if (dev->resource[i].start == 0)
continue;
printk ("%d: %016lx - %016lx (flags %04lx)\n",
i, dev->resource[i].start,
dev->resource[i].end, dev->resource[i].flags);
}
}
#endif
/*
* Assign sane resources to and enable all devices. The requirement
* for the SCSI controllers is well-known: a 256-byte I/O region
* which we must assign, and a 1-page memory region which is
* assigned by the system firmware.
*/
pci_for_each_dev (dev) {
switch (PCI_SLOT (dev->devfn)) {
case 1: /* SCSI bus 0 */
dev->resource[0].start = 0x1000UL;
dev->resource[0].end = 0x10ffUL;
break;
case 2: /* SCSI bus 1 */
dev->resource[0].start = 0x2000UL;
dev->resource[0].end = 0x20ffUL;
break;
default: /* Slots - I guess we have only 1 */
for (i=0; i < 6; i++) {
size = dev->resource[i].end
- dev->resource[i].start;
if (!size
|| !(dev->resource[i].flags
& (IORESOURCE_IO|IORESOURCE_MEM))) {
dev->resource[i].start
= dev->resource[i].end = 0UL;
continue;
}
if (dev->resource[i].flags & IORESOURCE_IO) {
dev->resource[i].start = base_io;
base_io += PAGE_ALIGN (size);
} else {
dev->resource[i].start = base_mem;
base_mem += 0x100000UL;
}
dev->resource[i].end =
dev->resource[i].start + size;
}
break;
}
for (i=0; i < 6; i++) {
if (dev->resource[i].start == 0)
continue;
start = dev->resource[i].start;
if (dev->resource[i].flags & IORESOURCE_IO)
start |= 1;
pci_write_config_dword (dev,
PCI_BASE_ADDRESS_0 + (i << 2), (u32) start);
}
pci_write_config_byte (dev, PCI_CACHE_LINE_SIZE, 0x20);
pci_write_config_byte (dev, PCI_LATENCY_TIMER, 0x30);
pci_read_config_word (dev, PCI_COMMAND, &cmd);
cmd |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_PARITY);
pci_write_config_word (dev, PCI_COMMAND, cmd);
pci_set_master (dev);
}
#ifdef DEBUG_MACE_PCI
printk ("Triggering PCI bridge interrupt...\n");
mace_write_32 (MACEPCI_ERROR_FLAGS, MACEPCI_ERROR_INTERRUPT_TEST);
pci_for_each_dev (dev) {
printk ("Device: %d/%d/%d final bus resource map\n",
dev->bus->number, PCI_SLOT (dev->devfn),
PCI_FUNC (dev->devfn));
for (i=0; i < DEVICE_COUNT_RESOURCE; i++) {
if (dev->resource[i].start == 0)
continue;
printk ("%d: %016lx - %016lx (flags %04lx)\n",
i, dev->resource[i].start,
dev->resource[i].end, dev->resource[i].flags);
}
}
#endif
}
/*
* Given a PCI slot number (a la PCI_SLOT(...)) and the interrupt pin of
* the device (1-4 => A-D), tell what irq to use. Note that we don't
* in theory have slots 4 and 5, and we never normally use the shared
* irqs. I suppose a device without a pin A will thank us for doing it
* right if there exists such a broken piece of crap.
*/
static int __init macepci_map_irq (struct pci_dev *dev, u8 slot, u8 pin)
{
chkslot (dev);
if (pin == 0)
pin = 1;
switch (slot) {
case 1:
return MACEPCI_SCSI0_IRQ;
case 2:
return MACEPCI_SCSI1_IRQ;
case 3:
switch (pin) {
case 2:
return MACEPCI_SHARED0_IRQ;
case 3:
return MACEPCI_SHARED1_IRQ;
case 4:
return MACEPCI_SHARED2_IRQ;
case 1:
default:
return MACEPCI_SLOT0_IRQ;
}
case 4:
switch (pin) {
case 2:
return MACEPCI_SHARED2_IRQ;
case 3:
return MACEPCI_SHARED0_IRQ;
case 4:
return MACEPCI_SHARED1_IRQ;
case 1:
default:
return MACEPCI_SLOT1_IRQ;
}
return MACEPCI_SLOT1_IRQ;
case 5:
switch (pin) {
case 2:
return MACEPCI_SHARED1_IRQ;
case 3:
return MACEPCI_SHARED2_IRQ;
case 4:
return MACEPCI_SHARED0_IRQ;
case 1:
default:
return MACEPCI_SLOT2_IRQ;
}
default:
return 0;
}
}
/*
* It's not entirely clear what this does in a system with no bridges.
* In any case, bridges are not supported by Linux in O2.
*/
static u8 __init macepci_swizzle (struct pci_dev *dev, u8 *pinp)
{
if (PCI_SLOT (dev->devfn) == 2)
*pinp = 2;
else
*pinp = 1;
return PCI_SLOT (dev->devfn);
}
/* All devices are enabled during initialization. */
int pcibios_enable_device (struct pci_dev *dev)
{
return PCIBIOS_SUCCESSFUL;
}
char * __init pcibios_setup (char *str)
{
return str;
}
void __init pcibios_align_resource (void *data, struct resource *res,
unsigned long size)
{
}
void __init pcibios_update_resource (struct pci_dev *dev, struct resource *root,
struct resource *res, int resource)
{
}
void __init pcibios_update_irq (struct pci_dev *dev, int irq)
{
pci_write_config_byte (dev, PCI_INTERRUPT_LINE, irq);
}
void __init pcibios_fixup_bus (struct pci_bus *b)
{
pci_fixup_irqs (macepci_swizzle, macepci_map_irq);
}
/* XXX anybody know what this is supposed to do? */
void __init pcibios_fixup_pbus_ranges(struct pci_bus * bus,
struct pbus_set_ranges_data * ranges)
{
ranges->io_start -= bus->resource[0]->start;
ranges->io_end -= bus->resource[0]->start;
ranges->mem_start -= bus->resource[1]->start;
ranges->mem_end -= bus->resource[1]->start;
}
/*
* Handle errors from the bridge. This includes master and target aborts,
* various command and address errors, and the interrupt test. This gets
* registered on the bridge error irq. It's conceivable that some of these
* conditions warrant a panic. Anybody care to say which ones?
*/
void macepci_error (int irq, void *dev, struct pt_regs *regs) {
u32 flags, error_addr;
char space;
flags = mace_read_32 (MACEPCI_ERROR_FLAGS);
error_addr = mace_read_32 (MACEPCI_ERROR_ADDR);
if (flags & MACEPCI_ERROR_MEMORY_ADDR)
space = 'M';
else if (flags & MACEPCI_ERROR_CONFIG_ADDR)
space = 'C';
else space = 'X';
if (flags & MACEPCI_ERROR_MASTER_ABORT) {
printk ("MACEPCI: Master abort at 0x%08x (%c)\n", error_addr,
space);
mace_write_32 (MACEPCI_ERROR_FLAGS, flags
& ~MACEPCI_ERROR_MASTER_ABORT);
}
if (flags & MACEPCI_ERROR_TARGET_ABORT) {
printk ("MACEPCI: Target abort at 0x%08x (%c)\n", error_addr,
space);
mace_write_32 (MACEPCI_ERROR_FLAGS, flags
& ~MACEPCI_ERROR_TARGET_ABORT);
}
if (flags & MACEPCI_ERROR_DATA_PARITY_ERR) {
printk ("MACEPCI: Data parity error at 0x%08x (%c)\n",
error_addr, space);
mace_write_32 (MACEPCI_ERROR_FLAGS, flags
& ~MACEPCI_ERROR_DATA_PARITY_ERR);
}
if (flags & MACEPCI_ERROR_RETRY_ERR) {
printk ("MACEPCI: Retry error at 0x%08x (%c)\n", error_addr,
space);
mace_write_32 (MACEPCI_ERROR_FLAGS, flags
& ~MACEPCI_ERROR_RETRY_ERR);
}
if (flags & MACEPCI_ERROR_ILLEGAL_CMD) {
printk ("MACEPCI: Illegal command at 0x%08x (%c)\n",
error_addr, space);
mace_write_32 (MACEPCI_ERROR_FLAGS,
flags & ~MACEPCI_ERROR_ILLEGAL_CMD);
}
if (flags & MACEPCI_ERROR_SYSTEM_ERR) {
printk ("MACEPCI: System error at 0x%08x (%c)\n",
error_addr, space);
mace_write_32 (MACEPCI_ERROR_FLAGS, flags
& ~MACEPCI_ERROR_SYSTEM_ERR);
}
if (flags & MACEPCI_ERROR_PARITY_ERR) {
printk ("MACEPCI: Parity error at 0x%08x (%c)\n", error_addr,
space);
mace_write_32 (MACEPCI_ERROR_FLAGS, flags
& ~MACEPCI_ERROR_PARITY_ERR);
}
if (flags & MACEPCI_ERROR_OVERRUN) {
printk ("MACEPCI: Overrun error at 0x%08x (%c)\n",
error_addr, space);
mace_write_32 (MACEPCI_ERROR_FLAGS, flags
& ~MACEPCI_ERROR_OVERRUN);
}
if (flags & MACEPCI_ERROR_SIG_TABORT) {
printk ("MACEPCI: Signaled target abort (clearing)\n");
mace_write_32 (MACEPCI_ERROR_FLAGS, flags
& ~MACEPCI_ERROR_SIG_TABORT);
}
if (flags & MACEPCI_ERROR_INTERRUPT_TEST) {
printk ("MACEPCI: Interrupt test triggered (clearing)\n");
mace_write_32 (MACEPCI_ERROR_FLAGS, flags
& ~MACEPCI_ERROR_INTERRUPT_TEST);
}
}
--- NEW FILE: ip32-rtc.c ---
/*
* RTC routines for IP32 style attached Dallas chip.
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2000 Harald Koerfgen
*/
#include <linux/mc146818rtc.h>
#include <asm/ip32/mace.h>
static unsigned char ip32_rtc_read_data(unsigned long addr)
{
return (unsigned char) mace_read_8 (MACEISA_RTC_BASE + (addr << 8));
}
static void ip32_rtc_write_data(unsigned char data, unsigned long addr)
{
mace_write_8 (MACEISA_RTC_BASE + (addr << 8), data);
}
static int ip32_rtc_bcd_mode(void)
{
return 0;
}
struct rtc_ops ip32_rtc_ops = {
&ip32_rtc_read_data,
&ip32_rtc_write_data,
&ip32_rtc_bcd_mode
};
--- NEW FILE: ip32-setup.c ---
/*
* IP32 basic setup
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2000 Harald Koerfgen
*/
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/mc146818rtc.h>
#include <linux/param.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <asm/mipsregs.h>
#include <asm/bootinfo.h>
#include <asm/irq.h>
#include <asm/mmu_context.h>
#include <asm/ip32/crime.h>
#include <asm/ip32/mace.h>
#include <asm/ip32/ip32_ints.h>
#include <asm/sgialib.h>
extern struct rtc_ops ip32_rtc_ops;
extern u32 cc_interval;
void __init ip32_init (int argc, char **argv, char **envp) {
arc_meminit ();
}
void __init ip32_setup(void)
{
#ifdef CONFIG_SERIAL_CONSOLE
char *ctype;
#endif
TLBMISS_HANDLER_SETUP ();
#ifdef CONFIG_SERIAL_CONSOLE
ctype = ArcGetEnvironmentVariable("console");
if (*ctype == 'd') {
if (ctype[1] == '2')
console_setup ("ttyS1");
else
console_setup ("ttyS0");
}
#endif
#ifdef CONFIG_VT
conswitchp = &dummy_con;
#endif
rtc_ops = &ip32_rtc_ops;
crime_init ();
}
int __init page_is_ram (unsigned long pagenr)
{
/* XXX: to do? */
return 1;
}
--- NEW FILE: ip32-timer.c ---
/*
* IP32 timer calibration
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2001 Keith M Wesolowski
*/
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <asm/mipsregs.h>
#include <asm/param.h>
#include <asm/ip32/crime.h>
#include <asm/ip32/ip32_ints.h>
extern u32 cc_interval;
/* An arbitrary time; this can be decreased if reliability looks good */
#define WAIT_MS 10
#define PER_MHZ (1000000 / 2 / HZ)
void __init ip32_timer_setup (struct irqaction *irq) {
u64 crime_time;
u32 cc_tick;
printk("Calibrating system timer... ");
crime_time = crime_read_64 (CRIME_TIME) & CRIME_TIME_MASK;
cc_tick = read_32bit_cp0_register (CP0_COUNT);
while ((crime_read_64 (CRIME_TIME) & CRIME_TIME_MASK) - crime_time
< WAIT_MS * 1000000 / CRIME_NS_PER_TICK)
;
cc_tick = read_32bit_cp0_register (CP0_COUNT) - cc_tick;
cc_interval = cc_tick / HZ * (1000 / WAIT_MS);
/* The round-off seems unnecessary; in testing, the error of the
* above procedure is < 100 ticks, which means it gets filtered
* out by the HZ adjustment.
*/
cc_interval = (cc_interval / PER_MHZ) * PER_MHZ;
printk("%d MHz CPU detected\n", (int) (cc_interval / PER_MHZ));
setup_irq (CLOCK_IRQ, irq);
}
|