Blame view

drivers/clocksource/nomadik-mtu.c 7.29 KB
28ad94ec6   Alessandro Rubini   [ARM] 5590/1: Add...
1
  /*
28ad94ec6   Alessandro Rubini   [ARM] 5590/1: Add...
2
   * Copyright (C) 2008 STMicroelectronics
b102c01fa   Alessandro Rubini   ARM: 5978/1: plat...
3
   * Copyright (C) 2010 Alessandro Rubini
8fbb97a2b   Linus Walleij   ARM: 6488/1: noma...
4
   * Copyright (C) 2010 Linus Walleij for ST-Ericsson
28ad94ec6   Alessandro Rubini   [ARM] 5590/1: Add...
5
6
7
8
9
10
11
12
13
14
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2, as
   * published by the Free Software Foundation.
   */
  #include <linux/init.h>
  #include <linux/interrupt.h>
  #include <linux/irq.h>
  #include <linux/io.h>
  #include <linux/clockchips.h>
694e33a7f   Linus Walleij   ARM: plat-nomadik...
15
  #include <linux/clocksource.h>
c7785ea0d   Rabin Vincent   clocksource: noma...
16
17
18
  #include <linux/of_address.h>
  #include <linux/of_irq.h>
  #include <linux/of_platform.h>
ba327b1e5   Linus Walleij   ARM: 6145/1: ux50...
19
  #include <linux/clk.h>
28ad94ec6   Alessandro Rubini   [ARM] 5590/1: Add...
20
  #include <linux/jiffies.h>
6f179b724   Fabio Baltieri   clocksource: noma...
21
  #include <linux/delay.h>
ba327b1e5   Linus Walleij   ARM: 6145/1: ux50...
22
  #include <linux/err.h>
38ff87f77   Stephen Boyd   sched_clock: Make...
23
  #include <linux/sched_clock.h>
28ad94ec6   Alessandro Rubini   [ARM] 5590/1: Add...
24
  #include <asm/mach/time.h>
28ad94ec6   Alessandro Rubini   [ARM] 5590/1: Add...
25

05387a9fb   Jonas Aaberg   ARM: plat-nomadik...
26
  /*
05387a9fb   Jonas Aaberg   ARM: plat-nomadik...
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
   * The MTU device hosts four different counters, with 4 set of
   * registers. These are register names.
   */
  
  #define MTU_IMSC	0x00	/* Interrupt mask set/clear */
  #define MTU_RIS		0x04	/* Raw interrupt status */
  #define MTU_MIS		0x08	/* Masked interrupt status */
  #define MTU_ICR		0x0C	/* Interrupt clear register */
  
  /* per-timer registers take 0..3 as argument */
  #define MTU_LR(x)	(0x10 + 0x10 * (x) + 0x00)	/* Load value */
  #define MTU_VAL(x)	(0x10 + 0x10 * (x) + 0x04)	/* Current value */
  #define MTU_CR(x)	(0x10 + 0x10 * (x) + 0x08)	/* Control reg */
  #define MTU_BGLR(x)	(0x10 + 0x10 * (x) + 0x0c)	/* At next overflow */
  
  /* bits for the control register */
  #define MTU_CRn_ENA		0x80
  #define MTU_CRn_PERIODIC	0x40	/* if 0 = free-running */
  #define MTU_CRn_PRESCALE_MASK	0x0c
  #define MTU_CRn_PRESCALE_1		0x00
  #define MTU_CRn_PRESCALE_16		0x04
  #define MTU_CRn_PRESCALE_256		0x08
  #define MTU_CRn_32BITS		0x02
  #define MTU_CRn_ONESHOT		0x01	/* if 0 = wraps reloading from BGLR*/
  
  /* Other registers are usual amba/primecell registers, currently not used */
  #define MTU_ITCR	0xff0
  #define MTU_ITOP	0xff4
  
  #define MTU_PERIPH_ID0	0xfe0
  #define MTU_PERIPH_ID1	0xfe4
  #define MTU_PERIPH_ID2	0xfe8
  #define MTU_PERIPH_ID3	0xfeC
  
  #define MTU_PCELL0	0xff0
  #define MTU_PCELL1	0xff4
  #define MTU_PCELL2	0xff8
  #define MTU_PCELL3	0xffC
