Skip to content

Commit 5fc1f93

Browse files
rperierdlezcano
authored andcommitted
clocksource/drivers: Add MStar MSC313e timer support
The MSC313e-compatible SoCs have 3 timer hardware blocks. All of these are free running 32-bit increasing counters and can generate interrupts. Based onto a maximum value register, each timer can either count from 0 to max, one time then stop (which generates interrupts) or can count from 0 to max and then roll. This commit adds basic support for these timers, the first timer block being used as clocksource/sched_clock and delay, while the others will be used as clockevents. Signed-off-by: Romain Perier <[email protected]> Co-developed-by: Daniel Palmer <[email protected]> Signed-off-by: Daniel Palmer <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Daniel Lezcano <[email protected]>
1 parent 0642fb4 commit 5fc1f93

File tree

4 files changed

+255
-0
lines changed

4 files changed

+255
-0
lines changed

MAINTAINERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2282,6 +2282,7 @@ F: Documentation/devicetree/bindings/gpio/mstar,msc313-gpio.yaml
22822282
F: arch/arm/boot/dts/mstar-*
22832283
F: arch/arm/mach-mstar/
22842284
F: drivers/clk/mstar/
2285+
F: drivers/clocksource/timer-msc313e.c
22852286
F: drivers/gpio/gpio-msc313.c
22862287
F: drivers/rtc/rtc-msc313.c
22872288
F: drivers/watchdog/msc313e_wdt.c

drivers/clocksource/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,15 @@ config MILBEAUT_TIMER
672672
help
673673
Enables the support for Milbeaut timer driver.
674674

675+
config MSC313E_TIMER
676+
bool "MSC313E timer driver" if COMPILE_TEST
677+
select TIMER_OF
678+
select CLKSRC_MMIO
679+
help
680+
Enables support for the MStar MSC313E timer driver.
681+
This provides access to multiple interrupt generating
682+
programmable 32-bit free running incrementing counters.
683+
675684
config INGENIC_TIMER
676685
bool "Clocksource/timer using the TCU in Ingenic JZ SoCs"
677686
default MACH_INGENIC

drivers/clocksource/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,4 @@ obj-$(CONFIG_CSKY_MP_TIMER) += timer-mp-csky.o
8888
obj-$(CONFIG_GX6605S_TIMER) += timer-gx6605s.o
8989
obj-$(CONFIG_HYPERV_TIMER) += hyperv_timer.o
9090
obj-$(CONFIG_MICROCHIP_PIT64B) += timer-microchip-pit64b.o
91+
obj-$(CONFIG_MSC313E_TIMER) += timer-msc313e.o

