xref: /linux/drivers/clocksource/timer-gx6605s.c (revision 26fbb4c8c7c3ee9a4c3b4de555a8587b5a19154e)
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 
25 static 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 	writel_relaxed(0, base + TIMER_INI);
32 
33 	ce->event_handler(ce);
34 
35 	return IRQ_HANDLED;
36 }
37 
38 static int gx6605s_timer_set_oneshot(struct clock_event_device *ce)
39 {
40 	void __iomem *base = timer_of_base(to_timer_of(ce));
41 
42 	/* reset and stop counter */
43 	writel_relaxed(GX6605S_CONTRL_RST, base + TIMER_CONTRL);
44 
45 	/* enable with irq and start */
46 	writel_relaxed(GX6605S_CONFIG_EN | GX6605S_CONFIG_IRQ_EN,
47 		       base + TIMER_CONFIG);
48 
49 	return 0;
50 }
51 
52 static int gx6605s_timer_set_next_event(unsigned long delta,
53 					struct clock_event_device *ce)
54 {
55 	void __iomem *base = timer_of_base(to_timer_of(ce));
56 
57 	/* use reset to pause timer */
58 	writel_relaxed(GX6605S_CONTRL_RST, base + TIMER_CONTRL);
59 
60 	/* config next timeout value */
61 	writel_relaxed(ULONG_MAX - delta, base + TIMER_INI);
62 	writel_relaxed(GX6605S_CONTRL_START, base + TIMER_CONTRL);
63 
64 	return 0;
65 }
66 
67 static int gx6605s_timer_shutdown(struct clock_event_device *ce)
68 {
69 	void __iomem *base = timer_of_base(to_timer_of(ce));
70 
71 	writel_relaxed(0, base + TIMER_CONTRL);
72 	writel_relaxed(0, base + TIMER_CONFIG);
73 
74 	return 0;
75 }
76 
77 static struct timer_of to = {
78 	.flags = TIMER_OF_IRQ | TIMER_OF_BASE | TIMER_OF_CLOCK,
79 	.clkevt = {
80 		.rating			= 300,
81 		.features		= CLOCK_EVT_FEAT_DYNIRQ |
82 					  CLOCK_EVT_FEAT_ONESHOT,
83 		.set_state_shutdown	= gx6605s_timer_shutdown,
84 		.set_state_oneshot	= gx6605s_timer_set_oneshot,
85 		.set_next_event		= gx6605s_timer_set_next_event,
86 		.cpumask		= cpu_possible_mask,
87 	},
88 	.of_irq = {
89 		.handler		= gx6605s_timer_interrupt,
90 		.flags			= IRQF_TIMER | IRQF_IRQPOLL,
91 	},
92 };
93 
94 static u64 notrace gx6605s_sched_clock_read(void)
95 {
96 	void __iomem *base;
97 
98 	base = timer_of_base(&to) + CLKSRC_OFFSET;
99 
100 	return (u64)readl_relaxed(base + TIMER_VALUE);
101 }
102 
103 static void gx6605s_clkevt_init(void __iomem *base)
104 {
105 	writel_relaxed(0, base + TIMER_DIV);
106 	writel_relaxed(0, base + TIMER_CONFIG);
107 
108 	clockevents_config_and_register(&to.clkevt, timer_of_rate(&to), 2,
109 					ULONG_MAX);
110 }
111 
112 static int gx6605s_clksrc_init(void __iomem *base)
113 {
114 	writel_relaxed(0, base + TIMER_DIV);
115 	writel_relaxed(0, base + TIMER_INI);
116 
117 	writel_relaxed(GX6605S_CONTRL_RST, base + TIMER_CONTRL);
118 
119 	writel_relaxed(GX6605S_CONFIG_EN, base + TIMER_CONFIG);
120 
121 	writel_relaxed(GX6605S_CONTRL_START, base + TIMER_CONTRL);
122 
123 	sched_clock_register(gx6605s_sched_clock_read, 32, timer_of_rate(&to));
124 
125 	return clocksource_mmio_init(base + TIMER_VALUE, "gx6605s",
126 			timer_of_rate(&to), 200, 32, clocksource_mmio_readl_up);
127 }
128 
129 static int __init gx6605s_timer_init(struct device_node *np)
130 {
131 	int ret;
132 
133 	/*
134 	 * The timer driver is for nationalchip gx6605s SOC and there are two
135 	 * same timer in gx6605s. We use one for clkevt and another for clksrc.
136 	 *
137 	 * The timer is mmio map to access, so we need give mmio address in dts.
138 	 *
139 	 * It provides a 32bit countup timer and interrupt will be caused by
140 	 * count-overflow.
141 	 * So we need set-next-event by ULONG_MAX - delta in TIMER_INI reg.
142 	 *
143 	 * The counter at 0x0  offset is clock event.
144 	 * The counter at 0x40 offset is clock source.
145 	 * They are the same in hardware, just different used by driver.
146 	 */
147 	ret = timer_of_init(np, &to);
148 	if (ret)
149 		return ret;
150 
151 	gx6605s_clkevt_init(timer_of_base(&to));
152 
153 	return gx6605s_clksrc_init(timer_of_base(&to) + CLKSRC_OFFSET);
154 }
155 TIMER_OF_DECLARE(csky_gx6605s_timer, "csky,gx6605s-timer", gx6605s_timer_init);
156