28ad94ec6   Alessandro Rubini   [ARM] 5590/1: Add...
65

b9576623c   Linus Walleij   ARM: plat-nomadik...
66
  static void __iomem *mtu_base;
2f73a0684   Jonas Aaberg   ARM: plat-nomadik...
67
68
69
  static bool clkevt_periodic;
  static u32 clk_prescale;
  static u32 nmdk_cycle;		/* write-once */
6f179b724   Fabio Baltieri   clocksource: noma...
70
  static struct delay_timer mtu_delay_timer;
2f73a0684   Jonas Aaberg   ARM: plat-nomadik...
71

ea7113f70   Linus Walleij   clocksource: noma...
72
  #ifdef CONFIG_CLKSRC_NOMADIK_MTU_SCHED_CLOCK
2a847513c   Linus Walleij   ARM: 6107/1: plat...
73
  /*
2a847513c   Linus Walleij   ARM: 6107/1: plat...
74
75
   * Override the global weak sched_clock symbol with this
   * local implementation which uses the clocksource to get some
8fbb97a2b   Linus Walleij   ARM: 6488/1: noma...
76
   * better resolution when scheduling the kernel.
2a847513c   Linus Walleij   ARM: 6107/1: plat...
77
   */
e25bc5f5a   Stephen Boyd   clocksource: noma...
78
  static u64 notrace nomadik_read_sched_clock(void)
2a847513c   Linus Walleij   ARM: 6107/1: plat...
79
  {
8fbb97a2b   Linus Walleij   ARM: 6488/1: noma...
80
81
  	if (unlikely(!mtu_base))
  		return 0;
2f0778afa   Marc Zyngier   ARM: 7205/2: sche...
82
  	return -readl(mtu_base + MTU_VAL(0));
2a847513c   Linus Walleij   ARM: 6107/1: plat...
83
  }
cba13830d   Mattias Wallin   ARM: plat-nomadik...
84
  #endif
2f73a0684   Jonas Aaberg   ARM: plat-nomadik...
85

6f179b724   Fabio Baltieri   clocksource: noma...
86
87
88
89
  static unsigned long nmdk_timer_read_current_timer(void)
  {
  	return ~readl_relaxed(mtu_base + MTU_VAL(0));
  }
b102c01fa   Alessandro Rubini   ARM: 5978/1: plat...
90
  /* Clockevent device: use one-shot mode */
2f73a0684   Jonas Aaberg   ARM: plat-nomadik...
91
92
93
94
95
96
97
98
99
100
101
  static int nmdk_clkevt_next(unsigned long evt, struct clock_event_device *ev)
  {
  	writel(1 << 1, mtu_base + MTU_IMSC);
  	writel(evt, mtu_base + MTU_LR(1));
  	/* Load highest value, enable device, enable interrupts */
  	writel(MTU_CRn_ONESHOT | clk_prescale |
  	       MTU_CRn_32BITS | MTU_CRn_ENA,
  	       mtu_base + MTU_CR(1));
  
  	return 0;
  }
7172c19a2   Linus Walleij   clksrc: delete no...
102
  static void nmdk_clkevt_reset(void)
2f73a0684   Jonas Aaberg   ARM: plat-nomadik...
103
104
  {
  	if (clkevt_periodic) {
2f73a0684   Jonas Aaberg   ARM: plat-nomadik...
105
106
107
108
109
110
111
112
113
114
115
116
117
  		/* Timer: configure load and background-load, and fire it up */
  		writel(nmdk_cycle, mtu_base + MTU_LR(1));
  		writel(nmdk_cycle, mtu_base + MTU_BGLR(1));
  
  		writel(MTU_CRn_PERIODIC | clk_prescale |
  		       MTU_CRn_32BITS | MTU_CRn_ENA,
  		       mtu_base + MTU_CR(1));
  		writel(1 << 1, mtu_base + MTU_IMSC);
  	} else {
  		/* Generate an interrupt to start the clockevent again */
  		(void) nmdk_clkevt_next(nmdk_cycle, NULL);
  	}
  }
