diff options
Diffstat (limited to 'drivers/clocksource/timer-fttmr010.c')
-rw-r--r-- | drivers/clocksource/timer-fttmr010.c | 276 |
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 | |||
77 | static unsigned int tick_rate; | ||
78 | static void __iomem *base; | ||
79 | |||
80 | static u64 notrace fttmr010_read_sched_clock(void) | ||
81 | { | ||
82 | return readl(base + TIMER3_COUNT); | ||
83 | } | ||
84 | |||
85 | static 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 | |||
99 | static 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 | |||
130 | static 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 */ | ||
161 | static 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 | */ | ||
178 | static 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 | |||
186 | static struct irqaction fttmr010_timer_irq = { | ||
187 | .name = "Faraday FTTMR010 Timer Tick", | ||
188 | .flags = IRQF_TIMER, | ||
189 | .handler = fttmr010_timer_interrupt, | ||
190 | }; | ||
191 | |||
192 | static 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 | } | ||
276 | CLOCKSOURCE_OF_DECLARE(gemini, "cortina,gemini-timer", gemini_timer_of_init); | ||