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  
gx6605s_timer_interrupt(int irq,void * dev)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  
gx6605s_timer_set_oneshot(struct clock_event_device * ce)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  
gx6605s_timer_set_next_event(unsigned long delta,struct clock_event_device * ce)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  
gx6605s_timer_shutdown(struct clock_event_device * ce)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  
gx6605s_sched_clock_read(void)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  
gx6605s_clkevt_init(void __iomem * base)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  
gx6605s_clksrc_init(void __iomem * base)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  
gx6605s_timer_init(struct device_node * np)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