28ad94ec6   Alessandro Rubini   [ARM] 5590/1: Add...
118
119
120
  static void nmdk_clkevt_mode(enum clock_event_mode mode,
  			     struct clock_event_device *dev)
  {
28ad94ec6   Alessandro Rubini   [ARM] 5590/1: Add...
121
122
  	switch (mode) {
  	case CLOCK_EVT_MODE_PERIODIC:
2f73a0684   Jonas Aaberg   ARM: plat-nomadik...
123
124
  		clkevt_periodic = true;
  		nmdk_clkevt_reset();
28ad94ec6   Alessandro Rubini   [ARM] 5590/1: Add...
125
126
  		break;
  	case CLOCK_EVT_MODE_ONESHOT:
2f73a0684   Jonas Aaberg   ARM: plat-nomadik...
127
  		clkevt_periodic = false;
b102c01fa   Alessandro Rubini   ARM: 5978/1: plat...
128
  		break;
28ad94ec6   Alessandro Rubini   [ARM] 5590/1: Add...
129
130
  	case CLOCK_EVT_MODE_SHUTDOWN:
  	case CLOCK_EVT_MODE_UNUSED:
b102c01fa   Alessandro Rubini   ARM: 5978/1: plat...
131
  		writel(0, mtu_base + MTU_IMSC);
2917947a6   Linus Walleij   ARM: 6153/1: noma...
132
  		/* disable timer */
2f73a0684   Jonas Aaberg   ARM: plat-nomadik...
133
  		writel(0, mtu_base + MTU_CR(1));
2917947a6   Linus Walleij   ARM: 6153/1: noma...
134
135
  		/* load some high default value */
  		writel(0xffffffff, mtu_base + MTU_LR(1));
28ad94ec6   Alessandro Rubini   [ARM] 5590/1: Add...
136
137
138
139
140
  		break;
  	case CLOCK_EVT_MODE_RESUME:
  		break;
  	}
  }
7172c19a2   Linus Walleij   clksrc: delete no...
141
  static void nmdk_clksrc_reset(void)
8726e96fc   Stephen Warren   ARM: ux500: conve...
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
  {
  	/* Disable */
  	writel(0, mtu_base + MTU_CR(0));
  
  	/* ClockSource: configure load and background-load, and fire it up */
  	writel(nmdk_cycle, mtu_base + MTU_LR(0));
  	writel(nmdk_cycle, mtu_base + MTU_BGLR(0));
  
  	writel(clk_prescale | MTU_CRn_32BITS | MTU_CRn_ENA,
  	       mtu_base + MTU_CR(0));
  }
  
  static void nmdk_clkevt_resume(struct clock_event_device *cedev)
  {
  	nmdk_clkevt_reset();
  	nmdk_clksrc_reset();
  }
28ad94ec6   Alessandro Rubini   [ARM] 5590/1: Add...
159
  static struct clock_event_device nmdk_clkevt = {
b102c01fa   Alessandro Rubini   ARM: 5978/1: plat...
160
  	.name		= "mtu_1",
74adcbffa   Daniel Lezcano   ARM: nomadik: add...
161
162
  	.features	= CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC |
  	                  CLOCK_EVT_FEAT_DYNIRQ,
b102c01fa   Alessandro Rubini   ARM: 5978/1: plat...
163
  	.rating		= 200,
28ad94ec6   Alessandro Rubini   [ARM] 5590/1: Add...
164
  	.set_mode	= nmdk_clkevt_mode,
b102c01fa   Alessandro Rubini   ARM: 5978/1: plat...
165
  	.set_next_event	= nmdk_clkevt_next,
8726e96fc   Stephen Warren   ARM: ux500: conve...
166
  	.resume		= nmdk_clkevt_resume,
28ad94ec6   Alessandro Rubini   [ARM] 5590/1: Add...
167
168
169
  };
  
  /*
b102c01fa   Alessandro Rubini   ARM: 5978/1: plat...
170
   * IRQ Handler for timer 1 of the MTU block.
28ad94ec6   Alessandro Rubini   [ARM] 5590/1: Add...
171
172
173
   */
  static irqreturn_t nmdk_timer_interrupt(int irq, void *dev_id)
  {
b102c01fa   Alessandro Rubini   ARM: 5978/1: plat...
174
  	struct clock_event_device *evdev = dev_id;
28ad94ec6   Alessandro Rubini   [ARM] 5590/1: Add...
175

b102c01fa   Alessandro Rubini   ARM: 5978/1: plat...
176
177
  	writel(1 << 1, mtu_base + MTU_ICR); /* Interrupt clear reg */
  	evdev->event_handler(evdev);
28ad94ec6   Alessandro Rubini   [ARM] 5590/1: Add...
178
179
  	return IRQ_HANDLED;
  }
