18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2015 Numascale AS. All rights reserved.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/clockchips.h>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <asm/irq.h>
108c2ecf20Sopenharmony_ci#include <asm/numachip/numachip.h>
118c2ecf20Sopenharmony_ci#include <asm/numachip/numachip_csr.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(struct clock_event_device, numachip2_ced);
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_cistatic cycles_t numachip2_timer_read(struct clocksource *cs)
168c2ecf20Sopenharmony_ci{
178c2ecf20Sopenharmony_ci	return numachip2_read64_lcsr(NUMACHIP2_TIMER_NOW);
188c2ecf20Sopenharmony_ci}
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistatic struct clocksource numachip2_clocksource = {
218c2ecf20Sopenharmony_ci	.name            = "numachip2",
228c2ecf20Sopenharmony_ci	.rating          = 295,
238c2ecf20Sopenharmony_ci	.read            = numachip2_timer_read,
248c2ecf20Sopenharmony_ci	.mask            = CLOCKSOURCE_MASK(64),
258c2ecf20Sopenharmony_ci	.flags           = CLOCK_SOURCE_IS_CONTINUOUS,
268c2ecf20Sopenharmony_ci	.mult            = 1,
278c2ecf20Sopenharmony_ci	.shift           = 0,
288c2ecf20Sopenharmony_ci};
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic int numachip2_set_next_event(unsigned long delta, struct clock_event_device *ced)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	numachip2_write64_lcsr(NUMACHIP2_TIMER_DEADLINE + numachip2_timer(),
338c2ecf20Sopenharmony_ci		delta);
348c2ecf20Sopenharmony_ci	return 0;
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic const struct clock_event_device numachip2_clockevent __initconst = {
388c2ecf20Sopenharmony_ci	.name            = "numachip2",
398c2ecf20Sopenharmony_ci	.rating          = 400,
408c2ecf20Sopenharmony_ci	.set_next_event  = numachip2_set_next_event,
418c2ecf20Sopenharmony_ci	.features        = CLOCK_EVT_FEAT_ONESHOT,
428c2ecf20Sopenharmony_ci	.mult            = 1,
438c2ecf20Sopenharmony_ci	.shift           = 0,
448c2ecf20Sopenharmony_ci	.min_delta_ns    = 1250,
458c2ecf20Sopenharmony_ci	.min_delta_ticks = 1250,
468c2ecf20Sopenharmony_ci	.max_delta_ns    = LONG_MAX,
478c2ecf20Sopenharmony_ci	.max_delta_ticks = LONG_MAX,
488c2ecf20Sopenharmony_ci};
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic void numachip_timer_interrupt(void)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	struct clock_event_device *ced = this_cpu_ptr(&numachip2_ced);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	ced->event_handler(ced);
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic __init void numachip_timer_each(struct work_struct *work)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	unsigned local_apicid = __this_cpu_read(x86_cpu_to_apicid) & 0xff;
608c2ecf20Sopenharmony_ci	struct clock_event_device *ced = this_cpu_ptr(&numachip2_ced);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	/* Setup IPI vector to local core and relative timing mode */
638c2ecf20Sopenharmony_ci	numachip2_write64_lcsr(NUMACHIP2_TIMER_INT + numachip2_timer(),
648c2ecf20Sopenharmony_ci		(3 << 22) | (X86_PLATFORM_IPI_VECTOR << 14) |
658c2ecf20Sopenharmony_ci		(local_apicid << 6));
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	*ced = numachip2_clockevent;
688c2ecf20Sopenharmony_ci	ced->cpumask = cpumask_of(smp_processor_id());
698c2ecf20Sopenharmony_ci	clockevents_register_device(ced);
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic int __init numachip_timer_init(void)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	if (numachip_system != 2)
758c2ecf20Sopenharmony_ci		return -ENODEV;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	/* Reset timer */
788c2ecf20Sopenharmony_ci	numachip2_write64_lcsr(NUMACHIP2_TIMER_RESET, 0);
798c2ecf20Sopenharmony_ci	clocksource_register_hz(&numachip2_clocksource, NSEC_PER_SEC);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	/* Setup per-cpu clockevents */
828c2ecf20Sopenharmony_ci	x86_platform_ipi_callback = numachip_timer_interrupt;
838c2ecf20Sopenharmony_ci	schedule_on_each_cpu(&numachip_timer_each);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	return 0;
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ciarch_initcall(numachip_timer_init);
89