MIPS: Add new GIC clockevent driver.

Add new clockevent driver that uses the counter present on the MIPS
Global Interrupt Controller.

Signed-off-by: Raghu Gandham <Raghu.Gandham@imgtec.com>
Signed-off-by: Steven J. Hill <Steven.Hill@imgtec.com>
This commit is contained in:
Raghu Gandham 2013-04-10 16:30:12 -05:00 committed by Ralf Baechle
parent 2675fa7c7b
commit 0ab2b7d08e
8 changed files with 163 additions and 2 deletions

View File

@ -912,6 +912,9 @@ config CEVT_GT641XX
config CEVT_R4K config CEVT_R4K
bool bool
config CEVT_GIC
bool
config CEVT_SB1250 config CEVT_SB1250
bool bool
@ -1819,6 +1822,15 @@ config FORCE_MAX_ZONEORDER
The page size is not necessarily 4KB. Keep this in mind The page size is not necessarily 4KB. Keep this in mind
when choosing a value for this option. when choosing a value for this option.
config CEVT_GIC
bool "Use GIC global counter for clock events"
depends on IRQ_GIC && !(MIPS_SEAD3 || MIPS_MT_SMTC)
help
Use the GIC global counter for the clock events. The R4K clock
event driver is always present, so if the platform ends up not
detecting a GIC, it will fall back to the R4K timer for the
generation of clock events.
config BOARD_SCACHE config BOARD_SCACHE
bool bool

View File

@ -202,7 +202,7 @@
#define GIC_VPE_WD_COUNT0_OFS 0x0094 #define GIC_VPE_WD_COUNT0_OFS 0x0094
#define GIC_VPE_WD_INITIAL0_OFS 0x0098 #define GIC_VPE_WD_INITIAL0_OFS 0x0098
#define GIC_VPE_COMPARE_LO_OFS 0x00a0 #define GIC_VPE_COMPARE_LO_OFS 0x00a0
#define GIC_VPE_COMPARE_HI 0x00a4 #define GIC_VPE_COMPARE_HI_OFS 0x00a4
#define GIC_VPE_EIC_SHADOW_SET_BASE 0x0100 #define GIC_VPE_EIC_SHADOW_SET_BASE 0x0100
#define GIC_VPE_EIC_SS(intr) \ #define GIC_VPE_EIC_SS(intr) \
@ -373,7 +373,10 @@ extern void gic_init(unsigned long gic_base_addr,
unsigned long gic_addrspace_size, struct gic_intr_map *intrmap, unsigned long gic_addrspace_size, struct gic_intr_map *intrmap,
unsigned int intrmap_size, unsigned int irqbase); unsigned int intrmap_size, unsigned int irqbase);
extern void gic_clocksource_init(unsigned int); extern void gic_clocksource_init(unsigned int);
extern unsigned int gic_compare_int (void);
extern cycle_t gic_read_count(void); extern cycle_t gic_read_count(void);
extern cycle_t gic_read_compare(void);
extern void gic_write_compare(cycle_t cnt);
extern void gic_send_ipi(unsigned int intr); extern void gic_send_ipi(unsigned int intr);
extern unsigned int plat_ipi_call_int_xlate(unsigned int); extern unsigned int plat_ipi_call_int_xlate(unsigned int);
extern unsigned int plat_ipi_resched_int_xlate(unsigned int); extern unsigned int plat_ipi_resched_int_xlate(unsigned int);

View File