28ad94ec6   Alessandro Rubini   [ARM] 5590/1: Add...
180
181
  static struct irqaction nmdk_timer_irq = {
  	.name		= "Nomadik Timer Tick",
38c30a842   Michael Opdenacker   clocksource: misc...
182
  	.flags		= IRQF_TIMER,
28ad94ec6   Alessandro Rubini   [ARM] 5590/1: Add...
183
  	.handler	= nmdk_timer_interrupt,
b102c01fa   Alessandro Rubini   ARM: 5978/1: plat...
184
  	.dev_id		= &nmdk_clkevt,
28ad94ec6   Alessandro Rubini   [ARM] 5590/1: Add...
185
  };
7172c19a2   Linus Walleij   clksrc: delete no...
186
187
  static void __init nmdk_timer_init(void __iomem *base, int irq,
  				   struct clk *pclk, struct clk *clk)
28ad94ec6   Alessandro Rubini   [ARM] 5590/1: Add...
188
  {
28ad94ec6   Alessandro Rubini   [ARM] 5590/1: Add...
189
  	unsigned long rate;
ba327b1e5   Linus Walleij   ARM: 6145/1: ux50...
190

b9576623c   Linus Walleij   ARM: plat-nomadik...
191
  	mtu_base = base;
16defa668   Ulf Hansson   clocksource/mtu-n...
192

c7785ea0d   Rabin Vincent   clocksource: noma...
193
194
  	BUG_ON(clk_prepare_enable(pclk));
  	BUG_ON(clk_prepare_enable(clk));
b102c01fa   Alessandro Rubini   ARM: 5978/1: plat...
195
196
  
  	/*
a0719f52d   Linus Walleij   ARM: 6376/1: plat...
197
198
199
200
201
202
  	 * Tick rate is 2.4MHz for Nomadik and 2.4Mhz, 100MHz or 133 MHz
  	 * for ux500.
  	 * Use a divide-by-16 counter if the tick rate is more than 32MHz.
  	 * At 32 MHz, the timer (with 32 bit counter) can be programmed
  	 * to wake-up at a max 127s a head in time. Dividing a 2.4 MHz timer
  	 * with 16 gives too low timer resolution.
b102c01fa   Alessandro Rubini   ARM: 5978/1: plat...
203
  	 */
c7785ea0d   Rabin Vincent   clocksource: noma...
204
  	rate = clk_get_rate(clk);
a0719f52d   Linus Walleij   ARM: 6376/1: plat...
205
  	if (rate > 32000000) {
b102c01fa   Alessandro Rubini   ARM: 5978/1: plat...
206
  		rate /= 16;
2f73a0684   Jonas Aaberg   ARM: plat-nomadik...
207
  		clk_prescale = MTU_CRn_PRESCALE_16;
b102c01fa   Alessandro Rubini   ARM: 5978/1: plat...
208
  	} else {
2f73a0684   Jonas Aaberg   ARM: plat-nomadik...
209
  		clk_prescale = MTU_CRn_PRESCALE_1;
b102c01fa   Alessandro Rubini   ARM: 5978/1: plat...
210
  	}
28ad94ec6   Alessandro Rubini   [ARM] 5590/1: Add...
211

213668317   Linus Walleij   ARM: plat-nomadik...
212
213
  	/* Cycles for periodic mode */
  	nmdk_cycle = DIV_ROUND_CLOSEST(rate, HZ);
2f73a0684   Jonas Aaberg   ARM: plat-nomadik...
214

b102c01fa   Alessandro Rubini   ARM: 5978/1: plat...
215
  	/* Timer 0 is the free running clocksource */
2f73a0684   Jonas Aaberg   ARM: plat-nomadik...
216
  	nmdk_clksrc_reset();
28ad94ec6   Alessandro Rubini   [ARM] 5590/1: Add...
217

bfe45e0be   Russell King   clocksource: conv...
218
219
  	if (clocksource_mmio_init(mtu_base + MTU_VAL(0), "mtu_0",
  			rate, 200, 32, clocksource_mmio_readl_down))
b102c01fa   Alessandro Rubini   ARM: 5978/1: plat...
220
221
  		pr_err("timer: failed to initialize clock source %s
  ",
bfe45e0be   Russell King   clocksource: conv...
222
  		       "mtu_0");
2f0778afa   Marc Zyngier   ARM: 7205/2: sche...
223

ea7113f70   Linus Walleij   clocksource: noma...
224
  #ifdef CONFIG_CLKSRC_NOMADIK_MTU_SCHED_CLOCK
e25bc5f5a   Stephen Boyd   clocksource: noma...
225
  	sched_clock_register(nomadik_read_sched_clock, 32, rate);
cba13830d   Mattias Wallin   ARM: plat-nomadik...
226
  #endif
2f0778afa   Marc Zyngier   ARM: 7205/2: sche...
227

a3b86a6d6   Linus Walleij   ARM: plat-nomadik...
228
  	/* Timer 1 is used for events, register irq and clockevents */
0813069d0   Linus Walleij   ARM: plat-nomadik...
229
  	setup_irq(irq, &nmdk_timer_irq);
a3b86a6d6   Linus Walleij   ARM: plat-nomadik...
230
  	nmdk_clkevt.cpumask = cpumask_of(0);
00f4e13c4   Daniel Lezcano   clocksource : Nom...
231
  	nmdk_clkevt.irq = irq;
a3b86a6d6   Linus Walleij   ARM: plat-nomadik...
232
  	clockevents_config_and_register(&nmdk_clkevt, rate, 2, 0xffffffffU);
6f179b724   Fabio Baltieri   clocksource: noma...
233
234
235
236
  
  	mtu_delay_timer.read_current_timer = &nmdk_timer_read_current_timer;
  	mtu_delay_timer.freq = rate;
  	register_current_timer_delay(&mtu_delay_timer);
28ad94ec6   Alessandro Rubini   [ARM] 5590/1: Add...
237
  }
