setup.c 14.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/*
 * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/seq_file.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/root_dev.h>
#include <linux/console.h>
#include <linux/module.h>
#include <linux/cpu.h>
16
#include <linux/clk-provider.h>
V
Vineet Gupta 已提交
17
#include <linux/of_fdt.h>
18
#include <linux/of_platform.h>
19
#include <linux/cache.h>
V
Vineet Gupta 已提交
20
#include <asm/sections.h>
21 22 23 24 25
#include <asm/arcregs.h>
#include <asm/tlb.h>
#include <asm/setup.h>
#include <asm/page.h>
#include <asm/irq.h>
26
#include <asm/unwind.h>
27
#include <asm/clk.h>
28
#include <asm/mach_desc.h>
29
#include <asm/smp.h>
30 31 32

#define FIX_PTR(x)  __asm__ __volatile__(";" : "+r"(x))

33 34
unsigned int intr_to_DE_cnt;

35 36 37 38
/* Part of U-boot ABI: see head.S */
int __initdata uboot_tag;
char __initdata *uboot_arg;

39
const struct machine_desc *machine_desc;
40 41 42 43 44

struct task_struct *_current_task[NR_CPUS];	/* For stack switching */

struct cpuinfo_arc cpuinfo_arc700[NR_CPUS];

45
static void read_arc_build_cfg_regs(void)
46
{
47
	struct bcr_perip uncached_space;
48
	struct bcr_generic bcr;
49 50 51 52
	struct cpuinfo_arc *cpu = &cpuinfo_arc700[smp_processor_id()];
	FIX_PTR(cpu);

	READ_BCR(AUX_IDENTITY, cpu->core);
53
	READ_BCR(ARC_REG_ISA_CFG_BCR, cpu->isa);
54

55
	READ_BCR(ARC_REG_TIMERS_BCR, cpu->timers);
56 57 58
	cpu->vec_base = read_aux_reg(AUX_INTR_VEC_BASE);

	READ_BCR(ARC_REG_D_UNCACH_BCR, uncached_space);
59
	BUG_ON((uncached_space.start << 24) != ARC_UNCACHED_ADDR_SPACE);
60

61
	READ_BCR(ARC_REG_MUL_BCR, cpu->extn_mpy);
62

63 64 65 66 67
	cpu->extn.norm = read_aux_reg(ARC_REG_NORM_BCR) > 1 ? 1 : 0; /* 2,3 */
	cpu->extn.barrel = read_aux_reg(ARC_REG_BARREL_BCR) > 1 ? 1 : 0; /* 2,3 */
	cpu->extn.swap = read_aux_reg(ARC_REG_SWAP_BCR) ? 1 : 0;        /* 1,3 */
	cpu->extn.crc = read_aux_reg(ARC_REG_CRC_BCR) ? 1 : 0;
	cpu->extn.minmax = read_aux_reg(ARC_REG_MIXMAX_BCR) > 1 ? 1 : 0; /* 2 */
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
	/* Note that we read the CCM BCRs independent of kernel config
	 * This is to catch the cases where user doesn't know that
	 * CCMs are present in hardware build
	 */
	{
		struct bcr_iccm iccm;
		struct bcr_dccm dccm;
		struct bcr_dccm_base dccm_base;
		unsigned int bcr_32bit_val;

		bcr_32bit_val = read_aux_reg(ARC_REG_ICCM_BCR);
		if (bcr_32bit_val) {
			iccm = *((struct bcr_iccm *)&bcr_32bit_val);
			cpu->iccm.base_addr = iccm.base << 16;
			cpu->iccm.sz = 0x2000 << (iccm.sz - 1);
		}

		bcr_32bit_val = read_aux_reg(ARC_REG_DCCM_BCR);
		if (bcr_32bit_val) {
			dccm = *((struct bcr_dccm *)&bcr_32bit_val);
			cpu->dccm.sz = 0x800 << (dccm.sz);

			READ_BCR(ARC_REG_DCCMBASE_BCR, dccm_base);
			cpu->dccm.base_addr = dccm_base.addr << 8;
		}
	}

96 97
	READ_BCR(ARC_REG_XY_MEM_BCR, cpu->extn_xymem);

98 99
	read_decode_mmu_bcr();
	read_decode_cache_bcr();
100

101
	if (is_isa_arcompact()) {
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
		struct bcr_fp_arcompact sp, dp;
		struct bcr_bpu_arcompact bpu;

		READ_BCR(ARC_REG_FP_BCR, sp);
		READ_BCR(ARC_REG_DPFP_BCR, dp);
		cpu->extn.fpu_sp = sp.ver ? 1 : 0;
		cpu->extn.fpu_dp = dp.ver ? 1 : 0;

		READ_BCR(ARC_REG_BPU_BCR, bpu);
		cpu->bpu.ver = bpu.ver;
		cpu->bpu.full = bpu.fam ? 1 : 0;
		if (bpu.ent) {
			cpu->bpu.num_cache = 256 << (bpu.ent - 1);
			cpu->bpu.num_pred = 256 << (bpu.ent - 1);
		}
117 118 119 120 121 122 123 124 125 126 127 128 129
	} else {
		struct bcr_fp_arcv2 spdp;
		struct bcr_bpu_arcv2 bpu;

		READ_BCR(ARC_REG_FP_V2_BCR, spdp);
		cpu->extn.fpu_sp = spdp.sp ? 1 : 0;
		cpu->extn.fpu_dp = spdp.dp ? 1 : 0;

		READ_BCR(ARC_REG_BPU_BCR, bpu);
		cpu->bpu.ver = bpu.ver;
		cpu->bpu.full = bpu.ft;
		cpu->bpu.num_cache = 256 << bpu.bce;
		cpu->bpu.num_pred = 2048 << bpu.pte;
130 131 132 133 134 135 136 137
	}

	READ_BCR(ARC_REG_AP_BCR, bcr);
	cpu->extn.ap = bcr.ver ? 1 : 0;

	READ_BCR(ARC_REG_SMART_BCR, bcr);
	cpu->extn.smart = bcr.ver ? 1 : 0;

V
Vineet Gupta 已提交
138 139 140 141
	READ_BCR(ARC_REG_RTT_BCR, bcr);
	cpu->extn.rtt = bcr.ver ? 1 : 0;

	cpu->extn.debug = cpu->extn.ap | cpu->extn.smart | cpu->extn.rtt;
142 143 144 145 146 147
}

