Skip to content

Commit e7607d5

Browse files
CharlesWu465CL Chin-Long WangCL Wang
authored andcommitted
clocksource: andes: atcpit: Support Andes ATCPIT driver (torvalds#202)
Co-authored-by: CL Chin-Long Wang <[email protected]> Co-authored-by: CL Wang <[email protected]> Reviewed-on: https://gitea.andestech.com/RD-SW/linux/pulls/202 Reviewed-by: randolph <[email protected]> Reviewed-by: CL Chin-Long Wang <[email protected]>
1 parent 03a1f18 commit e7607d5

File tree

3 files changed

+367
-0
lines changed

3 files changed

+367
-0
lines changed

drivers/clocksource/Kconfig

100644100755
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,15 @@ config GXP_TIMER
642642
Provides a driver for the timer control found on HPE
643643
GXP SOCs. This is required for all GXP SOCs.
644644

645+
config ATCPIT100_TIMER
646+
bool "ATCPIT100 timer driver"
647+
select CLKSRC_MMIO
648+
depends on HAS_IOMEM
649+
select TIMER_PROBE
650+
select TIMER_OF
651+
help
652+
This option enables support for Andes ATCPIT100 timer.
653+
645654
config RISCV_TIMER
646655
bool "Timer for the RISC-V platform" if COMPILE_TEST
647656
depends on GENERIC_SCHED_CLOCK && RISCV && RISCV_SBI

drivers/clocksource/Makefile

100644100755
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ obj-$(CONFIG_INGENIC_SYSOST) += ingenic-sysost.o
8080
obj-$(CONFIG_INGENIC_TIMER) += ingenic-timer.o
8181
obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o
8282
obj-$(CONFIG_X86_NUMACHIP) += numachip.o
83+
obj-$(CONFIG_ATCPIT100_TIMER) += timer-atcpit.o
8384
obj-$(CONFIG_RISCV_TIMER) += timer-riscv.o
8485
obj-$(CONFIG_CLINT_TIMER) += timer-clint.o
8586
obj-$(CONFIG_CSKY_MP_TIMER) += timer-mp-csky.o

drivers/clocksource/timer-atcpit.c

Lines changed: 357 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,357 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Copyright (C) 2023 Andes Technology Corporation
4+
*/
5+
6+
#include <linux/clk.h>
7+
#include <linux/irq.h>
8+
#include <linux/clocksource.h>
9+
#include <linux/clockchips.h>
10+
#include <linux/interrupt.h>
11+
#include <linux/ioport.h>
12+
#include <linux/of.h>
13+
#include <linux/of_address.h>
14+
#include <linux/of_irq.h>
15+
#include <linux/of_platform.h>
16+
#include "timer-of.h"
17+
18+
/* ID and Revision Register */
19+
#define ID_REV 0x0
20+
#define ATCPIT_ID 0x03031
21+
#define ATCPIT_ID_MSK 0xFFFFF000
22+
#define ATCPIT_ID_SFT 12
23+
24+
/* Configuration Register */
25+
#define CFG 0x10
26+
#define NUM_PIT_CH_MSK 0x7
27+
28+
/* Interrupt Enable Register */
29+
#define INT_EN 0x14
30+
#define CH_INT_EN(c, t) (t << (c * 4))
31+
32+
/* Interrupt Status Register */
33+
#define INT_STA 0x18
34+
#define CH_INT_STA(c, t) (t << (c * 4))
35+
36+
/* Channel Enable Register */
37+
#define CH_EN_REG 0x1C
38+
#define CH_EN_TIME(c, t) (t << (c * 4))
39+
40+
/* Channel Control Register */
41+
#define CH_CTRL(ch) (0x20 + (ch * 0x10))
42+
#define CH_CTRL_CLK_SFT 3
43+
#define CH_CTRL_MODE_MSK 0x7
44+
#define CH_CTRL_CLK_MSK 0x8
45+
#define TIMER0 1
46+
#define TIMER1 2
47+
#define TIMER2 4
48+
#define TIMER3 8
49+
50+
/* Channel clock source, bit 3, 0:External clock, 1:APB clock */
51+
#define PIT_CLK_SRC_PCLK 1
52+
#define PIT_CLK_SRC_EXT 0
53+
54+
/* Channel mode, bit 0~2 */
55+
#define TIMER_32 0x1
56+
#define TIMER_16 0x2
57+
#define TIMER_8 0x3
58+
59+
/* Channel 0, 1 Reload Register */
60+
#define CH_REL(ch) (0x24 + (ch * 0x10))
61+
62+
/* Channel 0, 1 Counter Register */
63+
#define CH_CNT(ch) (0x28 + (ch * 0x10))
64+
65+
#define to_atcpit_data_clksrc(x) \
66+
container_of(x, struct atcpit_data, clksrc)
67+
68+
#define to_atcpit_data_clkevt(x) \
69+
container_of(x, struct atcpit_data, clkevt)
70+
71+
struct atcpit_data {
72+
struct clock_event_device clkevt;
73+
struct clocksource clksrc;
74+
void __iomem *base;
75+
struct clk *src_clk;
76+
u8 pit_num_ch;
77+
u8 clock_src_ch;
78+
u8 clock_evt_ch;
79+
u8 pit_clk_src;
80+
u8 irq;
81+
};
82+
83+
static inline u8 atcpit_check_pit_id(void __iomem *base)
84+
{
85+
return ((readl(base + ID_REV) & ATCPIT_ID_MSK) >> ATCPIT_ID_SFT) == ATCPIT_ID;
86+
}
87+
88+
static inline u8 atcpit_get_num_ch(void __iomem *base)
89+
{
90+
return readl(base + CFG) & NUM_PIT_CH_MSK;
91+
}
92+
93+
static inline void atcpit_ch_reload(void __iomem *base, u8 ch, u32 reload)
94+
{
95+
writel(reload, base + CH_REL(ch));
96+
}
97+
98+
static void atcpit_ch_crtl(void __iomem *base, u8 ch, u8 ch_clksrc, u8 ch_mode)
99+
{
100+
u32 ch_ctrl;
101+
102+
ch_ctrl = readl(base + CH_CTRL(ch));
103+
ch_ctrl &= ~(CH_CTRL_MODE_MSK | CH_CTRL_CLK_MSK);
104+
ch_ctrl |= (ch_clksrc << CH_CTRL_CLK_SFT) | ch_mode;
105+
writel(ch_ctrl, base + CH_CTRL(ch));
106+
}
107+
108+
static void atcpit_ch_en(void __iomem *base, u8 ch, u8 timer_id, u8 en)
109+
{
110+
u32 pit_en;
111+
112+
pit_en = readl(base + CH_EN_REG);
113+
if (en)
114+
pit_en |= CH_EN_TIME(ch, timer_id);
115+
else
116+
pit_en &= ~(CH_EN_TIME(ch, timer_id));
117+
118+
writel(pit_en, base + CH_EN_REG);
119+
}
120+
121+
static void atcpit_ch_int_en(void __iomem *base, u8 ch, u8 timer_id, u8 en)
122+
{
123+
u32 pit_int_en;
124+
125+
pit_int_en = readl(base + INT_EN);
126+
127+
if (en)
128+
pit_int_en |= CH_INT_EN(ch, timer_id);
129+
else
130+
pit_int_en &= ~(CH_INT_EN(ch, timer_id));
131+
132+
writel(pit_int_en, base + INT_EN);
133+
}
134+
135+
static void atcpit_ch_clear_int(void __iomem *base, u8 ch, u8 timer_id)
136+
{
137+
writel(CH_INT_STA(ch, timer_id), base + INT_STA);
138+
}
139+
140+
static inline void atcpit_clkevt_time_setup(struct atcpit_data *pit_data, unsigned long delay)
141+
{
142+
atcpit_ch_reload(pit_data->base, pit_data->clock_evt_ch, delay);
143+
}
144+
145+
static inline void atcpit_clkevt_time_start(struct atcpit_data *pit_data)
146+
{
147+
atcpit_ch_en(pit_data->base, pit_data->clock_evt_ch, TIMER0, 1);
148+
}
149+
150+
static inline void atcpit_clkevt_time_stop(struct atcpit_data *pit_data)
151+
{
152+
atcpit_ch_en(pit_data->base, pit_data->clock_evt_ch, TIMER0, 0);
153+
atcpit_ch_clear_int(pit_data->base, pit_data->clock_evt_ch, TIMER0);
154+
}
155+
156+
static int atcpit_clkevt_set_periodic(struct clock_event_device *evt)
157+
{
158+
struct atcpit_data *pit_data;
159+
struct timer_of *to = to_timer_of(evt);
160+
161+
pit_data = to_atcpit_data_clkevt(evt);
162+
163+
atcpit_clkevt_time_stop(pit_data);
164+
atcpit_clkevt_time_setup(pit_data, timer_of_period(to));
165+
atcpit_clkevt_time_start(pit_data);
166+
167+
return 0;
168+
}
169+
170+
static int atcpit_clkevt_shutdown(struct clock_event_device *evt)
171+
{
172+
struct atcpit_data *pit_data;
173+
174+
pit_data = to_atcpit_data_clkevt(evt);
175+
atcpit_clkevt_time_stop(pit_data);
176+
177+
return 0;
178+
}
179+
180+
static irqreturn_t atcpit_timer_interrupt(int irq, void *dev_id)
181+
{
182+
struct clock_event_device *evt = (struct clock_event_device *)dev_id;
183+
struct atcpit_data *pit_data = to_atcpit_data_clkevt(evt);
184+
185+
atcpit_ch_clear_int(pit_data->base, pit_data->clock_evt_ch, TIMER0);
186+
187+
if (evt->event_handler)
188+
evt->event_handler(evt);
189+
190+
return IRQ_HANDLED;
191+
}
192+
193+
static u64 atcpit_clksrc_read(struct clocksource *clksrc)
194+
{
195+
struct atcpit_data *atcpit;
196+
197+
atcpit = to_atcpit_data_clksrc(clksrc);
198+
199+
return ~readl(atcpit->base + CH_CNT(atcpit->clock_src_ch));
200+
}
201+
202+
static int __init atcpit_clockevent_init(struct device_node *node, struct atcpit_data *pit_data)
203+
{
204+
unsigned long pit_clk_rate;
205+
int ret = 0;
206+
207+
pit_clk_rate = clk_get_rate(pit_data->src_clk);
208+
if (!pit_clk_rate) {
209+
pr_err("Invalid clock rate\n");
210+
return -EINVAL;
211+
}
212+
213+
pit_data->clkevt.name = "atcpit_timer";
214+
pit_data->clkevt.features = CLOCK_EVT_FEAT_PERIODIC;
215+
pit_data->clkevt.set_state_shutdown = atcpit_clkevt_shutdown;
216+
pit_data->clkevt.set_state_periodic = atcpit_clkevt_set_periodic;
217+
pit_data->clkevt.tick_resume = atcpit_clkevt_shutdown;
218+
pit_data->clkevt.rating = 200;
219+
pit_data->irq = pit_data->irq;
220+
pit_data->clkevt.cpumask = cpu_possible_mask;
221+
222+
atcpit_ch_crtl(pit_data->base, pit_data->clock_evt_ch, pit_data->pit_clk_src, TIMER_32);
223+
atcpit_ch_clear_int(pit_data->base, pit_data->clock_evt_ch, TIMER0);
224+
atcpit_ch_int_en(pit_data->base, pit_data->clock_evt_ch, TIMER0, 1);
225+
ret = request_irq(pit_data->irq, atcpit_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL,
226+
"atcpit_timer", &pit_data->clkevt);
227+
if (ret) {
228+
pr_err("Unable to register interrupt for atcpit_timer\n");
229+
goto ERR_EXIT;
230+
}
231+
232+
clockevents_config_and_register(&pit_data->clkevt, pit_clk_rate, 0xf, 0xfffffffe);
233+
234+
ERR_EXIT:
235+
return ret;
236+
}
237+
238+
239+
static int __init atcpit_clocksource_init(struct device_node *node, struct atcpit_data *pit_data)
240+
{
241+
unsigned long pit_clk_rate;
242+
int ret = 0;
243+
244+
pit_clk_rate = clk_get_rate(pit_data->src_clk);
245+
if (!pit_clk_rate) {
246+
pr_err("Invalid clock rate\n");
247+
return -EINVAL;
248+
}
249+
250+
pit_data->clksrc.mask = CLOCKSOURCE_MASK(32);
251+
pit_data->clksrc.name = "atcpit_clocksource";
252+
pit_data->clksrc.rating = 200;
253+
pit_data->clksrc.read = atcpit_clksrc_read;
254+
pit_data->clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS;
255+
256+
atcpit_ch_crtl(pit_data->base, pit_data->clock_src_ch, pit_data->pit_clk_src, TIMER_32);
257+
atcpit_ch_reload(pit_data->base, pit_data->clock_src_ch, 0xFFFFFFFF);
258+
atcpit_ch_en(pit_data->base, pit_data->clock_src_ch, TIMER0, 1);
259+
260+
ret = clocksource_register_hz(&pit_data->clksrc, pit_clk_rate);
261+
262+
if (ret)
263+
pr_err("Failed to register clocksource\n");
264+
265+
return ret;
266+
}
267+
268+
static int __init atcpit_timer_init(struct device_node *node)
269+
{
270+
int (*read_fixup)(void __iomem *addr, unsigned int val, unsigned int shift_bits);
271+
struct atcpit_data *pit_data;
272+
int ret = 0;
273+
u32 val;
274+
275+
pit_data = kzalloc(sizeof(*pit_data), GFP_KERNEL);
276+
if (!pit_data)
277+
return -ENOMEM;
278+
279+
pit_data->clock_evt_ch = 0xFF;
280+
pit_data->clock_src_ch = 0xFF;
281+
282+
pit_data->base = of_iomap(node, 0);
283+
if (!pit_data->base) {
284+
pr_err("Invalid io addr\n");
285+
ret = -ENXIO;
286+
goto ERR_EXIT;
287+
}
288+
289+
/* Check ID register */
290+
read_fixup = symbol_get(readl_fixup);
291+
if (read_fixup != NULL) {
292+
ret = read_fixup(pit_data->base, ATCPIT_ID, 12);
293+
symbol_put(readl_fixup);
294+
} else {
295+
ret = atcpit_check_pit_id(pit_data->base);
296+
}
297+
if (!ret) {
298+
pr_err("Invalid ID register, ATCPIT is not supported\n");
299+
ret = -ENXIO;
300+
goto ERR_EXIT;
301+
}
302+
303+
pit_data->pit_num_ch = atcpit_get_num_ch(pit_data->base);
304+
305+
pit_data->src_clk = of_clk_get(node, 0);
306+
if (IS_ERR(pit_data->src_clk)) {
307+
pr_err("Invalid src_clk\n");
308+
ret = PTR_ERR(pit_data->src_clk);
309+
goto ERR_EXIT;
310+
}
311+
312+
pit_data->irq = irq_of_parse_and_map(node, 0);
313+
if (!pit_data->irq) {
314+
pr_err("Invalid irq\n");
315+
ret = -EINVAL;
316+
goto ERR_EXIT;
317+
}
318+
319+
ret = of_property_read_u32(node, "pit_clk_src", &val);
320+
if (ret || val > PIT_CLK_SRC_PCLK) {
321+
pr_err("Invalid pit clock source\n");
322+
goto ERR_EXIT;
323+
}
324+
pit_data->pit_clk_src = val;
325+
326+
ret = of_property_read_u32(node, "clock_src_ch", &val);
327+
if (!ret) {
328+
if (val < pit_data->pit_num_ch) {
329+
pit_data->clock_src_ch = val;
330+
ret = atcpit_clocksource_init(node, pit_data);
331+
if (ret)
332+
goto ERR_EXIT;
333+
} else {
334+
pr_err("Invalid clock_src_ch:%d\n", val);
335+
}
336+
}
337+
338+
ret = of_property_read_u32(node, "clock_evt_ch", &val);
339+
if (!ret) {
340+
if (val < pit_data->pit_num_ch) {
341+
pit_data->clock_evt_ch = val;
342+
ret = atcpit_clockevent_init(node, pit_data);
343+
if (ret)
344+
goto ERR_EXIT;
345+
} else {
346+
pr_err("Invalid clock_evt_ch:%d\n", val);
347+
}
348+
}
349+
350+
return ret;
351+
352+
ERR_EXIT:
353+
kfree(pit_data);
354+
return ret;
355+
}
356+
357+
TIMER_OF_DECLARE(andes_atcpit, "andestech,atcpit100", atcpit_timer_init);

0 commit comments

Comments
 (0)