aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorNicolas Ferre <nicolas.ferre@atmel.com>2012-01-19 12:44:49 -0500
committerNicolas Ferre <nicolas.ferre@atmel.com>2012-03-01 07:38:49 -0500
commit8e315a7b0c082c6743a6636ead5674a2265638d3 (patch)
tree9eabbe8dfff1679fcc7c1ca72f81e58631dfdfcb /drivers
parent3a61a5dae49bf3d1afb7f75c8acb3607f26565af (diff)
ARM: at91/tc/clocksource: Add 32 bit variant to Timer Counter
Some SoC have a 32 bit variant of Timer Counter Blocks. We do not need the chaining of two 16 bit counters anymore for them. The SoC nature is deduced from the device tree "compatible" string. For non-device-tree configurations, backward compatibility is maintained by using the default 16 bit counter configuration. This patch addresses both the atmel_tclib and its user: tcb_clksrc clocksource. Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com> Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> Acked-by: Grant Likely <grant.likely@secretlab.ca>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clocksource/tcb_clksrc.c90
-rw-r--r--drivers/misc/atmel_tclib.c20
2 files changed, 82 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
59static cycle_t tc_get_cycles32(struct clocksource *cs)
60{
61 return __raw_readl(tcaddr + ATMEL_TC_REG(0, CV));
62}
63
57static struct clocksource clksrc = { 64static 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
219static 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
247static 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
212static int __init tcb_clksrc_init(void) 261static 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)
114EXPORT_SYMBOL_GPL(atmel_tc_free); 114EXPORT_SYMBOL_GPL(atmel_tc_free);
115 115
116#if defined(CONFIG_OF) 116#if defined(CONFIG_OF)
117static struct atmel_tcb_config tcb_rm9200_config = {
118 .counter_width = 16,
119};
120
121static struct atmel_tcb_config tcb_sam9x5_config = {
122 .counter_width = 32,
123};
124
117static const struct of_device_id atmel_tcb_dt_ids[] = { 125static 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]))