static const struct cpuinfo_data arc_cpu_tbl[] = {
	{ {0x20, "ARC 600"      }, 0x2F},
	{ {0x30, "ARC 700"      }, 0x33},
	{ {0x34, "ARC 700 R4.10"}, 0x34},
148
	{ {0x35, "ARC 700 R4.11"}, 0x35},
149
	{ {0x50, "ARC HS38"	}, 0x51},
150 151 152
	{ {0x00, NULL		} }
};

153 154 155 156
#define IS_AVAIL1(v, s)		((v) ? s : "")
#define IS_USED_RUN(v)		((v) ? "" : "(not used) ")
#define IS_USED_CFG(cfg)	IS_USED_RUN(IS_ENABLED(cfg))
#define IS_AVAIL2(v, s, cfg)	IS_AVAIL1(v, s), IS_AVAIL1(v, IS_USED_CFG(cfg))
157

158
static char *arc_cpu_mumbojumbo(int cpu_id, char *buf, int len)
159 160 161 162
{
	struct cpuinfo_arc *cpu = &cpuinfo_arc700[cpu_id];
	struct bcr_identity *core = &cpu->core;
	const struct cpuinfo_data *tbl;
163 164 165 166
	char *isa_nm;
	int i, be, atomic;
	int n = 0;

167 168
	FIX_PTR(cpu);

169
	if (is_isa_arcompact()) {
170 171 172 173 174 175
		isa_nm = "ARCompact";
		be = IS_ENABLED(CONFIG_CPU_BIG_ENDIAN);

		atomic = cpu->isa.atomic1;
		if (!cpu->isa.ver)	/* ISA BCR absent, use Kconfig info */
			atomic = IS_ENABLED(CONFIG_ARC_HAS_LLSC);
176 177 178 179
	} else {
		isa_nm = "ARCv2";
		be = cpu->isa.be;
		atomic = cpu->isa.atomic;
180 181
	}

182
	n += scnprintf(buf + n, len - n,
183 184
		       "\nIDENTITY\t: ARCVER [%#02x] ARCNUM [%#02x] CHIPID [%#4x]\n",
		       core->family, core->cpu_id, core->chip_id);
185 186 187 188 189

	for (tbl = &arc_cpu_tbl[0]; tbl->info.id != 0; tbl++) {
		if ((core->family >= tbl->info.id) &&
		    (core->family <= tbl->up_range)) {
			n += scnprintf(buf + n, len - n,
190 191 192
				       "processor [%d]\t: %s (%s ISA) %s\n",
				       cpu_id, tbl->info.str, isa_nm,
				       IS_AVAIL1(be, "[Big-Endian]"));
193 194 195 196 197 198 199 200 201 202 203
			break;
		}
	}

	if (tbl->info.id == 0)
		n += scnprintf(buf + n, len - n, "UNKNOWN ARC Processor\n");

	n += scnprintf(buf + n, len - n, "CPU speed\t: %u.%02u Mhz\n",
		       (unsigned int)(arc_get_core_freq() / 1000000),
		       (unsigned int)(arc_get_core_freq() / 10000) % 100);

204
	n += scnprintf(buf + n, len - n, "Timers\t\t: %s%s%s%s\nISA Extn\t: ",
205
		       IS_AVAIL1(cpu->timers.t0, "Timer0 "),
206 207 208
		       IS_AVAIL1(cpu->timers.t1, "Timer1 "),
		       IS_AVAIL2(cpu->timers.rtc, "64-bit RTC ",
				 CONFIG_ARC_HAS_RTC));
209

210 211 212 213
	n += i = scnprintf(buf + n, len - n, "%s%s%s%s%s",
			   IS_AVAIL2(atomic, "atomic ", CONFIG_ARC_HAS_LLSC),
			   IS_AVAIL2(cpu->isa.ldd, "ll64 ", CONFIG_ARC_HAS_LL64),
			   IS_AVAIL1(cpu->isa.unalign, "unalign (not used)"));
214

215 216
	if (i)
		n += scnprintf(buf + n, len - n, "\n\t\t: ");
217

218 219 220 221 222 223 224 225 226 227 228 229
	if (cpu->extn_mpy.ver) {
		if (cpu->extn_mpy.ver <= 0x2) {	/* ARCompact */
			n += scnprintf(buf + n, len - n, "mpy ");
		} else {
			int opt = 2;	/* stock MPY/MPYH */

			if (cpu->extn_mpy.dsp)	/* OPT 7-9 */
				opt = cpu->extn_mpy.dsp + 6;

			n += scnprintf(buf + n, len - n, "mpy[opt %d] ", opt);
		}
		n += scnprintf(buf + n, len - n, "%s",
230
			       IS_USED_CFG(CONFIG_ARC_HAS_HW_MPY));
231 232
	}

233
	n += scnprintf(buf + n, len - n, "%s%s%s%s%s%s%s%s\n",
234
		       IS_AVAIL1(cpu->isa.div_rem, "div_rem "),
235 236 237 238 239 240
		       IS_AVAIL1(cpu->extn.norm, "norm "),
		       IS_AVAIL1(cpu->extn.barrel, "barrel-shift "),
		       IS_AVAIL1(cpu->extn.swap, "swap "),
		       IS_AVAIL1(cpu->extn.minmax, "minmax "),
		       IS_AVAIL1(cpu->extn.crc, "crc "),
		       IS_AVAIL2(1, "swape", CONFIG_ARC_HAS_SWAPE));
241

242 243 244 245 246 247
	if (cpu->bpu.ver)
		n += scnprintf(buf + n, len - n,
			      "BPU\t\t: %s%s match, cache:%d, Predict Table:%d\n",
			      IS_AVAIL1(cpu->bpu.full, "full"),
			      IS_AVAIL1(!cpu->bpu.full, "partial"),
			      cpu->bpu.num_cache, cpu->bpu.num_pred);
248

249 250
	return buf;
}
251

252
static char *arc_extn_mumbojumbo(int cpu_id, char *buf, int len)
253 254 255 256 257 258 259
{
	int n = 0;
	struct cpuinfo_arc *cpu = &cpuinfo_arc700[cpu_id];

	FIX_PTR(cpu);

	n += scnprintf(buf + n, len - n,
260
		       "Vector Table\t: %#x\nUncached Base\t: %#x\n",
261
		       cpu->vec_base, ARC_UNCACHED_ADDR_SPACE);
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276

	if (cpu->extn.fpu_sp || cpu->extn.fpu_dp)
		n += scnprintf(buf + n, len - n, "FPU\t\t: %s%s\n",
			       IS_AVAIL1(cpu->extn.fpu_sp, "SP "),
			       IS_AVAIL1(cpu->extn.fpu_dp, "DP "));

	if (cpu->extn.debug)
		n += scnprintf(buf + n, len - n, "DEBUG\t\t: %s%s%s\n",
			       IS_AVAIL1(cpu->extn.ap, "ActionPoint "),
			       IS_AVAIL1(cpu->extn.smart, "smaRT "),
			       IS_AVAIL1(cpu->extn.rtt, "RTT "));

	if (cpu->dccm.sz || cpu->iccm.sz)
		n += scnprintf(buf + n, len - n, "Extn [CCM]\t: DCCM @ %x, %d KB / ICCM: @ %x, %d KB\n",
			       cpu->dccm.base_addr, TO_KB(cpu->dccm.sz),
277 278 279
			       cpu->iccm.base_addr, TO_KB(cpu->iccm.sz));

	n += scnprintf(buf + n, len - n,
280
		       "OS ABI [v3]\t: no-legacy-syscalls\n");
281 282 283 284

	return buf;
}

285
static void arc_chk_core_config(void)
286 287
{
	struct cpuinfo_arc *cpu = &cpuinfo_arc700[smp_processor_id()];
288
	int fpu_enabled;
289

290 291 292 293 294 295
	if (!cpu->timers.t0)
		panic("Timer0 is not present!\n");

	if (!cpu->timers.t1)
		panic("Timer1 is not present!\n");

296 297 298
	if (IS_ENABLED(CONFIG_ARC_HAS_RTC) && !cpu->timers.rtc)
		panic("RTC is not present\n");

299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
#ifdef CONFIG_ARC_HAS_DCCM
	/*
	 * DCCM can be arbit placed in hardware.
	 * Make sure it's placement/sz matches what Linux is built with
	 */
	if ((unsigned int)__arc_dccm_base != cpu->dccm.base_addr)
		panic("Linux built with incorrect DCCM Base address\n");

	if (CONFIG_ARC_DCCM_SZ != cpu->dccm.sz)
		panic("Linux built with incorrect DCCM Size\n");
#endif

#ifdef CONFIG_ARC_HAS_ICCM
	if (CONFIG_ARC_ICCM_SZ != cpu->iccm.sz)
		panic("Linux built with incorrect ICCM Size\n");
#endif

316 317 318 319 320 321 322 323
	/*
	 * FP hardware/software config sanity
	 * -If hardware contains DPFP, kernel needs to save/restore FPU state
	 * -If not, it will crash trying to save/restore the non-existant regs
	 *
	 * (only DPDP checked since SP has no arch visible regs)
	 */
	fpu_enabled = IS_ENABLED(CONFIG_ARC_FPU_SAVE_RESTORE);
324

325
	if (cpu->extn.fpu_dp && !fpu_enabled)
326
		pr_warn("CONFIG_ARC_FPU_SAVE_RESTORE needed for working apps\n");
327
	else if (!cpu->extn.fpu_dp && fpu_enabled)
328
		panic("FPU non-existent, disable CONFIG_ARC_FPU_SAVE_RESTORE\n");
329 330 331 332 333 334 335 336
}

/*
 * Initialize and setup the processor core
 * This is called by all the CPUs thus should not do special case stuff
 *    such as only for boot CPU etc
 */

337
void setup_processor(void)
338
{
339 340 341
	char str[512];
	int cpu_id = smp_processor_id();

342 343
	read_arc_build_cfg_regs();
	arc_init_IRQ();
344 345 346

	printk(arc_cpu_mumbojumbo(cpu_id, str, sizeof(str)));

347 348
	arc_mmu_init();
	arc_cache_init();
349 350 351 352

	printk(arc_extn_mumbojumbo(cpu_id, str, sizeof(str)));
	printk(arc_platform_smp_cpuinfo());

353
	arc_chk_core_config();
354 355
}

356 357 358 359 360 361 362
static inline int is_kernel(unsigned long addr)
{
	if (addr >= (unsigned long)_stext && addr <= (unsigned long)_end)
		return 1;
	return 0;
}

363 364
void __init setup_arch(char **cmdline_p)
{
V
Vineet Gupta 已提交
365
#ifdef CONFIG_ARC_UBOOT_SUPPORT
366 367 368 369 370 371
	/* make sure that uboot passed pointer to cmdline/dtb is valid */
	if (uboot_tag && is_kernel((unsigned long)uboot_arg))
		panic("Invalid uboot arg\n");

	/* See if u-boot passed an external Device Tree blob */
	machine_desc = setup_machine_fdt(uboot_arg);	/* uboot_tag == 2 */
V
Vineet Gupta 已提交
372 373 374
	if (!machine_desc)
#endif
	{
375
		/* No, so try the embedded one */
376 377 378 379 380
		machine_desc = setup_machine_fdt(__dtb_start);
		if (!machine_desc)
			panic("Embedded DT invalid\n");

		/*
381 382 383
		 * If we are here, it is established that @uboot_arg didn't
		 * point to DT blob. Instead if u-boot says it is cmdline,
		 * Appent to embedded DT cmdline.
384 385 386 387 388 389 390 391
		 * setup_machine_fdt() would have populated @boot_command_line
		 */
		if (uboot_tag == 1) {
			/* Ensure a whitespace between the 2 cmdlines */
			strlcat(boot_command_line, " ", COMMAND_LINE_SIZE);
			strlcat(boot_command_line, uboot_arg,
				COMMAND_LINE_SIZE);
		}
392
	}
393 394

	/* Save unparsed command line copy for /proc/cmdline */
395
	*cmdline_p = boot_command_line;
V
Vineet Gupta 已提交
396

397 398 399 400
	/* To force early parsing of things like mem=xxx */
	parse_early_param();

	/* Platform/board specific: e.g. early console registration */
401 402
	if (machine_desc->init_early)
		machine_desc->init_early();
403 404

	setup_processor();
V
Vineet Gupta 已提交
405
	smp_init_cpus();
406 407
	setup_arch_memory();

V
Vineet Gupta 已提交
408
	/* copy flat DT out of .init and then unflatten it */
409
	unflatten_and_copy_device_tree();
V
Vineet Gupta 已提交
410

411 412 413 414 415 416 417 418 419
	/* Can be issue if someone passes cmd line arg "ro"
	 * But that is unlikely so keeping it as it is
	 */
	root_mountflags &= ~MS_RDONLY;

#if defined(CONFIG_VT) && defined(CONFIG_DUMMY_CONSOLE)
	conswitchp = &dummy_con;
#endif

420 421
	arc_unwind_init();
	arc_unwind_setup();
422 423
}

424 425
static int __init customize_machine(void)
{
426 427 428 429 430 431 432
	of_clk_init(NULL);
	/*
	 * Traverses flattened DeviceTree - registering platform devices
	 * (if any) complete with their resources
	 */
	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);

433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
	if (machine_desc->init_machine)
		machine_desc->init_machine();

	return 0;
}
arch_initcall(customize_machine);

static int __init init_late_machine(void)
{
	if (machine_desc->init_late)
		machine_desc->init_late();

	return 0;
}
late_initcall(init_late_machine);
448 449 450 451 452 453 454 455 456 457 458 459
/*
 *  Get CPU information for use by the procfs.
 */

#define cpu_to_ptr(c)	((void *)(0xFFFF0000 | (unsigned int)(c)))
#define ptr_to_cpu(p)	(~0xFFFF0000UL & (unsigned int)(p))

static int show_cpuinfo(struct seq_file *m, void *v)
{
	char *str;
	int cpu_id = ptr_to_cpu(v);

460 461 462 463 464
	if (!cpu_online(cpu_id)) {
		seq_printf(m, "processor [%d]\t: Offline\n", cpu_id);
		goto done;
	}

465 466 467 468
	str = (char *)__get_free_page(GFP_TEMPORARY);
	if (!str)
		goto done;

469
	seq_printf(m, arc_cpu_mumbojumbo(cpu_id, str, PAGE_SIZE));
470

471
	seq_printf(m, "Bogo MIPS\t: %lu.%02lu\n",
472 473 474
		   loops_per_jiffy / (500000 / HZ),
		   (loops_per_jiffy / (5000 / HZ)) % 100);

475 476 477 478 479
	seq_printf(m, arc_mmu_mumbojumbo(cpu_id, str, PAGE_SIZE));
	seq_printf(m, arc_cache_mumbojumbo(cpu_id, str, PAGE_SIZE));
	seq_printf(m, arc_extn_mumbojumbo(cpu_id, str, PAGE_SIZE));
	seq_printf(m, arc_platform_smp_cpuinfo());

480 481
	free_page((unsigned long)str);
done:
482
	seq_printf(m, "\n");
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

	return 0;
}

static void *c_start(struct seq_file *m, loff_t *pos)
{
	/*
	 * Callback returns cpu-id to iterator for show routine, NULL to stop.
	 * However since NULL is also a valid cpu-id (0), we use a round-about
	 * way to pass it w/o having to kmalloc/free a 2 byte string.
	 * Encode cpu-id as 0xFFcccc, which is decoded by show routine.
	 */
	return *pos < num_possible_cpus() ? cpu_to_ptr(*pos) : NULL;
}

static void *c_next(struct seq_file *m, void *v, loff_t *pos)
{
	++*pos;
	return c_start(m, pos);
}

static void c_stop(struct seq_file *m, void *v)
{
}

const struct seq_operations cpuinfo_op = {
	.start	= c_start,
	.next	= c_next,
	.stop	= c_stop,
	.show	= show_cpuinfo
};

static DEFINE_PER_CPU(struct cpu, cpu_topology);

static int __init topology_init(void)
{
	int cpu;

	for_each_present_cpu(cpu)
	    register_cpu(&per_cpu(cpu_topology, cpu), cpu);

	return 0;
}

subsys_initcall(topology_init);