Skip to content

Commit

Permalink
MIPS: Add new GIC clockevent driver.
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Raghu Gandham authored and Ralf Baechle committed May 9, 2013
1 parent 2675fa7 commit 0ab2b7d
Show file tree
Hide file tree
Showing 8 changed files with 163 additions and 2 deletions.
12 changes: 12 additions & 0 deletions arch/mips/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -912,6 +912,9 @@ config CEVT_GT641XX
config CEVT_R4K
bool

config CEVT_GIC
bool

config CEVT_SB1250
bool

Expand Down Expand Up @@ -1819,6 +1822,15 @@ config FORCE_MAX_ZONEORDER
The page size is not necessarily 4KB. Keep this in mind
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
bool

Expand Down
5 changes: 4 additions & 1 deletion arch/mips/include/asm/gic.h
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@
#define GIC_VPE_WD_COUNT0_OFS 0x0094
#define GIC_VPE_WD_INITIAL0_OFS 0x0098
#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_SS(intr) \
Expand Down Expand Up @@ -373,7 +373,10 @@ extern void gic_init(unsigned long gic_base_addr,
unsigned long gic_addrspace_size, struct gic_intr_map *intrmap,
unsigned int intrmap_size, unsigned int irqbase);
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_compare(void);
extern void gic_write_compare(cycle_t cnt);
extern void gic_send_ipi(unsigned int intr);
extern unsigned int plat_ipi_call_int_xlate(unsigned int);
extern unsigned int plat_ipi_resched_int_xlate(unsigned int);
Expand Down
3 changes: 3 additions & 0 deletions arch/mips/include/asm/time.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,14 @@ extern int (*perf_irq)(void);
extern unsigned int __weak get_c0_compare_int(void);
extern int r4k_clockevent_init(void);
extern int smtc_clockevent_init(void);
extern int gic_clockevent_init(void);

static inline int mips_clockevent_init(void)
{
#ifdef CONFIG_MIPS_MT_SMTC
return smtc_clockevent_init();
#elif defined(CONFIG_CEVT_GIC)
return (gic_clockevent_init() | r4k_clockevent_init());
#elif defined(CONFIG_CEVT_R4K)
return r4k_clockevent_init();
#else
Expand Down
1 change: 1 addition & 0 deletions arch/mips/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ obj-$(CONFIG_CEVT_BCM1480) += cevt-bcm1480.o
obj-$(CONFIG_CEVT_R4K) += cevt-r4k.o
obj-$(CONFIG_MIPS_MT_SMTC) += cevt-smtc.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_SB1250) += cevt-sb1250.o
obj-$(CONFIG_CEVT_TXX9) += cevt-txx9.o
Expand Down
104 changes: 104 additions & 0 deletions arch/mips/kernel/cevt-gic.c
Original file line number Diff line number Diff line change
@@ -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;
}
6 changes: 6 additions & 0 deletions arch/mips/kernel/cevt-r4k.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ irqreturn_t c0_compare_interrupt(int irq, void *dev_id)
/* Clear Count/Compare Interrupt */
write_c0_compare(read_c0_compare());
cd = &per_cpu(mips_clockevent_device, cpu);
#ifdef CONFIG_CEVT_GIC
if (!gic_present)
#endif
cd->event_handler(cd);
}

Expand Down Expand Up @@ -203,6 +206,9 @@ int __cpuinit r4k_clockevent_init(void)
cd->set_mode = mips_set_clock_mode;
cd->event_handler = mips_event_handler;

#ifdef CONFIG_CEVT_GIC
if (!gic_present)
#endif
clockevents_register_device(cd);

if (cp0_timer_irq_installed)
Expand Down
31 changes: 30 additions & 1 deletion arch/mips/kernel/irq-gic.c
Original file line number Diff line number Diff line change
Expand Up @@ -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_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)
{
unsigned int hi, hi2, lo;
Expand All @@ -46,6 +46,24 @@ cycle_t gic_read_count(void)

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

unsigned int gic_get_timer_pending(void)
Expand Down Expand Up @@ -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 i;
Expand Down
3 changes: 3 additions & 0 deletions arch/mips/mti-malta/malta-int.c
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@ static void malta_ipi_irqdispatch(void)
{
int irq;

if (gic_compare_int())
do_IRQ(MIPS_GIC_IRQ_BASE);

irq = gic_get_int();
if (irq < 0)
return; /* interrupt has already been cleared */
Expand Down

0 comments on commit 0ab2b7d

Please sign in to comment.