1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2012 Altera Corporation |
4 | * Copyright (c) 2011 Picochip Ltd., Jamie Iles |
5 | * |
6 | * Modified from mach-picoxcell/time.c |
7 | */ |
8 | #include <linux/delay.h> |
9 | #include <linux/dw_apb_timer.h> |
10 | #include <linux/of.h> |
11 | #include <linux/of_address.h> |
12 | #include <linux/of_irq.h> |
13 | #include <linux/clk.h> |
14 | #include <linux/reset.h> |
15 | #include <linux/sched_clock.h> |
16 | |
17 | static int __init timer_get_base_and_rate(struct device_node *np, |
18 | void __iomem **base, u32 *rate) |
19 | { |
20 | struct clk *timer_clk; |
21 | struct clk *pclk; |
22 | struct reset_control *rstc; |
23 | int ret; |
24 | |
25 | *base = of_iomap(node: np, index: 0); |
26 | |
27 | if (!*base) |
28 | panic(fmt: "Unable to map regs for %pOFn" , np); |
29 | |
30 | /* |
31 | * Reset the timer if the reset control is available, wiping |
32 | * out the state the firmware may have left it |
33 | */ |
34 | rstc = of_reset_control_get(node: np, NULL); |
35 | if (!IS_ERR(ptr: rstc)) { |
36 | reset_control_assert(rstc); |
37 | reset_control_deassert(rstc); |
38 | } |
39 | |
40 | /* |
41 | * Not all implementations use a peripheral clock, so don't panic |
42 | * if it's not present |
43 | */ |
44 | pclk = of_clk_get_by_name(np, name: "pclk" ); |
45 | if (!IS_ERR(ptr: pclk)) |
46 | if (clk_prepare_enable(clk: pclk)) |
47 | pr_warn("pclk for %pOFn is present, but could not be activated\n" , |
48 | np); |
49 | |
50 | if (!of_property_read_u32(np, propname: "clock-freq" , out_value: rate) || |
51 | !of_property_read_u32(np, propname: "clock-frequency" , out_value: rate)) |
52 | return 0; |
53 | |
54 | timer_clk = of_clk_get_by_name(np, name: "timer" ); |
55 | if (IS_ERR(ptr: timer_clk)) { |
56 | ret = PTR_ERR(ptr: timer_clk); |
57 | goto out_pclk_disable; |
58 | } |
59 | |
60 | ret = clk_prepare_enable(clk: timer_clk); |
61 | if (ret) |
62 | goto out_timer_clk_put; |
63 | |
64 | *rate = clk_get_rate(clk: timer_clk); |
65 | if (!(*rate)) { |
66 | ret = -EINVAL; |
67 | goto out_timer_clk_disable; |
68 | } |
69 | |
70 | return 0; |
71 | |
72 | out_timer_clk_disable: |
73 | clk_disable_unprepare(clk: timer_clk); |
74 | out_timer_clk_put: |
75 | clk_put(clk: timer_clk); |
76 | out_pclk_disable: |
77 | if (!IS_ERR(ptr: pclk)) { |
78 | clk_disable_unprepare(clk: pclk); |
79 | clk_put(clk: pclk); |
80 | } |
81 | iounmap(addr: *base); |
82 | return ret; |
83 | } |
84 | |
85 | static int __init add_clockevent(struct device_node *event_timer) |
86 | { |
87 | void __iomem *iobase; |
88 | struct dw_apb_clock_event_device *ced; |
89 | u32 irq, rate; |
90 | int ret = 0; |
91 | |
92 | irq = irq_of_parse_and_map(node: event_timer, index: 0); |
93 | if (irq == 0) |
94 | panic(fmt: "No IRQ for clock event timer" ); |
95 | |
96 | ret = timer_get_base_and_rate(np: event_timer, base: &iobase, rate: &rate); |
97 | if (ret) |
98 | return ret; |
99 | |
100 | ced = dw_apb_clockevent_init(cpu: -1, name: event_timer->name, rating: 300, base: iobase, irq, |
101 | freq: rate); |
102 | if (!ced) |
103 | return -EINVAL; |
104 | |
105 | dw_apb_clockevent_register(dw_ced: ced); |
106 | |
107 | return 0; |
108 | } |
109 | |
110 | static void __iomem *sched_io_base; |
111 | static u32 sched_rate; |
112 | |
113 | static int __init add_clocksource(struct device_node *source_timer) |
114 | { |
115 | void __iomem *iobase; |
116 | struct dw_apb_clocksource *cs; |
117 | u32 rate; |
118 | int ret; |
119 | |
120 | ret = timer_get_base_and_rate(np: source_timer, base: &iobase, rate: &rate); |
121 | if (ret) |
122 | return ret; |
123 | |
124 | cs = dw_apb_clocksource_init(rating: 300, name: source_timer->name, base: iobase, freq: rate); |
125 | if (!cs) |
126 | return -EINVAL; |
127 | |
128 | dw_apb_clocksource_start(dw_cs: cs); |
129 | dw_apb_clocksource_register(dw_cs: cs); |
130 | |
131 | /* |
132 | * Fallback to use the clocksource as sched_clock if no separate |
133 | * timer is found. sched_io_base then points to the current_value |
134 | * register of the clocksource timer. |
135 | */ |
136 | sched_io_base = iobase + 0x04; |
137 | sched_rate = rate; |
138 | |
139 | return 0; |
140 | } |
141 | |
142 | static u64 notrace read_sched_clock(void) |
143 | { |
144 | return ~readl_relaxed(sched_io_base); |
145 | } |
146 | |
147 | static const struct of_device_id sptimer_ids[] __initconst = { |
148 | { .compatible = "picochip,pc3x2-rtc" }, |
149 | { /* Sentinel */ }, |
150 | }; |
151 | |
152 | static void __init init_sched_clock(void) |
153 | { |
154 | struct device_node *sched_timer; |
155 | |
156 | sched_timer = of_find_matching_node(NULL, matches: sptimer_ids); |
157 | if (sched_timer) { |
158 | timer_get_base_and_rate(np: sched_timer, base: &sched_io_base, |
159 | rate: &sched_rate); |
160 | of_node_put(node: sched_timer); |
161 | } |
162 | |
163 | sched_clock_register(read: read_sched_clock, bits: 32, rate: sched_rate); |
164 | } |
165 | |
166 | #ifdef CONFIG_ARM |
167 | static unsigned long dw_apb_delay_timer_read(void) |
168 | { |
169 | return ~readl_relaxed(sched_io_base); |
170 | } |
171 | |
172 | static struct delay_timer dw_apb_delay_timer = { |
173 | .read_current_timer = dw_apb_delay_timer_read, |
174 | }; |
175 | #endif |
176 | |
177 | static int num_called; |
178 | static int __init dw_apb_timer_init(struct device_node *timer) |
179 | { |
180 | int ret = 0; |
181 | |
182 | switch (num_called) { |
183 | case 1: |
184 | pr_debug("%s: found clocksource timer\n" , __func__); |
185 | ret = add_clocksource(source_timer: timer); |
186 | if (ret) |
187 | return ret; |
188 | init_sched_clock(); |
189 | #ifdef CONFIG_ARM |
190 | dw_apb_delay_timer.freq = sched_rate; |
191 | register_current_timer_delay(&dw_apb_delay_timer); |
192 | #endif |
193 | break; |
194 | default: |
195 | pr_debug("%s: found clockevent timer\n" , __func__); |
196 | ret = add_clockevent(event_timer: timer); |
197 | if (ret) |
198 | return ret; |
199 | break; |
200 | } |
201 | |
202 | num_called++; |
203 | |
204 | return 0; |
205 | } |
206 | TIMER_OF_DECLARE(pc3x2_timer, "picochip,pc3x2-timer" , dw_apb_timer_init); |
207 | TIMER_OF_DECLARE(apb_timer_osc, "snps,dw-apb-timer-osc" , dw_apb_timer_init); |
208 | TIMER_OF_DECLARE(apb_timer_sp, "snps,dw-apb-timer-sp" , dw_apb_timer_init); |
209 | TIMER_OF_DECLARE(apb_timer, "snps,dw-apb-timer" , dw_apb_timer_init); |
210 | |