Skip to content

Commit c92e7ef

Browse files
viviergeertu
authored andcommitted
clocksource/drivers: Add a goldfish-timer clocksource
Add a clocksource based on the goldfish-rtc device. Move the timer register definition to <clocksource/timer-goldfish.h> This kernel implementation is based on the QEMU upstream implementation: https://git.qemu.org/?p=qemu.git;a=blob_plain;f=hw/rtc/goldfish_rtc.c goldfish-timer is a high-precision signed 64-bit nanosecond timer. It is part of the 'goldfish' virtual hardware platform used to run some emulated Android systems under QEMU. This timer only supports oneshot event. Signed-off-by: Laurent Vivier <[email protected]> Acked-by: Daniel Lezcano <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Geert Uytterhoeven <[email protected]>
1 parent 3378c7f commit c92e7ef

File tree

5 files changed

+193
-12
lines changed

5 files changed

+193
-12
lines changed

drivers/clocksource/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,4 +711,11 @@ config MICROCHIP_PIT64B
711711
modes and high resolution. It is used as a clocksource
712712
and a clockevent.
713713

714+
config GOLDFISH_TIMER
715+
bool "Clocksource using goldfish-rtc"
716+
depends on M68K || COMPILE_TEST
717+
depends on RTC_DRV_GOLDFISH
718+
help
719+
Support for the timer/counter of goldfish-rtc
720+
714721
endmenu

drivers/clocksource/Makefile

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

drivers/clocksource/timer-goldfish.c

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
#include <linux/interrupt.h>
4+
#include <linux/ioport.h>
5+
#include <linux/clocksource.h>
6+
#include <linux/clockchips.h>
7+
#include <linux/module.h>
8+
#include <linux/slab.h>
9+
#include <linux/goldfish.h>
10+
#include <clocksource/timer-goldfish.h>
11+
12+
struct goldfish_timer {
13+
struct clocksource cs;
14+
struct clock_event_device ced;
15+
struct resource res;
16+
void __iomem *base;
17+
};
18+
19+
static struct goldfish_timer *ced_to_gf(struct clock_event_device *ced)
20+
{
21+
return container_of(ced, struct goldfish_timer, ced);
22+
}
23+
24+
static struct goldfish_timer *cs_to_gf(struct clocksource *cs)
25+
{
26+
return container_of(cs, struct goldfish_timer, cs);
27+
}
28+
29+
static u64 goldfish_timer_read(struct clocksource *cs)
30+
{
31+
struct goldfish_timer *timerdrv = cs_to_gf(cs);
32+
void __iomem *base = timerdrv->base;
33+
u32 time_low, time_high;
34+
u64 ticks;
35+
36+
/*
37+
* time_low: get low bits of current time and update time_high
38+
* time_high: get high bits of time at last time_low read
39+
*/
40+
time_low = gf_ioread32(base + TIMER_TIME_LOW);
41+
time_high = gf_ioread32(base + TIMER_TIME_HIGH);
42+
43+
ticks = ((u64)time_high << 32) | time_low;
44+
45+
return ticks;
46+
}
47+
48+
static int goldfish_timer_set_oneshot(struct clock_event_device *evt)
49+
{
50+
struct goldfish_timer *timerdrv = ced_to_gf(evt);
51+
void __iomem *base = timerdrv->base;
52+
53+
gf_iowrite32(0, base + TIMER_ALARM_HIGH);
54+
gf_iowrite32(0, base + TIMER_ALARM_LOW);
55+
gf_iowrite32(1, base + TIMER_IRQ_ENABLED);
56+
57+
return 0;
58+
}
59+
60+
static int goldfish_timer_shutdown(struct clock_event_device *evt)
61+
{
62+
struct goldfish_timer *timerdrv = ced_to_gf(evt);
63+
void __iomem *base = timerdrv->base;
64+
65+
gf_iowrite32(0, base + TIMER_IRQ_ENABLED);
66+
67+
return 0;
68+
}
69+
70+
static int goldfish_timer_next_event(unsigned long delta,
71+
struct clock_event_device *evt)
72+
{
73+
struct goldfish_timer *timerdrv = ced_to_gf(evt);
74+
void __iomem *base = timerdrv->base;
75+
u64 now;
76+
77+
now = goldfish_timer_read(&timerdrv->cs);
78+
79+
now += delta;
80+
81+
gf_iowrite32(upper_32_bits(now), base + TIMER_ALARM_HIGH);
82+
gf_iowrite32(lower_32_bits(now), base + TIMER_ALARM_LOW);
83+
84+
return 0;
85+
}
86+
87+
static irqreturn_t goldfish_timer_irq(int irq, void *dev_id)
88+
{
89+
struct goldfish_timer *timerdrv = dev_id;
90+
struct clock_event_device *evt = &timerdrv->ced;
91+
void __iomem *base = timerdrv->base;
92+
93+
gf_iowrite32(1, base + TIMER_CLEAR_INTERRUPT);
94+
95+
evt->event_handler(evt);
96+
97+
return IRQ_HANDLED;
98+
}
99+
100+
int __init goldfish_timer_init(int irq, void __iomem *base)
101+
{
102+
struct goldfish_timer *timerdrv;
103+
int ret;
104+
105+
timerdrv = kzalloc(sizeof(*timerdrv), GFP_KERNEL);
106+
if (!timerdrv)
107+
return -ENOMEM;
108+
109+
timerdrv->base = base;
110+
111+
timerdrv->ced = (struct clock_event_device){
112+
.name = "goldfish_timer",
113+
.features = CLOCK_EVT_FEAT_ONESHOT,
114+
.set_state_shutdown = goldfish_timer_shutdown,
115+
.set_state_oneshot = goldfish_timer_set_oneshot,
116+
.set_next_event = goldfish_timer_next_event,
117+
};
118+
119+
timerdrv->res = (struct resource){
120+
.name = "goldfish_timer",
121+
.start = (unsigned long)base,
122+
.end = (unsigned long)base + 0xfff,
123+
};
124+
125+
ret = request_resource(&iomem_resource, &timerdrv->res);
126+
if (ret) {
127+
pr_err("Cannot allocate '%s' resource\n", timerdrv->res.name);
128+
return ret;
129+
}
130+
131+
timerdrv->cs = (struct clocksource){
132+
.name = "goldfish_timer",
133+
.rating = 400,
134+
.read = goldfish_timer_read,
135+
.mask = CLOCKSOURCE_MASK(64),
136+
.flags = 0,
137+
.max_idle_ns = LONG_MAX,
138+
};
139+
140+
clocksource_register_hz(&timerdrv->cs, NSEC_PER_SEC);
141+
142+
ret = request_irq(irq, goldfish_timer_irq, IRQF_TIMER,
143+
"goldfish_timer", timerdrv);
144+
if (ret) {
145+
pr_err("Couldn't register goldfish-timer interrupt\n");
146+
return ret;
147+
}
148+
149+
clockevents_config_and_register(&timerdrv->ced, NSEC_PER_SEC,
150+
1, 0xffffffff);
151+
152+
return 0;
153+
}

