aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/clocksource/tcb_clksrc.c90
-rw-r--r--drivers/misc/atmel_tclib.c20
-rw-r--r--include/linux/atmel_tc.h10
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
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]))
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 @@
34struct clk; 34struct 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 */
40struct 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;