Commit | Line | Data |
---|---|---|
e4a6b378 CC |
1 | /* |
2 | * Amlogic Meson6 SoCs timer handling. | |
3 | * | |
4 | * Copyright (C) 2014 Carlo Caione <carlo@caione.org> | |
5 | * | |
6 | * Based on code from Amlogic, Inc | |
7 | * | |
8 | * This file is licensed under the terms of the GNU General Public | |
9 | * License version 2. This program is licensed "as is" without any | |
10 | * warranty of any kind, whether express or implied. | |
11 | */ | |
12 | ||
bed8fc13 MB |
13 | #include <linux/bitfield.h> |
14 | #include <linux/bitops.h> | |
e4a6b378 CC |
15 | #include <linux/clk.h> |
16 | #include <linux/clockchips.h> | |
17 | #include <linux/interrupt.h> | |
18 | #include <linux/irq.h> | |
19 | #include <linux/irqreturn.h> | |
20 | #include <linux/sched_clock.h> | |
21 | #include <linux/of.h> | |
22 | #include <linux/of_address.h> | |
23 | #include <linux/of_irq.h> | |
24 | ||
fa83c6f4 MB |
25 | #ifdef CONFIG_ARM |
26 | #include <linux/delay.h> | |
27 | #endif | |
28 | ||
bed8fc13 MB |
29 | #define MESON_ISA_TIMER_MUX 0x00 |
30 | #define MESON_ISA_TIMER_MUX_TIMERD_EN BIT(19) | |
31 | #define MESON_ISA_TIMER_MUX_TIMERC_EN BIT(18) | |
32 | #define MESON_ISA_TIMER_MUX_TIMERB_EN BIT(17) | |
33 | #define MESON_ISA_TIMER_MUX_TIMERA_EN BIT(16) | |
34 | #define MESON_ISA_TIMER_MUX_TIMERD_MODE BIT(15) | |
35 | #define MESON_ISA_TIMER_MUX_TIMERC_MODE BIT(14) | |
36 | #define MESON_ISA_TIMER_MUX_TIMERB_MODE BIT(13) | |
37 | #define MESON_ISA_TIMER_MUX_TIMERA_MODE BIT(12) | |
38 | #define MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_MASK GENMASK(10, 8) | |
39 | #define MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_SYSTEM_CLOCK 0x0 | |
40 | #define MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_1US 0x1 | |
41 | #define MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_10US 0x2 | |
42 | #define MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_100US 0x3 | |
43 | #define MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_1MS 0x4 | |
44 | #define MESON_ISA_TIMER_MUX_TIMERD_INPUT_CLOCK_MASK GENMASK(7, 6) | |
45 | #define MESON_ISA_TIMER_MUX_TIMERC_INPUT_CLOCK_MASK GENMASK(5, 4) | |
46 | #define MESON_ISA_TIMER_MUX_TIMERB_INPUT_CLOCK_MASK GENMASK(3, 2) | |
47 | #define MESON_ISA_TIMER_MUX_TIMERA_INPUT_CLOCK_MASK GENMASK(1, 0) | |
48 | #define MESON_ISA_TIMER_MUX_TIMERABCD_INPUT_CLOCK_1US 0x0 | |
49 | #define MESON_ISA_TIMER_MUX_TIMERABCD_INPUT_CLOCK_10US 0x1 | |
50 | #define MESON_ISA_TIMER_MUX_TIMERABCD_INPUT_CLOCK_100US 0x0 | |
51 | #define MESON_ISA_TIMER_MUX_TIMERABCD_INPUT_CLOCK_1MS 0x3 | |
52 | ||
53 | #define MESON_ISA_TIMERA 0x04 | |
54 | #define MESON_ISA_TIMERB 0x08 | |
55 | #define MESON_ISA_TIMERC 0x0c | |
56 | #define MESON_ISA_TIMERD 0x10 | |
57 | #define MESON_ISA_TIMERE 0x14 | |
e4a6b378 CC |
58 | |
59 | static void __iomem *timer_base; | |
60 | ||
fa83c6f4 MB |
61 | #ifdef CONFIG_ARM |
62 | static unsigned long meson6_read_current_timer(void) | |
63 | { | |
64 | return readl_relaxed(timer_base + MESON_ISA_TIMERE); | |
65 | } | |
66 | ||
67 | static struct delay_timer meson6_delay_timer = { | |
68 | .read_current_timer = meson6_read_current_timer, | |
69 | .freq = 1000 * 1000, | |
70 | }; | |
71 | #endif | |
72 | ||
e4a6b378 CC |
73 | static u64 notrace meson6_timer_sched_read(void) |
74 | { | |
bed8fc13 | 75 | return (u64)readl(timer_base + MESON_ISA_TIMERE); |
e4a6b378 CC |
76 | } |
77 | ||
bed8fc13 | 78 | static void meson6_clkevt_time_stop(void) |
e4a6b378 | 79 | { |
bed8fc13 | 80 | u32 val = readl(timer_base + MESON_ISA_TIMER_MUX); |
e4a6b378 | 81 | |
bed8fc13 MB |
82 | writel(val & ~MESON_ISA_TIMER_MUX_TIMERA_EN, |
83 | timer_base + MESON_ISA_TIMER_MUX); | |
e4a6b378 CC |
84 | } |
85 | ||
bed8fc13 | 86 | static void meson6_clkevt_time_setup(unsigned long delay) |
e4a6b378 | 87 | { |
bed8fc13 | 88 | writel(delay, timer_base + MESON_ISA_TIMERA); |
e4a6b378 CC |
89 | } |
90 | ||
bed8fc13 | 91 | static void meson6_clkevt_time_start(bool periodic) |
e4a6b378 | 92 | { |
bed8fc13 | 93 | u32 val = readl(timer_base + MESON_ISA_TIMER_MUX); |
e4a6b378 CC |
94 | |
95 | if (periodic) | |
bed8fc13 | 96 | val |= MESON_ISA_TIMER_MUX_TIMERA_MODE; |
e4a6b378 | 97 | else |
bed8fc13 | 98 | val &= ~MESON_ISA_TIMER_MUX_TIMERA_MODE; |
e4a6b378 | 99 | |
bed8fc13 MB |
100 | writel(val | MESON_ISA_TIMER_MUX_TIMERA_EN, |
101 | timer_base + MESON_ISA_TIMER_MUX); | |
e4a6b378 CC |
102 | } |
103 | ||
40117bd5 | 104 | static int meson6_shutdown(struct clock_event_device *evt) |
e4a6b378 | 105 | { |
bed8fc13 | 106 | meson6_clkevt_time_stop(); |
40117bd5 VK |
107 | return 0; |
108 | } | |
109 | ||
110 | static int meson6_set_oneshot(struct clock_event_device *evt) | |
111 | { | |
bed8fc13 MB |
112 | meson6_clkevt_time_stop(); |
113 | meson6_clkevt_time_start(false); | |
40117bd5 VK |
114 | return 0; |
115 | } | |
116 | ||
117 | static int meson6_set_periodic(struct clock_event_device *evt) | |
118 | { | |
bed8fc13 MB |
119 | meson6_clkevt_time_stop(); |
120 | meson6_clkevt_time_setup(USEC_PER_SEC / HZ - 1); | |
121 | meson6_clkevt_time_start(true); | |
40117bd5 | 122 | return 0; |
e4a6b378 CC |
123 | } |
124 | ||
125 | static int meson6_clkevt_next_event(unsigned long evt, | |
126 | struct clock_event_device *unused) | |
127 | { | |
bed8fc13 MB |
128 | meson6_clkevt_time_stop(); |
129 | meson6_clkevt_time_setup(evt); | |
130 | meson6_clkevt_time_start(false); | |
e4a6b378 CC |
131 | |
132 | return 0; | |
133 | } | |
134 | ||
135 | static struct clock_event_device meson6_clockevent = { | |
40117bd5 VK |
136 | .name = "meson6_tick", |
137 | .rating = 400, | |
138 | .features = CLOCK_EVT_FEAT_PERIODIC | | |
139 | CLOCK_EVT_FEAT_ONESHOT, | |
140 | .set_state_shutdown = meson6_shutdown, | |
141 | .set_state_periodic = meson6_set_periodic, | |
142 | .set_state_oneshot = meson6_set_oneshot, | |
143 | .tick_resume = meson6_shutdown, | |
144 | .set_next_event = meson6_clkevt_next_event, | |
e4a6b378 CC |
145 | }; |
146 | ||
147 | static irqreturn_t meson6_timer_interrupt(int irq, void *dev_id) | |
148 | { | |
149 | struct clock_event_device *evt = (struct clock_event_device *)dev_id; | |
150 | ||
151 | evt->event_handler(evt); | |
152 | ||
153 | return IRQ_HANDLED; | |
154 | } | |
155 | ||
156 | static struct irqaction meson6_timer_irq = { | |
157 | .name = "meson6_timer", | |
158 | .flags = IRQF_TIMER | IRQF_IRQPOLL, | |
159 | .handler = meson6_timer_interrupt, | |
160 | .dev_id = &meson6_clockevent, | |
161 | }; | |
162 | ||
ca46acb9 | 163 | static int __init meson6_timer_init(struct device_node *node) |
e4a6b378 CC |
164 | { |
165 | u32 val; | |
166 | int ret, irq; | |
167 | ||
168 | timer_base = of_io_request_and_map(node, 0, "meson6-timer"); | |
ca46acb9 | 169 | if (IS_ERR(timer_base)) { |
ac9ce6d1 | 170 | pr_err("Can't map registers\n"); |
ca46acb9 DL |
171 | return -ENXIO; |
172 | } | |
e4a6b378 CC |
173 | |
174 | irq = irq_of_parse_and_map(node, 0); | |
ca46acb9 | 175 | if (irq <= 0) { |
ac9ce6d1 | 176 | pr_err("Can't parse IRQ\n"); |
ca46acb9 DL |
177 | return -EINVAL; |
178 | } | |
e4a6b378 CC |
179 | |
180 | /* Set 1us for timer E */ | |
bed8fc13 MB |
181 | val = readl(timer_base + MESON_ISA_TIMER_MUX); |
182 | val &= ~MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_MASK; | |
183 | val |= FIELD_PREP(MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_MASK, | |
184 | MESON_ISA_TIMER_MUX_TIMERE_INPUT_CLOCK_1US); | |
185 | writel(val, timer_base + MESON_ISA_TIMER_MUX); | |
e4a6b378 CC |
186 | |
187 | sched_clock_register(meson6_timer_sched_read, 32, USEC_PER_SEC); | |
bed8fc13 | 188 | clocksource_mmio_init(timer_base + MESON_ISA_TIMERE, node->name, |
e4a6b378 CC |
189 | 1000 * 1000, 300, 32, clocksource_mmio_readl_up); |
190 | ||
191 | /* Timer A base 1us */ | |
bed8fc13 MB |
192 | val &= ~MESON_ISA_TIMER_MUX_TIMERA_INPUT_CLOCK_MASK; |
193 | val |= FIELD_PREP(MESON_ISA_TIMER_MUX_TIMERA_INPUT_CLOCK_MASK, | |
194 | MESON_ISA_TIMER_MUX_TIMERABCD_INPUT_CLOCK_1US); | |
195 | writel(val, timer_base + MESON_ISA_TIMER_MUX); | |
e4a6b378 CC |
196 | |
197 | /* Stop the timer A */ | |
bed8fc13 | 198 | meson6_clkevt_time_stop(); |
e4a6b378 CC |
199 | |
200 | ret = setup_irq(irq, &meson6_timer_irq); | |
ca46acb9 | 201 | if (ret) { |
e4a6b378 | 202 | pr_warn("failed to setup irq %d\n", irq); |
ca46acb9 DL |
203 | return ret; |
204 | } | |
e4a6b378 CC |
205 | |
206 | meson6_clockevent.cpumask = cpu_possible_mask; | |
207 | meson6_clockevent.irq = irq; | |
208 | ||
209 | clockevents_config_and_register(&meson6_clockevent, USEC_PER_SEC, | |
210 | 1, 0xfffe); | |
fa83c6f4 MB |
211 | |
212 | #ifdef CONFIG_ARM | |
213 | /* Also use MESON_ISA_TIMERE for delays */ | |
214 | register_current_timer_delay(&meson6_delay_timer); | |
215 | #endif | |
216 | ||
ca46acb9 | 217 | return 0; |
e4a6b378 | 218 | } |
17273395 | 219 | TIMER_OF_DECLARE(meson6, "amlogic,meson6-timer", |
e4a6b378 | 220 | meson6_timer_init); |