@ -53,11 +53,14 @@ extern int (*perf_irq)(void);
extern unsigned int __weak get_c0_compare_int(void); extern unsigned int __weak get_c0_compare_int(void);
extern int r4k_clockevent_init(void); extern int r4k_clockevent_init(void);
extern int smtc_clockevent_init(void); extern int smtc_clockevent_init(void);
extern int gic_clockevent_init(void);
static inline int mips_clockevent_init(void) static inline int mips_clockevent_init(void)
{ {
#ifdef CONFIG_MIPS_MT_SMTC #ifdef CONFIG_MIPS_MT_SMTC
return smtc_clockevent_init(); return smtc_clockevent_init();
#elif defined(CONFIG_CEVT_GIC)
return (gic_clockevent_init() | r4k_clockevent_init());
#elif defined(CONFIG_CEVT_R4K) #elif defined(CONFIG_CEVT_R4K)
return r4k_clockevent_init(); return r4k_clockevent_init();
#else #else

View File

@ -19,6 +19,7 @@ obj-$(CONFIG_CEVT_BCM1480) += cevt-bcm1480.o
obj-$(CONFIG_CEVT_R4K) += cevt-r4k.o obj-$(CONFIG_CEVT_R4K) += cevt-r4k.o
obj-$(CONFIG_MIPS_MT_SMTC) += cevt-smtc.o obj-$(CONFIG_MIPS_MT_SMTC) += cevt-smtc.o
obj-$(CONFIG_CEVT_DS1287) += cevt-ds1287.o obj-$(CONFIG_CEVT_DS1287) += cevt-ds1287.o
obj-$(CONFIG_CEVT_GIC) += cevt-gic.o
obj-$(CONFIG_CEVT_GT641XX) += cevt-gt641xx.o obj-$(CONFIG_CEVT_GT641XX) += cevt-gt641xx.o
obj-$(CONFIG_CEVT_SB1250) += cevt-sb1250.o obj-$(CONFIG_CEVT_SB1250) += cevt-sb1250.o
obj-$(CONFIG_CEVT_TXX9) += cevt-txx9.o obj-$(CONFIG_CEVT_TXX9) += cevt-txx9.o

104
arch/mips/kernel/cevt-gic.c Normal file
View File

@ -0,0 +1,104 @@
/*
* 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_compare(cnt);
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 __cpuinit 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;
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;
}

View File

@ -72,6 +72,9 @@ irqreturn_t c0_compare_interrupt(int irq, void *dev_id)
/* Clear Count/Compare Interrupt */ /* Clear Count/Compare Interrupt */
write_c0_compare(read_c0_compare()); write_c0_compare(read_c0_compare());
cd = &per_cpu(mips_clockevent_device, cpu); cd = &per_cpu(mips_clockevent_device, cpu);
#ifdef CONFIG_CEVT_GIC
if (!gic_present)
#endif
cd->event_handler(cd); cd->event_handler(cd);
} }
@ -203,6 +206,9 @@ int __cpuinit r4k_clockevent_init(void)
cd->set_mode = mips_set_clock_mode; cd->set_mode = mips_set_clock_mode;
cd->event_handler = mips_event_handler; cd->event_handler = mips_event_handler;
#ifdef CONFIG_CEVT_GIC
if (!gic_present)
#endif
clockevents_register_device(cd); clockevents_register_device(cd);
if (cp0_timer_irq_installed) if (cp0_timer_irq_installed)

View File

@ -33,7 +33,7 @@ static struct gic_pcpu_mask pcpu_masks[NR_CPUS];
static struct gic_pending_regs pending_regs[NR_CPUS]; static struct gic_pending_regs pending_regs[NR_CPUS];
static struct gic_intrmask_regs intrmask_regs[NR_CPUS]; static struct gic_intrmask_regs intrmask_regs[NR_CPUS];
#ifdef CONFIG_CSRC_GIC #if defined(CONFIG_CSRC_GIC) || defined(CONFIG_CEVT_GIC)
cycle_t gic_read_count(void) cycle_t gic_read_count(void)
{ {
unsigned int hi, hi2, lo; unsigned int hi, hi2, lo;
@ -46,6 +46,24 @@ cycle_t gic_read_count(void)
return (((cycle_t) hi) << 32) + lo; return (((cycle_t) hi) << 32) + lo;
} }
void gic_write_compare(cycle_t cnt)
{
GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_HI),
(int)(cnt >> 32));
GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_LO),
(int)(cnt & 0xffffffff));
}
cycle_t gic_read_compare(void)
{
unsigned int hi, lo;
GICREAD(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_HI), hi);
GICREAD(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_LO), lo);
return (((cycle_t) hi) << 32) + lo;
}
#endif #endif
unsigned int gic_get_timer_pending(void) unsigned int gic_get_timer_pending(void)
@ -134,6 +152,17 @@ static void __init vpe_local_setup(unsigned int numvpes)
} }
} }
unsigned int gic_compare_int(void)
{
unsigned int pending;
GICREAD(GIC_REG(VPE_LOCAL, GIC_VPE_PEND), pending);
if (pending & GIC_VPE_PEND_CMP_MSK)
return 1;
else
return 0;
}
unsigned int gic_get_int(void) unsigned int gic_get_int(void)
{ {
unsigned int i; unsigned int i;

View File

@ -133,6 +133,9 @@ static void malta_ipi_irqdispatch(void)
{ {
int irq; int irq;
if (gic_compare_int())
do_IRQ(MIPS_GIC_IRQ_BASE);
irq = gic_get_int(); irq = gic_get_int();
if (irq < 0) if (irq < 0)
return; /* interrupt has already been cleared */ return; /* interrupt has already been cleared */