Commit 8e315a7b0c082c6743a6636ead5674a2265638d3

Authored by Nicolas Ferre
1 parent 3a61a5dae4

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>

Showing 3 changed files with 92 additions and 28 deletions Side-by-side Diff

drivers/clocksource/tcb_clksrc.c
... ... @@ -19,6 +19,8 @@
19 19 * - Two channels combine to create a free-running 32 bit counter
20 20 * with a base rate of 5+ MHz, packaged as a clocksource (with
21 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 25 * - The third channel may be used to provide a 16-bit clockevent
24 26 * source, used in either periodic or oneshot mode. This runs
... ... @@ -54,6 +56,11 @@
54 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 64 static struct clocksource clksrc = {
58 65 .name = "tcb_clksrc",
59 66 .rating = 200,
... ... @@ -209,6 +216,48 @@
209 216  
210 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 261 static int __init tcb_clksrc_init(void)
213 262 {
214 263 static char bootinfo[] __initdata
... ... @@ -260,34 +309,19 @@
260 309 divided_rate / 1000000,
261 310 ((divided_rate + 500000) % 1000000) / 1000);
262 311  
263   - /* tclib will give us three clocks no matter what the
264   - * underlying platform supports.
265   - */
266   - clk_enable(tc->clk[1]);
267   -
268   - /* channel 0: waveform mode, input mclk/8, clock TIOA0 on overflow */
269   - __raw_writel(best_divisor_idx /* likely divide-by-8 */
270   - | ATMEL_TC_WAVE
271   - | ATMEL_TC_WAVESEL_UP /* free-run */
272   - | ATMEL_TC_ACPA_SET /* TIOA0 rises at 0 */
273   - | ATMEL_TC_ACPC_CLEAR, /* (duty cycle 50%) */
274   - tcaddr + ATMEL_TC_REG(0, CMR));
275   - __raw_writel(0x0000, tcaddr + ATMEL_TC_REG(0, RA));
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);
  312 + if (tc->tcb_config && tc->tcb_config->counter_width == 32) {
  313 + /* use apropriate function to read 32 bit counter */
  314 + clksrc.read = tc_get_cycles32;
  315 + /* setup ony channel 0 */
  316 + tcb_setup_single_chan(tc, best_divisor_idx);
  317 + } else {
  318 + /* tclib will give us three clocks no matter what the
  319 + * underlying platform supports.
  320 + */
  321 + clk_enable(tc->clk[1]);
  322 + /* setup both channel 0 & 1 */
  323 + tcb_setup_dual_chan(tc, best_divisor_idx);
  324 + }
291 325  
292 326 /* and away we go! */
293 327 clocksource_register_hz(&clksrc, divided_rate);
drivers/misc/atmel_tclib.c
... ... @@ -114,10 +114,22 @@
114 114 EXPORT_SYMBOL_GPL(atmel_tc_free);
115 115  
116 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 125 static const struct of_device_id atmel_tcb_dt_ids[] = {
118 126 {
119 127 .compatible = "atmel,at91rm9200-tcb",
  128 + .data = &tcb_rm9200_config,
120 129 }, {
  130 + .compatible = "atmel,at91sam9x5-tcb",
  131 + .data = &tcb_sam9x5_config,
  132 + }, {
121 133 /* sentinel */
122 134 }
123 135 };
... ... @@ -148,6 +160,14 @@
148 160 if (IS_ERR(clk)) {
149 161 kfree(tc);
150 162 return -EINVAL;
  163 + }
  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;
151 171 }
152 172  
153 173 tc->clk[0] = clk;
include/linux/atmel_tc.h
... ... @@ -34,10 +34,19 @@
34 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 45 * struct atmel_tc - information about a Timer/Counter Block
38 46 * @pdev: physical device
39 47 * @iomem: resource associated with the I/O register
40 48 * @regs: mapping through which the I/O registers can be accessed
  49 + * @tcb_config: configuration data from SoC
41 50 * @irq: irq for each of the three channels
42 51 * @clk: internal clock source for each of the three channels
43 52 * @node: list node, for tclib internal use
... ... @@ -54,6 +63,7 @@
54 63 struct platform_device *pdev;
55 64 struct resource *iomem;
56 65 void __iomem *regs;
  66 + struct atmel_tcb_config *tcb_config;
57 67 int irq[3];
58 68 struct clk *clk[3];
59 69 struct list_head node;