diff options
Diffstat (limited to 'drivers/clocksource/tcb_clksrc.c')
-rw-r--r-- | drivers/clocksource/tcb_clksrc.c | 44 |
1 files changed, 26 insertions, 18 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); |