drivers/rtc/rtc-goldfish.c

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,7 @@
1111
#include <linux/platform_device.h>
1212
#include <linux/rtc.h>
1313
#include <linux/goldfish.h>
14-
15-
#define TIMER_TIME_LOW 0x00 /* get low bits of current time */
16-
/* and update TIMER_TIME_HIGH */
17-
#define TIMER_TIME_HIGH 0x04 /* get high bits of time at last */
18-
/* TIMER_TIME_LOW read */
19-
#define TIMER_ALARM_LOW 0x08 /* set low bits of alarm and */
20-
/* activate it */
21-
#define TIMER_ALARM_HIGH 0x0c /* set high bits of next alarm */
22-
#define TIMER_IRQ_ENABLED 0x10
23-
#define TIMER_CLEAR_ALARM 0x14
24-
#define TIMER_ALARM_STATUS 0x18
25-
#define TIMER_CLEAR_INTERRUPT 0x1c
14+
#include <clocksource/timer-goldfish.h>
2615

2716
struct goldfish_rtc {
2817
void __iomem *base;

include/clocksource/timer-goldfish.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* goldfish-timer clocksource
4+
* Registers definition for the goldfish-timer device
5+
*/
6+
7+
#ifndef _CLOCKSOURCE_TIMER_GOLDFISH_H
8+
#define _CLOCKSOURCE_TIMER_GOLDFISH_H
9+
10+
/*
11+
* TIMER_TIME_LOW get low bits of current time and update TIMER_TIME_HIGH
12+
* TIMER_TIME_HIGH get high bits of time at last TIMER_TIME_LOW read
13+
* TIMER_ALARM_LOW set low bits of alarm and activate it
14+
* TIMER_ALARM_HIGH set high bits of next alarm
15+
* TIMER_IRQ_ENABLED enable alarm interrupt
16+
* TIMER_CLEAR_ALARM disarm an existing alarm
17+
* TIMER_ALARM_STATUS alarm status (running or not)
18+
* TIMER_CLEAR_INTERRUPT clear interrupt
19+
*/
20+
#define TIMER_TIME_LOW 0x00
21+
#define TIMER_TIME_HIGH 0x04
22+
#define TIMER_ALARM_LOW 0x08
23+
#define TIMER_ALARM_HIGH 0x0c
24+
#define TIMER_IRQ_ENABLED 0x10
25+
#define TIMER_CLEAR_ALARM 0x14
26+
#define TIMER_ALARM_STATUS 0x18
27+
#define TIMER_CLEAR_INTERRUPT 0x1c
28+
29+
extern int goldfish_timer_init(int irq, void __iomem *base);
30+
31+
#endif /* _CLOCKSOURCE_TIMER_GOLDFISH_H */

0 commit comments

Comments
 (0)