riscv: Fix Kendryte K210 device tree
[linux-block.git] / drivers / clocksource / timer-clint.c
CommitLineData
2ac6795f
AP
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2020 Western Digital Corporation or its affiliates.
4 *
5 * Most of the M-mode (i.e. NoMMU) RISC-V systems usually have a
6 * CLINT MMIO timer device.
7 */
8
9#define pr_fmt(fmt) "clint: " fmt
10#include <linux/bitops.h>
11#include <linux/clocksource.h>
12#include <linux/clockchips.h>
13#include <linux/cpu.h>
14#include <linux/delay.h>
15#include <linux/module.h>
16#include <linux/of_address.h>
17#include <linux/sched_clock.h>
18#include <linux/io-64-nonatomic-lo-hi.h>
19#include <linux/interrupt.h>
20#include <linux/of_irq.h>
21#include <linux/smp.h>
22
23#define CLINT_IPI_OFF 0
24#define CLINT_TIMER_CMP_OFF 0x4000
25#define CLINT_TIMER_VAL_OFF 0xbff8
26
27/* CLINT manages IPI and Timer for RISC-V M-mode */
28static u32 __iomem *clint_ipi_base;
29static u64 __iomem *clint_timer_cmp;
30static u64 __iomem *clint_timer_val;
31static unsigned long clint_timer_freq;
32static unsigned int clint_timer_irq;
33
34static void clint_send_ipi(const struct cpumask *target)
35{
36 unsigned int cpu;
37
38 for_each_cpu(cpu, target)
39 writel(1, clint_ipi_base + cpuid_to_hartid_map(cpu));
40}
41
42static void clint_clear_ipi(void)
43{
44 writel(0, clint_ipi_base + cpuid_to_hartid_map(smp_processor_id()));
45}
46
47static struct riscv_ipi_ops clint_ipi_ops = {
48 .ipi_inject = clint_send_ipi,
49 .ipi_clear = clint_clear_ipi,
50};
51
52#ifdef CONFIG_64BIT
53#define clint_get_cycles() readq_relaxed(clint_timer_val)
54#else
55#define clint_get_cycles() readl_relaxed(clint_timer_val)
56#define clint_get_cycles_hi() readl_relaxed(((u32 *)clint_timer_val) + 1)
57#endif
58
59#ifdef CONFIG_64BIT
60static u64 notrace clint_get_cycles64(void)
61{
62 return clint_get_cycles();
63}
64#else /* CONFIG_64BIT */
65static u64 notrace clint_get_cycles64(void)
66{
67 u32 hi, lo;
68
69 do {
70 hi = clint_get_cycles_hi();
71 lo = clint_get_cycles();
72 } while (hi != clint_get_cycles_hi());
73
74 return ((u64)hi << 32) | lo;
75}
76#endif /* CONFIG_64BIT */
77
78static u64 clint_rdtime(struct clocksource *cs)
79{
80 return clint_get_cycles64();
81}
82
83static struct clocksource clint_clocksource = {
84 .name = "clint_clocksource",
85 .rating = 300,
86 .mask = CLOCKSOURCE_MASK(64),
87 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
88 .read = clint_rdtime,
89};
90
91static int clint_clock_next_event(unsigned long delta,
92 struct clock_event_device *ce)
93{
94 void __iomem *r = clint_timer_cmp +
95 cpuid_to_hartid_map(smp_processor_id());
96
97 csr_set(CSR_IE, IE_TIE);
98 writeq_relaxed(clint_get_cycles64() + delta, r);
99 return 0;
100}
101
102static DEFINE_PER_CPU(struct clock_event_device, clint_clock_event) = {
103 .name = "clint_clockevent",
104 .features = CLOCK_EVT_FEAT_ONESHOT,
105 .rating = 100,
106 .set_next_event = clint_clock_next_event,
107};
108
109static int clint_timer_starting_cpu(unsigned int cpu)
110{
111 struct clock_event_device *ce = per_cpu_ptr(&clint_clock_event, cpu);
112
113 ce->cpumask = cpumask_of(cpu);
114 clockevents_config_and_register(ce, clint_timer_freq, 100, 0x7fffffff);
115
116 enable_percpu_irq(clint_timer_irq,
117 irq_get_trigger_type(clint_timer_irq));
118 return 0;
119}
120
121static int clint_timer_dying_cpu(unsigned int cpu)
122{
123 disable_percpu_irq(clint_timer_irq);
124 return 0;
125}
126
127static irqreturn_t clint_timer_interrupt(int irq, void *dev_id)
128{
129 struct clock_event_device *evdev = this_cpu_ptr(&clint_clock_event);
130
131 csr_clear(CSR_IE, IE_TIE);
132 evdev->event_handler(evdev);
133
134 return IRQ_HANDLED;
135}
136
137static int __init clint_timer_init_dt(struct device_node *np)
138{
139 int rc;
140 u32 i, nr_irqs;
141 void __iomem *base;
142 struct of_phandle_args oirq;
143
144 /*
145 * Ensure that CLINT device interrupts are either RV_IRQ_TIMER or
146 * RV_IRQ_SOFT. If it's anything else then we ignore the device.
147 */
148 nr_irqs = of_irq_count(np);
149 for (i = 0; i < nr_irqs; i++) {
150 if (of_irq_parse_one(np, i, &oirq)) {
151 pr_err("%pOFP: failed to parse irq %d.\n", np, i);
152 continue;
153 }
154
155 if ((oirq.args_count != 1) ||
156 (oirq.args[0] != RV_IRQ_TIMER &&
157 oirq.args[0] != RV_IRQ_SOFT)) {
158 pr_err("%pOFP: invalid irq %d (hwirq %d)\n",
159 np, i, oirq.args[0]);
160 return -ENODEV;
161 }
162
163 /* Find parent irq domain and map timer irq */
164 if (!clint_timer_irq &&
165 oirq.args[0] == RV_IRQ_TIMER &&
166 irq_find_host(oirq.np))
167 clint_timer_irq = irq_of_parse_and_map(np, i);
168 }
169
170 /* If CLINT timer irq not found then fail */
171 if (!clint_timer_irq) {
172 pr_err("%pOFP: timer irq not found\n", np);
173 return -ENODEV;
174 }
175
176 base = of_iomap(np, 0);
177 if (!base) {
178 pr_err("%pOFP: could not map registers\n", np);
179 return -ENODEV;
180 }
181
182 clint_ipi_base = base + CLINT_IPI_OFF;
183 clint_timer_cmp = base + CLINT_TIMER_CMP_OFF;
184 clint_timer_val = base + CLINT_TIMER_VAL_OFF;
185 clint_timer_freq = riscv_timebase;
186
187 pr_info("%pOFP: timer running at %ld Hz\n", np, clint_timer_freq);
188
189 rc = clocksource_register_hz(&clint_clocksource, clint_timer_freq);
190 if (rc) {
191 pr_err("%pOFP: clocksource register failed [%d]\n", np, rc);
192 goto fail_iounmap;
193 }
194
195 sched_clock_register(clint_get_cycles64, 64, clint_timer_freq);
196
197 rc = request_percpu_irq(clint_timer_irq, clint_timer_interrupt,
198 "clint-timer", &clint_clock_event);
199 if (rc) {
200 pr_err("registering percpu irq failed [%d]\n", rc);
201 goto fail_iounmap;
202 }
203
204 rc = cpuhp_setup_state(CPUHP_AP_CLINT_TIMER_STARTING,
205 "clockevents/clint/timer:starting",
206 clint_timer_starting_cpu,
207 clint_timer_dying_cpu);
208 if (rc) {
209 pr_err("%pOFP: cpuhp setup state failed [%d]\n", np, rc);
210 goto fail_free_irq;
211 }
212
213 riscv_set_ipi_ops(&clint_ipi_ops);
214 clint_clear_ipi();
215
216 return 0;
217
218fail_free_irq:
219 free_irq(clint_timer_irq, &clint_clock_event);
220fail_iounmap:
221 iounmap(base);
222 return rc;
223}
224
225TIMER_OF_DECLARE(clint_timer, "riscv,clint0", clint_timer_init_dt);
226TIMER_OF_DECLARE(clint_timer1, "sifive,clint0", clint_timer_init_dt);