/* * 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. * * This program is distributed in the hope that 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. * * HISTORY * $Version $Date $Author $Comment * 1.0 19/07/07 Teh Kok How */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#include #include #include //static unsigned int r4k_offset; /* Amount to increment compare reg each time */ //static unsigned int r4k_cur; /* What counter should be at next timer irq */ extern void amazon_s_reboot_setup(void); extern int kgdb_serial_init(void); void prom_printf(const char * fmt, ...); void __init bus_error_init(void) { /* nothing */ } int (*adsl_link_notify)(int); /* modify danube register with a small delay after writing to the register */ /* this delay is located in the same cache line as the write access, to prevent */ /* erroneous updates of the caches (seen with CGU_DIVCR register !) */ /* Input: reg = address of register */ /* andmask = bits to clear in the register */ /* ormask = bits to set in the register */ static inline unsigned long modify_reg(volatile unsigned int *reg, unsigned long andmask, unsigned long ormask) { int count=15000; unsigned long regValue = 0; /* equivalent C-Code: (*reg) = ((*reg) & (~andmask)) | (ormask); */ asm volatile (" .set noreorder\n" "\tlw\t%2,(%3)\n" "\tand\t%2,%2,%5\n" "\tor\t%2,%2,%6\n" "\t.align\t4\n" /* align on cache line boundary */ "\tsw\t%2,(%3)\n" "\t0:\n" "\tbnez\t%4,0b\n" "\taddiu\t%4,%4,-1\n" /* branch delay */ "\t.set\treorder\n" : "=r" (regValue), /* %0 - output */ "=r" (count) /* %1 - count is modified */ : "0" (regValue), /* %2 - same register as %0 */ "r" (reg), /* %3 - address to modify */ "1" (count), /* %4 - number of delay cycles */ "r" (~andmask), /* %5 - value to 'and' with on-chip register */ "r" (ormask) /* %6 - value to 'or' with on-chip register */ /* : */ /* nothing clobbered */ ); return regValue; } unsigned int amazon_s_get_ddr_hz(void) { unsigned int sys_freq; switch ( *AMAZON_S_CGU_SYS & (0x03 << 3) ) { case (0 << 3): sys_freq = CLOCK_333M; break; case (1 << 3): sys_freq = CLOCK_500M; break; case (2 << 3): if ( *AMAZON_S_CGU_PLL1_CFG == 0x9800f25f ) { sys_freq = CLOCK_333M; break; } default: sys_freq = CLOCK_393M; break; } if ( (*AMAZON_S_CGU_SYS & 0x01) ) return sys_freq / 3; else return sys_freq >> 1; } /* the CPU clock rate - lifted from u-boot */ unsigned int amazon_s_get_cpu_hz(void) { #ifdef CONFIG_USE_EMULATOR return EMULATOR_CPU_SPEED; #else //NOT CONFIG_USE_EMULATOR if ( (*AMAZON_S_CGU_SYS & (1 << 2)) ) return amazon_s_get_ddr_hz(); else { switch ( *AMAZON_S_CGU_SYS & (0x03 << 3) ) { case (0 << 3): return CLOCK_333M; case (1 << 3): return CLOCK_500M; case (2 << 3): if ( *AMAZON_S_CGU_PLL1_CFG == 0x9800f25f ) return CLOCK_333M; default: return CLOCK_393M; } } #endif } /* the FPI clock rate - lifted from u-boot */ unsigned int amazon_s_get_fpi_hz(void) { #ifdef CONFIG_USE_EMULATOR unsigned int clkCPU; clkCPU = amazon_s_get_cpu_hz(); return clkCPU >> 2; #else //NOT CONFIG_USE_EMULATOR unsigned int ddr_clock=amazon_s_get_ddr_hz(); if ((*AMAZON_S_CGU_SYS) & 0x40){ return ddr_clock >> 1; } return ddr_clock; #endif } #if 0 // not needed in Amazon-S /* get the CPU version number - based on sysLib.c from VxWorks sources */ /* this doesn't really belong here, but it's a convenient location */ unsigned int amazon_s_get_cpu_ver(void) { static unsigned int cpu_ver = 0; if (cpu_ver == 0) cpu_ver = *AMAZON_S_MCD_CHIPID & 0xFFFFF000; return cpu_ver; } #endif EXPORT_SYMBOL(amazon_s_get_cpu_hz); EXPORT_SYMBOL(amazon_s_get_fpi_hz); static long get_counter_resolution(void) { volatile long res; __asm__ __volatile__( ".set push\n" ".set noreorder\n" ".set mips32r2\n" // ".set noat\n" // "li $1, 3\n" // "nop\n" "rdhwr %0, $3\n" "ehb\n" // "nop\n" // "move $2, %0\n" // "nop\n" ".set pop\n" : "=&r" (res) : /* no input */ : "memory"); instruction_hazard(); return res; } #ifdef CONFIG_MIPS_APSP_KSPD unsigned long cpu_khz; #endif void platform_time_init(void) { volatile u32 resolution = 2; #ifdef CONFIG_CPU_MIPSR2 resolution = get_counter_resolution(); #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,31) #ifdef CONFIG_USE_EMULATOR //we do some hacking here to give illusion we have a faster counter frequency so that // the time interrupt happends less frequently mips_hpt_frequency = amazon_s_get_cpu_hz()/resolution * 25; #else //not CONFIG_USE_EMULATOR mips_hpt_frequency = amazon_s_get_cpu_hz()/resolution; #endif //not CONFIG_USE_EMULATOR //r4k_offset = (mips_hpt_frequency + HZ / 2) / HZ; prom_printf("mips_hpt_frequency:%d, counter_resolution = %d\n",mips_hpt_frequency, resolution); //prom_printf("r4k_offset: %08x(%d)\n",r4k_offset,r4k_offset); #else #ifdef CONFIG_USE_EMULATOR //we do some hacking here to give illusion we have a faster counter frequency so that // the time interrupt happends less frequently mips_counter_frequency = amazon_s_get_cpu_hz()/resolution * 25; #else //not CONFIG_USE_EMULATOR mips_counter_frequency = amazon_s_get_cpu_hz()/resolution; #endif //not CONFIG_USE_EMULATOR //r4k_offset = (mips_counter_frequency + HZ / 2) / HZ; prom_printf("mips_counter_frequency:%d\n",mips_counter_frequency); //prom_printf("r4k_offset: %08x(%d)\n",r4k_offset,r4k_offset); #endif /* kernel version */ #ifdef CONFIG_MIPS_APSP_KSPD cpu_khz = amazon_s_get_cpu_hz() / 1000; #endif } #ifdef CONFIG_HIGH_RES_TIMERS int hr_time_resolution = 1024 * 1024; /*--- irgend ein Wert damit die Variable nicht 0 ist ---*/ /* ISR GPTU Timer 6 for high resolution timer */ irqreturn_t amazon_s_timer6_interrupt(int irq, void *dev_id) { /*--- timer_interrupt(DANUBE_TIMER6_INT, NULL); ---*/ return IRQ_HANDLED; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static struct irqaction hrt_irqaction = { .handler = amazon_s_timer6_interrupt, .flags = IRQF_DISABLED, .name = "hrt" }; #endif //CONFIG_HIGH_RES_TIMERS extern void hw5_irqdispatch(void); /* * THe CPU counter for System timer, set to HZ * GPTU Timer 6 for high resolution timer, set to hr_time_resolution * Also misuse this routine to print out the CPU type and clock. */ //void danube_timer_setup(struct irqaction *irq) /*--- void __init plat_timer_setup(struct irqaction *irq) ---*/ /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void __init plat_time_init(void) { struct irqaction *irq = &hrt_irqaction; /* cpu counter for timer interrupts */ // irq->handler = no_action; /* we use our own handler */ /*--- setup_irq(MIPS_CPU_TIMER_IRQ, irq); ---*/ /* to generate the first timer interrupt */ //r4k_cur = (read_c0_count() + r4k_offset); //write_c0_compare(r4k_cur); #if 0 #ifdef CONFIG_HIGH_RES_TIMERS { /* GPTU timer 6 ( entspricht 3b) */ int retval; if ( hr_time_resolution > 200000000 || hr_time_resolution < 40) { printk(KERN_ERR "hr_time_resolution is out of range, HIGH_RES_TIMER is diabled.\n"); return; } /* enable the timer in the PMU */ *(AMAZON_S_PMU_PWDCR) = (*(AMAZON_S_PMU_PWDCR))| AMAZON_S_PMU_PWDCR_GPTC | AMAZON_S_PMU_PWDCR_FPI0; /* setup the GPTU for timer tick f_fpi == f_gptu*/ *(AMAZON_S_GPTU_GPT_CLC) = 0x100; #ifdef CONFIG_USE_EMULATOR //reload value = fpi/(HZ * P), timer mode, Prescaler = 4 ( T6I = 000, T6BPS2 = 0) *(AMAZON_S_GPTU_GPT_CAPREL) = (amazon_s_get_fpi_hz()*hr_time_resolution/1000000000)>>2; *(AMAZON_S_GPTU_GPT_T6CON) = 0x80C0; #else *(AMAZON_S_GPTU_GPT_CAPREL) = 0xffff; *(AMAZON_S_GPTU_GPT_T6CON) = 0x80C0; #endif retval = setup_irq(INT_NUM_IM3_IRL27, &hrt_irqaction); if (retval) printk(KERN_ERR "reqeust_irq failed %d. HIGH_RES_TIMER is diabled\n", INT_NUM_IM3_IRL27); } #endif //CONFIG_HIGH_RES_TIMERS #endif } arch_initcall(plat_time_init); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static char buf[1024]; void prom_printf(const char * fmt, ...) { va_list args; int l; char *p, *buf_end; /* Low level, brute force, not SMP safe... */ va_start(args, fmt); l = vsprintf(buf, fmt, args); /* hopefully i < sizeof(buf) */ va_end(args); buf_end = buf + l; for (p = buf; p < buf_end; p++) { /* Wait for FIFO to empty */ #ifdef CONFIG_IFX_ASC_CONSOLE_ASC0 while ((((*AMAZON_S_ASC0_FSTAT)& ASCFSTAT_TXFFLMASK) >> ASCFSTAT_TXFFLOFF) != 0x00) ; /* Crude cr/nl handling is better than none */ if(*p == '\n') *AMAZON_S_ASC0_TBUF=('\r'); *AMAZON_S_ASC0_TBUF=(*p); #else while ((((*AMAZON_S_ASC1_FSTAT)& ASCFSTAT_TXFFLMASK) >> ASCFSTAT_TXFFLOFF) != 0x00) ; /* Crude cr/nl handling is better than none */ if(*p == '\n') *AMAZON_S_ASC1_TBUF=('\r'); *AMAZON_S_ASC1_TBUF=(*p); #endif } } EXPORT_SYMBOL(prom_printf); #ifdef CONFIG_DEBUG_LL void printascii(const char *string) { char *buf = "%s"; prom_printf(buf,string); } #endif #if defined(CONFIG_MIPS_VPE_LOADER) && defined(CONFIG_VPE_LOADER_SHARED_TLB) static void configure_shared_tlb(void) { int i, tlbsiz, vpes; u32 mvpconf0; u32 config1val; mvpconf0 = read_c0_mvpconf0(); if ( (vpes = ((mvpconf0 & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1) >> 1 ) { if ( (tlbsiz = ((mvpconf0 & MVPCONF0_PTLBE) >> MVPCONF0_PTLBE_SHIFT)) == 0 ) { settc(1); write_tc_c0_tchalt(TCHALT_H); mips_ihb(); for ( i = 0; i < vpes; i++ ) { write_tc_c0_tcbind(i); write_c0_mvpcontrol(read_c0_mvpcontrol() & ~MVPCONTROL_VPC); mips_ihb(); if ( ((read_vpe_c0_config() & MIPS_CONF_MT) >> 7) == 1 ) { config1val = read_vpe_c0_config1(); tlbsiz += ((config1val >> 25) & 0x3F) + 1; } write_c0_mvpcontrol(read_c0_mvpcontrol() | MVPCONTROL_VPC); mips_ihb(); } } write_c0_mvpcontrol(read_c0_mvpcontrol() | MVPCONTROL_STLB); ehb(); printk("%s - tlbsiz = %d\n", __func__, tlbsiz); } } #endif void __init plat_mem_setup(void) { #ifdef CONFIG_USE_EMULATOR prom_printf("press any key to continue...\n"); #ifdef CONFIG_IFX_ASC_CONSOLE_ASC0 while (((*AMAZON_S_ASC0_FSTAT)& ASCFSTAT_RXFFLMASK) == 0x00) ; #else while (((*AMAZON_S_ASC1_FSTAT)& ASCFSTAT_RXFFLMASK) == 0x00) ; #endif #endif #if 0 /* clear RE bit*/ status = read_c0_status(); status &= (~(1<<25)); write_c0_status(status); #endif amazon_s_reboot_setup(); platform_time_init(); #ifdef CONFIG_KGDB kgdb_serial_init(); prom_printf("\n===>Please connect GDB to console tty0\n"); #endif #if defined(CONFIG_MIPS_VPE_LOADER) && defined(CONFIG_VPE_LOADER_SHARED_TLB) configure_shared_tlb(); #endif }