c7785ea0d   Rabin Vincent   clocksource: noma...
238

3c09f4dae   Arnd Bergmann   ARM: nomadik: fix...
239
  static void __init nmdk_timer_of_init(struct device_node *node)
c7785ea0d   Rabin Vincent   clocksource: noma...
240
  {
c7785ea0d   Rabin Vincent   clocksource: noma...
241
242
243
244
  	struct clk *pclk;
  	struct clk *clk;
  	void __iomem *base;
  	int irq;
c7785ea0d   Rabin Vincent   clocksource: noma...
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
  	base = of_iomap(node, 0);
  	if (!base)
  		panic("Can't remap registers");
  
  	pclk = of_clk_get_by_name(node, "apb_pclk");
  	if (IS_ERR(pclk))
  		panic("could not get apb_pclk");
  
  	clk = of_clk_get_by_name(node, "timclk");
  	if (IS_ERR(clk))
  		panic("could not get timclk");
  
  	irq = irq_of_parse_and_map(node, 0);
  	if (irq <= 0)
  		panic("Can't parse IRQ");
7172c19a2   Linus Walleij   clksrc: delete no...
260
  	nmdk_timer_init(base, irq, pclk, clk);
c7785ea0d   Rabin Vincent   clocksource: noma...
261
262
263
  }
  CLOCKSOURCE_OF_DECLARE(nomadik_mtu, "st,nomadik-mtu",
  		       nmdk_timer_of_init);