From: Pete P. <pp...@us...> - 2002-05-09 17:22:37
|
Update of /cvsroot/linux-mips/linux/arch/mips/lx In directory usw-pr-cvs1:/tmp/cvs-serv27584/arch/mips/lx Added Files: Makefile dbg_io.c int-handler.S irq.c lx.c lxRi.c lx_io.c lxpci.c lxserial.c puts.c rtc.c startice.cmd Log Message: Added support for the Lexra 4189 CPU and eval board. Initial port completed by Lexra. --- NEW FILE: Makefile --- # # Makefile for the Lexra 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... # # Note: for indenting files, use # indent -bl -bls -bad -bli0 -di4 -i4 -nlp .S.s: $(CPP) $(CFLAGS) $< -o $*.s .S.o: $(CC) $(CFLAGS) -c $< -o $*.o all: lx4189.o O_TARGET := lx4189.o obj-y := lx.o rtc.o irq.o int-handler.o lxpci.o lx_io.o lxserial.o lxRi.o puts.o obj-$(CONFIG_REMOTE_DEBUG) += dbg_io.o clean: rm -f lx4189.o lx.o include $(TOPDIR)/Rules.make --- NEW FILE: dbg_io.c --- #include <linux/config.h> #include <asm/io.h> #include <asm/lexra/lx_defs.h> #include <asm/lexra/lx_io.h> #ifdef CONFIG_REMOTE_DEBUG /* * FIXME the user should be able to select the * uart to be used for debugging. */ #define DEBUG_BASE LX_UART2_REGS_BASE static u32 base = DEBUG_BASE; static inline unsigned int serial_inl(u32 base, u32 offset) { return lx_inl((unsigned int *)(base + offset)); } static inline void serial_outl(u32 base, u32 offset, u32 value) { lx_outl(value, (unsigned int *)(base+offset)); } static inline unsigned int serial_inb(u32 base, u32 offset) { return lx_inb((unsigned int *)(base + offset)); } static inline void serial_outb(u32 base, u32 offset, char value) { lx_outb(value, (unsigned int *)(base+offset)); } void debugInit() { u32 reg; /* disable interrupts */ reg = (serial_inl(base, LX_UART_CONF) & ~(LX_UART_TIE | LX_UART_RIE)); serial_outl(base, LX_UART_CONF, reg); /* Discard everything in the RX port */ while( (serial_inl(base, LX_UART_CONF) & LX_UART_RX_READY) != 0 ) serial_inb(base, LX_UART_DATA); /* set up baud rate */ reg = (serial_inl(base, LX_UART_CONF) & ~LX_CBAUD); serial_outl(base, LX_UART_CONF, reg); reg = (serial_inl(base, LX_UART_CONF) | LX_QUOT_DEFAULT); serial_outl(base, LX_UART_CONF, reg); } static int remoteDebugInitialized = 0; u8 getDebugChar(void) { if (!remoteDebugInitialized) { remoteDebugInitialized = 1; debugInit(); } while (!((serial_inl(base, LX_UART_CONF)) & LX_UART_RX_READY)) ; return lx_inb(base + LX_UART_DATA); } int putDebugChar(u8 byte) { unsigned int status, tmout = 10000; if (!remoteDebugInitialized) { remoteDebugInitialized = 1; debugInit(); } do { status = serial_inl(base, LX_UART_CONF); if (--tmout == 0) break; } while(!(status & LX_UART_TX_READY)); serial_outb(base, LX_UART_DATA, byte); return 1; } #endif --- NEW FILE: int-handler.S --- /* *arch/mips/lx/int-handler.S */ #include <asm/asm.h> #include <asm/regdef.h> #include <asm/mipsregs.h> #include <asm/stackframe.h> #include <asm/addrspace.h> /** * This functions within are written in assembly. */ #define __ASSEMBLY__ #include <asm/lexra/lxirq.h> #include <asm/lexra/lx_defs.h> .text .set noreorder /** * @file int-handler.S * * This file contains the assembly language interrupt dispatchers for the * Lexra board. There are two routines provided. The first, lx_handle_int, * is registered as the MIPS interrupt exception handler, therefore, handles * the MIPS style interrupts. The second handler lx_vec_handle_int, handles * the Lexra vectored interrupts. Each of the vectored interrupt vectors have * been coded to jump to this routine. This allows the different types of * interrupts to be handled using the same functions in the higher level code. * * We follow the model in the Indy interrupt code by David Miller, where he * says: a lot of complication here is taken away because: * * 1) We handle one interrupt and return, sitting in a loop * and moving across all the pending IRQ bits in the cause * register is _NOT_ the answer, the common case is one * pending IRQ so optimize in that direction. * * 2) We need not check against bits in the status register * IRQ mask, that would make this routine slow as hell. * * 3) Linux only thinks in terms of all IRQs on or all IRQs * off, nothing in between like BSD spl() brain-damage. * * LX4xxx/5xxx interrupt mapping: *@verbatim MIPS IRQ Source _________ ________ 0 Software (ignored) 1 Software (ignored) 2 PCI in A,B,C and D 3 UART 1&2 tx&rx 4 BUSCLK Counter 5 SYSCLK Counter 6 UARTCLK Counter 7 System Timer We handle the IRQ according to _our_ priority. The priority of an interrupt may be changed by changing its location in its mask and irq_nr tables through modification of the initialization function lx_init_PB20K(). The default priority is: Highest ---- System Timer UARTCLK Counter BUSCLK Counter BUSCLK Counter UART 1&2 tx&rx PCI in A,B,C and D SW1 Lowest ---- SW0 @endverbatim * The highest priority pending interrupt is handled first * then we just return, if multiple IRQs are pending then we will just take * another exception, big deal. * */ /** * The interrupt handler for Lexra. * The highest priority pending interrupt is handled first * then we just return, if multiple IRQs are pending then we will just take * another exception. * Spurious interrupts are ignored. */ #ifdef DOC_GENERATE asmlinkage void lx_handle_int(void) {}; #else .align 5 NESTED(lx_handle_int, PT_SIZE, ra) .set noat SAVE_ALL CLI # TEST: interrupts should be off .set at .set noreorder /* * Get pending Interrupts */ mfc0 t0,CP0_CAUSE # get pending interrupts mfc0 t2,CP0_STATUS la t1,cpu_mask_tbl andi t0,0xff00 and t0,t2 # isolate allowed ones /* * detection of spurious interrupts */ beq t0,zero,return_ nop # delay slot /* * Find irq with highest priority */ 1: lw t2,(t1) move t3,t0 and t3,t2 beq t3,zero,1b addu t1,PTRSIZE # delay slot /* * Do the low-level stuff */ lw a0,%lo(cpu_irq_nr-cpu_mask_tbl-PTRSIZE)(t1) nop bgez a0, handle_it # irq_nr >= 0? # irq_nr < 0: just exit nop j return_ nop # delay slot handle_it: jal do_IRQ move a1,sp return_: j ret_from_irq nop END(lx_handle_int) #endif /** * This is the interrupt handler for the Lexra vectored interrupts. Each of * the vectors have been setup to jump to this routine. Similar in operation * to the MIPS interrupt handler lx_handle_int(). */ #ifdef DOC_GENERATE asmlinkage void lx_vec_handle_int(void){}; #else NESTED(lx_vec_handle_int, PT_SIZE, ra) .set noat SAVE_ALL CLI # TEST: interrupts should be off .set at .set noreorder /** * Get pending Interrupts using * hand compiled Lexra specific instructions */ .word (MFLXCO | (8<<16) | (LX_ECAUSE<<11)) nop nop .word (MFLXCO | (10<<16) | (LX_ESTATUS<<11)) nop nop la t1,lx_mask_tbl and t0,t2 # isolate allowed ones /* * detection of spurious interrupts */ beq t0,zero,lx_return_ nop # delay slot /** * Find irq with highest priority, if none found just return. */ 1: lw t2,(t1) move t3,t0 and t3,t2 beq t3,zero,1b addu t1,PTRSIZE # delay slot /* * Do the low-level stuff */ lw a0,%lo(lx_irq_nr-lx_mask_tbl-PTRSIZE)(t1) nop bgez a0, lx_handle_it # irq_nr >= 0? # irq_nr < 0: just exit nop j lx_return_ nop # delay slot lx_handle_it: jal do_IRQ move a1,sp lx_return_: j ret_from_irq nop END(lx_vec_handle_int) #endif /** * The function for masking the Lexra vectored interrupts using Lexra specific * opcodes. * @param mask Compliment of mask passed in through register a0. */ #ifdef DOC_GENERATE inline void mask_lx_interrupt(unsigned int mask){}; #else LEAF(mask_lx_interrupt) #compliment of mask in a0 move t0, a0 .word (MFLXCO | (9<<16) | (LX_ESTATUS<<11)) #get status reg t1 nop nop and t0, t1 # and the register with the mask complement .word (MTLXCO | (8<<16) | (LX_ESTATUS<<11)) # copy back status nop nop j ra nop END(mask_lx_interrupt) #endif /** * The function used for unmasking the Lexra vectored interrupts using Lexra * specific opcodes. * @param mask Mask passed in through register a0. */ #ifdef DOC_GENERATE inline void unmask_lx_interrupt(unsigned int mask){}; #else LEAF(unmask_lx_interrupt) move t0, a0 .word (MFLXCO | (9<<16) | (LX_ESTATUS<<11)) #get status reg t1 nop nop or t0, t1, t0 # and the register with the mask complement .word (MTLXCO | (8<<16) | (LX_ESTATUS<<11)) # copy back status nop nop j ra nop END(unmask_lx_interrupt) #endif /** * The function used for unmasking the Lexra vectored interrupts using Lexra * specific opcodes. * @param reg Pointer to the location to store the value in the cause register. */ #ifdef DOC_GENERATE asmlinkage void read_lx_cause(unsigned int* reg){}; #else LEAF(read_lx_cause) .word (MFLXCO | (8<<16) | (LX_ECAUSE<<11)) nop nop sw t0, 0(a0) nop j ra nop END(read_lx_cause) #endif /** * A function used to read the Lexra processor revision register. * @return The value in the revision register. */ #ifdef DOC_GENERATE asmlinkage unsigned int read_lx_revision(void){}; #else LEAF(read_lx_revision) .word (MFLXCO | (2<<16) | (3<<11)) nop nop j ra nop END(read_lx_revision) #endif /** * The function used for reading the Lexra vectored interrupt status register * using Lexra specific opcodes. * @param reg Pointer to the location to store the value in the status register. */ #ifdef DOC_GENERATE asmlinkage void read_lx_status(unsigned int* reg){}; #else LEAF(read_lx_status) .word (MFLXCO | (8<<16) | (LX_ESTATUS<<11)) nop nop sw t0, 0(a0) nop j ra nop END(read_lx_status) #endif /** * The cpu_mask_tbl contains the interrupt masks for the interrupts in * the corresponding positions of the cpu_irq_nr table. It is initialised * in the funtion lx_init_lxPB20K() found in the file lx.c. */ #ifdef DOC_GENERATE unsigned long cpu_mask_tbl[8]; #else .data EXPORT(cpu_mask_tbl) cpu_mask_tbl: .word 0x00000000 .word 0x00000000 .word 0x00000000 .word 0x00000000 .word 0x00000000 .word 0x00000000 .word 0x00000000 # these two are unlikely .word 0x00000000 # to be used .word 0x0000ff00 # End of list #endif /** * This array maps each of the interrupts to a number enabling configuation * of interrupt priority. */ #ifdef DOC_GENERATE unsigned long cpu_irq_nr[8]; #else EXPORT(cpu_irq_nr) cpu_irq_nr: .word 0x00000000 .word 0x00000000 .word 0x00000000 .word 0x00000000 .word 0x00000000 .word 0x00000000 .word 0x00000000 # these two are unlikely .word 0x00000000 # to be used .word 0x00ffffff # End of list #endif /** * The lx_mask_tbl contains the interrupt masks for the interrupts in the * corresponding positions of the lx_irq_nr table. It is initialised in the * funtion lx_init_lxPB20K() found in the file lx.c. */ #ifdef DOC_GENERATE unsigned long lx_mask_tbl[8]; #else EXPORT(lx_mask_tbl) lx_mask_tbl: .word 0x00000000 .word 0x00000000 .word 0x00000000 .word 0x00000000 .word 0x00000000 .word 0x00000000 .word 0x00000000 .word 0x00000000 .word 0xffffffff # EOL #endif /** * This array maps each of the interrupts to a number enabling configuation * of interrupt priority. */ #ifdef DOC_GENERATE unsigned long lx_irq_nr[8]; #else EXPORT(lx_irq_nr) lx_irq_nr: .word 0x00000000 .word 0x00000000 .word 0x00000000 .word 0x00000000 .word 0x00000000 .word 0x00000000 .word 0x00000000 .word 0x00000000 .word 0xffffffff # EOL #endif --- NEW FILE: irq.c --- /** * @file irq.c * * This file contains code to interface generic Linux interrupt handling * functions in arch/mips/kernel/irq.c. Additional * code added to implement the Lexra vectored interrupts. */ #include <linux/errno.h> #include <linux/init.h> #include <linux/kernel_stat.h> #include <linux/signal.h> #include <linux/sched.h> #include <linux/types.h> #include <linux/interrupt.h> #include <linux/ioport.h> #include <linux/timex.h> #include <linux/malloc.h> #include <linux/random.h> #include <asm/bitops.h> #include <asm/bootinfo.h> #include <asm/io.h> #include <asm/irq.h> #include <asm/mipsregs.h> #include <asm/system.h> #include <asm/lexra/lxirq.h> #include <asm/lexra/lx_defs.h> #ifdef CONFIG_REMOTE_DEBUG extern void breakpoint(void); #endif /** * External function defined in int-handler.S. This is an assembly language * routine used to mask the Lexra vectored interrupts using Lexra specific * instructions. */ extern inline void mask_lx_interrupt(unsigned int mask); /** * External function defined in int-handler.S. This is an assembly language * routine used to unmask the Lexra vectored interrupts using Lexra specific * instructions. */ extern inline void unmask_lx_interrupt(unsigned int mask); extern int pci_devices_count; extern void lx_irq_setup(void); extern void init_generic_irq(void); /** * Internal function to mask an interrupt. Will mask any of the three * types of Lexra interrupts: standard MIPS or Lexra vectored. * @param irq_nr The interrupt number to be masked. */ static inline void mask_irq(unsigned int irq_nr) { /* This is a lexra vectored interrupt */ if (lx_interrupt[irq_nr].LX_mask != 0) { mask_lx_interrupt(~lx_interrupt[irq_nr].LX_mask); } else if(lx_interrupt[irq_nr].cpu_mask != 0) { /* This is a cpu interrupt */ clear_cp0_status(lx_interrupt[irq_nr].cpu_mask); } } /** * Internal function to unmask an interrupt. Will unmask any of the three * types of Lexra interrupts: standard MIPS or Lexra vectored. * @param irq_nr The interrupt number to be unmasked. */ static inline void unmask_irq(unsigned int irq_nr) { /* This is a Lexra vectored interrupt */ if (lx_interrupt[irq_nr].LX_mask != 0) { unmask_lx_interrupt(lx_interrupt[irq_nr].LX_mask); } else if (lx_interrupt[irq_nr].cpu_mask != 0){ /* This is a cpu interrupt */ set_cp0_status(lx_interrupt[irq_nr].cpu_mask); } } /** * Following four functions are needed by arch/mips/kernel/irq.c */ static unsigned int startup_irq(unsigned int irq_nr) { if(irq_nr != PCI_ABCD) unmask_irq(irq_nr); else if(--pci_devices_count <= 0 ) unmask_irq(irq_nr); return 0; } static void shutdown_irq(unsigned int irq_nr) { mask_irq(irq_nr); } static inline void ack_level_irq(unsigned int irq_nr) { mask_irq(irq_nr); } static void end_irq(unsigned int irq_nr) { if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))) unmask_irq(irq_nr); else printk("warning: end_irq %d did not enable (%x)\n", irq_nr, irq_desc[irq_nr].status); } /** * Currently only level-triggered interrupts are considered */ static struct hw_interrupt_type level_irq_type = { "Lexra Level", startup_irq, shutdown_irq, unmask_irq, mask_irq, ack_level_irq, end_irq, NULL }; /** * Initial setup of interrupts. Called by start_kernel(). Only * function is to call the function irq_setup(). */ void __init init_IRQ(void) { int i; memset(irq_desc, 0, sizeof(irq_desc)); init_generic_irq(); lx_irq_setup(); for(i=0; i < 8; i++) { /* for 8 MIPS interrupts */ irq_desc[cpu_irq_nr[i]].handler = &level_irq_type; /* for 8 Lexra vectored interrupts */ irq_desc[lx_irq_nr[i]].handler = &level_irq_type; } #ifdef CONFIG_REMOTE_DEBUG /* If local serial I/O used for debug port, enter kgdb at once */ puts("Waiting for kgdb to connect..."); set_debug_traps(); breakpoint(); #endif } --- NEW FILE: lx.c --- /* * Copyright (C) 2001 Lexra, Inc */ #include <linux/config.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/types.h> #include <linux/mm.h> #include <linux/swap.h> #include <linux/ioport.h> #include <linux/interrupt.h> #include <linux/timex.h> #include <linux/kernel_stat.h> #include <asm/bootinfo.h> #include <asm/page.h> #include <asm/io.h> #include <asm/irq.h> #include <asm/processor.h> #include <asm/ptrace.h> #include <asm/reboot.h> #include <asm/mc146818rtc.h> #include <linux/version.h> #include <linux/bootmem.h> #include <asm/irq.h> #include <linux/mc146818rtc.h> #include <asm/lexra/lxirq.h> #include <asm/lexra/lx_defs.h> /** * The control register addresses of the Lexra BUSCLK timer. */ #define BUSCLK_CTRL_ADDR 0xbc000010 /** * The control register addresses of the Lexra SYSCLK timer. */ #define SYSCLK_CTRL_ADDR 0xbc000014 /** * The control register addresses of the Lexra UARTCLK timer. */ #define UARTCLK_CTRL_ADDR 0xbc000018 /** * The control register addresses of the Lexra System timer. */ #define SYS_TIMER_ADDR 0xbc000030 /* This is for machines which generate the exact clock. */ #define USECS_PER_JIFFY (1000000/HZ) /* sys_timer_reload equals the number of system timer ticks per 10ms */ static unsigned long sys_timer_reload; /* usecs per counter cycle, shifted to left by 32 bits */ static unsigned int sll32_usecs_per_cycle=0; /* Cycle counter value at the previous timer interrupt.. */ static unsigned int timerlo; /* the function pointer to one of the gettimeoffset funcs*/ extern unsigned long (*do_gettimeoffset)(void); extern unsigned int mips_counter_frequency; /** * Function stub. Function not required for Lexra implementation of Linux. */ void page_is_ram(void) {}; /** * Function stub. Function not required for Lexra implementation of Linux. */ void prom_free_prom_memory(void) {}; /** * A structure to store parameters to be passed from the boot loader * to the kernel. */ struct bootParams lxBootParams; /** * Macro to get address of next page. * @param x Address of current page. */ #define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) /** * Macro to align the supplied address to the next page boundary * @param x Address to align. */ #define PFN_ALIGN(x) (((unsigned long)(x) + (PAGE_SIZE - 1)) & PAGE_MASK) /** * Variable containing the address of the end of the kernel data */ extern int _end; /** * Variable to store the size of the RAM. */ unsigned long mem_size; /** * Boolean variable to indicate if the Lexra processor has DSP features. */ unsigned int dspProduct; /** * The xtime clock lock used to lock the xtime structure while it is being updated. */ extern rwlock_t xtime_lock; /** * Declaration of the MIPS standard interrupt dispatch function. * This function is defined in int-handler.S. */ extern asmlinkage void lx_handle_int(void); /** * Declaration of the Lexra vectored interrupt dispatch function. * This function is defined in int-handler.S. */ extern asmlinkage void lx_vec_handle_int(void); extern asmlinkage void handle_ri(void); extern char except_vec3_generic; /* for reserved instruction exception */ void lx_init_lxPB20K(void); /** * An array of lxint_t structures which store the masking information * for each of the 32 Linux interrupts. This data is used to enable/disable * either a MIPS, Lexra vectored or PCI interrupt. */ lxint_t lx_interrupt[NR_INTS]; /** * Unimplemented power control function. */ extern void lx_machine_restart(char *command); /** * Unimplemented power control function. */ extern void lx_machine_halt(void); /** * Unimplemented power control function. */ extern void lx_machine_power_off(void); extern void wbflush_setup(void); /** * The irqaction structure for the system timer. */ extern void (*board_timer_setup) (struct irqaction * irq); extern struct rtc_ops lx45xxx_rtc_ops; /** * Pointer to the system clock control register. */ volatile unsigned int* Sys_clock_ctrl_reg; /** * The default UART channel, UARTA. */ #define channel 1 /** * External function to send a character to the UART. */ extern void serial_putc(int chan, int c); /** * External function used to set an interger value to the UART as * a hexidecimal string. */ extern void int2hex(int i); /** * Variable to store the limits of the initial ramdisk. */ extern unsigned long initrd_start,initrd_end; extern void * __rd_start, * __rd_end; const char *get_system_type(void) { return "Lexra 4189"; } /** * This funtion initializes kernel variables with data from the boot * loader. */ int prom_init(int argc, char **argv, char **envp, int *prom_vec) { #ifdef CONFIG_BLK_DEV_INITRD ROOT_DEV=MKDEV(RAMDISK_MAJOR, 0); initrd_start = (unsigned long)&__rd_start; initrd_end = (unsigned long)&__rd_end; #endif memcpy(&lxBootParams.cmd_line, ((struct bootParams *)prom_vec)->cmd_line, ((struct bootParams *)prom_vec)->cmd_line_size); lxBootParams.cmd_line_size = ((struct bootParams *)prom_vec)->cmd_line_size; mips_machgroup = MACH_GROUP_LEXRA; mips_machtype = MACH_LXPB20K; mem_size = 64 << 20; printk("%d MB SDRAM.\n", (int)(mem_size >> 20)); add_memory_region(0, mem_size, BOOT_MEM_RAM); /* Redefine the end of ioport resource memory space */ ioport_resource.end = 0xbcffffff; return 0; }; unsigned long lexra_readtimer() { return *((unsigned long *)SYS_TIMER_ADDR); } unsigned long lx_gettimeoffset(void) { u32 count; unsigned long res; count = *((unsigned long *)SYS_TIMER_ADDR); /* .. relative to previous jiffy (32 bits is enough) */ count -= timerlo; __asm__("multu\t%1,%2\n\t" "mfhi\t%0" :"=r" (res) :"r" (count), "r" (sll32_usecs_per_cycle)); /* * Due to possible jiffies inconsistencies, we need to check * the result so that we'll get a timer that is monotonic. */ if (res >= USECS_PER_JIFFY) res = USECS_PER_JIFFY-1; return res; } /** * Variable to store the time of the last clock update. */ static long last_rtc_update; /** * This function stub allows the Lexra board to run Linux without * a real time clock. * @param nowtime Unused. */ static int set_rtc_mmss(unsigned long nowtime) { return 0; } /** * The sytem timer interrupt routine. This is the only time base * for Lexra boards, there is no real time clock. * The handler uses the System Timer to keep track of time and generate * an interrupt every 10ms. * Additional code exists for updating profiling structures and the * system time. * @param irq The timer irq number. * @param dev_id Device id, NULL for timer interrupt. * @param regs Pointer to stack passed through by interrupt handler. * @return void */ static void inline lx_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { unsigned long count, compare, temp; int cpu = smp_processor_id(); irq_enter(cpu, irq); kstat.irqs[cpu][irq]++; /* use the free running counter to calculate our time offsets */ count = Sys_clock_ctrl_reg[0]; compare = Sys_clock_ctrl_reg[2]; Sys_clock_ctrl_reg[2] += sys_timer_reload; if (count > (compare + sys_timer_reload)) { /* if there was a long delay in getting to the timer * interrupt handler, and count is already greater than * compare, we won't get another interrupt for a very long * time if we just increment compare with sys_timer_reload. * Plus, we might have to adjust the jiffies. */ while ((compare + sys_timer_reload) < count) { (*(unsigned long *)&jiffies)++; compare += sys_timer_reload; count = Sys_clock_ctrl_reg[0]; } /* adjust compare */ Sys_clock_ctrl_reg[2] = compare + sys_timer_reload; } Sys_clock_ctrl_reg[1] |= 1; /* ack interrupt */ timerlo = count; if (!user_mode(regs)) { if (prof_buffer && current->pid) { extern int _stext; unsigned long pc = regs->cp0_epc; pc -= (unsigned long) &_stext; pc >>= prof_shift; /* * Dont ignore out-of-bounds pc values silently, * put them into the last histogram slot, so if * present, they will show up as a sharp peak. */ if (pc > prof_len - 1) pc = prof_len - 1; atomic_inc((atomic_t *) & prof_buffer[pc]); } } do_timer(regs); /* * If we have an externally synchronized Linux clock, then update * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be * called as close as possible to 500 ms before the new second starts. */ read_lock(&xtime_lock); if ((time_status & STA_UNSYNC) == 0 && xtime.tv_sec > last_rtc_update + 660 && xtime.tv_usec >= 500000 - tick / 2 && xtime.tv_usec <= 500000 + tick / 2) { if (set_rtc_mmss(xtime.tv_sec) == 0) last_rtc_update = xtime.tv_sec; else /* do it again in 60 s */ last_rtc_update = xtime.tv_sec - 600; } read_unlock(&xtime_lock); /* * If jiffies has overflowed in this timer_interrupt we must * update the timer[hi]/[lo] to make fast gettimeoffset funcs * quotient calc still valid. -arca */ if (!jiffies) { timerlo = 0; } irq_exit(cpu, irq); } /** * Function to initialize the Lexra timer. This initialization includes * calculating the appropriate decrementer value based on the configured * clock rate, requesting and registering the interrupt and starting the * timer. * @param irq Pointer to the irqaction structure for the timer. This structure * stores information required by the do_IRQ() function which dispatches interrupts. */ static void __init lx_time_init(struct irqaction *irq) { unsigned long start, end; volatile unsigned long *uart_clk = (volatile unsigned long *)UARTCLK_CTRL_ADDR; /* setup ptr to contrl reg */ Sys_clock_ctrl_reg = (unsigned int *)SYS_TIMER_ADDR; irq->handler = lx_timer_interrupt; irq->flags = 0; irq->mask = 0; irq->name = "System Clock"; irq->next = NULL; irq->dev_id = NULL; setup_irq(SYSTIMER, irq); /* Use the UART clock which is at a known, fixed frequency, * to figure out the System Timer clock. */ start = Sys_clock_ctrl_reg[0]; *uart_clk = 0x80000000; /* up counter, reset */ while (*uart_clk < 1843200); /* let it run for 100 ms */ end = Sys_clock_ctrl_reg[0]; mips_counter_frequency = (end - start) * 10; sys_timer_reload = mips_counter_frequency / HZ; Sys_clock_ctrl_reg[2] = Sys_clock_ctrl_reg[0] + sys_timer_reload; Sys_clock_ctrl_reg[1] &= ~(1<<16); /* enable compare 0 int */ Sys_clock_ctrl_reg[1] |= (1<<17); /* disable compare 1 int */ /* sll32_usecs_per_cycle = 10^6 * 2^32 / mips_counter_freq */ /* any better way to do this? */ sll32_usecs_per_cycle = mips_counter_frequency / 100000; sll32_usecs_per_cycle = 0xffffffff / sll32_usecs_per_cycle; sll32_usecs_per_cycle *= 10; /* override the pointer in kernel/time.c */ do_gettimeoffset = lx_gettimeoffset; } /** * Lexra specific interrupt setup function. This function calls the * routine which initializes the Lexra interrupt handling structures. * Following this the MIPS interrupt exception vector is installed. */ void __init lx_irq_setup(void) { int i; switch (mips_machgroup) { case MACH_GROUP_LEXRA: lx_init_lxPB20K(); break; } /* setupt the interrupt exception vector to dispatch * MIPS type interrupts */ set_except_vector(0, lx_handle_int); for (i=0; i<NR_INTS; i++) { disable_irq(i); } } /** * Stores the command line entered at the boot prompt. */ char arcs_cmdline[CL_SIZE] = {0, }; /** * Lexra machine specific setup routine. This routine sets up function * pointers used by the kernel intitialization code to intialize * various hardware specific operations like interrupts, timers and * io space. */ void lexra_setup(void) { /** * LX4xxx/5xxx specific RTC routines * this is a structure containing pointers to RTC functions * we don't have an RTC so stubs must be defined */ rtc_ops = &lx45xxx_rtc_ops; board_timer_setup = lx_time_init; memcpy(&arcs_cmdline, &(lxBootParams.cmd_line), lxBootParams.cmd_line_size); mips_io_port_base = KSEG1; /* trap MAC instructions before trap_init is called in Linux kernel, it will be override later in trap_init */ set_except_vector(10, handle_ri); memcpy((void *)(KSEG0 + 0x80), &except_vec3_generic, 0x80); flush_icache_range(KSEG0+0x80, KSEG0+0x80); }; /** * Lexra specific initialization of interrupts. * Support is provided for the MIPS 6 hardware and 2 software interrups, * the 8 Lexra vectored interrupts and 16 available PCI interrupts. * The interrupts are prioritized through the used of the mask and irq_nr * tables. This allows for simple modification of the interrupt priority * by changing the relative location of the interrupt in these tables. */ void __init lx_init_lxPB20K(void) { /** * Setup interrupt structure */ lx_interrupt[I2S].cpu_mask = 0; lx_interrupt[I2S].LX_mask = LX_IRQ11; lx_mask_tbl[4] = LX_IRQ11; lx_irq_nr[4] = I2S; lx_interrupt[PCI_ABCD].cpu_mask = IE_IRQ0; lx_interrupt[PCI_ABCD].LX_mask = 0; cpu_mask_tbl[2] = IE_IRQ0; cpu_irq_nr[2] = PCI_ABCD; lx_interrupt[UART12TXRX].cpu_mask = IE_IRQ1; lx_interrupt[UART12TXRX].LX_mask = 0; cpu_mask_tbl[3] = IE_IRQ1; cpu_irq_nr[3] = UART12TXRX; lx_interrupt[BUSCLK].cpu_mask = IE_IRQ2; lx_interrupt[BUSCLK].LX_mask = 0; cpu_mask_tbl[4] = IE_IRQ2; cpu_irq_nr[4] = BUSCLK; lx_interrupt[SYSCLK].cpu_mask = IE_IRQ3; lx_interrupt[SYSCLK].LX_mask = 0; cpu_mask_tbl[5] = IE_IRQ3; cpu_irq_nr[5] = SYSCLK; lx_interrupt[UARTCLK].cpu_mask = IE_IRQ4; lx_interrupt[UARTCLK].LX_mask = 0; cpu_mask_tbl[6] = IE_IRQ4; cpu_irq_nr[6] = UARTCLK; lx_interrupt[SYSTIMER].cpu_mask = IE_IRQ5; lx_interrupt[SYSTIMER].LX_mask = 0; cpu_mask_tbl[7] = IE_IRQ5; cpu_irq_nr[7] = SYSTIMER; }/* lx_init_lxBP20K */ --- NEW FILE: lxRi.c --- /* * lxRi.c - Reserved instruction handler specific to Lexra processores. */ /* ** Copyright 2001 Lexra, Inc. */ #include "asm/mipsregs.h" #include "asm/ptrace.h" #include "asm/branch.h" #include "asm/asm.h" #include "asm/uaccess.h" #define OP_MASK 0x3f #define OP_SHIFT 26 #define R_MASK 0x1f #define RS_SHIFT 21 #define RT_SHIFT 16 #define RD_SHIFT 11 #define OFFSET_MASK 0xffff #define _OP_(x) (OP_MASK & ((x) >> OP_SHIFT)) #define _OPS_(x) (OP_MASK & (x)) #define _RS_(x) (R_MASK & ((x) >> RS_SHIFT)) #define _RT_(x) (R_MASK & ((x) >> RT_SHIFT)) #define _RD_(x) (R_MASK & ((x) >> RD_SHIFT)) #define _OFFSET_(x) (OFFSET_MASK & (x)) /* get opcode for reserved instruction without verify_area since Lexra's reserved instructions maybe used in both kernel and user mode */ int get_insn_ri(struct pt_regs *regs, unsigned int *opcode) { unsigned int *epc; epc = (unsigned int *) (unsigned long) regs->cp0_epc; if (regs->cp0_cause & CAUSEF_BD) epc ++; *opcode = *epc; return 0; } /* fixup code when address error occurs in lwl/lwr/swl/swr */ void fixup_lxRi(long epc) { siginfo_t info; info.si_code = SIGABRT; info.si_errno = EFAULT; info.si_addr = (void *) epc; force_sig_info(SIGSEGV, &info, current); } /* Lexra-supplied "reserved instruction exception handler" Returns 1 if handled or 0 if not handled. The instruction will not be handled if it is not a Lexra's reserved instruction */ int simulate_lxRI(struct pt_regs *regp, unsigned int inst) { register unsigned long rs; register unsigned long rt; unsigned long va; unsigned long mem = 0; unsigned int byte; int mad_msub; /* In case the emulated zero register is somehow trashed. */ regp->regs[0] = 0; /* ================================= Isolate the two source registers: ================================= */ rs = regp->regs[_RS_(inst)]; rt = regp->regs[_RT_(inst)]; /* ======================================= Calculate the offset and alignment for lwl, lwr, swl, or swr instructions. For these instructions, 'rs' represents the base to which the offset is added: ======================================= */ va = rs + (unsigned long)((short)_OFFSET_(inst)); byte = va & 3; /* ============================================================= Two types of instructions deserve special consideration: lwl, lwr, swl, and swr are unaligned load and store inst- ructions. These four instructions are _always_ implement- ed in software. mult, multu, div, and divu instructions may be either soft- ware emulated or placed in the optional MAC-DIV module. When these instructions are implemented in the MAC-DIC module they do not generate Reserved Instruction (RI) traps. The 8 instructions described above are decoded by the foll- owing switch statement. They may be uniquely identified by bits 26 through 31 (the primary opcode field) and bits 0 through 5 (the subcode field). ============================================================= */ switch (_OP_(inst)) { /*=================================================================== Load Word Left: lwl rt, offset + rs Add the sign-extended offset to base register 'rs'; this is the source address. Copy the byte at this address to the leftmost unwritten byte in 'rt', proceeding from left to right. When the rightmost byte of the source is copied, the operation is complete. =====================================================================*/ case 0x22: /* lwl */ __asm__( "1:\tsubu\t%0,%1,%2\n" "2:\tlw\t%0,0(%0)\n\t" ".section\t__ex_table,\"a\"\n\t" STR(PTR)"\t1b,%3\n\t" STR(PTR)"\t2b,%3\n\t" ".previous" :"=r" (mem) :"r" (va), "r" (byte), "i" (&fixup_lxRi)); mem = mem << byte*8; rt = (rt & ~(-1UL << byte*8)) | mem; regp->regs[_RT_(inst)] = rt; break; /*==================================================================== Load Word Right: lwr rt, offset + rs Add the sign-extended offset to base register 'rs'; this is the source address. Copy the byte at this address to the rightmost unwritten byte in 'rt', proceeding from right to left. When the leftmost byte of the source is copied, the operation is complete. =====================================================================*/ case 0x26: /* lwr */ __asm__( "1:\tsubu\t%0,%1,%2\n" "2:\tlw\t%0,0(%0)\n\t" ".section\t__ex_table,\"a\"\n\t" STR(PTR)"\t1b,%3\n\t" STR(PTR)"\t2b,%3\n\t" ".previous" :"=r" (mem) :"r" (va), "r" (byte), "i" (&fixup_lxRi)); mem = mem >> (3-byte)*8; rt = (rt & ~(-1UL >> (3-byte)*8)) | mem; regp->regs[_RT_(inst)] = rt; break; /*==================================================================== Store Word Left: swl rt, offset + rs Add the sign-extended offset to base register 'rs'; this is the destination address. Proceeding from left to right, copy bytes from the register specified by 'rt' to bytes starting at the destination address. When the rightmost byte of the destination is written, the operation is complete. =====================================================================*/ case 0x2A: /* swl */ __asm__( "1:\tsubu\t%0,%1,%2\n" "2:\tlw\t%0,0(%0)\n\t" ".section\t__ex_table,\"a\"\n\t" STR(PTR)"\t1b,%3\n\t" STR(PTR)"\t2b,%3\n\t" ".previous" :"=r" (mem) :"r" (va), "r" (byte), "i" (&fixup_lxRi)); mem = mem & ~(-1UL >> byte*8); rt = (rt >> byte*8) | mem; *(unsigned long *)(va - byte) = rt; break; /*==================================================================== Store Word Right: swr rt, offset + rs Add the sign-extended offset to base register 'rs'; this is the destination address. Proceeding from right to left, copy bytes from the register specified by 'rt' to bytes starting at the destination address. When the leftmost byte of the destination is written, the operation is complete. =====================================================================*/ case 0x2E: /* swr */ __asm__( "1:\tsubu\t%0,%1,%2\n" "2:\tlw\t%0,0(%0)\n\t" ".section\t__ex_table,\"a\"\n\t" STR(PTR)"\t1b,%3\n\t" STR(PTR)"\t2b,%3\n\t" ".previous" :"=r" (mem) :"r" (va), "r" (byte), "i" (&fixup_lxRi)); mem = mem & ~(-1UL << (3-byte)*8); rt = (rt << (3-byte)*8) | mem; *(unsigned long *)(va - byte) = rt; break; case 0x1C: /* Special2 */ switch (_OPS_(inst)) { case 0x00: /* mad */ mad_msub = 1; goto ri_mult; case 0x01: /* madu */ mad_msub = 1; goto ri_multu; case 0x04: /* msub */ mad_msub = 2; goto ri_mult; case 0x05: /* msubu */ mad_msub = 2; goto ri_multu; } return 0; /* not a Lexra reserved instruction */ case 0x00: /* Special */ mad_msub = 0; switch (_OPS_(inst)) { /* =================================== The signed multiply instruction may be emulated in software or implemented in the optional MAC-DIV Module. =================================== */ case 0x18: /* mult */ { ri_mult: { register unsigned int X = (unsigned int) rs; register unsigned int Y = (unsigned int) rt; register unsigned int i; register unsigned int Mask = 0x80000000; register unsigned int sign = (Mask & X) ^ (Mask & Y); register unsigned int HI = 0; register unsigned int LO = Mask&Y ? -Y : Y; register unsigned int CARRY; X = Mask&X ? -X : X; for (i=0; i<32; i++) { CARRY = 0; if (LO&1) { CARRY = HI; HI += X; CARRY = (HI < CARRY) | (HI < X); } LO >>= 1; if (HI&1) { LO |= Mask; } HI >>= 1; if (CARRY) { HI |= Mask; } } if (sign) { LO = ~LO; HI = ~HI; CARRY = LO; LO += 1; if (LO < CARRY) { HI += 1; } } if (mad_msub) { unsigned long long HI_LO = ((long long)HI << 32) | LO; unsigned long long hi_lo = ((long long)regp->hi << 32) | regp->lo; if (mad_msub == 2) hi_lo -= HI_LO; else hi_lo += HI_LO; regp->lo = hi_lo & 0xffffffff; regp->hi = hi_lo >> 32; } else { regp->hi = HI; regp->lo = LO; } } } break; /* =================================== The unsigned multiply instruction may be emulated in software or implemented in the optional MAC-DIV Module. =================================== */ case 0x19: /* multu */ { ri_multu: { register unsigned int X = (unsigned int) rs; register unsigned int i; register unsigned int Mask = 0x80000000; register unsigned int HI = 0; register unsigned int LO = (unsigned int) rt; register unsigned int CARRY; for (i=0; i<32; i++) { CARRY = 0; if (LO&1) { CARRY = HI; HI += X; CARRY = (HI < CARRY) | (HI < X); } LO >>= 1; if (HI&1) { LO |= Mask; } HI >>= 1; if (CARRY) { HI |= Mask; } } if (mad_msub) { unsigned long long HI_LO = ((long long)HI << 32) | LO; unsigned long long hi_lo = ((long long)regp->hi << 32) | regp->lo; if (mad_msub == 2) hi_lo -= HI_LO; else hi_lo += HI_LO; regp->lo = hi_lo & 0xffffffff; regp->hi = hi_lo >> 32; } else { regp->hi = HI; regp->lo = LO; } } } break; /* =================================== The signed divide instruction may be emulated in software or implemented in the optional MAC-DIV Module. =================================== */ case 0x1a: /* div */ { register unsigned int X = (unsigned int) rs; register unsigned int Y = (unsigned int) rt; register unsigned int i; register unsigned int Mask = 0x80000000; register unsigned int signHI = (Mask & X); register unsigned int sign = signHI ^ (Mask & Y); register unsigned int HI = 0; register unsigned int LO = Mask&X ? -X : X; Y = Mask&Y ? -Y : Y; for (i=0; i<32; i++) { HI <<= 1; if (LO&Mask) HI |= 1; LO <<= 1; if (Y > HI) { LO &= ~1; } else { HI -= Y; LO |= 1; } } LO = sign ? -LO : LO; HI = signHI ? -HI : HI; regp->hi = HI; regp->lo = LO; } break; /* =================================== The unsigned divide instruction may be emulated in software or implemented in the optional MAC-DIV Module. =================================== */ case 0x1b: /* divu */ { register unsigned int X = (unsigned int) rs; register unsigned int Y = (unsigned int) rt; register unsigned int i; register unsigned int Mask = 0x80000000; register unsigned int HI = 0; register unsigned int LO = X; for (i=0; i<32; i++) { HI <<= 1; if (LO&Mask) HI |= 1; LO <<= 1; if (Y > HI) { LO &= ~1; } else { HI -= Y; LO |= 1; } } regp->hi = HI; regp->lo = LO; } break; default: return 0; /* not a special Lexra reserved instruction */ } /* end of switch(_OPS_(inst)) */ break; /* special instructions */ default: /* for switch(_OP_(inst)) */ do_ri(regp); return 0; /* not a Lexra reserved instruction */ } /* end of switch(_OP_(inst)) */ return 1; /* reserved instruction handled */ } void do_lxRi(struct pt_regs *regs) { unsigned int opcode; get_insn_ri(regs, &opcode); simulate_lxRI(regs, opcode); compute_return_epc(regs); return; } --- NEW FILE: lx_io.c --- /** * @file lx_io.c * Lexra specifice io routines. */ #include <asm/io.h> #include <asm/byteorder.h> #include <asm/addrspace.h> #include <asm/lexra/lx_defs.h> /** * Function for byte Input. * @param addr pointer to the address of the byte * @return the byte requested */ unsigned char lx_inb(const unsigned char *addr) { unsigned int ia = (unsigned int)(addr); return *(volatile unsigned char *)KSEG1ADDR(ia); } /** * Function for byte Output. * @param val the byte of data to be output * @param addr pointer to the destination address of the byte */ void lx_outb( unsigned char val, unsigned char *addr) { unsigned int ia = (unsigned int)(addr); *(volatile unsigned char *)KSEG1ADDR(ia) = val; } /** * Function for word (16 bit) Input. * @param addr pointer to the address of the word * @return the word requested */ unsigned short lx_inw(const unsigned short *addr) { unsigned int ia = (unsigned int)(addr); if(PHYSADDR(ia) > LX_PCI_TOP) return *(volatile unsigned short *)KSEG1ADDR(ia); else return le16_to_cpu(*(volatile unsigned short *)KSEG1ADDR(ia)); } /** * Function for word (16 bit) Output. * @param val the byte of word to be output * @param addr pointer to the destination address of the word */ void lx_outw( unsigned short val, unsigned short *addr) { unsigned int ia = (unsigned int)(addr); if(PHYSADDR(ia) > LX_PCI_TOP) *(volatile unsigned short *)KSEG1ADDR(ia) = val; else *(volatile unsigned short *) (KSEG1ADDR(ia)) = cpu_to_le16(val); } /** * Function for long word (32 bit) Input. * @param addr pointer to the address of the long word * @return the long word requested */ unsigned int lx_inl(const unsigned int *addr) { unsigned int ia = (unsigned int)(addr); if(PHYSADDR(ia) > LX_PCI_TOP) return *(volatile unsigned int *)KSEG1ADDR(ia); else return le32_to_cpu(*(volatile unsigned int *) KSEG1ADDR(ia)); } /** * Function for long word (32 bit) Output. * @param val the long word of data to be output * @param addr pointer to the destination address of the long word */ void lx_outl( unsigned int val, unsigned int *addr) { unsigned int ia = (unsigned int)(addr); if(PHYSADDR(ia) > LX_PCI_TOP) *(volatile unsigned int *)KSEG1ADDR(ia) = val; else *(volatile unsigned int *)KSEG1ADDR(ia) = cpu_to_le32(val); } /** * Function for multiple long word (32 bit) Input. Count long words * are read starting at the address specified by port and placed in * the location specified by buffer. * @param port Pointer to the address of the long words to be input. * @param buffer Pointer to the buffer for the input data. * @param count Number of words to input. */ void lx_insl(unsigned int port, void *buffer, unsigned long count) { unsigned long *buf=buffer; while(count--) *buf++=inl(port); } /** * Function for multiple long word (32 bit) Output. Count long words * are read starting at the address specified by buffer and placed in * the location specified by port. * @param port Pointer to the destination address for the long words. * @param buffer The buffer of long words to be output. * @param count The number of long words to output. */ void lx_outsl(unsigned int port, const void *buffer, unsigned long count) { const unsigned long *buf=buffer; while(count--) outl(*buf++, port); } --- NEW FILE: lxpci.c --- /* * ######################################################################## * * This program is free software; you can distribute it and/or modify it * under the terms of the GNU General Public License (Version 2) as * published by the Free Software Foundation. * * This program is distributed in the hope 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. * * ######################################################################## */ /** @file lxpci.c * Lexra PCI support functions. This file contains functions used to setup, configure * and access PCI devices conected to the Lexra board via PCI. */ #include <linux/config.h> #ifdef CONFIG_PCI #include <linux/types.h> #include <linux/pci.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/delay.h> #include <asm/irq.h> #include <linux/interrupt.h> #include <asm/io.h> #include <asm/lexra/lx_defs.h> #include <asm/lexra/lxirq.h> /** * External function defined in irq.c. */ extern void enable_irq(unsigned int irq_nr); /** * External function defined in irq.c. */ extern int get_irq_list(char * buf); /** * Variable to allow disabling of PCI bus probing. */ static unsigned int pci_probe=1; /** * number of PCI devices found in initialization */ int pci_devices_count = 0; /** * starting address of memory-mapped PCI devices */ static unsigned int nextStartAddr = LX_PCI_BASE_0; /** * The general configuration space access funtion. The function is uses the LX_PCI_WRITE * and LX_PCI_READ functions to perform the PCI config space access. * @param access_type Parameter to specify read or write access. * @param dev Pointer to the pci_dev structure. * @param where Offset of config register to access. * @param data Pointer to the data to write or where to store the read data. */ static int mips_pcibios_config_access(unsigned char access_type, struct pci_dev *dev, unsigned char where, u32 *data) { unsigned char bus = dev->bus->number; unsigned char dev_fn = dev->devfn; /** * Setup address */ LX_PCI_WRITE(LX_PCI0_CFGADDR_OFS, (bus << LX_PCI0_CFGADDR_BUSNUM_SHF) | (dev_fn << LX_PCI0_CFGADDR_FUNCTNUM_SHF) | ((where / 4) << LX_PCI0_CFGADDR_REGNUM_SHF) | LX_PCI0_CFGADDR_CONFIGEN_BIT); if (access_type == PCI_ACCESS_WRITE) { LX_PCI_WRITE(LX_PCI0_CFGDATA_OFS, cpu_to_le32(*data)); } else { LX_PCI_READ(LX_PCI0_CFGDATA_OFS, *data); *data = le32_to_cpu(*data); } return 0; } /** * Function to read a byte value from PCI config space. * We can't address 8 and 16 bit words directly. Instead we have to * read/write a 32bit word and mask/modify the data we actually want. * @param dev Pointer to the pci_dev structure for the device. * @param where Offset of the config register to access. * @param data Pointer to store the read data. * @return Returns -1 if error, PCIBIOS_SUCCESSFUL otherwise. */ static int mips_pcibios_read_config_byte (struct pci_dev *dev, int where, u8 *val) { u32 data = 0; if (mips_pcibios_config_access(PCI_ACCESS_READ, dev, where, &data)) return -1; *val = (data >> ((where & 3) << 3)) & 0xff; return PCIBIOS_SUCCESSFUL; } /** * Function to read a word value from PCI config space. * We can't address 8 and 16 bit words directly. Instead we have to * read/write a 32bit word and mask/modify the data we actually want. * @param dev Pointer to the pci_dev structure for the device. * @param where Offset of the config register to access. * @param data Pointer to store the read data. * @return Returns -1 if error, PCIBIOS_SUCCESSFUL otherwise. */ static int mips_pcibios_read_config_word (struct pci_dev *dev, int where, u16 *val) { u32 data = 0; if (where & 1) return PCIBIOS_BAD_REGISTER_NUMBER; if (mips_pcibios_config_access(PCI_ACCESS_READ, dev, where, &data)) return -1; *val = (data >> ((where & 3) << 3)) & 0xffff; return PCIBIOS_SUCCESSFUL; } /** * Function to read a dword value from PCI config space. * @param dev Pointer to the pci_dev structure for the device. * @param where Offset of the config register to access. * @param data Pointer to store the read data. * @return Returns -1 if error, PCIBIOS_SUCCESSFUL otherwise. */ static int mips_pcibios_read_config_dword (struct pci_dev *dev, int where, u32 *val) { u32 data = 0; if (where & 3) return PCIBIOS_BAD_REGISTER_NUMBER; if (mips_pcibios_config_access(PCI_ACCESS_READ, dev, where, &data)) return -1; *val = data; return PCIBIOS_SUCCESSFUL; } /** * Function to write a byte value to PCI config space. * @param dev Pointer to the pci_dev structure for the device. * @param where Offset of config register to access. * @param val The data to write. * @return Returns -1 if error, PCIBIOS_SUCCESSFUL otherwise */ static int mips_pcibios_write_config_byte (struct pci_dev *dev, int where, u8 val) { u32 data = 0; if (mips_pcibios_config_access(PCI_ACCESS_READ, dev, where, &data)) return -1; data = (data & ~(0xff << ((where & 3) << 3))) | (val << ((where & 3) << 3)); if (mips_pcibios_config_access(PCI_ACCESS_WRITE, dev, where, &data)) return -1; return PCIBIOS_SUCCESSFUL; } /** * Function to write a word value to PCI config space. * @param dev Pointer to the pci_dev structure for the device. * @param where Offset of config register to access. * @param val The data to write. * @return Returns -1 if error, PCIBIOS_SUCCESSFUL otherwise */ static int mips_pcibios_write_config_word (struct pci_dev *dev, int where, u16 val) { u32 data = 0; if (where & 1) return PCIBIOS_BAD_REGISTER_NUMBER; if (mips_pcibios_config_access(PCI_ACCESS_READ, dev, where, &data)) return -1; data = (data & ~(0xffff << ((where & 3) << 3))) | (val << ((where & 3) << 3)); if (mips_pcibios_config_access(PCI_ACCESS_WRITE, dev, where, &data)) return -1; return PCIBIOS_SUCCESSFUL; } /** * Function to write a dword value to PCI config space. * @param dev Pointer to the pci_dev structure for the device. * @param where Offset of config register to access. * @param val The data to write. * @return Returns -1 if error, PCIBIOS_SUCCESSFUL otherwise */ static int mips_pcibios_write_config_dword(struct pci_dev *dev, int where, u32 val) { if (where & 3) return PCIBIOS_BAD_REGISTER_NUMBER; if (mips_pcibios_config_access(PCI_ACCESS_WRITE, dev, where, &val)) return -1; return PCIBIOS_SUCCESSFUL; } /** * Stucture to store pointers to Lexra specific PCI functions. */ struct pci_ops mips_pci_ops = { mips_pcibios_read_config_byte, mips_pcibios_read_config_word, mips_pcibios_read_config_dword, mips_pcibios_write_config_byte, mips_pcibios_write_config_word, mips_pcibios_write_config_dword }; /** * Lexra resources structure */ struct { /** * RAM resource tracking structure. */ struct resource ram; /** * PCI memory resource tracking structure. */ struct resource pci_mem; } Lexra_resources = { { "RAM", 0x00000000, 0x03ffffff, IORESOURCE_MEM | PCI_BASE_ADDRESS_MEM_TYPE_32}, { "Lexra PCI mem", LX_PCI_BASE_0, (LX_PCI_BASE_1 + 0xffffff), IORESOURCE_MEM} }; /** * Function to determine the size of PCI memory space required by a PCI device. * @param base The base address read from the devices config space. * @param mask IO or memory space mask used to determine size. * @return Returns the size of the memory space requested by the device. */ static u32 lx_pci_size(u32 base, unsigned long mask) { u32 size = mask & base; /* Find the significant bits */ /* Get the lowest of them to find the decode size */ size = size & ~(size-1); return size-1; /* extent = size - 1 */ } static void __init setup_pci_dev(struct pci_dev *dev) { unsigned int pos, reg, tmp, MEMsize; u32 l, sz; unsigned int nIOBaseAddrs = 0; unsigned int nMemBaseAddrs = 0; /** * We must ofset all devices base address registers * by 0x08000000 and let them know the cache line size. */ nIOBaseAddrs = 0; nMemBaseAddrs = 0; pci_devices_count++; for(pos=0; pos<6; pos++) { if (dev->resource[pos].start != dev->resource[pos].end) { reg = PCI_BASE_ADDRESS_0 + (pos << 2); pci_read_config_dword(dev, reg, &tmp); if ((tmp & 0x1) == 1) { printk("Lexra does not support PCI IO \n"); #if 0 /* * this block of code was put it for an Ethernet * card driver that required IO space to be * allocated for its init code to pass */ dev->resource[pos-nIOBaseAddrs].start = dev->resource[pos].start + nextStartAddr; dev->resource[pos-nIOBaseAddrs].end = dev->resource[pos].end + nextStartAddr; nextStartAddr += (dev->resource[pos-nIOBaseAddrs].end - dev->resource[pos-nIOBaseAddrs].start) +1; reg = PCI_BASE_ADDRESS_0 + (pos << 2); pci_write_config_dword(dev, reg, dev->resource[pos-nIOBaseAddrs].start); #endif nIOBaseAddrs++; } else { MEMsize = dev->resource[pos-nIOBaseAddrs].end - dev->resource[pos-nIOBaseAddrs].start; if(MEMsize > (1<<20)){ printk("Device %d attempted to request \ more that 64 MB of PCI memory\n" "Allocated 64 MB.\n", dev->device); dev->resource[pos-nIOBaseAddrs].end = dev->resource[pos-nIOBaseAddrs].start +(1<<20); } if( !request_resource(&(Lexra_resources.pci_mem), &dev->resource[pos-nIOBaseAddrs]) ) { printk("Resources unavailable for PCI \ device %d\n", dev->device); } else { tmp = nextStartAddr + MEMsize; /* alignment! */ nextStartAddr =( tmp & ~MEMsize ); dev->resource[pos-nIOBaseAddrs].start = dev->resource[pos].start + nextStartAddr; dev->resource[pos-nIOBaseAddrs].end = dev->resource[pos].end + nextStartAddr; nextStartAddr += (dev->resource[pos-nIOBaseAddrs].end - dev->resource[pos-nIOBaseAddrs].start)+1; reg = PCI_BASE_ADDRESS_0 + (pos << 2); pci_write_config_dword(dev, reg, dev->resource[pos-nIOBaseAddrs].start); printk(KERN_DEBUG "PCI mem at 0x%x\n", (unsigned int)dev->resource[pos-nIOBaseAddrs].start); nMemBaseAddrs++; if (nIOBaseAddrs != 0) { dev->resource[pos].start = 0; dev->resource[pos].end = 0; } } /* if !request_resource */ } /* if IO */ } /* if start != end */ } /* for pos = ... */ if (nMemBaseAddrs == 0) { printk("PCI Device %d does not support PCI memory accesses \n" "and is therefore, unsupported.\n", dev->device); return; } /** * Allocate space for expansion rom */ pci_read_config_dword(dev, PCI_ROM_ADDRESS, &l); pci_write_config_dword(dev, PCI_ROM_ADDRESS, ~PCI_ROM_ADDRESS_ENABLE); pci_read_config_dword(dev, PCI_ROM_ADDRESS, &sz); if (l == 0xffffffff) l = 0; if (sz && sz != 0xffffffff) { dev->resource[PCI_ROM_RESOURCE].flags = (l & PCI_ROM_ADDRESS_ENABLE) | IORESOURCE_MEM | IORESOURCE_PREFETCH | IORESOURCE_READONLY | IORESOURCE_CACHEABLE; sz = lx_pci_size(sz, PCI_ROM_ADDRESS_MASK); dev->resource[PCI_ROM_RESOURCE].start = (nextStartAddr + sz) & PCI_ROM_ADDRESS_MASK; dev->resource[PCI_ROM_RESOURCE].end = dev->resource[PCI_ROM_RESOURCE].start + (unsigned long) sz; pci_write_config_dword(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | 0x1); nextStartAddr = dev->resource[PCI_ROM_RESOURCE].end + 1; } /** * Set the cache line size */ pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, L1_CACHE_BYTES/4); /** * Finally we must assign an IRQ to each of the devices */ pci_write_config_byte(dev, PCI_INTERRUPT_LINE, PCI_ABCD); dev->irq = PCI_ABCD; /** * Enable busmastering and memory space accesses */ pci_write_config_byte(dev, PCI_COMMAND, 0x6); /** * If the device has a BIST execute it */ l = 0; pci_read_config_byte(dev, PCI_BIST, (unsigned char *)&l); if ( l && PCI_BIST_CAPABLE > 0) { pci_write_config_byte(dev, PCI_BIST, PCI_BIST_START | PCI_BIST_CAPABLE); mdelay(2000); /* delay 2 sec to allow test to complete */ pci_read_config_byte(dev, PCI_BIST, (unsigned char *)&l); if (l && PCI_BIST_START != 0) printk("BIST timed out\n"); else if (l && PCI_BIST_CODE_MASK != 0) printk("BIST failed with code: 0x%x\n", (l && PCI_BIST_CODE_MASK)); else printk("BIST passed\n"); } } /** * The Lexra bios fixup function. This function acts as a bios by allocating * resources to all of the PCI devices found on the bus. These resources * include PCI memory space and interrupts. It should be noted that the Lexra * PCI implementation cannot perform IO accesses. For this reason PCI devices * that cannot be communicated with through memory access will not operate * with the Lexra board. This function also initiates device Built In Self * Tests (BIST) if available. Finally the four PCI interrupts are initialized * and their handlers are registered. * @param c Pointer to the pci_bus structure filled when the kernal initially * walked the bus */ void __init lx_pcibios_fixup(struct pci_bus *c) { unsigned int tmp; struct pci_dev *dev; #ifdef LX_DEBUG char buff[256]; #endif pci_for_each_dev(dev) { printk(KERN_DEBUG "Vendor: 0x%04x Device: 0x%04x\n", dev->vendor, dev->device); if (dev->vendor == PCI_VENDOR_ID_LEXRA && dev->device == PCI_DEVICE_ID_LXPB20K) { dev->resource[0] = Lexra_resources.pci_mem; pci_write_config_dword(dev, PCI_BASE_ADDRESS_0,0); pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &tmp); #ifdef PCI_DEBUG printk("PCI Base 0: %08x\n",tmp); #endif dev->resource[1] = Lexra_resources.pci_mem; pci_write_config_dword(dev, PCI_BASE_ADDRESS_1, 0x04000000); pci_read_config_dword(dev, PCI_BASE_ADDRESS_1, &tmp); #ifdef PCI_DEBUG printk("PCI Base 1: %08x\n",tmp); #endif } else { setup_pci_dev(dev); } } #ifdef LX_DEBUG get_irq_list(&buff); printk(&buff); printk("End fixup Bus\n"); #endif } /** * Called after each bus is probed, but before its children * are examined. Function simply calls the function lx_pcibios_fixup(). * @param c Pointer to the pci_bus structure */ void __init pcibios_fixup_bus(struct pci_bus *c) { printk(KERN_DEBUG "PCI: pcibios_fixup_bus\n"); lx_pcibios_fixup(c); } /** * Function to initiate the scan of the pci bus unless disabled * with the command line option 'pci=off'. */ void __init pcibios_init(void) { if(pci_probe != 0) { printk("PCI: Probing PCI hardware on host bus 0.\n"); pci_scan_bus(0, &mips_pci_ops, NULL); } else { printk("PCI disabled from command line.\n"); } } /** * Bogus PCI enable function, all devices are enabled at startup. * @param dev Pointer to the devices pci_dev structure */ int __init pcibios_enable_device(struct pci_dev *dev) { /* Not needed, since we enable all devices at startup. */ return 0; } /** * Stubbed pci function, not required in Lexra Linux implementation. */ void __init pcibios_align_resource(void *data, struct resource *res, unsigned long size) { } /** * Function which determines if PCI has been disabled from the kernel * command line with the option 'pci=off'. */ char * __init pcibios_setup(char *str) { /* Nothing to do for now. */ if(strcmp(str, "off")==0) { pci_probe = 0; return NULL; } else pci_probe = 1; return str; } /** * Array to store pci_fixup structures. Currently unused in Lexra Linux implementation. */ struct pci_fixup pcibios_fixups[] = { { 0 } }; /** * Function provided to configure a pci resource during initialization. * @param dev Pointer to the devices pci_dev structure * @param root Pointer to the root resource structure * @param res Pointer to the requested resource structure * @param resource Index of the the resource in the devices PCI configuration * space. */ void __init pcibios_update_resource(struct pci_dev *dev, struct resource *root, struct resource *res, int resource) { unsigned long where, size; u32 reg; where = PCI_BASE_ADDRESS_0 + (resource * 4); size = res->end - res->start; pci_read_config_dword(dev, where, ®); reg ... [truncated message content] |