1a7ad38b0SGuo Ren // SPDX-License-Identifier: GPL-2.0
2a7ad38b0SGuo Ren // Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
3a7ad38b0SGuo Ren
4a7ad38b0SGuo Ren #include <linux/init.h>
5a7ad38b0SGuo Ren #include <linux/interrupt.h>
6a7ad38b0SGuo Ren #include <linux/sched_clock.h>
7a7ad38b0SGuo Ren #include <linux/cpu.h>
8a7ad38b0SGuo Ren #include <linux/of_irq.h>
9a7ad38b0SGuo Ren #include <asm/reg_ops.h>
10a7ad38b0SGuo Ren
11a7ad38b0SGuo Ren #include "timer-of.h"
12a7ad38b0SGuo Ren
13a7ad38b0SGuo Ren #define PTIM_CCVR "cr<3, 14>"
14a7ad38b0SGuo Ren #define PTIM_CTLR "cr<0, 14>"
15a7ad38b0SGuo Ren #define PTIM_LVR "cr<6, 14>"
16a7ad38b0SGuo Ren #define PTIM_TSR "cr<1, 14>"
17a7ad38b0SGuo Ren
18a7ad38b0SGuo Ren static int csky_mptimer_irq;
19a7ad38b0SGuo Ren
csky_mptimer_set_next_event(unsigned long delta,struct clock_event_device * ce)20a7ad38b0SGuo Ren static int csky_mptimer_set_next_event(unsigned long delta,
21a7ad38b0SGuo Ren struct clock_event_device *ce)
22a7ad38b0SGuo Ren {
23a7ad38b0SGuo Ren mtcr(PTIM_LVR, delta);
24a7ad38b0SGuo Ren
25a7ad38b0SGuo Ren return 0;
26a7ad38b0SGuo Ren }
27a7ad38b0SGuo Ren
csky_mptimer_shutdown(struct clock_event_device * ce)28a7ad38b0SGuo Ren static int csky_mptimer_shutdown(struct clock_event_device *ce)
29a7ad38b0SGuo Ren {
30a7ad38b0SGuo Ren mtcr(PTIM_CTLR, 0);
31a7ad38b0SGuo Ren
32a7ad38b0SGuo Ren return 0;
33a7ad38b0SGuo Ren }
34a7ad38b0SGuo Ren
csky_mptimer_oneshot(struct clock_event_device * ce)35a7ad38b0SGuo Ren static int csky_mptimer_oneshot(struct clock_event_device *ce)
36a7ad38b0SGuo Ren {
37a7ad38b0SGuo Ren mtcr(PTIM_CTLR, 1);
38a7ad38b0SGuo Ren
39a7ad38b0SGuo Ren return 0;
40a7ad38b0SGuo Ren }
41a7ad38b0SGuo Ren
csky_mptimer_oneshot_stopped(struct clock_event_device * ce)42a7ad38b0SGuo Ren static int csky_mptimer_oneshot_stopped(struct clock_event_device *ce)
43a7ad38b0SGuo Ren {
44a7ad38b0SGuo Ren mtcr(PTIM_CTLR, 0);
45a7ad38b0SGuo Ren
46a7ad38b0SGuo Ren return 0;
47a7ad38b0SGuo Ren }
48a7ad38b0SGuo Ren
49a7ad38b0SGuo Ren static DEFINE_PER_CPU(struct timer_of, csky_to) = {
50a7ad38b0SGuo Ren .flags = TIMER_OF_CLOCK,
51a7ad38b0SGuo Ren .clkevt = {
52a7ad38b0SGuo Ren .rating = 300,
53a7ad38b0SGuo Ren .features = CLOCK_EVT_FEAT_PERCPU |
54a7ad38b0SGuo Ren CLOCK_EVT_FEAT_ONESHOT,
55a7ad38b0SGuo Ren .set_state_shutdown = csky_mptimer_shutdown,
56a7ad38b0SGuo Ren .set_state_oneshot = csky_mptimer_oneshot,
57a7ad38b0SGuo Ren .set_state_oneshot_stopped = csky_mptimer_oneshot_stopped,
58a7ad38b0SGuo Ren .set_next_event = csky_mptimer_set_next_event,
59a7ad38b0SGuo Ren },
60a7ad38b0SGuo Ren };
61a7ad38b0SGuo Ren
csky_timer_interrupt(int irq,void * dev)62a7ad38b0SGuo Ren static irqreturn_t csky_timer_interrupt(int irq, void *dev)
63a7ad38b0SGuo Ren {
64a7ad38b0SGuo Ren struct timer_of *to = this_cpu_ptr(&csky_to);
65a7ad38b0SGuo Ren
66a7ad38b0SGuo Ren mtcr(PTIM_TSR, 0);
67a7ad38b0SGuo Ren
68a7ad38b0SGuo Ren to->clkevt.event_handler(&to->clkevt);
69a7ad38b0SGuo Ren
70a7ad38b0SGuo Ren return IRQ_HANDLED;
71a7ad38b0SGuo Ren }
72a7ad38b0SGuo Ren
73a7ad38b0SGuo Ren /*
74a7ad38b0SGuo Ren * clock event for percpu
75a7ad38b0SGuo Ren */
csky_mptimer_starting_cpu(unsigned int cpu)76a7ad38b0SGuo Ren static int csky_mptimer_starting_cpu(unsigned int cpu)
77a7ad38b0SGuo Ren {
78a7ad38b0SGuo Ren struct timer_of *to = per_cpu_ptr(&csky_to, cpu);
79a7ad38b0SGuo Ren
80a7ad38b0SGuo Ren to->clkevt.cpumask = cpumask_of(cpu);
81a7ad38b0SGuo Ren
821d95fe4dSGuo Ren enable_percpu_irq(csky_mptimer_irq, 0);
831d95fe4dSGuo Ren
84a7ad38b0SGuo Ren clockevents_config_and_register(&to->clkevt, timer_of_rate(to),
85a7ad38b0SGuo Ren 2, ULONG_MAX);
86a7ad38b0SGuo Ren
87a7ad38b0SGuo Ren return 0;
88a7ad38b0SGuo Ren }
89a7ad38b0SGuo Ren
csky_mptimer_dying_cpu(unsigned int cpu)90a7ad38b0SGuo Ren static int csky_mptimer_dying_cpu(unsigned int cpu)
91a7ad38b0SGuo Ren {
92a7ad38b0SGuo Ren disable_percpu_irq(csky_mptimer_irq);
93a7ad38b0SGuo Ren
94a7ad38b0SGuo Ren return 0;
95a7ad38b0SGuo Ren }
96a7ad38b0SGuo Ren
97a7ad38b0SGuo Ren /*
98a7ad38b0SGuo Ren * clock source
99a7ad38b0SGuo Ren */
sched_clock_read(void)100*0c87bb0eSGuo Ren static u64 notrace sched_clock_read(void)
101a7ad38b0SGuo Ren {
102a7ad38b0SGuo Ren return (u64)mfcr(PTIM_CCVR);
103a7ad38b0SGuo Ren }
104a7ad38b0SGuo Ren
clksrc_read(struct clocksource * c)105a7ad38b0SGuo Ren static u64 clksrc_read(struct clocksource *c)
106a7ad38b0SGuo Ren {
107a7ad38b0SGuo Ren return (u64)mfcr(PTIM_CCVR);
108a7ad38b0SGuo Ren }
109a7ad38b0SGuo Ren
110a7ad38b0SGuo Ren struct clocksource csky_clocksource = {
111a7ad38b0SGuo Ren .name = "csky",
112a7ad38b0SGuo Ren .rating = 400,
113a7ad38b0SGuo Ren .mask = CLOCKSOURCE_MASK(32),
114a7ad38b0SGuo Ren .flags = CLOCK_SOURCE_IS_CONTINUOUS,
115a7ad38b0SGuo Ren .read = clksrc_read,
116a7ad38b0SGuo Ren };
117a7ad38b0SGuo Ren
csky_mptimer_init(struct device_node * np)118a7ad38b0SGuo Ren static int __init csky_mptimer_init(struct device_node *np)
119a7ad38b0SGuo Ren {
120a7ad38b0SGuo Ren int ret, cpu, cpu_rollback;
121a7ad38b0SGuo Ren struct timer_of *to = NULL;
122a7ad38b0SGuo Ren
123a7ad38b0SGuo Ren /*
124a7ad38b0SGuo Ren * Csky_mptimer is designed for C-SKY SMP multi-processors and
125a7ad38b0SGuo Ren * every core has it's own private irq and regs for clkevt and
126a7ad38b0SGuo Ren * clksrc.
127a7ad38b0SGuo Ren *
128a7ad38b0SGuo Ren * The regs is accessed by cpu instruction: mfcr/mtcr instead of
129a7ad38b0SGuo Ren * mmio map style. So we needn't mmio-address in dts, but we still
130a7ad38b0SGuo Ren * need to give clk and irq number.
131a7ad38b0SGuo Ren *
132a7ad38b0SGuo Ren * We use private irq for the mptimer and irq number is the same
133a7ad38b0SGuo Ren * for every core. So we use request_percpu_irq() in timer_of_init.
134a7ad38b0SGuo Ren */
135a7ad38b0SGuo Ren csky_mptimer_irq = irq_of_parse_and_map(np, 0);
136a7ad38b0SGuo Ren if (csky_mptimer_irq <= 0)
137a7ad38b0SGuo Ren return -EINVAL;
138a7ad38b0SGuo Ren
139a7ad38b0SGuo Ren ret = request_percpu_irq(csky_mptimer_irq, csky_timer_interrupt,
140a7ad38b0SGuo Ren "csky_mp_timer", &csky_to);
141a7ad38b0SGuo Ren if (ret)
142a7ad38b0SGuo Ren return -EINVAL;
143a7ad38b0SGuo Ren
144a7ad38b0SGuo Ren for_each_possible_cpu(cpu) {
145a7ad38b0SGuo Ren to = per_cpu_ptr(&csky_to, cpu);
146a7ad38b0SGuo Ren ret = timer_of_init(np, to);
147a7ad38b0SGuo Ren if (ret)
148a7ad38b0SGuo Ren goto rollback;
149a7ad38b0SGuo Ren }
150a7ad38b0SGuo Ren
151a7ad38b0SGuo Ren clocksource_register_hz(&csky_clocksource, timer_of_rate(to));
152a7ad38b0SGuo Ren sched_clock_register(sched_clock_read, 32, timer_of_rate(to));
153a7ad38b0SGuo Ren
154a7ad38b0SGuo Ren ret = cpuhp_setup_state(CPUHP_AP_CSKY_TIMER_STARTING,
155a7ad38b0SGuo Ren "clockevents/csky/timer:starting",
156a7ad38b0SGuo Ren csky_mptimer_starting_cpu,
157a7ad38b0SGuo Ren csky_mptimer_dying_cpu);
158a7ad38b0SGuo Ren if (ret)
159a7ad38b0SGuo Ren return -EINVAL;
160a7ad38b0SGuo Ren
161a7ad38b0SGuo Ren return 0;
162a7ad38b0SGuo Ren
163a7ad38b0SGuo Ren rollback:
164a7ad38b0SGuo Ren for_each_possible_cpu(cpu_rollback) {
165a7ad38b0SGuo Ren if (cpu_rollback == cpu)
166a7ad38b0SGuo Ren break;
167a7ad38b0SGuo Ren
168a7ad38b0SGuo Ren to = per_cpu_ptr(&csky_to, cpu_rollback);
169a7ad38b0SGuo Ren timer_of_cleanup(to);
170a7ad38b0SGuo Ren }
171a7ad38b0SGuo Ren return -EINVAL;
172a7ad38b0SGuo Ren }
173a7ad38b0SGuo Ren TIMER_OF_DECLARE(csky_mptimer, "csky,mptimer", csky_mptimer_init);
174