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