]>
Commit | Line | Data |
---|---|---|
050dd322 JE |
1 | /* |
2 | * Clocksource driver for NXP LPC32xx/18xx/43xx timer | |
3 | * | |
4 | * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com> | |
5 | * | |
6 | * Based on: | |
7 | * time-efm32 Copyright (C) 2013 Pengutronix | |
8 | * mach-lpc32xx/timer.c Copyright (C) 2009 - 2010 NXP Semiconductors | |
9 | * | |
10 | * This file is licensed under the terms of the GNU General Public | |
11 | * License version 2. This program is licensed "as is" without any | |
12 | * warranty of any kind, whether express or implied. | |
13 | * | |
14 | */ | |
15 | ||
16 | #define pr_fmt(fmt) "%s: " fmt, __func__ | |
17 | ||
18 | #include <linux/clk.h> | |
19 | #include <linux/clockchips.h> | |
20 | #include <linux/clocksource.h> | |
21 | #include <linux/interrupt.h> | |
22 | #include <linux/irq.h> | |
23 | #include <linux/kernel.h> | |
24 | #include <linux/of.h> | |
25 | #include <linux/of_address.h> | |
26 | #include <linux/of_irq.h> | |
27 | #include <linux/sched_clock.h> | |
28 | ||
29 | #define LPC32XX_TIMER_IR 0x000 | |
30 | #define LPC32XX_TIMER_IR_MR0INT BIT(0) | |
31 | #define LPC32XX_TIMER_TCR 0x004 | |
32 | #define LPC32XX_TIMER_TCR_CEN BIT(0) | |
33 | #define LPC32XX_TIMER_TCR_CRST BIT(1) | |
34 | #define LPC32XX_TIMER_TC 0x008 | |
35 | #define LPC32XX_TIMER_PR 0x00c | |
36 | #define LPC32XX_TIMER_MCR 0x014 | |
37 | #define LPC32XX_TIMER_MCR_MR0I BIT(0) | |
38 | #define LPC32XX_TIMER_MCR_MR0R BIT(1) | |
39 | #define LPC32XX_TIMER_MCR_MR0S BIT(2) | |
40 | #define LPC32XX_TIMER_MR0 0x018 | |
41 | #define LPC32XX_TIMER_CTCR 0x070 | |
42 | ||
43 | struct lpc32xx_clock_event_ddata { | |
44 | struct clock_event_device evtdev; | |
45 | void __iomem *base; | |
46 | }; | |
47 | ||
48 | /* Needed for the sched clock */ | |
49 | static void __iomem *clocksource_timer_counter; | |
50 | ||
51 | static u64 notrace lpc32xx_read_sched_clock(void) | |
52 | { | |
53 | return readl(clocksource_timer_counter); | |
54 | } | |
55 | ||
56 | static int lpc32xx_clkevt_next_event(unsigned long delta, | |
57 | struct clock_event_device *evtdev) | |
58 | { | |
59 | struct lpc32xx_clock_event_ddata *ddata = | |
60 | container_of(evtdev, struct lpc32xx_clock_event_ddata, evtdev); | |
61 | ||
62 | /* | |
751db1a6 EG |
63 | * Place timer in reset and program the delta in the match |
64 | * channel 0 (MR0). When the timer counter matches the value | |
65 | * in MR0 register the match will trigger an interrupt. | |
66 | * After setup the timer is released from reset and enabled. | |
050dd322 JE |
67 | */ |
68 | writel_relaxed(LPC32XX_TIMER_TCR_CRST, ddata->base + LPC32XX_TIMER_TCR); | |
751db1a6 | 69 | writel_relaxed(delta, ddata->base + LPC32XX_TIMER_MR0); |
050dd322 JE |
70 | writel_relaxed(LPC32XX_TIMER_TCR_CEN, ddata->base + LPC32XX_TIMER_TCR); |
71 | ||
72 | return 0; | |
73 | } | |
74 | ||
75 | static int lpc32xx_clkevt_shutdown(struct clock_event_device *evtdev) | |
76 | { | |
77 | struct lpc32xx_clock_event_ddata *ddata = | |
78 | container_of(evtdev, struct lpc32xx_clock_event_ddata, evtdev); | |
79 | ||
80 | /* Disable the timer */ | |
81 | writel_relaxed(0, ddata->base + LPC32XX_TIMER_TCR); | |
82 | ||
83 | return 0; | |
84 | } | |
85 | ||
86 | static int lpc32xx_clkevt_oneshot(struct clock_event_device *evtdev) | |
87 | { | |
88 | /* | |
89 | * When using oneshot, we must also disable the timer | |
90 | * to wait for the first call to set_next_event(). | |
91 | */ | |
92 | return lpc32xx_clkevt_shutdown(evtdev); | |
93 | } | |
94 | ||
95 | static irqreturn_t lpc32xx_clock_event_handler(int irq, void *dev_id) | |
96 | { | |
97 | struct lpc32xx_clock_event_ddata *ddata = dev_id; | |
98 | ||
99 | /* Clear match on channel 0 */ | |
100 | writel_relaxed(LPC32XX_TIMER_IR_MR0INT, ddata->base + LPC32XX_TIMER_IR); | |
101 | ||
102 | ddata->evtdev.event_handler(&ddata->evtdev); | |
103 | ||
104 | return IRQ_HANDLED; | |
105 | } | |
106 | ||
107 | static struct lpc32xx_clock_event_ddata lpc32xx_clk_event_ddata = { | |
108 | .evtdev = { | |
109 | .name = "lpc3220 clockevent", | |
110 | .features = CLOCK_EVT_FEAT_ONESHOT, | |
111 | .rating = 300, | |
112 | .set_next_event = lpc32xx_clkevt_next_event, | |
113 | .set_state_shutdown = lpc32xx_clkevt_shutdown, | |
114 | .set_state_oneshot = lpc32xx_clkevt_oneshot, | |
115 | }, | |
116 | }; | |
117 | ||
118 | static int __init lpc32xx_clocksource_init(struct device_node *np) | |
119 | { | |
120 | void __iomem *base; | |
121 | unsigned long rate; | |
122 | struct clk *clk; | |
123 | int ret; | |
124 | ||
125 | clk = of_clk_get_by_name(np, "timerclk"); | |
126 | if (IS_ERR(clk)) { | |
d662ed20 | 127 | pr_err("clock get failed (%ld)\n", PTR_ERR(clk)); |
050dd322 JE |
128 | return PTR_ERR(clk); |
129 | } | |
130 | ||
131 | ret = clk_prepare_enable(clk); | |
132 | if (ret) { | |
133 | pr_err("clock enable failed (%d)\n", ret); | |
134 | goto err_clk_enable; | |
135 | } | |
136 | ||
137 | base = of_iomap(np, 0); | |
138 | if (!base) { | |
139 | pr_err("unable to map registers\n"); | |
140 | ret = -EADDRNOTAVAIL; | |
141 | goto err_iomap; | |
142 | } | |
143 | ||
144 | /* | |
145 | * Disable and reset timer then set it to free running timer | |
146 | * mode (CTCR) with no prescaler (PR) or match operations (MCR). | |
147 | * After setup the timer is released from reset and enabled. | |
148 | */ | |
149 | writel_relaxed(LPC32XX_TIMER_TCR_CRST, base + LPC32XX_TIMER_TCR); | |
150 | writel_relaxed(0, base + LPC32XX_TIMER_PR); | |
151 | writel_relaxed(0, base + LPC32XX_TIMER_MCR); | |
152 | writel_relaxed(0, base + LPC32XX_TIMER_CTCR); | |
153 | writel_relaxed(LPC32XX_TIMER_TCR_CEN, base + LPC32XX_TIMER_TCR); | |
154 | ||
155 | rate = clk_get_rate(clk); | |
156 | ret = clocksource_mmio_init(base + LPC32XX_TIMER_TC, "lpc3220 timer", | |
157 | rate, 300, 32, clocksource_mmio_readl_up); | |
158 | if (ret) { | |
159 | pr_err("failed to init clocksource (%d)\n", ret); | |
160 | goto err_clocksource_init; | |
161 | } | |
162 | ||
163 | clocksource_timer_counter = base + LPC32XX_TIMER_TC; | |
164 | sched_clock_register(lpc32xx_read_sched_clock, 32, rate); | |
165 | ||
166 | return 0; | |
167 | ||
168 | err_clocksource_init: | |
169 | iounmap(base); | |
170 | err_iomap: | |
171 | clk_disable_unprepare(clk); | |
172 | err_clk_enable: | |
173 | clk_put(clk); | |
174 | return ret; | |
175 | } | |
176 | ||
177 | static int __init lpc32xx_clockevent_init(struct device_node *np) | |
178 | { | |
179 | void __iomem *base; | |
180 | unsigned long rate; | |
181 | struct clk *clk; | |
182 | int ret, irq; | |
183 | ||
184 | clk = of_clk_get_by_name(np, "timerclk"); | |
185 | if (IS_ERR(clk)) { | |
d662ed20 | 186 | pr_err("clock get failed (%ld)\n", PTR_ERR(clk)); |
050dd322 JE |
187 | return PTR_ERR(clk); |
188 | } | |
189 | ||
190 | ret = clk_prepare_enable(clk); | |
191 | if (ret) { | |
192 | pr_err("clock enable failed (%d)\n", ret); | |
193 | goto err_clk_enable; | |
194 | } | |
195 | ||
196 | base = of_iomap(np, 0); | |
197 | if (!base) { | |
198 | pr_err("unable to map registers\n"); | |
199 | ret = -EADDRNOTAVAIL; | |
200 | goto err_iomap; | |
201 | } | |
202 | ||
203 | irq = irq_of_parse_and_map(np, 0); | |
204 | if (!irq) { | |
205 | pr_err("get irq failed\n"); | |
206 | ret = -ENOENT; | |
207 | goto err_irq; | |
208 | } | |
209 | ||
210 | /* | |
211 | * Disable timer and clear any pending interrupt (IR) on match | |
751db1a6 EG |
212 | * channel 0 (MR0). Clear the prescaler as it's not used. |
213 | * Enable interrupt, reset on match and stop on match (MCR). | |
050dd322 JE |
214 | */ |
215 | writel_relaxed(0, base + LPC32XX_TIMER_TCR); | |
751db1a6 | 216 | writel_relaxed(0, base + LPC32XX_TIMER_PR); |
050dd322 JE |
217 | writel_relaxed(0, base + LPC32XX_TIMER_CTCR); |
218 | writel_relaxed(LPC32XX_TIMER_IR_MR0INT, base + LPC32XX_TIMER_IR); | |
050dd322 JE |
219 | writel_relaxed(LPC32XX_TIMER_MCR_MR0I | LPC32XX_TIMER_MCR_MR0R | |
220 | LPC32XX_TIMER_MCR_MR0S, base + LPC32XX_TIMER_MCR); | |
221 | ||
222 | rate = clk_get_rate(clk); | |
223 | lpc32xx_clk_event_ddata.base = base; | |
224 | clockevents_config_and_register(&lpc32xx_clk_event_ddata.evtdev, | |
225 | rate, 1, -1); | |
226 | ||
227 | ret = request_irq(irq, lpc32xx_clock_event_handler, | |
228 | IRQF_TIMER | IRQF_IRQPOLL, "lpc3220 clockevent", | |
229 | &lpc32xx_clk_event_ddata); | |
230 | if (ret) { | |
231 | pr_err("request irq failed\n"); | |
232 | goto err_irq; | |
233 | } | |
234 | ||
235 | return 0; | |
236 | ||
237 | err_irq: | |
238 | iounmap(base); | |
239 | err_iomap: | |
240 | clk_disable_unprepare(clk); | |
241 | err_clk_enable: | |
242 | clk_put(clk); | |
243 | return ret; | |
244 | } | |
245 | ||
246 | /* | |
247 | * This function asserts that we have exactly one clocksource and one | |
248 | * clock_event_device in the end. | |
249 | */ | |
250 | static void __init lpc32xx_timer_init(struct device_node *np) | |
251 | { | |
252 | static int has_clocksource, has_clockevent; | |
253 | int ret; | |
254 | ||
255 | if (!has_clocksource) { | |
256 | ret = lpc32xx_clocksource_init(np); | |
257 | if (!ret) { | |
258 | has_clocksource = 1; | |
259 | return; | |
260 | } | |
261 | } | |
262 | ||
263 | if (!has_clockevent) { | |
264 | ret = lpc32xx_clockevent_init(np); | |
265 | if (!ret) { | |
266 | has_clockevent = 1; | |
267 | return; | |
268 | } | |
269 | } | |
270 | } | |
271 | CLOCKSOURCE_OF_DECLARE(lpc32xx_timer, "nxp,lpc3220-timer", lpc32xx_timer_init); |