aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/clocksource/Kconfig8
-rw-r--r--drivers/clocksource/Makefile1
-rw-r--r--drivers/clocksource/timer-gx6605s.c154
3 files changed, 163 insertions, 0 deletions
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 591c9a8649a5..55c77e44bb2d 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -630,4 +630,12 @@ config CSKY_MP_TIMER
630 csky,mptimer is not only used in SMP system, it also could be used 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. 631 single core system. It's not a mmio reg and it use mtcr/mfcr instruction.
632 632
633config GX6605S_TIMER
634 bool "Gx6605s SOC system timer driver" if COMPILE_TEST
635 depends on CSKY
636 select CLKSRC_MMIO
637 select TIMER_OF
638 help
639 This option enables support for gx6605s SOC's timer.
640
633endmenu 641endmenu
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 5ce82d39cda7..919633133271 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -80,3 +80,4 @@ obj-$(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 82obj-$(CONFIG_CSKY_MP_TIMER) += timer-mp-csky.o
83obj-$(CONFIG_GX6605S_TIMER) += timer-gx6605s.o
diff --git a/drivers/clocksource/timer-gx6605s.c b/drivers/clocksource/timer-gx6605s.c
new file mode 100644
index 000000000000..80d0939d040b
--- /dev/null
+++ b/drivers/clocksource/timer-gx6605s.c
@@ -0,0 +1,154 @@
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
8#include "timer-of.h"
9
10#define CLKSRC_OFFSET 0x40
11
12#define TIMER_STATUS 0x00
13#define TIMER_VALUE 0x04
14#define TIMER_CONTRL 0x10
15#define TIMER_CONFIG 0x20
16#define TIMER_DIV 0x24
17#define TIMER_INI 0x28
18
19#define GX6605S_STATUS_CLR BIT(0)
20#define GX6605S_CONTRL_RST BIT(0)
21#define GX6605S_CONTRL_START BIT(1)
22#define GX6605S_CONFIG_EN BIT(0)
23#define GX6605S_CONFIG_IRQ_EN BIT(1)
24
25static irqreturn_t gx6605s_timer_interrupt(int irq, void *dev)
26{
27 struct clock_event_device *ce = dev;
28 void __iomem *base = timer_of_base(to_timer_of(ce));
29
30 writel_relaxed(GX6605S_STATUS_CLR, base + TIMER_STATUS);
31
32 ce->event_handler(ce);
33
34 return IRQ_HANDLED;
35}
36
37static int gx6605s_timer_set_oneshot(struct clock_event_device *ce)
38{
39 void __iomem *base = timer_of_base(to_timer_of(ce));
40
41 /* reset and stop counter */
42 writel_relaxed(GX6605S_CONTRL_RST, base + TIMER_CONTRL);
43
44 /* enable with irq and start */
45 writel_relaxed(GX6605S_CONFIG_EN | GX6605S_CONFIG_IRQ_EN,
46 base + TIMER_CONFIG);
47
48 return 0;
49}
50
51static int gx6605s_timer_set_next_event(unsigned long delta,
52 struct clock_event_device *ce)
53{
54 void __iomem *base = timer_of_base(to_timer_of(ce));
55
56 /* use reset to pause timer */
57 writel_relaxed(GX6605S_CONTRL_RST, base + TIMER_CONTRL);
58
59 /* config next timeout value */
60 writel_relaxed(ULONG_MAX - delta, base + TIMER_INI);
61 writel_relaxed(GX6605S_CONTRL_START, base + TIMER_CONTRL);
62
63 return 0;
64}
65
66static int gx6605s_timer_shutdown(struct clock_event_device *ce)
67{
68 void __iomem *base = timer_of_base(to_timer_of(ce));
69
70 writel_relaxed(0, base + TIMER_CONTRL);
71 writel_relaxed(0, base + TIMER_CONFIG);
72
73 return 0;
74}
75
76static struct timer_of to = {
77 .flags = TIMER_OF_IRQ | TIMER_OF_BASE | TIMER_OF_CLOCK,
78 .clkevt = {
79 .rating = 300,
80 .features = CLOCK_EVT_FEAT_DYNIRQ |
81 CLOCK_EVT_FEAT_ONESHOT,
82 .set_state_shutdown = gx6605s_timer_shutdown,
83 .set_state_oneshot = gx6605s_timer_set_oneshot,
84 .set_next_event = gx6605s_timer_set_next_event,
85 .cpumask = cpu_possible_mask,
86 },
87 .of_irq = {
88 .handler = gx6605s_timer_interrupt,
89 .flags = IRQF_TIMER | IRQF_IRQPOLL,
90 },
91};
92
93static u64 notrace gx6605s_sched_clock_read(void)
94{
95 void __iomem *base;
96
97 base = timer_of_base(&to) + CLKSRC_OFFSET;
98
99 return (u64)readl_relaxed(base + TIMER_VALUE);
100}
101
102static void gx6605s_clkevt_init(void __iomem *base)
103{
104 writel_relaxed(0, base + TIMER_DIV);
105 writel_relaxed(0, base + TIMER_CONFIG);
106
107 clockevents_config_and_register(&to.clkevt, timer_of_rate(&to), 2,
108 ULONG_MAX);
109}
110
111static int gx6605s_clksrc_init(void __iomem *base)
112{
113 writel_relaxed(0, base + TIMER_DIV);
114 writel_relaxed(0, base + TIMER_INI);
115
116 writel_relaxed(GX6605S_CONTRL_RST, base + TIMER_CONTRL);
117
118 writel_relaxed(GX6605S_CONFIG_EN, base + TIMER_CONFIG);
119
120 writel_relaxed(GX6605S_CONTRL_START, base + TIMER_CONTRL);
121
122 sched_clock_register(gx6605s_sched_clock_read, 32, timer_of_rate(&to));
123
124 return clocksource_mmio_init(base + TIMER_VALUE, "gx6605s",
125 timer_of_rate(&to), 200, 32, clocksource_mmio_readl_up);
126}
127
128static int __init gx6605s_timer_init(struct device_node *np)
129{
130 int ret;
131
132 /*
133 * The timer driver is for nationalchip gx6605s SOC and there are two
134 * same timer in gx6605s. We use one for clkevt and another for clksrc.
135 *
136 * The timer is mmio map to access, so we need give mmio address in dts.
137 *
138 * It provides a 32bit countup timer and interrupt will be caused by
139 * count-overflow.
140 * So we need set-next-event by ULONG_MAX - delta in TIMER_INI reg.
141 *
142 * The counter at 0x0 offset is clock event.
143 * The counter at 0x40 offset is clock source.
144 * They are the same in hardware, just different used by driver.
145 */
146 ret = timer_of_init(np, &to);
147 if (ret)
148 return ret;
149
150 gx6605s_clkevt_init(timer_of_base(&to));
151
152 return gx6605s_clksrc_init(timer_of_base(&to) + CLKSRC_OFFSET);
153}
154TIMER_OF_DECLARE(csky_gx6605s_timer, "csky,gx6605s-timer", gx6605s_timer_init);