drivers/clocksource/timer-msc313e.c

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* MStar timer driver
4+
*
5+
* Copyright (C) 2021 Daniel Palmer
6+
* Copyright (C) 2021 Romain Perier
7+
*
8+
*/
9+
10+
#include <linux/clk.h>
11+
#include <linux/clockchips.h>
12+
#include <linux/interrupt.h>
13+
#include <linux/irq.h>
14+
#include <linux/irqreturn.h>
15+
#include <linux/sched_clock.h>
16+
#include <linux/of.h>
17+
#include <linux/of_address.h>
18+
#include <linux/of_irq.h>
19+
20+
#ifdef CONFIG_ARM
21+
#include <linux/delay.h>
22+
#endif
23+
24+
#include "timer-of.h"
25+
26+
#define TIMER_NAME "msc313e_timer"
27+
28+
#define MSC313E_REG_CTRL 0x00
29+
#define MSC313E_REG_CTRL_TIMER_EN BIT(0)
30+
#define MSC313E_REG_CTRL_TIMER_TRIG BIT(1)
31+
#define MSC313E_REG_CTRL_TIMER_INT_EN BIT(8)
32+
#define MSC313E_REG_TIMER_MAX_LOW 0x08
33+
#define MSC313E_REG_TIMER_MAX_HIGH 0x0c
34+
#define MSC313E_REG_COUNTER_LOW 0x10
35+
#define MSC313E_REG_COUNTER_HIGH 0x14
36+
37+
#define TIMER_SYNC_TICKS 3
38+
39+
#ifdef CONFIG_ARM
40+
struct msc313e_delay {
41+
void __iomem *base;
42+
struct delay_timer delay;
43+
};
44+
static struct msc313e_delay msc313e_delay;
45+
#endif
46+
47+
static void __iomem *msc313e_clksrc;
48+
49+
static void msc313e_timer_stop(void __iomem *base)
50+
{
51+
writew(0, base + MSC313E_REG_CTRL);
52+
}
53+
54+
static void msc313e_timer_start(void __iomem *base, bool periodic)
55+
{
56+
u16 reg;
57+
58+
reg = readw(base + MSC313E_REG_CTRL);
59+
if (periodic)
60+
reg |= MSC313E_REG_CTRL_TIMER_EN;
61+
else
62+
reg |= MSC313E_REG_CTRL_TIMER_TRIG;
63+
writew(reg | MSC313E_REG_CTRL_TIMER_INT_EN, base + MSC313E_REG_CTRL);
64+
}
65+
66+
static void msc313e_timer_setup(void __iomem *base, unsigned long delay)
67+
{
68+
unsigned long flags;
69+
70+
local_irq_save(flags);
71+
writew(delay >> 16, base + MSC313E_REG_TIMER_MAX_HIGH);
72+
writew(delay & 0xffff, base + MSC313E_REG_TIMER_MAX_LOW);
73+
local_irq_restore(flags);
74+
}
75+
76+
static unsigned long msc313e_timer_current_value(void __iomem *base)
77+
{
78+
unsigned long flags;
79+
u16 l, h;
80+
81+
local_irq_save(flags);
82+
l = readw(base + MSC313E_REG_COUNTER_LOW);
83+
h = readw(base + MSC313E_REG_COUNTER_HIGH);
84+
local_irq_restore(flags);
85+
86+
return (((u32)h) << 16 | l);
87+
}
88+
89+
static int msc313e_timer_clkevt_shutdown(struct clock_event_device *evt)
90+
{
91+
struct timer_of *timer = to_timer_of(evt);
92+
93+
msc313e_timer_stop(timer_of_base(timer));
94+
95+
return 0;
96+
}
97+
98+
static int msc313e_timer_clkevt_set_oneshot(struct clock_event_device *evt)
99+
{
100+
struct timer_of *timer = to_timer_of(evt);
101+
102+
msc313e_timer_stop(timer_of_base(timer));
103+
msc313e_timer_start(timer_of_base(timer), false);
104+
105+
return 0;
106+
}
107+
108+
static int msc313e_timer_clkevt_set_periodic(struct clock_event_device *evt)
109+
{
110+
struct timer_of *timer = to_timer_of(evt);
111+
112+
msc313e_timer_stop(timer_of_base(timer));
113+
msc313e_timer_setup(timer_of_base(timer), timer_of_period(timer));
114+
msc313e_timer_start(timer_of_base(timer), true);
115+
116+
return 0;
117+
}
118+
119+
static int msc313e_timer_clkevt_next_event(unsigned long evt, struct clock_event_device *clkevt)
120+
{
121+
struct timer_of *timer = to_timer_of(clkevt);
122+
123+
msc313e_timer_stop(timer_of_base(timer));
124+
msc313e_timer_setup(timer_of_base(timer), evt);
125+
msc313e_timer_start(timer_of_base(timer), false);
126+
127+
return 0;
128+
}
129+
130+
static irqreturn_t msc313e_timer_clkevt_irq(int irq, void *dev_id)
131+
{
132+
struct clock_event_device *evt = dev_id;
133+
134+
evt->event_handler(evt);
135+
136+
return IRQ_HANDLED;
137+
}
138+
139+
static u64 msc313e_timer_clksrc_read(struct clocksource *cs)
140+
{
141+
return msc313e_timer_current_value(msc313e_clksrc) & cs->mask;
142+
}
143+
144+
#ifdef CONFIG_ARM
145+
static unsigned long msc313e_read_delay_timer_read(void)
146+
{
147+
return msc313e_timer_current_value(msc313e_delay.base);
148+
}
149+
#endif
150+
151+
static u64 msc313e_timer_sched_clock_read(void)
152+
{
153+
return msc313e_timer_current_value(msc313e_clksrc);
154+
}
155+
156+
static struct clock_event_device msc313e_clkevt = {
157+
.name = TIMER_NAME,
158+
.rating = 300,
159+
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
160+
.set_state_shutdown = msc313e_timer_clkevt_shutdown,
161+
.set_state_periodic = msc313e_timer_clkevt_set_periodic,
162+
.set_state_oneshot = msc313e_timer_clkevt_set_oneshot,
163+
.tick_resume = msc313e_timer_clkevt_shutdown,
164+
.set_next_event = msc313e_timer_clkevt_next_event,
165+
};
166+
167+
static int __init msc313e_clkevt_init(struct device_node *np)
168+
{
169+
int ret;
170+
struct timer_of *to;
171+
172+
to = kzalloc(sizeof(*to), GFP_KERNEL);
173+
if (!to)
174+
return -ENOMEM;
175+
176+
to->flags = TIMER_OF_IRQ | TIMER_OF_CLOCK | TIMER_OF_BASE;
177+
to->of_irq.handler = msc313e_timer_clkevt_irq;
178+
ret = timer_of_init(np, to);
179+
if (ret)
180+
return ret;
181+
182+
msc313e_clkevt.cpumask = cpu_possible_mask;
183+
msc313e_clkevt.irq = to->of_irq.irq;
184+
to->clkevt = msc313e_clkevt;
185+
186+
clockevents_config_and_register(&to->clkevt, timer_of_rate(to),
187+
TIMER_SYNC_TICKS, 0xffffffff);
188+
return 0;
189+
}
190+
191+
static int __init msc313e_clksrc_init(struct device_node *np)
192+
{
193+
struct timer_of to = { 0 };
194+
int ret;
195+
u16 reg;
196+
197+
to.flags = TIMER_OF_BASE | TIMER_OF_CLOCK;
198+
ret = timer_of_init(np, &to);
199+
if (ret)
200+
return ret;
201+
202+
msc313e_clksrc = timer_of_base(&to);
203+
reg = readw(msc313e_clksrc + MSC313E_REG_CTRL);
204+
reg |= MSC313E_REG_CTRL_TIMER_EN;
205+
writew(reg, msc313e_clksrc + MSC313E_REG_CTRL);
206+
207+
#ifdef CONFIG_ARM
208+
msc313e_delay.base = timer_of_base(&to);
209+
msc313e_delay.delay.read_current_timer = msc313e_read_delay_timer_read;
210+
msc313e_delay.delay.freq = timer_of_rate(&to);
211+
212+
register_current_timer_delay(&msc313e_delay.delay);
213+
#endif
214+
215+
sched_clock_register(msc313e_timer_sched_clock_read, 32, timer_of_rate(&to));
216+
return clocksource_mmio_init(timer_of_base(&to), TIMER_NAME, timer_of_rate(&to), 300, 32,
217+
msc313e_timer_clksrc_read);
218+
}
219+
220+
static int __init msc313e_timer_init(struct device_node *np)
221+
{
222+
int ret = 0;
223+
static int num_called;
224+
225+
switch (num_called) {
226+
case 0:
227+
ret = msc313e_clksrc_init(np);
228+
if (ret)
229+
return ret;
230+
break;
231+
232+
default:
233+
ret = msc313e_clkevt_init(np);
234+
if (ret)
235+
return ret;
236+
break;
237+
}
238+
239+
num_called++;
240+
241+
return 0;
242+
}
243+
244+
TIMER_OF_DECLARE(msc313, "mstar,msc313e-timer", msc313e_timer_init);

0 commit comments

Comments
 (0)