Blame view
drivers/clocksource/timer-qcom.c
5.99 KB
9c92ab619 treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-only |
dd15ab814 msm: timer: Use G... |
2 |
/* |
3e4ea3728 [ARM] msm: irq an... |
3 4 |
* * Copyright (C) 2007 Google, Inc. |
3f8e8cee2 clocksource: qcom... |
5 |
* Copyright (c) 2009-2012,2014, The Linux Foundation. All rights reserved. |
3e4ea3728 [ARM] msm: irq an... |
6 |
*/ |
4a1840755 msm: timer: Clean... |
7 8 |
#include <linux/clocksource.h> #include <linux/clockchips.h> |
4d70c59bb ARM: msm: Divorce... |
9 |
#include <linux/cpu.h> |
3e4ea3728 [ARM] msm: irq an... |
10 |
#include <linux/init.h> |
3e4ea3728 [ARM] msm: irq an... |
11 12 |
#include <linux/interrupt.h> #include <linux/irq.h> |
fced80c73 [ARM] Convert asm... |
13 |
#include <linux/io.h> |
6e3321631 ARM: msm: Add DT ... |
14 15 16 |
#include <linux/of.h> #include <linux/of_address.h> #include <linux/of_irq.h> |
38ff87f77 sched_clock: Make... |
17 |
#include <linux/sched_clock.h> |
3e4ea3728 [ARM] msm: irq an... |
18 |
|
013be5adf clocksource: qcom... |
19 |
#include <asm/delay.h> |
e25e3d1fe ARM: msm: Wait fo... |
20 21 22 23 24 25 26 27 28 |
#define TIMER_MATCH_VAL 0x0000 #define TIMER_COUNT_VAL 0x0004 #define TIMER_ENABLE 0x0008 #define TIMER_ENABLE_CLR_ON_MATCH_EN BIT(1) #define TIMER_ENABLE_EN BIT(0) #define TIMER_CLEAR 0x000C #define DGT_CLK_CTL 0x10 #define DGT_CLK_CTL_DIV_4 0x3 #define TIMER_STS_GPT0_CLR_PEND BIT(10) |
3e4ea3728 [ARM] msm: irq an... |
29 30 |
#define GPT_HZ 32768 |
672039f03 msm: timer: suppo... |
31 |
|
2a00c1068 msm: timer: Remov... |
32 |
static void __iomem *event_base; |
e25e3d1fe ARM: msm: Wait fo... |
33 |
static void __iomem *sts_base; |
a850c3f64 msm: timer: Fix O... |
34 |
|
3e4ea3728 [ARM] msm: irq an... |
35 36 |
static irqreturn_t msm_timer_interrupt(int irq, void *dev_id) { |
4d70c59bb ARM: msm: Divorce... |
37 |
struct clock_event_device *evt = dev_id; |
a850c3f64 msm: timer: Fix O... |
38 |
/* Stop the timer tick */ |
736b2df2a clockevents/drive... |
39 |
if (clockevent_state_oneshot(evt)) { |
2a00c1068 msm: timer: Remov... |
40 |
u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE); |
a850c3f64 msm: timer: Fix O... |
41 |
ctrl &= ~TIMER_ENABLE_EN; |
2a00c1068 msm: timer: Remov... |
42 |
writel_relaxed(ctrl, event_base + TIMER_ENABLE); |
a850c3f64 msm: timer: Fix O... |
43 |
} |
3e4ea3728 [ARM] msm: irq an... |
44 45 46 |
evt->event_handler(evt); return IRQ_HANDLED; } |
3e4ea3728 [ARM] msm: irq an... |
47 48 49 |
static int msm_timer_set_next_event(unsigned long cycles, struct clock_event_device *evt) { |
2a00c1068 msm: timer: Remov... |
50 |
u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE); |
3e4ea3728 [ARM] msm: irq an... |
51 |
|
4080d2d11 ARM: msm: Stop co... |
52 53 54 55 |
ctrl &= ~TIMER_ENABLE_EN; writel_relaxed(ctrl, event_base + TIMER_ENABLE); writel_relaxed(ctrl, event_base + TIMER_CLEAR); |
2a00c1068 msm: timer: Remov... |
56 |
writel_relaxed(cycles, event_base + TIMER_MATCH_VAL); |
e25e3d1fe ARM: msm: Wait fo... |
57 58 59 60 |
if (sts_base) while (readl_relaxed(sts_base) & TIMER_STS_GPT0_CLR_PEND) cpu_relax(); |
2a00c1068 msm: timer: Remov... |
61 |
writel_relaxed(ctrl | TIMER_ENABLE_EN, event_base + TIMER_ENABLE); |
3e4ea3728 [ARM] msm: irq an... |
62 63 |
return 0; } |
736b2df2a clockevents/drive... |
64 |
static int msm_timer_shutdown(struct clock_event_device *evt) |
3e4ea3728 [ARM] msm: irq an... |
65 |
{ |
a850c3f64 msm: timer: Fix O... |
66 |
u32 ctrl; |
2a00c1068 msm: timer: Remov... |
67 |
ctrl = readl_relaxed(event_base + TIMER_ENABLE); |
a850c3f64 msm: timer: Fix O... |
68 |
ctrl &= ~(TIMER_ENABLE_EN | TIMER_ENABLE_CLR_ON_MATCH_EN); |
2a00c1068 msm: timer: Remov... |
69 |
writel_relaxed(ctrl, event_base + TIMER_ENABLE); |
736b2df2a clockevents/drive... |
70 |
return 0; |
3e4ea3728 [ARM] msm: irq an... |
71 |
} |
4d70c59bb ARM: msm: Divorce... |
72 |
static struct clock_event_device __percpu *msm_evt; |
2a00c1068 msm: timer: Remov... |
73 74 |
static void __iomem *source_base; |
a5a1d1c29 clocksource: Use ... |
75 |
static notrace u64 msm_read_timer_count(struct clocksource *cs) |
2a00c1068 msm: timer: Remov... |
76 |
{ |
2081a6b57 msm: timer: Remov... |
77 78 |
return readl_relaxed(source_base + TIMER_COUNT_VAL); } |
2a00c1068 msm: timer: Remov... |
79 80 81 82 |
static struct clocksource msm_clocksource = { .name = "dg_timer", .rating = 300, .read = msm_read_timer_count, |
2081a6b57 msm: timer: Remov... |
83 |
.mask = CLOCKSOURCE_MASK(32), |
2a00c1068 msm: timer: Remov... |
84 |
.flags = CLOCK_SOURCE_IS_CONTINUOUS, |
3e4ea3728 [ARM] msm: irq an... |
85 |
}; |
4d70c59bb ARM: msm: Divorce... |
86 87 |
static int msm_timer_irq; static int msm_timer_has_ppi; |
b04041655 clocksource/qcom-... |
88 |
static int msm_local_timer_starting_cpu(unsigned int cpu) |
5ca709c16 ARM: local timers... |
89 |
{ |
b04041655 clocksource/qcom-... |
90 |
struct clock_event_device *evt = per_cpu_ptr(msm_evt, cpu); |
4d70c59bb ARM: msm: Divorce... |
91 92 93 94 95 96 |
int err; evt->irq = msm_timer_irq; evt->name = "msm_timer"; evt->features = CLOCK_EVT_FEAT_ONESHOT; evt->rating = 200; |
736b2df2a clockevents/drive... |
97 98 99 |
evt->set_state_shutdown = msm_timer_shutdown; evt->set_state_oneshot = msm_timer_shutdown; evt->tick_resume = msm_timer_shutdown; |
5ca709c16 ARM: local timers... |
100 |
evt->set_next_event = msm_timer_set_next_event; |
4d70c59bb ARM: msm: Divorce... |
101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
evt->cpumask = cpumask_of(cpu); clockevents_config_and_register(evt, GPT_HZ, 4, 0xffffffff); if (msm_timer_has_ppi) { enable_percpu_irq(evt->irq, IRQ_TYPE_EDGE_RISING); } else { err = request_irq(evt->irq, msm_timer_interrupt, IRQF_TIMER | IRQF_NOBALANCING | IRQF_TRIGGER_RISING, "gp_timer", evt); if (err) pr_err("request_irq failed "); } |
5ca709c16 ARM: local timers... |
115 |
|
5ca709c16 ARM: local timers... |
116 117 |
return 0; } |
b04041655 clocksource/qcom-... |
118 |
static int msm_local_timer_dying_cpu(unsigned int cpu) |
5ca709c16 ARM: local timers... |
119 |
{ |
b04041655 clocksource/qcom-... |
120 |
struct clock_event_device *evt = per_cpu_ptr(msm_evt, cpu); |
736b2df2a clockevents/drive... |
121 |
evt->set_state_shutdown(evt); |
5ca709c16 ARM: local timers... |
122 |
disable_percpu_irq(evt->irq); |
b04041655 clocksource/qcom-... |
123 |
return 0; |
5ca709c16 ARM: local timers... |
124 |
} |
6aa16a265 ARM: msm: Switch ... |
125 |
static u64 notrace msm_sched_clock_read(void) |
f8e56c42e msm: timer: Suppo... |
126 127 128 |
{ return msm_clocksource.read(&msm_clocksource); } |
013be5adf clocksource: qcom... |
129 130 131 132 133 134 135 136 |
static unsigned long msm_read_current_timer(void) { return msm_clocksource.read(&msm_clocksource); } static struct delay_timer msm_delay_timer = { .read_current_timer = msm_read_current_timer, }; |
ab51189ca clocksource/drive... |
137 |
static int __init msm_timer_init(u32 dgt_hz, int sched_bits, int irq, |
4312a7ef9 ARM: msm: Allow t... |
138 |
bool percpu) |
3e4ea3728 [ARM] msm: irq an... |
139 |
{ |
2a00c1068 msm: timer: Remov... |
140 |
struct clocksource *cs = &msm_clocksource; |
4d70c59bb ARM: msm: Divorce... |
141 142 143 144 145 146 147 148 149 150 151 |
int res = 0; msm_timer_irq = irq; msm_timer_has_ppi = percpu; msm_evt = alloc_percpu(struct clock_event_device); if (!msm_evt) { pr_err("memory allocation failed for clockevents "); goto err; } |
3e4ea3728 [ARM] msm: irq an... |
152 |
|
4d70c59bb ARM: msm: Divorce... |
153 154 155 |
if (percpu) res = request_percpu_irq(irq, msm_timer_interrupt, "gp_timer", msm_evt); |
dd15ab814 msm: timer: Use G... |
156 |
|
4d70c59bb ARM: msm: Divorce... |
157 158 159 160 |
if (res) { pr_err("request_percpu_irq failed "); } else { |
b04041655 clocksource/qcom-... |
161 162 |
/* Install and invoke hotplug callbacks */ res = cpuhp_setup_state(CPUHP_AP_QCOM_TIMER_STARTING, |
73c1b41e6 cpu/hotplug: Clea... |
163 |
"clockevents/qcom/timer:starting", |
b04041655 clocksource/qcom-... |
164 165 |
msm_local_timer_starting_cpu, msm_local_timer_dying_cpu); |
4d70c59bb ARM: msm: Divorce... |
166 167 |
if (res) { free_percpu_irq(irq, msm_evt); |
dd15ab814 msm: timer: Use G... |
168 |
goto err; |
28af690a2 ARM: gic, local t... |
169 |
} |
3e4ea3728 [ARM] msm: irq an... |
170 |
} |
dd15ab814 msm: timer: Use G... |
171 |
|
dd15ab814 msm: timer: Use G... |
172 |
err: |
2a00c1068 msm: timer: Remov... |
173 |
writel_relaxed(TIMER_ENABLE_EN, source_base + TIMER_ENABLE); |
2081a6b57 msm: timer: Remov... |
174 |
res = clocksource_register_hz(cs, dgt_hz); |
dd15ab814 msm: timer: Use G... |
175 |
if (res) |
2a00c1068 msm: timer: Remov... |
176 177 |
pr_err("clocksource_register failed "); |
6aa16a265 ARM: msm: Switch ... |
178 |
sched_clock_register(msm_sched_clock_read, sched_bits, dgt_hz); |
013be5adf clocksource: qcom... |
179 180 |
msm_delay_timer.freq = dgt_hz; register_current_timer_delay(&msm_delay_timer); |
ab51189ca clocksource/drive... |
181 182 |
return res; |
3e4ea3728 [ARM] msm: irq an... |
183 |
} |
ab51189ca clocksource/drive... |
184 |
static int __init msm_dt_timer_init(struct device_node *np) |
6e3321631 ARM: msm: Add DT ... |
185 |
{ |
6e3321631 ARM: msm: Add DT ... |
186 |
u32 freq; |
ab51189ca clocksource/drive... |
187 |
int irq, ret; |
6e3321631 ARM: msm: Add DT ... |
188 189 |
struct resource res; u32 percpu_offset; |
eebdb0c1e ARM: msm: Rework ... |
190 191 |
void __iomem *base; void __iomem *cpu0_base; |
6e3321631 ARM: msm: Add DT ... |
192 |
|
eebdb0c1e ARM: msm: Rework ... |
193 194 |
base = of_iomap(np, 0); if (!base) { |
6e3321631 ARM: msm: Add DT ... |
195 196 |
pr_err("Failed to map event base "); |
ab51189ca clocksource/drive... |
197 |
return -ENXIO; |
6e3321631 ARM: msm: Add DT ... |
198 |
} |
eebdb0c1e ARM: msm: Rework ... |
199 200 |
/* We use GPT0 for the clockevent */ irq = irq_of_parse_and_map(np, 1); |
6e3321631 ARM: msm: Add DT ... |
201 202 203 |
if (irq <= 0) { pr_err("Can't get irq "); |
ab51189ca clocksource/drive... |
204 |
return -EINVAL; |
6e3321631 ARM: msm: Add DT ... |
205 |
} |
6e3321631 ARM: msm: Add DT ... |
206 |
|
eebdb0c1e ARM: msm: Rework ... |
207 |
/* We use CPU0's DGT for the clocksource */ |
6e3321631 ARM: msm: Add DT ... |
208 209 |
if (of_property_read_u32(np, "cpu-offset", &percpu_offset)) percpu_offset = 0; |
ab51189ca clocksource/drive... |
210 211 |
ret = of_address_to_resource(np, 0, &res); if (ret) { |
6e3321631 ARM: msm: Add DT ... |
212 213 |
pr_err("Failed to parse DGT resource "); |
ab51189ca clocksource/drive... |
214 |
return ret; |
6e3321631 ARM: msm: Add DT ... |
215 |
} |
eebdb0c1e ARM: msm: Rework ... |
216 217 |
cpu0_base = ioremap(res.start + percpu_offset, resource_size(&res)); if (!cpu0_base) { |
6e3321631 ARM: msm: Add DT ... |
218 219 |
pr_err("Failed to map source base "); |
ab51189ca clocksource/drive... |
220 |
return -EINVAL; |
6e3321631 ARM: msm: Add DT ... |
221 |
} |
6e3321631 ARM: msm: Add DT ... |
222 223 224 |
if (of_property_read_u32(np, "clock-frequency", &freq)) { pr_err("Unknown frequency "); |
ab51189ca clocksource/drive... |
225 |
return -EINVAL; |
6e3321631 ARM: msm: Add DT ... |
226 |
} |
6e3321631 ARM: msm: Add DT ... |
227 |
|
eebdb0c1e ARM: msm: Rework ... |
228 |
event_base = base + 0x4; |
e25e3d1fe ARM: msm: Wait fo... |
229 |
sts_base = base + 0x88; |
eebdb0c1e ARM: msm: Rework ... |
230 231 232 |
source_base = cpu0_base + 0x24; freq /= 4; writel_relaxed(DGT_CLK_CTL_DIV_4, source_base + DGT_CLK_CTL); |
ab51189ca clocksource/drive... |
233 |
return msm_timer_init(freq, 32, irq, !!percpu_offset); |
6e3321631 ARM: msm: Add DT ... |
234 |
} |
172733959 clocksource/drive... |
235 236 |
TIMER_OF_DECLARE(kpss_timer, "qcom,kpss-timer", msm_dt_timer_init); TIMER_OF_DECLARE(scss_timer, "qcom,scss-timer", msm_dt_timer_init); |