From: James S. <jsi...@us...> - 2002-03-14 22:32:29
|
Update of /cvsroot/linuxconsole/ruby/linux/arch/ppc/platforms In directory usw-pr-cvs1:/tmp/cvs-serv14110/linux/arch/ppc/platforms Added Files: apus_setup.c chrp_setup.c gemini_setup.c iSeries_setup.c oak_setup.c pmac_setup.c pplus_setup.c prep_setup.c sandpoint_setup.c spruce_setup.c Log Message: synced to 2.5.5 --- NEW FILE: apus_setup.c --- /* * BK Id: %F% %I% %G% %U% %#% */ /* * arch/ppc/platforms/apus_setup.c * * Copyright (C) 1998, 1999 Jesper Skov * * Basically what is needed to replace functionality found in * arch/m68k allowing Amiga drivers to work under APUS. * Bits of code and/or ideas from arch/m68k and arch/ppc files. * * TODO: * This file needs a *really* good cleanup. Restructure and optimize. * Make sure it can be compiled for non-APUS configs. Begin to move * Amiga specific stuff into mach/amiga. */ #include <linux/config.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/init.h> #include <linux/blk.h> #include <linux/seq_file.h> /* Needs INITSERIAL call in head.S! */ #undef APUS_DEBUG #include <asm/bootinfo.h> #include <asm/setup.h> #include <asm/amigahw.h> #include <asm/amigaints.h> #include <asm/amigappc.h> #include <asm/pgtable.h> #include <asm/dma.h> #include <asm/machdep.h> #include <asm/time.h> unsigned long m68k_machtype; char debug_device[6] = ""; extern void amiga_init_IRQ(void); extern void apus_setup_pci_ptrs(void); void (*mach_sched_init) (void (*handler)(int, void *, struct pt_regs *)) __initdata = NULL; /* machine dependent irq functions */ void (*mach_init_IRQ) (void) __initdata = NULL; void (*(*mach_default_handler)[]) (int, void *, struct pt_regs *) = NULL; void (*mach_get_model) (char *model) = NULL; int (*mach_get_hardware_list) (char *buffer) = NULL; int (*mach_get_irq_list) (struct seq_file *, void *) = NULL; void (*mach_process_int) (int, struct pt_regs *) = NULL; /* machine dependent timer functions */ unsigned long (*mach_gettimeoffset) (void); void (*mach_gettod) (int*, int*, int*, int*, int*, int*); int (*mach_hwclk) (int, struct hwclk_time*) = NULL; int (*mach_set_clock_mmss) (unsigned long) = NULL; void (*mach_reset)( void ); long mach_max_dma_address = 0x00ffffff; /* default set to the lower 16MB */ #if defined(CONFIG_AMIGA_FLOPPY) void (*mach_floppy_setup) (char *, int *) __initdata = NULL; #endif #ifdef CONFIG_HEARTBEAT void (*mach_heartbeat) (int) = NULL; extern void apus_heartbeat (void); #endif extern unsigned long amiga_model; extern unsigned decrementer_count;/* count value for 1e6/HZ microseconds */ extern unsigned count_period_num; /* 1 decrementer count equals */ extern unsigned count_period_den; /* count_period_num / count_period_den us */ int num_memory = 0; struct mem_info memory[NUM_MEMINFO];/* memory description */ /* FIXME: Duplicate memory data to avoid conflicts with m68k shared code. */ int m68k_realnum_memory = 0; struct mem_info m68k_memory[NUM_MEMINFO];/* memory description */ struct mem_info ramdisk; extern void amiga_floppy_setup(char *, int *); extern void config_amiga(void); static int __60nsram = 0; /* for cpuinfo */ static int __bus_speed = 0; static int __speed_test_failed = 0; /********************************************** COMPILE PROTECTION */ /* Provide some stubs that links to Amiga specific functions. * This allows CONFIG_APUS to be removed from generic PPC files while * preventing link errors for other PPC targets. */ unsigned long apus_get_rtc_time(void) { #ifdef CONFIG_APUS extern unsigned long m68k_get_rtc_time(void); return m68k_get_rtc_time (); #else return 0; #endif } int apus_set_rtc_time(unsigned long nowtime) { #ifdef CONFIG_APUS extern int m68k_set_rtc_time(unsigned long nowtime); return m68k_set_rtc_time (nowtime); #else return 0; #endif } /*********************************************************** SETUP */ /* From arch/m68k/kernel/setup.c. */ void __init apus_setup_arch(void) { #ifdef CONFIG_APUS extern char cmd_line[]; int i; char *p, *q; /* Let m68k-shared code know it should do the Amiga thing. */ m68k_machtype = MACH_AMIGA; /* Parse the command line for arch-specific options. * For the m68k, this is currently only "debug=xxx" to enable printing * certain kernel messages to some machine-specific device. */ for( p = cmd_line; p && *p; ) { i = 0; if (!strncmp( p, "debug=", 6 )) { strncpy( debug_device, p+6, sizeof(debug_device)-1 ); debug_device[sizeof(debug_device)-1] = 0; if ((q = strchr( debug_device, ' ' ))) *q = 0; i = 1; } else if (!strncmp( p, "60nsram", 7 )) { APUS_WRITE (APUS_REG_WAITSTATE, REGWAITSTATE_SETRESET |REGWAITSTATE_PPCR |REGWAITSTATE_PPCW); __60nsram = 1; i = 1; } if (i) { /* option processed, delete it */ if ((q = strchr( p, ' ' ))) strcpy( p, q+1 ); else *p = 0; } else { if ((p = strchr( p, ' ' ))) ++p; } } config_amiga(); #if 0 /* Enable for logging - also include logging.o in Makefile rule */ { #define LOG_SIZE 4096 void* base; /* Throw away some memory - the P5 firmare stomps on top * of CHIP memory during bootup. */ amiga_chip_alloc(0x1000); base = amiga_chip_alloc(LOG_SIZE+sizeof(klog_data_t)); LOG_INIT(base, base+sizeof(klog_data_t), LOG_SIZE); } #endif #endif } int apus_show_cpuinfo(struct seq_file *m) { extern int __map_without_bats; extern unsigned long powerup_PCI_present; seq_printf(m, "machine\t\t: Amiga\n"); seq_printf(m, "bus speed\t: %d%s", __bus_speed, (__speed_test_failed) ? " [failed]\n" : "\n"); seq_printf(m, "using BATs\t: %s\n", (__map_without_bats) ? "No" : "Yes"); seq_printf(m, "ram speed\t: %dns\n", (__60nsram) ? 60 : 70); seq_printf(m, "PCI bridge\t: %s\n", (powerup_PCI_present) ? "Yes" : "No"); return 0; } static void get_current_tb(unsigned long long *time) { __asm __volatile ("1:mftbu 4 \n\t" " mftb 5 \n\t" " mftbu 6 \n\t" " cmpw 4,6 \n\t" " bne 1b \n\t" " stw 4,0(%0)\n\t" " stw 5,4(%0)\n\t" : : "r" (time) : "r4", "r5", "r6"); } void apus_calibrate_decr(void) { #ifdef CONFIG_APUS unsigned long freq; /* This algorithm for determining the bus speed was contributed by Ralph Schmidt. */ unsigned long long start, stop; int bus_speed; int speed_test_failed = 0; { unsigned long loop = amiga_eclock / 10; get_current_tb (&start); while (loop--) { unsigned char tmp; tmp = ciaa.pra; } get_current_tb (&stop); } bus_speed = (((unsigned long)(stop-start))*10*4) / 1000000; if (AMI_1200 == amiga_model) bus_speed /= 2; if ((bus_speed >= 47) && (bus_speed < 53)) { bus_speed = 50; freq = 12500000; } else if ((bus_speed >= 57) && (bus_speed < 63)) { bus_speed = 60; freq = 15000000; } else if ((bus_speed >= 63) && (bus_speed < 69)) { bus_speed = 67; freq = 16666667; } else { printk ("APUS: Unable to determine bus speed (%d). " "Defaulting to 50MHz", bus_speed); bus_speed = 50; freq = 12500000; speed_test_failed = 1; } /* Ease diagnostics... */ { extern int __map_without_bats; extern unsigned long powerup_PCI_present; printk ("APUS: BATs=%d, BUS=%dMHz", (__map_without_bats) ? 0 : 1, bus_speed); if (speed_test_failed) printk ("[FAILED - please report]"); printk (", RAM=%dns, PCI bridge=%d\n", (__60nsram) ? 60 : 70, (powerup_PCI_present) ? 1 : 0); /* print a bit more if asked politely... */ if (!(ciaa.pra & 0x40)){ extern unsigned int bat_addrs[4][3]; int b; for (b = 0; b < 4; ++b) { printk ("APUS: BAT%d ", b); printk ("%08x-%08x -> %08x\n", bat_addrs[b][0], bat_addrs[b][1], bat_addrs[b][2]); } } } printk("time_init: decrementer frequency = %lu.%.6lu MHz\n", freq/1000000, freq%1000000); tb_ticks_per_jiffy = freq / HZ; tb_to_us = mulhwu_scale_factor(freq, 1000000); __bus_speed = bus_speed; __speed_test_failed = speed_test_failed; #endif } void arch_gettod(int *year, int *mon, int *day, int *hour, int *min, int *sec) { #ifdef CONFIG_APUS if (mach_gettod) mach_gettod(year, mon, day, hour, min, sec); else *year = *mon = *day = *hour = *min = *sec = 0; #endif } /*********************************************************** FLOPPY */ #if defined(CONFIG_AMIGA_FLOPPY) __init void floppy_setup(char *str, int *ints) { if (mach_floppy_setup) mach_floppy_setup (str, ints); } #endif /*********************************************************** MEMORY */ #define KMAP_MAX 32 unsigned long kmap_chunks[KMAP_MAX*3]; int kmap_chunk_count = 0; /* From pgtable.h */ static __inline__ pte_t *my_find_pte(struct mm_struct *mm,unsigned long va) { pgd_t *dir = 0; pmd_t *pmd = 0; pte_t *pte = 0; va &= PAGE_MASK; dir = pgd_offset( mm, va ); if (dir) { pmd = pmd_offset(dir, va & PAGE_MASK); if (pmd && pmd_present(*pmd)) { pte = pte_offset(pmd, va); } } return pte; } /* Again simulating an m68k/mm/kmap.c function. */ void kernel_set_cachemode( unsigned long address, unsigned long size, unsigned int cmode ) { unsigned long mask, flags; switch (cmode) { case IOMAP_FULL_CACHING: mask = ~(_PAGE_NO_CACHE | _PAGE_GUARDED); flags = 0; break; case IOMAP_NOCACHE_SER: mask = ~0; flags = (_PAGE_NO_CACHE | _PAGE_GUARDED); break; default: panic ("kernel_set_cachemode() doesn't support mode %d\n", cmode); break; } size /= PAGE_SIZE; address &= PAGE_MASK; while (size--) { pte_t *pte; pte = my_find_pte(&init_mm, address); if ( !pte ) { printk("pte NULL in kernel_set_cachemode()\n"); return; } pte_val (*pte) &= mask; pte_val (*pte) |= flags; flush_tlb_page(find_vma(&init_mm,address),address); address += PAGE_SIZE; } } unsigned long mm_ptov (unsigned long paddr) { unsigned long ret; if (paddr < 16*1024*1024) ret = ZTWO_VADDR(paddr); else { int i; for (i = 0; i < kmap_chunk_count;){ unsigned long phys = kmap_chunks[i++]; unsigned long size = kmap_chunks[i++]; unsigned long virt = kmap_chunks[i++]; if (paddr >= phys && paddr < (phys + size)){ ret = virt + paddr - phys; goto exit; } } ret = (unsigned long) __va(paddr); } exit: #ifdef DEBUGPV printk ("PTOV(%lx)=%lx\n", paddr, ret); #endif return ret; } int mm_end_of_chunk (unsigned long addr, int len) { if (memory[0].addr + memory[0].size == addr + len) return 1; return 0; } /*********************************************************** CACHE */ #define L1_CACHE_BYTES 32 #define MAX_CACHE_SIZE 8192 void cache_push(__u32 addr, int length) { addr = mm_ptov(addr); if (MAX_CACHE_SIZE < length) length = MAX_CACHE_SIZE; while(length > 0){ __asm ("dcbf 0,%0\n\t" : : "r" (addr)); addr += L1_CACHE_BYTES; length -= L1_CACHE_BYTES; } /* Also flush trailing block */ __asm ("dcbf 0,%0\n\t" "sync \n\t" : : "r" (addr)); } void cache_clear(__u32 addr, int length) { if (MAX_CACHE_SIZE < length) length = MAX_CACHE_SIZE; addr = mm_ptov(addr); __asm ("dcbf 0,%0\n\t" "sync \n\t" "icbi 0,%0 \n\t" "isync \n\t" : : "r" (addr)); addr += L1_CACHE_BYTES; length -= L1_CACHE_BYTES; while(length > 0){ __asm ("dcbf 0,%0\n\t" "sync \n\t" "icbi 0,%0 \n\t" "isync \n\t" : : "r" (addr)); addr += L1_CACHE_BYTES; length -= L1_CACHE_BYTES; } __asm ("dcbf 0,%0\n\t" "sync \n\t" "icbi 0,%0 \n\t" "isync \n\t" : : "r" (addr)); } /****************************************************** from setup.c */ void apus_restart(char *cmd) { cli(); APUS_WRITE(APUS_REG_LOCK, REGLOCK_BLACKMAGICK1|REGLOCK_BLACKMAGICK2); APUS_WRITE(APUS_REG_LOCK, REGLOCK_BLACKMAGICK1|REGLOCK_BLACKMAGICK3); APUS_WRITE(APUS_REG_LOCK, REGLOCK_BLACKMAGICK2|REGLOCK_BLACKMAGICK3); APUS_WRITE(APUS_REG_SHADOW, REGSHADOW_SELFRESET); APUS_WRITE(APUS_REG_RESET, REGRESET_AMIGARESET); for(;;); } void apus_power_off(void) { for (;;); } void apus_halt(void) { apus_restart(NULL); } /****************************************************** IRQ stuff */ static unsigned char last_ipl[8]; int apus_get_irq(struct pt_regs* regs) { unsigned char ipl_emu, mask; unsigned int level; APUS_READ(APUS_IPL_EMU, ipl_emu); level = (ipl_emu >> 3) & IPLEMU_IPLMASK; mask = IPLEMU_SETRESET|IPLEMU_DISABLEINT|level; level ^= 7; /* Save previous IPL value */ if (last_ipl[level]) return -2; last_ipl[level] = ipl_emu; /* Set to current IPL value */ APUS_WRITE(APUS_IPL_EMU, mask); APUS_WRITE(APUS_IPL_EMU, IPLEMU_DISABLEINT|level); #ifdef __INTERRUPT_DEBUG printk("<%d:%d>", level, ~ipl_emu & IPLEMU_IPLMASK); #endif return level + IRQ_AMIGA_AUTO; } void apus_end_irq(unsigned int irq) { unsigned char ipl_emu; unsigned int level = irq - IRQ_AMIGA_AUTO; #ifdef __INTERRUPT_DEBUG printk("{%d}", ~last_ipl[level] & IPLEMU_IPLMASK); #endif /* Restore IPL to the previous value */ ipl_emu = last_ipl[level] & IPLEMU_IPLMASK; APUS_WRITE(APUS_IPL_EMU, IPLEMU_SETRESET|IPLEMU_DISABLEINT|ipl_emu); last_ipl[level] = 0; ipl_emu ^= 7; APUS_WRITE(APUS_IPL_EMU, IPLEMU_DISABLEINT|ipl_emu); } /****************************************************** debugging */ /* some serial hardware definitions */ #define SDR_OVRUN (1<<15) #define SDR_RBF (1<<14) #define SDR_TBE (1<<13) #define SDR_TSRE (1<<12) #define AC_SETCLR (1<<15) #define AC_UARTBRK (1<<11) #define SER_DTR (1<<7) #define SER_RTS (1<<6) #define SER_DCD (1<<5) #define SER_CTS (1<<4) #define SER_DSR (1<<3) static __inline__ void ser_RTSon(void) { ciab.pra &= ~SER_RTS; /* active low */ } int __debug_ser_out( unsigned char c ) { custom.serdat = c | 0x100; mb(); while (!(custom.serdatr & 0x2000)) barrier(); return 1; } unsigned char __debug_ser_in( void ) { unsigned char c; /* XXX: is that ok?? derived from amiga_ser.c... */ while( !(custom.intreqr & IF_RBF) ) barrier(); c = custom.serdatr; /* clear the interrupt, so that another character can be read */ custom.intreq = IF_RBF; return c; } int __debug_serinit( void ) { unsigned long flags; save_flags (flags); cli(); /* turn off Rx and Tx interrupts */ custom.intena = IF_RBF | IF_TBE; /* clear any pending interrupt */ custom.intreq = IF_RBF | IF_TBE; restore_flags (flags); /* * set the appropriate directions for the modem control flags, * and clear RTS and DTR */ ciab.ddra |= (SER_DTR | SER_RTS); /* outputs */ ciab.ddra &= ~(SER_DCD | SER_CTS | SER_DSR); /* inputs */ #ifdef CONFIG_KGDB /* turn Rx interrupts on for GDB */ custom.intena = IF_SETCLR | IF_RBF; ser_RTSon(); #endif return 0; } void __debug_print_hex(unsigned long x) { int i; char hexchars[] = "0123456789ABCDEF"; for (i = 0; i < 8; i++) { __debug_ser_out(hexchars[(x >> 28) & 15]); x <<= 4; } __debug_ser_out('\n'); __debug_ser_out('\r'); } void __debug_print_string(char* s) { unsigned char c; while((c = *s++)) __debug_ser_out(c); __debug_ser_out('\n'); __debug_ser_out('\r'); } static void apus_progress(char *s, unsigned short value) { __debug_print_string(s); } /****************************************************** init */ /* The number of spurious interrupts */ volatile unsigned int num_spurious; extern struct irqaction amiga_sys_irqaction[AUTO_IRQS]; extern void amiga_enable_irq(unsigned int irq); extern void amiga_disable_irq(unsigned int irq); struct hw_interrupt_type amiga_sys_irqctrl = { typename: "Amiga IPL", end: apus_end_irq, }; struct hw_interrupt_type amiga_irqctrl = { typename: "Amiga ", enable: amiga_enable_irq, disable: amiga_disable_irq, }; #define HARDWARE_MAPPED_SIZE (512*1024) unsigned long __init apus_find_end_of_memory(void) { int shadow = 0; unsigned long total; /* The memory size reported by ADOS excludes the 512KB reserved for PPC exception registers and possibly 512KB containing a shadow of the ADOS ROM. */ { unsigned long size = memory[0].size; /* If 2MB aligned, size was probably user specified. We can't tell anything about shadowing in this case so skip shadow assignment. */ if (0 != (size & 0x1fffff)){ /* Align to 512KB to ensure correct handling of both memfile and system specified sizes. */ size = ((size+0x0007ffff) & 0xfff80000); /* If memory is 1MB aligned, assume shadowing. */ shadow = !(size & 0x80000); } /* Add the chunk that ADOS does not see. by aligning the size to the nearest 2MB limit upwards. */ memory[0].size = ((size+0x001fffff) & 0xffe00000); } ppc_memstart = memory[0].addr; ppc_memoffset = PAGE_OFFSET - PPC_MEMSTART; total = memory[0].size; /* Remove the memory chunks that are controlled by special Phase5 hardware. */ /* Remove the upper 512KB if it contains a shadow of the ADOS ROM. FIXME: It might be possible to disable this shadow HW. Check the booter (ppc_boot.c) */ if (shadow) total -= HARDWARE_MAPPED_SIZE; /* Remove the upper 512KB where the PPC exception vectors are mapped. */ total -= HARDWARE_MAPPED_SIZE; /* Linux/APUS only handles one block of memory -- the one on the PowerUP board. Other system memory is horrible slow in comparison. The user can use other memory for swapping using the z2ram device. */ return total; } static void __init apus_map_io(void) { /* Map PPC exception vectors. */ io_block_mapping(0xfff00000, 0xfff00000, 0x00020000, _PAGE_KERNEL); /* Map chip and ZorroII memory */ io_block_mapping(zTwoBase, 0x00000000, 0x01000000, _PAGE_IO); } __init void apus_init_IRQ(void) { struct irqaction *action; int i; #ifdef CONFIG_PCI apus_setup_pci_ptrs(); #endif for ( i = 0 ; i < AMI_IRQS; i++ ) { irq_desc[i].status = IRQ_LEVEL; if (i < IRQ_AMIGA_AUTO) { irq_desc[i].handler = &amiga_irqctrl; } else { irq_desc[i].handler = &amiga_sys_irqctrl; action = &amiga_sys_irqaction[i-IRQ_AMIGA_AUTO]; if (action->name) setup_irq(i, action); } } amiga_init_IRQ(); } __init void platform_init(unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7) { extern int parse_bootinfo(const struct bi_record *); extern char _end[]; /* Parse bootinfo. The bootinfo is located right after the kernel bss */ parse_bootinfo((const struct bi_record *)&_end); #ifdef CONFIG_BLK_DEV_INITRD /* Take care of initrd if we have one. Use data from bootinfo to avoid the need to initialize PPC registers when kernel is booted via a PPC reset. */ if ( ramdisk.addr ) { initrd_start = (unsigned long) __va(ramdisk.addr); initrd_end = (unsigned long) __va(ramdisk.size + ramdisk.addr); } #endif /* CONFIG_BLK_DEV_INITRD */ ISA_DMA_THRESHOLD = 0x00ffffff; ppc_md.setup_arch = apus_setup_arch; ppc_md.show_cpuinfo = apus_show_cpuinfo; ppc_md.init_IRQ = apus_init_IRQ; ppc_md.get_irq = apus_get_irq; #ifdef CONFIG_HEARTBEAT ppc_md.heartbeat = apus_heartbeat; ppc_md.heartbeat_count = 1; #endif #ifdef APUS_DEBUG __debug_serinit(); ppc_md.progress = apus_progress; #endif ppc_md.init = NULL; ppc_md.restart = apus_restart; ppc_md.power_off = apus_power_off; ppc_md.halt = apus_halt; ppc_md.time_init = NULL; ppc_md.set_rtc_time = apus_set_rtc_time; ppc_md.get_rtc_time = apus_get_rtc_time; ppc_md.calibrate_decr = apus_calibrate_decr; ppc_md.find_end_of_memory = apus_find_end_of_memory; ppc_md.setup_io_mappings = apus_map_io; } --- NEW FILE: chrp_setup.c --- /* * BK Id: %F% %I% %G% %U% %#% */ /* * arch/ppc/platforms/setup.c * * Copyright (C) 1995 Linus Torvalds * Adapted from 'alpha' version by Gary Thomas * Modified by Cort Dougan (co...@cs...) */ /* * bootup setup stuff.. */ #include <linux/config.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/kernel.h> #include <linux/mm.h> #include <linux/stddef.h> #include <linux/unistd.h> #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/user.h> #include <linux/a.out.h> #include <linux/tty.h> #include <linux/major.h> #include <linux/interrupt.h> #include <linux/reboot.h> #include <linux/init.h> #include <linux/pci.h> #include <linux/version.h> #include <linux/adb.h> #include <linux/module.h> #include <linux/delay.h> #include <linux/ide.h> #include <linux/irq.h> #include <linux/seq_file.h> #include <asm/processor.h> #include <asm/io.h> #include <asm/pgtable.h> #include <asm/prom.h> #include <asm/gg2.h> #include <asm/pci-bridge.h> #include <asm/dma.h> #include <asm/machdep.h> #include <asm/irq.h> #include <asm/hydra.h> #include <asm/sections.h> #include <asm/time.h> #include <asm/btext.h> #include <asm/i8259.h> #include <asm/open_pic.h> unsigned long chrp_get_rtc_time(void); int chrp_set_rtc_time(unsigned long nowtime); void chrp_calibrate_decr(void); long chrp_time_init(void); void chrp_find_bridges(void); void chrp_event_scan(void); void rtas_display_progress(char *, unsigned short); void rtas_indicator_progress(char *, unsigned short); void btext_progress(char *, unsigned short); extern unsigned long pmac_find_end_of_memory(void); extern int of_show_percpuinfo(struct seq_file *, int); extern kdev_t boot_dev; extern PTE *Hash, *Hash_end; extern unsigned long Hash_size, Hash_mask; extern int probingmem; extern unsigned long loops_per_jiffy; static int max_width; #ifdef CONFIG_SMP extern struct smp_ops_t chrp_smp_ops; #endif static const char *gg2_memtypes[4] = { "FPM", "SDRAM", "EDO", "BEDO" }; static const char *gg2_cachesizes[4] = { "256 KB", "512 KB", "1 MB", "Reserved" }; static const char *gg2_cachetypes[4] = { "Asynchronous", "Reserved", "Flow-Through Synchronous", "Pipelined Synchronous" }; static const char *gg2_cachemodes[4] = { "Disabled", "Write-Through", "Copy-Back", "Transparent Mode" }; int __chrp chrp_show_cpuinfo(struct seq_file *m) { int i, sdramen; unsigned int t; struct device_node *root; const char *model = ""; root = find_path_device("/"); if (root) model = get_property(root, "model", NULL); seq_printf(m, "machine\t\t: CHRP %s\n", model); /* longtrail (goldengate) stuff */ if (!strncmp(model, "IBM,LongTrail", 13)) { /* VLSI VAS96011/12 `Golden Gate 2' */ /* Memory banks */ sdramen = (in_le32((unsigned *)(gg2_pci_config_base+ GG2_PCI_DRAM_CTRL)) >>31) & 1; for (i = 0; i < (sdramen ? 4 : 6); i++) { t = in_le32((unsigned *)(gg2_pci_config_base+ GG2_PCI_DRAM_BANK0+ i*4)); if (!(t & 1)) continue; switch ((t>>8) & 0x1f) { case 0x1f: model = "4 MB"; break; case 0x1e: model = "8 MB"; break; case 0x1c: model = "16 MB"; break; case 0x18: model = "32 MB"; break; case 0x10: model = "64 MB"; break; case 0x00: model = "128 MB"; break; default: model = "Reserved"; break; } seq_printf(m, "memory bank %d\t: %s %s\n", i, model, gg2_memtypes[sdramen ? 1 : ((t>>1) & 3)]); } /* L2 cache */ t = in_le32((unsigned *)(gg2_pci_config_base+GG2_PCI_CC_CTRL)); seq_printf(m, "board l2\t: %s %s (%s)\n", gg2_cachesizes[(t>>7) & 3], gg2_cachetypes[(t>>2) & 3], gg2_cachemodes[t & 3]); } return 0; } /* * Fixes for the National Semiconductor PC78308VUL SuperI/O * * Some versions of Open Firmware incorrectly initialize the IRQ settings * for keyboard and mouse */ static inline void __init sio_write(u8 val, u8 index) { outb(index, 0x15c); outb(val, 0x15d); } static inline u8 __init sio_read(u8 index) { outb(index, 0x15c); return inb(0x15d); } static void __init sio_fixup_irq(const char *name, u8 device, u8 level, u8 type) { u8 level0, type0, active; /* select logical device */ sio_write(device, 0x07); active = sio_read(0x30); level0 = sio_read(0x70); type0 = sio_read(0x71); if (level0 != level || type0 != type || !active) { printk(KERN_WARNING "sio: %s irq level %d, type %d, %sactive: " "remapping to level %d, type %d, active\n", name, level0, type0, !active ? "in" : "", level, type); sio_write(0x01, 0x30); sio_write(level, 0x70); sio_write(type, 0x71); } } static void __init sio_init(void) { struct device_node *root; if ((root = find_path_device("/")) && !strncmp(get_property(root, "model", NULL), "IBM,LongTrail", 13)) { /* logical device 0 (KBC/Keyboard) */ sio_fixup_irq("keyboard", 0, 1, 2); /* select logical device 1 (KBC/Mouse) */ sio_fixup_irq("mouse", 1, 12, 2); } } void __init chrp_setup_arch(void) { struct device_node *device; /* init to some ~sane value until calibrate_delay() runs */ loops_per_jiffy = 50000000/HZ; #ifdef CONFIG_BLK_DEV_INITRD /* this is fine for chrp */ initrd_below_start_ok = 1; if (initrd_start) ROOT_DEV = mk_kdev(RAMDISK_MAJOR, 0); else #endif ROOT_DEV = to_kdev_t(0x0802); /* sda2 (sda1 is for the kernel) */ /* Lookup PCI host bridges */ chrp_find_bridges(); #ifndef CONFIG_PPC64BRIDGE /* * Temporary fixes for PCI devices. * -- Geert */ hydra_init(); /* Mac I/O */ #endif /* CONFIG_PPC64BRIDGE */ /* * Fix the Super I/O configuration */ sio_init(); /* Get the event scan rate for the rtas so we know how * often it expects a heartbeat. -- Cort */ if ( rtas_data ) { struct property *p; device = find_devices("rtas"); for ( p = device->properties; p && strncmp(p->name, "rtas-event-scan-rate", 20); p = p->next ) /* nothing */ ; if ( p && *(unsigned long *)p->value ) { ppc_md.heartbeat = chrp_event_scan; ppc_md.heartbeat_reset = (HZ/(*(unsigned long *)p->value)*30)-1; ppc_md.heartbeat_count = 1; printk("RTAS Event Scan Rate: %lu (%lu jiffies)\n", *(unsigned long *)p->value, ppc_md.heartbeat_reset ); } } pci_create_OF_bus_map(); } void __chrp chrp_event_scan(void) { unsigned char log[1024]; unsigned long ret = 0; /* XXX: we should loop until the hardware says no more error logs -- Cort */ call_rtas( "event-scan", 4, 1, &ret, 0xffffffff, 0, __pa(log), 1024 ); ppc_md.heartbeat_count = ppc_md.heartbeat_reset; } void __chrp chrp_restart(char *cmd) { printk("RTAS system-reboot returned %d\n", call_rtas("system-reboot", 0, 1, NULL)); for (;;); } void __chrp chrp_power_off(void) { /* allow power on only with power button press */ printk("RTAS power-off returned %d\n", call_rtas("power-off", 2, 1, NULL,0xffffffff,0xffffffff)); for (;;); } void __chrp chrp_halt(void) { chrp_power_off(); } u_int __chrp chrp_irq_cannonicalize(u_int irq) { if (irq == 2) return 9; return irq; } /* * Finds the open-pic node and sets OpenPIC_Addr based on its reg property. * Then checks if it has an interrupt-ranges property. If it does then * we have a distributed open-pic, so call openpic_set_sources to tell * the openpic code where to find the interrupt source registers. */ static void __init chrp_find_openpic(void) { struct device_node *np; int len, i; unsigned int *iranges; void *isu; np = find_type_devices("open-pic"); if (np == NULL || np->n_addrs == 0) return; printk(KERN_INFO "OpenPIC at %x (size %x)\n", np->addrs[0].address, np->addrs[0].size); OpenPIC_Addr = ioremap(np->addrs[0].address, 0x40000); if (OpenPIC_Addr == NULL) { printk(KERN_ERR "Failed to map OpenPIC!\n"); return; } iranges = (unsigned int *) get_property(np, "interrupt-ranges", &len); if (iranges == NULL || len < 2 * sizeof(unsigned int)) return; /* not distributed */ /* * The first pair of cells in interrupt-ranges refers to the * IDU; subsequent pairs refer to the ISUs. */ len /= 2 * sizeof(unsigned int); if (np->n_addrs < len) { printk(KERN_ERR "Insufficient addresses for distributed" " OpenPIC (%d < %d)\n", np->n_addrs, len); return; } if (iranges[1] != 0) { printk(KERN_INFO "OpenPIC irqs %d..%d in IDU\n", iranges[0], iranges[0] + iranges[1] - 1); openpic_set_sources(iranges[0], iranges[1], NULL); } for (i = 1; i < len; ++i) { iranges += 2; printk(KERN_INFO "OpenPIC irqs %d..%d in ISU at %x (%x)\n", iranges[0], iranges[0] + iranges[1] - 1, np->addrs[i].address, np->addrs[i].size); isu = ioremap(np->addrs[i].address, np->addrs[i].size); if (isu != NULL) openpic_set_sources(iranges[0], iranges[1], isu); else printk(KERN_ERR "Failed to map OpenPIC ISU at %x!\n", np->addrs[i].address); } } void __init chrp_init_IRQ(void) { struct device_node *np; int i; unsigned char* chrp_int_ack_special = 0; unsigned char init_senses[NR_IRQS - NUM_8259_INTERRUPTS]; int nmi_irq = -1; for (np = find_devices("pci"); np != NULL; np = np->next) { unsigned int *addrp = (unsigned int *) get_property(np, "8259-interrupt-acknowledge", NULL); if (addrp == NULL) continue; chrp_int_ack_special = (unsigned char *) ioremap(addrp[prom_n_addr_cells(np)-1], 1); break; } if (np == NULL) printk("Cannot find pci to get ack address\n"); chrp_find_openpic(); prom_get_irq_senses(init_senses, NUM_8259_INTERRUPTS, NR_IRQS); OpenPIC_InitSenses = init_senses; OpenPIC_NumInitSenses = NR_IRQS - NUM_8259_INTERRUPTS; openpic_init(1, NUM_8259_INTERRUPTS, chrp_int_ack_special, nmi_irq); for (i = 0; i < NUM_8259_INTERRUPTS; i++) irq_desc[i].handler = &i8259_pic; i8259_init(0); } void __init chrp_init2(void) { #ifdef CONFIG_NVRAM pmac_nvram_init(); #endif request_region(0x20,0x20,"pic1"); request_region(0xa0,0x20,"pic2"); request_region(0x00,0x20,"dma1"); request_region(0x40,0x20,"timer"); request_region(0x80,0x10,"dma page reg"); request_region(0xc0,0x20,"dma2"); if (ppc_md.progress) ppc_md.progress(" Have fun! ", 0x7777); } #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) /* * IDE stuff. */ static int __chrp chrp_ide_check_region(ide_ioreg_t from, unsigned int extent) { return check_region(from, extent); } static void __chrp chrp_ide_request_region(ide_ioreg_t from, unsigned int extent, const char *name) { request_region(from, extent, name); } static void __chrp chrp_ide_release_region(ide_ioreg_t from, unsigned int extent) { release_region(from, extent); } #endif /* * One of the main thing these mappings are needed for is so that * xmon can get to the serial port early on. We probably should * handle the machines with the mpc106 as well as the python (F50) * and the GG2 (longtrail). Actually we should look in the device * tree and do the right thing. */ static void __init chrp_map_io(void) { char *name; /* * The code below tends to get removed, please don't take it out. * The F50 needs this mapping and it you take it out I'll track you * down and slap your hands. If it causes problems please email me. * -- Cort <co...@fs...> */ name = get_property(find_path_device("/"), "name", NULL); if (name && strncmp(name, "IBM-70", 6) == 0 && strstr(name, "-F50")) { io_block_mapping(0x80000000, 0x80000000, 0x10000000, _PAGE_IO); io_block_mapping(0x90000000, 0x90000000, 0x10000000, _PAGE_IO); return; } else { io_block_mapping(0xf8000000, 0xf8000000, 0x04000000, _PAGE_IO); } } void __init chrp_init(unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7) { #ifdef CONFIG_BLK_DEV_INITRD /* take care of initrd if we have one */ if ( r6 ) { initrd_start = r6 + KERNELBASE; initrd_end = r6 + r7 + KERNELBASE; } #endif /* CONFIG_BLK_DEV_INITRD */ ISA_DMA_THRESHOLD = ~0L; DMA_MODE_READ = 0x44; DMA_MODE_WRITE = 0x48; isa_io_base = CHRP_ISA_IO_BASE; /* default value */ ppc_md.setup_arch = chrp_setup_arch; ppc_md.show_percpuinfo = of_show_percpuinfo; ppc_md.show_cpuinfo = chrp_show_cpuinfo; ppc_md.irq_cannonicalize = chrp_irq_cannonicalize; ppc_md.init_IRQ = chrp_init_IRQ; ppc_md.get_irq = openpic_get_irq; ppc_md.init = chrp_init2; ppc_md.restart = chrp_restart; ppc_md.power_off = chrp_power_off; ppc_md.halt = chrp_halt; ppc_md.time_init = chrp_time_init; ppc_md.set_rtc_time = chrp_set_rtc_time; ppc_md.get_rtc_time = chrp_get_rtc_time; ppc_md.calibrate_decr = chrp_calibrate_decr; ppc_md.find_end_of_memory = pmac_find_end_of_memory; ppc_md.setup_io_mappings = chrp_map_io; if (rtas_data) { struct device_node *rtas; unsigned int *p; rtas = find_devices("rtas"); if (rtas != NULL) { if (get_property(rtas, "display-character", NULL)) { ppc_md.progress = rtas_display_progress; p = (unsigned int *) get_property (rtas, "ibm,display-line-length", NULL); if (p) max_width = *p; } else if (get_property(rtas, "set-indicator", NULL)) ppc_md.progress = rtas_indicator_progress; } } #ifdef CONFIG_BOOTX_TEXT if (ppc_md.progress == NULL && boot_text_mapped) ppc_md.progress = btext_progress; #endif #ifdef CONFIG_SMP ppc_md.smp_ops = &chrp_smp_ops; #endif /* CONFIG_SMP */ #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) ppc_ide_md.ide_check_region = chrp_ide_check_region; ppc_ide_md.ide_request_region = chrp_ide_request_region; ppc_ide_md.ide_release_region = chrp_ide_release_region; #endif /* * Print the banner, then scroll down so boot progress * can be printed. -- Cort */ if ( ppc_md.progress ) ppc_md.progress("Linux/PPC "UTS_RELEASE"\n", 0x0); } void __chrp rtas_display_progress(char *s, unsigned short hex) { int width; char *os = s; if ( call_rtas( "display-character", 1, 1, NULL, '\r' ) ) return; width = max_width; while ( *os ) { if ( (*os == '\n') || (*os == '\r') ) width = max_width; else width--; call_rtas( "display-character", 1, 1, NULL, *os++ ); /* if we overwrite the screen length */ if ( width == 0 ) while ( (*os != 0) && (*os != '\n') && (*os != '\r') ) os++; } /*while ( width-- > 0 )*/ call_rtas( "display-character", 1, 1, NULL, ' ' ); } void __chrp rtas_indicator_progress(char *s, unsigned short hex) { call_rtas("set-indicator", 3, 1, NULL, 6, 0, hex); } #ifdef CONFIG_BOOTX_TEXT void btext_progress(char *s, unsigned short hex) { prom_print(s); prom_print("\n"); } #endif /* CONFIG_BOOTX_TEXT */ --- NEW FILE: gemini_setup.c --- /* * BK Id: %F% %I% %G% %U% %#% */ /* * arch/ppc/platforms/setup.c * * Copyright (C) 1995 Linus Torvalds * Adapted from 'alpha' version by Gary Thomas * Modified by Cort Dougan (co...@cs...) * Synergy Microsystems board support by Dan Cox (da...@sy...) * */ #include <linux/config.h> #include <linux/stddef.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/errno.h> #include <linux/reboot.h> #include <linux/pci.h> #include <linux/time.h> #include <linux/kdev_t.h> #include <linux/types.h> #include <linux/major.h> #include <linux/blk.h> #include <linux/console.h> #include <linux/irq.h> #include <linux/seq_file.h> #include <asm/system.h> #include <asm/pgtable.h> #include <asm/page.h> #include <asm/dma.h> #include <asm/io.h> #include <asm/m48t35.h> #include <platforms/gemini.h> #include <asm/time.h> #include <asm/open_pic.h> #include <asm/bootinfo.h> void gemini_find_bridges(void); static int gemini_get_clock_speed(void); extern void gemini_pcibios_fixup(void); static char *gemini_board_families[] = { "VGM", "VSS", "KGM", "VGR", "VCM", "VCS", "KCM", "VCR" }; static int gemini_board_count = sizeof(gemini_board_families) / sizeof(gemini_board_families[0]); static unsigned int cpu_7xx[16] = { 0, 15, 14, 0, 0, 13, 5, 9, 6, 11, 8, 10, 16, 12, 7, 0 }; static unsigned int cpu_6xx[16] = { 0, 0, 14, 0, 0, 13, 5, 9, 6, 11, 8, 10, 0, 12, 7, 0 }; /* * prom_init is the Gemini version of prom.c:prom_init. We only need * the BSS clearing code, so I copied that out of prom.c. This is a * lot simpler than hacking prom.c so it will build with Gemini. -VAL */ #define PTRRELOC(x) ((typeof(x))((unsigned long)(x) + offset)) unsigned long prom_init(void) { unsigned long offset = reloc_offset(); unsigned long phys; extern char __bss_start, _end; /* First zero the BSS -- use memset, some arches don't have * caches on yet */ memset_io(PTRRELOC(&__bss_start),0 , &_end - &__bss_start); /* Default */ phys = offset + KERNELBASE; gemini_prom_init(); return phys; } int gemini_show_cpuinfo(struct seq_file *m) { unsigned char reg, rev; char *family; unsigned int type; reg = readb(GEMINI_FEAT); family = gemini_board_families[((reg>>4) & 0xf)]; if (((reg>>4) & 0xf) > gemini_board_count) printk(KERN_ERR "cpuinfo(): unable to determine board family\n"); reg = readb(GEMINI_BREV); type = (reg>>4) & 0xf; rev = reg & 0xf; reg = readb(GEMINI_BECO); seq_printf(m, "machine\t\t: Gemini %s%d, rev %c, eco %d\n", family, type, (rev + 'A'), (reg & 0xf)); seq_printf(m, "board\t\t: Gemini %s", family); if (type > 9) seq_printf(m, "%c", (type - 10) + 'A'); else seq_printf(m, "%d", type); seq_printf(m, ", rev %c, eco %d\n", (rev + 'A'), (reg & 0xf)); seq_printf(m, "clock\t\t: %dMhz\n", gemini_get_clock_speed()); return 0; } static u_char gemini_openpic_initsenses[] = { 1, 1, 1, 1, 0, 0, 1, /* remainder are level-triggered */ }; #define GEMINI_MPIC_ADDR (0xfcfc0000) #define GEMINI_MPIC_PCI_CFG (0x80005800) void __init gemini_openpic_init(void) { OpenPIC_Addr = (volatile struct OpenPIC *) grackle_read(GEMINI_MPIC_PCI_CFG + 0x10); OpenPIC_InitSenses = gemini_openpic_initsenses; OpenPIC_NumInitSenses = sizeof( gemini_openpic_initsenses ); ioremap( GEMINI_MPIC_ADDR, OPENPIC_SIZE); } extern unsigned long loops_per_jiffy; extern int root_mountflags; extern char cmd_line[]; void gemini_heartbeat(void) { static unsigned long led = GEMINI_LEDBASE+(4*8); static char direction = 8; /* We only want to do this on 1 CPU */ if (smp_processor_id()) return; *(char *)led = 0; if ( (led + direction) > (GEMINI_LEDBASE+(7*8)) || (led + direction) < (GEMINI_LEDBASE+(4*8)) ) direction *= -1; led += direction; *(char *)led = 0xff; ppc_md.heartbeat_count = ppc_md.heartbeat_reset; } void __init gemini_setup_arch(void) { extern char cmd_line[]; loops_per_jiffy = 50000000/HZ; #ifdef CONFIG_BLK_DEV_INITRD /* bootable off CDROM */ if (initrd_start) ROOT_DEV = MKDEV(SCSI_CDROM_MAJOR, 0); else #endif ROOT_DEV = to_kdev_t(0x0801); /* nothing but serial consoles... */ sprintf(cmd_line, "%s console=ttyS0", cmd_line); printk("Boot arguments: %s\n", cmd_line); ppc_md.heartbeat = gemini_heartbeat; ppc_md.heartbeat_reset = HZ/8; ppc_md.heartbeat_count = 1; /* Lookup PCI hosts */ gemini_find_bridges(); /* take special pains to map the MPIC, since it isn't mapped yet */ gemini_openpic_init(); /* start the L2 */ gemini_init_l2(); } int gemini_get_clock_speed(void) { unsigned long hid1, pvr; int clock; pvr = mfspr(PVR); hid1 = (mfspr(HID1) >> 28) & 0xf; if (PVR_VER(pvr) == 8 || PVR_VER(pvr) == 12) hid1 = cpu_7xx[hid1]; else hid1 = cpu_6xx[hid1]; switch((readb(GEMINI_BSTAT) & 0xc) >> 2) { case 0: default: clock = (hid1*100)/3; break; case 1: clock = (hid1*125)/3; break; case 2: clock = (hid1*50); break; } return clock; } void __init gemini_init_l2(void) { unsigned char reg, brev, fam, creg; unsigned long cache; unsigned long pvr; reg = readb(GEMINI_L2CFG); brev = readb(GEMINI_BREV); fam = readb(GEMINI_FEAT); pvr = mfspr(PVR); switch(PVR_VER(pvr)) { case 8: if (reg & 0xc0) cache = (((reg >> 6) & 0x3) << 28); else cache = 0x3 << 28; #ifdef CONFIG_SMP /* Pre-3.0 processor revs had snooping errata. Leave their L2's disabled with SMP. -- Dan */ if (PVR_CFG(pvr) < 3) { printk("Pre-3.0 750; L2 left disabled!\n"); return; } #endif /* CONFIG_SMP */ /* Special case: VGM5-B's came before L2 ratios were set on the board. Processor speed shouldn't be too high, so set L2 ratio to 1:1.5. */ if ((brev == 0x51) && ((fam & 0xa0) >> 4) == 0) reg |= 1; /* determine best cache ratio based upon what the board tells us (which sometimes _may_ not be true) and the processor speed. */ else { if (gemini_get_clock_speed() > 250) reg = 2; } break; case 12: { static unsigned long l2_size_val = 0; if (!l2_size_val) l2_size_val = _get_L2CR(); cache = l2_size_val; break; } case 4: case 9: creg = readb(GEMINI_CPUSTAT); if (((creg & 0xc) >> 2) != 1) printk("Dual-604 boards don't support the use of L2\n"); else writeb(1, GEMINI_L2CFG); return; default: printk("Unknown processor; L2 left disabled\n"); return; } cache |= ((1<<reg) << 25); cache |= (L2CR_L2RAM_MASK|L2CR_L2CTL|L2CR_L2DO); _set_L2CR(0); _set_L2CR(cache | L2CR_L2E); } void gemini_restart(char *cmd) { __cli(); /* make a clean restart, not via the MPIC */ _gemini_reboot(); for(;;); } void gemini_power_off(void) { for(;;); } void gemini_halt(void) { gemini_restart(NULL); } void __init gemini_init_IRQ(void) { /* gemini has no 8259 */ openpic_init(1, 0, 0, -1); } #define gemini_rtc_read(x) (readb(GEMINI_RTC+(x))) #define gemini_rtc_write(val,x) (writeb((val),(GEMINI_RTC+(x)))) /* ensure that the RTC is up and running */ long __init gemini_time_init(void) { unsigned char reg; reg = gemini_rtc_read(M48T35_RTC_CONTROL); if ( reg & M48T35_RTC_STOPPED ) { printk(KERN_INFO "M48T35 real-time-clock was stopped. Now starting...\n"); gemini_rtc_write((reg & ~(M48T35_RTC_STOPPED)), M48T35_RTC_CONTROL); gemini_rtc_write((reg | M48T35_RTC_SET), M48T35_RTC_CONTROL); } return 0; } #undef DEBUG_RTC unsigned long gemini_get_rtc_time(void) { unsigned int year, mon, day, hour, min, sec; unsigned char reg; reg = gemini_rtc_read(M48T35_RTC_CONTROL); gemini_rtc_write((reg|M48T35_RTC_READ), M48T35_RTC_CONTROL); #ifdef DEBUG_RTC printk("get rtc: reg = %x\n", reg); #endif do { sec = gemini_rtc_read(M48T35_RTC_SECONDS); min = gemini_rtc_read(M48T35_RTC_MINUTES); hour = gemini_rtc_read(M48T35_RTC_HOURS); day = gemini_rtc_read(M48T35_RTC_DOM); mon = gemini_rtc_read(M48T35_RTC_MONTH); year = gemini_rtc_read(M48T35_RTC_YEAR); } while( sec != gemini_rtc_read(M48T35_RTC_SECONDS)); #ifdef DEBUG_RTC printk("get rtc: sec=%x, min=%x, hour=%x, day=%x, mon=%x, year=%x\n", sec, min, hour, day, mon, year); #endif gemini_rtc_write(reg, M48T35_RTC_CONTROL); BCD_TO_BIN(sec); BCD_TO_BIN(min); BCD_TO_BIN(hour); BCD_TO_BIN(day); BCD_TO_BIN(mon); BCD_TO_BIN(year); if ((year += 1900) < 1970) year += 100; #ifdef DEBUG_RTC printk("get rtc: sec=%x, min=%x, hour=%x, day=%x, mon=%x, year=%x\n", sec, min, hour, day, mon, year); #endif return mktime( year, mon, day, hour, min, sec ); } int gemini_set_rtc_time( unsigned long now ) { unsigned char reg; struct rtc_time tm; to_tm( now, &tm ); reg = gemini_rtc_read(M48T35_RTC_CONTROL); #if DEBUG_RTC printk("set rtc: reg = %x\n", reg); #endif gemini_rtc_write((reg|M48T35_RTC_SET), M48T35_RTC_CONTROL); #if DEBUG_RTC printk("set rtc: tm vals - sec=%x, min=%x, hour=%x, mon=%x, mday=%x, year=%x\n", tm.tm_sec, tm.tm_min, tm.tm_hour, tm.tm_mon, tm.tm_mday, tm.tm_year); #endif tm.tm_year -= 1900; BIN_TO_BCD(tm.tm_sec); BIN_TO_BCD(tm.tm_min); BIN_TO_BCD(tm.tm_hour); BIN_TO_BCD(tm.tm_mon); BIN_TO_BCD(tm.tm_mday); BIN_TO_BCD(tm.tm_year); #ifdef DEBUG_RTC printk("set rtc: tm vals - sec=%x, min=%x, hour=%x, mon=%x, mday=%x, year=%x\n", tm.tm_sec, tm.tm_min, tm.tm_hour, tm.tm_mon, tm.tm_mday, tm.tm_year); #endif gemini_rtc_write(tm.tm_sec, M48T35_RTC_SECONDS); gemini_rtc_write(tm.tm_min, M48T35_RTC_MINUTES); gemini_rtc_write(tm.tm_hour, M48T35_RTC_HOURS); gemini_rtc_write(tm.tm_mday, M48T35_RTC_DOM); gemini_rtc_write(tm.tm_mon, M48T35_RTC_MONTH); gemini_rtc_write(tm.tm_year, M48T35_RTC_YEAR); /* done writing */ gemini_rtc_write(reg, M48T35_RTC_CONTROL); if ((time_state == TIME_ERROR) || (time_state == TIME_BAD)) time_state = TIME_OK; return 0; } /* use the RTC to determine the decrementer count */ void __init gemini_calibrate_decr(void) { int freq, divisor; unsigned char reg; /* determine processor bus speed */ reg = readb(GEMINI_BSTAT); switch(((reg & 0x0c)>>2)&0x3) { case 0: default: freq = 66667; break; case 1: freq = 83000; break; case 2: freq = 100000; break; } freq *= 1000; divisor = 4; tb_ticks_per_jiffy = freq / HZ / divisor; tb_to_us = mulhwu_scale_factor(freq/divisor, 1000000); } unsigned long __init gemini_find_end_of_memory(void) { unsigned long total; unsigned char reg; reg = readb(GEMINI_MEMCFG); total = ((1<<((reg & 0x7) - 1)) * (8<<((reg >> 3) & 0x7))); total *= (1024*1024); return total; } static void __init gemini_map_io(void) { io_block_mapping(0xf0000000, 0xf0000000, 0x10000000, _PAGE_IO); io_block_mapping(0x80000000, 0x80000000, 0x10000000, _PAGE_IO); } #ifdef CONFIG_SMP static int smp_gemini_probe(void) { int i, nr; nr = (readb(GEMINI_CPUSTAT) & GEMINI_CPU_COUNT_MASK) >> 2; if (nr == 0) nr = 4; if (nr > 1) { openpic_request_IPIs(); for (i = 1; i < nr; ++i) smp_hw_index[i] = i; } return nr; } static void smp_gemini_kick_cpu(int nr) { openpic_reset_processor_phys(1 << nr); openpic_reset_processor_phys(0); } static void smp_gemini_setup_cpu(int cpu_nr) { if (OpenPIC_Addr) do_openpic_setup_cpu(); if (cpu_nr > 0) gemini_init_l2(); } static struct smp_ops_t gemini_smp_ops = { smp_openpic_message_pass, smp_gemini_probe, smp_gemini_kick_cpu, smp_gemini_setup_cpu, }; #endif /* CONFIG_SMP */ void __init platform_init(unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7) { int i; /* Restore BATs for now */ mtspr(DBAT3U, 0xf0001fff); mtspr(DBAT3L, 0xf000002a); parse_bootinfo(find_bootinfo()); for(i = 0; i < GEMINI_LEDS; i++) gemini_led_off(i); ISA_DMA_THRESHOLD = 0; DMA_MODE_READ = 0; DMA_MODE_WRITE = 0; #ifdef CONFIG_BLK_DEV_INITRD if ( r4 ) { initrd_start = r4 + KERNELBASE; initrd_end = r5 + KERNELBASE; } #endif ppc_md.setup_arch = gemini_setup_arch; ppc_md.show_cpuinfo = gemini_show_cpuinfo; ppc_md.irq_cannonicalize = NULL; ppc_md.init_IRQ = gemini_init_IRQ; ppc_md.get_irq = openpic_get_irq; ppc_md.init = NULL; ppc_md.restart = gemini_restart; ppc_md.power_off = gemini_power_off; ppc_md.halt = gemini_halt; ppc_md.time_init = gemini_time_init; ppc_md.set_rtc_time = gemini_set_rtc_time; ppc_md.get_rtc_time = gemini_get_rtc_time; ppc_md.calibrate_decr = gemini_calibrate_decr; ppc_md.find_end_of_memory = gemini_find_end_of_memory; ppc_md.setup_io_mappings = gemini_map_io; ppc_md.pcibios_fixup_bus = gemini_pcibios_fixup; #ifdef CONFIG_SMP ppc_md.smp_ops = &gemini_smp_ops; #endif /* CONFIG_SMP */ } --- NEW FILE: iSeries_setup.c --- /* * * * Copyright (c) 2000 Mike Corrigan <mi...@us...> * Copyright (c) 1999-2000 Grant Erickson <gr...@lc...> * * Module name: iSeries_setup.c * * Description: * Architecture- / platform-specific boot-time initialization code for * the IBM iSeries LPAR. Adapted from original code by Grant Erickson and * code by Gary Thomas, Cort Dougan <co...@fs...>, and Dan Malek * <da...@ne...>. * */ #include <linux/pci.h> #include <linux/config.h> #include <linux/init.h> #include <linux/threads.h> #include <linux/smp.h> #include <linux/param.h> #include <linux/string.h> #include <linux/bootmem.h> #include <linux/blk.h> #include <linux/ide.h> #include <linux/seq_file.h> #include <asm/processor.h> #include <asm/machdep.h> #include <asm/page.h> #include <asm/bootinfo.h> #include <asm/time.h> #include "iSeries_setup.h" #include <asm/iSeries/LparData.h> #include <asm/iSeries/HvCallHpt.h> #include <asm/iSeries/HvLpConfig.h> #include <asm/iSeries/HvCallEvent.h> #include <asm/iSeries/HvCallSm.h> #include <asm/iSeries/ItLpQueue.h> #include <asm/iSeries/IoHriMainStore.h> #include <asm/iSeries/iSeries_proc.h> #include <asm/iSeries/pmc_proc.h> #include <asm/iSeries/mf.h> #include <asm/pci-bridge.h> #include <asm/iSeries/HvCallXm.h> #include <asm/iSeries/iSeries_fixup.h> #include <asm/iSeries/HvReleaseData.h> /* Function Prototypes */ extern void abort(void); static void build_iSeries_Memory_Map( void ); static void setup_iSeries_cache_sizes( void ); extern void iSeries_pci_Initialize(void); static int iSeries_show_cpuinfo(struct seq_file *m); static int iSeries_show_percpuinfo(struct seq_file *m, int i); extern struct pci_ops iSeries_pci_ops; /* Global Variables */ unsigned short iSeries_icache_line_size = 0; unsigned short iSeries_dcache_line_size = 0; unsigned short iSeries_icache_lines_per_page = 0; unsigned short iSeries_dcache_lines_per_page = 0; unsigned short iSeries_log_icache_line_size = 0; unsigned short iSeries_log_dcache_line_size = 0; unsigned long procFreqHz = 0; unsigned long procFreqMhz = 0; unsigned long procFreqMhzHundreths = 0; unsigned long tbFreqHz = 0; unsigned long tbFreqMhz = 0; unsigned long tbFreqMhzHundreths = 0; unsigned long decr_overclock = 8; unsigned long decr_overclock_proc0 = 8; unsigned long decr_overclock_set = 0; unsigned long decr_overclock_proc0_set = 0; extern unsigned long embedded_sysmap_start; extern unsigned long embedded_sysmap_end; extern unsigned long sysmap; extern unsigned long sysmap_size; extern unsigned long end_of_DRAM; // Defined in ppc/mm/init.c #ifdef CONFIG_SMP extern struct smp_ops_t iSeries_smp_ops; #endif /* CONFIG_SMP */ /* XXX for now... */ #ifndef CONFIG_PCI unsigned long isa_io_base; #endif /* * void __init platform_init() * * Description: * This routine... * * Input(s): * r3 - Optional pointer to a board information structure. * r4 - Optional pointer to the physical starting address of the init RAM * disk. * r5 - Optional pointer to the physical ending address of the init RAM * disk. * r6 - Optional pointer to the physical starting address of any kernel * command-line parameters. * r7 - Optional pointer to the physical ending address of any kernel * command-line parameters. * * Output(s): * N/A * * Returns: * N/A * */ extern int rd_size; // Defined in drivers/block/rd.c extern u64 next_jiffy_update_tb[]; extern u64 get_tb64(void); unsigned long __init iSeries_find_end_of_memory(void) { /* totalLpChunks contains the size of memory (in units of 256K) */ unsigned long memory_end = (totalLpChunks << 18); #ifndef CONFIG_HIGHMEM /* Max memory if highmem is not configured is 768 MB */ if (memory_end > (768 << 20)) memory_end = 768 << 20; #endif /* CONFIG_HIGHMEM */ return memory_end; } void __init platform_init(unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7) { parse_bootinfo(find_bootinfo()); #if defined(CONFIG_BLK_DEV_INITRD) /* * If the init RAM disk has been configured and there is * a non-zero starting address for it, set it up */ if ( xNaca.xRamDisk ) { initrd_start = xNaca.xRamDisk + KERNELBASE; initrd_end = initrd_start + xNaca.xRamDiskSize * PAGE_SIZE; initrd_below_start_ok = 1; // ramdisk in kernel space ROOT_DEV = MKDEV( RAMDISK_MAJOR, 0 ); if ( ((rd_size*1024)/PAGE_SIZE) < xNaca.xRamDiskSize ) rd_size = (xNaca.xRamDiskSize*PAGE_SIZE)/1024; } else #endif /* CONFIG_BLK_DEV_INITRD */ #if CONFIG_VIODASD_IDE { ROOT_DEV = MKDEV( IDE0_MAJOR, 1 ); } #elif defined(CONFIG_VIODASD) { ROOT_DEV = MKDEV( VIODASD_MAJOR, 1 ); } #endif /* CONFIG_VIODASD_IDE */ /* If an embedded System.map has been added to the kernel, * set it up. */ if ( embedded_sysmap_start ) { sysmap = embedded_sysmap_start + KERNELBASE; sysmap_size = embedded_sysmap_end - embedded_sysmap_start; } /* Copy the kernel command line arguments to a safe place. */ if (r6) { *(char *)(r7 + KERNELBASE) = 0; strcpy(cmd_line, (char *)(r6 + KERNELBASE)); } /* Initialize the table which translate Linux physical addresses to * iSeries absolute addresses */ build_iSeries_Memory_Map(); setup_iSeries_cache_sizes(); /* Initialize machine-dependency vectors */ ppc_md.setup_arch = iSeries_setup_arch; ppc_md.show_cpuinfo = iSeries_show_cpuinfo; ppc_md.show_percpuinfo = iSeries_show_percpuinfo; ppc_md.irq_cannonicalize = NULL; ppc_md.init_IRQ = iSeries_init_IRQ; ppc_md.get_irq = iSeries_get_irq; ppc_md.init = NULL; ppc_md.restart = iSeries_restart; ppc_md.power_off = iSeries_power_off; ppc_md.halt = iSeries_halt; ppc_md.time_init = NULL; ppc_md.set_rtc_time = iSeries_set_rtc_time; ppc_md.get_rtc_time = iSeries_get_rtc_time; ppc_md.calibrate_decr = iSeries_calibrate_decr; ppc_md.progress = iSeries_progress; ppc_md.find_end_of_memory = iSeries_find_end_of_memory; #ifdef CONFIG_PCI ppc_md.pcibios_fixup_bus = iSeries_fixup_bus; ppc_md.pcibios_fixup = iSeries_fixup; #else ppc_md.pcibios_fixup_bus = NULL; ppc_md.pcibios_fixup = NULL; #endif /* CONFIG_PCI */ #ifdef CONFIG_SMP ppc_md.smp_ops = &iSeries_smp_ops; #endif /* CONFIG_SMP */ // Associate Lp Event Queue 0 with processor 0 HvCallEvent_setLpEventQueueInterruptProc( 0, 0 ); { // copy the command line parameter from the primary VSP char *p, *q; HvCallEvent_dmaToSp( cmd_line, 2*64*1024, 256, HvLpDma_Direction_RemoteToLocal ); p = q = cmd_line + 255; while( p > cmd_line ) { if ((*p == 0) || (*p == ' ') || (*p == '\n')) --p; else break; } if ( p < q ) *(p+1) = 0; } next_jiffy_update_tb[0] = get_tb64(); iSeries_proc_early_init(); mf_init(); iSeries_proc_callback( &pmc_proc_init ); return; } /* * The iSeries may have very large memories ( > 128 GB ) and a partition * may get memory in "chunks" that may be anywhere in the 2**52 real * address space. The chunks are 256K in size. To map this to the * memory model Linux expects, the iSeries specific code builds a * translation table to translate what Linux thinks are "physical" * addresses to the actual real addresses. This allows us to make * it appear to Linux that we have contiguous memory starting at * physical address zero while in fact this could be far from the truth. * To avoid confusion, I'll let the words physical and/or real address * apply to the Linux addresses while I'll use "absolute address" to * refer to the actual hardware real address. * * build_iSeries_Memory_Map gets information from the Hypervisor and * looks at the Main Store VPD to determine the absolute addresses * of the memory that has been assigned to our partition and builds * a table used to translate Linux's physical addresses to these * absolute addresses. Absolute addresses are needed when * communicating with the hypervisor (e.g. to build HPT entries) */ static void __init build_iSeries_Memory_Map(void) { u32 loadAreaFirstChunk, loadAreaLastChunk, loadAreaSize; u32 hptFirstChunk, hptLastChunk, hptSizeChunks; u32 absAddrHi, absAddrLo; u32 nextPhysChunk; u32 holeFirstChunk, holeSizeChunks; u32 totalChunks,moreChunks; u32 currChunk, thisChunk, absChunk; u32 currDword; u32 chunkBit; u64 holeStart, holeEnd, holeSize; u64 map; struct IoHriMainStoreSegment4 * msVpd; // Get absolute address of our load area // and map it to physical address 0 // This guarantees that the loadarea ends up at physical 0 // otherwise, it might not be returned by PLIC as the first // chunks loadAreaFirstChunk = (u32)(itLpNaca.xLoadAreaAddr >> 18); loadAreaSize = itLpNaca.xLoadAreaChunks; loadAreaLastChunk = loadAreaFirstChunk + loadAreaSize - 1; // Get absolute address of our HPT and remember it so // we won't map it to any physical address hptFirstChunk = (u32)(HvCallHpt_getHptAddress() >> 18 ); hptSizeChunks = (u32)(HvCallHpt_getHptPages() >> 6 ); hptLastChunk = hptFirstChunk + hptSizeChunks - 1; loadAreaLastChunk = loadAreaFirstChunk + loadAreaSize - 1; absAddrLo = loadAreaFirstChunk << 18; absAddrHi = loadAreaFirstChunk >> 14; printk( "Mapping load area - physical addr = 0, absolute addr = %08x%08x\n", absAddrHi, absAddrLo ); printk( "Load area size %dK\n", loadAreaSize*256 ); nextPhysChunk = 0; for ( absChunk = loadAreaFirstChunk; absChunk <= loadAreaLastChunk; ++absChunk ) { if ( ( absChunk < hptFirstChunk ) || ( absChunk > hptLastChunk ) ) { msChunks[nextPhysChunk] = absChunk; ++nextPhysChunk; } } // Get absolute address of our HPT and remember it so // we won't map it to any physical address hptFirstChunk = (u32)(HvCallHpt_getHptAddress() >> 18 ); hptSizeChunks = (u32)(HvCallHpt_getHptPages() >> 6 ); hptLastChunk = hptFirstChunk + hptSizeChunks - 1; absAddrLo = hptFirstChunk << 18; absAddrHi = hptFirstChunk >> 14; printk( "HPT absolute addr = %08x%08x, size = %dK\n", absAddrHi, absAddrLo, hptSizeChunks*256 ); // Determine if absolute memory has any // holes so that we can interpret the // access map we get back from the hypervisor // correctly. msVpd = (struct IoHriMainStoreSegment4 *)xMsVpd; holeStart = msVpd->nonInterleavedBlocksStartAdr; holeEnd = msVpd->nonInterleavedBlocksEndAdr; holeSize = holeEnd - holeStart; if ( holeSize ) { holeStart = holeStart & 0x000fffffffffffff; holeStart = holeStart >> 18; holeFirstChunk = (u32)holeStart; holeSize = holeSize >> 18; holeSizeChunks = (u32)holeSize; printk( "Main store hole: start chunk = %0x, size = %0x chunks\n", holeFirstChunk, holeSizeChunks ); } else { holeFirstChunk = 0xffffffff; holeSizeChunks = 0; } // Process the main store access map from the hypervisor // to build up our physical -> absolute translation table totalChunks = (u32)HvLpConfig_getMsChunks(); if ((totalChunks-hptSizeChunks) > 16384) { panic("More than 4GB of memory assigned to this partition"); } currChunk = 0; currDword = 0; moreChunks = totalChunks; while ( moreChunks ) { map = HvCallSm_get64BitsOfAccessMap( itLpNaca.xLpIndex, currDword ); thisChunk = currChunk; while ( map ) { chunkBit = map >> 63; map <<= 1; if ( chunkBit ) { --moreChunks; absChunk = thisChunk; if ( absChunk >= holeFirstChunk ) absChunk += holeSizeChunks; if ( ( ( absChunk < hptFirstChunk ) || ( absChunk > hptLastChunk ) ) && ( ( absChunk < loadAreaFirstChunk ) || ( absChunk > loadAreaLastChunk ) ) ) { // printk( "Mapping physical = %0x to absolute %0x for 256K\n", nextPhysChunk << 18, absChunk << 18 ); msChunks[nextPhysChunk] = absChunk; ++nextPhysChunk; } } ++thisChunk; } ++currDword; currChunk += 64; } // main store size (in chunks) is // totalChunks - hptSizeChunks // which should be equal to // nextPhysChunk totalLpChunks = nextPhysChunk; } /* * Set up the variables that describe the cache line sizes * for this machine. */ static void __init setup_iSeries_cache_sizes(void) { unsigned i,n; iSeries_icache_line_size = xIoHriProcessorVpd[0].xInstCacheOperandSize; iSeries_dcache_line_size = xIoHriProcessorVpd[0].xDataCacheOperandSize; iSeries_icache_lines_per_page = PAGE_SIZE / iSeries_icache_line_size; iSeries_dcache_lines_per_page = PAGE_SIZE / iSeries_dcache_line_size; i = iSeries_icache_line_size; n = 0; while ((i=(i/2))) ++n; iSeries_log_icache_line_size = n; i = iSeries_dcache_line_size; n = 0; while ((i=(i/2))) ++n; iSeries_log_dcache_line_size = n; printk( "D-cache line size = %d (log = %d)\n", (unsigned)iSeries_dcache_line_size, (unsigned)iSeries_log_dcache_line_size ); printk( "I-cache line size = %d (log = %d)\n", (unsigned)iSeries_icache_line_size, (unsigned)iSeries_log_icache_line_size ); } int piranha_simulator = 0; /* * Document me. */ void __init iSeries_setup_arch(void) { void * eventStack; u32 procFreq; u32 tbFreq; // u32 evStackContigReal; // u6... [truncated message content] |