1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Allwinner A1X SoCs timer handling. |
4 | * |
5 | * Copyright (C) 2012 Maxime Ripard |
6 | * |
7 | * Maxime Ripard <maxime.ripard@free-electrons.com> |
8 | * |
9 | * Based on code from |
10 | * Allwinner Technology Co., Ltd. <www.allwinnertech.com> |
11 | * Benn Huang <benn@allwinnertech.com> |
12 | */ |
13 | |
14 | #include <linux/clk.h> |
15 | #include <linux/clockchips.h> |
16 | #include <linux/interrupt.h> |
17 | #include <linux/irq.h> |
18 | #include <linux/irqreturn.h> |
19 | #include <linux/sched_clock.h> |
20 | #include <linux/of.h> |
21 | #include <linux/of_address.h> |
22 | #include <linux/of_irq.h> |
23 | |
24 | #include "timer-of.h" |
25 | |
26 | #define TIMER_IRQ_EN_REG 0x00 |
27 | #define TIMER_IRQ_EN(val) BIT(val) |
28 | #define TIMER_IRQ_ST_REG 0x04 |
29 | #define TIMER_IRQ_CLEAR(val) BIT(val) |
30 | #define TIMER_CTL_REG(val) (0x10 * val + 0x10) |
31 | #define TIMER_CTL_ENABLE BIT(0) |
32 | #define TIMER_CTL_RELOAD BIT(1) |
33 | #define TIMER_CTL_CLK_SRC(val) (((val) & 0x3) << 2) |
34 | #define TIMER_CTL_CLK_SRC_OSC24M (1) |
35 | #define TIMER_CTL_CLK_PRES(val) (((val) & 0x7) << 4) |
36 | #define TIMER_CTL_ONESHOT BIT(7) |
37 | #define TIMER_INTVAL_REG(val) (0x10 * (val) + 0x14) |
38 | #define TIMER_CNTVAL_REG(val) (0x10 * (val) + 0x18) |
39 | |
40 | #define TIMER_SYNC_TICKS 3 |
41 | |
42 | /* |
43 | * When we disable a timer, we need to wait at least for 2 cycles of |
44 | * the timer source clock. We will use for that the clocksource timer |
45 | * that is already setup and runs at the same frequency than the other |
46 | * timers, and we never will be disabled. |
47 | */ |
48 | static void sun4i_clkevt_sync(void __iomem *base) |
49 | { |
50 | u32 old = readl(addr: base + TIMER_CNTVAL_REG(1)); |
51 | |
52 | while ((old - readl(addr: base + TIMER_CNTVAL_REG(1))) < TIMER_SYNC_TICKS) |
53 | cpu_relax(); |
54 | } |
55 | |
56 | static void sun4i_clkevt_time_stop(void __iomem *base, u8 timer) |
57 | { |
58 | u32 val = readl(addr: base + TIMER_CTL_REG(timer)); |
59 | writel(val: val & ~TIMER_CTL_ENABLE, addr: base + TIMER_CTL_REG(timer)); |
60 | sun4i_clkevt_sync(base); |
61 | } |
62 | |
63 | static void sun4i_clkevt_time_setup(void __iomem *base, u8 timer, |
64 | unsigned long delay) |
65 | { |
66 | writel(val: delay, addr: base + TIMER_INTVAL_REG(timer)); |
67 | } |
68 | |
69 | static void sun4i_clkevt_time_start(void __iomem *base, u8 timer, |
70 | bool periodic) |
71 | { |
72 | u32 val = readl(addr: base + TIMER_CTL_REG(timer)); |
73 | |
74 | if (periodic) |
75 | val &= ~TIMER_CTL_ONESHOT; |
76 | else |
77 | val |= TIMER_CTL_ONESHOT; |
78 | |
79 | writel(val: val | TIMER_CTL_ENABLE | TIMER_CTL_RELOAD, |
80 | addr: base + TIMER_CTL_REG(timer)); |
81 | } |
82 | |
83 | static int sun4i_clkevt_shutdown(struct clock_event_device *evt) |
84 | { |
85 | struct timer_of *to = to_timer_of(clkevt: evt); |
86 | |
87 | sun4i_clkevt_time_stop(base: timer_of_base(to), timer: 0); |
88 | |
89 | return 0; |
90 | } |
91 | |
92 | static int sun4i_clkevt_set_oneshot(struct clock_event_device *evt) |
93 | { |
94 | struct timer_of *to = to_timer_of(clkevt: evt); |
95 | |
96 | sun4i_clkevt_time_stop(base: timer_of_base(to), timer: 0); |
97 | sun4i_clkevt_time_start(base: timer_of_base(to), timer: 0, periodic: false); |
98 | |
99 | return 0; |
100 | } |
101 | |
102 | static int sun4i_clkevt_set_periodic(struct clock_event_device *evt) |
103 | { |
104 | struct timer_of *to = to_timer_of(clkevt: evt); |
105 | |
106 | sun4i_clkevt_time_stop(base: timer_of_base(to), timer: 0); |
107 | sun4i_clkevt_time_setup(base: timer_of_base(to), timer: 0, delay: timer_of_period(to)); |
108 | sun4i_clkevt_time_start(base: timer_of_base(to), timer: 0, periodic: true); |
109 | |
110 | return 0; |
111 | } |
112 | |
113 | static int sun4i_clkevt_next_event(unsigned long evt, |
114 | struct clock_event_device *clkevt) |
115 | { |
116 | struct timer_of *to = to_timer_of(clkevt); |
117 | |
118 | sun4i_clkevt_time_stop(base: timer_of_base(to), timer: 0); |
119 | sun4i_clkevt_time_setup(base: timer_of_base(to), timer: 0, delay: evt - TIMER_SYNC_TICKS); |
120 | sun4i_clkevt_time_start(base: timer_of_base(to), timer: 0, periodic: false); |
121 | |
122 | return 0; |
123 | } |
124 | |
125 | static void sun4i_timer_clear_interrupt(void __iomem *base) |
126 | { |
127 | writel(TIMER_IRQ_CLEAR(0), addr: base + TIMER_IRQ_ST_REG); |
128 | } |
129 | |
130 | static irqreturn_t sun4i_timer_interrupt(int irq, void *dev_id) |
131 | { |
132 | struct clock_event_device *evt = dev_id; |
133 | struct timer_of *to = to_timer_of(clkevt: evt); |
134 | |
135 | sun4i_timer_clear_interrupt(base: timer_of_base(to)); |
136 | evt->event_handler(evt); |
137 | |
138 | return IRQ_HANDLED; |
139 | } |
140 | |
141 | static struct timer_of to = { |
142 | .flags = TIMER_OF_IRQ | TIMER_OF_CLOCK | TIMER_OF_BASE, |
143 | |
144 | .clkevt = { |
145 | .name = "sun4i_tick" , |
146 | .rating = 350, |
147 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | |
148 | CLOCK_EVT_FEAT_DYNIRQ, |
149 | .set_state_shutdown = sun4i_clkevt_shutdown, |
150 | .set_state_periodic = sun4i_clkevt_set_periodic, |
151 | .set_state_oneshot = sun4i_clkevt_set_oneshot, |
152 | .tick_resume = sun4i_clkevt_shutdown, |
153 | .set_next_event = sun4i_clkevt_next_event, |
154 | .cpumask = cpu_possible_mask, |
155 | }, |
156 | |
157 | .of_irq = { |
158 | .handler = sun4i_timer_interrupt, |
159 | .flags = IRQF_TIMER | IRQF_IRQPOLL, |
160 | }, |
161 | }; |
162 | |
163 | static u64 notrace sun4i_timer_sched_read(void) |
164 | { |
165 | return ~readl(addr: timer_of_base(to: &to) + TIMER_CNTVAL_REG(1)); |
166 | } |
167 | |
168 | static int __init sun4i_timer_init(struct device_node *node) |
169 | { |
170 | int ret; |
171 | u32 val; |
172 | |
173 | ret = timer_of_init(np: node, to: &to); |
174 | if (ret) |
175 | return ret; |
176 | |
177 | writel(val: ~0, addr: timer_of_base(to: &to) + TIMER_INTVAL_REG(1)); |
178 | writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD | |
179 | TIMER_CTL_CLK_SRC(TIMER_CTL_CLK_SRC_OSC24M), |
180 | addr: timer_of_base(to: &to) + TIMER_CTL_REG(1)); |
181 | |
182 | /* |
183 | * sched_clock_register does not have priorities, and on sun6i and |
184 | * later there is a better sched_clock registered by arm_arch_timer.c |
185 | */ |
186 | if (of_machine_is_compatible(compat: "allwinner,sun4i-a10" ) || |
187 | of_machine_is_compatible(compat: "allwinner,sun5i-a13" ) || |
188 | of_machine_is_compatible(compat: "allwinner,sun5i-a10s" ) || |
189 | of_machine_is_compatible(compat: "allwinner,suniv-f1c100s" )) |
190 | sched_clock_register(read: sun4i_timer_sched_read, bits: 32, |
191 | rate: timer_of_rate(to: &to)); |
192 | |
193 | ret = clocksource_mmio_init(timer_of_base(to: &to) + TIMER_CNTVAL_REG(1), |
194 | node->name, timer_of_rate(to: &to), 350, 32, |
195 | clocksource_mmio_readl_down); |
196 | if (ret) { |
197 | pr_err("Failed to register clocksource\n" ); |
198 | return ret; |
199 | } |
200 | |
201 | writel(TIMER_CTL_CLK_SRC(TIMER_CTL_CLK_SRC_OSC24M), |
202 | addr: timer_of_base(to: &to) + TIMER_CTL_REG(0)); |
203 | |
204 | /* Make sure timer is stopped before playing with interrupts */ |
205 | sun4i_clkevt_time_stop(base: timer_of_base(to: &to), timer: 0); |
206 | |
207 | /* clear timer0 interrupt */ |
208 | sun4i_timer_clear_interrupt(base: timer_of_base(to: &to)); |
209 | |
210 | clockevents_config_and_register(dev: &to.clkevt, freq: timer_of_rate(to: &to), |
211 | TIMER_SYNC_TICKS, max_delta: 0xffffffff); |
212 | |
213 | /* Enable timer0 interrupt */ |
214 | val = readl(addr: timer_of_base(to: &to) + TIMER_IRQ_EN_REG); |
215 | writel(val: val | TIMER_IRQ_EN(0), addr: timer_of_base(to: &to) + TIMER_IRQ_EN_REG); |
216 | |
217 | return ret; |
218 | } |
219 | TIMER_OF_DECLARE(sun4i, "allwinner,sun4i-a10-timer" , |
220 | sun4i_timer_init); |
221 | TIMER_OF_DECLARE(sun8i_a23, "allwinner,sun8i-a23-timer" , |
222 | sun4i_timer_init); |
223 | TIMER_OF_DECLARE(sun8i_v3s, "allwinner,sun8i-v3s-timer" , |
224 | sun4i_timer_init); |
225 | TIMER_OF_DECLARE(suniv, "allwinner,suniv-f1c100s-timer" , |
226 | sun4i_timer_init); |
227 | |