LKML Archive on lore.kernel.org help / color / mirror / Atom feed
* [PATCH query] arm: i.MX/MX1 clock event source @ 2007-01-21 0:01 Pavel Pisa 2007-01-22 19:59 ` Ingo Molnar 0 siblings, 1 reply; 6+ messages in thread From: Pavel Pisa @ 2007-01-21 0:01 UTC (permalink / raw) To: Thomas Gleixner; +Cc: Ingo Molnar, linux-kernel, Sascha Hauer Hello Thomas, Sascha and Ingo please can you find some time to review next patch arm: i.MX/MX1 clock event source which has been sent to you and to the ALKML at 2007-01-13. http://thread.gmane.org/gmane.linux.ports.arm.kernel/29510/focus=29533 There seems to be some problems, because this patch has not been accepted to patch-2.6.20-rc5-rt7.patch, but GENERIC_CLOCKEVENTS are set already for i.MX and this results in a problems to run RT kernel on this architecture. config ARCH_IMX bool "IMX" + select GENERIC_TIME + select GENERIC_CLOCKEVENTS Thanks for review and your time Pavel ------------------------------------------------------------------------------------- Subject: arm: i.MX/MX1 clock event source Support clock event source based on i.MX general purpose timer in free running timer mode. Signed-off-by: Pavel Pisa <pisa@cmp.felk.cvut.cz> arch/arm/mach-imx/time.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) Index: linux-2.6.20-rc4/arch/arm/mach-imx/time.c =================================================================== --- linux-2.6.20-rc4.orig/arch/arm/mach-imx/time.c +++ linux-2.6.20-rc4/arch/arm/mach-imx/time.c @@ -15,6 +15,9 @@ #include <linux/irq.h> #include <linux/time.h> #include <linux/clocksource.h> +#ifdef CONFIG_GENERIC_CLOCKEVENTS +#include <linux/clockchips.h> +#endif #include <asm/hardware.h> #include <asm/io.h> @@ -25,6 +28,11 @@ /* Use timer 1 as system timer */ #define TIMER_BASE IMX_TIM1_BASE +#ifdef CONFIG_GENERIC_CLOCKEVENTS +static struct clock_event_device clockevent_imx; +static enum clock_event_mode clockevent_mode = CLOCK_EVT_PERIODIC; +#endif + static unsigned long evt_diff; /* @@ -42,9 +50,16 @@ imx_timer_interrupt(int irq, void *dev_i if (tstat & TSTAT_COMP) { do { +#ifdef CONFIG_GENERIC_CLOCKEVENTS + if (clockevent_imx.event_handler) + clockevent_imx.event_handler(); + if (likely(clockevent_mode != CLOCK_EVT_PERIODIC)) + break; +#else write_seqlock(&xtime_lock); timer_tick(); write_sequnlock(&xtime_lock); +#endif IMX_TCMP(TIMER_BASE) += evt_diff; } while (unlikely((int32_t)(IMX_TCMP(TIMER_BASE) @@ -99,11 +114,88 @@ static int __init imx_clocksource_init(v return 0; } +#ifdef CONFIG_GENERIC_CLOCKEVENTS + +static void imx_set_next_event(unsigned long evt, + struct clock_event_device *unused) +{ + evt_diff = evt; + IMX_TCMP(TIMER_BASE) = IMX_TCN(TIMER_BASE) + evt; +} + +static void imx_set_mode(enum clock_event_mode mode, struct clock_event_device *evt) +{ + unsigned long flags; + + /* + * The timer interrupt generation is disabled at least + * for enough time to call imx_set_next_event() + */ + local_irq_save(flags); + /* Disable interrupt in GPT module */ + IMX_TCTL(TIMER_BASE) &= ~TCTL_IRQEN; + if ((mode != CLOCK_EVT_PERIODIC) || (mode != clockevent_mode)) { + /* Set event time into far-far future */ + IMX_TCMP(TIMER_BASE) = IMX_TCN(TIMER_BASE) - 3; + /* Clear pending interrupt */ + IMX_TSTAT(TIMER_BASE) &= ~TSTAT_COMP; + } + /* Remember timer mode */ + clockevent_mode = mode; + local_irq_restore(flags); + + switch (mode) { + case CLOCK_EVT_PERIODIC: + case CLOCK_EVT_ONESHOT: + /* + * Do not put overhead of interrupt enable/disable into + * imx_set_next_event(), the core has about 4 minutes + * to call imx_set_next_event() or shutdown clock after + * mode switching + */ + local_irq_save(flags); + IMX_TCTL(TIMER_BASE) |= TCTL_IRQEN; + local_irq_restore(flags); + break; + case CLOCK_EVT_SHUTDOWN: + /* Left event sources disabled, no more interrupts appears */ + break; + } +} + +static struct clock_event_device clockevent_imx = { + .name = "imx_timer1", + .capabilities = CLOCK_CAP_NEXTEVT | CLOCK_CAP_TICK | + CLOCK_CAP_UPDATE | CLOCK_CAP_PROFILE, + .shift = 32, + .set_mode = imx_set_mode, + .set_next_event = imx_set_next_event, +}; + +static int __init imx_clockevent_init(void) +{ + clockevent_imx.mult = div_sc(imx_get_perclk1(), NSEC_PER_SEC, + clockevent_imx.shift); + clockevent_imx.max_delta_ns = + clockevent_delta2ns(0xfffffffe, &clockevent_imx); + clockevent_imx.min_delta_ns = + clockevent_delta2ns(0xf, &clockevent_imx); + register_local_clockevent(&clockevent_imx); + + return 0; +} +#endif + + static void __init imx_timer_init(void) { imx_timer_hardware_init(); imx_clocksource_init(); +#ifdef CONFIG_GENERIC_CLOCKEVENTS + imx_clockevent_init(); +#endif + /* * Make irqs happen for the system timer */ ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH query] arm: i.MX/MX1 clock event source 2007-01-21 0:01 [PATCH query] arm: i.MX/MX1 clock event source Pavel Pisa @ 2007-01-22 19:59 ` Ingo Molnar 2007-01-23 2:52 ` Pavel Pisa 0 siblings, 1 reply; 6+ messages in thread From: Ingo Molnar @ 2007-01-22 19:59 UTC (permalink / raw) To: Pavel Pisa; +Cc: Thomas Gleixner, linux-kernel, Sascha Hauer * Pavel Pisa <pisa@cmp.felk.cvut.cz> wrote: > Hello Thomas, Sascha and Ingo > > please can you find some time to review next patch > arm: i.MX/MX1 clock event source > which has been sent to you and to the ALKML at 2007-01-13. > > http://thread.gmane.org/gmane.linux.ports.arm.kernel/29510/focus=29533 > > There seems to be some problems, because this patch has not been > accepted to patch-2.6.20-rc5-rt7.patch, but GENERIC_CLOCKEVENTS are > set already for i.MX and this results in a problems to run RT kernel > on this architecture. i've added your patch to -rt, but note that there's a new, slightly incompatible clockevents code in -rt now so you'll need to do some more (hopefully trivial) fixups for this to build and work. Ingo ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH query] arm: i.MX/MX1 clock event source 2007-01-22 19:59 ` Ingo Molnar @ 2007-01-23 2:52 ` Pavel Pisa 2007-01-24 2:00 ` Pavel Pisa 0 siblings, 1 reply; 6+ messages in thread From: Pavel Pisa @ 2007-01-23 2:52 UTC (permalink / raw) To: Ingo Molnar; +Cc: Thomas Gleixner, linux-kernel, Sascha Hauer On Monday 22 January 2007 20:59, Ingo Molnar wrote: > * Pavel Pisa <pisa@cmp.felk.cvut.cz> wrote: > > Hello Thomas, Sascha and Ingo > > > > please can you find some time to review next patch > > arm: i.MX/MX1 clock event source > > which has been sent to you and to the ALKML at 2007-01-13. > > > > http://thread.gmane.org/gmane.linux.ports.arm.kernel/29510/focus=29533 > > > > There seems to be some problems, because this patch has not been > > accepted to patch-2.6.20-rc5-rt7.patch, but GENERIC_CLOCKEVENTS are > > set already for i.MX and this results in a problems to run RT kernel > > on this architecture. > > i've added your patch to -rt, but note that there's a new, slightly > incompatible clockevents code in -rt now so you'll need to do some more > (hopefully trivial) fixups for this to build and work. > > Ingo Hello Ingo, thanks for reply. I am attaching updated version of the patch at the end of e-mail. There is problem with missing include in tick-sched.c CC kernel/time/tick-sched.o /usr/src/linux-2.6.20-rc5/kernel/time/tick-sched.c: In function `tick_nohz_handler': /usr/src/linux-2.6.20-rc5/kernel/time/tick-sched.c:330: warning: implicit declaration of function `get_irq_regs' /usr/src/linux-2.6.20-rc5/kernel/time/tick-sched.c:330: warning: initialization makes pointer from integer without a cast /usr/src/linux-2.6.20-rc5/kernel/time/tick-sched.c: In function `tick_sched_timer': /usr/src/linux-2.6.20-rc5/kernel/time/tick-sched.c:425: warning: initialization makes pointer from integer without a cast LD kernel/time/built-in.o --- linux-2.6.20-rc5.orig/kernel/time/tick-sched.c +++ linux-2.6.20-rc5/kernel/time/tick-sched.c @@ -20,6 +20,7 @@ #include <linux/profile.h> #include <linux/sched.h> #include <linux/tick.h> +#include <asm/irq_regs.h> #include "tick-internal.h" And CC arch/arm/kernel/process.o /usr/src/linux-2.6.20-rc5/arch/arm/kernel/process.c: In function `cpu_idle': /usr/src/linux-2.6.20-rc5/arch/arm/kernel/process.c:157: warning: implicit declaration of function `hrtimer_stop_sched_tick' /usr/src/linux-2.6.20-rc5/arch/arm/kernel/process.c:161: warning: implicit declaration of function `hrtimer_restart_sched_tick' --- linux-2.6.20-rc5.orig/arch/arm/kernel/process.c +++ linux-2.6.20-rc5/arch/arm/kernel/process.c @@ -154,11 +154,11 @@ void cpu_idle(void) if (!idle) idle = default_idle; leds_event(led_idle_start); - hrtimer_stop_sched_tick(); + tick_nohz_stop_sched_tick(); while (!need_resched() && !need_resched_delayed()) idle(); leds_event(led_idle_end); - hrtimer_restart_sched_tick(); + tick_nohz_restart_sched_tick(); local_irq_disable(); __preempt_enable_no_resched(); __schedule(); Unfortunately, even with these corrections boot stuck at Memory: 18972KB available (2488K code, 358K data, 92K init) I have not time now to start JTAG debugging session, so I look at that tomorrow or on Friday. It seems, that the interrupts are not coming from device. Best wishes Pavel ========================================================================== Subject: arm: i.MX/MX1 clock event source Support clock event source based on i.MX general purpose timer in free running timer mode. Signed-off-by: Pavel Pisa <pisa@cmp.felk.cvut.cz> arch/arm/mach-imx/time.c | 112 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 107 insertions(+), 5 deletions(-) Index: linux-2.6.20-rc5/arch/arm/mach-imx/time.c =================================================================== --- linux-2.6.20-rc5.orig/arch/arm/mach-imx/time.c +++ linux-2.6.20-rc5/arch/arm/mach-imx/time.c @@ -15,6 +15,9 @@ #include <linux/irq.h> #include <linux/time.h> #include <linux/clocksource.h> +#ifdef CONFIG_GENERIC_CLOCKEVENTS +#include <linux/clockchips.h> +#endif #include <asm/hardware.h> #include <asm/io.h> @@ -25,6 +28,11 @@ /* Use timer 1 as system timer */ #define TIMER_BASE IMX_TIM1_BASE +#ifdef CONFIG_GENERIC_CLOCKEVENTS +static struct clock_event_device clockevent_imx; +static enum clock_event_mode clockevent_mode = CLOCK_EVT_MODE_PERIODIC; +#endif + static unsigned long evt_diff; /* @@ -33,6 +41,7 @@ static unsigned long evt_diff; static irqreturn_t imx_timer_interrupt(int irq, void *dev_id) { + unsigned long tcmp; uint32_t tstat; /* clear the interrupt */ @@ -42,13 +51,20 @@ imx_timer_interrupt(int irq, void *dev_i if (tstat & TSTAT_COMP) { do { +#ifdef CONFIG_GENERIC_CLOCKEVENTS + if (clockevent_imx.event_handler) + clockevent_imx.event_handler(&clockevent_imx); + if (likely(clockevent_mode != CLOCK_EVT_MODE_PERIODIC)) + break; +#else write_seqlock(&xtime_lock); timer_tick(); write_sequnlock(&xtime_lock); - IMX_TCMP(TIMER_BASE) += evt_diff; +#endif + tcmp = IMX_TCMP(TIMER_BASE) + evt_diff; + IMX_TCMP(TIMER_BASE) = tcmp; - } while (unlikely((int32_t)(IMX_TCMP(TIMER_BASE) - - IMX_TCN(TIMER_BASE)) < 0)); + } while (unlikely((int32_t)(tcmp - IMX_TCN(TIMER_BASE)) < 0)); } return IRQ_HANDLED; @@ -70,7 +86,7 @@ static void __init imx_timer_hardware_in */ IMX_TCTL(TIMER_BASE) = 0; IMX_TPRER(TIMER_BASE) = 0; - IMX_TCMP(TIMER_BASE) = LATCH - 1; + IMX_TCMP(TIMER_BASE) += LATCH; IMX_TCTL(TIMER_BASE) = TCTL_FRR | TCTL_CLK_PCLK1 | TCTL_IRQEN | TCTL_TEN; evt_diff = LATCH; @@ -87,7 +103,7 @@ static struct clocksource clocksource_im .read = imx_get_cycles, .mask = 0xFFFFFFFF, .shift = 20, - .is_continuous = 1, + .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; static int __init imx_clocksource_init(void) @@ -99,11 +115,97 @@ static int __init imx_clocksource_init(v return 0; } +#ifdef CONFIG_GENERIC_CLOCKEVENTS + +static int imx_set_next_event(unsigned long evt, + struct clock_event_device *unused) +{ + unsigned long tcmp; + evt_diff = evt; + + tcmp = IMX_TCN(TIMER_BASE) + evt; + IMX_TCMP(TIMER_BASE) = tcmp; + + return unlikely((int32_t)(tcmp - IMX_TCN(TIMER_BASE)) < 0) ? -ETIME : 0; +} + +static void imx_set_mode(enum clock_event_mode mode, struct clock_event_device *evt) +{ + unsigned long flags; + + /* + * The timer interrupt generation is disabled at least + * for enough time to call imx_set_next_event() + */ + local_irq_save(flags); + /* Disable interrupt in GPT module */ + IMX_TCTL(TIMER_BASE) &= ~TCTL_IRQEN; + if ((mode != CLOCK_EVT_MODE_PERIODIC) || (mode != clockevent_mode)) { + /* Set event time into far-far future */ + IMX_TCMP(TIMER_BASE) = IMX_TCN(TIMER_BASE) - 3; + /* Clear pending interrupt */ + IMX_TSTAT(TIMER_BASE) &= ~TSTAT_COMP; + } + /* Remember timer mode */ + clockevent_mode = mode; + local_irq_restore(flags); + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + /* It seems, that periodic mode expects old ugly latch period */ + evt_diff = LATCH; + IMX_TCMP(TIMER_BASE) = IMX_TCN(TIMER_BASE) + evt_diff; + case CLOCK_EVT_MODE_ONESHOT: + /* + * Do not put overhead of interrupt enable/disable into + * imx_set_next_event(), the core has about 4 minutes + * to call imx_set_next_event() or shutdown clock after + * mode switching + */ + local_irq_save(flags); + IMX_TCTL(TIMER_BASE) |= TCTL_IRQEN; + local_irq_restore(flags); + break; + case CLOCK_EVT_MODE_SHUTDOWN: + case CLOCK_EVT_MODE_UNUSED: + /* Left event sources disabled, no more interrupts appears */ + break; + } +} + +static struct clock_event_device clockevent_imx = { + .name = "imx_timer1", + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .shift = 32, + .set_mode = imx_set_mode, + .set_next_event = imx_set_next_event, + .rating = 200, +}; + +static int __init imx_clockevent_init(void) +{ + clockevent_imx.mult = div_sc(imx_get_perclk1(), NSEC_PER_SEC, + clockevent_imx.shift); + clockevent_imx.max_delta_ns = + clockevent_delta2ns(0xfffffffe, &clockevent_imx); + clockevent_imx.min_delta_ns = + clockevent_delta2ns(0xf, &clockevent_imx); + clockevents_register_device(&clockevent_imx); + + return 0; +} +#endif + + static void __init imx_timer_init(void) { imx_timer_hardware_init(); imx_clocksource_init(); +#ifdef CONFIG_GENERIC_CLOCKEVENTS + imx_clockevent_init(); +#endif + /* * Make irqs happen for the system timer */ ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH query] arm: i.MX/MX1 clock event source 2007-01-23 2:52 ` Pavel Pisa @ 2007-01-24 2:00 ` Pavel Pisa 2007-01-24 11:37 ` Thomas Gleixner 0 siblings, 1 reply; 6+ messages in thread From: Pavel Pisa @ 2007-01-24 2:00 UTC (permalink / raw) To: Ingo Molnar; +Cc: Thomas Gleixner, linux-kernel, Sascha Hauer On Tuesday 23 January 2007 03:52, Pavel Pisa wrote: > > i've added your patch to -rt, but note that there's a new, slightly > > incompatible clockevents code in -rt now so you'll need to do some more > > (hopefully trivial) fixups for this to build and work. > > > > Ingo > > Hello Ingo, > > Unfortunately, even with these corrections boot stuck at > > Memory: 18972KB available (2488K code, 358K data, 92K init) > > I have not time now to start JTAG debugging session, so I look at that > tomorrow or on Friday. > > It seems, that the interrupts are not coming from device. > > Best wishes > > Pavel > Hello Ingo, I have found some time and tried to debugg problem with help of JTAG debugger. But I have found, that IRQs are processed right. The plain rc5 runs flawlessly. The rt8 doesnot start. The problem is, that handler stays NULL after clock event registration. Interrupts runs, but my code doesnot call any function. The notification chain and clock events list seems to be filled correctly. I have added clockevent_imx.cpumask = cpumask_of_cpu(0); to ensure that clock are not used for affinity mask reasons. I have tried even exchange clock event forcibly at the end of clockevent_imx initialization. But it only resulted in asking timer to switch off into unused mode. I have added next hack into IRQ write_seqlock(&xtime_lock); timer_tick(); write_sequnlock(&xtime_lock); which stays enabled until imx_set_mode() is called first time, The system boots after this modification, but imx_set_mode() is never called and there is no switch to high resolution mode. CONFIG_ARM=y CONFIG_GENERIC_TIME=y CONFIG_GENERIC_CLOCKEVENTS=y ??? CONFIG_TICK_ONESHOT=y ??? CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y CONFIG_PREEMPT_RT=y CONFIG_PREEMPT=y CONFIG_PREEMPT_SOFTIRQS=y CONFIG_PREEMPT_HARDIRQS=y CONFIG_PREEMPT_BKL=y Could I preset some handler directly during timer initialization? Can I declare somehow, that I want to use that clock event device as source of tick from beginning? Should I try to rebuild with CONFIG_NO_HZ=n? I would like to keep up with changes, because I have tested RTs for ARM already over more "stable" releases and over many rc-s. And high resolution timers support has worked well over for me last three months (up to 2.6.20-rc4). So I want to learn what is required to be compatible with latest code. Thanks for any hints to the problem Pavel ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH query] arm: i.MX/MX1 clock event source 2007-01-24 2:00 ` Pavel Pisa @ 2007-01-24 11:37 ` Thomas Gleixner 2007-01-26 19:34 ` [PATCH updated] " Pavel Pisa 0 siblings, 1 reply; 6+ messages in thread From: Thomas Gleixner @ 2007-01-24 11:37 UTC (permalink / raw) To: Pavel Pisa; +Cc: Ingo Molnar, linux-kernel, Sascha Hauer On Wed, 2007-01-24 at 03:00 +0100, Pavel Pisa wrote: > stays NULL after clock event registration. Interrupts > runs, but my code doesnot call any function. The notification > chain and clock events list seems to be filled correctly. > I have added > > clockevent_imx.cpumask = cpumask_of_cpu(0); Correct. I probably should disable the check for UP. > to ensure that clock are not used for affinity > mask reasons. I have tried even exchange clock > event forcibly at the end of clockevent_imx initialization. > But it only resulted in asking timer to switch off > into unused mode. > > I have added next hack into IRQ > write_seqlock(&xtime_lock); > timer_tick(); > write_sequnlock(&xtime_lock); > which stays enabled until imx_set_mode() is called first time, > The system boots after this modification, but imx_set_mode() > is never called and there is no switch to high resolution mode. You should not set up the timer at all. The setup of the timer happens from the registration. We need to know, why the timer is not picked from the tick code in the first place and why set_mode() is not called. tglx ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH updated] arm: i.MX/MX1 clock event source 2007-01-24 11:37 ` Thomas Gleixner @ 2007-01-26 19:34 ` Pavel Pisa 0 siblings, 0 replies; 6+ messages in thread From: Pavel Pisa @ 2007-01-26 19:34 UTC (permalink / raw) To: tglx; +Cc: Ingo Molnar, linux-kernel, Sascha Hauer On Wednesday 24 January 2007 12:37, Thomas Gleixner wrote: > On Wed, 2007-01-24 at 03:00 +0100, Pavel Pisa wrote: > > stays NULL after clock event registration. Interrupts > > runs, but my code doesnot call any function. The notification > > chain and clock events list seems to be filled correctly. > > I have added > > > > clockevent_imx.cpumask = cpumask_of_cpu(0); > > Correct. I probably should disable the check for UP. I have made mistake last time on 2.6.20-rc5-rt8. I have returned to testing today with 2.6.20-rc6-rt2. I have added checking outputs to the tickregistration routines, but all works correctly after right cpumask correction. I have rechecked even without additional printks and I have observed no problems. The clock events enabled kernels requires next minor changes to compile on ARM still ================================================================================ ================================================================================ arch/arm/kernel/process.c | 5 +++-- kernel/time/tick-sched.c | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) Index: linux-2.6.20-rc6/kernel/time/tick-sched.c =================================================================== --- linux-2.6.20-rc6.orig/kernel/time/tick-sched.c +++ linux-2.6.20-rc6/kernel/time/tick-sched.c @@ -20,6 +20,7 @@ #include <linux/profile.h> #include <linux/sched.h> #include <linux/tick.h> +#include <asm/irq_regs.h> #include "tick-internal.h" Index: linux-2.6.20-rc6/arch/arm/kernel/process.c =================================================================== --- linux-2.6.20-rc6.orig/arch/arm/kernel/process.c +++ linux-2.6.20-rc6/arch/arm/kernel/process.c @@ -28,6 +28,7 @@ #include <linux/cpu.h> #include <linux/elfcore.h> #include <linux/pm.h> +#include <linux/tick.h> #include <asm/leds.h> #include <asm/processor.h> @@ -154,11 +155,11 @@ void cpu_idle(void) if (!idle) idle = default_idle; leds_event(led_idle_start); - hrtimer_stop_sched_tick(); + tick_nohz_stop_sched_tick(); while (!need_resched() && !need_resched_delayed()) idle(); leds_event(led_idle_end); - hrtimer_restart_sched_tick(); + tick_nohz_restart_sched_tick(); local_irq_disable(); __preempt_enable_no_resched(); __schedule(); ================================================================================ ================================================================================ Updated version of i.MX patch follows. If somebody wants it, I can send patch to revert code of previous version from full 2.6.20-rc6-rt2 patch. Ingo, please, can you replace i.MX1 patch in your series? Thanks Pavel ================================================================================ ================================================================================ Subject: arm: i.MX/MX1 clock event source Support clock event source based on i.MX general purpose timer in free running timer mode. Signed-off-by: Pavel Pisa <pisa@cmp.felk.cvut.cz> arch/arm/mach-imx/time.c | 130 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 125 insertions(+), 5 deletions(-) Index: linux-2.6.20-rc6/arch/arm/mach-imx/time.c =================================================================== --- linux-2.6.20-rc6.orig/arch/arm/mach-imx/time.c +++ linux-2.6.20-rc6/arch/arm/mach-imx/time.c @@ -15,6 +15,9 @@ #include <linux/irq.h> #include <linux/time.h> #include <linux/clocksource.h> +#ifdef CONFIG_GENERIC_CLOCKEVENTS +#include <linux/clockchips.h> +#endif #include <asm/hardware.h> #include <asm/io.h> @@ -25,6 +28,11 @@ /* Use timer 1 as system timer */ #define TIMER_BASE IMX_TIM1_BASE +#ifdef CONFIG_GENERIC_CLOCKEVENTS +static struct clock_event_device clockevent_imx; +static enum clock_event_mode clockevent_mode = CLOCK_EVT_MODE_PERIODIC; +#endif + static unsigned long evt_diff; /* @@ -33,6 +41,7 @@ static unsigned long evt_diff; static irqreturn_t imx_timer_interrupt(int irq, void *dev_id) { + unsigned long tcmp; uint32_t tstat; /* clear the interrupt */ @@ -42,13 +51,20 @@ imx_timer_interrupt(int irq, void *dev_i if (tstat & TSTAT_COMP) { do { +#ifdef CONFIG_GENERIC_CLOCKEVENTS + if (clockevent_imx.event_handler) + clockevent_imx.event_handler(&clockevent_imx); + if (likely(clockevent_mode != CLOCK_EVT_MODE_PERIODIC)) + break; +#else write_seqlock(&xtime_lock); timer_tick(); write_sequnlock(&xtime_lock); - IMX_TCMP(TIMER_BASE) += evt_diff; +#endif + tcmp = IMX_TCMP(TIMER_BASE) + evt_diff; + IMX_TCMP(TIMER_BASE) = tcmp; - } while (unlikely((int32_t)(IMX_TCMP(TIMER_BASE) - - IMX_TCN(TIMER_BASE)) < 0)); + } while (unlikely((int32_t)(tcmp - IMX_TCN(TIMER_BASE)) < 0)); } return IRQ_HANDLED; @@ -70,7 +86,7 @@ static void __init imx_timer_hardware_in */ IMX_TCTL(TIMER_BASE) = 0; IMX_TPRER(TIMER_BASE) = 0; - IMX_TCMP(TIMER_BASE) = LATCH - 1; + IMX_TCMP(TIMER_BASE) = IMX_TCN(TIMER_BASE) + LATCH; IMX_TCTL(TIMER_BASE) = TCTL_FRR | TCTL_CLK_PCLK1 | TCTL_IRQEN | TCTL_TEN; evt_diff = LATCH; @@ -87,7 +103,7 @@ static struct clocksource clocksource_im .read = imx_get_cycles, .mask = 0xFFFFFFFF, .shift = 20, - .is_continuous = 1, + .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; static int __init imx_clocksource_init(void) @@ -99,11 +115,115 @@ static int __init imx_clocksource_init(v return 0; } +#ifdef CONFIG_GENERIC_CLOCKEVENTS + +static int imx_set_next_event(unsigned long evt, + struct clock_event_device *unused) +{ + unsigned long tcmp; + evt_diff = evt; + + tcmp = IMX_TCN(TIMER_BASE) + evt; + IMX_TCMP(TIMER_BASE) = tcmp; + + return unlikely((int32_t)(tcmp - IMX_TCN(TIMER_BASE)) < 0) ? -ETIME : 0; +} + +#ifdef DEBUG +static const char *clock_event_mode_label[]={ + [CLOCK_EVT_MODE_PERIODIC] = "CLOCK_EVT_MODE_PERIODIC", + [CLOCK_EVT_MODE_ONESHOT] = "CLOCK_EVT_MODE_ONESHOT", + [CLOCK_EVT_MODE_SHUTDOWN] = "CLOCK_EVT_MODE_SHUTDOWN", + [CLOCK_EVT_MODE_UNUSED] = "CLOCK_EVT_MODE_UNUSED" +}; +#endif /*DEBUG*/ + +static void imx_set_mode(enum clock_event_mode mode, struct clock_event_device *evt) +{ + unsigned long flags; + + /* + * The timer interrupt generation is disabled at least + * for enough time to call imx_set_next_event() + */ + local_irq_save(flags); + /* Disable interrupt in GPT module */ + IMX_TCTL(TIMER_BASE) &= ~TCTL_IRQEN; + if ((mode != CLOCK_EVT_MODE_PERIODIC) || (mode != clockevent_mode)) { + /* Set event time into far-far future */ + IMX_TCMP(TIMER_BASE) = IMX_TCN(TIMER_BASE) - 3; + /* Clear pending interrupt */ + IMX_TSTAT(TIMER_BASE) &= ~TSTAT_COMP; + } + +#ifdef DEBUG + printk(KERN_INFO "imx_set_mode: changing mode from %s to %s\n", + clock_event_mode_label[clockevent_mode], clock_event_mode_label[mode]); +#endif /*DEBUG*/ + + /* Remember timer mode */ + clockevent_mode = mode; + local_irq_restore(flags); + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + /* It seems, that periodic mode expects old ugly latch period */ + evt_diff = LATCH; + IMX_TCMP(TIMER_BASE) = IMX_TCN(TIMER_BASE) + evt_diff; + case CLOCK_EVT_MODE_ONESHOT: + /* + * Do not put overhead of interrupt enable/disable into + * imx_set_next_event(), the core has about 4 minutes + * to call imx_set_next_event() or shutdown clock after + * mode switching + */ + local_irq_save(flags); + IMX_TCTL(TIMER_BASE) |= TCTL_IRQEN; + local_irq_restore(flags); + break; + case CLOCK_EVT_MODE_SHUTDOWN: + case CLOCK_EVT_MODE_UNUSED: + /* Left event sources disabled, no more interrupts appears */ + break; + } +} + +static struct clock_event_device clockevent_imx = { + .name = "imx_timer1", + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .shift = 32, + .set_mode = imx_set_mode, + .set_next_event = imx_set_next_event, + .rating = 200, +}; + +static int __init imx_clockevent_init(void) +{ + clockevent_imx.mult = div_sc(imx_get_perclk1(), NSEC_PER_SEC, + clockevent_imx.shift); + clockevent_imx.max_delta_ns = + clockevent_delta2ns(0xfffffffe, &clockevent_imx); + clockevent_imx.min_delta_ns = + clockevent_delta2ns(0xf, &clockevent_imx); + + clockevent_imx.cpumask = cpumask_of_cpu(0); + + clockevents_register_device(&clockevent_imx); + + return 0; +} +#endif + + static void __init imx_timer_init(void) { imx_timer_hardware_init(); imx_clocksource_init(); +#ifdef CONFIG_GENERIC_CLOCKEVENTS + imx_clockevent_init(); +#endif + /* * Make irqs happen for the system timer */ ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2007-01-26 19:34 UTC | newest] Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2007-01-21 0:01 [PATCH query] arm: i.MX/MX1 clock event source Pavel Pisa 2007-01-22 19:59 ` Ingo Molnar 2007-01-23 2:52 ` Pavel Pisa 2007-01-24 2:00 ` Pavel Pisa 2007-01-24 11:37 ` Thomas Gleixner 2007-01-26 19:34 ` [PATCH updated] " Pavel Pisa
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).