Free Electrons

Embedded Linux Experts

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
/*
 *  linux/arch/m32r/kernel/smpboot.c
 *    orig : i386 2.4.10
 *
 *  M32R SMP booting functions
 *
 *  Copyright (c) 2001, 2002, 2003  Hitoshi Yamamoto
 *
 *  Taken from i386 version.
 *	  (c) 1995 Alan Cox, Building #3 <alan@redhat.com>
 *	  (c) 1998, 1999, 2000 Ingo Molnar <mingo@redhat.com>
 *
 *	Much of the core SMP work is based on previous work by Thomas Radke, to
 *	whom a great many thanks are extended.
 *
 *	Thanks to Intel for making available several different Pentium,
 *	Pentium Pro and Pentium-II/Xeon MP machines.
 *	Original development of Linux SMP code supported by Caldera.
 *
 *	This code is released under the GNU General Public License version 2 or
 *	later.
 *
 *	Fixes
 *		Felix Koop	:	NR_CPUS used properly
 *		Jose Renau	:	Handle single CPU case.
 *		Alan Cox	:	By repeated request
 *					8) - Total BogoMIP report.
 *		Greg Wright	:	Fix for kernel stacks panic.
 *		Erich Boleyn	:	MP v1.4 and additional changes.
 *	Matthias Sattler	:	Changes for 2.1 kernel map.
 *	Michel Lespinasse	:	Changes for 2.1 kernel map.
 *	Michael Chastain	:	Change trampoline.S to gnu as.
 *		Alan Cox	:	Dumb bug: 'B' step PPro's are fine
 *		Ingo Molnar	:	Added APIC timers, based on code
 *					from Jose Renau
 *		Ingo Molnar	:	various cleanups and rewrites
 *		Tigran Aivazian	:	fixed "0.00 in /proc/uptime on SMP" bug.
 *	Maciej W. Rozycki	:	Bits for genuine 82489DX APICs
 *		Martin J. Bligh	: 	Added support for multi-quad systems
 */

#include <linux/module.h>
#include <linux/cpu.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/err.h>
#include <linux/irq.h>
#include <linux/bootmem.h>
#include <linux/delay.h>

#include <asm/io.h>
#include <asm/pgalloc.h>
#include <asm/tlbflush.h>

#define DEBUG_SMP
#ifdef DEBUG_SMP
#define Dprintk(x...) printk(x)
#else
#define Dprintk(x...)
#endif

extern cpumask_t cpu_initialized;

/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/
/* Data structures and variables                                             */
/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/

/* Processor that is doing the boot up */
static unsigned int bsp_phys_id = -1;

/* Bitmask of physically existing CPUs */
physid_mask_t phys_cpu_present_map;

cpumask_t cpu_bootout_map;
cpumask_t cpu_bootin_map;
static cpumask_t cpu_callin_map;
cpumask_t cpu_callout_map;
EXPORT_SYMBOL(cpu_callout_map);

/* Per CPU bogomips and other parameters */
struct cpuinfo_m32r cpu_data[NR_CPUS] __cacheline_aligned;

static int cpucount;
static cpumask_t smp_commenced_mask;

extern struct {
	void * spi;
	unsigned short ss;
} stack_start;

/* which physical physical ID maps to which logical CPU number */
static volatile int physid_2_cpu[NR_CPUS];
#define physid_to_cpu(physid)	physid_2_cpu[physid]

/* which logical CPU number maps to which physical ID */
volatile int cpu_2_physid[NR_CPUS];

DEFINE_PER_CPU(int, prof_multiplier) = 1;
DEFINE_PER_CPU(int, prof_old_multiplier) = 1;
DEFINE_PER_CPU(int, prof_counter) = 1;

spinlock_t ipi_lock[NR_IPIS];

static unsigned int calibration_result;

/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/
/* Function Prototypes                                                       */
/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/

