From: James S. <jsi...@us...> - 2002-03-15 18:28:16
|
Update of /cvsroot/linuxconsole/ruby/linux/arch/ppc64/kernel In directory usw-pr-cvs1:/tmp/cvs-serv4512/linux/arch/ppc64/kernel Added Files: chrp_setup.c iSeries_setup.c ioctl32.c open_pic.c Log Message: Alots of fixes across many platforms. --- NEW FILE: chrp_setup.c --- /* * linux/arch/ppc/kernel/setup.c * * Copyright (C) 1995 Linus Torvalds * Adapted from 'alpha' version by Gary Thomas * Modified by Cort Dougan (co...@cs...) * Modified by PPC64 Team, IBM Corp * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ /* * 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/blk.h> #include <linux/ioport.h> #include <linux/pci.h> #include <linux/version.h> #include <linux/adb.h> #include <linux/module.h> #include <linux/delay.h> #include <linux/irq.h> #include <linux/seq_file.h> #include <asm/mmu.h> #include <asm/processor.h> #include <asm/io.h> #include <asm/pgtable.h> #include <asm/prom.h> #include <asm/rtas.h> #include <asm/pci-bridge.h> #include <asm/pci_dma.h> #include <asm/dma.h> #include <asm/machdep.h> #include <asm/irq.h> #include <asm/keyboard.h> #include <asm/init.h> #include <asm/Naca.h> #include <asm/time.h> #include "local_irq.h" #include "i8259.h" #include "open_pic.h" #include "xics.h" #include <asm/ppcdebug.h> extern volatile unsigned char *chrp_int_ack_special; extern struct Naca *naca; void chrp_setup_pci_ptrs(void); void chrp_progress(char *, unsigned short); void chrp_request_regions(void); extern void openpic_init_IRQ(void); extern void init_ras_IRQ(void); extern void find_and_init_phbs(void); extern void pSeries_pcibios_fixup(void); extern void iSeries_pcibios_fixup(void); extern void pSeries_get_rtc_time(struct rtc_time *rtc_time); extern int pSeries_set_rtc_time(struct rtc_time *rtc_time); void pSeries_calibrate_decr(void); kdev_t boot_dev; unsigned long virtPython0Facilities = 0; // python0 facility area (memory mapped io) (64-bit format) VIRTUAL address. extern HPTE *Hash, *Hash_end; extern unsigned long Hash_size, Hash_mask; extern int probingmem; extern unsigned long loops_per_jiffy; #ifdef CONFIG_BLK_DEV_RAM extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */ extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */ extern int rd_image_start; /* starting block # of image */ #endif void chrp_get_cpuinfo(struct seq_file *m) { 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); } void __init chrp_request_regions(void) { 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"); } void __init chrp_setup_arch(void) { extern char cmd_line[]; struct device_node *root; unsigned int *opprop; /* openpic global configuration register (64-bit format). */ /* openpic Interrupt Source Unit pointer (64-bit format). */ /* python0 facility area (mmio) (64-bit format) REAL address. */ /* init to some ~sane value until calibrate_delay() runs */ loops_per_jiffy = 50000000; #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) */ printk("Boot arguments: %s\n", cmd_line); /* Find and initialize PCI host bridges */ /* iSeries needs to be done much later. */ #ifndef CONFIG_PPC_ISERIES find_and_init_phbs(); #endif /* Find the Open PIC if present */ root = find_path_device("/"); opprop = (unsigned int *) get_property(root, "platform-open-pic", NULL); if (opprop != 0) { int n = prom_n_addr_cells(root); unsigned long openpic; for (openpic = 0; n > 0; --n) openpic = (openpic << 32) + *opprop++; printk(KERN_DEBUG "OpenPIC addr: %lx\n", openpic); udbg_printf("OpenPIC addr: %lx\n", openpic); OpenPIC_Addr = __ioremap(openpic, 0x40000, _PAGE_NO_CACHE); } #ifdef CONFIG_DUMMY_CONSOLE conswitchp = &dummy_con; #endif } void __init chrp_init2(void) { /* * It is sensitive, when this is called (not too earlu) * -- tibit */ chrp_request_regions(); ppc_md.progress(UTS_RELEASE, 0x7777); } /* Early initialization. Relocation is on but do not reference unbolted pages */ void __init pSeries_init_early(void) { #ifdef CONFIG_PPC_PSERIES /* This ifdef should go away */ void *comport; hpte_init_pSeries(); tce_init_pSeries(); pSeries_pcibios_init_early(); #ifdef CONFIG_SMP smp_init_pSeries(); #endif /* Map the uart for udbg. */ comport = (void *)__ioremap(naca->serialPortAddr, 16, _PAGE_NO_CACHE); udbg_init_uart(comport); ppc_md.udbg_putc = udbg_putc; ppc_md.udbg_getc = udbg_getc; ppc_md.udbg_getc_poll = udbg_getc_poll; #endif } void __init chrp_init(unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7) { #if 0 /* PPPBBB remove this later... -Peter */ #ifdef CONFIG_BLK_DEV_INITRD /* take care of initrd if we have one */ if ( r6 ) { initrd_start = __va(r6); initrd_end = __va(r6 + r7); } #endif /* CONFIG_BLK_DEV_INITRD */ #endif ppc_md.ppc_machine = _machine; ppc_md.setup_arch = chrp_setup_arch; ppc_md.setup_residual = NULL; ppc_md.get_cpuinfo = chrp_get_cpuinfo; if(naca->interrupt_controller == IC_OPEN_PIC) { ppc_md.init_IRQ = openpic_init_IRQ; ppc_md.get_irq = openpic_get_irq; ppc_md.post_irq = NULL; } else { ppc_md.init_IRQ = xics_init_IRQ; ppc_md.get_irq = xics_get_irq; ppc_md.post_irq = NULL; } ppc_md.init_ras_IRQ = init_ras_IRQ; #ifndef CONFIG_PPC_ISERIES ppc_md.pcibios_fixup = pSeries_pcibios_fixup; #else ppc_md.pcibios_fixup = NULL; // ppc_md.pcibios_fixup = iSeries_pcibios_fixup; #endif ppc_md.init = chrp_init2; ppc_md.restart = rtas_restart; ppc_md.power_off = rtas_power_off; ppc_md.halt = rtas_halt; ppc_md.time_init = NULL; ppc_md.get_boot_time = pSeries_get_rtc_time; ppc_md.get_rtc_time = pSeries_get_rtc_time; ppc_md.set_rtc_time = pSeries_set_rtc_time; ppc_md.calibrate_decr = pSeries_calibrate_decr; ppc_md.progress = chrp_progress; ppc_md.progress("Linux ppc64\n", 0x0); } void __chrp chrp_progress(char *s, unsigned short hex) { struct device_node *root; int width, *p; char *os; static int display_character, set_indicator; static int max_width; if (hex) udbg_printf("<chrp_progress> %s\n", s); if (!rtas.base || (_machine != _MACH_pSeries)) return; if (max_width == 0) { if ( (root = find_path_device("/rtas")) && (p = (unsigned int *)get_property(root, "ibm,display-line-length", NULL)) ) max_width = *p; else max_width = 0x10; display_character = rtas_token("display-character"); set_indicator = rtas_token("set-indicator"); } if (display_character == RTAS_UNKNOWN_SERVICE) { /* use hex display */ if (set_indicator == RTAS_UNKNOWN_SERVICE) return; rtas_call(set_indicator, 3, 1, NULL, 6, 0, hex); return; } rtas_call(display_character, 1, 1, NULL, '\r'); width = max_width; os = s; while ( *os ) { if ( (*os == '\n') || (*os == '\r') ) width = max_width; else width--; rtas_call(display_character, 1, 1, NULL, *os++ ); /* if we overwrite the screen length */ if ( width == 0 ) while ( (*os != 0) && (*os != '\n') && (*os != '\r') ) os++; } /* Blank to end of line. */ while ( width-- > 0 ) rtas_call(display_character, 1, 1, NULL, ' ' ); } extern void setup_default_decr(void); void __init pSeries_calibrate_decr(void) { struct device_node *cpu; struct div_result divres; int *fp; unsigned long freq; /* * The cpu node should have a timebase-frequency property * to tell us the rate at which the decrementer counts. */ freq = 16666000; /* hardcoded default */ cpu = find_type_devices("cpu"); if (cpu != 0) { fp = (int *) get_property(cpu, "timebase-frequency", NULL); if (fp != 0) freq = *fp; } printk("time_init: decrementer frequency = %lu.%.6lu MHz\n", freq/1000000, freq%1000000 ); tb_ticks_per_jiffy = freq / HZ; tb_ticks_per_sec = tb_ticks_per_jiffy * HZ; tb_ticks_per_usec = freq / 1000000; tb_to_us = mulhwu_scale_factor(freq, 1000000); div128_by_32( 1024*1024, 0, tb_ticks_per_sec, &divres ); tb_to_xs = divres.result_low; setup_default_decr(); } --- 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...>. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #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 <asm/processor.h> #include <asm/machdep.h> #include <asm/page.h> #include <asm/mmu.h> #include <asm/pgtable.h> #include <asm/mmu_context.h> #include <asm/time.h> #include "iSeries_setup.h" #include <asm/Naca.h> #include <asm/Paca.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/HvCallXm.h> #include <asm/iSeries/ItLpQueue.h> #include <asm/iSeries/IoHriMainStore.h> #include <asm/iSeries/iSeries_proc.h> #include <asm/proc_pmc.h> #include <asm/iSeries/mf.h> /* Function Prototypes */ extern void abort(void); #ifdef CONFIG_PPC_ISERIES static void build_iSeries_Memory_Map( void ); static void setup_iSeries_cache_sizes( void ); static void iSeries_bolt_kernel(unsigned long saddr, unsigned long eaddr); #endif void build_valid_hpte( unsigned long vsid, unsigned long ea, unsigned long pa, pte_t * ptep, unsigned hpteflags, unsigned bolted ); extern void ppcdbg_initialize(void); extern void iSeries_pcibios_init(void); extern void iSeries_pcibios_fixup(void); extern void iSeries_pcibios_fixup_bus(int); static void iSeries_setup_dprofile(void); /* Global Variables */ static unsigned long procFreqHz = 0; static unsigned long procFreqMhz = 0; static unsigned long procFreqMhzHundreths = 0; static unsigned long tbFreqHz = 0; static unsigned long tbFreqMhz = 0; static unsigned long tbFreqMhzHundreths = 0; unsigned long dprof_shift = 0; unsigned long dprof_len = 0; unsigned int * dprof_buffer = NULL; int piranha_simulator = 0; extern char _end[]; extern struct Naca *naca; extern int rd_size; /* Defined in drivers/block/rd.c */ extern unsigned long klimit; extern unsigned long embedded_sysmap_start; extern unsigned long embedded_sysmap_end; extern unsigned long iSeries_recal_tb; extern unsigned long iSeries_recal_titan; extern char _stext; extern char _etext; static int mf_initialized = 0; struct MemoryBlock { unsigned long absStart; unsigned long absEnd; unsigned long logicalStart; unsigned long logicalEnd; }; /* * Process the main store vpd to determine where the holes in memory are * and return the number of physical blocks and fill in the array of * block data. */ unsigned long iSeries_process_Condor_mainstore_vpd( struct MemoryBlock *mb_array, unsigned long max_entries ) { /* Determine if absolute memory has any * holes so that we can interpret the * access map we get back from the hypervisor * correctly. */ unsigned long holeFirstChunk, holeSizeChunks; unsigned long numMemoryBlocks = 1; struct IoHriMainStoreSegment4 * msVpd = (struct IoHriMainStoreSegment4 *)xMsVpd; unsigned long holeStart = msVpd->nonInterleavedBlocksStartAdr; unsigned long holeEnd = msVpd->nonInterleavedBlocksEndAdr; unsigned long holeSize = holeEnd - holeStart; printk("Mainstore_VPD: Condor\n"); mb_array[0].logicalStart = 0; mb_array[0].logicalEnd = 0x100000000; mb_array[0].absStart = 0; mb_array[0].absEnd = 0x100000000; if ( holeSize ) { numMemoryBlocks = 2; holeStart = holeStart & 0x000fffffffffffff; holeStart = addr_to_chunk(holeStart); holeFirstChunk = holeStart; holeSize = addr_to_chunk(holeSize); holeSizeChunks = holeSize; printk( "Main store hole: start chunk = %0lx, size = %0lx chunks\n", holeFirstChunk, holeSizeChunks ); mb_array[0].logicalEnd = holeFirstChunk; mb_array[0].absEnd = holeFirstChunk; mb_array[1].logicalStart = holeFirstChunk; mb_array[1].logicalEnd = 0x100000000 - holeSizeChunks; mb_array[1].absStart = holeFirstChunk + holeSizeChunks; mb_array[1].absEnd = 0x100000000; } return numMemoryBlocks; } #define MaxSegmentAreas 32 #define MaxSegmentAdrRangeBlocks 128 #define MaxAreaRangeBlocks 4 unsigned long iSeries_process_Regatta_mainstore_vpd( struct MemoryBlock *mb_array, unsigned long max_entries ) { struct IoHriMainStoreSegment5 * msVpdP = (struct IoHriMainStoreSegment5 *)xMsVpd; unsigned long numSegmentBlocks = 0; u32 existsBits = msVpdP->msAreaExists; unsigned long area_num; printk("Mainstore_VPD: Regatta\n"); for ( area_num = 0; area_num < MaxSegmentAreas; ++area_num ) { unsigned long numAreaBlocks; struct IoHriMainStoreArea4 * currentArea; if ( existsBits & 0x80000000 ) { unsigned long block_num; currentArea = &msVpdP->msAreaArray[area_num]; numAreaBlocks = currentArea->numAdrRangeBlocks; printk("ms_vpd: processing area %2ld blocks=%ld", area_num, numAreaBlocks); for ( block_num = 0; block_num < numAreaBlocks; ++block_num ) { /* Process an address range block */ struct MemoryBlock tempBlock; unsigned long i; tempBlock.absStart = (unsigned long)currentArea->xAdrRangeBlock[block_num].blockStart; tempBlock.absEnd = (unsigned long)currentArea->xAdrRangeBlock[block_num].blockEnd; tempBlock.logicalStart = 0; tempBlock.logicalEnd = 0; printk("\n block %ld absStart=%016lx absEnd=%016lx", block_num, tempBlock.absStart, tempBlock.absEnd); for ( i=0; i<numSegmentBlocks; ++i ) { if ( mb_array[i].absStart == tempBlock.absStart ) break; } if ( i == numSegmentBlocks ) { if ( numSegmentBlocks == max_entries ) { panic("iSeries_process_mainstore_vpd: too many memory blocks"); } mb_array[numSegmentBlocks] = tempBlock; ++numSegmentBlocks; } else { printk(" (duplicate)"); } } printk("\n"); } existsBits <<= 1; } /* Now sort the blocks found into ascending sequence */ if ( numSegmentBlocks > 1 ) { unsigned long m, n; for ( m=0; m<numSegmentBlocks-1; ++m ) { for ( n=numSegmentBlocks-1; m<n; --n ) { if ( mb_array[n].absStart < mb_array[n-1].absStart ) { struct MemoryBlock tempBlock; tempBlock = mb_array[n]; mb_array[n] = mb_array[n-1]; mb_array[n-1] = tempBlock; } } } } /* Assign "logical" addresses to each block. These * addresses correspond to the hypervisor "bitmap" space. * Convert all addresses into units of 256K chunks. */ { unsigned long i, nextBitmapAddress; printk("ms_vpd: %ld sorted memory blocks\n", numSegmentBlocks); nextBitmapAddress = 0; for ( i=0; i<numSegmentBlocks; ++i ) { unsigned long length = mb_array[i].absEnd - mb_array[i].absStart; mb_array[i].logicalStart = nextBitmapAddress; mb_array[i].logicalEnd = nextBitmapAddress + length; nextBitmapAddress += length; printk(" Bitmap range: %016lx - %016lx\n" " Absolute range: %016lx - %016lx\n", mb_array[i].logicalStart, mb_array[i].logicalEnd, mb_array[i].absStart, mb_array[i].absEnd); mb_array[i].absStart = addr_to_chunk( mb_array[i].absStart & 0x000fffffffffffff ); mb_array[i].absEnd = addr_to_chunk( mb_array[i].absEnd & 0x000fffffffffffff ); mb_array[i].logicalStart = addr_to_chunk( mb_array[i].logicalStart ); mb_array[i].logicalEnd = addr_to_chunk( mb_array[i].logicalEnd ); } } return numSegmentBlocks; } unsigned long iSeries_process_mainstore_vpd( struct MemoryBlock *mb_array, unsigned long max_entries ) { unsigned long i; unsigned long mem_blocks = 0; if ( __is_processor( PV_POWER4 ) ) mem_blocks = iSeries_process_Regatta_mainstore_vpd( mb_array, max_entries ); else mem_blocks = iSeries_process_Condor_mainstore_vpd( mb_array, max_entries ); printk("Mainstore_VPD: numMemoryBlocks = %ld \n", mem_blocks); for ( i=0; i<mem_blocks; ++i ) { printk("Mainstore_VPD: block %3ld logical chunks %016lx - %016lx\n" " abs chunks %016lx - %016lx\n", i, mb_array[i].logicalStart, mb_array[i].logicalEnd, mb_array[i].absStart, mb_array[i].absEnd); } return mem_blocks; } /* * void __init iSeries_init_early() */ void __init iSeries_init_early(void) { #ifdef CONFIG_PPC_ISERIES ppcdbg_initialize(); #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 ( naca->xRamDisk ) { initrd_start = (unsigned long)__va(naca->xRamDisk); initrd_end = initrd_start + naca->xRamDiskSize * PAGE_SIZE; initrd_below_start_ok = 1; // ramdisk in kernel space ROOT_DEV = MKDEV( RAMDISK_MAJOR, 0 ); if ( ((rd_size*1024)/PAGE_SIZE) < naca->xRamDiskSize ) rd_size = (naca->xRamDiskSize*PAGE_SIZE)/1024; } else #endif /* CONFIG_BLK_DEV_INITRD */ { /* ROOT_DEV = MKDEV( VIODASD_MAJOR, 1 ); */ } iSeries_recal_tb = get_tb(); iSeries_recal_titan = HvCallXm_loadTod(); ppc_md.setup_arch = iSeries_setup_arch; ppc_md.setup_residual = iSeries_setup_residual; ppc_md.get_cpuinfo = iSeries_get_cpuinfo; ppc_md.irq_cannonicalize = NULL; ppc_md.init_IRQ = iSeries_init_IRQ; ppc_md.init_ras_IRQ = NULL; ppc_md.get_irq = iSeries_get_irq; ppc_md.init = NULL; ppc_md.pcibios_fixup = iSeries_pcibios_fixup; ppc_md.pcibios_fixup_bus = iSeries_pcibios_fixup_bus; ppc_md.restart = iSeries_restart; ppc_md.power_off = iSeries_power_off; ppc_md.halt = iSeries_halt; ppc_md.time_init = NULL; ppc_md.get_boot_time = iSeries_get_boot_time; 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; hpte_init_iSeries(); tce_init_iSeries(); /* Initialize the table which translate Linux physical addresses to * AS/400 absolute addresses */ build_iSeries_Memory_Map(); setup_iSeries_cache_sizes(); /* Initialize machine-dependency vectors */ #ifdef CONFIG_SMP smp_init_iSeries(); #endif if ( itLpNaca.xPirEnvironMode == 0 ) piranha_simulator = 1; #endif } /* * void __init iSeries_init() */ void __init iSeries_init(unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7) { /* 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; } if (strstr(cmd_line, "dprofile=")) { char *p, *q; for (q = cmd_line; (p = strstr(q, "dprofile=")) != 0; ) { unsigned long size, new_klimit; q = p + 9; if (p > cmd_line && p[-1] != ' ') continue; dprof_shift = simple_strtoul(q, &q, 0); dprof_len = (unsigned long)&_etext - (unsigned long)&_stext; dprof_len >>= dprof_shift; size = ((dprof_len * sizeof(unsigned int)) + (PAGE_SIZE-1)) & PAGE_MASK; dprof_buffer = (unsigned int *)((klimit + (PAGE_SIZE-1)) & PAGE_MASK); new_klimit = ((unsigned long)dprof_buffer) + size; lmb_reserve( __pa(klimit), (new_klimit-klimit)); klimit = new_klimit; memset( dprof_buffer, 0, size ); } } iSeries_setup_dprofile(); iSeries_proc_early_init(); mf_init(); mf_initialized = 1; mb(); iSeries_proc_callback( &pmc_proc_init ); } #ifdef CONFIG_PPC_ISERIES /* * 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 AS/400 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 nextPhysChunk; u32 hptFirstChunk, hptLastChunk, hptSizeChunks, hptSizePages; u32 num_ptegs; u32 totalChunks,moreChunks; u32 currChunk, thisChunk, absChunk; u32 currDword; u32 chunkBit; u64 map; struct MemoryBlock mb[32]; unsigned long numMemoryBlocks, curBlock; /* Chunk size on iSeries is 256K bytes */ totalChunks = (u32)HvLpConfig_getMsChunks(); klimit = msChunks_alloc(klimit, totalChunks, 1UL<<18); /* 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)addr_to_chunk(itLpNaca.xLoadAreaAddr); loadAreaSize = itLpNaca.xLoadAreaChunks; /* Only add the pages already mapped here. * Otherwise we might add the hpt pages * The rest of the pages of the load area * aren't in the HPT yet and can still * be assigned an arbitrary physical address */ if ( (loadAreaSize * 64) > HvPagesToMap ) loadAreaSize = HvPagesToMap / 64; loadAreaLastChunk = loadAreaFirstChunk + loadAreaSize - 1; /* TODO Do we need to do something if the HPT is in the 64MB load area? * This would be required if the itLpNaca.xLoadAreaChunks includes * the HPT size */ printk( "Mapping load area - physical addr = 0000000000000000\n" " absolute addr = %016lx\n", chunk_to_addr(loadAreaFirstChunk) ); printk( "Load area size %dK\n", loadAreaSize*256 ); for ( nextPhysChunk = 0; nextPhysChunk < loadAreaSize; ++nextPhysChunk ) { msChunks.abs[nextPhysChunk] = loadAreaFirstChunk+nextPhysChunk; } /* Get absolute address of our HPT and remember it so * we won't map it to any physical address */ hptFirstChunk = (u32)addr_to_chunk(HvCallHpt_getHptAddress()); hptSizePages = (u32)(HvCallHpt_getHptPages()); hptSizeChunks = hptSizePages >> (msChunks.chunk_shift-PAGE_SHIFT); hptLastChunk = hptFirstChunk + hptSizeChunks - 1; printk( "HPT absolute addr = %016lx, size = %dK\n", chunk_to_addr(hptFirstChunk), hptSizeChunks*256 ); /* Fill in the htab_data structure */ /* Fill in size of hashed page table */ num_ptegs = hptSizePages * (PAGE_SIZE/(sizeof(HPTE)*HPTES_PER_GROUP)); htab_data.htab_num_ptegs = num_ptegs; htab_data.htab_hash_mask = num_ptegs - 1; /* The actual hashed page table is in the hypervisor, we have no direct access */ htab_data.htab = NULL; /* Determine if absolute memory has any * holes so that we can interpret the * access map we get back from the hypervisor * correctly. */ numMemoryBlocks = iSeries_process_mainstore_vpd( mb, 32 ); /* Process the main store access map from the hypervisor * to build up our physical -> absolute translation table */ curBlock = 0; 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; while ( thisChunk >= mb[curBlock].logicalEnd ) { ++curBlock; if ( curBlock >= numMemoryBlocks ) panic("out of memory blocks"); } if ( thisChunk < mb[curBlock].logicalStart ) panic("memory block error"); absChunk = mb[curBlock].absStart + ( thisChunk - mb[curBlock].logicalStart ); if ( ( ( absChunk < hptFirstChunk ) || ( absChunk > hptLastChunk ) ) && ( ( absChunk < loadAreaFirstChunk ) || ( absChunk > loadAreaLastChunk ) ) ) { msChunks.abs[nextPhysChunk] = absChunk; ++nextPhysChunk; } } ++thisChunk; } ++currDword; currChunk += 64; } /* main store size (in chunks) is * totalChunks - hptSizeChunks * which should be equal to * nextPhysChunk */ naca->physicalMemorySize = chunk_to_addr(nextPhysChunk); /* Bolt kernel mappings for all of memory */ iSeries_bolt_kernel( 0, naca->physicalMemorySize ); lmb_init(); lmb_add( 0, naca->physicalMemorySize ); lmb_reserve( 0, __pa(klimit)); /* * Hardcode to GP size. I am not sure where to get this info. DRENG */ naca->slb_size = 64; } /* * Set up the variables that describe the cache line sizes * for this machine. */ static void __init setup_iSeries_cache_sizes(void) { unsigned i,n; naca->iCacheL1LineSize = xIoHriProcessorVpd[0].xInstCacheOperandSize; naca->dCacheL1LineSize = xIoHriProcessorVpd[0].xDataCacheOperandSize; naca->iCacheL1LinesPerPage = PAGE_SIZE / naca->iCacheL1LineSize; naca->dCacheL1LinesPerPage = PAGE_SIZE / naca->dCacheL1LineSize; i = naca->iCacheL1LineSize; n = 0; while ((i=(i/2))) ++n; naca->iCacheL1LogLineSize = n; i = naca->dCacheL1LineSize; n = 0; while ((i=(i/2))) ++n; naca->dCacheL1LogLineSize = n; printk( "D-cache line size = %d (log = %d)\n", (unsigned)naca->dCacheL1LineSize, (unsigned)naca->dCacheL1LogLineSize ); printk( "I-cache line size = %d (log = %d)\n", (unsigned)naca->iCacheL1LineSize, (unsigned)naca->iCacheL1LogLineSize ); } /* * Bolt the kernel addr space into the HPT */ static void __init iSeries_bolt_kernel(unsigned long saddr, unsigned long eaddr) { unsigned long pa; unsigned long mode_rw = _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX; HPTE hpte; for (pa=saddr; pa < eaddr ;pa+=PAGE_SIZE) { unsigned long ea = (unsigned long)__va(pa); unsigned long vsid = get_kernel_vsid( ea ); unsigned long va = ( vsid << 28 ) | ( pa & 0xfffffff ); unsigned long vpn = va >> PAGE_SHIFT; unsigned long slot = HvCallHpt_findValid( &hpte, vpn ); if ( hpte.dw0.dw0.v ) { /* HPTE exists, so just bolt it */ HvCallHpt_setSwBits( slot, 0x10, 0 ); } else { /* No HPTE exists, so create a new bolted one */ build_valid_hpte(vsid, ea, pa, NULL, mode_rw, 1); } } } #endif /* CONFIG_PPC_ISERIES */ /* * Document me. */ void __init iSeries_setup_arch(void) { void * eventStack; /* Setup the Lp Event Queue */ /* Allocate a page for the Event Stack * The hypervisor wants the absolute real address, so * we subtract out the KERNELBASE and add in the * absolute real address of the kernel load area */ eventStack = alloc_bootmem_pages( LpEventStackSize ); memset( eventStack, 0, LpEventStackSize ); /* Invoke the hypervisor to initialize the event stack */ HvCallEvent_setLpEventStack( 0, eventStack, LpEventStackSize ); /* Initialize fields in our Lp Event Queue */ xItLpQueue.xSlicEventStackPtr = (char *)eventStack; xItLpQueue.xSlicCurEventPtr = (char *)eventStack; xItLpQueue.xSlicLastValidEventPtr = (char *)eventStack + (LpEventStackSize - LpEventMaxSize); xItLpQueue.xIndex = 0; /* Compute processor frequency */ procFreqHz = (((1UL<<34) * 1000000) / xIoHriProcessorVpd[0].xProcFreq ); procFreqMhz = procFreqHz / 1000000; procFreqMhzHundreths = (procFreqHz/10000) - (procFreqMhz*100); /* Compute time base frequency */ tbFreqHz = (((1UL<<32) * 1000000) / xIoHriProcessorVpd[0].xTimeBaseFreq ); tbFreqMhz = tbFreqHz / 1000000; tbFreqMhzHundreths = (tbFreqHz/10000) - (tbFreqMhz*100); printk("Max logical processors = %d\n", itVpdAreas.xSlicMaxLogicalProcs ); printk("Max physical processors = %d\n", itVpdAreas.xSlicMaxPhysicalProcs ); printk("Processor frequency = %lu.%02lu\n", procFreqMhz, procFreqMhzHundreths ); printk("Time base frequency = %lu.%02lu\n", tbFreqMhz, tbFreqMhzHundreths ); printk("Processor version = %x\n", xIoHriProcessorVpd[0].xPVR ); } /* * int iSeries_setup_residual() * * Description: * This routine pretty-prints CPU information gathered from the VPD * for use in /proc/cpuinfo * * Input(s): * *buffer - Buffer into which CPU data is to be printed. * * Output(s): * *buffer - Buffer with CPU data. * * Returns: * The number of bytes copied into 'buffer' if OK, otherwise zero or less * on error. */ void iSeries_setup_residual(struct seq_file *m, unsigned long cpu_id) { seq_printf(m, "clock\t\t: %lu.%02luMhz\n", procFreqMhz, procFreqMhzHundreths); seq_printf(m, "time base\t: %lu.%02luMHz\n", tbFreqMhz, tbFreqMhzHundreths); seq_printf(m, "i-cache\t\t: %d\n", naca->iCacheL1LineSize); seq_printf(m, "d-cache\t\t: %d\n", naca->dCacheL1LineSize); } void iSeries_get_cpuinfo(struct seq_file *m) { seq_printf(m, "machine\t\t: 64-bit iSeries Logical Partition\n"); } /* * Document me. * and Implement me. */ int iSeries_get_irq(struct pt_regs *regs) { /* -2 means ignore this interrupt */ return -2; } /* * Document me. */ void iSeries_restart(char *cmd) { mf_reboot(); } /* * Document me. */ void iSeries_power_off(void) { mf_powerOff(); } /* * Document me. */ void iSeries_halt(void) { mf_powerOff(); } /* * Nothing to do here. */ void __init iSeries_time_init(void) { /* Nothing to do */ } /* JDH Hack */ unsigned long jdh_time = 0; extern void setup_default_decr(void); /* * void __init iSeries_calibrate_decr() * * Description: * This routine retrieves the internal processor frequency from the VPD, * and sets up the kernel timer decrementer based on that value. * */ void __init iSeries_calibrate_decr(void) { unsigned long freq; unsigned long cyclesPerUsec; unsigned long tbf; struct div_result divres; /* Compute decrementer (and TB) frequency * in cycles/sec */ tbf = xIoHriProcessorVpd[0].xTimeBaseFreq; freq = 0x0100000000; freq *= 1000000; /* 2^32 * 10^6 */ freq = freq / tbf; /* cycles / sec */ cyclesPerUsec = freq / 1000000; /* cycles / usec */ /* Set the amount to refresh the decrementer by. This * is the number of decrementer ticks it takes for * 1/HZ seconds. */ tb_ticks_per_jiffy = freq / HZ; /* * tb_ticks_per_sec = freq; would give better accuracy * but tb_ticks_per_sec = tb_ticks_per_jiffy*HZ; assures * that jiffies (and xtime) will match the time returned * by do_gettimeofday. */ tb_ticks_per_sec = tb_ticks_per_jiffy * HZ; tb_ticks_per_usec = cyclesPerUsec; tb_to_us = mulhwu_scale_factor(freq, 1000000); div128_by_32( 1024*1024, 0, tb_ticks_per_sec, &divres ); tb_to_xs = divres.result_low; setup_default_decr(); } void __init iSeries_progress( char * st, unsigned short code ) { printk( "Progress: [%04x] - %s\n", (unsigned)code, st ); if ( !piranha_simulator && mf_initialized ) { if (code != 0xffff) mf_displayProgress( code ); else mf_clearSrc(); } } void iSeries_fixup_klimit(void) { /* Change klimit to take into account any ram disk that may be included */ if (naca->xRamDisk) klimit = KERNELBASE + (u64)naca->xRamDisk + (naca->xRamDiskSize * PAGE_SIZE); else { /* No ram disk was included - check and see if there was an embedded system map */ /* Change klimit to take into account any embedded system map */ if (embedded_sysmap_end) klimit = KERNELBASE + ((embedded_sysmap_end+4095) & 0xfffffffffffff000); } } static void iSeries_setup_dprofile(void) { if ( dprof_buffer ) { unsigned i; for (i=0; i<maxPacas; ++i) { xPaca[i].prof_shift = dprof_shift; xPaca[i].prof_len = dprof_len-1; xPaca[i].prof_buffer = dprof_buffer; xPaca[i].prof_stext = (unsigned *)&_stext; mb(); xPaca[i].prof_enabled = 1; } } } --- NEW FILE: ioctl32.c --- /* * ioctl32.c: Conversion between 32bit and 64bit native ioctls. * * Based on sparc64 ioctl32.c by: * * Copyright (C) 1997-2000 Jakub Jelinek (ja...@re...) * Copyright (C) 1998 Eddie C. Dost (ec...@sk...) * * ppc64 changes: * * Copyright (C) 2000 Ken Aaker (kd...@rc...) * Copyright (C) 2001 Anton Blanchard (an...@au...) * * These routines maintain argument size conversion between 32bit and 64bit * ioctls. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version [...4532 lines suppressed...] while (t && t->cmd != cmd) t = (struct ioctl_trans *)t->next; if (t) { handler = (void *)t->handler; error = handler(fd, cmd, arg, filp); } else { static int count = 0; if (++count <= 20) printk("sys32_ioctl(%s:%d): Unknown cmd fd(%d) " "cmd(%08x) arg(%08x)\n", current->comm, current->pid, (int)fd, (unsigned int)cmd, (unsigned int)arg); error = -EINVAL; } out: fput(filp); out2: return error; } --- NEW FILE: open_pic.c --- /* * arch/ppc/kernel/open_pic.c -- OpenPIC Interrupt Handling * * Copyright (C) 1997 Geert Uytterhoeven * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive * for more details. */ #include <linux/config.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/init.h> #include <linux/irq.h> #include <asm/ptrace.h> #include <asm/signal.h> #include <asm/io.h> #include <asm/pgtable.h> #include <asm/irq.h> #include <asm/prom.h> #include <asm/machdep.h> #include "local_irq.h" #include "open_pic.h" #include "open_pic_defs.h" #include "i8259.h" #include <asm/ppcdebug.h> void* OpenPIC_Addr; static volatile struct OpenPIC *OpenPIC = NULL; u_int OpenPIC_NumInitSenses __initdata = 0; u_char *OpenPIC_InitSenses __initdata = NULL; extern int use_of_interrupt_tree; void find_ISUs(void); static u_int NumProcessors; static u_int NumSources; static int NumISUs; static int open_pic_irq_offset; static volatile unsigned char* chrp_int_ack_special; static int broken_ipi_registers; OpenPIC_SourcePtr ISU[OPENPIC_MAX_ISU]; static void openpic_end_irq(unsigned int irq_nr); static void openpic_ack_irq(unsigned int irq_nr); static void openpic_set_affinity(unsigned int irq_nr, unsigned long cpumask); struct hw_interrupt_type open_pic = { " OpenPIC ", NULL, NULL, openpic_enable_irq, openpic_disable_irq, openpic_ack_irq, openpic_end_irq, openpic_set_affinity }; #ifdef CONFIG_SMP static void openpic_end_ipi(unsigned int irq_nr); static void openpic_ack_ipi(unsigned int irq_nr); static void openpic_enable_ipi(unsigned int irq_nr); static void openpic_disable_ipi(unsigned int irq_nr); struct hw_interrupt_type open_pic_ipi = { " OpenPIC ", NULL, NULL, openpic_enable_ipi, openpic_disable_ipi, openpic_ack_ipi, openpic_end_ipi, 0 }; #endif /* CONFIG_SMP */ unsigned int openpic_vec_ipi; unsigned int openpic_vec_timer; unsigned int openpic_vec_spurious; /* * Accesses to the current processor's openpic registers */ #ifdef CONFIG_SMP #define THIS_CPU Processor[cpu] #define DECL_THIS_CPU int cpu = hard_smp_processor_id() #define CHECK_THIS_CPU check_arg_cpu(cpu) #else #define THIS_CPU Processor[hard_smp_processor_id()] #define DECL_THIS_CPU #define CHECK_THIS_CPU #endif /* CONFIG_SMP */ #if 0 #define check_arg_ipi(ipi) \ if (ipi < 0 || ipi >= OPENPIC_NUM_IPI) \ printk(KERN_ERR "open_pic.c:%d: illegal ipi %d\n", __LINE__, ipi); #define check_arg_timer(timer) \ if (timer < 0 || timer >= OPENPIC_NUM_TIMERS) \ printk(KERN_ERR "open_pic.c:%d: illegal timer %d\n", __LINE__, timer); #define check_arg_vec(vec) \ if (vec < 0 || vec >= OPENPIC_NUM_VECTORS) \ printk(KERN_ERR "open_pic.c:%d: illegal vector %d\n", __LINE__, vec); #define check_arg_pri(pri) \ if (pri < 0 || pri >= OPENPIC_NUM_PRI) \ printk(KERN_ERR "open_pic.c:%d: illegal priority %d\n", __LINE__, pri); /* * Print out a backtrace if it's out of range, since if it's larger than NR_IRQ's * data has probably been corrupted and we're going to panic or deadlock later * anyway --Troy */ extern unsigned long* _get_SP(void); #define check_arg_irq(irq) \ if (irq < open_pic_irq_offset || irq >= (NumSources+open_pic_irq_offset)){ \ printk(KERN_ERR "open_pic.c:%d: illegal irq %d\n", __LINE__, irq); \ print_backtrace(_get_SP()); } #define check_arg_cpu(cpu) \ if (cpu < 0 || cpu >= OPENPIC_MAX_PROCESSORS){ \ printk(KERN_ERR "open_pic.c:%d: illegal cpu %d\n", __LINE__, cpu); \ print_backtrace(_get_SP()); } #else #define check_arg_ipi(ipi) do {} while (0) #define check_arg_timer(timer) do {} while (0) #define check_arg_vec(vec) do {} while (0) #define check_arg_pri(pri) do {} while (0) #define check_arg_irq(irq) do {} while (0) #define check_arg_cpu(cpu) do {} while (0) #endif #define GET_ISU(source) ISU[(source) >> 4][(source) & 0xf] void __init openpic_init_IRQ(void) { struct device_node *np; int i; unsigned int *addrp; unsigned char* chrp_int_ack_special = 0; unsigned char init_senses[NR_IRQS - NUM_8259_INTERRUPTS]; int nmi_irq = -1; if (!(np = find_devices("pci")) || !(addrp = (unsigned int *) get_property(np, "8259-interrupt-acknowledge", NULL))) printk(KERN_ERR "Cannot find pci to get ack address\n"); else chrp_int_ack_special = (unsigned char *) __ioremap(addrp[prom_n_addr_cells(np)-1], 1, _PAGE_NO_CACHE); /* hydra still sets OpenPIC_InitSenses to a static set of values */ if (OpenPIC_InitSenses == NULL) { 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(); } static inline u_int openpic_read(volatile u_int *addr) { u_int val; val = in_le32(addr); return val; } static inline void openpic_write(volatile u_int *addr, u_int val) { out_le32(addr, val); } static inline u_int openpic_readfield(volatile u_int *addr, u_int mask) { u_int val = openpic_read(addr); return val & mask; } static inline void openpic_writefield(volatile u_int *addr, u_int mask, u_int field) { u_int val = openpic_read(addr); openpic_write(addr, (val & ~mask) | (field & mask)); } static inline void openpic_clearfield(volatile u_int *addr, u_int mask) { openpic_writefield(addr, mask, 0); } static inline void openpic_setfield(volatile u_int *addr, u_int mask) { openpic_writefield(addr, mask, mask); } static void openpic_safe_writefield(volatile u_int *addr, u_int mask, u_int field) { unsigned int loops = 100000; openpic_setfield(addr, OPENPIC_MASK); while (openpic_read(addr) & OPENPIC_ACTIVITY) { if (!loops--) { printk(KERN_ERR "openpic_safe_writefield timeout\n"); break; } } openpic_writefield(addr, mask | OPENPIC_MASK, field | OPENPIC_MASK); } #ifdef CONFIG_SMP static u_int openpic_read_IPI(volatile u_int* addr) { u_int val = 0; if (broken_ipi_registers) /* yes this is right ... bug, feature, you decide! -- tgall */ val = in_be32(addr); else val = in_le32(addr); return val; } static void openpic_test_broken_IPI(void) { u_int t; openpic_write(&OpenPIC->Global.IPI_Vector_Priority(0), OPENPIC_MASK); t = openpic_read(&OpenPIC->Global.IPI_Vector_Priority(0)); if (t == le32_to_cpu(OPENPIC_MASK)) { printk(KERN_INFO "OpenPIC reversed IPI registers detected\n"); broken_ipi_registers = 1; } } /* because of the power3 be / le above, this is needed */ static inline void openpic_writefield_IPI(volatile u_int* addr, u_int mask, u_int field) { u_int val = openpic_read_IPI(addr); openpic_write(addr, (val & ~mask) | (field & mask)); } static inline void openpic_clearfield_IPI(volatile u_int *addr, u_int mask) { openpic_writefield_IPI(addr, mask, 0); } static inline void openpic_setfield_IPI(volatile u_int *addr, u_int mask) { openpic_writefield_IPI(addr, mask, mask); } static void openpic_safe_writefield_IPI(volatile u_int *addr, u_int mask, u_int field) { unsigned int loops = 100000; openpic_setfield_IPI(addr, OPENPIC_MASK); /* wait until it's not in use */ /* BenH: Is this code really enough ? I would rather check the result * and eventually retry ... */ while(openpic_read_IPI(addr) & OPENPIC_ACTIVITY) { if (!loops--) { printk(KERN_ERR "openpic_safe_writefield timeout\n"); break; } } openpic_writefield_IPI(addr, mask, field | OPENPIC_MASK); } #endif /* CONFIG_SMP */ void __init openpic_init(int main_pic, int offset, unsigned char* chrp_ack, int programmer_switch_irq) { u_int t, i; u_int timerfreq; const char *version; if (!OpenPIC_Addr) { printk(KERN_INFO "No OpenPIC found !\n"); return; } OpenPIC = (volatile struct OpenPIC *)OpenPIC_Addr; ppc_md.progress("openpic enter",0x122); t = openpic_read(&OpenPIC->Global.Feature_Reporting0); switch (t & OPENPIC_FEATURE_VERSION_MASK) { case 1: version = "1.0"; break; case 2: version = "1.2"; break; case 3: version = "1.3"; break; default: version = "?"; break; } NumProcessors = ((t & OPENPIC_FEATURE_LAST_PROCESSOR_MASK) >> OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT) + 1; NumSources = ((t & OPENPIC_FEATURE_LAST_SOURCE_MASK) >> OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1; printk(KERN_INFO "OpenPIC Version %s (%d CPUs and %d IRQ sources) at %p\n", version, NumProcessors, NumSources, OpenPIC); timerfreq = openpic_read(&OpenPIC->Global.Timer_Frequency); if (timerfreq) printk(KERN_INFO "OpenPIC timer frequency is %d.%06d MHz\n", timerfreq / 1000000, timerfreq % 1000000); if (!main_pic) return; open_pic_irq_offset = offset; chrp_int_ack_special = (volatile unsigned char*)chrp_ack; find_ISUs(); /* Initialize timer interrupts */ ppc_md.progress("openpic timer",0x3ba); for (i = 0; i < OPENPIC_NUM_TIMERS; i++) { /* Disabled, Priority 0 */ openpic_inittimer(i, 0, openpic_vec_timer+i); /* No processor */ openpic_maptimer(i, 0); } #ifdef CONFIG_SMP /* Initialize IPI interrupts */ ppc_md.progress("openpic ipi",0x3bb); openpic_test_broken_IPI(); for (i = 0; i < OPENPIC_NUM_IPI; i++) { /* Disabled, Priority 10..13 */ openpic_initipi(i, 10+i, openpic_vec_ipi+i); /* IPIs are per-CPU */ irq_desc[openpic_vec_ipi+i].status |= IRQ_PER_CPU; irq_desc[openpic_vec_ipi+i].handler = &open_pic_ipi; } #endif /* Initialize external interrupts */ ppc_md.progress("openpic ext",0x3bc); openpic_set_priority(0xf); /* SIOint (8259 cascade) is special */ if (offset) { openpic_initirq(0, 8, offset, 1, 1); openpic_mapirq(0, 1<<get_hard_smp_processor_id(0)); } /* Init all external sources */ for (i = 1; i < NumSources; i++) { int pri, sense; /* the bootloader may have left it enabled (bad !) */ openpic_disable_irq(i+offset); pri = (i == programmer_switch_irq)? 9: 8; sense = (i < OpenPIC_NumInitSenses)? OpenPIC_InitSenses[i]: 1; if (sense) irq_desc[i+offset].status = IRQ_LEVEL; /* Enabled, Priority 8 or 9 */ openpic_initirq(i, pri, i+offset, !sense, sense); /* Processor 0 */ openpic_mapirq(i, 1<<get_hard_smp_processor_id(0)); } /* Init descriptors */ for (i = offset; i < NumSources + offset; i++) irq_desc[i].handler = &open_pic; /* Initialize the spurious interrupt */ ppc_md.progress("openpic spurious",0x3bd); openpic_set_spurious(openpic_vec_spurious); /* Initialize the cascade */ if (offset) { if (request_irq(offset, no_action, SA_INTERRUPT, "82c59 cascade", NULL)) printk(KERN_ERR "Unable to get OpenPIC IRQ 0 for cascade\n"); } openpic_set_priority(0); openpic_disable_8259_pass_through(); ppc_md.progress("openpic exit",0x222); } void openpic_setup_ISU(int isu_num, unsigned long addr) { if (isu_num >= OPENPIC_MAX_ISU) return; ISU[isu_num] = (OpenPIC_SourcePtr) __ioremap(addr, 0x400, _PAGE_NO_CACHE); if (isu_num >= NumISUs) NumISUs = isu_num + 1; } void find_ISUs(void) { /* Use /interrupt-controller/reg and * /interrupt-controller/interrupt-ranges from OF device tree * the ISU array is setup in chrp_pci.c in ibm_add_bridges * as a result * -- tgall */ /* basically each ISU is a bus, and this assumes that * open_pic_isu_count interrupts per bus are possible * ISU == Interrupt Source */ NumSources = NumISUs * 0x10; openpic_vec_ipi = NumSources + open_pic_irq_offset; openpic_vec_timer = openpic_vec_ipi + OPENPIC_NUM_IPI; openpic_vec_spurious = openpic_vec_timer + OPENPIC_NUM_TIMERS; } static inline void openpic_reset(void) { openpic_setfield(&OpenPIC->Global.Global_Configuration0, OPENPIC_CONFIG_RESET); } static inline void openpic_enable_8259_pass_through(void) { openpic_clearfield(&OpenPIC->Global.Global_Configuration0, OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); } static void openpic_disable_8259_pass_through(void) { openpic_setfield(&OpenPIC->Global.Global_Configuration0, OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); } /* * Find out the current interrupt */ static u_int openpic_irq(void) { u_int vec; DECL_THIS_CPU; CHECK_THIS_CPU; vec = openpic_readfield(&OpenPIC->THIS_CPU.Interrupt_Acknowledge, OPENPIC_VECTOR_MASK); return vec; } static void openpic_eoi(void) { DECL_THIS_CPU; CHECK_THIS_CPU; openpic_write(&OpenPIC->THIS_CPU.EOI, 0); /* Handle PCI write posting */ (void)openpic_read(&OpenPIC->THIS_CPU.EOI); } static inline u_int openpic_get_priority(void) { DECL_THIS_CPU; CHECK_THIS_CPU; return openpic_readfield(&OpenPIC->THIS_CPU.Current_Task_Priority, OPENPIC_CURRENT_TASK_PRIORITY_MASK); } static void openpic_set_priority(u_int pri) { DECL_THIS_CPU; CHECK_THIS_CPU; check_arg_pri(pri); openpic_writefield(&OpenPIC->THIS_CPU.Current_Task_Priority, OPENPIC_CURRENT_TASK_PRIORITY_MASK, pri); } /* * Get/set the spurious vector */ static inline u_int openpic_get_spurious(void) { return openpic_readfield(&OpenPIC->Global.Spurious_Vector, OPENPIC_VECTOR_MASK); } static void openpic_set_spurious(u_int vec) { check_arg_vec(vec); openpic_writefield(&OpenPIC->Global.Spurious_Vector, OPENPIC_VECTOR_MASK, vec); } /* * Convert a cpu mask from logical to physical cpu numbers. */ static inline u32 physmask(u32 cpumask) { int i; u32 mask = 0; for (i = 0; i < smp_num_cpus; ++i, cpumask >>= 1) mask |= (cpumask & 1) << get_hard_smp_processor_id(i); return mask; } void openpic_init_processor(u_int cpumask) { openpic_write(&OpenPIC->Global.Processor_Initialization, physmask(cpumask)); } #ifdef CONFIG_SMP /* * Initialize an interprocessor interrupt (and disable it) * * ipi: OpenPIC interprocessor interrupt number * pri: interrupt source priority * vec: the vector it will produce */ static void __init openpic_initipi(u_int ipi, u_int pri, u_int vec) { check_arg_ipi(ipi); check_arg_pri(pri); check_arg_vec(vec); openpic_safe_writefield_IPI(&OpenPIC->Global.IPI_Vector_Priority(ipi), OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK, (pri << OPENPIC_PRIORITY_SHIFT) | vec); } /* * Send an IPI to one or more CPUs * * Externally called, however, it takes an IPI number (0...OPENPIC_NUM_IPI) * and not a system-wide interrupt number */ void openpic_cause_IPI(u_int ipi, u_int cpumask) { DECL_THIS_CPU; CHECK_THIS_CPU; check_arg_ipi(ipi); openpic_write(&OpenPIC->THIS_CPU.IPI_Dispatch(ipi), physmask(cpumask)); } void openpic_request_IPIs(void) { int i; /* * Make sure this matches what is defined in smp.c for * smp_message_{pass|recv}() or what shows up in * /proc/interrupts will be wrong!!! --Troy */ if (OpenPIC == NULL) return; request_irq(openpic_vec_ipi, openpic_ipi_action, 0, "IPI0 (call function)", 0); request_irq(openpic_vec_ipi+1, openpic_ipi_action, 0, "IPI1 (reschedule)", 0); request_irq(openpic_vec_ipi+2, openpic_ipi_action, 0, "IPI2 (invalidate tlb)", 0); request_irq(openpic_vec_ipi+3, openpic_ipi_action, 0, "IPI3 (xmon break)", 0); for ( i = 0; i < OPENPIC_NUM_IPI ; i++ ) openpic_enable_ipi(openpic_vec_ipi+i); } /* * Do per-cpu setup for SMP systems. * * Get IPI's working and start taking interrupts. * -- Cort */ static spinlock_t openpic_setup_lock __initdata = SPIN_LOCK_UNLOCKED; void __init do_openpic_setup_cpu(void) { #ifdef CONFIG_IRQ_ALL_CPUS int i; u32 msk = 1 << hard_smp_processor_id(); #endif spin_lock(&openpic_setup_lock); #ifdef CONFIG_IRQ_ALL_CPUS /* let the openpic know we want intrs. default affinity * is 0xffffffff until changed via /proc * That's how it's done on x86. If we want it differently, then * we should make sure we also change the default values of irq_affinity * in irq.c. */ for (i = 0; i < NumSources ; i++) openpic_mapirq(i, openpic_read(&GET_ISU(i).Destination) | msk); #endif /* CONFIG_IRQ_ALL_CPUS */ openpic_set_priority(0); spin_unlock(&openpic_setup_lock); } #endif /* CONFIG_SMP */ /* * Initialize a timer interrupt (and disable it) * * timer: OpenPIC timer number * pri: interrupt source priority * vec: the vector it will produce */ static void __init openpic_inittimer(u_int timer, u_int pri, u_int vec) { check_arg_timer(timer); check_arg_pri(pri); check_arg_vec(vec); openpic_safe_writefield(&OpenPIC->Global.Timer[timer].Vector_Priority, OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK, (pri << OPENPIC_PRIORITY_SHIFT) | vec); } /* * Map a timer interrupt to one or more CPUs */ static void __init openpic_maptimer(u_int timer, u_int cpumask) { check_arg_timer(timer); openpic_write(&OpenPIC->Global.Timer[timer].Destination, physmask(cpumask)); } /* * * All functions below take an offset'ed irq argument * */ /* * Enable/disable an external interrupt source * * Externally called, irq is an offseted system-wide interrupt number */ static void openpic_enable_irq(u_int irq) { unsigned int loops = 100000; check_arg_irq(irq); openpic_clearfield(&GET_ISU(irq - open_pic_irq_offset).Vector_Priority, OPENPIC_MASK); /* make sure mask gets to controller before we return to user */ do { if (!loops--) { printk(KERN_ERR "openpic_enable_irq timeout\n"); break; } mb(); /* sync is probably useless here */ } while(openpic_readfield(&GET_ISU(irq - open_pic_irq_offset).Vector_Priority, OPENPIC_MASK)); } static void openpic_disable_irq(u_int irq) { u32 vp; unsigned int loops = 100000; check_arg_irq(irq); openpic_setfield(&GET_ISU(irq - open_pic_irq_offset).Vector_Priority, OPENPIC_MASK); /* make sure mask gets to controller before we return to user */ do { if (!loops--) { printk(KERN_ERR "openpic_disable_irq timeout\n"); break; } mb(); /* sync is probably useless here */ vp = openpic_readfield(&GET_ISU(irq - open_pic_irq_offset).Vector_Priority, OPENPIC_MASK | OPENPIC_ACTIVITY); } while((vp & OPENPIC_ACTIVITY) && !(vp & OPENPIC_MASK)); } #ifdef CONFIG_SMP /* * Enable/disable an IPI interrupt source * * Externally called, irq is an offseted system-wide interrupt number */ void openpic_enable_ipi(u_int irq) { irq -= openpic_vec_ipi; check_arg_ipi(irq); openpic_clearfield_IPI(&OpenPIC->Global.IPI_Vector_Priority(irq), OPENPIC_MASK); } void openpic_disable_ipi(u_int irq) { /* NEVER disable an IPI... that's just plain wrong! */ } #endif /* * Initialize an interrupt source (and disable it!) * * irq: OpenPIC interrupt number * pri: interrupt source priority * vec: the vector it will produce * pol: polarity (1 for positive, 0 for negative) * sense: 1 for level, 0 for edge */ static void openpic_initirq(u_int irq, u_int pri, u_int vec, int pol, int sense) { openpic_safe_writefield(&GET_ISU(irq).Vector_Priority, OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK | OPENPIC_SENSE_MASK | OPENPIC_POLARITY_MASK, (pri << OPENPIC_PRIORITY_SHIFT) | vec | (pol ? OPENPIC_POLARITY_POSITIVE : OPENPIC_POLARITY_NEGATIVE) | (sense ? OPENPIC_SENSE_LEVEL : OPENPIC_SENSE_EDGE)); } /* * Map an interrupt source to one or more CPUs */ static void openpic_mapirq(u_int irq, u_int physmask) { openpic_write(&GET_ISU(irq).Destination, physmask); } /* * Set the sense for an interrupt source (and disable it!) * * sense: 1 for level, 0 for edge */ static inline void openpic_set_sense(u_int irq, int sense) { openpic_safe_writefield(&GET_ISU(irq).Vector_Priority, OPENPIC_SENSE_LEVEL, (sense ? OPENPIC_SENSE_LEVEL : 0)); } /* No spinlocks, should not be necessary with the OpenPIC * (1 register = 1 interrupt and we have the desc lock). */ static void openpic_ack_irq(unsigned int irq_nr) { } static void openpic_end_irq(unsigned int irq_nr) { if ((irq_desc[irq_nr].status & IRQ_LEVEL) != 0) openpic_eoi(); } static void openpic_set_affinity(unsigned int irq_nr, unsigned long cpumask) { openpic_mapirq(irq_nr - open_pic_irq_offset, physmask(cpumask)); } #ifdef CONFIG_SMP static void openpic_ack_ipi(unsigned int irq_nr) { } static void openpic_end_ipi(unsigned int irq_nr) { /* IPIs are marked IRQ_PER_CPU. This has the side effect of * preventing the IRQ_PENDING/IRQ_INPROGRESS logic from * applying to them. We EOI them late to avoid re-entering. * however, I'm wondering if we could simply let them have the * SA_INTERRUPT flag and let them execute with all interrupts OFF. * This would have the side effect of either running cross-CPU * functions with interrupts off, or we can re-enable them explicitely * with a __sti() in smp_call_function_interrupt(), since * smp_call_function() is protected by a spinlock. * Or maybe we shouldn't set the IRQ_PER_CPU flag on cross-CPU * function calls IPI at all but that would make a special case. */ openpic_eoi(); } static void openpic_ipi_action(int cpl, void *dev_id, struct pt_regs *regs) { smp_message_recv(cpl-openpic_vec_ipi, regs); } #endif /* CONFIG_SMP */ int openpic_get_irq(struct pt_regs *regs) { extern int i8259_irq(int cpu); int irq = openpic_irq(); /* Management of the cascade should be moved out of here */ if (open_pic_irq_offset && irq == open_pic_irq_offset) { /* * This magic address generates a PCI IACK cycle. */ if ( chrp_int_ack_special ) irq = *chrp_int_ack_special; else irq = i8259_irq( smp_processor_id() ); openpic_eoi(); } if (irq == openpic_vec_spurious) irq = -1; return irq; } |