File Index
Files and directories
Sign in
FILE

time.c

File preview with syntax highlighting.

linux-6.19.14/arch/loongarch/kernel/time.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Common time service routines for LoongArch machines.
  4 *
  5 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
  6 */
  7#include <linux/clockchips.h>
  8#include <linux/cpuhotplug.h>
  9#include <linux/delay.h>
 10#include <linux/export.h>
 11#include <linux/init.h>
 12#include <linux/interrupt.h>
 13#include <linux/kernel.h>
 14#include <linux/sched_clock.h>
 15#include <linux/spinlock.h>
 16
 17#include <asm/cpu-features.h>
 18#include <asm/loongarch.h>
 19#include <asm/paravirt.h>
 20#include <asm/time.h>
 21#include <asm/timex.h>
 22
 23u64 cpu_clock_freq;
 24EXPORT_SYMBOL(cpu_clock_freq);
 25u64 const_clock_freq;
 26EXPORT_SYMBOL(const_clock_freq);
 27
 28static DEFINE_RAW_SPINLOCK(state_lock);
 29static DEFINE_PER_CPU(struct clock_event_device, constant_clockevent_device);
 30
 31static void constant_event_handler(struct clock_event_device *dev)
 32{
 33}
 34
 35static irqreturn_t constant_timer_interrupt(int irq, void *data)
 36{
 37	int cpu = smp_processor_id();
 38	struct clock_event_device *cd;
 39
 40	/* Clear Timer Interrupt */
 41	write_csr_tintclear(CSR_TINTCLR_TI);
 42	cd = &per_cpu(constant_clockevent_device, cpu);
 43	cd->event_handler(cd);
 44
 45	return IRQ_HANDLED;
 46}
 47
 48static int constant_set_state_oneshot(struct clock_event_device *evt)
 49{
 50	unsigned long timer_config;
 51
 52	raw_spin_lock(&state_lock);
 53
 54	timer_config = csr_read(LOONGARCH_CSR_TCFG);
 55	timer_config |= CSR_TCFG_EN;
 56	timer_config &= ~CSR_TCFG_PERIOD;
 57	csr_write(timer_config, LOONGARCH_CSR_TCFG);
 58
 59	raw_spin_unlock(&state_lock);
 60
 61	return 0;
 62}
 63
 64static int constant_set_state_periodic(struct clock_event_device *evt)
 65{
 66	unsigned long timer_config;
 67	u64 period = const_clock_freq;
 68
 69	raw_spin_lock(&state_lock);
 70
 71	do_div(period, HZ);
 72	timer_config = period & CSR_TCFG_VAL;
 73	timer_config |= (CSR_TCFG_PERIOD | CSR_TCFG_EN);
 74	csr_write(timer_config, LOONGARCH_CSR_TCFG);
 75
 76	raw_spin_unlock(&state_lock);
 77
 78	return 0;
 79}
 80
 81static int constant_set_state_shutdown(struct clock_event_device *evt)
 82{
 83	unsigned long timer_config;
 84
 85	raw_spin_lock(&state_lock);
 86
 87	timer_config = csr_read(LOONGARCH_CSR_TCFG);
 88	timer_config &= ~CSR_TCFG_EN;
 89	csr_write(timer_config, LOONGARCH_CSR_TCFG);
 90
 91	raw_spin_unlock(&state_lock);
 92
 93	return 0;
 94}
 95
 96static int constant_timer_next_event(unsigned long delta, struct clock_event_device *evt)
 97{
 98	unsigned long timer_config;
 99
100	delta &= CSR_TCFG_VAL;
101	timer_config = delta | CSR_TCFG_EN;
102	csr_write(timer_config, LOONGARCH_CSR_TCFG);
103
104	return 0;
105}
106
107static int arch_timer_starting(unsigned int cpu)
108{
109	set_csr_ecfg(ECFGF_TIMER);
110
111	return 0;
112}
113
114static int arch_timer_dying(unsigned int cpu)
115{
116	/* Clear Timer Interrupt */
117	write_csr_tintclear(CSR_TINTCLR_TI);
118
119	return 0;
120}
121
122static unsigned long get_loops_per_jiffy(void)
123{
124	u64 lpj = const_clock_freq;
125
126	do_div(lpj, HZ);
127
128	return lpj;
129}
130
131static long init_offset;
132
133void save_counter(void)
134{
135	init_offset = get_cycles();
136}
137
138void sync_counter(void)
139{
140	/* Ensure counter begin at 0 */
141	csr_write(init_offset, LOONGARCH_CSR_CNTC);
142}
143
144int constant_clockevent_init(void)
145{
146	unsigned int cpu = smp_processor_id();
147#ifdef CONFIG_PREEMPT_RT
148	unsigned long min_delta = 100;
149#else
150	unsigned long min_delta = 1000;
151#endif
152	unsigned long max_delta = GENMASK_ULL(boot_cpu_data.timerbits, 0);
153	struct clock_event_device *cd;
154	static int irq = 0, timer_irq_installed = 0;
155
156	if (!timer_irq_installed) {
157		irq = get_percpu_irq(INT_TI);
158		if (irq < 0)
159			pr_err("Failed to map irq %d (timer)\n", irq);
160	}
161
162	cd = &per_cpu(constant_clockevent_device, cpu);
163
164	cd->name = "Constant";
165	cd->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_PERCPU;
166
167	cd->irq = irq;
168	cd->rating = 320;
169	cd->cpumask = cpumask_of(cpu);
170	cd->set_state_oneshot = constant_set_state_oneshot;
171	cd->set_state_oneshot_stopped = constant_set_state_shutdown;
172	cd->set_state_periodic = constant_set_state_periodic;
173	cd->set_state_shutdown = constant_set_state_shutdown;
174	cd->set_next_event = constant_timer_next_event;
175	cd->event_handler = constant_event_handler;
176
177	clockevents_config_and_register(cd, const_clock_freq, min_delta, max_delta);
178
179	if (timer_irq_installed)
180		return 0;
181
182	timer_irq_installed = 1;
183
184	sync_counter();
185
186	if (request_irq(irq, constant_timer_interrupt, IRQF_PERCPU | IRQF_TIMER, "timer", NULL))
187		pr_err("Failed to request irq %d (timer)\n", irq);
188
189	lpj_fine = get_loops_per_jiffy();
190	pr_info("Constant clock event device register\n");
191
192	cpuhp_setup_state(CPUHP_AP_LOONGARCH_ARCH_TIMER_STARTING,
193			  "clockevents/loongarch/timer:starting",
194			  arch_timer_starting, arch_timer_dying);
195
196	return 0;
197}
198
199static u64 read_const_counter(struct clocksource *clk)
200{
201	return get_cycles64();
202}
203
204static noinstr u64 sched_clock_read(void)
205{
206	return get_cycles64();
207}
208
209static struct clocksource clocksource_const = {
210	.name = "Constant",
211	.rating = 400,
212	.read = read_const_counter,
213	.mask = CLOCKSOURCE_MASK(64),
214	.flags = CLOCK_SOURCE_IS_CONTINUOUS,
215#ifdef CONFIG_GENERIC_GETTIMEOFDAY
216	.vdso_clock_mode = VDSO_CLOCKMODE_CPU,
217#endif
218};
219
220int __init constant_clocksource_init(void)
221{
222	int res;
223	unsigned long freq = const_clock_freq;
224
225	res = clocksource_register_hz(&clocksource_const, freq);
226
227	sched_clock_register(sched_clock_read, 64, freq);
228
229	pr_info("Constant clock source device register\n");
230
231	return res;
232}
233
234void __init time_init(void)
235{
236	if (!cpu_has_cpucfg)
237		const_clock_freq = cpu_clock_freq;
238	else
239		const_clock_freq = calc_const_freq();
240
241	init_offset = -(get_cycles() - csr_read(LOONGARCH_CSR_CNTC));
242
243	constant_clockevent_init();
244	constant_clocksource_init();
245	pv_time_init();
246}
~/ Read-only file preview.