?? time.patch
字號:
- au_sync();- offset = pc0 - last_pc0;- if (offset > 2*MATCH20_INC) {- printk("huge offset %x, last_pc0 %x last_match20 %x pc0 %x\n", - (unsigned)offset, (unsigned)last_pc0, - (unsigned)last_match20, (unsigned)pc0);- }- offset = (unsigned long)((offset * 305) / 10);- return offset;+ irq_exit();+ return;++null:+ irq_exit();+ ack_r4ktimer(0); }+++/*+ * We read the real processor speed from the PLL. This is important+ * because it is more accurate than computing it from the 32KHz+ * counter, if it exists. If we don't have an accurate processor+ * speed, all of the peripherals that derive their clocks based on+ * this advertised speed will introduce error and sometimes not work+ * properly. This function is futher convoluted to still allow configurations+ * to do that in case they have really, really old silicon with a+ * write-only PLL register, that we need the 32KHz when power management+ * "wait" is enabled, and we need to detect if the 32KHz isn't present+ * but requested......got it? :-) -- Dan+ */+unsigned long cal_r4koff(void)+{+ u32 cpu_freq, flags;++ spin_lock_irqsave(&time_lock, flags);++#ifdef CONFIG_SOC_AU1000+ /* On early Au1000s, sys_cpupll was write-only. Since these+ silicon versions of Au1000 are not sold by AMD, we don't bend+ over backwards trying to determine the frequency */+#ifndef CONFIG_SOC_AU1000_FREQUENCY+#define CONFIG_SOC_AU1000_FREQUENCY 396000000+#endif+ if ((read_c0_prid() == 0x00030100) || /* Au1000 DA */+ (read_c0_prid() == 0x00030101) || /* Au1000 HA */+ (read_c0_prid() == 0x00030102)) /* Au1000 HB */+ cpu_freq = CONFIG_SOC_AU1000_FREQUENCY;+ else #endif+ cpu_freq = (au_readl(SYS_CPUPLL) & 0x0000003f) * CONFIG_AU1000_SRC_CLK;++ mips_hpt_frequency = cpu_freq; /* On Alchemy, Count is 1:1 CPU */+ /* Equation: Baudrate = CPU / (SD * 2 * CLKDIV * 16) */+ set_au1x00_uart_baud_base(cpu_freq / (2 * ((int)(au_readl(SYS_POWERCTRL)&0x03) + 2) * 16));+ set_au1x00_speed(cpu_freq);+ set_au1x00_lcd_clock(); /* program the LCD clock */++ spin_unlock_irqrestore(&time_lock, flags);++ return (cpu_freq / HZ);+} void au1xxx_timer_setup(struct irqaction *irq) {- unsigned int est_freq;- extern unsigned long (*do_gettimeoffset)(void); extern void au1k_wait(void); - printk("calculating r4koff... ");- r4k_offset = cal_r4koff();- printk("%08lx(%d)\n", r4k_offset, (int) r4k_offset);+ /* This code called at the end of time_init() in arch/mips/kernel/time.c.+ The Cp0 Counter has already been chosen as the timer source, here we+ can override the choices made by time_init(). */++#ifdef CONFIG_SOC_AU1X00_32KHZ_TIMER+ /* Setup the 32kHz oscillator as the kernel timer instead */+ if (au1xxx_32khz_startup()) {+ extern void hook_rtcm1_interrupt(void);++ /* it's too early to call request_irq(), but need to hook it anyway */+ setup_rtcm1_interrupt();+ hook_rtcm1_interrupt(); - //est_freq = 2*r4k_offset*HZ; - est_freq = r4k_offset*HZ; - est_freq += 5000; /* round */- est_freq -= est_freq%10000;- printk("CPU frequency %d.%02d MHz\n", est_freq/1000000, - (est_freq%1000000)*100/1000000);- set_au1x00_speed(est_freq);- set_au1x00_lcd_clock(); // program the LCD clock+ /* use 32khz flavor of do_fast_gettimeoffset() */+ do_gettimeoffset = do_fast_rtcm1_gettimeoffset; - r4k_cur = (read_c0_count() + r4k_offset);- write_c0_compare(r4k_cur);+ /* short-circuit CP0 Count timer handler */+ r4k_offset = 0; -#ifdef CONFIG_PM- /*- * setup counter 0, since it keeps ticking after a- * 'wait' instruction has been executed. The CP0 timer and- * counter 1 do NOT continue running after 'wait'- *- * It's too early to call request_irq() here, so we handle- * counter 0 interrupt as a special irq and it doesn't show- * up under /proc/interrupts.- *- * Check to ensure we really have a 32KHz oscillator before- * we do this.- */- if (no_au1xxx_32khz) {- unsigned int c0_status;-- printk("WARNING: no 32KHz clock found.\n");- do_gettimeoffset = do_fast_cp0_gettimeoffset;-- /* Ensure we get CPO_COUNTER interrupts.- */- c0_status = read_c0_status();- c0_status |= IE_IRQ5;- write_c0_status(c0_status);+ /* allow WAIT instruction in cpu-probe.c */+ au1k_wait_ptr = au1k_wait; } else {- while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C0S);- au_writel(0, SYS_TOYWRITE);- while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C0S);-- au_writel(au_readl(SYS_WAKEMSK) | (1<<8), SYS_WAKEMSK);- au_writel(~0, SYS_WAKESRC);- au_sync();- while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20);-- /* setup match20 to interrupt once every 10ms */- last_pc0 = last_match20 = au_readl(SYS_TOYREAD);- au_writel(last_match20 + MATCH20_INC, SYS_TOYMATCH2);- au_sync();- while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20);- startup_match20_interrupt();-- do_gettimeoffset = do_fast_pm_gettimeoffset;-- /* We can use the real 'wait' instruction.- */- au1k_wait_ptr = au1k_wait;+ printk("WARNING: no 32KHz clock found...reverting to CP0 timer\n"); }- #else- /* We have to do this here instead of in timer_init because- * the generic code in arch/mips/kernel/time.c will write- * over our function pointer.- */- do_gettimeoffset = do_fast_cp0_gettimeoffset;-#endif+ /* Setup first CP0 timer interrupt */+ setup_cp0_interrupt();+#endif /* CONFIG_SOC_AU1X00_32KHZ_TIMER */+ } void __init au1xxx_time_init(void) {+ /* This called at the start of time_init() in arch/mips/kernel/time.c */+ /* An RTC for use by time_init()/kernel is supposed to be setup here */++#ifdef CONFIG_SOC_AU1X00_32KHZ+ if (au1xxx_32khz_startup()) {+ /* set the kernel function pointers */+ rtc_get_time = rtc_au1xxx_get_time;+ rtc_set_time = rtc_au1xxx_set_time;+ }+#endif /* CONFIG_SOC_AU1X00_32KHZ */++ /* Determine CP0 timer values */+ printk("calculating r4koff... ");+ r4k_offset = cal_r4koff();+ printk("%08x(%d)\n", r4k_offset, (int) r4k_offset); }diff -Naur linux26-cvs/arch/mips/Kconfig _timer/arch/mips/Kconfig--- linux26-cvs/arch/mips/Kconfig 2005-05-18 11:27:54.000000000 -0500+++ _timer/arch/mips/Kconfig 2005-05-18 11:46:45.271794264 -0500@@ -34,6 +34,8 @@ select HW_HAS_PCI select SWAP_IO_SPACE select SYS_SUPPORTS_LITTLE_ENDIAN+ select SOC_AU1X00_32KHZ+ select SOC_AU1X00_32KHZ_TIMER config MIPS_PB1100 bool "AMD Alchemy PB1100 board"@@ -42,6 +44,8 @@ select HW_HAS_PCI select SWAP_IO_SPACE select SYS_SUPPORTS_LITTLE_ENDIAN+ select SOC_AU1X00_32KHZ+ select SOC_AU1X00_32KHZ_TIMER config MIPS_PB1500 bool "AMD Alchemy PB1500 board"@@ -49,6 +53,8 @@ select DMA_NONCOHERENT select HW_HAS_PCI select SYS_SUPPORTS_LITTLE_ENDIAN+ select SOC_AU1X00_32KHZ+ select SOC_AU1X00_32KHZ_TIMER config MIPS_PB1550 bool "AMD Alchemy PB1550 board"@@ -57,6 +63,8 @@ select HW_HAS_PCI select MIPS_DISABLE_OBSOLETE_IDE select SYS_SUPPORTS_LITTLE_ENDIAN+ select SOC_AU1X00_32KHZ+ select SOC_AU1X00_32KHZ_TIMER config MIPS_PB1200 bool "AMD Alchemy PB1200 board"@@ -65,6 +73,8 @@ select MIPS_DISABLE_OBSOLETE_IDE select SYS_SUPPORTS_LITTLE_ENDIAN select HW_HAS_PCI+ select SOC_AU1X00_32KHZ+ select SOC_AU1X00_32KHZ_TIMER config MIPS_DB1000 bool "AMD Alchemy DB1000 board"@@ -72,12 +82,16 @@ select DMA_NONCOHERENT select HW_HAS_PCI select SYS_SUPPORTS_LITTLE_ENDIAN+ select SOC_AU1X00_32KHZ+ select SOC_AU1X00_32KHZ_TIMER config MIPS_DB1100 bool "AMD Alchemy DB1100 board" select SOC_AU1100 select DMA_NONCOHERENT select SYS_SUPPORTS_LITTLE_ENDIAN+ select SOC_AU1X00_32KHZ+ select SOC_AU1X00_32KHZ_TIMER config MIPS_DB1500 bool "AMD Alchemy DB1500 board"@@ -86,6 +100,8 @@ select HW_HAS_PCI select MIPS_DISABLE_OBSOLETE_IDE select SYS_SUPPORTS_LITTLE_ENDIAN+ select SOC_AU1X00_32KHZ+ select SOC_AU1X00_32KHZ_TIMER config MIPS_DB1550 bool "AMD Alchemy DB1550 board"@@ -94,6 +110,8 @@ select DMA_NONCOHERENT select MIPS_DISABLE_OBSOLETE_IDE select SYS_SUPPORTS_LITTLE_ENDIAN+ select SOC_AU1X00_32KHZ+ select SOC_AU1X00_32KHZ_TIMER config MIPS_DB1200 bool "AMD Alchemy DB1200 board"@@ -102,6 +120,8 @@ select MIPS_DISABLE_OBSOLETE_IDE select SYS_SUPPORTS_LITTLE_ENDIAN select HW_HAS_PCI+ select SOC_AU1X00_32KHZ+ select SOC_AU1X00_32KHZ_TIMER config MIPS_MIRAGE bool "AMD Alchemy Mirage board"@@ -802,6 +822,13 @@ bool select SYS_SUPPORTS_32BIT_KERNEL +config SOC_AU1X00_32KHZ+ bool++config SOC_AU1X00_32KHZ_TIMER+ select SOC_AU1X00_32KHZ+ bool+ config SWAP_IO_SPACE bool diff -Naur -x CVS linux26-cvs/Documentation/Alchemy.README _timer/Documentation/Alchemy.README--- linux26-cvs/Documentation/Alchemy.README 1969-12-31 18:00:00.000000000 -0600+++ _timer/Documentation/Alchemy.README 2005-05-13 22:21:00.000000000 -0500@@ -0,0 +1,105 @@+README for arch/mips/au1000/common directory and subdirectories++Embedded Edge, Embedded Alley++MOTIVATION+----------++The Alchemy family of SOCs are very similarly architecturally, which+lends itself well to sharing a great deal of kernel code, and drivers.++The code in this directory is utilized by all Alchemy-based designs,+and is intended to be as board-independent as possible, but with the+appropriate SOC changes.++HOW TO CHOOSE THE KERNEL TIMER++ There can be three sources for the kernel timer tick with the Au1x00:+ - MIPS CP0 Count/Compare mechanism+ - Au1XXX RTC/TOY integrated timer+ - External timer+ The CP0 Counter operates at the frequency of the Au1 core; however,+ it does not count during the WAIT/Idle modes.++ The TOY/RTC operates from an optional 32kHz external oscillator; both+ count during the WAIT/Idle modes, but only the TOY continues to+ count during the Sleep mode of the Au1x00.++ The external timer could be anything, presumably connected via a GPIO.+ This might be used in systems where an external battery-backed clock+ source is required since early Au1x00 silicon drained the battery+ too quickly in Sleep mode. With an external timer source, the WAIT/Idle+ mode can be utilized.++ The different abilities of the timing sources lead to several possible+ configurations for building this time management code:++ 1) Use CP0 Count/Compare as the ticker and source for real-time clock;+ note this prevents the use of the WAIT instruction and its power savings.+ 2) Use Cp0 Count/Compare as the ticker and RTC/TOY as the source for+ the real-time clock (again, can not use the WAIT instruction).+ 3) Use the TOY/RTC as the ticker source and also as the source for+ the real-time clock. This mode permits the use of the WAIT+ instruction and the Au1 core Idle modes for power savings.+ 4) Use of an external timer as the ticker source, which also permits+ the use of WAIT instruction and its power savings.++ The support for these scenarios is modularized as such:+ CONFIG_SOC_AU1X00_32KHZ is declared if the board populates a 32kHz+ oscillator. If so, it is assumed that the TOY/RTC will be used for+ the kernel's real-time clock, and thus will enable the appropriate+ code.++ CONFIG_SOC_AU1X00_32KHZ_TIMER is declared if the board is to utilize+ the TOY/RTC as the kernel timer tick. In the absence of this+ declaration, the kernel will assume the use of the MIPS CP0 Counter.++ If an external timer source is utilized, then arch/mips/au1000/common/+ time.c will need to be examined and changed appropriately.++ When the TOY/RTC is used to source the real-time clock, it is the TOY+ that is used, not the RTC. While this is definitely possibly confusing,+ keep in mind that the TOY is able to count during Sleep modes, and so+ it makes sense to maintain the real-time clock in the TOY so that an+ alarm based on date/time configured in the TOY match 2 register can+ be utilized to awake the Au1x00 system from sleep state.++ Since the 32KHz crystal is not [typically] an integer multiple+ of HZ, there will be some drift associated with utilizing it+ as a timer tick source. Here's the scoop.+ 1/32768 = 0.00003051s is 30.5microseconds per tick+ if HZ=100:+ trim is 32768/100 rounded UP = 328+ 328 * 30.5us = 0.010004us+ So the periodic rate is extremely close to the desired 0.01,+ but it is not exact. Thus the drift is calculated as:+ 0.01 / 0.000004 = 2500+ Which means that every 2500 timer intervals, the timer has+ drift one HZ. So we must account for that.+ if HZ=1000:+ trim is 32768/1000 rounded UP = 33+ 33 * 30.5us = 0.0010065us+ 0.001 / 0.0000065 = 153.8++ Also, in rtcm1_irq(), the code checks to make sure that the counter+ is not at the value we're about to program into the match; since it+ takes one counter tick for the update to occur. This can occur since+ Linux' interrupt response time is highly irregular, and so the RTC+ may already be near the next RTCM1 value by the time this handler+ is invoked. To do this, the code estimates how many RTC ticks will+ elapse by the time the update occurs, and figures this into the+ check. The check is complicated since the RTC can be ahead of RTCM1,+ and vice versa, and that can be valid, or not, depending upon whether+ or not RTC, or RTCM1, has rolled-over.++ See also Documentation/mips/time.README.++HOW TO ADD A BOARD+------------------++TBD++TO-DO LIST+----------++TBDdiff -Naur -x CVS linux26-cvs/include/asm-mips/mach-au1x00/au1000.h _timer/include/asm-mips/mach-au1x00/au1000.h--- linux26-cvs/include/asm-mips/mach-au1x00/au1000.h 2005-05-16 13:39:23.924915792 -0500+++ _timer/include/asm-mips/mach-au1x00/au1000.h 2005-05-13 22:20:15.000000000 -0500@@ -155,13 +155,6 @@ #endif /* !defined (_LANGUAGE_ASSEMBLY) */ -#ifdef CONFIG_PM-/* no CP0 timer irq */-#define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4)-#else-#define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5)-#endif- /* * SDRAM Register Offsets */@@ -1036,25 +1029,22 @@ /* Programmable Counters 0 and 1 */ #define SYS_BASE 0xB1900000 #define SYS_COUNTER_CNTRL (SYS_BASE + 0x14)- #define SYS_CNTRL_E1S (1<<23)- #define SYS_CNTRL_T1S (1<<20)- #define SYS_CNTRL_M21 (1<<19)- #define SYS_CNTRL_M11 (1<<18)- #define SYS_CNTRL_M01 (1<<17)- #define SYS_CNTRL_C1S (1<<16)- #define SYS_CNTRL_BP (1<<14)- #define SYS_CNTRL_EN1 (1<<13)- #define SYS_CNTRL_BT1 (1<<12)- #define SYS_CNTRL_EN0 (1<<11)- #define SYS_CNTRL_BT0 (1<<10)- #define SYS_CNTRL_E0 (1<<8)- #define SYS_CNTRL_E0S (1<<7)- #define SYS_CNTRL_32S (1<<5)- #define SYS_CNTRL_T0S (1<<4)- #define SYS_CNTRL_M20 (1<<3)- #define SYS_CNTRL_M10 (1<<2)- #define SYS_CNTRL_M00 (1<<1)- #define SYS_CNTRL_C0S (1<<0)+#define SYS_CNTRCTRL_RTS (1<<20)+#define SYS_CNTRCTRL_RM2 (1<<19)+#define SYS_CNTRCTRL_RM1 (1<<18)+#define SYS_CNTRCTRL_RM0 (1<<17)+#define SYS_CNTRCTRL_RS (1<<16)+#define SYS_CNTRCTRL_BP (1<<14)+#define SYS_CNTRCTRL_E0 (1<<8)+#define SYS_CNTRCTRL_CCS (1<<7)+#define SYS_CNTRCTRL_32S (1<<5)+#define SYS_CNTRCTRL_TTS (1<<4)+#define SYS_CNTRCTRL_TM2 (1<<3)+#define SYS_CNTRCTRL_TM1 (1<<2)+#define SYS_CNTRCTRL_TM0 (1<<1)+#define SYS_CNTRCTRL_TS (1<<0)++ /* Programmable Counter 0 Registers */ #define SYS_TOYTRIM (SYS_BASE + 0)@@ -1781,7 +1771,7 @@ #define PCI_IO_START 0 #define PCI_IO_END 0 #define PCI_MEM_START 0-#define PCI_MEM_END 0 +#define PCI_MEM_END 0 #define PCI_FIRST_DEVFN 0 #define PCI_LAST_DEVFN 0
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -