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