?? time.patch
字號:
diff -Naur -x CVS linux26-cvs/arch/mips/au1000/common/irq.c _timer/arch/mips/au1000/common/irq.c--- linux26-cvs/arch/mips/au1000/common/irq.c 2005-05-16 13:39:23.091042560 -0500+++ _timer/arch/mips/au1000/common/irq.c 2005-05-13 22:18:07.000000000 -0500@@ -48,6 +48,7 @@ #include <asm/mipsregs.h> #include <asm/system.h> #include <asm/mach-au1x00/au1000.h>+ #ifdef CONFIG_MIPS_PB1000 #include <asm/mach-pb1x00/pb1000.h> #endif@@ -82,10 +83,6 @@ void (*board_init_irq)(void); -#ifdef CONFIG_PM-extern void counter0_irq(int irq, void *dev_id, struct pt_regs *regs);-#endif- static DEFINE_SPINLOCK(irq_lock); @@ -253,7 +250,7 @@ static struct hw_interrupt_type rise_edge_irq_type = {- .typename = "Au1000 Rise Edge",+ .typename = "Au1 Rise Edge", .startup = startup_irq, .shutdown = shutdown_irq, .enable = local_enable_irq,@@ -263,7 +260,7 @@ }; static struct hw_interrupt_type fall_edge_irq_type = {- .typename = "Au1000 Fall Edge",+ .typename = "Au1 Fall Edge", .startup = startup_irq, .shutdown = shutdown_irq, .enable = local_enable_irq,@@ -273,7 +270,7 @@ }; static struct hw_interrupt_type either_edge_irq_type = {- .typename = "Au1000 Rise or Fall Edge",+ .typename = "Au1 Rise or Fall Edge", .startup = startup_irq, .shutdown = shutdown_irq, .enable = local_enable_irq,@@ -283,7 +280,7 @@ }; static struct hw_interrupt_type level_irq_type = {- .typename = "Au1000 Level",+ .typename = "Au1 Level", .startup = startup_irq, .shutdown = shutdown_irq, .enable = local_enable_irq,@@ -292,12 +289,36 @@ .end = end_irq, }; -#ifdef CONFIG_PM-void startup_match20_interrupt(void)+#ifdef CONFIG_SOC_AU1X00_32KHZ_TIMER+void hook_rtcm1_interrupt(void) {- local_enable_irq(AU1000_TOY_MATCH2_INT);+ extern irqreturn_t rtcm1_irq(int irq, void *dev_id, struct pt_regs *regs);+ static struct irqaction action;+ /* This is a big problem.... since we didn't use request_irq+ when kernel/irq.c calls probe_irq_xxx this interrupt will+ be probed for usage. This will end up disabling the device :(++ Give it a bogus "action" pointer -- this will keep it from+ getting auto-probed!++ By setting the status to match that of request_irq() we+ can avoid it. --cgray+ */+ action.dev_id = rtcm1_irq;+ action.flags = SA_SHIRQ | SA_INTERRUPT;+ cpus_clear(action.mask);+ action.name = "Au1 RTCM1 timer";+ action.handler = rtcm1_irq;+ action.next = NULL;++ irq_desc[AU1000_RTC_MATCH1_INT].action = &action;+ irq_desc[AU1000_RTC_MATCH1_INT].status+ &= ~(IRQ_DISABLED | IRQ_AUTODETECT | IRQ_WAITING | IRQ_INPROGRESS);++ local_enable_irq(AU1000_RTC_MATCH1_INT); }-#endif+#endif /* CONFIG_SOC_AU1X00_32KHZ_TIMER */+ static void setup_local_irq(unsigned int irq_nr, int type, int int_req) {@@ -422,7 +443,9 @@ extern int au1xxx_ic0_nr_irqs; cp0_status = read_c0_status();+ /* This causes issues when using IRQ probing -- IDE for example memset(irq_desc, 0, sizeof(irq_desc));+ */ set_except_vector(0, au1000_IRQ); /* Initialize interrupt controllers to a safe state.@@ -465,7 +488,7 @@ imp++; } - set_c0_status(ALLINTS);+ set_c0_status(IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5); /* Board specific IRQ initialization. */@@ -517,17 +540,7 @@ irq = au_ffs(intc0_req1) - 1; intc0_req1 &= ~(1<<irq);-#ifdef CONFIG_PM- if (irq == AU1000_TOY_MATCH2_INT) {- mask_and_ack_rise_edge_irq(irq);- counter0_irq(irq, NULL, regs);- local_enable_irq(irq);- }- else-#endif- {- do_IRQ(irq, regs);- }+ do_IRQ(irq, regs); } diff -Naur -x CVS linux26-cvs/arch/mips/au1000/common/power.c _timer/arch/mips/au1000/common/power.c--- linux26-cvs/arch/mips/au1000/common/power.c 2005-05-16 13:39:23.092042408 -0500+++ _timer/arch/mips/au1000/common/power.c 2005-05-13 22:19:33.000000000 -0500@@ -97,7 +97,7 @@ #define SLEEP_TEST_TIMEOUT 1 #ifdef SLEEP_TEST_TIMEOUT static int sleep_ticks;-void wakeup_counter0_set(int ticks);+void wakeup_toym2_set(int ticks); #endif static void@@ -165,7 +165,7 @@ restore_core_regs(void) { extern void restore_au1xxx_intctl(void);- extern void wakeup_counter0_adjust(void);+ extern void wakeup_kernel_timer(void); au_writel(sleep_aux_pll_cntrl, SYS_AUXPLL); au_sync(); au_writel(sleep_cpu_pll_cntrl, SYS_CPUPLL); au_sync();@@ -200,7 +200,7 @@ } restore_au1xxx_intctl();- wakeup_counter0_adjust();+ wakeup_kernel_timer(); } unsigned long suspend_mode;@@ -239,7 +239,7 @@ /* For testing, allow match20 to wake us up. */ #ifdef SLEEP_TEST_TIMEOUT- wakeup_counter0_set(sleep_ticks);+ wakeup_toym2_set(sleep_ticks); #endif wakeup = 1 << 8; /* turn on match20 wakeup */ wakeup = 0;diff -Naur -x CVS linux26-cvs/arch/mips/au1000/common/setup.c _timer/arch/mips/au1000/common/setup.c--- linux26-cvs/arch/mips/au1000/common/setup.c 2005-05-16 13:39:23.092042408 -0500+++ _timer/arch/mips/au1000/common/setup.c 2005-05-13 22:19:41.000000000 -0500@@ -148,12 +148,13 @@ iomem_resource.start = IOMEM_RESOURCE_START; iomem_resource.end = IOMEM_RESOURCE_END; +#if 0 /* was removed in the timer patch 2.4.30 */ while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_E0S); au_writel(SYS_CNTRL_E0 | SYS_CNTRL_EN0, SYS_COUNTER_CNTRL); au_sync(); while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T0S); au_writel(0, SYS_TOYTRIM);-+#endif return 0; } diff -Naur -x CVS linux26-cvs/arch/mips/au1000/common/time.c _timer/arch/mips/au1000/common/time.c--- linux26-cvs/arch/mips/au1000/common/time.c 2005-05-16 13:39:23.092042408 -0500+++ _timer/arch/mips/au1000/common/time.c 2005-05-13 22:18:01.000000000 -0500@@ -23,13 +23,16 @@ * * ######################################################################## *- * Setting up the clock on the MIPS boards.+ * Setting up the clock on the Alchemy boards. * * Update. Always configure the kernel with CONFIG_NEW_TIME_C. This * will use the user interface gettimeofday() functions from the * arch/mips/kernel/time.c, and we provide the clock interrupt processing- * and the timer offset compute functions. If CONFIG_PM is selected,- * we also ensure the 32KHz timer is available. -- Dan+ * and the timer offset compute functions. -- Dan+ */++/*+ * See discussion on kernel timer in Docuemtation/mips/Alchemy.README */ #include <linux/types.h>@@ -38,432 +41,572 @@ #include <linux/kernel_stat.h> #include <linux/sched.h> #include <linux/spinlock.h>+#include <linux/kernel.h>+#include <linux/fcntl.h>+#include <linux/miscdevice.h>+#include <linux/module.h> #include <linux/hardirq.h>+#include <linux/fs.h> #include <asm/compiler.h> #include <asm/mipsregs.h> #include <asm/ptrace.h> #include <asm/time.h>-#include <asm/div64.h>+#include <asm/debug.h>+#include <asm/time.h>+#include <asm/uaccess.h>+ #include <asm/mach-au1x00/au1000.h> -#include <linux/mc146818rtc.h> #include <linux/timex.h> +/* I haven't found anyone that doesn't use a 12 MHz source clock,+ * but just in case.....+ */+#ifndef CONFIG_AU1000_SRC_CLK+#define CONFIG_AU1000_SRC_CLK 12000000+#endif+ extern void startup_match20_interrupt(void);-extern void do_softirq(void);-extern volatile unsigned long wall_jiffies;-unsigned long missed_heart_beats = 0;--static unsigned long r4k_offset; /* Amount to increment compare reg each time */-static unsigned long r4k_cur; /* What counter should be at next timer irq */-int no_au1xxx_32khz; void (*au1k_wait_ptr)(void); -/* Cycle counter value at the previous timer interrupt.. */-static unsigned int timerhi = 0, timerlo = 0; -#ifdef CONFIG_PM-#define MATCH20_INC 328-extern void startup_match20_interrupt(void);-static unsigned long last_pc0, last_match20;+#ifndef CONFIG_AU1000_32KHZ_CLK+#define CONFIG_AU1000_32KHZ_CLK 32768 #endif -static DEFINE_SPINLOCK(time_lock);+/* I haven't found anyone that doesn't use a 12 MHz source clock,+ * but just in case.....+ */+#ifdef CONFIG_AU1000_SRC_CLK+#define AU1000_SRC_CLK CONFIG_AU1000_SRC_CLK+#else+#define AU1000_SRC_CLK 12000000+#endif -static inline void ack_r4ktimer(unsigned long newval)-{- write_c0_compare(newval);-} -/*- * There are a lot of conceptually broken versions of the MIPS timer interrupt- * handler floating around. This one is rather different, but the algorithm- * is provably more robust.- */-unsigned long wtimer;-void mips_timer_interrupt(struct pt_regs *regs)-{- int irq = 63;- unsigned long count;+static DEFINE_SPINLOCK(time_lock);+static u32 r4k_offset; /* Amount to increment compare reg each time */+static u32 r4k_cur; /* What counter should be at next timer irq */+/* Cycle counter value at the previous timer interrupt.. */+static unsigned int timerhi = 0, timerlo = 0; - irq_enter();- kstat_this_cpu.irqs[irq]++; - if (r4k_offset == 0)- goto null;+#ifdef CONFIG_SOC_AU1X00_32KHZ_TIMER+/* Use the integrated RTC timer as the system timer tick. RTC (counter1)+match1 is used as the tick source */++static volatile u32 rtcm1_offset; /* Amount to increment match reg each time */+static volatile u32 rtcm1_cur; /* What counter should be at next timer irq */+static volatile u32 rtcm1_last; /* What match/counter was at at the last timer irq */+static volatile u32 rtcm1_drift; - do {- count = read_c0_count();- timerhi += (count < timerlo); /* Wrap around */- timerlo = count;+irqreturn_t rtcm1_irq(int irq, void *dev_id, struct pt_regs *regs)+{+ /* This handler mirrors mips_timer_interrupt() below, except that+ this handler is called by do_IRQ() */+ static int jiffie_drift = 0;+ int num_do_timer = 0;+ u32 rtc;++ /* disable all other IRQs. without this, this handler can be+ interrupted which allows the RTC to advance *WAY* ahead of the RTCM1+ value which causes the kernel to hang, waiting for rollover. */+ spin_lock_irq(&time_lock); - kstat_this_cpu.irqs[irq]++;- do_timer(regs); #ifndef CONFIG_SMP- update_process_times(user_mode(regs));+ update_process_times(user_mode(regs)); #endif- r4k_cur += r4k_offset;- ack_r4ktimer(r4k_cur);+ /* check if RTC counter has rolled-over, but rtcm1_cur has not */+ rtc = au_readl(SYS_RTCREAD);+ while (rtc < rtcm1_cur) {+ ++num_do_timer;+ rtcm1_last = rtcm1_cur;+ rtcm1_cur += rtcm1_offset;+ }+ while (rtcm1_cur <= rtc) {+ ++num_do_timer;+ rtcm1_last = rtcm1_cur;+ rtcm1_cur += rtcm1_offset;+ /* check if rtcm1_cur has rolled over, and thus ahead of rtc */+ if (rtcm1_last > rtcm1_cur)+ break;+ }+ jiffie_drift += num_do_timer;+ if ((rtcm1_drift != 0) && (jiffie_drift >= rtcm1_drift)) {+ jiffie_drift -= rtcm1_drift;+ ++num_do_timer;+ }++ /* Check to make sure that the counter is not at the value we're about+ to program into the match; if so, adjust accordingly. */+ rtc = au_readl(SYS_RTCREAD);++ /* Check to make sure that the value programmed into RTCM1 is no more+ than rtcm1_offset ticks away. This is complicated by the need to handle+ 32-bit rollover of the counter, and the fact that the RTC may be ahead+ of RTCM1. This value represents the number of RTC ticks needed for this+ code to read the current RTC value, make any decisions, and write+ to RTCMATCH1. */+#define RTCM1_UPDATE_DELAY 8+ rtc += RTCM1_UPDATE_DELAY;++ if (rtcm1_cur > rtc) {+ if ((rtcm1_cur - rtc) < (2 * rtcm1_offset))+ /* rtcm1_cur is just ahead of rtc */+ au_writel(rtcm1_cur, SYS_RTCMATCH1); /* normal update */+ else+ /* rtc has rolled-over, ahead of rtcm1_cur */+ au_writel(rtc + 2, SYS_RTCMATCH1); /* special update */+ } else {+ /* rtc > rtcm1_cur */+ if ((rtc - rtcm1_cur) < (2 * rtcm1_offset))+ /* rtc is just ahead of rtcm1_cur */+ au_writel(rtc + 2, SYS_RTCMATCH1); /* special update */+ else+ /* rtcm1_cur has rolled-over, ahead of rtc */+ au_writel(rtcm1_cur, SYS_RTCMATCH1); /* normal update */+ } - } while (((unsigned long)read_c0_count()- - r4k_cur) < 0x7fffffff);+ while (num_do_timer-- > 0)+ do_timer(regs); - irq_exit();- return;+ /* SYS_CNTRCTRL_RM1 shouldn't need to be examined since this register is+ written no faster than HZ rate, which is plenty long for the updates to+ occur. Nonetheless, in practice this is needed, but by doing it at the+ end, where the update likely should have already occured, we allow the+ update to RTCM1 to occur in parallel with the calls to do_timer().+ This also guarantees that the next rtcm1_irq() write to RTCMATCH1 can+ occur without polling. */+ while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRCTRL_RM1); -null:- ack_r4ktimer(0);+ spin_unlock_irq(&time_lock);+ return IRQ_HANDLED; } -#ifdef CONFIG_PM-void counter0_irq(int irq, void *dev_id, struct pt_regs *regs)+static void setup_rtcm1_interrupt(void) {- unsigned long pc0;- int time_elapsed;- static int jiffie_drift = 0;+ /* Setup match1 to interrupt HZ times per second.*/+ rtcm1_offset = CONFIG_AU1000_32KHZ_CLK / HZ;+ if ((rtcm1_offset * HZ) != CONFIG_AU1000_32KHZ_CLK)+ rtcm1_offset += 1; /* round up */+ /* Determine the number of ticks until we are one tick off the real rtc+ due to rounding of rtcm1_offset. */+ rtcm1_drift = CONFIG_AU1000_32KHZ_CLK / (rtcm1_offset * HZ - CONFIG_AU1000_32KHZ_CLK);+ rtcm1_cur = au_readl(SYS_RTCREAD) + rtcm1_offset;+ while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRCTRL_RM1);+ au_writel(rtcm1_cur, SYS_RTCMATCH1);+ while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRCTRL_RM1);+} - kstat.irqs[0][irq]++;- if (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20) {- /* should never happen! */- printk(KERN_WARNING "counter 0 w status eror\n");- return;- }+static unsigned long do_fast_rtcm1_gettimeoffset(void)+{+ unsigned long rtc, offset;+ static u32 num_huge_offsets = 0; - pc0 = au_readl(SYS_TOYREAD);- if (pc0 < last_match20) {- /* counter overflowed */- time_elapsed = (0xffffffff - last_match20) + pc0;- }- else {- time_elapsed = pc0 - last_match20;+ rtc = au_readl(SYS_RTCREAD);+ if (rtc < rtcm1_last) /* RTC has rolled-over */+ offset = (0 - rtcm1_last) + rtc;+ else+ offset = rtc - rtcm1_last;++ /* NOTE: The huge offsets are unavoidable since Linux's interrupt+ response time is highly irregular. */+ if (offset > 2*rtcm1_offset) {+ if (++num_huge_offsets > 100) {+ printk("huge offset %x, rtc %x, rtcm1_last %x rtcm1 %x rtcm1_cur %x\n",+ (int)offset, (int)rtc, rtcm1_last, au_readl(SYS_RTCMATCH1), rtcm1_cur);+ num_huge_offsets = 0;+ } } - while (time_elapsed > 0) {- do_timer(regs);-#ifndef CONFIG_SMP
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -