1/* 2 * Copyright (C) 2005-2006 Atmel Corporation 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 as 6 * published by the Free Software Foundation. 7 */ 8#include <linux/init.h> 9#include <linux/sysdev.h> 10#include <linux/seq_file.h> 11#include <linux/cpu.h> 12#include <linux/module.h> 13#include <linux/percpu.h> 14#include <linux/param.h> 15#include <linux/errno.h> 16#include <linux/clk.h> 17 18#include <asm/setup.h> 19#include <asm/sysreg.h> 20 21static DEFINE_PER_CPU(struct cpu, cpu_devices); 22 23#ifdef CONFIG_PERFORMANCE_COUNTERS 24 25static ssize_t show_pc0event(struct sys_device *dev, 26 struct sysdev_attribute *attr, char *buf) 27{ 28 unsigned long pccr; 29 30 pccr = sysreg_read(PCCR); 31 return sprintf(buf, "0x%lx\n", (pccr >> 12) & 0x3f); 32} 33static ssize_t store_pc0event(struct sys_device *dev, 34 struct sysdev_attribute *attr, const char *buf, 35 size_t count) 36{ 37 unsigned long val; 38 char *endp; 39 40 val = simple_strtoul(buf, &endp, 0); 41 if (endp == buf || val > 0x3f) 42 return -EINVAL; 43 val = (val << 12) | (sysreg_read(PCCR) & 0xfffc0fff); 44 sysreg_write(PCCR, val); 45 return count; 46} 47static ssize_t show_pc0count(struct sys_device *dev, 48 struct sysdev_attribute *attr, char *buf) 49{ 50 unsigned long pcnt0; 51 52 pcnt0 = sysreg_read(PCNT0); 53 return sprintf(buf, "%lu\n", pcnt0); 54} 55static ssize_t store_pc0count(struct sys_device *dev, 56 struct sysdev_attribute *attr, 57 const char *buf, size_t count) 58{ 59 unsigned long val; 60 char *endp; 61 62 val = simple_strtoul(buf, &endp, 0); 63 if (endp == buf) 64 return -EINVAL; 65 sysreg_write(PCNT0, val); 66 67 return count; 68} 69 70static ssize_t show_pc1event(struct sys_device *dev, 71 struct sysdev_attribute *attr, char *buf) 72{ 73 unsigned long pccr; 74 75 pccr = sysreg_read(PCCR); 76 return sprintf(buf, "0x%lx\n", (pccr >> 18) & 0x3f); 77} 78static ssize_t store_pc1event(struct sys_device *dev, 79 struct sysdev_attribute *attr, const char *buf, 80 size_t count) 81{ 82 unsigned long val; 83 char *endp; 84 85 val = simple_strtoul(buf, &endp, 0); 86 if (endp == buf || val > 0x3f) 87 return -EINVAL; 88 val = (val << 18) | (sysreg_read(PCCR) & 0xff03ffff); 89 sysreg_write(PCCR, val); 90 return count; 91} 92static ssize_t show_pc1count(struct sys_device *dev, 93 struct sysdev_attribute *attr, char *buf) 94{ 95 unsigned long pcnt1; 96 97 pcnt1 = sysreg_read(PCNT1); 98 return sprintf(buf, "%lu\n", pcnt1); 99} 100static ssize_t store_pc1count(struct sys_device *dev, 101 struct sysdev_attribute *attr, const char *buf, 102 size_t count) 103{ 104 unsigned long val; 105 char *endp; 106 107 val = simple_strtoul(buf, &endp, 0); 108 if (endp == buf) 109 return -EINVAL; 110 sysreg_write(PCNT1, val); 111 112 return count; 113} 114 115static ssize_t show_pccycles(struct sys_device *dev, 116 struct sysdev_attribute *attr, char *buf) 117{ 118 unsigned long pccnt; 119 120 pccnt = sysreg_read(PCCNT); 121 return sprintf(buf, "%lu\n", pccnt); 122} 123static ssize_t store_pccycles(struct sys_device *dev, 124 struct sysdev_attribute *attr, const char *buf, 125 size_t count) 126{ 127 unsigned long val; 128 char *endp; 129 130 val = simple_strtoul(buf, &endp, 0); 131 if (endp == buf) 132 return -EINVAL; 133 sysreg_write(PCCNT, val); 134 135 return count; 136} 137 138static ssize_t show_pcenable(struct sys_device *dev, 139 struct sysdev_attribute *attr, char *buf) 140{ 141 unsigned long pccr; 142 143 pccr = sysreg_read(PCCR); 144 return sprintf(buf, "%c\n", (pccr & 1)?'1':'0'); 145} 146static ssize_t store_pcenable(struct sys_device *dev, 147 struct sysdev_attribute *attr, const char *buf, 148 size_t count) 149{ 150 unsigned long pccr, val; 151 char *endp; 152 153 val = simple_strtoul(buf, &endp, 0); 154 if (endp == buf) 155 return -EINVAL; 156 if (val) 157 val = 1; 158 159 pccr = sysreg_read(PCCR); 160 pccr = (pccr & ~1UL) | val; 161 sysreg_write(PCCR, pccr); 162 163 return count; 164} 165 166static SYSDEV_ATTR(pc0event, 0600, show_pc0event, store_pc0event); 167static SYSDEV_ATTR(pc0count, 0600, show_pc0count, store_pc0count); 168static SYSDEV_ATTR(pc1event, 0600, show_pc1event, store_pc1event); 169static SYSDEV_ATTR(pc1count, 0600, show_pc1count, store_pc1count); 170static SYSDEV_ATTR(pccycles, 0600, show_pccycles, store_pccycles); 171static SYSDEV_ATTR(pcenable, 0600, show_pcenable, store_pcenable); 172 173#endif /* CONFIG_PERFORMANCE_COUNTERS */ 174 175static int __init topology_init(void) 176{ 177 int cpu; 178 179 for_each_possible_cpu(cpu) { 180 struct cpu *c = &per_cpu(cpu_devices, cpu); 181 182 register_cpu(c, cpu); 183 184#ifdef CONFIG_PERFORMANCE_COUNTERS 185 sysdev_create_file(&c->sysdev, &attr_pc0event); 186 sysdev_create_file(&c->sysdev, &attr_pc0count); 187 sysdev_create_file(&c->sysdev, &attr_pc1event); 188 sysdev_create_file(&c->sysdev, &attr_pc1count); 189 sysdev_create_file(&c->sysdev, &attr_pccycles); 190 sysdev_create_file(&c->sysdev, &attr_pcenable); 191#endif 192 } 193 194 return 0; 195} 196 197subsys_initcall(topology_init); 198 199struct chip_id_map { 200 u16 mid; 201 u16 pn; 202 const char *name; 203}; 204 205static const struct chip_id_map chip_names[] = { 206 { .mid = 0x1f, .pn = 0x1e82, .name = "AT32AP700x" }, 207}; 208#define NR_CHIP_NAMES ARRAY_SIZE(chip_names) 209 210static const char *cpu_names[] = { 211 "Morgan", 212 "AP7", 213}; 214#define NR_CPU_NAMES ARRAY_SIZE(cpu_names) 215 216static const char *arch_names[] = { 217 "AVR32A", 218 "AVR32B", 219}; 220#define NR_ARCH_NAMES ARRAY_SIZE(arch_names) 221 222static const char *mmu_types[] = { 223 "No MMU", 224 "ITLB and DTLB", 225 "Shared TLB", 226 "MPU" 227}; 228 229static const char *cpu_feature_flags[] = { 230 "rmw", "dsp", "simd", "ocd", "perfctr", "java", "fpu", 231}; 232 233static const char *get_chip_name(struct avr32_cpuinfo *cpu) 234{ 235 unsigned int i; 236 unsigned int mid = avr32_get_manufacturer_id(cpu); 237 unsigned int pn = avr32_get_product_number(cpu); 238 239 for (i = 0; i < NR_CHIP_NAMES; i++) { 240 if (chip_names[i].mid == mid && chip_names[i].pn == pn) 241 return chip_names[i].name; 242 } 243 244 return "(unknown)"; 245} 246 247void __init setup_processor(void) 248{ 249 unsigned long config0, config1; 250 unsigned long features; 251 unsigned cpu_id, cpu_rev, arch_id, arch_rev, mmu_type; 252 unsigned device_id; 253 unsigned tmp; 254 unsigned i; 255 256 config0 = sysreg_read(CONFIG0); 257 config1 = sysreg_read(CONFIG1); 258 cpu_id = SYSREG_BFEXT(PROCESSORID, config0); 259 cpu_rev = SYSREG_BFEXT(PROCESSORREVISION, config0); 260 arch_id = SYSREG_BFEXT(AT, config0); 261 arch_rev = SYSREG_BFEXT(AR, config0); 262 mmu_type = SYSREG_BFEXT(MMUT, config0); 263 264 device_id = ocd_read(DID); 265 266 boot_cpu_data.arch_type = arch_id; 267 boot_cpu_data.cpu_type = cpu_id; 268 boot_cpu_data.arch_revision = arch_rev; 269 boot_cpu_data.cpu_revision = cpu_rev; 270 boot_cpu_data.tlb_config = mmu_type; 271 boot_cpu_data.device_id = device_id; 272 273 tmp = SYSREG_BFEXT(ILSZ, config1); 274 if (tmp) { 275 boot_cpu_data.icache.ways = 1 << SYSREG_BFEXT(IASS, config1); 276 boot_cpu_data.icache.sets = 1 << SYSREG_BFEXT(ISET, config1); 277 boot_cpu_data.icache.linesz = 1 << (tmp + 1); 278 } 279 tmp = SYSREG_BFEXT(DLSZ, config1); 280 if (tmp) { 281 boot_cpu_data.dcache.ways = 1 << SYSREG_BFEXT(DASS, config1); 282 boot_cpu_data.dcache.sets = 1 << SYSREG_BFEXT(DSET, config1); 283 boot_cpu_data.dcache.linesz = 1 << (tmp + 1); 284 } 285 286 if ((cpu_id >= NR_CPU_NAMES) || (arch_id >= NR_ARCH_NAMES)) { 287 printk ("Unknown CPU configuration (ID %02x, arch %02x), " 288 "continuing anyway...\n", 289 cpu_id, arch_id); 290 return; 291 } 292 293 printk ("CPU: %s chip revision %c\n", get_chip_name(&boot_cpu_data), 294 avr32_get_chip_revision(&boot_cpu_data) + 'A'); 295 printk ("CPU: %s [%02x] core revision %d (%s arch revision %d)\n", 296 cpu_names[cpu_id], cpu_id, cpu_rev, 297 arch_names[arch_id], arch_rev); 298 printk ("CPU: MMU configuration: %s\n", mmu_types[mmu_type]); 299 300 printk ("CPU: features:"); 301 features = 0; 302 if (config0 & SYSREG_BIT(CONFIG0_R)) 303 features |= AVR32_FEATURE_RMW; 304 if (config0 & SYSREG_BIT(CONFIG0_D)) 305 features |= AVR32_FEATURE_DSP; 306 if (config0 & SYSREG_BIT(CONFIG0_S)) 307 features |= AVR32_FEATURE_SIMD; 308 if (config0 & SYSREG_BIT(CONFIG0_O)) 309 features |= AVR32_FEATURE_OCD; 310 if (config0 & SYSREG_BIT(CONFIG0_P)) 311 features |= AVR32_FEATURE_PCTR; 312 if (config0 & SYSREG_BIT(CONFIG0_J)) 313 features |= AVR32_FEATURE_JAVA; 314 if (config0 & SYSREG_BIT(CONFIG0_F)) 315 features |= AVR32_FEATURE_FPU; 316 317 for (i = 0; i < ARRAY_SIZE(cpu_feature_flags); i++) 318 if (features & (1 << i)) 319 printk(" %s", cpu_feature_flags[i]); 320 321 printk("\n"); 322 boot_cpu_data.features = features; 323} 324 325#ifdef CONFIG_PROC_FS 326static int c_show(struct seq_file *m, void *v) 327{ 328 unsigned int icache_size, dcache_size; 329 unsigned int cpu = smp_processor_id(); 330 unsigned int freq; 331 unsigned int i; 332 333 icache_size = boot_cpu_data.icache.ways * 334 boot_cpu_data.icache.sets * 335 boot_cpu_data.icache.linesz; 336 dcache_size = boot_cpu_data.dcache.ways * 337 boot_cpu_data.dcache.sets * 338 boot_cpu_data.dcache.linesz; 339 340 seq_printf(m, "processor\t: %d\n", cpu); 341 342 seq_printf(m, "chip type\t: %s revision %c\n", 343 get_chip_name(&boot_cpu_data), 344 avr32_get_chip_revision(&boot_cpu_data) + 'A'); 345 if (boot_cpu_data.arch_type < NR_ARCH_NAMES) 346 seq_printf(m, "cpu arch\t: %s revision %d\n", 347 arch_names[boot_cpu_data.arch_type], 348 boot_cpu_data.arch_revision); 349 if (boot_cpu_data.cpu_type < NR_CPU_NAMES) 350 seq_printf(m, "cpu core\t: %s revision %d\n", 351 cpu_names[boot_cpu_data.cpu_type], 352 boot_cpu_data.cpu_revision); 353 354 freq = (clk_get_rate(boot_cpu_data.clk) + 500) / 1000; 355 seq_printf(m, "cpu MHz\t\t: %u.%03u\n", freq / 1000, freq % 1000); 356 357 seq_printf(m, "i-cache\t\t: %dK (%u ways x %u sets x %u)\n", 358 icache_size >> 10, 359 boot_cpu_data.icache.ways, 360 boot_cpu_data.icache.sets, 361 boot_cpu_data.icache.linesz); 362 seq_printf(m, "d-cache\t\t: %dK (%u ways x %u sets x %u)\n", 363 dcache_size >> 10, 364 boot_cpu_data.dcache.ways, 365 boot_cpu_data.dcache.sets, 366 boot_cpu_data.dcache.linesz); 367 368 seq_printf(m, "features\t:"); 369 for (i = 0; i < ARRAY_SIZE(cpu_feature_flags); i++) 370 if (boot_cpu_data.features & (1 << i)) 371 seq_printf(m, " %s", cpu_feature_flags[i]); 372 373 seq_printf(m, "\nbogomips\t: %lu.%02lu\n", 374 boot_cpu_data.loops_per_jiffy / (500000/HZ), 375 (boot_cpu_data.loops_per_jiffy / (5000/HZ)) % 100); 376 377 return 0; 378} 379 380static void *c_start(struct seq_file *m, loff_t *pos) 381{ 382 return *pos < 1 ? (void *)1 : NULL; 383} 384 385static void *c_next(struct seq_file *m, void *v, loff_t *pos) 386{ 387 ++*pos; 388 return NULL; 389} 390 391static void c_stop(struct seq_file *m, void *v) 392{ 393 394} 395 396const struct seq_operations cpuinfo_op = { 397 .start = c_start, 398 .next = c_next, 399 .stop = c_stop, 400 .show = c_show 401}; 402#endif /* CONFIG_PROC_FS */ 403