Path: blob/master/arch/powerpc/platforms/iseries/setup.c
10820 views
/*1* Copyright (c) 2000 Mike Corrigan <[email protected]>2* Copyright (c) 1999-2000 Grant Erickson <[email protected]>3*4* Description:5* Architecture- / platform-specific boot-time initialization code for6* the IBM iSeries LPAR. Adapted from original code by Grant Erickson and7* code by Gary Thomas, Cort Dougan <[email protected]>, and Dan Malek8* <[email protected]>.9*10* This program is free software; you can redistribute it and/or11* modify it under the terms of the GNU General Public License12* as published by the Free Software Foundation; either version13* 2 of the License, or (at your option) any later version.14*/1516#undef DEBUG1718#include <linux/init.h>19#include <linux/threads.h>20#include <linux/smp.h>21#include <linux/param.h>22#include <linux/string.h>23#include <linux/seq_file.h>24#include <linux/kdev_t.h>25#include <linux/kexec.h>26#include <linux/major.h>27#include <linux/root_dev.h>28#include <linux/kernel.h>29#include <linux/hrtimer.h>30#include <linux/tick.h>3132#include <asm/processor.h>33#include <asm/machdep.h>34#include <asm/page.h>35#include <asm/mmu.h>36#include <asm/pgtable.h>37#include <asm/mmu_context.h>38#include <asm/cputable.h>39#include <asm/sections.h>40#include <asm/iommu.h>41#include <asm/firmware.h>42#include <asm/system.h>43#include <asm/time.h>44#include <asm/paca.h>45#include <asm/cache.h>46#include <asm/abs_addr.h>47#include <asm/iseries/hv_lp_config.h>48#include <asm/iseries/hv_call_event.h>49#include <asm/iseries/hv_call_xm.h>50#include <asm/iseries/it_lp_queue.h>51#include <asm/iseries/mf.h>52#include <asm/iseries/hv_lp_event.h>53#include <asm/iseries/lpar_map.h>54#include <asm/udbg.h>55#include <asm/irq.h>5657#include "naca.h"58#include "setup.h"59#include "irq.h"60#include "vpd_areas.h"61#include "processor_vpd.h"62#include "it_lp_naca.h"63#include "main_store.h"64#include "call_sm.h"65#include "call_hpt.h"66#include "pci.h"6768#ifdef DEBUG69#define DBG(fmt...) udbg_printf(fmt)70#else71#define DBG(fmt...)72#endif7374/* Function Prototypes */75static unsigned long build_iSeries_Memory_Map(void);76static void iseries_shared_idle(void);77static void iseries_dedicated_idle(void);787980struct MemoryBlock {81unsigned long absStart;82unsigned long absEnd;83unsigned long logicalStart;84unsigned long logicalEnd;85};8687/*88* Process the main store vpd to determine where the holes in memory are89* and return the number of physical blocks and fill in the array of90* block data.91*/92static unsigned long iSeries_process_Condor_mainstore_vpd(93struct MemoryBlock *mb_array, unsigned long max_entries)94{95unsigned long holeFirstChunk, holeSizeChunks;96unsigned long numMemoryBlocks = 1;97struct IoHriMainStoreSegment4 *msVpd =98(struct IoHriMainStoreSegment4 *)xMsVpd;99unsigned long holeStart = msVpd->nonInterleavedBlocksStartAdr;100unsigned long holeEnd = msVpd->nonInterleavedBlocksEndAdr;101unsigned long holeSize = holeEnd - holeStart;102103printk("Mainstore_VPD: Condor\n");104/*105* Determine if absolute memory has any106* holes so that we can interpret the107* access map we get back from the hypervisor108* correctly.109*/110mb_array[0].logicalStart = 0;111mb_array[0].logicalEnd = 0x100000000UL;112mb_array[0].absStart = 0;113mb_array[0].absEnd = 0x100000000UL;114115if (holeSize) {116numMemoryBlocks = 2;117holeStart = holeStart & 0x000fffffffffffffUL;118holeStart = addr_to_chunk(holeStart);119holeFirstChunk = holeStart;120holeSize = addr_to_chunk(holeSize);121holeSizeChunks = holeSize;122printk( "Main store hole: start chunk = %0lx, size = %0lx chunks\n",123holeFirstChunk, holeSizeChunks );124mb_array[0].logicalEnd = holeFirstChunk;125mb_array[0].absEnd = holeFirstChunk;126mb_array[1].logicalStart = holeFirstChunk;127mb_array[1].logicalEnd = 0x100000000UL - holeSizeChunks;128mb_array[1].absStart = holeFirstChunk + holeSizeChunks;129mb_array[1].absEnd = 0x100000000UL;130}131return numMemoryBlocks;132}133134#define MaxSegmentAreas 32135#define MaxSegmentAdrRangeBlocks 128136#define MaxAreaRangeBlocks 4137138static unsigned long iSeries_process_Regatta_mainstore_vpd(139struct MemoryBlock *mb_array, unsigned long max_entries)140{141struct IoHriMainStoreSegment5 *msVpdP =142(struct IoHriMainStoreSegment5 *)xMsVpd;143unsigned long numSegmentBlocks = 0;144u32 existsBits = msVpdP->msAreaExists;145unsigned long area_num;146147printk("Mainstore_VPD: Regatta\n");148149for (area_num = 0; area_num < MaxSegmentAreas; ++area_num ) {150unsigned long numAreaBlocks;151struct IoHriMainStoreArea4 *currentArea;152153if (existsBits & 0x80000000) {154unsigned long block_num;155156currentArea = &msVpdP->msAreaArray[area_num];157numAreaBlocks = currentArea->numAdrRangeBlocks;158printk("ms_vpd: processing area %2ld blocks=%ld",159area_num, numAreaBlocks);160for (block_num = 0; block_num < numAreaBlocks;161++block_num ) {162/* Process an address range block */163struct MemoryBlock tempBlock;164unsigned long i;165166tempBlock.absStart =167(unsigned long)currentArea->xAdrRangeBlock[block_num].blockStart;168tempBlock.absEnd =169(unsigned long)currentArea->xAdrRangeBlock[block_num].blockEnd;170tempBlock.logicalStart = 0;171tempBlock.logicalEnd = 0;172printk("\n block %ld absStart=%016lx absEnd=%016lx",173block_num, tempBlock.absStart,174tempBlock.absEnd);175176for (i = 0; i < numSegmentBlocks; ++i) {177if (mb_array[i].absStart ==178tempBlock.absStart)179break;180}181if (i == numSegmentBlocks) {182if (numSegmentBlocks == max_entries)183panic("iSeries_process_mainstore_vpd: too many memory blocks");184mb_array[numSegmentBlocks] = tempBlock;185++numSegmentBlocks;186} else187printk(" (duplicate)");188}189printk("\n");190}191existsBits <<= 1;192}193/* Now sort the blocks found into ascending sequence */194if (numSegmentBlocks > 1) {195unsigned long m, n;196197for (m = 0; m < numSegmentBlocks - 1; ++m) {198for (n = numSegmentBlocks - 1; m < n; --n) {199if (mb_array[n].absStart <200mb_array[n-1].absStart) {201struct MemoryBlock tempBlock;202203tempBlock = mb_array[n];204mb_array[n] = mb_array[n-1];205mb_array[n-1] = tempBlock;206}207}208}209}210/*211* Assign "logical" addresses to each block. These212* addresses correspond to the hypervisor "bitmap" space.213* Convert all addresses into units of 256K chunks.214*/215{216unsigned long i, nextBitmapAddress;217218printk("ms_vpd: %ld sorted memory blocks\n", numSegmentBlocks);219nextBitmapAddress = 0;220for (i = 0; i < numSegmentBlocks; ++i) {221unsigned long length = mb_array[i].absEnd -222mb_array[i].absStart;223224mb_array[i].logicalStart = nextBitmapAddress;225mb_array[i].logicalEnd = nextBitmapAddress + length;226nextBitmapAddress += length;227printk(" Bitmap range: %016lx - %016lx\n"228" Absolute range: %016lx - %016lx\n",229mb_array[i].logicalStart,230mb_array[i].logicalEnd,231mb_array[i].absStart, mb_array[i].absEnd);232mb_array[i].absStart = addr_to_chunk(mb_array[i].absStart &2330x000fffffffffffffUL);234mb_array[i].absEnd = addr_to_chunk(mb_array[i].absEnd &2350x000fffffffffffffUL);236mb_array[i].logicalStart =237addr_to_chunk(mb_array[i].logicalStart);238mb_array[i].logicalEnd = addr_to_chunk(mb_array[i].logicalEnd);239}240}241242return numSegmentBlocks;243}244245static unsigned long iSeries_process_mainstore_vpd(struct MemoryBlock *mb_array,246unsigned long max_entries)247{248unsigned long i;249unsigned long mem_blocks = 0;250251if (mmu_has_feature(MMU_FTR_SLB))252mem_blocks = iSeries_process_Regatta_mainstore_vpd(mb_array,253max_entries);254else255mem_blocks = iSeries_process_Condor_mainstore_vpd(mb_array,256max_entries);257258printk("Mainstore_VPD: numMemoryBlocks = %ld\n", mem_blocks);259for (i = 0; i < mem_blocks; ++i) {260printk("Mainstore_VPD: block %3ld logical chunks %016lx - %016lx\n"261" abs chunks %016lx - %016lx\n",262i, mb_array[i].logicalStart, mb_array[i].logicalEnd,263mb_array[i].absStart, mb_array[i].absEnd);264}265return mem_blocks;266}267268static void __init iSeries_get_cmdline(void)269{270char *p, *q;271272/* copy the command line parameter from the primary VSP */273HvCallEvent_dmaToSp(cmd_line, 2 * 64* 1024, 256,274HvLpDma_Direction_RemoteToLocal);275276p = cmd_line;277q = cmd_line + 255;278while(p < q) {279if (!*p || *p == '\n')280break;281++p;282}283*p = 0;284}285286static void __init iSeries_init_early(void)287{288DBG(" -> iSeries_init_early()\n");289290/* Snapshot the timebase, for use in later recalibration */291iSeries_time_init_early();292293/*294* Initialize the DMA/TCE management295*/296iommu_init_early_iSeries();297298/* Initialize machine-dependency vectors */299#ifdef CONFIG_SMP300smp_init_iSeries();301#endif302303/* Associate Lp Event Queue 0 with processor 0 */304HvCallEvent_setLpEventQueueInterruptProc(0, 0);305306mf_init();307308DBG(" <- iSeries_init_early()\n");309}310311struct mschunks_map mschunks_map = {312/* XXX We don't use these, but Piranha might need them. */313.chunk_size = MSCHUNKS_CHUNK_SIZE,314.chunk_shift = MSCHUNKS_CHUNK_SHIFT,315.chunk_mask = MSCHUNKS_OFFSET_MASK,316};317EXPORT_SYMBOL(mschunks_map);318319static void mschunks_alloc(unsigned long num_chunks)320{321klimit = _ALIGN(klimit, sizeof(u32));322mschunks_map.mapping = (u32 *)klimit;323klimit += num_chunks * sizeof(u32);324mschunks_map.num_chunks = num_chunks;325}326327/*328* The iSeries may have very large memories ( > 128 GB ) and a partition329* may get memory in "chunks" that may be anywhere in the 2**52 real330* address space. The chunks are 256K in size. To map this to the331* memory model Linux expects, the AS/400 specific code builds a332* translation table to translate what Linux thinks are "physical"333* addresses to the actual real addresses. This allows us to make334* it appear to Linux that we have contiguous memory starting at335* physical address zero while in fact this could be far from the truth.336* To avoid confusion, I'll let the words physical and/or real address337* apply to the Linux addresses while I'll use "absolute address" to338* refer to the actual hardware real address.339*340* build_iSeries_Memory_Map gets information from the Hypervisor and341* looks at the Main Store VPD to determine the absolute addresses342* of the memory that has been assigned to our partition and builds343* a table used to translate Linux's physical addresses to these344* absolute addresses. Absolute addresses are needed when345* communicating with the hypervisor (e.g. to build HPT entries)346*347* Returns the physical memory size348*/349350static unsigned long __init build_iSeries_Memory_Map(void)351{352u32 loadAreaFirstChunk, loadAreaLastChunk, loadAreaSize;353u32 nextPhysChunk;354u32 hptFirstChunk, hptLastChunk, hptSizeChunks, hptSizePages;355u32 totalChunks,moreChunks;356u32 currChunk, thisChunk, absChunk;357u32 currDword;358u32 chunkBit;359u64 map;360struct MemoryBlock mb[32];361unsigned long numMemoryBlocks, curBlock;362363/* Chunk size on iSeries is 256K bytes */364totalChunks = (u32)HvLpConfig_getMsChunks();365mschunks_alloc(totalChunks);366367/*368* Get absolute address of our load area369* and map it to physical address 0370* This guarantees that the loadarea ends up at physical 0371* otherwise, it might not be returned by PLIC as the first372* chunks373*/374375loadAreaFirstChunk = (u32)addr_to_chunk(itLpNaca.xLoadAreaAddr);376loadAreaSize = itLpNaca.xLoadAreaChunks;377378/*379* Only add the pages already mapped here.380* Otherwise we might add the hpt pages381* The rest of the pages of the load area382* aren't in the HPT yet and can still383* be assigned an arbitrary physical address384*/385if ((loadAreaSize * 64) > HvPagesToMap)386loadAreaSize = HvPagesToMap / 64;387388loadAreaLastChunk = loadAreaFirstChunk + loadAreaSize - 1;389390/*391* TODO Do we need to do something if the HPT is in the 64MB load area?392* This would be required if the itLpNaca.xLoadAreaChunks includes393* the HPT size394*/395396printk("Mapping load area - physical addr = 0000000000000000\n"397" absolute addr = %016lx\n",398chunk_to_addr(loadAreaFirstChunk));399printk("Load area size %dK\n", loadAreaSize * 256);400401for (nextPhysChunk = 0; nextPhysChunk < loadAreaSize; ++nextPhysChunk)402mschunks_map.mapping[nextPhysChunk] =403loadAreaFirstChunk + nextPhysChunk;404405/*406* Get absolute address of our HPT and remember it so407* we won't map it to any physical address408*/409hptFirstChunk = (u32)addr_to_chunk(HvCallHpt_getHptAddress());410hptSizePages = (u32)HvCallHpt_getHptPages();411hptSizeChunks = hptSizePages >>412(MSCHUNKS_CHUNK_SHIFT - HW_PAGE_SHIFT);413hptLastChunk = hptFirstChunk + hptSizeChunks - 1;414415printk("HPT absolute addr = %016lx, size = %dK\n",416chunk_to_addr(hptFirstChunk), hptSizeChunks * 256);417418/*419* Determine if absolute memory has any420* holes so that we can interpret the421* access map we get back from the hypervisor422* correctly.423*/424numMemoryBlocks = iSeries_process_mainstore_vpd(mb, 32);425426/*427* Process the main store access map from the hypervisor428* to build up our physical -> absolute translation table429*/430curBlock = 0;431currChunk = 0;432currDword = 0;433moreChunks = totalChunks;434435while (moreChunks) {436map = HvCallSm_get64BitsOfAccessMap(itLpNaca.xLpIndex,437currDword);438thisChunk = currChunk;439while (map) {440chunkBit = map >> 63;441map <<= 1;442if (chunkBit) {443--moreChunks;444while (thisChunk >= mb[curBlock].logicalEnd) {445++curBlock;446if (curBlock >= numMemoryBlocks)447panic("out of memory blocks");448}449if (thisChunk < mb[curBlock].logicalStart)450panic("memory block error");451452absChunk = mb[curBlock].absStart +453(thisChunk - mb[curBlock].logicalStart);454if (((absChunk < hptFirstChunk) ||455(absChunk > hptLastChunk)) &&456((absChunk < loadAreaFirstChunk) ||457(absChunk > loadAreaLastChunk))) {458mschunks_map.mapping[nextPhysChunk] =459absChunk;460++nextPhysChunk;461}462}463++thisChunk;464}465++currDword;466currChunk += 64;467}468469/*470* main store size (in chunks) is471* totalChunks - hptSizeChunks472* which should be equal to473* nextPhysChunk474*/475return chunk_to_addr(nextPhysChunk);476}477478/*479* Document me.480*/481static void __init iSeries_setup_arch(void)482{483if (get_lppaca()->shared_proc) {484ppc_md.idle_loop = iseries_shared_idle;485printk(KERN_DEBUG "Using shared processor idle loop\n");486} else {487ppc_md.idle_loop = iseries_dedicated_idle;488printk(KERN_DEBUG "Using dedicated idle loop\n");489}490491/* Setup the Lp Event Queue */492setup_hvlpevent_queue();493494printk("Max logical processors = %d\n",495itVpdAreas.xSlicMaxLogicalProcs);496printk("Max physical processors = %d\n",497itVpdAreas.xSlicMaxPhysicalProcs);498499iSeries_pcibios_init();500}501502static void iSeries_show_cpuinfo(struct seq_file *m)503{504seq_printf(m, "machine\t\t: 64-bit iSeries Logical Partition\n");505}506507static void __init iSeries_progress(char * st, unsigned short code)508{509printk("Progress: [%04x] - %s\n", (unsigned)code, st);510mf_display_progress(code);511}512513static void __init iSeries_fixup_klimit(void)514{515/*516* Change klimit to take into account any ram disk517* that may be included518*/519if (naca.xRamDisk)520klimit = KERNELBASE + (u64)naca.xRamDisk +521(naca.xRamDiskSize * HW_PAGE_SIZE);522}523524static int __init iSeries_src_init(void)525{526/* clear the progress line */527if (firmware_has_feature(FW_FEATURE_ISERIES))528ppc_md.progress(" ", 0xffff);529return 0;530}531532late_initcall(iSeries_src_init);533534static inline void process_iSeries_events(void)535{536asm volatile ("li 0,0x5555; sc" : : : "r0", "r3");537}538539static void yield_shared_processor(void)540{541unsigned long tb;542543HvCall_setEnabledInterrupts(HvCall_MaskIPI |544HvCall_MaskLpEvent |545HvCall_MaskLpProd |546HvCall_MaskTimeout);547548tb = get_tb();549/* Compute future tb value when yield should expire */550HvCall_yieldProcessor(HvCall_YieldTimed, tb+tb_ticks_per_jiffy);551552/*553* The decrementer stops during the yield. Force a fake decrementer554* here and let the timer_interrupt code sort out the actual time.555*/556get_lppaca()->int_dword.fields.decr_int = 1;557ppc64_runlatch_on();558process_iSeries_events();559}560561static void iseries_shared_idle(void)562{563while (1) {564tick_nohz_stop_sched_tick(1);565while (!need_resched() && !hvlpevent_is_pending()) {566local_irq_disable();567ppc64_runlatch_off();568569/* Recheck with irqs off */570if (!need_resched() && !hvlpevent_is_pending())571yield_shared_processor();572573HMT_medium();574local_irq_enable();575}576577ppc64_runlatch_on();578tick_nohz_restart_sched_tick();579580if (hvlpevent_is_pending())581process_iSeries_events();582583preempt_enable_no_resched();584schedule();585preempt_disable();586}587}588589static void iseries_dedicated_idle(void)590{591set_thread_flag(TIF_POLLING_NRFLAG);592593while (1) {594tick_nohz_stop_sched_tick(1);595if (!need_resched()) {596while (!need_resched()) {597ppc64_runlatch_off();598HMT_low();599600if (hvlpevent_is_pending()) {601HMT_medium();602ppc64_runlatch_on();603process_iSeries_events();604}605}606607HMT_medium();608}609610ppc64_runlatch_on();611tick_nohz_restart_sched_tick();612preempt_enable_no_resched();613schedule();614preempt_disable();615}616}617618static void __iomem *iseries_ioremap(phys_addr_t address, unsigned long size,619unsigned long flags, void *caller)620{621return (void __iomem *)address;622}623624static void iseries_iounmap(volatile void __iomem *token)625{626}627628static int __init iseries_probe(void)629{630unsigned long root = of_get_flat_dt_root();631if (!of_flat_dt_is_compatible(root, "IBM,iSeries"))632return 0;633634hpte_init_iSeries();635/* iSeries does not support 16M pages */636cur_cpu_spec->mmu_features &= ~MMU_FTR_16M_PAGE;637638return 1;639}640641#ifdef CONFIG_KEXEC642static int iseries_kexec_prepare(struct kimage *image)643{644return -ENOSYS;645}646#endif647648define_machine(iseries) {649.name = "iSeries",650.setup_arch = iSeries_setup_arch,651.show_cpuinfo = iSeries_show_cpuinfo,652.init_IRQ = iSeries_init_IRQ,653.get_irq = iSeries_get_irq,654.init_early = iSeries_init_early,655.pcibios_fixup = iSeries_pci_final_fixup,656.pcibios_fixup_resources= iSeries_pcibios_fixup_resources,657.restart = mf_reboot,658.power_off = mf_power_off,659.halt = mf_power_off,660.get_boot_time = iSeries_get_boot_time,661.set_rtc_time = iSeries_set_rtc_time,662.get_rtc_time = iSeries_get_rtc_time,663.calibrate_decr = generic_calibrate_decr,664.progress = iSeries_progress,665.probe = iseries_probe,666.ioremap = iseries_ioremap,667.iounmap = iseries_iounmap,668#ifdef CONFIG_KEXEC669.machine_kexec_prepare = iseries_kexec_prepare,670#endif671/* XXX Implement enable_pmcs for iSeries */672};673674void * __init iSeries_early_setup(void)675{676unsigned long phys_mem_size;677678/* Identify CPU type. This is done again by the common code later679* on but calling this function multiple times is fine.680*/681identify_cpu(0, mfspr(SPRN_PVR));682initialise_paca(&boot_paca, 0);683684powerpc_firmware_features |= FW_FEATURE_ISERIES;685powerpc_firmware_features |= FW_FEATURE_LPAR;686687#ifdef CONFIG_SMP688/* On iSeries we know we can never have more than 64 cpus */689nr_cpu_ids = max(nr_cpu_ids, 64);690#endif691692iSeries_fixup_klimit();693694/*695* Initialize the table which translate Linux physical addresses to696* AS/400 absolute addresses697*/698phys_mem_size = build_iSeries_Memory_Map();699700iSeries_get_cmdline();701702return (void *) __pa(build_flat_dt(phys_mem_size));703}704705static void hvputc(char c)706{707if (c == '\n')708hvputc('\r');709710HvCall_writeLogBuffer(&c, 1);711}712713void __init udbg_init_iseries(void)714{715udbg_putc = hvputc;716}717718719