void smp_prepare_boot_cpu(void);
void smp_prepare_cpus(unsigned int);
static void init_ipi_lock(void);
static void do_boot_cpu(int);
int __cpu_up(unsigned int);
void smp_cpus_done(unsigned int);

int start_secondary(void *);
static void smp_callin(void);
static void smp_online(void);

static void show_mp_info(int);
static void smp_store_cpu_info(int);
static void show_cpu_info(int);
int setup_profiling_timer(unsigned int);
static void init_cpu_to_physid(void);
static void map_cpu_to_physid(int, int);
static void unmap_cpu_to_physid(int, int);

/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/
/* Boot up APs Routines : BSP                                                */
/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/
void __devinit smp_prepare_boot_cpu(void)
{
	bsp_phys_id = hard_smp_processor_id();
	physid_set(bsp_phys_id, phys_cpu_present_map);
	set_cpu_online(0, true);	/* BSP's cpu_id == 0 */
	cpumask_set_cpu(0, &cpu_callout_map);
	cpumask_set_cpu(0, &cpu_callin_map);

	/*
	 * Initialize the logical to physical CPU number mapping
	 */
	init_cpu_to_physid();
	map_cpu_to_physid(0, bsp_phys_id);
	current_thread_info()->cpu = 0;
}

/*==========================================================================*
 * Name:         smp_prepare_cpus (old smp_boot_cpus)
 *
 * Description:  This routine boot up APs.
 *
 * Born on Date: 2002.02.05
 *
 * Arguments:    NONE
 *
 * Returns:      void (cannot fail)
 *
 * Modification log:
 * Date       Who Description
 * ---------- --- --------------------------------------------------------
 * 2003-06-24 hy  modify for linux-2.5.69
 *
 *==========================================================================*/
void __init smp_prepare_cpus(unsigned int max_cpus)
{
	int phys_id;
	unsigned long nr_cpu;

	nr_cpu = inl(M32R_FPGA_NUM_OF_CPUS_PORTL);
	if (nr_cpu > NR_CPUS) {
		printk(KERN_INFO "NUM_OF_CPUS reg. value [%ld] > NR_CPU [%d]",
			nr_cpu, NR_CPUS);
		goto smp_done;
	}
	for (phys_id = 0 ; phys_id < nr_cpu ; phys_id++)
		physid_set(phys_id, phys_cpu_present_map);
#ifndef CONFIG_HOTPLUG_CPU
	init_cpu_present(cpu_possible_mask);
#endif

	show_mp_info(nr_cpu);

	init_ipi_lock();

	/*
	 * Setup boot CPU information
	 */
	smp_store_cpu_info(0); /* Final full version of the data */

	/*
	 * If SMP should be disabled, then really disable it!
	 */
	if (!max_cpus) {
		printk(KERN_INFO "SMP mode deactivated by commandline.\n");
		goto smp_done;
	}

	/*
	 * Now scan the CPU present map and fire up the other CPUs.
	 */
	Dprintk("CPU present map : %lx\n", physids_coerce(phys_cpu_present_map));

	for (phys_id = 0 ; phys_id < NR_CPUS ; phys_id++) {
		/*
		 * Don't even attempt to start the boot CPU!
		 */
		if (phys_id == bsp_phys_id)
			continue;

		if (!physid_isset(phys_id, phys_cpu_present_map))
			continue;

		if (max_cpus <= cpucount + 1)
			continue;

		do_boot_cpu(phys_id);

		/*
		 * Make sure we unmap all failed CPUs
		 */
		if (physid_to_cpu(phys_id) == -1) {
			physid_clear(phys_id, phys_cpu_present_map);
			printk("phys CPU#%d not responding - " \
				"cannot use it.\n", phys_id);
		}
	}

smp_done:
	Dprintk("Boot done.\n");
}

/*
 * init_ipi_lock : Initialize IPI locks.
 */
static void __init init_ipi_lock(void)
{
	int ipi;

	for (ipi = 0 ; ipi < NR_IPIS ; ipi++)
		spin_lock_init(&ipi_lock[ipi]);
}

