| /* | 
 |  * | 
 |  * Copyright (C) 2015 Numascale AS. All rights reserved. | 
 |  * | 
 |  * This software is licensed under the terms of the GNU General Public | 
 |  * License version 2, as published by the Free Software Foundation, and | 
 |  * may be copied, distributed, and modified under those terms. | 
 |  * | 
 |  * This program is distributed in the hope that it will be useful, | 
 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 |  * GNU General Public License for more details. | 
 |  * | 
 |  */ | 
 |  | 
 | #include <linux/clockchips.h> | 
 |  | 
 | #include <asm/irq.h> | 
 | #include <asm/numachip/numachip.h> | 
 | #include <asm/numachip/numachip_csr.h> | 
 |  | 
 | static DEFINE_PER_CPU(struct clock_event_device, numachip2_ced); | 
 |  | 
 | static cycles_t numachip2_timer_read(struct clocksource *cs) | 
 | { | 
 | 	return numachip2_read64_lcsr(NUMACHIP2_TIMER_NOW); | 
 | } | 
 |  | 
 | static struct clocksource numachip2_clocksource = { | 
 | 	.name            = "numachip2", | 
 | 	.rating          = 295, | 
 | 	.read            = numachip2_timer_read, | 
 | 	.mask            = CLOCKSOURCE_MASK(64), | 
 | 	.flags           = CLOCK_SOURCE_IS_CONTINUOUS, | 
 | 	.mult            = 1, | 
 | 	.shift           = 0, | 
 | }; | 
 |  | 
 | static int numachip2_set_next_event(unsigned long delta, struct clock_event_device *ced) | 
 | { | 
 | 	numachip2_write64_lcsr(NUMACHIP2_TIMER_DEADLINE + numachip2_timer(), | 
 | 		delta); | 
 | 	return 0; | 
 | } | 
 |  | 
 | static const struct clock_event_device numachip2_clockevent __initconst = { | 
 | 	.name            = "numachip2", | 
 | 	.rating          = 400, | 
 | 	.set_next_event  = numachip2_set_next_event, | 
 | 	.features        = CLOCK_EVT_FEAT_ONESHOT, | 
 | 	.mult            = 1, | 
 | 	.shift           = 0, | 
 | 	.min_delta_ns    = 1250, | 
 | 	.min_delta_ticks = 1250, | 
 | 	.max_delta_ns    = LONG_MAX, | 
 | 	.max_delta_ticks = LONG_MAX, | 
 | }; | 
 |  | 
 | static void numachip_timer_interrupt(void) | 
 | { | 
 | 	struct clock_event_device *ced = this_cpu_ptr(&numachip2_ced); | 
 |  | 
 | 	ced->event_handler(ced); | 
 | } | 
 |  | 
 | static __init void numachip_timer_each(struct work_struct *work) | 
 | { | 
 | 	unsigned local_apicid = __this_cpu_read(x86_cpu_to_apicid) & 0xff; | 
 | 	struct clock_event_device *ced = this_cpu_ptr(&numachip2_ced); | 
 |  | 
 | 	/* Setup IPI vector to local core and relative timing mode */ | 
 | 	numachip2_write64_lcsr(NUMACHIP2_TIMER_INT + numachip2_timer(), | 
 | 		(3 << 22) | (X86_PLATFORM_IPI_VECTOR << 14) | | 
 | 		(local_apicid << 6)); | 
 |  | 
 | 	*ced = numachip2_clockevent; | 
 | 	ced->cpumask = cpumask_of(smp_processor_id()); | 
 | 	clockevents_register_device(ced); | 
 | } | 
 |  | 
 | static int __init numachip_timer_init(void) | 
 | { | 
 | 	if (numachip_system != 2) | 
 | 		return -ENODEV; | 
 |  | 
 | 	/* Reset timer */ | 
 | 	numachip2_write64_lcsr(NUMACHIP2_TIMER_RESET, 0); | 
 | 	clocksource_register_hz(&numachip2_clocksource, NSEC_PER_SEC); | 
 |  | 
 | 	/* Setup per-cpu clockevents */ | 
 | 	x86_platform_ipi_callback = numachip_timer_interrupt; | 
 | 	schedule_on_each_cpu(&numachip_timer_each); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | arch_initcall(numachip_timer_init); |