diff options
| author | Benedikt Spranger <b.spranger@linutronix.de> | 2010-03-08 12:57:04 -0500 |
|---|---|---|
| committer | Thomas Gleixner <tglx@linutronix.de> | 2010-05-02 14:13:54 -0400 |
| commit | 917c377817e393775fb6a2eaf961c153e38760fd (patch) | |
| tree | 1208f7406f55cfb88978e3ff46f4a39baa637eed | |
| parent | b16bd4fb3290b5d380c2a67cc4c5109d65cee1d7 (diff) | |
Atmel TCLIB: Allow higher clock rates for clock events
As default the TCLIB uses the 32KiHz base clock rate for clock events.
Add a compile time selection to allow higher clock resulution.
Signed-off-by: Benedikt Spranger <b.spranger@linutronix.de>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
| -rw-r--r-- | drivers/clocksource/tcb_clksrc.c | 44 | ||||
| -rw-r--r-- | drivers/misc/Kconfig | 11 |
2 files changed, 35 insertions, 20 deletions
diff --git a/drivers/clocksource/tcb_clksrc.c b/drivers/clocksource/tcb_clksrc.c index 01b886e68822..90350b6612bb 100644 --- a/drivers/clocksource/tcb_clksrc.c +++ b/drivers/clocksource/tcb_clksrc.c | |||
| @@ -21,8 +21,7 @@ | |||
| 21 | * resolution better than 200 nsec). | 21 | * resolution better than 200 nsec). |
| 22 | * | 22 | * |
| 23 | * - The third channel may be used to provide a 16-bit clockevent | 23 | * - The third channel may be used to provide a 16-bit clockevent |
| 24 | * source, used in either periodic or oneshot mode. This runs | 24 | * source, used in either periodic or oneshot mode. |
| 25 | * at 32 KiHZ, and can handle delays of up to two seconds. | ||
| 26 | * | 25 | * |
| 27 | * A boot clocksource and clockevent source are also currently needed, | 26 | * A boot clocksource and clockevent source are also currently needed, |
| 28 | * unless the relevant platforms (ARM/AT91, AVR32/AT32) are changed so | 27 | * unless the relevant platforms (ARM/AT91, AVR32/AT32) are changed so |
| @@ -68,6 +67,7 @@ static struct clocksource clksrc = { | |||
| 68 | struct tc_clkevt_device { | 67 | struct tc_clkevt_device { |
| 69 | struct clock_event_device clkevt; | 68 | struct clock_event_device clkevt; |
| 70 | struct clk *clk; | 69 | struct clk *clk; |
| 70 | u32 freq; | ||
| 71 | void __iomem *regs; | 71 | void __iomem *regs; |
| 72 | }; | 72 | }; |
| 73 | 73 | ||
| @@ -76,13 +76,6 @@ static struct tc_clkevt_device *to_tc_clkevt(struct clock_event_device *clkevt) | |||
| 76 | return container_of(clkevt, struct tc_clkevt_device, clkevt); | 76 | return container_of(clkevt, struct tc_clkevt_device, clkevt); |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | /* For now, we always use the 32K clock ... this optimizes for NO_HZ, | ||
| 80 | * because using one of the divided clocks would usually mean the | ||
| 81 | * tick rate can never be less than several dozen Hz (vs 0.5 Hz). | ||
| 82 | * | ||
| 83 | * A divided clock could be good for high resolution timers, since | ||
| 84 | * 30.5 usec resolution can seem "low". | ||
| 85 | */ | ||
| 86 | static u32 timer_clock; | 79 | static u32 timer_clock; |
| 87 | 80 | ||
| 88 | static void tc_mode(enum clock_event_mode m, struct clock_event_device *d) | 81 | static void tc_mode(enum clock_event_mode m, struct clock_event_device *d) |
| @@ -105,11 +98,12 @@ static void tc_mode(enum clock_event_mode m, struct clock_event_device *d) | |||
| 105 | case CLOCK_EVT_MODE_PERIODIC: | 98 | case CLOCK_EVT_MODE_PERIODIC: |
| 106 | clk_enable(tcd->clk); | 99 | clk_enable(tcd->clk); |
| 107 | 100 | ||
| 108 | /* slow clock, count up to RC, then irq and restart */ | 101 | /* count up to RC, then irq and restart */ |
| 109 | __raw_writel(timer_clock | 102 | __raw_writel(timer_clock |
| 110 | | ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO, | 103 | | ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO, |
| 111 | regs + ATMEL_TC_REG(2, CMR)); | 104 | regs + ATMEL_TC_REG(2, CMR)); |
| 112 | __raw_writel((32768 + HZ/2) / HZ, tcaddr + ATMEL_TC_REG(2, RC)); | 105 | __raw_writel((tcd->freq + HZ/2)/HZ, |
| 106 | tcaddr + ATMEL_TC_REG(2, RC)); | ||
| 113 | 107 | ||
| 114 | /* Enable clock and interrupts on RC compare */ | 108 | /* Enable clock and interrupts on RC compare */ |
| 115 | __raw_writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER)); | 109 | __raw_writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER)); |
| @@ -122,7 +116,7 @@ static void tc_mode(enum clock_event_mode m, struct clock_event_device *d) | |||
| 122 | case CLOCK_EVT_MODE_ONESHOT: | 116 | case CLOCK_EVT_MODE_ONESHOT: |
| 123 | clk_enable(tcd->clk); | 117 | clk_enable(tcd->clk); |
| 124 | 118 | ||
| 125 | /* slow clock, count up to RC, then irq and stop */ | 119 | /* count up to RC, then irq and stop */ |
| 126 | __raw_writel(timer_clock | ATMEL_TC_CPCSTOP | 120 | __raw_writel(timer_clock | ATMEL_TC_CPCSTOP |
| 127 | | ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO, | 121 | | ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO, |
| 128 | regs + ATMEL_TC_REG(2, CMR)); | 122 | regs + ATMEL_TC_REG(2, CMR)); |
| @@ -152,8 +146,12 @@ static struct tc_clkevt_device clkevt = { | |||
| 152 | .features = CLOCK_EVT_FEAT_PERIODIC | 146 | .features = CLOCK_EVT_FEAT_PERIODIC |
| 153 | | CLOCK_EVT_FEAT_ONESHOT, | 147 | | CLOCK_EVT_FEAT_ONESHOT, |
| 154 | .shift = 32, | 148 | .shift = 32, |
| 149 | #ifdef CONFIG_ATMEL_TCB_CLKSRC_USE_SLOW_CLOCK | ||
| 155 | /* Should be lower than at91rm9200's system timer */ | 150 | /* Should be lower than at91rm9200's system timer */ |
| 156 | .rating = 125, | 151 | .rating = 125, |
| 152 | #else | ||
| 153 | .rating = 200, | ||
| 154 | #endif | ||
| 157 | .set_next_event = tc_next_event, | 155 | .set_next_event = tc_next_event, |
| 158 | .set_mode = tc_mode, | 156 | .set_mode = tc_mode, |
| 159 | }, | 157 | }, |
| @@ -179,8 +177,9 @@ static struct irqaction tc_irqaction = { | |||
| 179 | .handler = ch2_irq, | 177 | .handler = ch2_irq, |
| 180 | }; | 178 | }; |
| 181 | 179 | ||
| 182 | static void __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx) | 180 | static void __init setup_clkevents(struct atmel_tc *tc, int divisor_idx) |
| 183 | { | 181 | { |
| 182 | unsigned divisor = atmel_tc_divisors[divisor_idx]; | ||
| 184 | struct clk *t2_clk = tc->clk[2]; | 183 | struct clk *t2_clk = tc->clk[2]; |
| 185 | int irq = tc->irq[2]; | 184 | int irq = tc->irq[2]; |
| 186 | 185 | ||
| @@ -188,11 +187,17 @@ static void __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx) | |||
| 188 | clkevt.clk = t2_clk; | 187 | clkevt.clk = t2_clk; |
| 189 | tc_irqaction.dev_id = &clkevt; | 188 | tc_irqaction.dev_id = &clkevt; |
| 190 | 189 | ||
| 191 | timer_clock = clk32k_divisor_idx; | 190 | timer_clock = divisor_idx; |
| 192 | 191 | ||
| 193 | clkevt.clkevt.mult = div_sc(32768, NSEC_PER_SEC, clkevt.clkevt.shift); | 192 | if (!divisor) |
| 194 | clkevt.clkevt.max_delta_ns | 193 | clkevt.freq = 32768; |
| 195 | = clockevent_delta2ns(0xffff, &clkevt.clkevt); | 194 | else |
| 195 | clkevt.freq = clk_get_rate(t2_clk)/divisor; | ||
| 196 | |||
| 197 | clkevt.clkevt.mult = div_sc(clkevt.freq, NSEC_PER_SEC, | ||
| 198 | clkevt.clkevt.shift); | ||
| 199 | clkevt.clkevt.max_delta_ns = | ||
| 200 | clockevent_delta2ns(0xffff, &clkevt.clkevt); | ||
| 196 | clkevt.clkevt.min_delta_ns = clockevent_delta2ns(1, &clkevt.clkevt) + 1; | 201 | clkevt.clkevt.min_delta_ns = clockevent_delta2ns(1, &clkevt.clkevt) + 1; |
| 197 | clkevt.clkevt.cpumask = cpumask_of(0); | 202 | clkevt.clkevt.cpumask = cpumask_of(0); |
| 198 | 203 | ||
| @@ -295,8 +300,11 @@ static int __init tcb_clksrc_init(void) | |||
| 295 | clocksource_register(&clksrc); | 300 | clocksource_register(&clksrc); |
| 296 | 301 | ||
| 297 | /* channel 2: periodic and oneshot timer support */ | 302 | /* channel 2: periodic and oneshot timer support */ |
| 303 | #ifdef CONFIG_ATMEL_TCB_CLKSRC_USE_SLOW_CLOCK | ||
| 298 | setup_clkevents(tc, clk32k_divisor_idx); | 304 | setup_clkevents(tc, clk32k_divisor_idx); |
| 299 | 305 | #else | |
| 306 | setup_clkevents(tc, best_divisor_idx); | ||
| 307 | #endif | ||
| 300 | return 0; | 308 | return 0; |
| 301 | } | 309 | } |
| 302 | arch_initcall(tcb_clksrc_init); | 310 | arch_initcall(tcb_clksrc_init); |
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 9eaa6477cec2..3f3a4c864735 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig | |||
| @@ -54,8 +54,7 @@ config ATMEL_TCB_CLKSRC | |||
| 54 | are combined to make a single 32-bit timer. | 54 | are combined to make a single 32-bit timer. |
| 55 | 55 | ||
| 56 | When GENERIC_CLOCKEVENTS is defined, the third timer channel | 56 | When GENERIC_CLOCKEVENTS is defined, the third timer channel |
| 57 | may be used as a clock event device supporting oneshot mode | 57 | may be used as a clock event device supporting oneshot mode. |
| 58 | (delays of up to two seconds) based on the 32 KiHz clock. | ||
| 59 | 58 | ||
| 60 | config ATMEL_TCB_CLKSRC_BLOCK | 59 | config ATMEL_TCB_CLKSRC_BLOCK |
| 61 | int | 60 | int |
| @@ -69,6 +68,14 @@ config ATMEL_TCB_CLKSRC_BLOCK | |||
| 69 | TC can be used for other purposes, such as PWM generation and | 68 | TC can be used for other purposes, such as PWM generation and |
| 70 | interval timing. | 69 | interval timing. |
| 71 | 70 | ||
| 71 | config ATMEL_TCB_CLKSRC_USE_SLOW_CLOCK | ||
| 72 | bool "TC Block use 32 KiHz clock" | ||
| 73 | depends on ATMEL_TCB_CLKSRC | ||
| 74 | default y | ||
| 75 | help | ||
| 76 | Select this to use 32 KiHz base clock rate as TC block clock | ||
| 77 | source for clock events. | ||
| 78 | |||
| 72 | config IBM_ASM | 79 | config IBM_ASM |
| 73 | tristate "Device driver for IBM RSA service processor" | 80 | tristate "Device driver for IBM RSA service processor" |
| 74 | depends on X86 && PCI && INPUT && EXPERIMENTAL | 81 | depends on X86 && PCI && INPUT && EXPERIMENTAL |
