![Paul Burton](/assets/img/avatar_default.png)
This patch allows the GIC clockevent device for a CPU to be configured by another CPU. This makes GIC clockevent devices suitable for use as the tick broadcast device, where formerly the GIC timer local to the configuring CPU would have been configured incorrectly. Signed-off-by: Paul Burton <paul.burton@imgtec.com>
106 lines
2.4 KiB
C
106 lines
2.4 KiB
C
/*
|
|
* This file is subject to the terms and conditions of the GNU General Public
|
|
* License. See the file "COPYING" in the main directory of this archive
|
|
* for more details.
|
|
*
|
|
* Copyright (C) 2013 Imagination Technologies Ltd.
|
|
*/
|
|
#include <linux/clockchips.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/percpu.h>
|
|
#include <linux/smp.h>
|
|
#include <linux/irq.h>
|
|
|
|
#include <asm/time.h>
|
|
#include <asm/gic.h>
|
|
#include <asm/mips-boards/maltaint.h>
|
|
|
|
DEFINE_PER_CPU(struct clock_event_device, gic_clockevent_device);
|
|
int gic_timer_irq_installed;
|
|
|
|
|
|
static int gic_next_event(unsigned long delta, struct clock_event_device *evt)
|
|
{
|
|
u64 cnt;
|
|
int res;
|
|
|
|
cnt = gic_read_count();
|
|
cnt += (u64)delta;
|
|
gic_write_cpu_compare(cnt, cpumask_first(evt->cpumask));
|
|
res = ((int)(gic_read_count() - cnt) >= 0) ? -ETIME : 0;
|
|
return res;
|
|
}
|
|
|
|
void gic_set_clock_mode(enum clock_event_mode mode,
|
|
struct clock_event_device *evt)
|
|
{
|
|
/* Nothing to do ... */
|
|
}
|
|
|
|
irqreturn_t gic_compare_interrupt(int irq, void *dev_id)
|
|
{
|
|
struct clock_event_device *cd;
|
|
int cpu = smp_processor_id();
|
|
|
|
gic_write_compare(gic_read_compare());
|
|
cd = &per_cpu(gic_clockevent_device, cpu);
|
|
cd->event_handler(cd);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
struct irqaction gic_compare_irqaction = {
|
|
.handler = gic_compare_interrupt,
|
|
.flags = IRQF_PERCPU | IRQF_TIMER,
|
|
.name = "timer",
|
|
};
|
|
|
|
|
|
void gic_event_handler(struct clock_event_device *dev)
|
|
{
|
|
}
|
|
|
|
int gic_clockevent_init(void)
|
|
{
|
|
unsigned int cpu = smp_processor_id();
|
|
struct clock_event_device *cd;
|
|
unsigned int irq;
|
|
|
|
if (!cpu_has_counter || !gic_frequency)
|
|
return -ENXIO;
|
|
|
|
irq = MIPS_GIC_IRQ_BASE;
|
|
|
|
cd = &per_cpu(gic_clockevent_device, cpu);
|
|
|
|
cd->name = "MIPS GIC";
|
|
cd->features = CLOCK_EVT_FEAT_ONESHOT |
|
|
CLOCK_EVT_FEAT_C3STOP;
|
|
|
|
clockevent_set_clock(cd, gic_frequency);
|
|
|
|
/* Calculate the min / max delta */
|
|
cd->max_delta_ns = clockevent_delta2ns(0x7fffffff, cd);
|
|
cd->min_delta_ns = clockevent_delta2ns(0x300, cd);
|
|
|
|
cd->rating = 300;
|
|
cd->irq = irq;
|
|
cd->cpumask = cpumask_of(cpu);
|
|
cd->set_next_event = gic_next_event;
|
|
cd->set_mode = gic_set_clock_mode;
|
|
cd->event_handler = gic_event_handler;
|
|
|
|
clockevents_register_device(cd);
|
|
|
|
GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_MAP), 0x80000002);
|
|
GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_SMASK), GIC_VPE_SMASK_CMP_MSK);
|
|
|
|
if (gic_timer_irq_installed)
|
|
return 0;
|
|
|
|
gic_timer_irq_installed = 1;
|
|
|
|
setup_irq(irq, &gic_compare_irqaction);
|
|
irq_set_handler(irq, handle_percpu_irq);
|
|
return 0;
|
|
}
|