/* * Carsten Langgaard, carstenl@mips.com * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. * * This program is free software; you can distribute it and/or modify it * under the terms of the GNU General Public License (Version 2) as * published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. * * Setting up the clock on the MIPS boards. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(CONFIG_MIPS_OHIO) #include #include #include #elif defined(CONFIG_MIPS_AR7) #include #include #include #elif defined(CONFIG_MIPS_UR8) #include #include #include #else #error unknown CONFIG_MIPS_... #endif /*--- #if defined(CONFIG_MIPS_UR8) ---*/ unsigned long cpu_khz; #if defined(CONFIG_MIPS_SEAD) || defined(CONFIG_MIPS_OHIO) || defined(CONFIG_MIPS_AR7) || defined(CONFIG_MIPS_UR8) #define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ5) #else #define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5) #endif #if defined(CONFIG_MIPS_ATLAS) static char display_string[] = " LINUX ON ATLAS "; #endif #if defined(CONFIG_MIPS_MALTA) static char display_string[] = " LINUX ON MALTA "; #endif #if defined(CONFIG_MIPS_SEAD) static char display_string[] = " LINUX ON SEAD "; #endif #if defined(CONFIG_MIPS_OHIO) static char display_string[] = " LINUX ON OHIO "; #endif #if defined(CONFIG_MIPS_AR7) static char display_string[] = " LINUX ON AR7 "; #endif #if defined(CONFIG_MIPS_UR8) static char display_string[] = " LINUX ON UR8 "; #endif static unsigned int display_count = 0; #define MAX_DISPLAY_COUNT (sizeof(display_string) - 8) #define MIPS_CPU_TIMER_IRQ (NR_IRQS-1) static unsigned int timer_tick_count=0; /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #if defined(CONFIG_AVM_SIMPLE_PROFILING) #include #include #define PROFILE_BUFFER_LEN (CONFIG_AVM_PROFILING_TRACE_MODE * 1024) struct _simple_profiling { struct _avm_profile_data data[PROFILE_BUFFER_LEN]; atomic_t pos; unsigned int len; unsigned int enabled; unsigned long start_time; unsigned long end_time; } simple_profiling; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ /*--- #define REALTIME_LOG ---*/ #if defined(REALTIME_LOG) #define TIME_DIFF(act, old) ((old) > (act)) ? ((0xFFFFFFFFUL - (old)) + (act)) : ((act) - (old)) #define CLK_TO_USEC(a) ((a) / 211 / 2) /*--- 212 MHz CPU-Takt ---*/ /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void real_timelog(unsigned int time, enum _avm_profile_data_type type, unsigned int addr, unsigned int id) { static struct _actvalue { unsigned int peak; unsigned int id; unsigned int time; unsigned int addr; } actlist[avm_profile_data_type_unknown]; unsigned int timediff; switch(type) { case avm_profile_data_type_hw_irq_begin: case avm_profile_data_type_timer_begin: case avm_profile_data_type_tasklet_begin: case avm_profile_data_type_hi_tasklet_begin: case avm_profile_data_type_sw_irq_begin: case avm_profile_data_type_func_begin: actlist[type].addr = addr; actlist[type].id = id; actlist[type].time = time; break; case avm_profile_data_type_hw_irq_end: case avm_profile_data_type_sw_irq_end: case avm_profile_data_type_timer_end: case avm_profile_data_type_tasklet_end: case avm_profile_data_type_hi_tasklet_end: case avm_profile_data_type_func_end: type--; if(actlist[type].addr == addr) { timediff = TIME_DIFF(time, actlist[type].time); if((timediff > actlist[type].peak) || (CLK_TO_USEC(timediff) > 2000)) { actlist[type].peak = timediff; printk("PEAK: %s addr=%08x id=%08x(%08x) %d usec\n", type == avm_profile_data_type_hw_irq_begin ? "hw_irq" : type == avm_profile_data_type_timer_begin ? "timer" : type == avm_profile_data_type_tasklet_begin ? "tasklet" : type == avm_profile_data_type_hi_tasklet_begin ? "hi_tasklet" : type == avm_profile_data_type_sw_irq_begin ? "sw_irq" : type == avm_profile_data_type_func_begin ? "func" : "?", addr, id, actlist[type].id, CLK_TO_USEC(timediff)); } } break; default: break; } } #endif/*--- #if defined(REALTIME_LOG) ---*/ /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned int avm_simple_profiling(struct pt_regs *regs, unsigned int irq_num) { unsigned int pos, epc, time; if(simple_profiling.enabled == 0) return 0; #if defined(CONFIG_ARM) epc = regs->ARM_pc; #endif /*--- #if defined(CONFIG_ARM) ---*/ #if defined(CONFIG_MIPS) epc = regs->cp0_epc; #endif /*--- #if defined(CONFIG_MIPS) ---*/ time = avm_profile_counter(); pos = atomic_inc_return(&(simple_profiling.pos)) - 1; if(pos >= simple_profiling.len - 1) { simple_profiling.enabled = 0; simple_profiling.end_time = jiffies | 1; printk("[simple_profiling] buffer full: %u entries recorded\n", simple_profiling.len); } simple_profiling.data[pos].type = avm_profile_data_type_code_address_info; simple_profiling.data[pos].curr = current; simple_profiling.data[pos].id = irq_num; simple_profiling.data[pos].addr = epc; simple_profiling.data[pos].time = time; simple_profiling.data[pos].total_access = avm_profile_sdramacess(); simple_profiling.data[pos].total_activate = avm_profile_sdramactivate(); return time; } EXPORT_SYMBOL(avm_simple_profiling); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void avm_simple_profiling_log(enum _avm_profile_data_type type, unsigned int addr, unsigned int id) { unsigned int pos, time; struct sk_buff *skb; #if defined(REALTIME_LOG) real_timelog(avm_profile_counter(), type, addr, id); #endif/*--- #if defined(REALTIME_LOG) ---*/ if(simple_profiling.enabled == 0) return; time = avm_profile_counter(); pos = atomic_inc_return(&(simple_profiling.pos)) - 1; if(pos >= simple_profiling.len - 1) { simple_profiling.enabled = 0; simple_profiling.end_time = jiffies | 1; printk("[simple_profiling] buffer full: %u entries recorded\n", simple_profiling.len); } switch(type) { default: case avm_profile_data_type_free: case avm_profile_data_type_unknown: break; case avm_profile_data_type_text: case avm_profile_data_type_code_address_info: case avm_profile_data_type_data_address_info: simple_profiling.data[pos].type = type; simple_profiling.data[pos].addr = addr; simple_profiling.data[pos].time = time; simple_profiling.data[pos].total_access = avm_profile_sdramacess(); simple_profiling.data[pos].total_activate = avm_profile_sdramactivate(); break; case avm_profile_data_type_trace_skb: skb = (struct sk_buff *)addr; simple_profiling.data[pos].type = type; simple_profiling.data[pos].id = id; simple_profiling.data[pos].addr = skb->uniq_id; simple_profiling.data[pos].time = time; simple_profiling.data[pos].total_access = avm_profile_sdramacess(); simple_profiling.data[pos].total_activate = avm_profile_sdramactivate(); break; case avm_profile_data_type_hw_irq_begin: case avm_profile_data_type_hw_irq_end: case avm_profile_data_type_sw_irq_begin: case avm_profile_data_type_sw_irq_end: case avm_profile_data_type_timer_begin: case avm_profile_data_type_timer_end: case avm_profile_data_type_tasklet_begin: case avm_profile_data_type_tasklet_end: case avm_profile_data_type_hi_tasklet_begin: case avm_profile_data_type_hi_tasklet_end: case avm_profile_data_type_workitem_begin: case avm_profile_data_type_workitem_end: case avm_profile_data_type_cpphytx_begin: case avm_profile_data_type_cpphytx_end: case avm_profile_data_type_cpphyrx_begin: case avm_profile_data_type_cpphyrx_end: case avm_profile_data_type_func_begin: case avm_profile_data_type_func_end: simple_profiling.data[pos].type = type; simple_profiling.data[pos].id = id; simple_profiling.data[pos].addr = addr; simple_profiling.data[pos].time = time; simple_profiling.data[pos].total_access = avm_profile_sdramacess(); simple_profiling.data[pos].total_activate = avm_profile_sdramactivate(); break; } } EXPORT_SYMBOL(avm_simple_profiling_log); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void avm_simple_profiling_text(const char *text) { unsigned int pos; if(simple_profiling.enabled == 0) return; pos = atomic_inc_return(&(simple_profiling.pos)) - 1; if(pos >= simple_profiling.len - 1) { simple_profiling.enabled = 0; simple_profiling.end_time = jiffies | 1; printk("[simple_profiling] buffer full: %u entries recorded\n", simple_profiling.len); } simple_profiling.data[pos].type = avm_profile_data_type_text; simple_profiling.data[pos].time = avm_profile_counter(); simple_profiling.data[pos].addr = (unsigned int)text; simple_profiling.data[pos].total_access = avm_profile_sdramacess(); simple_profiling.data[pos].total_activate = avm_profile_sdramactivate(); return; } EXPORT_SYMBOL(avm_simple_profiling_text); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ struct _avm_profile_data *avm_simple_profiling_enable(unsigned int on, unsigned int *count, unsigned long *timediff) { if(on) { simple_profiling.enabled = 1; simple_profiling.start_time = jiffies; simple_profiling.end_time = 0; atomic_set(&(simple_profiling.pos), 0); if(count) *count = 0; if(timediff)*timediff = 0; return NULL; } simple_profiling.enabled = 0; if(simple_profiling.end_time == 0) simple_profiling.end_time = jiffies; if(count) *count = simple_profiling.pos.counter; printk("[simple_profiling] off: %u entries\n", simple_profiling.pos.counter); if(timediff) { if(time_after(simple_profiling.start_time, simple_profiling.end_time)) { *timediff = simple_profiling.end_time + 0xFFFFFFFF - simple_profiling.start_time; } else { *timediff = simple_profiling.end_time - simple_profiling.start_time; } } return simple_profiling.data; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int avm_simple_profiling_init(void) { simple_profiling.len = PROFILE_BUFFER_LEN; simple_profiling.enabled = 0; atomic_set(&(simple_profiling.pos), 0); printk("[simple_profiling] %u entries %u min\n", simple_profiling.len, CONFIG_AVM_PROFILING_TRACE_MODE); return 0; } module_init(avm_simple_profiling_init); #endif /*--- #if defined(CONFIG_AVM_SIMPLE_PROFILING) ---*/ /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void mips_timer_interrupt(struct pt_regs *regs) { extern void r4k_wait_end(void); r4k_wait_end(); #if defined(CONFIG_MIPS_OHIO) kstat_this_cpu.irqs[MIPS_EXCEPTION_OFFSET - 7]++; ohio_timer_interrupt(); #elif defined(CONFIG_MIPS_AR7) kstat_this_cpu.irqs[MIPS_EXCEPTION_OFFSET - 7]++; ar7_timer_interrupt(); #elif defined(CONFIG_MIPS_UR8) /*--- #if defined(CONFIG_MIPS_OHIO) ---*/ kstat_this_cpu.irqs[MIPS_EXCEPTION_OFFSET - 7]++; ur8_timer_interrupt(); #endif /*--- #elif ---*/ /*--- #if defined(CONFIG_MIPS_OHIO) ---*/ if ((timer_tick_count++ % HZ) == 0) { mips_display_message(&display_string[display_count++]); if (display_count == MAX_DISPLAY_COUNT) display_count = 0; } ll_timer_interrupt(MIPS_CPU_TIMER_IRQ, regs); } /* * Estimate CPU frequency. Sets mips_counter_frequency as a side-effect */ static unsigned int __init estimate_cpu_frequency(void) { unsigned int count; #ifdef CONFIG_MIPS_SEAD unsigned int prid = read_c0_prid() & 0xffff00; /* * The SEAD board doesn't have a real time clock, so we can't * really calculate the timer frequency * For now we hardwire the SEAD board frequency to 12MHz. */ if ((prid == (PRID_COMP_MIPS | PRID_IMP_20KC)) || (prid == (PRID_COMP_MIPS | PRID_IMP_25KF))) count = 12000000; else count = 6000000; #endif #if defined(CONFIG_MIPS_ATLAS) || defined(CONFIG_MIPS_MALTA) unsigned int flags; local_irq_save(flags); /* Start counter exactly on falling edge of update flag */ while (CMOS_READ(RTC_REG_A) & RTC_UIP); while (!(CMOS_READ(RTC_REG_A) & RTC_UIP)); /* Start r4k counter. */ write_c0_count(0); /* Read counter exactly on falling edge of update flag */ while (CMOS_READ(RTC_REG_A) & RTC_UIP); while (!(CMOS_READ(RTC_REG_A) & RTC_UIP)); count = read_c0_count(); /* restore interrupts */ local_irq_restore(flags); #endif #if defined(CONFIG_MIPS_OHIO) || defined(CONFIG_MIPS_AR7) || defined(CONFIG_MIPS_UR8) #if defined(CONFIG_OHIO_CLOCK_SWITCH) || defined(CONFIG_UR8_CLOCK_SWITCH) || defined(CONFIG_AR7_CLOCK_SWITCH) { static unsigned int mips_time_cpu_frequenz_change(enum _avm_clock_id, unsigned int); count = avm_get_clock_notify(avm_clock_id_cpu, mips_time_cpu_frequenz_change); } #else /*--- #if defined(CONFIG_OHIO_CLOCK_SWITCH) || defined(CONFIG_UR8_CLOCK_SWITCH) || defined(CONFIG_AR7_CLOCK_SWITCH) ---*/ count = avm_get_clock(avm_clock_id_cpu); #endif /*--- #else ---*//*--- #if defined(CONFIG_OHIO_CLOCK_SWITCH) || defined(CONFIG_UR8_CLOCK_SWITCH) || defined(CONFIG_AR7_CLOCK_SWITCH) ---*/ mips_hpt_frequency = count / 2; #endif #if !defined(CONFIG_MIPS_OHIO) && !defined(CONFIG_MIPS_AR7) && !defined(CONFIG_MIPS_UR8) if ((prid != (PRID_COMP_MIPS | PRID_IMP_20KC)) && (prid != (PRID_COMP_MIPS | PRID_IMP_25KF))) count *= 2; mips_hpt_frequency = count; #endif /*--- #if !defined(CONFIG_MIPS_OHIO) && !defined(CONFIG_MIPS_AR7) && !defined(CONFIG_MIPS_UR8) ---*/ count += 5000; /* round */ count -= count%10000; return count; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #if defined(CONFIG_OHIO_CLOCK_SWITCH) || defined(CONFIG_AR7_CLOCK_SWITCH) || defined(CONFIG_UR8_CLOCK_SWITCH) static unsigned int mips_time_cpu_frequenz_change(enum _avm_clock_id clock_id, unsigned int new_clk) { printk("[mips_time_cpu_frequenz_change] change to %u Hz\n", new_clk); return 0; } #endif /*--- #if defined(CONFIG_OHIO_CLOCK_SWITCH) || defined(CONFIG_AR7_CLOCK_SWITCH) || defined(CONFIG_UR8_CLOCK_SWITCH) ---*/ #if defined(CONFIG_MIPS_RTC) unsigned long __init mips_rtc_get_time(void) { return mc146818_get_cmos_time(); } #endif /*--- #if defined(CONFIG_MIPS_RTC) ---*/ /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void __init mips_time_init(void) { unsigned int est_freq, flags; local_irq_save(flags); #if defined(CONFIG_MIPS_ATLAS) || defined(CONFIG_MIPS_MALTA) /* Set Data mode - binary. */ CMOS_WRITE(CMOS_READ(RTC_CONTROL) | RTC_DM_BINARY, RTC_CONTROL); #endif est_freq = estimate_cpu_frequency (); printk("CPU frequency %d.%02d MHz\n", est_freq/1000000, (est_freq%1000000)*100/1000000); cpu_khz = est_freq / 1000; local_irq_restore(flags); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void __init mips_timer_setup(struct irqaction *irq) { /*--- printk("[mips_timer_setup] 1\n"); ---*/ /* we are using the cpu counter for timer interrupts */ irq->handler = no_action; /* we use our own handler */ /*--- printk("[mips_timer_setup] 2\n"); ---*/ setup_irq(MIPS_CPU_TIMER_IRQ, irq); /*--- printk("[mips_timer_setup] 3\n"); ---*/ /* to generate the first timer interrupt */ write_c0_compare (read_c0_count() + mips_hpt_frequency/HZ); /*--- printk("[mips_timer_setup] 4\n"); ---*/ set_c0_status(ALLINTS); /*--- printk("[mips_timer_setup] 5\n"); ---*/ }