aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/clocksource/Kconfig10
-rw-r--r--drivers/clocksource/Makefile1
-rw-r--r--drivers/clocksource/timer-mp-csky.c173
-rw-r--r--include/linux/cpuhotplug.h1
4 files changed, 185 insertions, 0 deletions
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index a11f4ba98b05..591c9a8649a5 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -620,4 +620,14 @@ config RISCV_TIMER
620 is accessed via both the SBI and the rdcycle instruction. This is 620 is accessed via both the SBI and the rdcycle instruction. This is
621 required for all RISC-V systems. 621 required for all RISC-V systems.
622 622
623config CSKY_MP_TIMER
624 bool "SMP Timer for the C-SKY platform" if COMPILE_TEST
625 depends on CSKY
626 select TIMER_OF
627 help
628 Say yes here to enable C-SKY SMP timer driver used for C-SKY SMP
629 system.
630 csky,mptimer is not only used in SMP system, it also could be used
631 single core system. It's not a mmio reg and it use mtcr/mfcr instruction.
632
623endmenu 633endmenu
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index db51b2427e8a..5ce82d39cda7 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -79,3 +79,4 @@ obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o
79obj-$(CONFIG_X86_NUMACHIP) += numachip.o 79obj-$(CONFIG_X86_NUMACHIP) += numachip.o
80obj-$(CONFIG_ATCPIT100_TIMER) += timer-atcpit100.o 80obj-$(CONFIG_ATCPIT100_TIMER) += timer-atcpit100.o
81obj-$(CONFIG_RISCV_TIMER) += riscv_timer.o 81obj-$(CONFIG_RISCV_TIMER) += riscv_timer.o
82obj-$(CONFIG_CSKY_MP_TIMER) += timer-mp-csky.o
diff --git a/drivers/clocksource/timer-mp-csky.c b/drivers/clocksource/timer-mp-csky.c
new file mode 100644
index 000000000000..a8acc431a774
--- /dev/null
+++ b/drivers/clocksource/timer-mp-csky.c
@@ -0,0 +1,173 @@
1// SPDX-License-Identifier: GPL-2.0
2// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
3
4#include <linux/init.h>
5#include <linux/interrupt.h>
6#include <linux/sched_clock.h>
7#include <linux/cpu.h>
8#include <linux/of_irq.h>
9#include <asm/reg_ops.h>
10
11#include "timer-of.h"
12
13#define PTIM_CCVR "cr<3, 14>"
14#define PTIM_CTLR "cr<0, 14>"
15#define PTIM_LVR "cr<6, 14>"
16#define PTIM_TSR "cr<1, 14>"
17
18static int csky_mptimer_irq;
19
20static int csky_mptimer_set_next_event(unsigned long delta,
21 struct clock_event_device *ce)
22{
23 mtcr(PTIM_LVR, delta);
24
25 return 0;
26}
27
28static int csky_mptimer_shutdown(struct clock_event_device *ce)
29{
30 mtcr(PTIM_CTLR, 0);
31
32 return 0;
33}
34
35static int csky_mptimer_oneshot(struct clock_event_device *ce)
36{
37 mtcr(PTIM_CTLR, 1);
38
39 return 0;
40}
41
42static int csky_mptimer_oneshot_stopped(struct clock_event_device *ce)
43{
44 mtcr(PTIM_CTLR, 0);
45
46 return 0;
47}
48
49static DEFINE_PER_CPU(struct timer_of, csky_to) = {
50 .flags = TIMER_OF_CLOCK,
51 .clkevt = {
52 .rating = 300,
53 .features = CLOCK_EVT_FEAT_PERCPU |
54 CLOCK_EVT_FEAT_ONESHOT,
55 .set_state_shutdown = csky_mptimer_shutdown,
56 .set_state_oneshot = csky_mptimer_oneshot,
57 .set_state_oneshot_stopped = csky_mptimer_oneshot_stopped,
58 .set_next_event = csky_mptimer_set_next_event,
59 },
60};
61
62static irqreturn_t csky_timer_interrupt(int irq, void *dev)
63{
64 struct timer_of *to = this_cpu_ptr(&csky_to);
65
66 mtcr(PTIM_TSR, 0);
67
68 to->clkevt.event_handler(&to->clkevt);
69
70 return IRQ_HANDLED;
71}
72
73/*
74 * clock event for percpu
75 */
76static int csky_mptimer_starting_cpu(unsigned int cpu)
77{
78 struct timer_of *to = per_cpu_ptr(&csky_to, cpu);
79
80 to->clkevt.cpumask = cpumask_of(cpu);
81
82 clockevents_config_and_register(&to->clkevt, timer_of_rate(to),
83 2, ULONG_MAX);
84
85 enable_percpu_irq(csky_mptimer_irq, 0);
86
87 return 0;
88}
89
90static int csky_mptimer_dying_cpu(unsigned int cpu)
91{
92 disable_percpu_irq(csky_mptimer_irq);
93
94 return 0;
95}
96
97/*
98 * clock source
99 */
100static u64 sched_clock_read(void)
101{
102 return (u64)mfcr(PTIM_CCVR);
103}
104
105static u64 clksrc_read(struct clocksource *c)
106{
107 return (u64)mfcr(PTIM_CCVR);
108}
109
110struct clocksource csky_clocksource = {
111 .name = "csky",
112 .rating = 400,
113 .mask = CLOCKSOURCE_MASK(32),
114 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
115 .read = clksrc_read,
116};
117
118static int __init csky_mptimer_init(struct device_node *np)
119{
120 int ret, cpu, cpu_rollback;
121 struct timer_of *to = NULL;
122
123 /*
124 * Csky_mptimer is designed for C-SKY SMP multi-processors and
125 * every core has it's own private irq and regs for clkevt and
126 * clksrc.
127 *
128 * The regs is accessed by cpu instruction: mfcr/mtcr instead of
129 * mmio map style. So we needn't mmio-address in dts, but we still
130 * need to give clk and irq number.
131 *
132 * We use private irq for the mptimer and irq number is the same
133 * for every core. So we use request_percpu_irq() in timer_of_init.
134 */
135 csky_mptimer_irq = irq_of_parse_and_map(np, 0);
136 if (csky_mptimer_irq <= 0)
137 return -EINVAL;
138
139 ret = request_percpu_irq(csky_mptimer_irq, csky_timer_interrupt,
140 "csky_mp_timer", &csky_to);
141 if (ret)
142 return -EINVAL;
143
144 for_each_possible_cpu(cpu) {
145 to = per_cpu_ptr(&csky_to, cpu);
146 ret = timer_of_init(np, to);
147 if (ret)
148 goto rollback;
149 }
150
151 clocksource_register_hz(&csky_clocksource, timer_of_rate(to));
152 sched_clock_register(sched_clock_read, 32, timer_of_rate(to));
153
154 ret = cpuhp_setup_state(CPUHP_AP_CSKY_TIMER_STARTING,
155 "clockevents/csky/timer:starting",
156 csky_mptimer_starting_cpu,
157 csky_mptimer_dying_cpu);
158 if (ret)
159 return -EINVAL;
160
161 return 0;
162
163rollback:
164 for_each_possible_cpu(cpu_rollback) {
165 if (cpu_rollback == cpu)
166 break;
167
168 to = per_cpu_ptr(&csky_to, cpu_rollback);
169 timer_of_cleanup(to);
170 }
171 return -EINVAL;
172}
173TIMER_OF_DECLARE(csky_mptimer, "csky,mptimer", csky_mptimer_init);
diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h
index caf40ad0bbc6..e0cd2baa8380 100644
--- a/include/linux/cpuhotplug.h
+++ b/include/linux/cpuhotplug.h
@@ -126,6 +126,7 @@ enum cpuhp_state {
126 CPUHP_AP_MIPS_GIC_TIMER_STARTING, 126 CPUHP_AP_MIPS_GIC_TIMER_STARTING,
127 CPUHP_AP_ARC_TIMER_STARTING, 127 CPUHP_AP_ARC_TIMER_STARTING,
128 CPUHP_AP_RISCV_TIMER_STARTING, 128 CPUHP_AP_RISCV_TIMER_STARTING,
129 CPUHP_AP_CSKY_TIMER_STARTING,
129 CPUHP_AP_KVM_STARTING, 130 CPUHP_AP_KVM_STARTING,
130 CPUHP_AP_KVM_ARM_VGIC_INIT_STARTING, 131 CPUHP_AP_KVM_ARM_VGIC_INIT_STARTING,
131 CPUHP_AP_KVM_ARM_VGIC_STARTING, 132 CPUHP_AP_KVM_ARM_VGIC_STARTING,