Commit ed9fd3ff authored by Rabin Vincent's avatar Rabin Vincent Committed by Jesper Nilsson

CRISv32: use generic clockevents

Implement a oneshot-capable clockevents device so we get support for
things like hrtimers and NOHZ.
Signed-off-by: default avatarRabin Vincent <rabin@rab.in>
Signed-off-by: default avatarJesper Nilsson <jespern@axis.com>
parent 16428f94
...@@ -55,6 +55,7 @@ config CRIS ...@@ -55,6 +55,7 @@ config CRIS
select IRQ_DOMAIN if ETRAX_ARCH_V32 select IRQ_DOMAIN if ETRAX_ARCH_V32
select OF if ETRAX_ARCH_V32 select OF if ETRAX_ARCH_V32
select OF_EARLY_FLATTREE if ETRAX_ARCH_V32 select OF_EARLY_FLATTREE if ETRAX_ARCH_V32
select GENERIC_CLOCKEVENTS if ETRAX_ARCH_V32
config HZ config HZ
int int
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <linux/timex.h> #include <linux/timex.h>
#include <linux/time.h> #include <linux/time.h>
#include <linux/clocksource.h> #include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/swap.h> #include <linux/swap.h>
#include <linux/sched.h> #include <linux/sched.h>
...@@ -36,6 +37,8 @@ ...@@ -36,6 +37,8 @@
/* Number of 763 counts before watchdog bites */ /* Number of 763 counts before watchdog bites */
#define ETRAX_WD_CNT ((2*ETRAX_WD_HZ)/HZ + 1) #define ETRAX_WD_CNT ((2*ETRAX_WD_HZ)/HZ + 1)
#define CRISV32_TIMER_FREQ (100000000lu)
/* Register the continuos readonly timer available in FS and ARTPEC-3. */ /* Register the continuos readonly timer available in FS and ARTPEC-3. */
static cycle_t read_cont_rotime(struct clocksource *cs) static cycle_t read_cont_rotime(struct clocksource *cs)
{ {
...@@ -186,81 +189,99 @@ void handle_watchdog_bite(struct pt_regs *regs) ...@@ -186,81 +189,99 @@ void handle_watchdog_bite(struct pt_regs *regs)
#endif #endif
} }
/* extern void cris_profile_sample(struct pt_regs *regs);
* timer_interrupt() needs to keep up the real-time clock, static void __iomem *timer_base;
* as well as call the "xtime_update()" routine every clocktick.
*/
extern void cris_do_profile(struct pt_regs *regs);
static inline irqreturn_t timer_interrupt(int irq, void *dev_id) static void crisv32_clkevt_mode(enum clock_event_mode mode,
struct clock_event_device *dev)
{ {
struct pt_regs *regs = get_irq_regs(); reg_timer_rw_tmr0_ctrl ctrl = {
int cpu = smp_processor_id(); .op = regk_timer_hold,
reg_timer_r_masked_intr masked_intr; .freq = regk_timer_f100,
reg_timer_rw_ack_intr ack_intr = { 0 }; };
/* Check if the timer interrupt is for us (a tmr0 int) */
masked_intr = REG_RD(timer, timer_regs[cpu], r_masked_intr);
if (!masked_intr.tmr0)
return IRQ_NONE;
/* Acknowledge the timer irq. */ REG_WR(timer, timer_base, rw_tmr0_ctrl, ctrl);
ack_intr.tmr0 = 1; }
REG_WR(timer, timer_regs[cpu], rw_ack_intr, ack_intr);
/* Reset watchdog otherwise it resets us! */ static int crisv32_clkevt_next_event(unsigned long evt,
reset_watchdog(); struct clock_event_device *dev)
{
reg_timer_rw_tmr0_ctrl ctrl = {
.op = regk_timer_ld,
.freq = regk_timer_f100,
};
REG_WR(timer, timer_base, rw_tmr0_div, evt);
REG_WR(timer, timer_base, rw_tmr0_ctrl, ctrl);
ctrl.op = regk_timer_run;
REG_WR(timer, timer_base, rw_tmr0_ctrl, ctrl);
return 0;
}
/* Update statistics. */ static irqreturn_t crisv32_timer_interrupt(int irq, void *dev_id)
update_process_times(user_mode(regs)); {
struct clock_event_device *evt = dev_id;
reg_timer_rw_tmr0_ctrl ctrl = {
.op = regk_timer_hold,
.freq = regk_timer_f100,
};
reg_timer_rw_ack_intr ack = { .tmr0 = 1 };
reg_timer_r_masked_intr intr;
intr = REG_RD(timer, timer_base, r_masked_intr);
if (!intr.tmr0)
return IRQ_NONE;
REG_WR(timer, timer_base, rw_tmr0_ctrl, ctrl);
REG_WR(timer, timer_base, rw_ack_intr, ack);
cris_do_profile(regs); /* Save profiling information */ reset_watchdog();
#ifdef CONFIG_SYSTEM_PROFILER
cris_profile_sample(get_irq_regs());
#endif
/* The master CPU is responsible for the time keeping. */ evt->event_handler(evt);
if (cpu != 0)
return IRQ_HANDLED;
/* Call the real timer interrupt handler */
xtime_update(1);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static struct clock_event_device crisv32_clockevent = {
.name = "crisv32-timer",
.rating = 300,
.features = CLOCK_EVT_FEAT_ONESHOT,
.set_mode = crisv32_clkevt_mode,
.set_next_event = crisv32_clkevt_next_event,
};
/* Timer is IRQF_SHARED so drivers can add stuff to the timer irq chain. */ /* Timer is IRQF_SHARED so drivers can add stuff to the timer irq chain. */
static struct irqaction irq_timer = { static struct irqaction irq_timer = {
.handler = timer_interrupt, .handler = crisv32_timer_interrupt,
.flags = IRQF_SHARED, .flags = IRQF_TIMER | IRQF_SHARED,
.name = "timer" .name = "crisv32-timer",
.dev_id = &crisv32_clockevent,
}; };
void __init cris_timer_init(void) static void __init crisv32_timer_init(void)
{ {
int cpu = smp_processor_id();
reg_timer_rw_tmr0_ctrl tmr0_ctrl = { 0 };
reg_timer_rw_tmr0_div tmr0_div = TIMER0_DIV;
reg_timer_rw_intr_mask timer_intr_mask; reg_timer_rw_intr_mask timer_intr_mask;
reg_timer_rw_tmr0_ctrl ctrl = {
.op = regk_timer_hold,
.freq = regk_timer_f100,
};
/* Setup the etrax timers. REG_WR(timer, timer_base, rw_tmr0_ctrl, ctrl);
* Base frequency is 100MHz, divider 1000000 -> 100 HZ
* We use timer0, so timer1 is free.
* The trig timer is used by the fasttimer API if enabled.
*/
tmr0_ctrl.op = regk_timer_ld;
tmr0_ctrl.freq = regk_timer_f100;
REG_WR(timer, timer_regs[cpu], rw_tmr0_div, tmr0_div);
REG_WR(timer, timer_regs[cpu], rw_tmr0_ctrl, tmr0_ctrl); /* Load */
tmr0_ctrl.op = regk_timer_run;
REG_WR(timer, timer_regs[cpu], rw_tmr0_ctrl, tmr0_ctrl); /* Start */
/* Enable the timer irq. */ timer_intr_mask = REG_RD(timer, timer_base, rw_intr_mask);
timer_intr_mask = REG_RD(timer, timer_regs[cpu], rw_intr_mask);
timer_intr_mask.tmr0 = 1; timer_intr_mask.tmr0 = 1;
REG_WR(timer, timer_regs[cpu], rw_intr_mask, timer_intr_mask); REG_WR(timer, timer_base, rw_intr_mask, timer_intr_mask);
} }
void __init time_init(void) void __init time_init(void)
{ {
reg_intr_vect_rw_mask intr_mask; int irq;
int ret;
/* Probe for the RTC and read it if it exists. /* Probe for the RTC and read it if it exists.
* Before the RTC can be probed the loops_per_usec variable needs * Before the RTC can be probed the loops_per_usec variable needs
...@@ -270,17 +291,21 @@ void __init time_init(void) ...@@ -270,17 +291,21 @@ void __init time_init(void)
*/ */
loops_per_usec = 50; loops_per_usec = 50;
/* Start CPU local timer. */ irq = TIMER0_INTR_VECT;
cris_timer_init(); timer_base = (void __iomem *) regi_timer0;
crisv32_timer_init();
crisv32_clockevent.cpumask = cpu_possible_mask;
crisv32_clockevent.irq = irq;
/* Enable the timer irq in global config. */ ret = setup_irq(irq, &irq_timer);
intr_mask = REG_RD_VECT(intr_vect, regi_irq, rw_mask, 1); if (ret)
intr_mask.timer0 = 1; pr_warn("failed to setup irq %d\n", irq);
REG_WR_VECT(intr_vect, regi_irq, rw_mask, 1, intr_mask);
/* Now actually register the timer irq handler that calls clockevents_config_and_register(&crisv32_clockevent,
* timer_interrupt(). */ CRISV32_TIMER_FREQ,
setup_irq(TIMER0_INTR_VECT, &irq_timer); 2, 0xffffffff);
/* Enable watchdog if we should use one. */ /* Enable watchdog if we should use one. */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment