0001
0002
0003
0004
0005
0006 #include <linux/seq_file.h>
0007 #include <linux/fs.h>
0008 #include <linux/delay.h>
0009 #include <linux/root_dev.h>
0010 #include <linux/clk.h>
0011 #include <linux/clocksource.h>
0012 #include <linux/console.h>
0013 #include <linux/module.h>
0014 #include <linux/sizes.h>
0015 #include <linux/cpu.h>
0016 #include <linux/of_clk.h>
0017 #include <linux/of_fdt.h>
0018 #include <linux/of.h>
0019 #include <linux/cache.h>
0020 #include <uapi/linux/mount.h>
0021 #include <asm/sections.h>
0022 #include <asm/arcregs.h>
0023 #include <asm/asserts.h>
0024 #include <asm/tlb.h>
0025 #include <asm/setup.h>
0026 #include <asm/page.h>
0027 #include <asm/irq.h>
0028 #include <asm/unwind.h>
0029 #include <asm/mach_desc.h>
0030 #include <asm/smp.h>
0031 #include <asm/dsp-impl.h>
0032
0033 #define FIX_PTR(x) __asm__ __volatile__(";" : "+r"(x))
0034
0035 unsigned int intr_to_DE_cnt;
0036
0037
0038 int __initdata uboot_tag;
0039 int __initdata uboot_magic;
0040 char __initdata *uboot_arg;
0041
0042 const struct machine_desc *machine_desc;
0043
0044 struct task_struct *_current_task[NR_CPUS];
0045
0046 struct cpuinfo_arc cpuinfo_arc700[NR_CPUS];
0047
0048 static const struct id_to_str arc_legacy_rel[] = {
0049
0050 #ifdef CONFIG_ISA_ARCOMPACT
0051 { 0x34, "R4.10"},
0052 { 0x35, "R4.11"},
0053 #else
0054 { 0x51, "R2.0" },
0055 { 0x52, "R2.1" },
0056 { 0x53, "R3.0" },
0057 #endif
0058 { 0x00, NULL }
0059 };
0060
0061 static const struct id_to_str arc_hs_ver54_rel[] = {
0062
0063 { 0, "R3.10a"},
0064 { 1, "R3.50a"},
0065 { 2, "R3.60a"},
0066 { 3, "R4.00a"},
0067 { 0xFF, NULL }
0068 };
0069
0070 static void read_decode_ccm_bcr(struct cpuinfo_arc *cpu)
0071 {
0072 if (is_isa_arcompact()) {
0073 struct bcr_iccm_arcompact iccm;
0074 struct bcr_dccm_arcompact dccm;
0075
0076 READ_BCR(ARC_REG_ICCM_BUILD, iccm);
0077 if (iccm.ver) {
0078 cpu->iccm.sz = 4096 << iccm.sz;
0079 cpu->iccm.base_addr = iccm.base << 16;
0080 }
0081
0082 READ_BCR(ARC_REG_DCCM_BUILD, dccm);
0083 if (dccm.ver) {
0084 unsigned long base;
0085 cpu->dccm.sz = 2048 << dccm.sz;
0086
0087 base = read_aux_reg(ARC_REG_DCCM_BASE_BUILD);
0088 cpu->dccm.base_addr = base & ~0xF;
0089 }
0090 } else {
0091 struct bcr_iccm_arcv2 iccm;
0092 struct bcr_dccm_arcv2 dccm;
0093 unsigned long region;
0094
0095 READ_BCR(ARC_REG_ICCM_BUILD, iccm);
0096 if (iccm.ver) {
0097 cpu->iccm.sz = 256 << iccm.sz00;
0098 if (iccm.sz00 == 0xF && iccm.sz01 > 0)
0099 cpu->iccm.sz <<= iccm.sz01;
0100
0101 region = read_aux_reg(ARC_REG_AUX_ICCM);
0102 cpu->iccm.base_addr = region & 0xF0000000;
0103 }
0104
0105 READ_BCR(ARC_REG_DCCM_BUILD, dccm);
0106 if (dccm.ver) {
0107 cpu->dccm.sz = 256 << dccm.sz0;
0108 if (dccm.sz0 == 0xF && dccm.sz1 > 0)
0109 cpu->dccm.sz <<= dccm.sz1;
0110
0111 region = read_aux_reg(ARC_REG_AUX_DCCM);
0112 cpu->dccm.base_addr = region & 0xF0000000;
0113 }
0114 }
0115 }
0116
0117 static void decode_arc_core(struct cpuinfo_arc *cpu)
0118 {
0119 struct bcr_uarch_build_arcv2 uarch;
0120 const struct id_to_str *tbl;
0121
0122 if (cpu->core.family < 0x54) {
0123
0124 for (tbl = &arc_legacy_rel[0]; tbl->id != 0; tbl++) {
0125 if (cpu->core.family == tbl->id) {
0126 cpu->release = tbl->str;
0127 break;
0128 }
0129 }
0130
0131 if (is_isa_arcompact())
0132 cpu->name = "ARC700";
0133 else if (tbl->str)
0134 cpu->name = "HS38";
0135 else
0136 cpu->name = cpu->release = "Unknown";
0137
0138 return;
0139 }
0140
0141
0142
0143
0144
0145
0146 READ_BCR(ARC_REG_MICRO_ARCH_BCR, uarch);
0147
0148 if (uarch.prod == 4) {
0149 cpu->name = "HS48";
0150 cpu->extn.dual = 1;
0151
0152 } else {
0153 cpu->name = "HS38";
0154 }
0155
0156 for (tbl = &arc_hs_ver54_rel[0]; tbl->id != 0xFF; tbl++) {
0157 if (uarch.maj == tbl->id) {
0158 cpu->release = tbl->str;
0159 break;
0160 }
0161 }
0162 }
0163
0164 static void read_arc_build_cfg_regs(void)
0165 {
0166 struct bcr_timer timer;
0167 struct bcr_generic bcr;
0168 struct cpuinfo_arc *cpu = &cpuinfo_arc700[smp_processor_id()];
0169 struct bcr_isa_arcv2 isa;
0170 struct bcr_actionpoint ap;
0171
0172 FIX_PTR(cpu);
0173
0174 READ_BCR(AUX_IDENTITY, cpu->core);
0175 decode_arc_core(cpu);
0176
0177 READ_BCR(ARC_REG_TIMERS_BCR, timer);
0178 cpu->extn.timer0 = timer.t0;
0179 cpu->extn.timer1 = timer.t1;
0180 cpu->extn.rtc = timer.rtc;
0181
0182 cpu->vec_base = read_aux_reg(AUX_INTR_VEC_BASE);
0183
0184 READ_BCR(ARC_REG_MUL_BCR, cpu->extn_mpy);
0185
0186
0187 read_decode_ccm_bcr(cpu);
0188
0189 read_decode_mmu_bcr();
0190 read_decode_cache_bcr();
0191
0192 if (is_isa_arcompact()) {
0193 struct bcr_fp_arcompact sp, dp;
0194 struct bcr_bpu_arcompact bpu;
0195
0196 READ_BCR(ARC_REG_FP_BCR, sp);
0197 READ_BCR(ARC_REG_DPFP_BCR, dp);
0198 cpu->extn.fpu_sp = sp.ver ? 1 : 0;
0199 cpu->extn.fpu_dp = dp.ver ? 1 : 0;
0200
0201 READ_BCR(ARC_REG_BPU_BCR, bpu);
0202 cpu->bpu.ver = bpu.ver;
0203 cpu->bpu.full = bpu.fam ? 1 : 0;
0204 if (bpu.ent) {
0205 cpu->bpu.num_cache = 256 << (bpu.ent - 1);
0206 cpu->bpu.num_pred = 256 << (bpu.ent - 1);
0207 }
0208 } else {
0209 struct bcr_fp_arcv2 spdp;
0210 struct bcr_bpu_arcv2 bpu;
0211
0212 READ_BCR(ARC_REG_FP_V2_BCR, spdp);
0213 cpu->extn.fpu_sp = spdp.sp ? 1 : 0;
0214 cpu->extn.fpu_dp = spdp.dp ? 1 : 0;
0215
0216 READ_BCR(ARC_REG_BPU_BCR, bpu);
0217 cpu->bpu.ver = bpu.ver;
0218 cpu->bpu.full = bpu.ft;
0219 cpu->bpu.num_cache = 256 << bpu.bce;
0220 cpu->bpu.num_pred = 2048 << bpu.pte;
0221 cpu->bpu.ret_stk = 4 << bpu.rse;
0222
0223
0224 if (cpu->extn.dual) {
0225 unsigned int exec_ctrl;
0226
0227 READ_BCR(AUX_EXEC_CTRL, exec_ctrl);
0228 cpu->extn.dual_enb = !(exec_ctrl & 1);
0229 }
0230 }
0231
0232 READ_BCR(ARC_REG_AP_BCR, ap);
0233 if (ap.ver) {
0234 cpu->extn.ap_num = 2 << ap.num;
0235 cpu->extn.ap_full = !ap.min;
0236 }
0237
0238 READ_BCR(ARC_REG_SMART_BCR, bcr);
0239 cpu->extn.smart = bcr.ver ? 1 : 0;
0240
0241 READ_BCR(ARC_REG_RTT_BCR, bcr);
0242 cpu->extn.rtt = bcr.ver ? 1 : 0;
0243
0244 READ_BCR(ARC_REG_ISA_CFG_BCR, isa);
0245
0246
0247 if (is_isa_arcompact()) {
0248 if (!isa.ver)
0249 cpu->isa.atomic = IS_ENABLED(CONFIG_ARC_HAS_LLSC);
0250 else {
0251
0252 struct bcr_generic bcr = *(struct bcr_generic *)&isa;
0253 cpu->isa.atomic = bcr.info & 1;
0254 }
0255
0256 cpu->isa.be = IS_ENABLED(CONFIG_CPU_BIG_ENDIAN);
0257
0258
0259 if (unlikely(cpu->core.family < 0x34 || cpu->mmu.ver < 3))
0260 cpu->name = "ARC750";
0261 } else {
0262 cpu->isa = isa;
0263 }
0264 }
0265
0266 static char *arc_cpu_mumbojumbo(int cpu_id, char *buf, int len)
0267 {
0268 struct cpuinfo_arc *cpu = &cpuinfo_arc700[cpu_id];
0269 struct bcr_identity *core = &cpu->core;
0270 char mpy_opt[16];
0271 int n = 0;
0272
0273 FIX_PTR(cpu);
0274
0275 n += scnprintf(buf + n, len - n,
0276 "\nIDENTITY\t: ARCVER [%#02x] ARCNUM [%#02x] CHIPID [%#4x]\n",
0277 core->family, core->cpu_id, core->chip_id);
0278
0279 n += scnprintf(buf + n, len - n, "processor [%d]\t: %s %s (%s ISA) %s%s%s\n",
0280 cpu_id, cpu->name, cpu->release,
0281 is_isa_arcompact() ? "ARCompact" : "ARCv2",
0282 IS_AVAIL1(cpu->isa.be, "[Big-Endian]"),
0283 IS_AVAIL3(cpu->extn.dual, cpu->extn.dual_enb, " Dual-Issue "));
0284
0285 n += scnprintf(buf + n, len - n, "Timers\t\t: %s%s%s%s%s%s\nISA Extn\t: ",
0286 IS_AVAIL1(cpu->extn.timer0, "Timer0 "),
0287 IS_AVAIL1(cpu->extn.timer1, "Timer1 "),
0288 IS_AVAIL2(cpu->extn.rtc, "RTC [UP 64-bit] ", CONFIG_ARC_TIMERS_64BIT),
0289 IS_AVAIL2(cpu->extn.gfrc, "GFRC [SMP 64-bit] ", CONFIG_ARC_TIMERS_64BIT));
0290
0291 if (cpu->extn_mpy.ver) {
0292 if (is_isa_arcompact()) {
0293 scnprintf(mpy_opt, 16, "mpy");
0294 } else {
0295
0296 int opt = 2;
0297
0298 if (cpu->extn_mpy.dsp)
0299 opt = cpu->extn_mpy.dsp + 6;
0300
0301 scnprintf(mpy_opt, 16, "mpy[opt %d] ", opt);
0302 }
0303 }
0304
0305 n += scnprintf(buf + n, len - n, "%s%s%s%s%s%s%s%s\n",
0306 IS_AVAIL2(cpu->isa.atomic, "atomic ", CONFIG_ARC_HAS_LLSC),
0307 IS_AVAIL2(cpu->isa.ldd, "ll64 ", CONFIG_ARC_HAS_LL64),
0308 IS_AVAIL2(cpu->isa.unalign, "unalign ", CONFIG_ARC_USE_UNALIGNED_MEM_ACCESS),
0309 IS_AVAIL1(cpu->extn_mpy.ver, mpy_opt),
0310 IS_AVAIL1(cpu->isa.div_rem, "div_rem "));
0311
0312 if (cpu->bpu.ver) {
0313 n += scnprintf(buf + n, len - n,
0314 "BPU\t\t: %s%s match, cache:%d, Predict Table:%d Return stk: %d",
0315 IS_AVAIL1(cpu->bpu.full, "full"),
0316 IS_AVAIL1(!cpu->bpu.full, "partial"),
0317 cpu->bpu.num_cache, cpu->bpu.num_pred, cpu->bpu.ret_stk);
0318
0319 if (is_isa_arcv2()) {
0320 struct bcr_lpb lpb;
0321
0322 READ_BCR(ARC_REG_LPB_BUILD, lpb);
0323 if (lpb.ver) {
0324 unsigned int ctl;
0325 ctl = read_aux_reg(ARC_REG_LPB_CTRL);
0326
0327 n += scnprintf(buf + n, len - n, " Loop Buffer:%d %s",
0328 lpb.entries,
0329 IS_DISABLED_RUN(!ctl));
0330 }
0331 }
0332 n += scnprintf(buf + n, len - n, "\n");
0333 }
0334
0335 return buf;
0336 }
0337
0338 static char *arc_extn_mumbojumbo(int cpu_id, char *buf, int len)
0339 {
0340 int n = 0;
0341 struct cpuinfo_arc *cpu = &cpuinfo_arc700[cpu_id];
0342
0343 FIX_PTR(cpu);
0344
0345 n += scnprintf(buf + n, len - n, "Vector Table\t: %#x\n", cpu->vec_base);
0346
0347 if (cpu->extn.fpu_sp || cpu->extn.fpu_dp)
0348 n += scnprintf(buf + n, len - n, "FPU\t\t: %s%s\n",
0349 IS_AVAIL1(cpu->extn.fpu_sp, "SP "),
0350 IS_AVAIL1(cpu->extn.fpu_dp, "DP "));
0351
0352 if (cpu->extn.ap_num | cpu->extn.smart | cpu->extn.rtt) {
0353 n += scnprintf(buf + n, len - n, "DEBUG\t\t: %s%s",
0354 IS_AVAIL1(cpu->extn.smart, "smaRT "),
0355 IS_AVAIL1(cpu->extn.rtt, "RTT "));
0356 if (cpu->extn.ap_num) {
0357 n += scnprintf(buf + n, len - n, "ActionPoint %d/%s",
0358 cpu->extn.ap_num,
0359 cpu->extn.ap_full ? "full":"min");
0360 }
0361 n += scnprintf(buf + n, len - n, "\n");
0362 }
0363
0364 if (cpu->dccm.sz || cpu->iccm.sz)
0365 n += scnprintf(buf + n, len - n, "Extn [CCM]\t: DCCM @ %x, %d KB / ICCM: @ %x, %d KB\n",
0366 cpu->dccm.base_addr, TO_KB(cpu->dccm.sz),
0367 cpu->iccm.base_addr, TO_KB(cpu->iccm.sz));
0368
0369 if (is_isa_arcv2()) {
0370
0371
0372 struct bcr_erp erp;
0373 READ_BCR(ARC_REG_ERP_BUILD, erp);
0374
0375 if (erp.ver) {
0376 struct ctl_erp ctl;
0377 READ_BCR(ARC_REG_ERP_CTRL, ctl);
0378
0379
0380 n += scnprintf(buf + n, len - n, "Extn [ECC]\t: %s%s%s%s%s%s\n",
0381 IS_AVAIL3(erp.ic, !ctl.dpi, "IC "),
0382 IS_AVAIL3(erp.dc, !ctl.dpd, "DC "),
0383 IS_AVAIL3(erp.mmu, !ctl.mpd, "MMU "));
0384 }
0385 }
0386
0387 return buf;
0388 }
0389
0390 void chk_opt_strict(char *opt_name, bool hw_exists, bool opt_ena)
0391 {
0392 if (hw_exists && !opt_ena)
0393 pr_warn(" ! Enable %s for working apps\n", opt_name);
0394 else if (!hw_exists && opt_ena)
0395 panic("Disable %s, hardware NOT present\n", opt_name);
0396 }
0397
0398 void chk_opt_weak(char *opt_name, bool hw_exists, bool opt_ena)
0399 {
0400 if (!hw_exists && opt_ena)
0401 panic("Disable %s, hardware NOT present\n", opt_name);
0402 }
0403
0404 static void arc_chk_core_config(void)
0405 {
0406 struct cpuinfo_arc *cpu = &cpuinfo_arc700[smp_processor_id()];
0407 int present = 0;
0408
0409 if (!cpu->extn.timer0)
0410 panic("Timer0 is not present!\n");
0411
0412 if (!cpu->extn.timer1)
0413 panic("Timer1 is not present!\n");
0414
0415 #ifdef CONFIG_ARC_HAS_DCCM
0416
0417
0418
0419
0420 if ((unsigned int)__arc_dccm_base != cpu->dccm.base_addr)
0421 panic("Linux built with incorrect DCCM Base address\n");
0422
0423 if (CONFIG_ARC_DCCM_SZ * SZ_1K != cpu->dccm.sz)
0424 panic("Linux built with incorrect DCCM Size\n");
0425 #endif
0426
0427 #ifdef CONFIG_ARC_HAS_ICCM
0428 if (CONFIG_ARC_ICCM_SZ * SZ_1K != cpu->iccm.sz)
0429 panic("Linux built with incorrect ICCM Size\n");
0430 #endif
0431
0432
0433
0434
0435
0436
0437
0438 if (is_isa_arcompact()) {
0439
0440 present = cpu->extn.fpu_dp;
0441 CHK_OPT_STRICT(CONFIG_ARC_FPU_SAVE_RESTORE, present);
0442 } else {
0443
0444 present = cpu->extn_mpy.dsp | cpu->extn.fpu_sp | cpu->extn.fpu_dp;
0445 CHK_OPT_STRICT(CONFIG_ARC_HAS_ACCL_REGS, present);
0446
0447 dsp_config_check();
0448 }
0449 }
0450
0451
0452
0453
0454
0455
0456
0457 void setup_processor(void)
0458 {
0459 char str[512];
0460 int cpu_id = smp_processor_id();
0461
0462 read_arc_build_cfg_regs();
0463 arc_init_IRQ();
0464
0465 pr_info("%s", arc_cpu_mumbojumbo(cpu_id, str, sizeof(str)));
0466
0467 arc_mmu_init();
0468 arc_cache_init();
0469
0470 pr_info("%s", arc_extn_mumbojumbo(cpu_id, str, sizeof(str)));
0471 pr_info("%s", arc_platform_smp_cpuinfo());
0472
0473 arc_chk_core_config();
0474 }
0475
0476 static inline bool uboot_arg_invalid(unsigned long addr)
0477 {
0478
0479
0480
0481
0482 if (addr < PAGE_OFFSET)
0483 return true;
0484
0485
0486 return addr >= (unsigned long)_stext && addr <= (unsigned long)_end;
0487 }
0488
0489 #define IGNORE_ARGS "Ignore U-boot args: "
0490
0491
0492 #define UBOOT_TAG_NONE 0
0493 #define UBOOT_TAG_CMDLINE 1
0494 #define UBOOT_TAG_DTB 2
0495
0496 #define UBOOT_MAGIC_VALUE 0
0497
0498 void __init handle_uboot_args(void)
0499 {
0500 bool use_embedded_dtb = true;
0501 bool append_cmdline = false;
0502
0503
0504 if (uboot_tag != UBOOT_TAG_NONE &&
0505 uboot_tag != UBOOT_TAG_CMDLINE &&
0506 uboot_tag != UBOOT_TAG_DTB) {
0507 pr_warn(IGNORE_ARGS "invalid uboot tag: '%08x'\n", uboot_tag);
0508 goto ignore_uboot_args;
0509 }
0510
0511 if (uboot_magic != UBOOT_MAGIC_VALUE) {
0512 pr_warn(IGNORE_ARGS "non zero uboot magic\n");
0513 goto ignore_uboot_args;
0514 }
0515
0516 if (uboot_tag != UBOOT_TAG_NONE &&
0517 uboot_arg_invalid((unsigned long)uboot_arg)) {
0518 pr_warn(IGNORE_ARGS "invalid uboot arg: '%px'\n", uboot_arg);
0519 goto ignore_uboot_args;
0520 }
0521
0522
0523 if (uboot_tag == UBOOT_TAG_DTB) {
0524 machine_desc = setup_machine_fdt((void *)uboot_arg);
0525
0526
0527 use_embedded_dtb = !machine_desc;
0528 }
0529
0530 if (uboot_tag == UBOOT_TAG_CMDLINE)
0531 append_cmdline = true;
0532
0533 ignore_uboot_args:
0534
0535 if (use_embedded_dtb) {
0536 machine_desc = setup_machine_fdt(__dtb_start);
0537 if (!machine_desc)
0538 panic("Embedded DT invalid\n");
0539 }
0540
0541
0542
0543
0544
0545 if (append_cmdline) {
0546
0547 strlcat(boot_command_line, " ", COMMAND_LINE_SIZE);
0548 strlcat(boot_command_line, uboot_arg, COMMAND_LINE_SIZE);
0549 }
0550 }
0551
0552 void __init setup_arch(char **cmdline_p)
0553 {
0554 handle_uboot_args();
0555
0556
0557 *cmdline_p = boot_command_line;
0558
0559
0560 parse_early_param();
0561
0562
0563 if (machine_desc->init_early)
0564 machine_desc->init_early();
0565
0566 smp_init_cpus();
0567
0568 setup_processor();
0569 setup_arch_memory();
0570
0571
0572 unflatten_and_copy_device_tree();
0573
0574
0575
0576
0577 root_mountflags &= ~MS_RDONLY;
0578
0579 arc_unwind_init();
0580 }
0581
0582
0583
0584
0585 void __init time_init(void)
0586 {
0587 of_clk_init(NULL);
0588 timer_probe();
0589 }
0590
0591 static int __init customize_machine(void)
0592 {
0593 if (machine_desc->init_machine)
0594 machine_desc->init_machine();
0595
0596 return 0;
0597 }
0598 arch_initcall(customize_machine);
0599
0600 static int __init init_late_machine(void)
0601 {
0602 if (machine_desc->init_late)
0603 machine_desc->init_late();
0604
0605 return 0;
0606 }
0607 late_initcall(init_late_machine);
0608
0609
0610
0611
0612 #define cpu_to_ptr(c) ((void *)(0xFFFF0000 | (unsigned int)(c)))
0613 #define ptr_to_cpu(p) (~0xFFFF0000UL & (unsigned int)(p))
0614
0615 static int show_cpuinfo(struct seq_file *m, void *v)
0616 {
0617 char *str;
0618 int cpu_id = ptr_to_cpu(v);
0619 struct device *cpu_dev = get_cpu_device(cpu_id);
0620 struct clk *cpu_clk;
0621 unsigned long freq = 0;
0622
0623 if (!cpu_online(cpu_id)) {
0624 seq_printf(m, "processor [%d]\t: Offline\n", cpu_id);
0625 goto done;
0626 }
0627
0628 str = (char *)__get_free_page(GFP_KERNEL);
0629 if (!str)
0630 goto done;
0631
0632 seq_printf(m, arc_cpu_mumbojumbo(cpu_id, str, PAGE_SIZE));
0633
0634 cpu_clk = clk_get(cpu_dev, NULL);
0635 if (IS_ERR(cpu_clk)) {
0636 seq_printf(m, "CPU speed \t: Cannot get clock for processor [%d]\n",
0637 cpu_id);
0638 } else {
0639 freq = clk_get_rate(cpu_clk);
0640 }
0641 if (freq)
0642 seq_printf(m, "CPU speed\t: %lu.%02lu Mhz\n",
0643 freq / 1000000, (freq / 10000) % 100);
0644
0645 seq_printf(m, "Bogo MIPS\t: %lu.%02lu\n",
0646 loops_per_jiffy / (500000 / HZ),
0647 (loops_per_jiffy / (5000 / HZ)) % 100);
0648
0649 seq_printf(m, arc_mmu_mumbojumbo(cpu_id, str, PAGE_SIZE));
0650 seq_printf(m, arc_cache_mumbojumbo(cpu_id, str, PAGE_SIZE));
0651 seq_printf(m, arc_extn_mumbojumbo(cpu_id, str, PAGE_SIZE));
0652 seq_printf(m, arc_platform_smp_cpuinfo());
0653
0654 free_page((unsigned long)str);
0655 done:
0656 seq_printf(m, "\n");
0657
0658 return 0;
0659 }
0660
0661 static void *c_start(struct seq_file *m, loff_t *pos)
0662 {
0663
0664
0665
0666
0667
0668
0669 return *pos < nr_cpu_ids ? cpu_to_ptr(*pos) : NULL;
0670 }
0671
0672 static void *c_next(struct seq_file *m, void *v, loff_t *pos)
0673 {
0674 ++*pos;
0675 return c_start(m, pos);
0676 }
0677
0678 static void c_stop(struct seq_file *m, void *v)
0679 {
0680 }
0681
0682 const struct seq_operations cpuinfo_op = {
0683 .start = c_start,
0684 .next = c_next,
0685 .stop = c_stop,
0686 .show = show_cpuinfo
0687 };
0688
0689 static DEFINE_PER_CPU(struct cpu, cpu_topology);
0690
0691 static int __init topology_init(void)
0692 {
0693 int cpu;
0694
0695 for_each_present_cpu(cpu)
0696 register_cpu(&per_cpu(cpu_topology, cpu), cpu);
0697
0698 return 0;
0699 }
0700
0701 subsys_initcall(topology_init);