1 | // SPDX-License-Identifier: GPL-2.0 |
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 |
8 | */ |
9 | |
10 | #include <linux/bitfield.h> |
11 | #include <linux/bitops.h> |
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 | |
22 | #ifdef CONFIG_ARM |
23 | #include <linux/delay.h> |
24 | #endif |
25 | |
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 |
55 | |
56 | static void __iomem *timer_base; |
57 | |
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 | |
70 | static u64 notrace meson6_timer_sched_read(void) |
71 | { |
72 | return (u64)readl(addr: timer_base + MESON_ISA_TIMERE); |
73 | } |
74 | |
75 | static void meson6_clkevt_time_stop(void) |
76 | { |
77 | u32 val = readl(addr: timer_base + MESON_ISA_TIMER_MUX); |
78 | |
79 | writel(val: val & ~MESON_ISA_TIMER_MUX_TIMERA_EN, |
80 | addr: timer_base + MESON_ISA_TIMER_MUX); |
81 | } |
82 | |
83 | static void meson6_clkevt_time_setup(unsigned long delay) |
84 | { |
85 | writel(val: delay, addr: timer_base + MESON_ISA_TIMERA); |
86 | } |
87 | |
88 | static void meson6_clkevt_time_start(bool periodic) |
89 | { |
90 | u32 val = readl(addr: timer_base + MESON_ISA_TIMER_MUX); |
91 | |
92 | if (periodic) |
93 | val |= MESON_ISA_TIMER_MUX_TIMERA_MODE; |
94 | else |
95 | val &= ~MESON_ISA_TIMER_MUX_TIMERA_MODE; |
96 | |
97 | writel(val: val | MESON_ISA_TIMER_MUX_TIMERA_EN, |
98 | addr: timer_base + MESON_ISA_TIMER_MUX); |
99 | } |
100 | |
101 | static int meson6_shutdown(struct clock_event_device *evt) |
102 | { |
103 | meson6_clkevt_time_stop(); |
104 | return 0; |
105 | } |
106 | |
107 | static int meson6_set_oneshot(struct clock_event_device *evt) |
108 | { |
109 | meson6_clkevt_time_stop(); |
110 | meson6_clkevt_time_start(periodic: false); |
111 | return 0; |
112 | } |
113 | |
114 | static int meson6_set_periodic(struct clock_event_device *evt) |
115 | { |
116 | meson6_clkevt_time_stop(); |
117 | meson6_clkevt_time_setup(USEC_PER_SEC / HZ - 1); |
118 | meson6_clkevt_time_start(periodic: true); |
119 | return 0; |
120 | } |
121 | |
122 | static int meson6_clkevt_next_event(unsigned long evt, |
123 | struct clock_event_device *unused) |
124 | { |
125 | meson6_clkevt_time_stop(); |
126 | meson6_clkevt_time_setup(delay: evt); |
127 | meson6_clkevt_time_start(periodic: false); |
128 | |
129 | return 0; |
130 | } |
131 | |
132 | static struct clock_event_device meson6_clockevent = { |
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, |
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 | |
153 | static int __init meson6_timer_init(struct device_node *node) |
154 | { |
155 | u32 val; |
156 | int ret, irq; |
157 | |
158 | timer_base = of_io_request_and_map(device: node, index: 0, name: "meson6-timer" ); |
159 | if (IS_ERR(ptr: timer_base)) { |
160 | pr_err("Can't map registers\n" ); |
161 | return -ENXIO; |
162 | } |
163 | |
164 | irq = irq_of_parse_and_map(node, index: 0); |
165 | if (irq <= 0) { |
166 | pr_err("Can't parse IRQ\n" ); |
167 | return -EINVAL; |
168 | } |
169 | |
170 | /* Set 1us for timer E */ |
171 | val = readl(addr: 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, addr: timer_base + MESON_ISA_TIMER_MUX); |
176 | |
177 | sched_clock_register(read: meson6_timer_sched_read, bits: 32, USEC_PER_SEC); |
178 | clocksource_mmio_init(timer_base + MESON_ISA_TIMERE, node->name, |
179 | 1000 * 1000, 300, 32, clocksource_mmio_readl_up); |
180 | |
181 | /* Timer A base 1us */ |
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, addr: timer_base + MESON_ISA_TIMER_MUX); |
186 | |
187 | /* Stop the timer A */ |
188 | meson6_clkevt_time_stop(); |
189 | |
190 | ret = request_irq(irq, handler: meson6_timer_interrupt, |
191 | IRQF_TIMER | IRQF_IRQPOLL, name: "meson6_timer" , |
192 | dev: &meson6_clockevent); |
193 | if (ret) { |
194 | pr_warn("failed to setup irq %d\n" , irq); |
195 | return ret; |
196 | } |
197 | |
198 | meson6_clockevent.cpumask = cpu_possible_mask; |
199 | meson6_clockevent.irq = irq; |
200 | |
201 | clockevents_config_and_register(dev: &meson6_clockevent, USEC_PER_SEC, |
202 | min_delta: 1, max_delta: 0xfffe); |
203 | |
204 | #ifdef CONFIG_ARM |
205 | /* Also use MESON_ISA_TIMERE for delays */ |
206 | register_current_timer_delay(&meson6_delay_timer); |
207 | #endif |
208 | |
209 | return 0; |
210 | } |
211 | TIMER_OF_DECLARE(meson6, "amlogic,meson6-timer" , |
212 | meson6_timer_init); |
213 | |