Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/powerpc/platforms/iseries/setup.c
10820 views
1
/*
2
* Copyright (c) 2000 Mike Corrigan <[email protected]>
3
* Copyright (c) 1999-2000 Grant Erickson <[email protected]>
4
*
5
* Description:
6
* Architecture- / platform-specific boot-time initialization code for
7
* the IBM iSeries LPAR. Adapted from original code by Grant Erickson and
8
* code by Gary Thomas, Cort Dougan <[email protected]>, and Dan Malek
9
* <[email protected]>.
10
*
11
* This program is free software; you can redistribute it and/or
12
* modify it under the terms of the GNU General Public License
13
* as published by the Free Software Foundation; either version
14
* 2 of the License, or (at your option) any later version.
15
*/
16
17
#undef DEBUG
18
19
#include <linux/init.h>
20
#include <linux/threads.h>
21
#include <linux/smp.h>
22
#include <linux/param.h>
23
#include <linux/string.h>
24
#include <linux/seq_file.h>
25
#include <linux/kdev_t.h>
26
#include <linux/kexec.h>
27
#include <linux/major.h>
28
#include <linux/root_dev.h>
29
#include <linux/kernel.h>
30
#include <linux/hrtimer.h>
31
#include <linux/tick.h>
32
33
#include <asm/processor.h>
34
#include <asm/machdep.h>
35
#include <asm/page.h>
36
#include <asm/mmu.h>
37
#include <asm/pgtable.h>
38
#include <asm/mmu_context.h>
39
#include <asm/cputable.h>
40
#include <asm/sections.h>
41
#include <asm/iommu.h>
42
#include <asm/firmware.h>
43
#include <asm/system.h>
44
#include <asm/time.h>
45
#include <asm/paca.h>
46
#include <asm/cache.h>
47
#include <asm/abs_addr.h>
48
#include <asm/iseries/hv_lp_config.h>
49
#include <asm/iseries/hv_call_event.h>
50
#include <asm/iseries/hv_call_xm.h>
51
#include <asm/iseries/it_lp_queue.h>
52
#include <asm/iseries/mf.h>
53
#include <asm/iseries/hv_lp_event.h>
54
#include <asm/iseries/lpar_map.h>
55
#include <asm/udbg.h>
56
#include <asm/irq.h>
57
58
#include "naca.h"
59
#include "setup.h"
60
#include "irq.h"
61
#include "vpd_areas.h"
62
#include "processor_vpd.h"
63
#include "it_lp_naca.h"
64
#include "main_store.h"
65
#include "call_sm.h"
66
#include "call_hpt.h"
67
#include "pci.h"
68
69
#ifdef DEBUG
70
#define DBG(fmt...) udbg_printf(fmt)
71
#else
72
#define DBG(fmt...)
73
#endif
74
75
/* Function Prototypes */
76
static unsigned long build_iSeries_Memory_Map(void);
77
static void iseries_shared_idle(void);
78
static void iseries_dedicated_idle(void);
79
80
81
struct MemoryBlock {
82
unsigned long absStart;
83
unsigned long absEnd;
84
unsigned long logicalStart;
85
unsigned long logicalEnd;
86
};
87
88
/*
89
* Process the main store vpd to determine where the holes in memory are
90
* and return the number of physical blocks and fill in the array of
91
* block data.
92
*/
93
static unsigned long iSeries_process_Condor_mainstore_vpd(
94
struct MemoryBlock *mb_array, unsigned long max_entries)
95
{
96
unsigned long holeFirstChunk, holeSizeChunks;
97
unsigned long numMemoryBlocks = 1;
98
struct IoHriMainStoreSegment4 *msVpd =
99
(struct IoHriMainStoreSegment4 *)xMsVpd;
100
unsigned long holeStart = msVpd->nonInterleavedBlocksStartAdr;
101
unsigned long holeEnd = msVpd->nonInterleavedBlocksEndAdr;
102
unsigned long holeSize = holeEnd - holeStart;
103
104
printk("Mainstore_VPD: Condor\n");
105
/*
106
* Determine if absolute memory has any
107
* holes so that we can interpret the
108
* access map we get back from the hypervisor
109
* correctly.
110
*/
111
mb_array[0].logicalStart = 0;
112
mb_array[0].logicalEnd = 0x100000000UL;
113
mb_array[0].absStart = 0;
114
mb_array[0].absEnd = 0x100000000UL;
115
116
if (holeSize) {
117
numMemoryBlocks = 2;
118
holeStart = holeStart & 0x000fffffffffffffUL;
119
holeStart = addr_to_chunk(holeStart);
120
holeFirstChunk = holeStart;
121
holeSize = addr_to_chunk(holeSize);
122
holeSizeChunks = holeSize;
123
printk( "Main store hole: start chunk = %0lx, size = %0lx chunks\n",
124
holeFirstChunk, holeSizeChunks );
125
mb_array[0].logicalEnd = holeFirstChunk;
126
mb_array[0].absEnd = holeFirstChunk;
127
mb_array[1].logicalStart = holeFirstChunk;
128
mb_array[1].logicalEnd = 0x100000000UL - holeSizeChunks;
129
mb_array[1].absStart = holeFirstChunk + holeSizeChunks;
130
mb_array[1].absEnd = 0x100000000UL;
131
}
132
return numMemoryBlocks;
133
}
134
135
#define MaxSegmentAreas 32
136
#define MaxSegmentAdrRangeBlocks 128
137
#define MaxAreaRangeBlocks 4
138
139
static unsigned long iSeries_process_Regatta_mainstore_vpd(
140
struct MemoryBlock *mb_array, unsigned long max_entries)
141
{
142
struct IoHriMainStoreSegment5 *msVpdP =
143
(struct IoHriMainStoreSegment5 *)xMsVpd;
144
unsigned long numSegmentBlocks = 0;
145
u32 existsBits = msVpdP->msAreaExists;
146
unsigned long area_num;
147
148
printk("Mainstore_VPD: Regatta\n");
149
150
for (area_num = 0; area_num < MaxSegmentAreas; ++area_num ) {
151
unsigned long numAreaBlocks;
152
struct IoHriMainStoreArea4 *currentArea;
153
154
if (existsBits & 0x80000000) {
155
unsigned long block_num;
156
157
currentArea = &msVpdP->msAreaArray[area_num];
158
numAreaBlocks = currentArea->numAdrRangeBlocks;
159
printk("ms_vpd: processing area %2ld blocks=%ld",
160
area_num, numAreaBlocks);
161
for (block_num = 0; block_num < numAreaBlocks;
162
++block_num ) {
163
/* Process an address range block */
164
struct MemoryBlock tempBlock;
165
unsigned long i;
166
167
tempBlock.absStart =
168
(unsigned long)currentArea->xAdrRangeBlock[block_num].blockStart;
169
tempBlock.absEnd =
170
(unsigned long)currentArea->xAdrRangeBlock[block_num].blockEnd;
171
tempBlock.logicalStart = 0;
172
tempBlock.logicalEnd = 0;
173
printk("\n block %ld absStart=%016lx absEnd=%016lx",
174
block_num, tempBlock.absStart,
175
tempBlock.absEnd);
176
177
for (i = 0; i < numSegmentBlocks; ++i) {
178
if (mb_array[i].absStart ==
179
tempBlock.absStart)
180
break;
181
}
182
if (i == numSegmentBlocks) {
183
if (numSegmentBlocks == max_entries)
184
panic("iSeries_process_mainstore_vpd: too many memory blocks");
185
mb_array[numSegmentBlocks] = tempBlock;
186
++numSegmentBlocks;
187
} else
188
printk(" (duplicate)");
189
}
190
printk("\n");
191
}
192
existsBits <<= 1;
193
}
194
/* Now sort the blocks found into ascending sequence */
195
if (numSegmentBlocks > 1) {
196
unsigned long m, n;
197
198
for (m = 0; m < numSegmentBlocks - 1; ++m) {
199
for (n = numSegmentBlocks - 1; m < n; --n) {
200
if (mb_array[n].absStart <
201
mb_array[n-1].absStart) {
202
struct MemoryBlock tempBlock;
203
204
tempBlock = mb_array[n];
205
mb_array[n] = mb_array[n-1];
206
mb_array[n-1] = tempBlock;
207
}
208
}
209
}
210
}
211
/*
212
* Assign "logical" addresses to each block. These
213
* addresses correspond to the hypervisor "bitmap" space.
214
* Convert all addresses into units of 256K chunks.
215
*/
216
{
217
unsigned long i, nextBitmapAddress;
218
219
printk("ms_vpd: %ld sorted memory blocks\n", numSegmentBlocks);
220
nextBitmapAddress = 0;
221
for (i = 0; i < numSegmentBlocks; ++i) {
222
unsigned long length = mb_array[i].absEnd -
223
mb_array[i].absStart;
224
225
mb_array[i].logicalStart = nextBitmapAddress;
226
mb_array[i].logicalEnd = nextBitmapAddress + length;
227
nextBitmapAddress += length;
228
printk(" Bitmap range: %016lx - %016lx\n"
229
" Absolute range: %016lx - %016lx\n",
230
mb_array[i].logicalStart,
231
mb_array[i].logicalEnd,
232
mb_array[i].absStart, mb_array[i].absEnd);
233
mb_array[i].absStart = addr_to_chunk(mb_array[i].absStart &
234
0x000fffffffffffffUL);
235
mb_array[i].absEnd = addr_to_chunk(mb_array[i].absEnd &
236
0x000fffffffffffffUL);
237
mb_array[i].logicalStart =
238
addr_to_chunk(mb_array[i].logicalStart);
239
mb_array[i].logicalEnd = addr_to_chunk(mb_array[i].logicalEnd);
240
}
241
}
242
243
return numSegmentBlocks;
244
}
245
246
static unsigned long iSeries_process_mainstore_vpd(struct MemoryBlock *mb_array,
247
unsigned long max_entries)
248
{
249
unsigned long i;
250
unsigned long mem_blocks = 0;
251
252
if (mmu_has_feature(MMU_FTR_SLB))
253
mem_blocks = iSeries_process_Regatta_mainstore_vpd(mb_array,
254
max_entries);
255
else
256
mem_blocks = iSeries_process_Condor_mainstore_vpd(mb_array,
257
max_entries);
258
259
printk("Mainstore_VPD: numMemoryBlocks = %ld\n", mem_blocks);
260
for (i = 0; i < mem_blocks; ++i) {
261
printk("Mainstore_VPD: block %3ld logical chunks %016lx - %016lx\n"
262
" abs chunks %016lx - %016lx\n",
263
i, mb_array[i].logicalStart, mb_array[i].logicalEnd,
264
mb_array[i].absStart, mb_array[i].absEnd);
265
}
266
return mem_blocks;
267
}
268
269
static void __init iSeries_get_cmdline(void)
270
{
271
char *p, *q;
272
273
/* copy the command line parameter from the primary VSP */
274
HvCallEvent_dmaToSp(cmd_line, 2 * 64* 1024, 256,
275
HvLpDma_Direction_RemoteToLocal);
276
277
p = cmd_line;
278
q = cmd_line + 255;
279
while(p < q) {
280
if (!*p || *p == '\n')
281
break;
282
++p;
283
}
284
*p = 0;
285
}
286
287
static void __init iSeries_init_early(void)
288
{
289
DBG(" -> iSeries_init_early()\n");
290
291
/* Snapshot the timebase, for use in later recalibration */
292
iSeries_time_init_early();
293
294
/*
295
* Initialize the DMA/TCE management
296
*/
297
iommu_init_early_iSeries();
298
299
/* Initialize machine-dependency vectors */
300
#ifdef CONFIG_SMP
301
smp_init_iSeries();
302
#endif
303
304
/* Associate Lp Event Queue 0 with processor 0 */
305
HvCallEvent_setLpEventQueueInterruptProc(0, 0);
306
307
mf_init();
308
309
DBG(" <- iSeries_init_early()\n");
310
}
311
312
struct mschunks_map mschunks_map = {
313
/* XXX We don't use these, but Piranha might need them. */
314
.chunk_size = MSCHUNKS_CHUNK_SIZE,
315
.chunk_shift = MSCHUNKS_CHUNK_SHIFT,
316
.chunk_mask = MSCHUNKS_OFFSET_MASK,
317
};
318
EXPORT_SYMBOL(mschunks_map);
319
320
static void mschunks_alloc(unsigned long num_chunks)
321
{
322
klimit = _ALIGN(klimit, sizeof(u32));
323
mschunks_map.mapping = (u32 *)klimit;
324
klimit += num_chunks * sizeof(u32);
325
mschunks_map.num_chunks = num_chunks;
326
}
327
328
/*
329
* The iSeries may have very large memories ( > 128 GB ) and a partition
330
* may get memory in "chunks" that may be anywhere in the 2**52 real
331
* address space. The chunks are 256K in size. To map this to the
332
* memory model Linux expects, the AS/400 specific code builds a
333
* translation table to translate what Linux thinks are "physical"
334
* addresses to the actual real addresses. This allows us to make
335
* it appear to Linux that we have contiguous memory starting at
336
* physical address zero while in fact this could be far from the truth.
337
* To avoid confusion, I'll let the words physical and/or real address
338
* apply to the Linux addresses while I'll use "absolute address" to
339
* refer to the actual hardware real address.
340
*
341
* build_iSeries_Memory_Map gets information from the Hypervisor and
342
* looks at the Main Store VPD to determine the absolute addresses
343
* of the memory that has been assigned to our partition and builds
344
* a table used to translate Linux's physical addresses to these
345
* absolute addresses. Absolute addresses are needed when
346
* communicating with the hypervisor (e.g. to build HPT entries)
347
*
348
* Returns the physical memory size
349
*/
350
351
static unsigned long __init build_iSeries_Memory_Map(void)
352
{
353
u32 loadAreaFirstChunk, loadAreaLastChunk, loadAreaSize;
354
u32 nextPhysChunk;
355
u32 hptFirstChunk, hptLastChunk, hptSizeChunks, hptSizePages;
356
u32 totalChunks,moreChunks;
357
u32 currChunk, thisChunk, absChunk;
358
u32 currDword;
359
u32 chunkBit;
360
u64 map;
361
struct MemoryBlock mb[32];
362
unsigned long numMemoryBlocks, curBlock;
363
364
/* Chunk size on iSeries is 256K bytes */
365
totalChunks = (u32)HvLpConfig_getMsChunks();
366
mschunks_alloc(totalChunks);
367
368
/*
369
* Get absolute address of our load area
370
* and map it to physical address 0
371
* This guarantees that the loadarea ends up at physical 0
372
* otherwise, it might not be returned by PLIC as the first
373
* chunks
374
*/
375
376
loadAreaFirstChunk = (u32)addr_to_chunk(itLpNaca.xLoadAreaAddr);
377
loadAreaSize = itLpNaca.xLoadAreaChunks;
378
379
/*
380
* Only add the pages already mapped here.
381
* Otherwise we might add the hpt pages
382
* The rest of the pages of the load area
383
* aren't in the HPT yet and can still
384
* be assigned an arbitrary physical address
385
*/
386
if ((loadAreaSize * 64) > HvPagesToMap)
387
loadAreaSize = HvPagesToMap / 64;
388
389
loadAreaLastChunk = loadAreaFirstChunk + loadAreaSize - 1;
390
391
/*
392
* TODO Do we need to do something if the HPT is in the 64MB load area?
393
* This would be required if the itLpNaca.xLoadAreaChunks includes
394
* the HPT size
395
*/
396
397
printk("Mapping load area - physical addr = 0000000000000000\n"
398
" absolute addr = %016lx\n",
399
chunk_to_addr(loadAreaFirstChunk));
400
printk("Load area size %dK\n", loadAreaSize * 256);
401
402
for (nextPhysChunk = 0; nextPhysChunk < loadAreaSize; ++nextPhysChunk)
403
mschunks_map.mapping[nextPhysChunk] =
404
loadAreaFirstChunk + nextPhysChunk;
405
406
/*
407
* Get absolute address of our HPT and remember it so
408
* we won't map it to any physical address
409
*/
410
hptFirstChunk = (u32)addr_to_chunk(HvCallHpt_getHptAddress());
411
hptSizePages = (u32)HvCallHpt_getHptPages();
412
hptSizeChunks = hptSizePages >>
413
(MSCHUNKS_CHUNK_SHIFT - HW_PAGE_SHIFT);
414
hptLastChunk = hptFirstChunk + hptSizeChunks - 1;
415
416
printk("HPT absolute addr = %016lx, size = %dK\n",
417
chunk_to_addr(hptFirstChunk), hptSizeChunks * 256);
418
419
/*
420
* Determine if absolute memory has any
421
* holes so that we can interpret the
422
* access map we get back from the hypervisor
423
* correctly.
424
*/
425
numMemoryBlocks = iSeries_process_mainstore_vpd(mb, 32);
426
427
/*
428
* Process the main store access map from the hypervisor
429
* to build up our physical -> absolute translation table
430
*/
431
curBlock = 0;
432
currChunk = 0;
433
currDword = 0;
434
moreChunks = totalChunks;
435
436
while (moreChunks) {
437
map = HvCallSm_get64BitsOfAccessMap(itLpNaca.xLpIndex,
438
currDword);
439
thisChunk = currChunk;
440
while (map) {
441
chunkBit = map >> 63;
442
map <<= 1;
443
if (chunkBit) {
444
--moreChunks;
445
while (thisChunk >= mb[curBlock].logicalEnd) {
446
++curBlock;
447
if (curBlock >= numMemoryBlocks)
448
panic("out of memory blocks");
449
}
450
if (thisChunk < mb[curBlock].logicalStart)
451
panic("memory block error");
452
453
absChunk = mb[curBlock].absStart +
454
(thisChunk - mb[curBlock].logicalStart);
455
if (((absChunk < hptFirstChunk) ||
456
(absChunk > hptLastChunk)) &&
457
((absChunk < loadAreaFirstChunk) ||
458
(absChunk > loadAreaLastChunk))) {
459
mschunks_map.mapping[nextPhysChunk] =
460
absChunk;
461
++nextPhysChunk;
462
}
463
}
464
++thisChunk;
465
}
466
++currDword;
467
currChunk += 64;
468
}
469
470
/*
471
* main store size (in chunks) is
472
* totalChunks - hptSizeChunks
473
* which should be equal to
474
* nextPhysChunk
475
*/
476
return chunk_to_addr(nextPhysChunk);
477
}
478
479
/*
480
* Document me.
481
*/
482
static void __init iSeries_setup_arch(void)
483
{
484
if (get_lppaca()->shared_proc) {
485
ppc_md.idle_loop = iseries_shared_idle;
486
printk(KERN_DEBUG "Using shared processor idle loop\n");
487
} else {
488
ppc_md.idle_loop = iseries_dedicated_idle;
489
printk(KERN_DEBUG "Using dedicated idle loop\n");
490
}
491
492
/* Setup the Lp Event Queue */
493
setup_hvlpevent_queue();
494
495
printk("Max logical processors = %d\n",
496
itVpdAreas.xSlicMaxLogicalProcs);
497
printk("Max physical processors = %d\n",
498
itVpdAreas.xSlicMaxPhysicalProcs);
499
500
iSeries_pcibios_init();
501
}
502
503
static void iSeries_show_cpuinfo(struct seq_file *m)
504
{
505
seq_printf(m, "machine\t\t: 64-bit iSeries Logical Partition\n");
506
}
507
508
static void __init iSeries_progress(char * st, unsigned short code)
509
{
510
printk("Progress: [%04x] - %s\n", (unsigned)code, st);
511
mf_display_progress(code);
512
}
513
514
static void __init iSeries_fixup_klimit(void)
515
{
516
/*
517
* Change klimit to take into account any ram disk
518
* that may be included
519
*/
520
if (naca.xRamDisk)
521
klimit = KERNELBASE + (u64)naca.xRamDisk +
522
(naca.xRamDiskSize * HW_PAGE_SIZE);
523
}
524
525
static int __init iSeries_src_init(void)
526
{
527
/* clear the progress line */
528
if (firmware_has_feature(FW_FEATURE_ISERIES))
529
ppc_md.progress(" ", 0xffff);
530
return 0;
531
}
532
533
late_initcall(iSeries_src_init);
534
535
static inline void process_iSeries_events(void)
536
{
537
asm volatile ("li 0,0x5555; sc" : : : "r0", "r3");
538
}
539
540
static void yield_shared_processor(void)
541
{
542
unsigned long tb;
543
544
HvCall_setEnabledInterrupts(HvCall_MaskIPI |
545
HvCall_MaskLpEvent |
546
HvCall_MaskLpProd |
547
HvCall_MaskTimeout);
548
549
tb = get_tb();
550
/* Compute future tb value when yield should expire */
551
HvCall_yieldProcessor(HvCall_YieldTimed, tb+tb_ticks_per_jiffy);
552
553
/*
554
* The decrementer stops during the yield. Force a fake decrementer
555
* here and let the timer_interrupt code sort out the actual time.
556
*/
557
get_lppaca()->int_dword.fields.decr_int = 1;
558
ppc64_runlatch_on();
559
process_iSeries_events();
560
}
561
562
static void iseries_shared_idle(void)
563
{
564
while (1) {
565
tick_nohz_stop_sched_tick(1);
566
while (!need_resched() && !hvlpevent_is_pending()) {
567
local_irq_disable();
568
ppc64_runlatch_off();
569
570
/* Recheck with irqs off */
571
if (!need_resched() && !hvlpevent_is_pending())
572
yield_shared_processor();
573
574
HMT_medium();
575
local_irq_enable();
576
}
577
578
ppc64_runlatch_on();
579
tick_nohz_restart_sched_tick();
580
581
if (hvlpevent_is_pending())
582
process_iSeries_events();
583
584
preempt_enable_no_resched();
585
schedule();
586
preempt_disable();
587
}
588
}
589
590
static void iseries_dedicated_idle(void)
591
{
592
set_thread_flag(TIF_POLLING_NRFLAG);
593
594
while (1) {
595
tick_nohz_stop_sched_tick(1);
596
if (!need_resched()) {
597
while (!need_resched()) {
598
ppc64_runlatch_off();
599
HMT_low();
600
601
if (hvlpevent_is_pending()) {
602
HMT_medium();
603
ppc64_runlatch_on();
604
process_iSeries_events();
605
}
606
}
607
608
HMT_medium();
609
}
610
611
ppc64_runlatch_on();
612
tick_nohz_restart_sched_tick();
613
preempt_enable_no_resched();
614
schedule();
615
preempt_disable();
616
}
617
}
618
619
static void __iomem *iseries_ioremap(phys_addr_t address, unsigned long size,
620
unsigned long flags, void *caller)
621
{
622
return (void __iomem *)address;
623
}
624
625
static void iseries_iounmap(volatile void __iomem *token)
626
{
627
}
628
629
static int __init iseries_probe(void)
630
{
631
unsigned long root = of_get_flat_dt_root();
632
if (!of_flat_dt_is_compatible(root, "IBM,iSeries"))
633
return 0;
634
635
hpte_init_iSeries();
636
/* iSeries does not support 16M pages */
637
cur_cpu_spec->mmu_features &= ~MMU_FTR_16M_PAGE;
638
639
return 1;
640
}
641
642
#ifdef CONFIG_KEXEC
643
static int iseries_kexec_prepare(struct kimage *image)
644
{
645
return -ENOSYS;
646
}
647
#endif
648
649
define_machine(iseries) {
650
.name = "iSeries",
651
.setup_arch = iSeries_setup_arch,
652
.show_cpuinfo = iSeries_show_cpuinfo,
653
.init_IRQ = iSeries_init_IRQ,
654
.get_irq = iSeries_get_irq,
655
.init_early = iSeries_init_early,
656
.pcibios_fixup = iSeries_pci_final_fixup,
657
.pcibios_fixup_resources= iSeries_pcibios_fixup_resources,
658
.restart = mf_reboot,
659
.power_off = mf_power_off,
660
.halt = mf_power_off,
661
.get_boot_time = iSeries_get_boot_time,
662
.set_rtc_time = iSeries_set_rtc_time,
663
.get_rtc_time = iSeries_get_rtc_time,
664
.calibrate_decr = generic_calibrate_decr,
665
.progress = iSeries_progress,
666
.probe = iseries_probe,
667
.ioremap = iseries_ioremap,
668
.iounmap = iseries_iounmap,
669
#ifdef CONFIG_KEXEC
670
.machine_kexec_prepare = iseries_kexec_prepare,
671
#endif
672
/* XXX Implement enable_pmcs for iSeries */
673
};
674
675
void * __init iSeries_early_setup(void)
676
{
677
unsigned long phys_mem_size;
678
679
/* Identify CPU type. This is done again by the common code later
680
* on but calling this function multiple times is fine.
681
*/
682
identify_cpu(0, mfspr(SPRN_PVR));
683
initialise_paca(&boot_paca, 0);
684
685
powerpc_firmware_features |= FW_FEATURE_ISERIES;
686
powerpc_firmware_features |= FW_FEATURE_LPAR;
687
688
#ifdef CONFIG_SMP
689
/* On iSeries we know we can never have more than 64 cpus */
690
nr_cpu_ids = max(nr_cpu_ids, 64);
691
#endif
692
693
iSeries_fixup_klimit();
694
695
/*
696
* Initialize the table which translate Linux physical addresses to
697
* AS/400 absolute addresses
698
*/
699
phys_mem_size = build_iSeries_Memory_Map();
700
701
iSeries_get_cmdline();
702
703
return (void *) __pa(build_flat_dt(phys_mem_size));
704
}
705
706
static void hvputc(char c)
707
{
708
if (c == '\n')
709
hvputc('\r');
710
711
HvCall_writeLogBuffer(&c, 1);
712
}
713
714
void __init udbg_init_iseries(void)
715
{
716
udbg_putc = hvputc;
717
}
718
719