162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2015 Numascale AS. All rights reserved.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/clockchips.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <asm/irq.h>
1062306a36Sopenharmony_ci#include <asm/numachip/numachip.h>
1162306a36Sopenharmony_ci#include <asm/numachip/numachip_csr.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_cistatic DEFINE_PER_CPU(struct clock_event_device, numachip2_ced);
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistatic cycles_t numachip2_timer_read(struct clocksource *cs)
1662306a36Sopenharmony_ci{
1762306a36Sopenharmony_ci	return numachip2_read64_lcsr(NUMACHIP2_TIMER_NOW);
1862306a36Sopenharmony_ci}
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic struct clocksource numachip2_clocksource = {
2162306a36Sopenharmony_ci	.name            = "numachip2",
2262306a36Sopenharmony_ci	.rating          = 295,
2362306a36Sopenharmony_ci	.read            = numachip2_timer_read,
2462306a36Sopenharmony_ci	.mask            = CLOCKSOURCE_MASK(64),
2562306a36Sopenharmony_ci	.flags           = CLOCK_SOURCE_IS_CONTINUOUS,
2662306a36Sopenharmony_ci	.mult            = 1,
2762306a36Sopenharmony_ci	.shift           = 0,
2862306a36Sopenharmony_ci};
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic int numachip2_set_next_event(unsigned long delta, struct clock_event_device *ced)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	numachip2_write64_lcsr(NUMACHIP2_TIMER_DEADLINE + numachip2_timer(),
3362306a36Sopenharmony_ci		delta);
3462306a36Sopenharmony_ci	return 0;
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic const struct clock_event_device numachip2_clockevent __initconst = {
3862306a36Sopenharmony_ci	.name            = "numachip2",
3962306a36Sopenharmony_ci	.rating          = 400,
4062306a36Sopenharmony_ci	.set_next_event  = numachip2_set_next_event,
4162306a36Sopenharmony_ci	.features        = CLOCK_EVT_FEAT_ONESHOT,
4262306a36Sopenharmony_ci	.mult            = 1,
4362306a36Sopenharmony_ci	.shift           = 0,
4462306a36Sopenharmony_ci	.min_delta_ns    = 1250,
4562306a36Sopenharmony_ci	.min_delta_ticks = 1250,
4662306a36Sopenharmony_ci	.max_delta_ns    = LONG_MAX,
4762306a36Sopenharmony_ci	.max_delta_ticks = LONG_MAX,
4862306a36Sopenharmony_ci};
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic void numachip_timer_interrupt(void)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	struct clock_event_device *ced = this_cpu_ptr(&numachip2_ced);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	ced->event_handler(ced);
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic __init void numachip_timer_each(struct work_struct *work)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	unsigned local_apicid = __this_cpu_read(x86_cpu_to_apicid) & 0xff;
6062306a36Sopenharmony_ci	struct clock_event_device *ced = this_cpu_ptr(&numachip2_ced);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	/* Setup IPI vector to local core and relative timing mode */
6362306a36Sopenharmony_ci	numachip2_write64_lcsr(NUMACHIP2_TIMER_INT + numachip2_timer(),
6462306a36Sopenharmony_ci		(3 << 22) | (X86_PLATFORM_IPI_VECTOR << 14) |
6562306a36Sopenharmony_ci		(local_apicid << 6));
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	*ced = numachip2_clockevent;
6862306a36Sopenharmony_ci	ced->cpumask = cpumask_of(smp_processor_id());
6962306a36Sopenharmony_ci	clockevents_register_device(ced);
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic int __init numachip_timer_init(void)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	if (numachip_system != 2)
7562306a36Sopenharmony_ci		return -ENODEV;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	/* Reset timer */
7862306a36Sopenharmony_ci	numachip2_write64_lcsr(NUMACHIP2_TIMER_RESET, 0);
7962306a36Sopenharmony_ci	clocksource_register_hz(&numachip2_clocksource, NSEC_PER_SEC);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	/* Setup per-cpu clockevents */
8262306a36Sopenharmony_ci	x86_platform_ipi_callback = numachip_timer_interrupt;
8362306a36Sopenharmony_ci	schedule_on_each_cpu(&numachip_timer_each);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	return 0;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ciarch_initcall(numachip_timer_init);
89