/*==========================================================================*
 * Name:         do_boot_cpu
 *
 * Description:  This routine boot up one AP.
 *
 * Born on Date: 2002.02.05
 *
 * Arguments:    phys_id - Target CPU physical ID
 *
 * Returns:      void (cannot fail)
 *
 * Modification log:
 * Date       Who Description
 * ---------- --- --------------------------------------------------------
 * 2003-06-24 hy  modify for linux-2.5.69
 *
 *==========================================================================*/
static void __init do_boot_cpu(int phys_id)
{
	struct task_struct *idle;
	unsigned long send_status, boot_status;
	int timeout, cpu_id;

	cpu_id = ++cpucount;

	/*
	 * We can't use kernel_thread since we must avoid to
	 * reschedule the child.
	 */
	idle = fork_idle(cpu_id);
	if (IS_ERR(idle))
		panic("failed fork for CPU#%d.", cpu_id);

	idle->thread.lr = (unsigned long)start_secondary;

	map_cpu_to_physid(cpu_id, phys_id);

	/* So we see what's up   */
	printk("Booting processor %d/%d\n", phys_id, cpu_id);
	stack_start.spi = (void *)idle->thread.sp;
	task_thread_info(idle)->cpu = cpu_id;

	/*
	 * Send Startup IPI
	 *   1.IPI received by CPU#(phys_id).
	 *   2.CPU#(phys_id) enter startup_AP (arch/m32r/kernel/head.S)
	 *   3.CPU#(phys_id) enter start_secondary()
	 */
	send_status = 0;
	boot_status = 0;

	cpumask_set_cpu(phys_id, &cpu_bootout_map);

	/* Send Startup IPI */
	send_IPI_mask_phys(cpumask_of(phys_id), CPU_BOOT_IPI, 0);

	Dprintk("Waiting for send to finish...\n");
	timeout = 0;

	/* Wait 100[ms] */
	do {
		Dprintk("+");
		udelay(1000);
		send_status = !cpumask_test_cpu(phys_id, &cpu_bootin_map);
	} while (send_status && (timeout++ < 100));

	Dprintk("After Startup.\n");

	if (!send_status) {
		/*
		 * allow APs to start initializing.
		 */
		Dprintk("Before Callout %d.\n", cpu_id);
		cpumask_set_cpu(cpu_id, &cpu_callout_map);
		Dprintk("After Callout %d.\n", cpu_id);

		/*
		 * Wait 5s total for a response
		 */
		for (timeout = 0; timeout < 5000; timeout++) {
			if (cpumask_test_cpu(cpu_id, &cpu_callin_map))
				break;	/* It has booted */
			udelay(1000);
		}

		if (cpumask_test_cpu(cpu_id, &cpu_callin_map)) {
			/* number CPUs logically, starting from 1 (BSP is 0) */
			Dprintk("OK.\n");
		} else {
			boot_status = 1;
			printk("Not responding.\n");
		}
	} else
		printk("IPI never delivered???\n");

	if (send_status || boot_status) {
		unmap_cpu_to_physid(cpu_id, phys_id);
		cpumask_clear_cpu(cpu_id, &cpu_callout_map);
		cpumask_clear_cpu(cpu_id, &cpu_callin_map);
		cpumask_clear_cpu(cpu_id, &cpu_initialized);
		cpucount--;
	}
}

int __cpuinit __cpu_up(unsigned int cpu_id)
{
	int timeout;

	cpumask_set_cpu(cpu_id, &smp_commenced_mask);

	/*
	 * Wait 5s total for a response
	 */
	for (timeout = 0; timeout < 5000; timeout++) {
		if (cpu_online(cpu_id))
			break;
		udelay(1000);
	}
	if (!cpu_online(cpu_id))
		BUG();

	return 0;
}

void __init smp_cpus_done(unsigned int max_cpus)
{
	int cpu_id, timeout;
	unsigned long bogosum = 0;

	for (timeout = 0; timeout < 5000; timeout++) {
		if (cpumask_equal(&cpu_callin_map, cpu_online_mask))
			break;
		udelay(1000);
	}
	if (!cpumask_equal(&cpu_callin_map, cpu_online_mask))
		BUG();

	for (cpu_id = 0 ; cpu_id < num_online_cpus() ; cpu_id++)
		show_cpu_info(cpu_id);

	/*
	 * Allow the user to impress friends.
	 */
	Dprintk("Before bogomips.\n");
	if (cpucount) {
		for_each_cpu(cpu_id,cpu_online_mask)
			bogosum += cpu_data[cpu_id].loops_per_jiffy;

		printk(KERN_INFO "Total of %d processors activated " \
			"(%lu.%02lu BogoMIPS).\n", cpucount + 1,
			bogosum / (500000 / HZ),
			(bogosum / (5000 / HZ)) % 100);
		Dprintk("Before bogocount - setting activated=1.\n");
	}
}

/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/
/* Activate a secondary processor Routines                                   */
/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/

/*==========================================================================*
 * Name:         start_secondary
 *
 * Description:  This routine activate a secondary processor.
 *
 * Born on Date: 2002.02.05
 *
 * Arguments:    *unused - currently unused.
 *
 * Returns:      void (cannot fail)
 *
 * Modification log:
 * Date       Who Description
 * ---------- --- --------------------------------------------------------
 * 2003-06-24 hy  modify for linux-2.5.69
 *
 *==========================================================================*/
int __init start_secondary(void *unused)
{
	cpu_init();
	preempt_disable();
	smp_callin();
	while (!cpumask_test_cpu(smp_processor_id(), &smp_commenced_mask))
		cpu_relax();

	smp_online();

	/*
	 * low-memory mappings have been cleared, flush them from
	 * the local TLBs too.
	 */
	local_flush_tlb_all();

	cpu_idle();
	return 0;
}

/*==========================================================================*
 * Name:         smp_callin
 *
 * Description:  This routine activate a secondary processor.
 *
 * Born on Date: 2002.02.05
 *
 * Arguments:    NONE
 *
 * Returns:      void (cannot fail)
 *
 * Modification log:
 * Date       Who Description
 * ---------- --- --------------------------------------------------------
 * 2003-06-24 hy  modify for linux-2.5.69
 *
 *==========================================================================*/
static void __init smp_callin(void)
{
	int phys_id = hard_smp_processor_id();
	int cpu_id = smp_processor_id();
	unsigned long timeout;

	if (cpumask_test_cpu(cpu_id, &cpu_callin_map)) {
		printk("huh, phys CPU#%d, CPU#%d already present??\n",
			phys_id, cpu_id);
		BUG();
	}
	Dprintk("CPU#%d (phys ID: %d) waiting for CALLOUT\n", cpu_id, phys_id);

	/* Waiting 2s total for startup (udelay is not yet working) */
	timeout = jiffies + (2 * HZ);
	while (time_before(jiffies, timeout)) {
		/* Has the boot CPU finished it's STARTUP sequence ? */
		if (cpumask_test_cpu(cpu_id, &cpu_callout_map))
			break;
		cpu_relax();
	}

	if (!time_before(jiffies, timeout)) {
		printk("BUG: CPU#%d started up but did not get a callout!\n",
			cpu_id);
		BUG();
	}

	/* Allow the master to continue. */
	cpumask_set_cpu(cpu_id, &cpu_callin_map);
}

static void __init smp_online(void)
{
	int cpu_id = smp_processor_id();

	notify_cpu_starting(cpu_id);

	local_irq_enable();

	/* Get our bogomips. */
	calibrate_delay();

	/* Save our processor parameters */
 	smp_store_cpu_info(cpu_id);

	set_cpu_online(cpu_id, true);
}

