summaryrefslogtreecommitdiffstats
path: root/drivers/clocksource/timer-fttmr010.c
diff options
context:
space:
mode:
authorLinus Walleij <linus.walleij@linaro.org>2017-03-24 17:32:34 -0400
committerDaniel Lezcano <daniel.lezcano@linaro.org>2017-04-07 10:23:08 -0400
commitf5bf0ee4ebf779e256bb710f638b4452d94e97fb (patch)
treef70d8f1cc97b7746ad94660a159155c1bb2edf04 /drivers/clocksource/timer-fttmr010.c
parentbb0c6bb3b71cec4b00c3fb1c055f92ad05b905db (diff)
clocksource/drivers/gemini: Rename Gemini timer to Faraday
After some research it turns out that the "Gemini" timer is actually a generic IP block from Faraday Technology named FTTMR010, so as to not make things too confusing we need to rename the driver and its symbols to make sense. The implementation remains the same in this patch but we fix the copy-paste error in the timer name "nomadik_mtu" as we're at it. Signed-off-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Diffstat (limited to 'drivers/clocksource/timer-fttmr010.c')
-rw-r--r--drivers/clocksource/timer-fttmr010.c276
1 files changed, 276 insertions, 0 deletions
diff --git a/drivers/clocksource/timer-fttmr010.c b/drivers/clocksource/timer-fttmr010.c
new file mode 100644
index 000000000000..e37ec3d69a7e
--- /dev/null
+++ b/drivers/clocksource/timer-fttmr010.c
@@ -0,0 +1,276 @@
1/*
2 * Faraday Technology FTTMR010 timer driver
3 * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
4 *
5 * Based on a rewrite of arch/arm/mach-gemini/timer.c:
6 * Copyright (C) 2001-2006 Storlink, Corp.
7 * Copyright (C) 2008-2009 Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
8 */
9#include <linux/interrupt.h>
10#include <linux/io.h>
11#include <linux/of.h>
12#include <linux/of_address.h>
13#include <linux/of_irq.h>
14#include <linux/mfd/syscon.h>
15#include <linux/regmap.h>
16#include <linux/clockchips.h>
17#include <linux/clocksource.h>
18#include <linux/sched_clock.h>
19
20/*
21 * Relevant registers in the global syscon
22 */
23#define GLOBAL_STATUS 0x04
24#define CPU_AHB_RATIO_MASK (0x3 << 18)
25#define CPU_AHB_1_1 (0x0 << 18)
26#define CPU_AHB_3_2 (0x1 << 18)
27#define CPU_AHB_24_13 (0x2 << 18)
28#define CPU_AHB_2_1 (0x3 << 18)
29#define REG_TO_AHB_SPEED(reg) ((((reg) >> 15) & 0x7) * 10 + 130)
30
31/*
32 * Register definitions for the timers
33 */
34#define TIMER1_COUNT (0x00)
35#define TIMER1_LOAD (0x04)
36#define TIMER1_MATCH1 (0x08)
37#define TIMER1_MATCH2 (0x0c)
38#define TIMER2_COUNT (0x10)
39#define TIMER2_LOAD (0x14)
40#define TIMER2_MATCH1 (0x18)
41#define TIMER2_MATCH2 (0x1c)
42#define TIMER3_COUNT (0x20)
43#define TIMER3_LOAD (0x24)
44#define TIMER3_MATCH1 (0x28)
45#define TIMER3_MATCH2 (0x2c)
46#define TIMER_CR (0x30)
47#define TIMER_INTR_STATE (0x34)
48#define TIMER_INTR_MASK (0x38)
49
50#define TIMER_1_CR_ENABLE (1 << 0)
51#define TIMER_1_CR_CLOCK (1 << 1)
52#define TIMER_1_CR_INT (1 << 2)
53#define TIMER_2_CR_ENABLE (1 << 3)
54#define TIMER_2_CR_CLOCK (1 << 4)
55#define TIMER_2_CR_INT (1 << 5)
56#define TIMER_3_CR_ENABLE (1 << 6)
57#define TIMER_3_CR_CLOCK (1 << 7)
58#define TIMER_3_CR_INT (1 << 8)
59#define TIMER_1_CR_UPDOWN (1 << 9)
60#define TIMER_2_CR_UPDOWN (1 << 10)
61#define TIMER_3_CR_UPDOWN (1 << 11)
62#define TIMER_DEFAULT_FLAGS (TIMER_1_CR_UPDOWN | \
63 TIMER_3_CR_ENABLE | \
64 TIMER_3_CR_UPDOWN)
65
66#define TIMER_1_INT_MATCH1 (1 << 0)
67#define TIMER_1_INT_MATCH2 (1 << 1)
68#define TIMER_1_INT_OVERFLOW (1 << 2)
69#define TIMER_2_INT_MATCH1 (1 << 3)
70#define TIMER_2_INT_MATCH2 (1 << 4)
71#define TIMER_2_INT_OVERFLOW (1 << 5)
72#define TIMER_3_INT_MATCH1 (1 << 6)
73#define TIMER_3_INT_MATCH2 (1 << 7)
74#define TIMER_3_INT_OVERFLOW (1 << 8)
75#define TIMER_INT_ALL_MASK 0x1ff
76
77static unsigned int tick_rate;
78static void __iomem *base;
79
80static u64 notrace fttmr010_read_sched_clock(void)
81{
82 return readl(base + TIMER3_COUNT);
83}
84
85static int fttmr010_timer_set_next_event(unsigned long cycles,
86 struct clock_event_device *evt)
87{
88 u32 cr;
89
90 /* Setup the match register */
91 cr = readl(base + TIMER1_COUNT);
92 writel(cr + cycles, base + TIMER1_MATCH1);
93 if (readl(base + TIMER1_COUNT) - cr > cycles)
94 return -ETIME;
95
96 return 0;
97}
98
99static int fttmr010_timer_shutdown(struct clock_event_device *evt)
100{
101 u32 cr;
102
103 /*
104 * Disable also for oneshot: the set_next() call will arm the timer
105 * instead.
106 */
107 /* Stop timer and interrupt. */
108 cr = readl(base + TIMER_CR);
109 cr &= ~(TIMER_1_CR_ENABLE | TIMER_1_CR_INT);
110 writel(cr, base + TIMER_CR);
111
112 /* Setup counter start from 0 */
113 writel(0, base + TIMER1_COUNT);
114 writel(0, base + TIMER1_LOAD);
115
116 /* enable interrupt */
117 cr = readl(base + TIMER_INTR_MASK);
118 cr &= ~(TIMER_1_INT_OVERFLOW | TIMER_1_INT_MATCH2);
119 cr |= TIMER_1_INT_MATCH1;
120 writel(cr, base + TIMER_INTR_MASK);
121
122 /* start the timer */
123 cr = readl(base + TIMER_CR);
124 cr |= TIMER_1_CR_ENABLE;
125 writel(cr, base + TIMER_CR);
126
127 return 0;
128}
129
130static int fttmr010_timer_set_periodic(struct clock_event_device *evt)
131{
132 u32 period = DIV_ROUND_CLOSEST(tick_rate, HZ);
133 u32 cr;
134
135 /* Stop timer and interrupt */
136 cr = readl(base + TIMER_CR);
137 cr &= ~(TIMER_1_CR_ENABLE | TIMER_1_CR_INT);
138 writel(cr, base + TIMER_CR);
139
140 /* Setup timer to fire at 1/HT intervals. */
141 cr = 0xffffffff - (period - 1);
142 writel(cr, base + TIMER1_COUNT);
143 writel(cr, base + TIMER1_LOAD);
144
145 /* enable interrupt on overflow */
146 cr = readl(base + TIMER_INTR_MASK);
147 cr &= ~(TIMER_1_INT_MATCH1 | TIMER_1_INT_MATCH2);
148 cr |= TIMER_1_INT_OVERFLOW;
149 writel(cr, base + TIMER_INTR_MASK);
150
151 /* Start the timer */
152 cr = readl(base + TIMER_CR);
153 cr |= TIMER_1_CR_ENABLE;
154 cr |= TIMER_1_CR_INT;
155 writel(cr, base + TIMER_CR);
156
157 return 0;
158}
159
160/* Use TIMER1 as clock event */
161static struct clock_event_device fttmr010_clockevent = {
162 .name = "TIMER1",
163 /* Reasonably fast and accurate clock event */
164 .rating = 300,
165 .shift = 32,
166 .features = CLOCK_EVT_FEAT_PERIODIC |
167 CLOCK_EVT_FEAT_ONESHOT,
168 .set_next_event = fttmr010_timer_set_next_event,
169 .set_state_shutdown = fttmr010_timer_shutdown,
170 .set_state_periodic = fttmr010_timer_set_periodic,
171 .set_state_oneshot = fttmr010_timer_shutdown,
172 .tick_resume = fttmr010_timer_shutdown,
173};
174
175/*
176 * IRQ handler for the timer
177 */
178static irqreturn_t fttmr010_timer_interrupt(int irq, void *dev_id)
179{
180 struct clock_event_device *evt = &fttmr010_clockevent;
181
182 evt->event_handler(evt);
183 return IRQ_HANDLED;
184}
185
186static struct irqaction fttmr010_timer_irq = {
187 .name = "Faraday FTTMR010 Timer Tick",
188 .flags = IRQF_TIMER,
189 .handler = fttmr010_timer_interrupt,
190};
191
192static int __init gemini_timer_of_init(struct device_node *np)
193{
194 static struct regmap *map;
195 int irq;
196 int ret;
197 u32 val;
198
199 map = syscon_regmap_lookup_by_phandle(np, "syscon");
200 if (IS_ERR(map)) {
201 pr_err("Can't get regmap for syscon handle");
202 return -ENODEV;
203 }
204 ret = regmap_read(map, GLOBAL_STATUS, &val);
205 if (ret) {
206 pr_err("Can't read syscon status register");
207 return -ENXIO;
208 }
209
210 base = of_iomap(np, 0);
211 if (!base) {
212 pr_err("Can't remap registers");
213 return -ENXIO;
214 }
215 /* IRQ for timer 1 */
216 irq = irq_of_parse_and_map(np, 0);
217 if (irq <= 0) {
218 pr_err("Can't parse IRQ");
219 return -EINVAL;
220 }
221
222 tick_rate = REG_TO_AHB_SPEED(val) * 1000000;
223 printk(KERN_INFO "Bus: %dMHz", tick_rate / 1000000);
224
225 tick_rate /= 6; /* APB bus run AHB*(1/6) */
226
227 switch (val & CPU_AHB_RATIO_MASK) {
228 case CPU_AHB_1_1:
229 printk(KERN_CONT "(1/1)\n");
230 break;
231 case CPU_AHB_3_2:
232 printk(KERN_CONT "(3/2)\n");
233 break;
234 case CPU_AHB_24_13:
235 printk(KERN_CONT "(24/13)\n");
236 break;
237 case CPU_AHB_2_1:
238 printk(KERN_CONT "(2/1)\n");
239 break;
240 }
241
242 /*
243 * Reset the interrupt mask and status
244 */
245 writel(TIMER_INT_ALL_MASK, base + TIMER_INTR_MASK);
246 writel(0, base + TIMER_INTR_STATE);
247 writel(TIMER_DEFAULT_FLAGS, base + TIMER_CR);
248
249 /*
250 * Setup free-running clocksource timer (interrupts
251 * disabled.)
252 */
253 writel(0, base + TIMER3_COUNT);
254 writel(0, base + TIMER3_LOAD);
255 writel(0, base + TIMER3_MATCH1);
256 writel(0, base + TIMER3_MATCH2);
257 clocksource_mmio_init(base + TIMER3_COUNT,
258 "fttmr010_clocksource", tick_rate,
259 300, 32, clocksource_mmio_readl_up);
260 sched_clock_register(fttmr010_read_sched_clock, 32, tick_rate);
261
262 /*
263 * Setup clockevent timer (interrupt-driven.)
264 */
265 writel(0, base + TIMER1_COUNT);
266 writel(0, base + TIMER1_LOAD);
267 writel(0, base + TIMER1_MATCH1);
268 writel(0, base + TIMER1_MATCH2);
269 setup_irq(irq, &fttmr010_timer_irq);
270 fttmr010_clockevent.cpumask = cpumask_of(0);
271 clockevents_config_and_register(&fttmr010_clockevent, tick_rate,
272 1, 0xffffffff);
273
274 return 0;
275}
276CLOCKSOURCE_OF_DECLARE(gemini, "cortina,gemini-timer", gemini_timer_of_init);