Re: [PATCH v3 5/7] clocksource/cadence_ttc: Use only one counter
Hi Thomas, On Tue, Feb 04, 2014 at 09:41:53PM +0100, Thomas Gleixner wrote: > On Mon, 3 Feb 2014, Soren Brinkmann wrote: > > > Currently the driver uses two of the three counters the TTC provides to > > implement a clocksource and a clockevent device. By using the TTC's > > match feature we can implement both use cases using a single counter > > only. > > Are you entirely sure that this match feature is free of the infamous > HPET match feature issues? > > See arch/x86/kernel/hpet.c: hpet_next_event() > > If yes, please add a comment. If no Good catch. Looks like the comparator compares on == as well. So, I assume it may show similar issues. I'll have to spend some more time looking into this. Thanks for applying the first two patches of the series. Thanks, Sören -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Re: [PATCH v3 5/7] clocksource/cadence_ttc: Use only one counter
Hi Thomas, On Tue, Feb 04, 2014 at 09:41:53PM +0100, Thomas Gleixner wrote: On Mon, 3 Feb 2014, Soren Brinkmann wrote: Currently the driver uses two of the three counters the TTC provides to implement a clocksource and a clockevent device. By using the TTC's match feature we can implement both use cases using a single counter only. Are you entirely sure that this match feature is free of the infamous HPET match feature issues? See arch/x86/kernel/hpet.c: hpet_next_event() If yes, please add a comment. If no Good catch. Looks like the comparator compares on == as well. So, I assume it may show similar issues. I'll have to spend some more time looking into this. Thanks for applying the first two patches of the series. Thanks, Sören -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Re: [PATCH v3 5/7] clocksource/cadence_ttc: Use only one counter
On Mon, 3 Feb 2014, Soren Brinkmann wrote: > Currently the driver uses two of the three counters the TTC provides to > implement a clocksource and a clockevent device. By using the TTC's > match feature we can implement both use cases using a single counter > only. Are you entirely sure that this match feature is free of the infamous HPET match feature issues? See arch/x86/kernel/hpet.c: hpet_next_event() If yes, please add a comment. If no Thanks, tglx -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Re: [PATCH v3 5/7] clocksource/cadence_ttc: Use only one counter
On Mon, 3 Feb 2014, Soren Brinkmann wrote: Currently the driver uses two of the three counters the TTC provides to implement a clocksource and a clockevent device. By using the TTC's match feature we can implement both use cases using a single counter only. Are you entirely sure that this match feature is free of the infamous HPET match feature issues? See arch/x86/kernel/hpet.c: hpet_next_event() If yes, please add a comment. If no Thanks, tglx -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH v3 5/7] clocksource/cadence_ttc: Use only one counter
Currently the driver uses two of the three counters the TTC provides to implement a clocksource and a clockevent device. By using the TTC's match feature we can implement both use cases using a single counter only. The old approach is to use timer over-/underflow to generate an interrupt. Using the match register allows to generate an interrupt on arbitrary counter values. This way a dedicated clockevent counter can be avoided. Signed-off-by: Soren Brinkmann --- drivers/clocksource/cadence_ttc_timer.c | 92 +++-- 1 file changed, 31 insertions(+), 61 deletions(-) diff --git a/drivers/clocksource/cadence_ttc_timer.c b/drivers/clocksource/cadence_ttc_timer.c index 49fbe2847c84..d922e982af95 100644 --- a/drivers/clocksource/cadence_ttc_timer.c +++ b/drivers/clocksource/cadence_ttc_timer.c @@ -47,6 +47,7 @@ #define TTC_CNT_CNTRL_OFFSET 0x0C /* Counter Control Reg, RW */ #define TTC_COUNT_VAL_OFFSET 0x18 /* Counter Value Reg, RO */ #define TTC_INTR_VAL_OFFSET0x24 /* Interval Count Reg, RW */ +#define TTC_MATCH1_OFFSET 0x30 /* Match reg, RW */ #define TTC_ISR_OFFSET 0x54 /* Interrupt Status Reg, RO */ #define TTC_IER_OFFSET 0x60 /* Interrupt Enable Reg, RW */ @@ -64,7 +65,10 @@ #define PRESCALE 2048/* The exponent must match this */ #define CLK_CNTRL_PRESCALE ((PRESCALE_EXPONENT - 1) << 1) #define CLK_CNTRL_PRESCALE_EN 1 -#define CNT_CNTRL_RESET(1 << 4) +#define CNT_CNTRL_RESETBIT(4) +#define CNT_CNTRL_MATCHBIT(3) + +#define TTC_INTERRUPT_MATCH1 BIT(1) #define MAX_F_ERR 50 @@ -99,6 +103,7 @@ struct ttc_timer_clocksource { struct ttc_timer_clockevent { struct ttc_timerttc; struct clock_event_device ce; + unsigned long interval; }; #define to_ttc_timer_clkevent(x) \ @@ -112,25 +117,20 @@ static void __iomem *ttc_sched_clock_val_reg; * @timer: Pointer to the timer instance * @cycles:Timer interval ticks **/ -static void ttc_set_interval(struct ttc_timer *timer, - unsigned long cycles) +static void ttc_set_interval(struct ttc_timer *timer, unsigned long cycles) { - u32 ctrl_reg; + struct ttc_timer_clockevent *ttcce = container_of(timer, + struct ttc_timer_clockevent, ttc); - /* Disable the counter, set the counter value and re-enable counter */ - ctrl_reg = __raw_readl(timer->base_addr + TTC_CNT_CNTRL_OFFSET); - ctrl_reg |= TTC_CNT_CNTRL_DISABLE_MASK; - __raw_writel(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET); + /* set interval */ + u32 reg = __raw_readl(timer->base_addr + TTC_COUNT_VAL_OFFSET); + reg += cycles; + __raw_writel(reg, timer->base_addr + TTC_MATCH1_OFFSET); - __raw_writel(cycles, timer->base_addr + TTC_INTR_VAL_OFFSET); + /* enable match interrupt */ + __raw_writel(TTC_INTERRUPT_MATCH1, timer->base_addr + TTC_IER_OFFSET); - /* -* Reset the counter (0x10) so that it starts from 0, one-shot -* mode makes this needed for timing to be right. -*/ - ctrl_reg |= CNT_CNTRL_RESET; - ctrl_reg &= ~TTC_CNT_CNTRL_DISABLE_MASK; - __raw_writel(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET); + ttcce->interval = cycles; } /** @@ -148,6 +148,8 @@ static irqreturn_t ttc_clock_event_interrupt(int irq, void *dev_id) /* Acknowledge the interrupt and call event handler */ __raw_readl(timer->base_addr + TTC_ISR_OFFSET); + if (ttce->ce.mode == CLOCK_EVT_MODE_PERIODIC) + ttc_set_interval(timer, ttce->interval); ttce->ce.event_handler(>ce); @@ -201,7 +203,6 @@ static void ttc_set_mode(enum clock_event_mode mode, { struct ttc_timer_clockevent *ttce = to_ttc_timer_clkevent(evt); struct ttc_timer *timer = >ttc; - u32 ctrl_reg; switch (mode) { case CLOCK_EVT_MODE_PERIODIC: @@ -211,18 +212,9 @@ static void ttc_set_mode(enum clock_event_mode mode, case CLOCK_EVT_MODE_ONESHOT: case CLOCK_EVT_MODE_UNUSED: case CLOCK_EVT_MODE_SHUTDOWN: - ctrl_reg = __raw_readl(timer->base_addr + - TTC_CNT_CNTRL_OFFSET); - ctrl_reg |= TTC_CNT_CNTRL_DISABLE_MASK; - __raw_writel(ctrl_reg, - timer->base_addr + TTC_CNT_CNTRL_OFFSET); + __raw_writel(0, timer->base_addr + TTC_IER_OFFSET); break; case CLOCK_EVT_MODE_RESUME: - ctrl_reg = __raw_readl(timer->base_addr + - TTC_CNT_CNTRL_OFFSET); - ctrl_reg &= ~TTC_CNT_CNTRL_DISABLE_MASK; - __raw_writel(ctrl_reg, - timer->base_addr + TTC_CNT_CNTRL_OFFSET); break; }
[PATCH v3 5/7] clocksource/cadence_ttc: Use only one counter
Currently the driver uses two of the three counters the TTC provides to implement a clocksource and a clockevent device. By using the TTC's match feature we can implement both use cases using a single counter only. The old approach is to use timer over-/underflow to generate an interrupt. Using the match register allows to generate an interrupt on arbitrary counter values. This way a dedicated clockevent counter can be avoided. Signed-off-by: Soren Brinkmann soren.brinkm...@xilinx.com --- drivers/clocksource/cadence_ttc_timer.c | 92 +++-- 1 file changed, 31 insertions(+), 61 deletions(-) diff --git a/drivers/clocksource/cadence_ttc_timer.c b/drivers/clocksource/cadence_ttc_timer.c index 49fbe2847c84..d922e982af95 100644 --- a/drivers/clocksource/cadence_ttc_timer.c +++ b/drivers/clocksource/cadence_ttc_timer.c @@ -47,6 +47,7 @@ #define TTC_CNT_CNTRL_OFFSET 0x0C /* Counter Control Reg, RW */ #define TTC_COUNT_VAL_OFFSET 0x18 /* Counter Value Reg, RO */ #define TTC_INTR_VAL_OFFSET0x24 /* Interval Count Reg, RW */ +#define TTC_MATCH1_OFFSET 0x30 /* Match reg, RW */ #define TTC_ISR_OFFSET 0x54 /* Interrupt Status Reg, RO */ #define TTC_IER_OFFSET 0x60 /* Interrupt Enable Reg, RW */ @@ -64,7 +65,10 @@ #define PRESCALE 2048/* The exponent must match this */ #define CLK_CNTRL_PRESCALE ((PRESCALE_EXPONENT - 1) 1) #define CLK_CNTRL_PRESCALE_EN 1 -#define CNT_CNTRL_RESET(1 4) +#define CNT_CNTRL_RESETBIT(4) +#define CNT_CNTRL_MATCHBIT(3) + +#define TTC_INTERRUPT_MATCH1 BIT(1) #define MAX_F_ERR 50 @@ -99,6 +103,7 @@ struct ttc_timer_clocksource { struct ttc_timer_clockevent { struct ttc_timerttc; struct clock_event_device ce; + unsigned long interval; }; #define to_ttc_timer_clkevent(x) \ @@ -112,25 +117,20 @@ static void __iomem *ttc_sched_clock_val_reg; * @timer: Pointer to the timer instance * @cycles:Timer interval ticks **/ -static void ttc_set_interval(struct ttc_timer *timer, - unsigned long cycles) +static void ttc_set_interval(struct ttc_timer *timer, unsigned long cycles) { - u32 ctrl_reg; + struct ttc_timer_clockevent *ttcce = container_of(timer, + struct ttc_timer_clockevent, ttc); - /* Disable the counter, set the counter value and re-enable counter */ - ctrl_reg = __raw_readl(timer-base_addr + TTC_CNT_CNTRL_OFFSET); - ctrl_reg |= TTC_CNT_CNTRL_DISABLE_MASK; - __raw_writel(ctrl_reg, timer-base_addr + TTC_CNT_CNTRL_OFFSET); + /* set interval */ + u32 reg = __raw_readl(timer-base_addr + TTC_COUNT_VAL_OFFSET); + reg += cycles; + __raw_writel(reg, timer-base_addr + TTC_MATCH1_OFFSET); - __raw_writel(cycles, timer-base_addr + TTC_INTR_VAL_OFFSET); + /* enable match interrupt */ + __raw_writel(TTC_INTERRUPT_MATCH1, timer-base_addr + TTC_IER_OFFSET); - /* -* Reset the counter (0x10) so that it starts from 0, one-shot -* mode makes this needed for timing to be right. -*/ - ctrl_reg |= CNT_CNTRL_RESET; - ctrl_reg = ~TTC_CNT_CNTRL_DISABLE_MASK; - __raw_writel(ctrl_reg, timer-base_addr + TTC_CNT_CNTRL_OFFSET); + ttcce-interval = cycles; } /** @@ -148,6 +148,8 @@ static irqreturn_t ttc_clock_event_interrupt(int irq, void *dev_id) /* Acknowledge the interrupt and call event handler */ __raw_readl(timer-base_addr + TTC_ISR_OFFSET); + if (ttce-ce.mode == CLOCK_EVT_MODE_PERIODIC) + ttc_set_interval(timer, ttce-interval); ttce-ce.event_handler(ttce-ce); @@ -201,7 +203,6 @@ static void ttc_set_mode(enum clock_event_mode mode, { struct ttc_timer_clockevent *ttce = to_ttc_timer_clkevent(evt); struct ttc_timer *timer = ttce-ttc; - u32 ctrl_reg; switch (mode) { case CLOCK_EVT_MODE_PERIODIC: @@ -211,18 +212,9 @@ static void ttc_set_mode(enum clock_event_mode mode, case CLOCK_EVT_MODE_ONESHOT: case CLOCK_EVT_MODE_UNUSED: case CLOCK_EVT_MODE_SHUTDOWN: - ctrl_reg = __raw_readl(timer-base_addr + - TTC_CNT_CNTRL_OFFSET); - ctrl_reg |= TTC_CNT_CNTRL_DISABLE_MASK; - __raw_writel(ctrl_reg, - timer-base_addr + TTC_CNT_CNTRL_OFFSET); + __raw_writel(0, timer-base_addr + TTC_IER_OFFSET); break; case CLOCK_EVT_MODE_RESUME: - ctrl_reg = __raw_readl(timer-base_addr + - TTC_CNT_CNTRL_OFFSET); - ctrl_reg = ~TTC_CNT_CNTRL_DISABLE_MASK; - __raw_writel(ctrl_reg, - timer-base_addr + TTC_CNT_CNTRL_OFFSET); break;