diff options
-rw-r--r-- | drivers/clocksource/tcb_clksrc.c | 90 | ||||
-rw-r--r-- | drivers/misc/atmel_tclib.c | 20 | ||||
-rw-r--r-- | include/linux/atmel_tc.h | 10 |
3 files changed, 92 insertions, 28 deletions
diff --git a/drivers/clocksource/tcb_clksrc.c b/drivers/clocksource/tcb_clksrc.c index 55d0f95f82f9..32cb929b8eb6 100644 --- a/drivers/clocksource/tcb_clksrc.c +++ b/drivers/clocksource/tcb_clksrc.c | |||
@@ -19,6 +19,8 @@ | |||
19 | * - Two channels combine to create a free-running 32 bit counter | 19 | * - Two channels combine to create a free-running 32 bit counter |
20 | * with a base rate of 5+ MHz, packaged as a clocksource (with | 20 | * with a base rate of 5+ MHz, packaged as a clocksource (with |
21 | * resolution better than 200 nsec). | 21 | * resolution better than 200 nsec). |
22 | * - Some chips support 32 bit counter. A single channel is used for | ||
23 | * this 32 bit free-running counter. the second channel is not used. | ||
22 | * | 24 | * |
23 | * - The third channel may be used to provide a 16-bit clockevent | 25 | * - The third channel may be used to provide a 16-bit clockevent |
24 | * source, used in either periodic or oneshot mode. This runs | 26 | * source, used in either periodic or oneshot mode. This runs |
@@ -54,6 +56,11 @@ static cycle_t tc_get_cycles(struct clocksource *cs) | |||
54 | return (upper << 16) | lower; | 56 | return (upper << 16) | lower; |
55 | } | 57 | } |
56 | 58 | ||
59 | static cycle_t tc_get_cycles32(struct clocksource *cs) | ||
60 | { | ||
61 | return __raw_readl(tcaddr + ATMEL_TC_REG(0, CV)); | ||
62 | } | ||
63 | |||
57 | static struct clocksource clksrc = { | 64 | static struct clocksource clksrc = { |
58 | .name = "tcb_clksrc", | 65 | .name = "tcb_clksrc", |
59 | .rating = 200, | 66 | .rating = 200, |
@@ -209,6 +216,48 @@ static void __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx) | |||
209 | 216 | ||
210 | #endif | 217 | #endif |
211 | 218 | ||
219 | static void __init tcb_setup_dual_chan(struct atmel_tc *tc, int mck_divisor_idx) | ||
220 | { | ||
221 | /* channel 0: waveform mode, input mclk/8, clock TIOA0 on overflow */ | ||
222 | __raw_writel(mck_divisor_idx /* likely divide-by-8 */ | ||
223 | | ATMEL_TC_WAVE | ||
224 | | ATMEL_TC_WAVESEL_UP /* free-run */ | ||
225 | | ATMEL_TC_ACPA_SET /* TIOA0 rises at 0 */ | ||
226 | | ATMEL_TC_ACPC_CLEAR, /* (duty cycle 50%) */ | ||
227 | tcaddr + ATMEL_TC_REG(0, CMR)); | ||
228 | __raw_writel(0x0000, tcaddr + ATMEL_TC_REG(0, RA)); | ||
229 | __raw_writel(0x8000, tcaddr + ATMEL_TC_REG(0, RC)); | ||
230 | __raw_writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR)); /* no irqs */ | ||
231 | __raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR)); | ||
232 | |||
233 | /* channel 1: waveform mode, input TIOA0 */ | ||
234 | __raw_writel(ATMEL_TC_XC1 /* input: TIOA0 */ | ||
235 | | ATMEL_TC_WAVE | ||
236 | | ATMEL_TC_WAVESEL_UP, /* free-run */ | ||
237 | tcaddr + ATMEL_TC_REG(1, CMR)); | ||
238 | __raw_writel(0xff, tcaddr + ATMEL_TC_REG(1, IDR)); /* no irqs */ | ||
239 | __raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(1, CCR)); | ||
240 | |||
241 | /* chain channel 0 to channel 1*/ | ||
242 | __raw_writel(ATMEL_TC_TC1XC1S_TIOA0, tcaddr + ATMEL_TC_BMR); | ||
243 | /* then reset all the timers */ | ||
244 | __raw_writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR); | ||
245 | } | ||
246 | |||
247 | static void __init tcb_setup_single_chan(struct atmel_tc *tc, int mck_divisor_idx) | ||
248 | { | ||
249 | /* channel 0: waveform mode, input mclk/8 */ | ||
250 | __raw_writel(mck_divisor_idx /* likely divide-by-8 */ | ||
251 | | ATMEL_TC_WAVE | ||
252 | | ATMEL_TC_WAVESEL_UP, /* free-run */ | ||
253 | tcaddr + ATMEL_TC_REG(0, CMR)); | ||
254 | __raw_writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR)); /* no irqs */ | ||
255 | __raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR)); | ||
256 | |||
257 | /* then reset all the timers */ | ||
258 | __raw_writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR); | ||
259 | } | ||
260 | |||
212 | static int __init tcb_clksrc_init(void) | 261 | static int __init tcb_clksrc_init(void) |
213 | { | 262 | { |
214 | static char bootinfo[] __initdata | 263 | static char bootinfo[] __initdata |
@@ -260,34 +309,19 @@ static int __init tcb_clksrc_init(void) | |||
260 | divided_rate / 1000000, | 309 | divided_rate / 1000000, |
261 | ((divided_rate + 500000) % 1000000) / 1000); | 310 | ((divided_rate + 500000) % 1000000) / 1000); |
262 | 311 | ||
263 | /* tclib will give us three clocks no matter what the | 312 | if (tc->tcb_config && tc->tcb_config->counter_width == 32) { |
264 | * underlying platform supports. | 313 | /* use apropriate function to read 32 bit counter */ |
265 | */ | 314 | clksrc.read = tc_get_cycles32; |
266 | clk_enable(tc->clk[1]); | 315 | /* setup ony channel 0 */ |
267 | 316 | tcb_setup_single_chan(tc, best_divisor_idx); | |
268 | /* channel 0: waveform mode, input mclk/8, clock TIOA0 on overflow */ | 317 | } else { |
269 | __raw_writel(best_divisor_idx /* likely divide-by-8 */ | 318 | /* tclib will give us three clocks no matter what the |
270 | | ATMEL_TC_WAVE | 319 | * underlying platform supports. |
271 | | ATMEL_TC_WAVESEL_UP /* free-run */ | 320 | */ |
272 | | ATMEL_TC_ACPA_SET /* TIOA0 rises at 0 */ | 321 | clk_enable(tc->clk[1]); |
273 | | ATMEL_TC_ACPC_CLEAR, /* (duty cycle 50%) */ | 322 | /* setup both channel 0 & 1 */ |
274 | tcaddr + ATMEL_TC_REG(0, CMR)); | 323 | tcb_setup_dual_chan(tc, best_divisor_idx); |
275 | __raw_writel(0x0000, tcaddr + ATMEL_TC_REG(0, RA)); | 324 | } |
276 | __raw_writel(0x8000, tcaddr + ATMEL_TC_REG(0, RC)); | ||
277 | __raw_writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR)); /* no irqs */ | ||
278 | __raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR)); | ||
279 | |||
280 | /* channel 1: waveform mode, input TIOA0 */ | ||
281 | __raw_writel(ATMEL_TC_XC1 /* input: TIOA0 */ | ||
282 | | ATMEL_TC_WAVE | ||
283 | | ATMEL_TC_WAVESEL_UP, /* free-run */ | ||
284 | tcaddr + ATMEL_TC_REG(1, CMR)); | ||
285 | __raw_writel(0xff, tcaddr + ATMEL_TC_REG(1, IDR)); /* no irqs */ | ||
286 | __raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(1, CCR)); | ||
287 | |||
288 | /* chain channel 0 to channel 1, then reset all the timers */ | ||
289 | __raw_writel(ATMEL_TC_TC1XC1S_TIOA0, tcaddr + ATMEL_TC_BMR); | ||
290 | __raw_writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR); | ||
291 | 325 | ||
292 | /* and away we go! */ | 326 | /* and away we go! */ |
293 | clocksource_register_hz(&clksrc, divided_rate); | 327 | clocksource_register_hz(&clksrc, divided_rate); |
diff --git a/drivers/misc/atmel_tclib.c b/drivers/misc/atmel_tclib.c index de6dea7c5d52..c8d8e38d0d8a 100644 --- a/drivers/misc/atmel_tclib.c +++ b/drivers/misc/atmel_tclib.c | |||
@@ -114,9 +114,21 @@ void atmel_tc_free(struct atmel_tc *tc) | |||
114 | EXPORT_SYMBOL_GPL(atmel_tc_free); | 114 | EXPORT_SYMBOL_GPL(atmel_tc_free); |
115 | 115 | ||
116 | #if defined(CONFIG_OF) | 116 | #if defined(CONFIG_OF) |
117 | static struct atmel_tcb_config tcb_rm9200_config = { | ||
118 | .counter_width = 16, | ||
119 | }; | ||
120 | |||
121 | static struct atmel_tcb_config tcb_sam9x5_config = { | ||
122 | .counter_width = 32, | ||
123 | }; | ||
124 | |||
117 | static const struct of_device_id atmel_tcb_dt_ids[] = { | 125 | static const struct of_device_id atmel_tcb_dt_ids[] = { |
118 | { | 126 | { |
119 | .compatible = "atmel,at91rm9200-tcb", | 127 | .compatible = "atmel,at91rm9200-tcb", |
128 | .data = &tcb_rm9200_config, | ||
129 | }, { | ||
130 | .compatible = "atmel,at91sam9x5-tcb", | ||
131 | .data = &tcb_sam9x5_config, | ||
120 | }, { | 132 | }, { |
121 | /* sentinel */ | 133 | /* sentinel */ |
122 | } | 134 | } |
@@ -150,6 +162,14 @@ static int __init tc_probe(struct platform_device *pdev) | |||
150 | return -EINVAL; | 162 | return -EINVAL; |
151 | } | 163 | } |
152 | 164 | ||
165 | /* Now take SoC information if available */ | ||
166 | if (pdev->dev.of_node) { | ||
167 | const struct of_device_id *match; | ||
168 | match = of_match_node(atmel_tcb_dt_ids, pdev->dev.of_node); | ||
169 | if (match) | ||
170 | tc->tcb_config = match->data; | ||
171 | } | ||
172 | |||
153 | tc->clk[0] = clk; | 173 | tc->clk[0] = clk; |
154 | tc->clk[1] = clk_get(&pdev->dev, "t1_clk"); | 174 | tc->clk[1] = clk_get(&pdev->dev, "t1_clk"); |
155 | if (IS_ERR(tc->clk[1])) | 175 | if (IS_ERR(tc->clk[1])) |
diff --git a/include/linux/atmel_tc.h b/include/linux/atmel_tc.h index 53ba65e30caa..1d14b1dc1aee 100644 --- a/include/linux/atmel_tc.h +++ b/include/linux/atmel_tc.h | |||
@@ -34,10 +34,19 @@ | |||
34 | struct clk; | 34 | struct clk; |
35 | 35 | ||
36 | /** | 36 | /** |
37 | * struct atmel_tcb_config - SoC data for a Timer/Counter Block | ||
38 | * @counter_width: size in bits of a timer counter register | ||
39 | */ | ||
40 | struct atmel_tcb_config { | ||
41 | size_t counter_width; | ||
42 | }; | ||
43 | |||
44 | /** | ||
37 | * struct atmel_tc - information about a Timer/Counter Block | 45 | * struct atmel_tc - information about a Timer/Counter Block |
38 | * @pdev: physical device | 46 | * @pdev: physical device |
39 | * @iomem: resource associated with the I/O register | 47 | * @iomem: resource associated with the I/O register |
40 | * @regs: mapping through which the I/O registers can be accessed | 48 | * @regs: mapping through which the I/O registers can be accessed |
49 | * @tcb_config: configuration data from SoC | ||
41 | * @irq: irq for each of the three channels | 50 | * @irq: irq for each of the three channels |
42 | * @clk: internal clock source for each of the three channels | 51 | * @clk: internal clock source for each of the three channels |
43 | * @node: list node, for tclib internal use | 52 | * @node: list node, for tclib internal use |
@@ -54,6 +63,7 @@ struct atmel_tc { | |||
54 | struct platform_device *pdev; | 63 | struct platform_device *pdev; |
55 | struct resource *iomem; | 64 | struct resource *iomem; |
56 | void __iomem *regs; | 65 | void __iomem *regs; |
66 | struct atmel_tcb_config *tcb_config; | ||
57 | int irq[3]; | 67 | int irq[3]; |
58 | struct clk *clk[3]; | 68 | struct clk *clk[3]; |
59 | struct list_head node; | 69 | struct list_head node; |