/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/
/* Boot up CPUs common Routines                                              */
/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/
static void __init show_mp_info(int nr_cpu)
{
	int i;
	char cpu_model0[17], cpu_model1[17], cpu_ver[9];

	strncpy(cpu_model0, (char *)M32R_FPGA_CPU_NAME_ADDR, 16);
	strncpy(cpu_model1, (char *)M32R_FPGA_MODEL_ID_ADDR, 16);
	strncpy(cpu_ver, (char *)M32R_FPGA_VERSION_ADDR, 8);

	cpu_model0[16] = '\0';
	for (i = 15 ; i >= 0 ; i--) {
		if (cpu_model0[i] != ' ')
			break;
		cpu_model0[i] = '\0';
	}
	cpu_model1[16] = '\0';
	for (i = 15 ; i >= 0 ; i--) {
		if (cpu_model1[i] != ' ')
			break;
		cpu_model1[i] = '\0';
	}
	cpu_ver[8] = '\0';
	for (i = 7 ; i >= 0 ; i--) {
		if (cpu_ver[i] != ' ')
			break;
		cpu_ver[i] = '\0';
	}

	printk(KERN_INFO "M32R-mp information\n");
	printk(KERN_INFO "  On-chip CPUs : %d\n", nr_cpu);
	printk(KERN_INFO "  CPU model : %s/%s(%s)\n", cpu_model0,
		cpu_model1, cpu_ver);
}

/*
 * The bootstrap kernel entry code has set these up. Save them for
 * a given CPU
 */
static void __init smp_store_cpu_info(int cpu_id)
{
	struct cpuinfo_m32r *ci = cpu_data + cpu_id;

	*ci = boot_cpu_data;
	ci->loops_per_jiffy = loops_per_jiffy;
}

static void __init show_cpu_info(int cpu_id)
{
	struct cpuinfo_m32r *ci = &cpu_data[cpu_id];

	printk("CPU#%d : ", cpu_id);

#define PRINT_CLOCK(name, value) \
	printk(name " clock %d.%02dMHz", \
		((value) / 1000000), ((value) % 1000000) / 10000)

	PRINT_CLOCK("CPU", (int)ci->cpu_clock);
	PRINT_CLOCK(", Bus", (int)ci->bus_clock);
	printk(", loops_per_jiffy[%ld]\n", ci->loops_per_jiffy);
}

/*
 * the frequency of the profiling timer can be changed
 * by writing a multiplier value into /proc/profile.
 */
int setup_profiling_timer(unsigned int multiplier)
{
	int i;

	/*
	 * Sanity check. [at least 500 APIC cycles should be
	 * between APIC interrupts as a rule of thumb, to avoid
	 * irqs flooding us]
	 */
	if ( (!multiplier) || (calibration_result / multiplier < 500))
		return -EINVAL;

	/*
	 * Set the new multiplier for each CPU. CPUs don't start using the
	 * new values until the next timer interrupt in which they do process
	 * accounting. At that time they also adjust their APIC timers
	 * accordingly.
	 */
	for_each_possible_cpu(i)
		per_cpu(prof_multiplier, i) = multiplier;

	return 0;
}

/* Initialize all maps between cpu number and apicids */
static void __init init_cpu_to_physid(void)
{
	int  i;

	for (i = 0 ; i < NR_CPUS ; i++) {
		cpu_2_physid[i] = -1;
		physid_2_cpu[i] = -1;
	}
}

/*
 * set up a mapping between cpu and apicid. Uses logical apicids for multiquad,
 * else physical apic ids
 */
static void __init map_cpu_to_physid(int cpu_id, int phys_id)
{
	physid_2_cpu[phys_id] = cpu_id;
	cpu_2_physid[cpu_id] = phys_id;
}

/*
 * undo a mapping between cpu and apicid. Uses logical apicids for multiquad,
 * else physical apic ids
 */
static void __init unmap_cpu_to_physid(int cpu_id, int phys_id)
{
	physid_2_cpu[phys_id] = -1;
	cpu_2_physid[cpu_id] = -1;
}