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 17#include <asm/setup.h> 18#include <asm/sysreg.h> 19 20static DEFINE_PER_CPU(struct cpu, cpu_devices); 21 22#ifdef CONFIG_PERFORMANCE_COUNTERS 23 24static ssize_t show_pc0event(struct sys_device *dev, char *buf) 25{ 26 unsigned long pccr; 27 28 pccr = sysreg_read(PCCR); 29 return sprintf(buf, "0x%lx\n", (pccr >> 12) & 0x3f); 30} 31static ssize_t store_pc0event(struct sys_device *dev, const char *buf, 32 size_t count) 33{ 34 unsigned long val; 35 char *endp; 36 37 val = simple_strtoul(buf, &endp, 0); 38 if (endp == buf || val > 0x3f) 39 return -EINVAL; 40 val = (val << 12) | (sysreg_read(PCCR) & 0xfffc0fff); 41 sysreg_write(PCCR, val); 42 return count; 43} 44static ssize_t show_pc0count(struct sys_device *dev, char *buf) 45{ 46 unsigned long pcnt0; 47 48 pcnt0 = sysreg_read(PCNT0); 49 return sprintf(buf, "%lu\n", pcnt0); 50} 51static ssize_t store_pc0count(struct sys_device *dev, const char *buf, 52 size_t count) 53{ 54 unsigned long val; 55 char *endp; 56 57 val = simple_strtoul(buf, &endp, 0); 58 if (endp == buf) 59 return -EINVAL; 60 sysreg_write(PCNT0, val); 61 62 return count; 63} 64 65static ssize_t show_pc1event(struct sys_device *dev, char *buf) 66{ 67 unsigned long pccr; 68 69 pccr = sysreg_read(PCCR); 70 return sprintf(buf, "0x%lx\n", (pccr >> 18) & 0x3f); 71} 72static ssize_t store_pc1event(struct sys_device *dev, const char *buf, 73 size_t count) 74{ 75 unsigned long val; 76 char *endp; 77 78 val = simple_strtoul(buf, &endp, 0); 79 if (endp == buf || val > 0x3f) 80 return -EINVAL; 81 val = (val << 18) | (sysreg_read(PCCR) & 0xff03ffff); 82 sysreg_write(PCCR, val); 83 return count; 84} 85static ssize_t show_pc1count(struct sys_device *dev, char *buf) 86{ 87 unsigned long pcnt1; 88 89 pcnt1 = sysreg_read(PCNT1); 90 return sprintf(buf, "%lu\n", pcnt1); 91} 92static ssize_t store_pc1count(struct sys_device *dev, const char *buf, 93 size_t count) 94{ 95 unsigned long val; 96 char *endp; 97 98 val = simple_strtoul(buf, &endp, 0); 99 if (endp == buf) 100 return -EINVAL; 101 sysreg_write(PCNT1, val); 102 103 return count; 104} 105 106static ssize_t show_pccycles(struct sys_device *dev, char *buf) 107{ 108 unsigned long pccnt; 109 110 pccnt = sysreg_read(PCCNT); 111 return sprintf(buf, "%lu\n", pccnt); 112} 113static ssize_t store_pccycles(struct sys_device *dev, const char *buf, 114 size_t count) 115{ 116 unsigned long val; 117 char *endp; 118 119 val = simple_strtoul(buf, &endp, 0); 120 if (endp == buf) 121 return -EINVAL; 122 sysreg_write(PCCNT, val); 123 124 return count; 125} 126 127static ssize_t show_pcenable(struct sys_device *dev, char *buf) 128{ 129 unsigned long pccr; 130 131 pccr = sysreg_read(PCCR); 132 return sprintf(buf, "%c\n", (pccr & 1)?'1':'0'); 133} 134static ssize_t store_pcenable(struct sys_device *dev, const char *buf, 135 size_t count) 136{ 137 unsigned long pccr, val; 138 char *endp; 139 140 val = simple_strtoul(buf, &endp, 0); 141 if (endp == buf) 142 return -EINVAL; 143 if (val) 144 val = 1; 145 146 pccr = sysreg_read(PCCR); 147 pccr = (pccr & ~1UL) | val; 148 sysreg_write(PCCR, pccr); 149 150 return count; 151} 152 153static SYSDEV_ATTR(pc0event, 0600, show_pc0event, store_pc0event); 154static SYSDEV_ATTR(pc0count, 0600, show_pc0count, store_pc0count); 155static SYSDEV_ATTR(pc1event, 0600, show_pc1event, store_pc1event); 156static SYSDEV_ATTR(pc1count, 0600, show_pc1count, store_pc1count); 157static SYSDEV_ATTR(pccycles, 0600, show_pccycles, store_pccycles); 158static SYSDEV_ATTR(pcenable, 0600, show_pcenable, store_pcenable); 159 160#endif /* CONFIG_PERFORMANCE_COUNTERS */ 161 162static int __init topology_init(void) 163{ 164 int cpu; 165 166 for_each_possible_cpu(cpu) { 167 struct cpu *c = &per_cpu(cpu_devices, cpu); 168 169 register_cpu(c, cpu); 170 171#ifdef CONFIG_PERFORMANCE_COUNTERS 172 sysdev_create_file(&c->sysdev, &attr_pc0event); 173 sysdev_create_file(&c->sysdev, &attr_pc0count); 174 sysdev_create_file(&c->sysdev, &attr_pc1event); 175 sysdev_create_file(&c->sysdev, &attr_pc1count); 176 sysdev_create_file(&c->sysdev, &attr_pccycles); 177 sysdev_create_file(&c->sysdev, &attr_pcenable); 178#endif 179 } 180 181 return 0; 182} 183 184subsys_initcall(topology_init); 185 186static const char *cpu_names[] = { 187 "Morgan", 188 "AP7000", 189}; 190#define NR_CPU_NAMES ARRAY_SIZE(cpu_names) 191 192static const char *arch_names[] = { 193 "AVR32A", 194 "AVR32B", 195}; 196#define NR_ARCH_NAMES ARRAY_SIZE(arch_names) 197 198static const char *mmu_types[] = { 199 "No MMU", 200 "ITLB and DTLB", 201 "Shared TLB", 202 "MPU" 203}; 204 205void __init setup_processor(void) 206{ 207 unsigned long config0, config1; 208 unsigned long features; 209 unsigned cpu_id, cpu_rev, arch_id, arch_rev, mmu_type; 210 unsigned tmp; 211 212 config0 = sysreg_read(CONFIG0); 213 config1 = sysreg_read(CONFIG1); 214 cpu_id = SYSREG_BFEXT(PROCESSORID, config0); 215 cpu_rev = SYSREG_BFEXT(PROCESSORREVISION, config0); 216 arch_id = SYSREG_BFEXT(AT, config0); 217 arch_rev = SYSREG_BFEXT(AR, config0); 218 mmu_type = SYSREG_BFEXT(MMUT, config0); 219 220 boot_cpu_data.arch_type = arch_id; 221 boot_cpu_data.cpu_type = cpu_id; 222 boot_cpu_data.arch_revision = arch_rev; 223 boot_cpu_data.cpu_revision = cpu_rev; 224 boot_cpu_data.tlb_config = mmu_type; 225 226 tmp = SYSREG_BFEXT(ILSZ, config1); 227 if (tmp) { 228 boot_cpu_data.icache.ways = 1 << SYSREG_BFEXT(IASS, config1); 229 boot_cpu_data.icache.sets = 1 << SYSREG_BFEXT(ISET, config1); 230 boot_cpu_data.icache.linesz = 1 << (tmp + 1); 231 } 232 tmp = SYSREG_BFEXT(DLSZ, config1); 233 if (tmp) { 234 boot_cpu_data.dcache.ways = 1 << SYSREG_BFEXT(DASS, config1); 235 boot_cpu_data.dcache.sets = 1 << SYSREG_BFEXT(DSET, config1); 236 boot_cpu_data.dcache.linesz = 1 << (tmp + 1); 237 } 238 239 if ((cpu_id >= NR_CPU_NAMES) || (arch_id >= NR_ARCH_NAMES)) { 240 printk ("Unknown CPU configuration (ID %02x, arch %02x), " 241 "continuing anyway...\n", 242 cpu_id, arch_id); 243 return; 244 } 245 246 printk ("CPU: %s [%02x] revision %d (%s revision %d)\n", 247 cpu_names[cpu_id], cpu_id, cpu_rev, 248 arch_names[arch_id], arch_rev); 249 printk ("CPU: MMU configuration: %s\n", mmu_types[mmu_type]); 250 251 printk ("CPU: features:"); 252 features = 0; 253 if (config0 & SYSREG_BIT(CONFIG0_R)) { 254 features |= AVR32_FEATURE_RMW; 255 printk(" rmw"); 256 } 257 if (config0 & SYSREG_BIT(CONFIG0_D)) { 258 features |= AVR32_FEATURE_DSP; 259 printk(" dsp"); 260 } 261 if (config0 & SYSREG_BIT(CONFIG0_S)) { 262 features |= AVR32_FEATURE_SIMD; 263 printk(" simd"); 264 } 265 if (config0 & SYSREG_BIT(CONFIG0_O)) { 266 features |= AVR32_FEATURE_OCD; 267 printk(" ocd"); 268 } 269 if (config0 & SYSREG_BIT(CONFIG0_P)) { 270 features |= AVR32_FEATURE_PCTR; 271 printk(" perfctr"); 272 } 273 if (config0 & SYSREG_BIT(CONFIG0_J)) { 274 features |= AVR32_FEATURE_JAVA; 275 printk(" java"); 276 } 277 if (config0 & SYSREG_BIT(CONFIG0_F)) { 278 features |= AVR32_FEATURE_FPU; 279 printk(" fpu"); 280 } 281 printk("\n"); 282 boot_cpu_data.features = features; 283} 284 285#ifdef CONFIG_PROC_FS 286static int c_show(struct seq_file *m, void *v) 287{ 288 unsigned int icache_size, dcache_size; 289 unsigned int cpu = smp_processor_id(); 290 291 icache_size = boot_cpu_data.icache.ways * 292 boot_cpu_data.icache.sets * 293 boot_cpu_data.icache.linesz; 294 dcache_size = boot_cpu_data.dcache.ways * 295 boot_cpu_data.dcache.sets * 296 boot_cpu_data.dcache.linesz; 297 298 seq_printf(m, "processor\t: %d\n", cpu); 299 300 if (boot_cpu_data.arch_type < NR_ARCH_NAMES) 301 seq_printf(m, "cpu family\t: %s revision %d\n", 302 arch_names[boot_cpu_data.arch_type], 303 boot_cpu_data.arch_revision); 304 if (boot_cpu_data.cpu_type < NR_CPU_NAMES) 305 seq_printf(m, "cpu type\t: %s revision %d\n", 306 cpu_names[boot_cpu_data.cpu_type], 307 boot_cpu_data.cpu_revision); 308 309 seq_printf(m, "i-cache\t\t: %dK (%u ways x %u sets x %u)\n", 310 icache_size >> 10, 311 boot_cpu_data.icache.ways, 312 boot_cpu_data.icache.sets, 313 boot_cpu_data.icache.linesz); 314 seq_printf(m, "d-cache\t\t: %dK (%u ways x %u sets x %u)\n", 315 dcache_size >> 10, 316 boot_cpu_data.dcache.ways, 317 boot_cpu_data.dcache.sets, 318 boot_cpu_data.dcache.linesz); 319 seq_printf(m, "bogomips\t: %lu.%02lu\n", 320 boot_cpu_data.loops_per_jiffy / (500000/HZ), 321 (boot_cpu_data.loops_per_jiffy / (5000/HZ)) % 100); 322 323 return 0; 324} 325 326static void *c_start(struct seq_file *m, loff_t *pos) 327{ 328 return *pos < 1 ? (void *)1 : NULL; 329} 330 331static void *c_next(struct seq_file *m, void *v, loff_t *pos) 332{ 333 ++*pos; 334 return NULL; 335} 336 337static void c_stop(struct seq_file *m, void *v) 338{ 339 340} 341 342struct seq_operations cpuinfo_op = { 343 .start = c_start, 344 .next = c_next, 345 .stop = c_stop, 346 .show = c_show 347}; 348#endif /* CONFIG_PROC_FS */ 349