aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clocksource
diff options
context:
space:
mode:
authorRick Chen <rickchen36@gmail.com>2017-12-11 02:53:15 -0500
committerGreentime Hu <greentime@andestech.com>2018-02-21 21:44:36 -0500
commit35dbb74aa752cff90e8dac1a24ed2a452aed0251 (patch)
treeabc5f51bd1582b665b0b7ec5d8b180b61762ff66 /drivers/clocksource
parenteac8173e7b99b215a386391dc95f5e4d7e4d7085 (diff)
clocksource/drivers/atcpit100: Add andestech atcpit100 timer
ATCPIT100 is often used on the Andes architecture, This timer provide 4 PIT channels. Each PIT channel is a multi-function timer, can be configured as 32,16,8 bit timers or PWM as well. For system timer it will set channel 1 32-bit timer0 as clock source and count downwards until underflow and restart again. It also set channel 0 32-bit timer0 as clock event and count downwards until condition match. It will generate an interrupt for handling periodically. Signed-off-by: Rick Chen <rickchen36@gmail.com> Signed-off-by: Greentime Hu <green.hu@gmail.com> Reviewed-by: Linus Walleij <linus.walleij@linaro.org> Add andestech atcpit100 timer
Diffstat (limited to 'drivers/clocksource')
-rw-r--r--drivers/clocksource/Kconfig9
-rw-r--r--drivers/clocksource/Makefile1
-rw-r--r--drivers/clocksource/timer-atcpit100.c248
3 files changed, 258 insertions, 0 deletions
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index b3b4ed9b6874..19d65fe0627e 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -591,4 +591,13 @@ config CLKSRC_ST_LPC
591 Enable this option to use the Low Power controller timer 591 Enable this option to use the Low Power controller timer
592 as clocksource. 592 as clocksource.
593 593
594config ATCPIT100_TIMER
595 bool "ATCPIT100 timer driver"
596 depends on NDS32 || COMPILE_TEST
597 depends on HAS_IOMEM
598 select TIMER_OF
599 default NDS32
600 help
601 This option enables support for the Andestech ATCPIT100 timers.
602
594endmenu 603endmenu
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index d6dec4489d66..a79523b22e52 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -76,3 +76,4 @@ obj-$(CONFIG_H8300_TMR16) += h8300_timer16.o
76obj-$(CONFIG_H8300_TPU) += h8300_tpu.o 76obj-$(CONFIG_H8300_TPU) += h8300_tpu.o
77obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o 77obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o
78obj-$(CONFIG_X86_NUMACHIP) += numachip.o 78obj-$(CONFIG_X86_NUMACHIP) += numachip.o
79obj-$(CONFIG_ATCPIT100_TIMER) += timer-atcpit100.o
diff --git a/drivers/clocksource/timer-atcpit100.c b/drivers/clocksource/timer-atcpit100.c
new file mode 100644
index 000000000000..2190096cffa3
--- /dev/null
+++ b/drivers/clocksource/timer-atcpit100.c
@@ -0,0 +1,248 @@
1// SPDX-License-Identifier: GPL-2.0
2// Copyright (C) 2005-2017 Andes Technology Corporation
3/*
4 * Andestech ATCPIT100 Timer Device Driver Implementation
5 * Rick Chen, Andes Technology Corporation <rick@andestech.com>
6 *
7 */
8
9#include <linux/irq.h>
10#include <linux/clocksource.h>
11#include <linux/clockchips.h>
12#include <linux/interrupt.h>
13#include <linux/ioport.h>
14#include <linux/cpufreq.h>
15#include <linux/sched.h>
16#include <linux/sched_clock.h>
17#include <linux/of_address.h>
18#include <linux/of_irq.h>
19#include <linux/of_platform.h>
20#include "timer-of.h"
21
22/*
23 * Definition of register offsets
24 */
25
26/* ID and Revision Register */
27#define ID_REV 0x0
28
29/* Configuration Register */
30#define CFG 0x10
31
32/* Interrupt Enable Register */
33#define INT_EN 0x14
34#define CH_INT_EN(c, i) ((1<<i)<<(4*c))
35#define CH0INT0EN 0x01
36
37/* Interrupt Status Register */
38#define INT_STA 0x18
39#define CH0INT0 0x01
40
41/* Channel Enable Register */
42#define CH_EN 0x1C
43#define CH0TMR0EN 0x1
44#define CH1TMR0EN 0x10
45
46/* Channel 0 , 1 Control Register */
47#define CH0_CTL (0x20)
48#define CH1_CTL (0x20 + 0x10)
49
50/* Channel clock source , bit 3 , 0:External clock , 1:APB clock */
51#define APB_CLK BIT(3)
52
53/* Channel mode , bit 0~2 */
54#define TMR_32 0x1
55#define TMR_16 0x2
56#define TMR_8 0x3
57
58/* Channel 0 , 1 Reload Register */
59#define CH0_REL (0x24)
60#define CH1_REL (0x24 + 0x10)
61
62/* Channel 0 , 1 Counter Register */
63#define CH0_CNT (0x28)
64#define CH1_CNT (0x28 + 0x10)
65
66#define TIMER_SYNC_TICKS 3
67
68static void atcpit100_ch1_tmr0_en(void __iomem *base)
69{
70 writel(~0, base + CH1_REL);
71 writel(APB_CLK|TMR_32, base + CH1_CTL);
72}
73
74static void atcpit100_ch0_tmr0_en(void __iomem *base)
75{
76 writel(APB_CLK|TMR_32, base + CH0_CTL);
77}
78
79static void atcpit100_clkevt_time_setup(void __iomem *base, unsigned long delay)
80{
81 writel(delay, base + CH0_CNT);
82 writel(delay, base + CH0_REL);
83}
84
85static void atcpit100_timer_clear_interrupt(void __iomem *base)
86{
87 u32 val;
88
89 val = readl(base + INT_STA);
90 writel(val | CH0INT0, base + INT_STA);
91}
92
93static void atcpit100_clocksource_start(void __iomem *base)
94{
95 u32 val;
96
97 val = readl(base + CH_EN);
98 writel(val | CH1TMR0EN, base + CH_EN);
99}
100
101static void atcpit100_clkevt_time_start(void __iomem *base)
102{
103 u32 val;
104
105 val = readl(base + CH_EN);
106 writel(val | CH0TMR0EN, base + CH_EN);
107}
108
109static void atcpit100_clkevt_time_stop(void __iomem *base)
110{
111 u32 val;
112
113 atcpit100_timer_clear_interrupt(base);
114 val = readl(base + CH_EN);
115 writel(val & ~CH0TMR0EN, base + CH_EN);
116}
117
118static int atcpit100_clkevt_next_event(unsigned long evt,
119 struct clock_event_device *clkevt)
120{
121 u32 val;
122 struct timer_of *to = to_timer_of(clkevt);
123
124 val = readl(timer_of_base(to) + CH_EN);
125 writel(val & ~CH0TMR0EN, timer_of_base(to) + CH_EN);
126 writel(evt, timer_of_base(to) + CH0_REL);
127 writel(val | CH0TMR0EN, timer_of_base(to) + CH_EN);
128
129 return 0;
130}
131
132static int atcpit100_clkevt_set_periodic(struct clock_event_device *evt)
133{
134 struct timer_of *to = to_timer_of(evt);
135
136 atcpit100_clkevt_time_setup(timer_of_base(to), timer_of_period(to));
137 atcpit100_clkevt_time_start(timer_of_base(to));
138
139 return 0;
140}
141static int atcpit100_clkevt_shutdown(struct clock_event_device *evt)
142{
143 struct timer_of *to = to_timer_of(evt);
144
145 atcpit100_clkevt_time_stop(timer_of_base(to));
146
147 return 0;
148}
149static int atcpit100_clkevt_set_oneshot(struct clock_event_device *evt)
150{
151 struct timer_of *to = to_timer_of(evt);
152 u32 val;
153
154 writel(~0x0, timer_of_base(to) + CH0_REL);
155 val = readl(timer_of_base(to) + CH_EN);
156 writel(val | CH0TMR0EN, timer_of_base(to) + CH_EN);
157
158 return 0;
159}
160
161static irqreturn_t atcpit100_timer_interrupt(int irq, void *dev_id)
162{
163 struct clock_event_device *evt = (struct clock_event_device *)dev_id;
164 struct timer_of *to = to_timer_of(evt);
165
166 atcpit100_timer_clear_interrupt(timer_of_base(to));
167
168 evt->event_handler(evt);
169
170 return IRQ_HANDLED;
171}
172
173static struct timer_of to = {
174 .flags = TIMER_OF_IRQ | TIMER_OF_CLOCK | TIMER_OF_BASE,
175
176 .clkevt = {
177 .name = "atcpit100_tick",
178 .rating = 300,
179 .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
180 .set_state_shutdown = atcpit100_clkevt_shutdown,
181 .set_state_periodic = atcpit100_clkevt_set_periodic,
182 .set_state_oneshot = atcpit100_clkevt_set_oneshot,
183 .tick_resume = atcpit100_clkevt_shutdown,
184 .set_next_event = atcpit100_clkevt_next_event,
185 .cpumask = cpu_all_mask,
186 },
187
188 .of_irq = {
189 .handler = atcpit100_timer_interrupt,
190 .flags = IRQF_TIMER | IRQF_IRQPOLL,
191 },
192
193 /*
194 * FIXME: we currently only support clocking using PCLK
195 * and using EXTCLK is not supported in the driver.
196 */
197 .of_clk = {
198 .name = "PCLK",
199 }
200};
201
202static u64 notrace atcpit100_timer_sched_read(void)
203{
204 return ~readl(timer_of_base(&to) + CH1_CNT);
205}
206
207static int __init atcpit100_timer_init(struct device_node *node)
208{
209 int ret;
210 u32 val;
211 void __iomem *base;
212
213 ret = timer_of_init(node, &to);
214 if (ret)
215 return ret;
216
217 base = timer_of_base(&to);
218
219 sched_clock_register(atcpit100_timer_sched_read, 32,
220 timer_of_rate(&to));
221
222 ret = clocksource_mmio_init(base + CH1_CNT,
223 node->name, timer_of_rate(&to), 300, 32,
224 clocksource_mmio_readl_down);
225
226 if (ret) {
227 pr_err("Failed to register clocksource\n");
228 return ret;
229 }
230
231 /* clear channel 0 timer0 interrupt */
232 atcpit100_timer_clear_interrupt(base);
233
234 clockevents_config_and_register(&to.clkevt, timer_of_rate(&to),
235 TIMER_SYNC_TICKS, 0xffffffff);
236 atcpit100_ch0_tmr0_en(base);
237 atcpit100_ch1_tmr0_en(base);
238 atcpit100_clocksource_start(base);
239 atcpit100_clkevt_time_start(base);
240
241 /* Enable channel 0 timer0 interrupt */
242 val = readl(base + INT_EN);
243 writel(val | CH0INT0EN, base + INT_EN);
244
245 return ret;
246}
247
248TIMER_OF_DECLARE(atcpit100, "andestech,atcpit100", atcpit100_timer_init);