mirror of
https://github.com/torvalds/linux.git
synced 2024-12-02 17:11:33 +00:00
x86/numachip: Introduce Numachip2 timer mechanisms
Add 1GHz 64-bit Numachip2 clocksource timer support for accurate system-wide timekeeping, as core TSCs are unsynchronised. Additionally, add a per-core clockevent mechanism that interrupts via the platform IPI vector after a programmed period. [ tglx: Taking it through x86 due to dependencies ] Signed-off-by: Daniel J Blueman <daniel@numascale.com> Acked-by: Steffen Persvold <sp@numascale.com> Cc: Daniel Lezcano <daniel.lezcano@linaro.org> Link: http://lkml.kernel.org/r/1442829745-29311-1-git-send-email-daniel@numascale.com Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
This commit is contained in:
parent
ad03a9c25d
commit
ce2e572cfe
@ -59,6 +59,10 @@ static inline void write_lcsr(unsigned long offset, unsigned int val)
|
||||
#define NUMACHIP2_LCSR_BASE 0xf0000000UL
|
||||
#define NUMACHIP2_LCSR_SIZE 0x1000000UL
|
||||
#define NUMACHIP2_APIC_ICR 0x100000
|
||||
#define NUMACHIP2_TIMER_DEADLINE 0x200000
|
||||
#define NUMACHIP2_TIMER_INT 0x200008
|
||||
#define NUMACHIP2_TIMER_NOW 0x200018
|
||||
#define NUMACHIP2_TIMER_RESET 0x200020
|
||||
|
||||
static inline void __iomem *numachip2_lcsr_address(unsigned long offset)
|
||||
{
|
||||
@ -86,4 +90,9 @@ static inline void numachip2_write64_lcsr(unsigned long offset, u64 val)
|
||||
writeq(val, numachip2_lcsr_address(offset));
|
||||
}
|
||||
|
||||
static inline unsigned int numachip2_timer(void)
|
||||
{
|
||||
return (smp_processor_id() % 48) << 6;
|
||||
}
|
||||
|
||||
#endif /* _ASM_X86_NUMACHIP_NUMACHIP_CSR_H */
|
||||
|
@ -62,3 +62,4 @@ obj-$(CONFIG_H8300) += h8300_timer8.o
|
||||
obj-$(CONFIG_H8300_TMR16) += h8300_timer16.o
|
||||
obj-$(CONFIG_H8300_TPU) += h8300_tpu.o
|
||||
obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o
|
||||
obj-$(CONFIG_X86_NUMACHIP) += numachip.o
|
||||
|
95
drivers/clocksource/numachip.c
Normal file
95
drivers/clocksource/numachip.c
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
*
|
||||
* 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, cpu_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 struct clock_event_device numachip2_clockevent = {
|
||||
.name = "numachip2",
|
||||
.rating = 400,
|
||||
.set_next_event = numachip2_set_next_event,
|
||||
.features = CLOCK_EVT_FEAT_ONESHOT,
|
||||
.mult = 1,
|
||||
.shift = 0,
|
||||
.min_delta_ns = 1250,
|
||||
.max_delta_ns = LONG_MAX,
|
||||
};
|
||||
|
||||
static void numachip_timer_interrupt(void)
|
||||
{
|
||||
struct clock_event_device *ced = this_cpu_ptr(&cpu_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(&cpu_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);
|
Loading…
Reference in New Issue
Block a user