Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/clocksource/timer-goldfish.c
26278 views
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
}
154
155