aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clocksource/mtk_timer.c
diff options
context:
space:
mode:
authorMatthias Brugger <matthias.bgg@gmail.com>2014-07-18 05:36:43 -0400
committerDaniel Lezcano <daniel.lezcano@linaro.org>2014-07-23 06:02:31 -0400
commitecb3530dd59ddd5158bcc507d634e698130fd937 (patch)
treef685fb5a6c0d6ace4aceee3628fd3cd6c76a6caf /drivers/clocksource/mtk_timer.c
parentefd342fb0031a17758571dce42e3f373d94e2fec (diff)
clocksource: Add support for the Mediatek SoCs
This patch adds a clock source and clock event for the timer found on the Mediatek SoCs. The Mediatek General Purpose Timer block provides five 32 bit timers and one 64 bit timer. Two 32 bit timers are used by this driver: TIMER1: clock events supporting periodic and oneshot events TIMER2: clock source configured as a free running counter The General Purpose Timer block can be run with two clocks. A 13 MHz system clock and the RTC clock running at 32 KHz. This implementation uses the system clock with no clock source divider. The interrupts are shared between the different timers and have to be read back from a register. We just enable one interrupt for the clock event. The clock event timer is used by all cores. Signed-off-by: Matthias Brugger <matthias.bgg@gmail.com> Acked-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Diffstat (limited to 'drivers/clocksource/mtk_timer.c')
-rw-r--r--drivers/clocksource/mtk_timer.c261
1 files changed, 261 insertions, 0 deletions
diff --git a/drivers/clocksource/mtk_timer.c b/drivers/clocksource/mtk_timer.c
new file mode 100644
index 000000000000..32a3d25795d3
--- /dev/null
+++ b/drivers/clocksource/mtk_timer.c
@@ -0,0 +1,261 @@
1/*
2 * Mediatek SoCs General-Purpose Timer handling.
3 *
4 * Copyright (C) 2014 Matthias Brugger
5 *
6 * Matthias Brugger <matthias.bgg@gmail.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 */
18
19#include <linux/clk.h>
20#include <linux/clockchips.h>
21#include <linux/interrupt.h>
22#include <linux/irq.h>
23#include <linux/irqreturn.h>
24#include <linux/of.h>
25#include <linux/of_address.h>
26#include <linux/of_irq.h>
27#include <linux/slab.h>
28
29#define GPT_IRQ_EN_REG 0x00
30#define GPT_IRQ_ENABLE(val) BIT((val) - 1)
31#define GPT_IRQ_ACK_REG 0x08
32#define GPT_IRQ_ACK(val) BIT((val) - 1)
33
34#define TIMER_CTRL_REG(val) (0x10 * (val))
35#define TIMER_CTRL_OP(val) (((val) & 0x3) << 4)
36#define TIMER_CTRL_OP_ONESHOT (0)
37#define TIMER_CTRL_OP_REPEAT (1)
38#define TIMER_CTRL_OP_FREERUN (3)
39#define TIMER_CTRL_CLEAR (2)
40#define TIMER_CTRL_ENABLE (1)
41#define TIMER_CTRL_DISABLE (0)
42
43#define TIMER_CLK_REG(val) (0x04 + (0x10 * (val)))
44#define TIMER_CLK_SRC(val) (((val) & 0x1) << 4)
45#define TIMER_CLK_SRC_SYS13M (0)
46#define TIMER_CLK_SRC_RTC32K (1)
47#define TIMER_CLK_DIV1 (0x0)
48#define TIMER_CLK_DIV2 (0x1)
49
50#define TIMER_CNT_REG(val) (0x08 + (0x10 * (val)))
51#define TIMER_CMP_REG(val) (0x0C + (0x10 * (val)))
52
53#define GPT_CLK_EVT 1
54#define GPT_CLK_SRC 2
55
56struct mtk_clock_event_device {
57 void __iomem *gpt_base;
58 u32 ticks_per_jiffy;
59 struct clock_event_device dev;
60};
61
62static inline struct mtk_clock_event_device *to_mtk_clk(
63 struct clock_event_device *c)
64{
65 return container_of(c, struct mtk_clock_event_device, dev);
66}
67
68static void mtk_clkevt_time_stop(struct mtk_clock_event_device *evt, u8 timer)
69{
70 u32 val;
71
72 val = readl(evt->gpt_base + TIMER_CTRL_REG(timer));
73 writel(val & ~TIMER_CTRL_ENABLE, evt->gpt_base +
74 TIMER_CTRL_REG(timer));
75}
76
77static void mtk_clkevt_time_setup(struct mtk_clock_event_device *evt,
78 unsigned long delay, u8 timer)
79{
80 writel(delay, evt->gpt_base + TIMER_CMP_REG(timer));
81}
82
83static void mtk_clkevt_time_start(struct mtk_clock_event_device *evt,
84 bool periodic, u8 timer)
85{
86 u32 val;
87
88 /* Acknowledge interrupt */
89 writel(GPT_IRQ_ACK(timer), evt->gpt_base + GPT_IRQ_ACK_REG);
90
91 val = readl(evt->gpt_base + TIMER_CTRL_REG(timer));
92
93 /* Clear 2 bit timer operation mode field */
94 val &= ~TIMER_CTRL_OP(0x3);
95
96 if (periodic)
97 val |= TIMER_CTRL_OP(TIMER_CTRL_OP_REPEAT);
98 else
99 val |= TIMER_CTRL_OP(TIMER_CTRL_OP_ONESHOT);
100
101 writel(val | TIMER_CTRL_ENABLE | TIMER_CTRL_CLEAR,
102 evt->gpt_base + TIMER_CTRL_REG(timer));
103}
104
105static void mtk_clkevt_mode(enum clock_event_mode mode,
106 struct clock_event_device *clk)
107{
108 struct mtk_clock_event_device *evt = to_mtk_clk(clk);
109
110 mtk_clkevt_time_stop(evt, GPT_CLK_EVT);
111
112 switch (mode) {
113 case CLOCK_EVT_MODE_PERIODIC:
114 mtk_clkevt_time_setup(evt, evt->ticks_per_jiffy, GPT_CLK_EVT);
115 mtk_clkevt_time_start(evt, true, GPT_CLK_EVT);
116 break;
117 case CLOCK_EVT_MODE_ONESHOT:
118 /* Timer is enabled in set_next_event */
119 break;
120 case CLOCK_EVT_MODE_UNUSED:
121 case CLOCK_EVT_MODE_SHUTDOWN:
122 default:
123 /* No more interrupts will occur as source is disabled */
124 break;
125 }
126}
127
128static int mtk_clkevt_next_event(unsigned long event,
129 struct clock_event_device *clk)
130{
131 struct mtk_clock_event_device *evt = to_mtk_clk(clk);
132
133 mtk_clkevt_time_stop(evt, GPT_CLK_EVT);
134 mtk_clkevt_time_setup(evt, event, GPT_CLK_EVT);
135 mtk_clkevt_time_start(evt, false, GPT_CLK_EVT);
136
137 return 0;
138}
139
140static irqreturn_t mtk_timer_interrupt(int irq, void *dev_id)
141{
142 struct mtk_clock_event_device *evt = dev_id;
143
144 /* Acknowledge timer0 irq */
145 writel(GPT_IRQ_ACK(GPT_CLK_EVT), evt->gpt_base + GPT_IRQ_ACK_REG);
146 evt->dev.event_handler(&evt->dev);
147
148 return IRQ_HANDLED;
149}
150
151static void mtk_timer_global_reset(struct mtk_clock_event_device *evt)
152{
153 /* Disable all interrupts */
154 writel(0x0, evt->gpt_base + GPT_IRQ_EN_REG);
155 /* Acknowledge all interrupts */
156 writel(0x3f, evt->gpt_base + GPT_IRQ_ACK_REG);
157}
158
159static void
160mtk_timer_setup(struct mtk_clock_event_device *evt, u8 timer, u8 option)
161{
162 writel(TIMER_CTRL_CLEAR | TIMER_CTRL_DISABLE,
163 evt->gpt_base + TIMER_CTRL_REG(timer));
164
165 writel(TIMER_CLK_SRC(TIMER_CLK_SRC_SYS13M) | TIMER_CLK_DIV1,
166 evt->gpt_base + TIMER_CLK_REG(timer));
167
168 writel(0x0, evt->gpt_base + TIMER_CMP_REG(timer));
169
170 writel(TIMER_CTRL_OP(option) | TIMER_CTRL_ENABLE,
171 evt->gpt_base + TIMER_CTRL_REG(timer));
172}
173
174static void mtk_timer_enable_irq(struct mtk_clock_event_device *evt, u8 timer)
175{
176 u32 val;
177
178 val = readl(evt->gpt_base + GPT_IRQ_EN_REG);
179 writel(val | GPT_IRQ_ENABLE(timer),
180 evt->gpt_base + GPT_IRQ_EN_REG);
181}
182
183static void __init mtk_timer_init(struct device_node *node)
184{
185 struct mtk_clock_event_device *evt;
186 struct resource res;
187 unsigned long rate = 0;
188 struct clk *clk;
189
190 evt = kzalloc(sizeof(*evt), GFP_KERNEL);
191 if (!evt) {
192 pr_warn("Can't allocate mtk clock event driver struct");
193 return;
194 }
195
196 evt->dev.name = "mtk_tick";
197 evt->dev.rating = 300;
198 evt->dev.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
199 evt->dev.set_mode = mtk_clkevt_mode;
200 evt->dev.set_next_event = mtk_clkevt_next_event;
201 evt->dev.cpumask = cpu_possible_mask;
202
203 evt->gpt_base = of_io_request_and_map(node, 0, "mtk-timer");
204 if (IS_ERR(evt->gpt_base)) {
205 pr_warn("Can't get resource\n");
206 return;
207 }
208
209 evt->dev.irq = irq_of_parse_and_map(node, 0);
210 if (evt->dev.irq <= 0) {
211 pr_warn("Can't parse IRQ");
212 goto err_mem;
213 }
214
215 clk = of_clk_get(node, 0);
216 if (IS_ERR(clk)) {
217 pr_warn("Can't get timer clock");
218 goto err_irq;
219 }
220
221 if (clk_prepare_enable(clk)) {
222 pr_warn("Can't prepare clock");
223 goto err_clk_put;
224 }
225 rate = clk_get_rate(clk);
226
227 if (request_irq(evt->dev.irq, mtk_timer_interrupt,
228 IRQF_TIMER | IRQF_IRQPOLL, "mtk_timer", evt)) {
229 pr_warn("failed to setup irq %d\n", evt->dev.irq);
230 goto err_clk_disable;
231 }
232
233 evt->ticks_per_jiffy = DIV_ROUND_UP(rate, HZ);
234
235 mtk_timer_global_reset(evt);
236
237 /* Configure clock source */
238 mtk_timer_setup(evt, GPT_CLK_SRC, TIMER_CTRL_OP_FREERUN);
239 clocksource_mmio_init(evt->gpt_base + TIMER_CNT_REG(GPT_CLK_SRC),
240 node->name, rate, 300, 32, clocksource_mmio_readl_up);
241
242 /* Configure clock event */
243 mtk_timer_setup(evt, GPT_CLK_EVT, TIMER_CTRL_OP_REPEAT);
244 mtk_timer_enable_irq(evt, GPT_CLK_EVT);
245
246 clockevents_config_and_register(&evt->dev, rate, 0x3,
247 0xffffffff);
248 return;
249
250err_clk_disable:
251 clk_disable_unprepare(clk);
252err_clk_put:
253 clk_put(clk);
254err_irq:
255 irq_dispose_mapping(evt->dev.irq);
256err_mem:
257 iounmap(evt->gpt_base);
258 of_address_to_resource(node, 0, &res);
259 release_mem_region(res.start, resource_size(&res));
260}
261CLOCKSOURCE_OF_DECLARE(mtk_mt6577, "mediatek,mt6577-timer", mtk_timer_init);