/* * Processor capabilities determination functions. * * Copyright (C) xxxx the Anonymous * Copyright (C) 2003 Maciej W. Rozycki * Copyright (C) 1994 - 2003 Ralf Baechle * Copyright (C) 2001 MIPS Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_AVM_POWER #include #endif /*--- #ifdef CONFIG_AVM_POWER ---*/ #include static int avm_idle_mode; static int avm_idle_mode_speedup; static int avm_prev_idle_mode_speedup; static int avm_idle_lock_speedup1; static int avm_idle_lock_speedup2; static int avm_idle_supported_speedup; static int avm_idle_timestamp; #define TIME_DIFF(t1, t2) (((t2) > (t1)) ? (t2 - t1) : (t1 - t2)) #define TIME_SLOWPOWER (60 * HZ) /*--- in Sekunden ---*/ #define TIME_SPEEDPOWER (10 * HZ) /*--- in Sekunden ---*/ #define LOW_SPEED_IDLE 50 #define NORMAL_SPEED_IDLE 25 #define HIGH_SPEED_IDLE 10 /* * Not all of the MIPS CPUs have the "wait" instruction available. Moreover, * the implementation of the "wait" feature differs between CPU families. This * points to the function that implements CPU specific wait. * The wait instruction stops the pipeline and reduces the power consumption of * the CPU very much. */ void (*cpu_wait)(void) = NULL; static void r3081_wait(void) { unsigned long cfg = read_c0_conf(); write_c0_conf(cfg | R30XX_CONF_HALT); } static void r39xx_wait(void) { unsigned long cfg = read_c0_conf(); write_c0_conf(cfg | TX39_CONF_HALT); } /*--------------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------------*/ static inline void r4k_idle_control(int p_idle, int p_run) { switch(avm_idle_mode) { case 1: case 3: case 5: case 7: /*--- printk("r4k_wait: sum %u wait %u run %u\n", r4k_sum_count, r4k_wait_count, r4k_run_count); ---*/ /*--- printk("[idle]: %u%% (run %u%%)\n", p_idle, p_run); ---*/ printk("[idle]: %u%% (run %u%%) dt=%d s=%x\n", p_idle, p_run, TIME_DIFF(avm_idle_timestamp, (int)jiffies), (unsigned int)avm_idle_mode_speedup); break; } #ifdef CONFIG_AVM_POWER #ifdef CONFIG_AVM_POWERMETER PowerManagmentRessourceInfo(powerdevice_loadrate, max(0,100 - p_idle)); #endif/*--- #ifdef CONFIG_AVM_POWERMETER ---*/ switch(avm_idle_mode_speedup) { case 0: /*--- 125 MHz ---*/ if(avm_prev_idle_mode_speedup != avm_idle_mode_speedup) { avm_prev_idle_mode_speedup = avm_idle_mode_speedup; printk("[speedup] -> 125 MHz\n"); } if(p_idle < HIGH_SPEED_IDLE) { if(avm_idle_lock_speedup2) { /*--- kein Hochschalten erlauben - Timer neu aufsetzen ---*/ avm_idle_timestamp = jiffies; break; } /*--- Hochschalten auf 150 MHz ---*/ if(avm_idle_supported_speedup & 1) { /*--- printk("[speedup] -> try fast\n"); ---*/ PowerManagmentActivatePowerMode("fastspeed"); } } else if(p_idle < LOW_SPEED_IDLE) { /*--- Timer aufsetzen fuer slowspeed ---*/ avm_idle_timestamp = jiffies; } else { if(avm_idle_lock_speedup1 || avm_idle_lock_speedup2) { /*--- kein Herunterschalten erlauben - Timer neu aufsetzen ---*/ avm_idle_timestamp = jiffies; break; } if(TIME_DIFF(avm_idle_timestamp, jiffies) > TIME_SLOWPOWER) { /*--- Zeit verstrichen, nun SlowSpeed ---*/ if(avm_idle_supported_speedup & 2) { /*--- printk("[speedup] -> try slow\n"); ---*/ PowerManagmentActivatePowerMode("slowspeed"); } } } break; case 1: /*--- 150 MHz ---*/ if(avm_prev_idle_mode_speedup != avm_idle_mode_speedup) { avm_prev_idle_mode_speedup = avm_idle_mode_speedup; printk("[speedup] -> 150 MHz\n"); } if(p_idle < HIGH_SPEED_IDLE) { /*--- Timer aufsetzen fuer normalspeed ---*/ avm_idle_timestamp = jiffies; } else if(p_idle > NORMAL_SPEED_IDLE) { if(avm_idle_lock_speedup1 || avm_idle_lock_speedup2) { /*--- kein Herunterschalten erlauben - Timer neu aufsetzen ---*/ avm_idle_timestamp = jiffies; break; } if(TIME_DIFF(avm_idle_timestamp, jiffies) > TIME_SPEEDPOWER) { /*--- Zeit verstrichen, nun NormalSpeed ---*/ /*--- printk("[speedup] -> normal from speed\n"); ---*/ PowerManagmentActivatePowerMode("normalspeed"); avm_idle_timestamp = jiffies; } } break; case 2: /*--- 62.5 MHz ---*/ if(avm_prev_idle_mode_speedup != avm_idle_mode_speedup) { avm_prev_idle_mode_speedup = avm_idle_mode_speedup; printk("[speedup] -> 62.5 MHz\n"); } if(p_idle < LOW_SPEED_IDLE) { /*--- Hochschalten auf 125 MHz ---*/ /*--- printk("[speedup] -> normal from slow\n"); ---*/ PowerManagmentActivatePowerMode("normalspeed"); avm_idle_timestamp = jiffies; } break; } #endif /*--- #ifdef CONFIG_AVM_POWER ---*/ } /*--------------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------------*/ static unsigned int last_value = 0; static unsigned int last_value_done = 0; unsigned int r4k_wait_count, r4k_run_count, r4k_sum_count; /*--------------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------------*/ int r4k_wait_end(void) { if(last_value_done == 0) { int count, tmp; last_value_done = 1; count = get_cycles(); if(last_value < count) tmp = count - last_value ; else tmp = last_value - count; r4k_wait_count += tmp; r4k_sum_count += tmp; last_value = count; return 0; } return 1; } /*--------------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------------*/ static void r4k_wait_idle(void) { unsigned int count, tmp; count = get_cycles(); if(last_value) { if(last_value < count) tmp = count - last_value ; else tmp = last_value - count; r4k_run_count += tmp; r4k_sum_count += tmp; } last_value = count; last_value_done = 0; __asm__(".set\tmips3\n\t" "wait\n\t" ".set\tmips0"); if(r4k_wait_end() == 0) { /*--- printk("?"); ---*/ } if(r4k_sum_count > (1 << 28)) { int p_idle = ((r4k_wait_count >> 8) * 100) / (r4k_sum_count >> 8); int p_run = ((r4k_run_count >> 8) * 100) / (r4k_sum_count >> 8); r4k_idle_control(p_idle, p_run); /*--- r4k_sum_count >>= 1; ---*/ /*--- r4k_wait_count >>= 1; ---*/ /*--- r4k_run_count >>= 1; ---*/ r4k_sum_count >>= 3; r4k_wait_count >>= 3; r4k_run_count >>= 3; } } /*------------------------------------------------------------------------------------------*\ * Wird vom Power-Treiber aufgrufen * state: * 0 normal * 1 fast * 2 slow * 0x8x setze clock (0,1,2) - ab jetzt kein idleabhängiges Speedup-Control * 0x10x Setzen der unterstuetzten Modi: (1 Fast, 2 Slow (verodern)) * 0x18x Setzen der unterstuezten Modi - allerdings nur manuelle Switching (idleunabhaengig) * 0x201 kein speed-down mehr - nur noch speedup (Telefonapplikation) * 0x200 speed-down entlocken * 0x40x Abfrage ob diese entsprechende Speedup erlaubt * 0x801 kein speedup/down (USB-Treiber) * 0x800 speed-down entlocken * 0x1001 kein speedup/down (ATM-Treiber) * 0x1000 speed-down entlocken * Initial sind Änderungen an der Clock nicht freigeschaltet \*------------------------------------------------------------------------------------------*/ #ifdef CONFIG_AVM_POWER int speedup_CallBackPowerManagmentControl (int state) { unsigned new_system_freq = 0; if(state & 0x400) { if(state & avm_idle_supported_speedup) { /*--- Speedup wird unterstützt ---*/ return 0; } return 1; } if(state & 0x200) { avm_idle_timestamp = jiffies; avm_idle_lock_speedup1 = state & 1; /*--- speed-down verboten (nur noch speedup) ---*/ return 0; } if(state & 0x800) { avm_idle_timestamp = jiffies; if(state & 1) { avm_idle_lock_speedup2 |= 0x800; /*--- speed-up/down verboten ---*/ } else { avm_idle_lock_speedup2 &= ~0x800; /*--- speed-up/down an ---*/ } return 0; } if(state & 0x1000) { avm_idle_timestamp = jiffies; if(state & 1) { avm_idle_lock_speedup2 |= 0x1000; /*--- speed-up/down verboten ---*/ } else { avm_idle_lock_speedup2 &= ~0x1000; /*--- speed-up/down an ---*/ } return 0; } if(state & 0x100) { avm_idle_supported_speedup = (state & ~0x100); if(state & 0x80) { /*--- jetzt idle-unabhaengig aber alten state merken ---*/ avm_idle_mode_speedup |= 0x80; } else { /*--- letzten State reseten ---*/ avm_idle_mode_speedup &= ~0x80; } return 0; } if(avm_idle_supported_speedup & 0x80) { /*--- keine idle-Abhängigkeit aber state merken ---*/ avm_idle_mode_speedup = state | 0x80; } else { /*--- idle-Abhaengigkeit ---*/ avm_idle_mode_speedup = state; } state &= ~0x80; if(state == 0) { new_system_freq = 125000000; } else if(state == 1) { if(avm_idle_supported_speedup & 1) { new_system_freq = 150000000; } } else if(state == 2) { if(avm_idle_supported_speedup & 2) { new_system_freq = 62500000; } } if(new_system_freq && (new_system_freq != avm_get_clock(avm_clock_id_system))) { avm_set_clock(avm_clock_id_system, new_system_freq); } return 0; } #endif /*--- #ifdef CONFIG_AVM_POWER ---*/ /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #ifdef CONFIG_AVM_POWER int __init speedup_init(void) { avm_idle_timestamp = jiffies; PowerManagmentRegister("speedup", speedup_CallBackPowerManagmentControl); return 0; } late_initcall(speedup_init); #endif /*--- #ifdef CONFIG_AVM_POWER ---*/ static void r4k_wait(void) { __asm__(".set\tmips3\n\t" "wait\n\t" ".set\tmips0"); } /* * The Au1xxx wait is available only if we run CONFIG_PM and * the timer setup found we had a 32KHz counter available. * There are still problems with functions that may call au1k_wait * directly, but that will be discovered pretty quickly. */ extern void (*au1k_wait_ptr)(void); void au1k_wait(void) { #ifdef CONFIG_PM /* using the wait instruction makes CP0 counter unusable */ __asm__(".set\tmips3\n\t" "wait\n\t" "nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t" ".set\tmips0"); #else __asm__("nop\n\t" "nop"); #endif } static inline void check_wait(void) { struct cpuinfo_mips *c = ¤t_cpu_data; printk("Checking for 'wait' instruction... "); switch (c->cputype) { case CPU_R3081: case CPU_R3081E: cpu_wait = r3081_wait; printk(" available.\n"); break; case CPU_TX3927: cpu_wait = r39xx_wait; printk(" available.\n"); break; case CPU_R4200: /* case CPU_R4300: */ case CPU_R4600: case CPU_R4640: case CPU_R4650: case CPU_R4700: case CPU_R5000: case CPU_NEVADA: case CPU_RM7000: case CPU_RM9000: case CPU_TX49XX: case CPU_4KC: case CPU_4KEC: case CPU_4KSC: case CPU_5KC: /* case CPU_20KC:*/ case CPU_24K: case CPU_25KF: { if(strstr(prom_getcmdline(), "idle")) { char *p; avm_idle_mode = 1; p = strstr(prom_getcmdline(), "idle="); if(p) { extern int avm_vlync_double_speed; avm_idle_mode = simple_strtol(p + sizeof("idle=") - 1, NULL, 0); printk("[speedup] idle_mode = %u\n", avm_idle_mode); switch(avm_idle_mode) { case 6: case 7: avm_vlync_double_speed = 1; break; } } cpu_wait = r4k_wait_idle; printk(" with idle values available.\n"); } else { cpu_wait = r4k_wait; printk(" available.\n"); } } cpu_wait = r4k_wait_idle; break; #ifdef CONFIG_PM case CPU_AU1000: case CPU_AU1100: case CPU_AU1500: if (au1k_wait_ptr != NULL) { cpu_wait = au1k_wait_ptr; printk(" available.\n"); } else { printk(" unavailable.\n"); } break; #endif default: printk(" unavailable.\n"); break; } } void __init check_bugs32(void) { check_wait(); } /* * Probe whether cpu has config register by trying to play with * alternate cache bit and see whether it matters. * It's used by cpu_probe to distinguish between R3000A and R3081. */ static inline int cpu_has_confreg(void) { #ifdef CONFIG_CPU_R3000 extern unsigned long r3k_cache_size(unsigned long); unsigned long size1, size2; unsigned long cfg = read_c0_conf(); size1 = r3k_cache_size(ST0_ISC); write_c0_conf(cfg ^ R30XX_CONF_AC); size2 = r3k_cache_size(ST0_ISC); write_c0_conf(cfg); return size1 != size2; #else return 0; #endif } /* * Get the FPU Implementation/Revision. */ static inline unsigned long cpu_get_fpu_id(void) { unsigned long tmp, fpu_id; tmp = read_c0_status(); __enable_fpu(); fpu_id = read_32bit_cp1_register(CP1_REVISION); write_c0_status(tmp); return fpu_id; } /* * Check the CPU has an FPU the official way. */ static inline int __cpu_has_fpu(void) { return ((cpu_get_fpu_id() & 0xff00) != FPIR_IMP_NONE); } #define R4K_OPTS (MIPS_CPU_TLB | MIPS_CPU_4KEX | MIPS_CPU_4KTLB \ | MIPS_CPU_COUNTER) static inline void cpu_probe_legacy(struct cpuinfo_mips *c) { switch (c->processor_id & 0xff00) { case PRID_IMP_R2000: c->cputype = CPU_R2000; c->isa_level = MIPS_CPU_ISA_I; c->options = MIPS_CPU_TLB | MIPS_CPU_NOFPUEX; if (__cpu_has_fpu()) c->options |= MIPS_CPU_FPU; c->tlbsize = 64; break; case PRID_IMP_R3000: if ((c->processor_id & 0xff) == PRID_REV_R3000A) if (cpu_has_confreg()) c->cputype = CPU_R3081E; else c->cputype = CPU_R3000A; else c->cputype = CPU_R3000; c->isa_level = MIPS_CPU_ISA_I; c->options = MIPS_CPU_TLB | MIPS_CPU_NOFPUEX; if (__cpu_has_fpu()) c->options |= MIPS_CPU_FPU; c->tlbsize = 64; break; case PRID_IMP_R4000: if (read_c0_config() & CONF_SC) { if ((c->processor_id & 0xff) >= PRID_REV_R4400) c->cputype = CPU_R4400PC; else c->cputype = CPU_R4000PC; } else { if ((c->processor_id & 0xff) >= PRID_REV_R4400) c->cputype = CPU_R4400SC; else c->cputype = CPU_R4000SC; } c->isa_level = MIPS_CPU_ISA_III; c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | MIPS_CPU_WATCH | MIPS_CPU_VCE | MIPS_CPU_LLSC; c->tlbsize = 48; break; case PRID_IMP_VR41XX: switch (c->processor_id & 0xf0) { #ifndef CONFIG_VR4181 case PRID_REV_VR4111: c->cputype = CPU_VR4111; break; #else case PRID_REV_VR4181: c->cputype = CPU_VR4181; break; #endif case PRID_REV_VR4121: c->cputype = CPU_VR4121; break; case PRID_REV_VR4122: if ((c->processor_id & 0xf) < 0x3) c->cputype = CPU_VR4122; else c->cputype = CPU_VR4181A; break; case PRID_REV_VR4130: if ((c->processor_id & 0xf) < 0x4) c->cputype = CPU_VR4131; else c->cputype = CPU_VR4133; break; default: printk(KERN_INFO "Unexpected CPU of NEC VR4100 series\n"); c->cputype = CPU_VR41XX; break; } c->isa_level = MIPS_CPU_ISA_III; c->options = R4K_OPTS; c->tlbsize = 32; break; case PRID_IMP_R4300: c->cputype = CPU_R4300; c->isa_level = MIPS_CPU_ISA_III; c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | MIPS_CPU_LLSC; c->tlbsize = 32; break; case PRID_IMP_R4600: c->cputype = CPU_R4600; c->isa_level = MIPS_CPU_ISA_III; c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_LLSC; c->tlbsize = 48; break; #if 0 case PRID_IMP_R4650: /* * This processor doesn't have an MMU, so it's not * "real easy" to run Linux on it. It is left purely * for documentation. Commented out because it shares * it's c0_prid id number with the TX3900. */ c->cputype = CPU_R4650; c->isa_level = MIPS_CPU_ISA_III; c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_LLSC; c->tlbsize = 48; break; #endif case PRID_IMP_TX39: c->isa_level = MIPS_CPU_ISA_I; c->options = MIPS_CPU_TLB; if ((c->processor_id & 0xf0) == (PRID_REV_TX3927 & 0xf0)) { c->cputype = CPU_TX3927; c->tlbsize = 64; } else { switch (c->processor_id & 0xff) { case PRID_REV_TX3912: c->cputype = CPU_TX3912; c->tlbsize = 32; break; case PRID_REV_TX3922: c->cputype = CPU_TX3922; c->tlbsize = 64; break; default: c->cputype = CPU_UNKNOWN; break; } } break; case PRID_IMP_R4700: c->cputype = CPU_R4700; c->isa_level = MIPS_CPU_ISA_III; c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | MIPS_CPU_LLSC; c->tlbsize = 48; break; case PRID_IMP_TX49: c->cputype = CPU_TX49XX; c->isa_level = MIPS_CPU_ISA_III; c->options = R4K_OPTS | MIPS_CPU_LLSC; if (!(c->processor_id & 0x08)) c->options |= MIPS_CPU_FPU | MIPS_CPU_32FPR; c->tlbsize = 48; break; case PRID_IMP_R5000: c->cputype = CPU_R5000; c->isa_level = MIPS_CPU_ISA_IV; c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | MIPS_CPU_LLSC; c->tlbsize = 48; break; case PRID_IMP_R5432: c->cputype = CPU_R5432; c->isa_level = MIPS_CPU_ISA_IV; c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | MIPS_CPU_WATCH | MIPS_CPU_LLSC; c->tlbsize = 48; break; case PRID_IMP_R5500: c->cputype = CPU_R5500; c->isa_level = MIPS_CPU_ISA_IV; c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | MIPS_CPU_WATCH | MIPS_CPU_LLSC; c->tlbsize = 48; break; case PRID_IMP_NEVADA: c->cputype = CPU_NEVADA; c->isa_level = MIPS_CPU_ISA_IV; c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | MIPS_CPU_DIVEC | MIPS_CPU_LLSC; c->tlbsize = 48; break; case PRID_IMP_R6000: c->cputype = CPU_R6000; c->isa_level = MIPS_CPU_ISA_II; c->options = MIPS_CPU_TLB | MIPS_CPU_FPU | MIPS_CPU_LLSC; c->tlbsize = 32; break; case PRID_IMP_R6000A: c->cputype = CPU_R6000A; c->isa_level = MIPS_CPU_ISA_II; c->options = MIPS_CPU_TLB | MIPS_CPU_FPU | MIPS_CPU_LLSC; c->tlbsize = 32; break; case PRID_IMP_RM7000: c->cputype = CPU_RM7000; c->isa_level = MIPS_CPU_ISA_IV; c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | MIPS_CPU_LLSC; /* * Undocumented RM7000: Bit 29 in the info register of * the RM7000 v2.0 indicates if the TLB has 48 or 64 * entries. * * 29 1 => 64 entry JTLB * 0 => 48 entry JTLB */ c->tlbsize = (read_c0_info() & (1 << 29)) ? 64 : 48; break; case PRID_IMP_RM9000: c->cputype = CPU_RM9000; c->isa_level = MIPS_CPU_ISA_IV; c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | MIPS_CPU_LLSC; /* * Bit 29 in the info register of the RM9000 * indicates if the TLB has 48 or 64 entries. * * 29 1 => 64 entry JTLB * 0 => 48 entry JTLB */ c->tlbsize = (read_c0_info() & (1 << 29)) ? 64 : 48; break; case PRID_IMP_R8000: c->cputype = CPU_R8000; c->isa_level = MIPS_CPU_ISA_IV; c->options = MIPS_CPU_TLB | MIPS_CPU_4KEX | MIPS_CPU_FPU | MIPS_CPU_32FPR | MIPS_CPU_LLSC; c->tlbsize = 384; /* has weird TLB: 3-way x 128 */ break; case PRID_IMP_R10000: c->cputype = CPU_R10000; c->isa_level = MIPS_CPU_ISA_IV; c->options = MIPS_CPU_TLB | MIPS_CPU_4KEX | MIPS_CPU_FPU | MIPS_CPU_32FPR | MIPS_CPU_COUNTER | MIPS_CPU_WATCH | MIPS_CPU_LLSC; c->tlbsize = 64; break; case PRID_IMP_R12000: c->cputype = CPU_R12000; c->isa_level = MIPS_CPU_ISA_IV; c->options = MIPS_CPU_TLB | MIPS_CPU_4KEX | MIPS_CPU_FPU | MIPS_CPU_32FPR | MIPS_CPU_COUNTER | MIPS_CPU_WATCH | MIPS_CPU_LLSC; c->tlbsize = 64; break; } } static inline void decode_config1(struct cpuinfo_mips *c) { unsigned long config0 = read_c0_config(); unsigned long config1; if ((config0 & (1 << 31)) == 0) return; /* actually wort a panic() */ /* MIPS32 or MIPS64 compliant CPU. Read Config 1 register. */ c->options = MIPS_CPU_TLB | MIPS_CPU_4KEX | MIPS_CPU_4KTLB | MIPS_CPU_COUNTER | MIPS_CPU_DIVEC | MIPS_CPU_LLSC | MIPS_CPU_MCHECK; config1 = read_c0_config1(); if (config1 & (1 << 3)) c->options |= MIPS_CPU_WATCH; if (config1 & (1 << 2)) c->options |= MIPS_CPU_MIPS16; if (config1 & (1 << 1)) c->options |= MIPS_CPU_EJTAG; if (config1 & 1) { c->options |= MIPS_CPU_FPU; c->options |= MIPS_CPU_32FPR; } c->scache.flags = MIPS_CACHE_NOT_PRESENT; c->tlbsize = ((config1 >> 25) & 0x3f) + 1; } static inline void cpu_probe_mips(struct cpuinfo_mips *c) { decode_config1(c); switch (c->processor_id & 0xff00) { case PRID_IMP_4KC: c->cputype = CPU_4KC; c->isa_level = MIPS_CPU_ISA_M32; break; case PRID_IMP_4KEC: c->cputype = CPU_4KEC; c->isa_level = MIPS_CPU_ISA_M32; break; case PRID_IMP_4KECR2: c->cputype = CPU_4KEC; c->isa_level = MIPS_CPU_ISA_M32; break; case PRID_IMP_4KSC: c->cputype = CPU_4KSC; c->isa_level = MIPS_CPU_ISA_M32; break; case PRID_IMP_5KC: c->cputype = CPU_5KC; c->isa_level = MIPS_CPU_ISA_M64; break; case PRID_IMP_20KC: c->cputype = CPU_20KC; c->isa_level = MIPS_CPU_ISA_M64; break; case PRID_IMP_24K: c->cputype = CPU_24K; c->isa_level = MIPS_CPU_ISA_M32; break; case PRID_IMP_25KF: c->cputype = CPU_25KF; c->isa_level = MIPS_CPU_ISA_M64; /* Probe for L2 cache */ c->scache.flags &= ~MIPS_CACHE_NOT_PRESENT; break; } } static inline void cpu_probe_alchemy(struct cpuinfo_mips *c) { decode_config1(c); switch (c->processor_id & 0xff00) { case PRID_IMP_AU1_REV1: case PRID_IMP_AU1_REV2: switch ((c->processor_id >> 24) & 0xff) { case 0: c->cputype = CPU_AU1000; break; case 1: c->cputype = CPU_AU1500; break; case 2: c->cputype = CPU_AU1100; break; case 3: c->cputype = CPU_AU1550; break; default: panic("Unknown Au Core!"); break; } c->isa_level = MIPS_CPU_ISA_M32; break; } } static inline void cpu_probe_sibyte(struct cpuinfo_mips *c) { decode_config1(c); switch (c->processor_id & 0xff00) { case PRID_IMP_SB1: c->cputype = CPU_SB1; c->isa_level = MIPS_CPU_ISA_M64; c->options = MIPS_CPU_TLB | MIPS_CPU_4KEX | MIPS_CPU_COUNTER | MIPS_CPU_DIVEC | MIPS_CPU_MCHECK | MIPS_CPU_EJTAG | MIPS_CPU_WATCH | MIPS_CPU_LLSC; #ifndef CONFIG_SB1_PASS_1_WORKAROUNDS /* FPU in pass1 is known to have issues. */ c->options |= MIPS_CPU_FPU | MIPS_CPU_32FPR; #endif break; } } static inline void cpu_probe_sandcraft(struct cpuinfo_mips *c) { decode_config1(c); switch (c->processor_id & 0xff00) { case PRID_IMP_SR71000: c->cputype = CPU_SR71000; c->isa_level = MIPS_CPU_ISA_M64; c->options = MIPS_CPU_TLB | MIPS_CPU_4KEX | MIPS_CPU_4KTLB | MIPS_CPU_FPU | MIPS_CPU_COUNTER | MIPS_CPU_MCHECK; c->scache.ways = 8; c->tlbsize = 64; break; } } __init void cpu_probe(void) { struct cpuinfo_mips *c = ¤t_cpu_data; c->processor_id = PRID_IMP_UNKNOWN; c->fpu_id = FPIR_IMP_NONE; c->cputype = CPU_UNKNOWN; c->processor_id = read_c0_prid(); switch (c->processor_id & 0xff0000) { case PRID_COMP_LEGACY: cpu_probe_legacy(c); break; case PRID_COMP_MIPS: cpu_probe_mips(c); break; case PRID_COMP_ALCHEMY: cpu_probe_alchemy(c); break; case PRID_COMP_SIBYTE: cpu_probe_sibyte(c); break; case PRID_COMP_SANDCRAFT: cpu_probe_sandcraft(c); break; default: c->cputype = CPU_UNKNOWN; } if (c->options & MIPS_CPU_FPU) c->fpu_id = cpu_get_fpu_id(); } __init void cpu_report(void) { struct cpuinfo_mips *c = ¤t_cpu_data; printk("CPU revision is: %08x\n", c->processor_id); if (c->options & MIPS_CPU_FPU) printk("FPU revision is: %08x\n", c->fpu_id); }