Commit ea580401 authored by Ralf Baechle's avatar Ralf Baechle

[MIPS] Dyntick support for SMTC:

The kernel currently only supports broadcasting of the timer interrupt
from a single timer, not multicasting into two multicast groups of
processors.  So the implemented mechanism for SMTC works by broadcasting
the cp0 compare interrupt on VPE 0 and ignoring it on any additional VPEs.
Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent 7bcf7717
...@@ -1368,6 +1368,7 @@ config MIPS_MT_SMTC ...@@ -1368,6 +1368,7 @@ config MIPS_MT_SMTC
depends on CPU_MIPS32_R2 depends on CPU_MIPS32_R2
#depends on CPU_MIPS64_R2 # once there is hardware ... #depends on CPU_MIPS64_R2 # once there is hardware ...
depends on SYS_SUPPORTS_MULTITHREADING depends on SYS_SUPPORTS_MULTITHREADING
select GENERIC_CLOCKEVENTS_BROADCAST
select CPU_MIPSR2_IRQ_VI select CPU_MIPSR2_IRQ_VI
select CPU_MIPSR2_IRQ_EI select CPU_MIPSR2_IRQ_EI
select CPU_MIPSR2_SRS select CPU_MIPSR2_SRS
...@@ -1537,6 +1538,9 @@ config CPU_HAS_SYNC ...@@ -1537,6 +1538,9 @@ config CPU_HAS_SYNC
depends on !CPU_R3000 depends on !CPU_R3000
default y default y
config GENERIC_CLOCKEVENTS_BROADCAST
bool
# #
# Use the generic interrupt handling code in kernel/irq/: # Use the generic interrupt handling code in kernel/irq/:
# #
......
/* Copyright (C) 2004 Mips Technologies, Inc */ /* Copyright (C) 2004 Mips Technologies, Inc */
#include <linux/clockchips.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/cpumask.h> #include <linux/cpumask.h>
...@@ -62,7 +63,7 @@ asiduse smtc_live_asid[MAX_SMTC_TLBS][MAX_SMTC_ASIDS]; ...@@ -62,7 +63,7 @@ asiduse smtc_live_asid[MAX_SMTC_TLBS][MAX_SMTC_ASIDS];
* Clock interrupt "latch" buffers, per "CPU" * Clock interrupt "latch" buffers, per "CPU"
*/ */
unsigned int ipi_timer_latch[NR_CPUS]; static atomic_t ipi_timer_latch[NR_CPUS];
/* /*
* Number of InterProcessor Interupt (IPI) message buffers to allocate * Number of InterProcessor Interupt (IPI) message buffers to allocate
...@@ -296,8 +297,10 @@ int __init mipsmt_build_cpu_map(int start_cpu_slot) ...@@ -296,8 +297,10 @@ int __init mipsmt_build_cpu_map(int start_cpu_slot)
__cpu_number_map[i] = i; __cpu_number_map[i] = i;
__cpu_logical_map[i] = i; __cpu_logical_map[i] = i;
} }
#ifdef CONFIG_MIPS_MT_FPAFF
/* Initialize map of CPUs with FPUs */ /* Initialize map of CPUs with FPUs */
cpus_clear(mt_fpu_cpumask); cpus_clear(mt_fpu_cpumask);
#endif
/* One of those TC's is the one booting, and not a secondary... */ /* One of those TC's is the one booting, and not a secondary... */
printk("%i available secondary CPU TC(s)\n", i - 1); printk("%i available secondary CPU TC(s)\n", i - 1);
...@@ -359,7 +362,7 @@ void mipsmt_prepare_cpus(void) ...@@ -359,7 +362,7 @@ void mipsmt_prepare_cpus(void)
IPIQ[i].head = IPIQ[i].tail = NULL; IPIQ[i].head = IPIQ[i].tail = NULL;
spin_lock_init(&IPIQ[i].lock); spin_lock_init(&IPIQ[i].lock);
IPIQ[i].depth = 0; IPIQ[i].depth = 0;
ipi_timer_latch[i] = 0; atomic_set(&ipi_timer_latch[i], 0);
} }
/* cpu_data index starts at zero */ /* cpu_data index starts at zero */
...@@ -482,10 +485,12 @@ void mipsmt_prepare_cpus(void) ...@@ -482,10 +485,12 @@ void mipsmt_prepare_cpus(void)
/* Set up coprocessor affinity CPU mask(s) */ /* Set up coprocessor affinity CPU mask(s) */
#ifdef CONFIG_MIPS_MT_FPAFF
for (tc = 0; tc < ntc; tc++) { for (tc = 0; tc < ntc; tc++) {
if (cpu_data[tc].options & MIPS_CPU_FPU) if (cpu_data[tc].options & MIPS_CPU_FPU)
cpu_set(tc, mt_fpu_cpumask); cpu_set(tc, mt_fpu_cpumask);
} }
#endif
/* set up ipi interrupts... */ /* set up ipi interrupts... */
...@@ -702,7 +707,7 @@ static void smtc_ipi_qdump(void) ...@@ -702,7 +707,7 @@ static void smtc_ipi_qdump(void)
* be done with the atomic.h primitives). And since this is * be done with the atomic.h primitives). And since this is
* MIPS MT, we can assume that we have LL/SC. * MIPS MT, we can assume that we have LL/SC.
*/ */
static __inline__ int atomic_postincrement(unsigned int *pv) static inline int atomic_postincrement(atomic_t *v)
{ {
unsigned long result; unsigned long result;
...@@ -714,8 +719,8 @@ static __inline__ int atomic_postincrement(unsigned int *pv) ...@@ -714,8 +719,8 @@ static __inline__ int atomic_postincrement(unsigned int *pv)
" sc %1, %2 \n" " sc %1, %2 \n"
" beqz %1, 1b \n" " beqz %1, 1b \n"
__WEAK_LLSC_MB __WEAK_LLSC_MB
: "=&r" (result), "=&r" (temp), "=m" (*pv) : "=&r" (result), "=&r" (temp), "=m" (v->counter)
: "m" (*pv) : "m" (v->counter)
: "memory"); : "memory");
return result; return result;
...@@ -743,6 +748,8 @@ void smtc_send_ipi(int cpu, int type, unsigned int action) ...@@ -743,6 +748,8 @@ void smtc_send_ipi(int cpu, int type, unsigned int action)
pipi->arg = (void *)action; pipi->arg = (void *)action;
pipi->dest = cpu; pipi->dest = cpu;
if (cpu_data[cpu].vpe_id != cpu_data[smp_processor_id()].vpe_id) { if (cpu_data[cpu].vpe_id != cpu_data[smp_processor_id()].vpe_id) {
if (type == SMTC_CLOCK_TICK)
atomic_inc(&ipi_timer_latch[cpu]);
/* If not on same VPE, enqueue and send cross-VPE interupt */ /* If not on same VPE, enqueue and send cross-VPE interupt */
smtc_ipi_nq(&IPIQ[cpu], pipi); smtc_ipi_nq(&IPIQ[cpu], pipi);
LOCK_CORE_PRA(); LOCK_CORE_PRA();
...@@ -784,6 +791,8 @@ void smtc_send_ipi(int cpu, int type, unsigned int action) ...@@ -784,6 +791,8 @@ void smtc_send_ipi(int cpu, int type, unsigned int action)
} }
smtc_ipi_nq(&IPIQ[cpu], pipi); smtc_ipi_nq(&IPIQ[cpu], pipi);
} else { } else {
if (type == SMTC_CLOCK_TICK)
atomic_inc(&ipi_timer_latch[cpu]);
post_direct_ipi(cpu, pipi); post_direct_ipi(cpu, pipi);
write_tc_c0_tchalt(0); write_tc_c0_tchalt(0);
UNLOCK_CORE_PRA(); UNLOCK_CORE_PRA();
...@@ -801,6 +810,7 @@ static void post_direct_ipi(int cpu, struct smtc_ipi *pipi) ...@@ -801,6 +810,7 @@ static void post_direct_ipi(int cpu, struct smtc_ipi *pipi)
unsigned long tcrestart; unsigned long tcrestart;
extern u32 kernelsp[NR_CPUS]; extern u32 kernelsp[NR_CPUS];
extern void __smtc_ipi_vector(void); extern void __smtc_ipi_vector(void);
//printk("%s: on %d for %d\n", __func__, smp_processor_id(), cpu);
/* Extract Status, EPC from halted TC */ /* Extract Status, EPC from halted TC */
tcstatus = read_tc_c0_tcstatus(); tcstatus = read_tc_c0_tcstatus();
...@@ -851,25 +861,31 @@ static void ipi_call_interrupt(void) ...@@ -851,25 +861,31 @@ static void ipi_call_interrupt(void)
smp_call_function_interrupt(); smp_call_function_interrupt();
} }
DECLARE_PER_CPU(struct clock_event_device, smtc_dummy_clockevent_device);
void ipi_decode(struct smtc_ipi *pipi) void ipi_decode(struct smtc_ipi *pipi)
{ {
unsigned int cpu = smp_processor_id();
struct clock_event_device *cd;
void *arg_copy = pipi->arg; void *arg_copy = pipi->arg;
int type_copy = pipi->type; int type_copy = pipi->type;
int dest_copy = pipi->dest; int ticks;
smtc_ipi_nq(&freeIPIq, pipi); smtc_ipi_nq(&freeIPIq, pipi);
switch (type_copy) { switch (type_copy) {
case SMTC_CLOCK_TICK: case SMTC_CLOCK_TICK:
irq_enter(); irq_enter();
kstat_this_cpu.irqs[MIPS_CPU_IRQ_BASE + cp0_compare_irq]++; kstat_this_cpu.irqs[MIPS_CPU_IRQ_BASE + 1]++;
/* Invoke Clock "Interrupt" */ cd = &per_cpu(smtc_dummy_clockevent_device, cpu);
ipi_timer_latch[dest_copy] = 0; ticks = atomic_read(&ipi_timer_latch[cpu]);
#ifdef CONFIG_SMTC_IDLE_HOOK_DEBUG atomic_sub(ticks, &ipi_timer_latch[cpu]);
clock_hang_reported[dest_copy] = 0; while (ticks) {
#endif /* CONFIG_SMTC_IDLE_HOOK_DEBUG */ cd->event_handler(cd);
local_timer_interrupt(0, NULL); ticks--;
}
irq_exit(); irq_exit();
break; break;
case LINUX_SMP_IPI: case LINUX_SMP_IPI:
switch ((int)arg_copy) { switch ((int)arg_copy) {
case SMP_RESCHEDULE_YOURSELF: case SMP_RESCHEDULE_YOURSELF:
...@@ -920,25 +936,6 @@ void deferred_smtc_ipi(void) ...@@ -920,25 +936,6 @@ void deferred_smtc_ipi(void)
} }
} }
/*
* Send clock tick to all TCs except the one executing the funtion
*/
void smtc_timer_broadcast(void)
{
int cpu;
int myTC = cpu_data[smp_processor_id()].tc_id;
int myVPE = cpu_data[smp_processor_id()].vpe_id;
smtc_cpu_stats[smp_processor_id()].timerints++;
for_each_online_cpu(cpu) {
if (cpu_data[cpu].vpe_id == myVPE &&
cpu_data[cpu].tc_id != myTC)
smtc_send_ipi(cpu, SMTC_CLOCK_TICK, 0);
}
}
/* /*
* Cross-VPE interrupts in the SMTC prototype use "software interrupts" * Cross-VPE interrupts in the SMTC prototype use "software interrupts"
* set via cross-VPE MTTR manipulation of the Cause register. It would be * set via cross-VPE MTTR manipulation of the Cause register. It would be
...@@ -1180,11 +1177,11 @@ void smtc_idle_loop_hook(void) ...@@ -1180,11 +1177,11 @@ void smtc_idle_loop_hook(void)
for (tc = 0; tc < NR_CPUS; tc++) { for (tc = 0; tc < NR_CPUS; tc++) {
/* Don't check ourself - we'll dequeue IPIs just below */ /* Don't check ourself - we'll dequeue IPIs just below */
if ((tc != smp_processor_id()) && if ((tc != smp_processor_id()) &&
ipi_timer_latch[tc] > timerq_limit) { atomic_read(&ipi_timer_latch[tc]) > timerq_limit) {
if (clock_hang_reported[tc] == 0) { if (clock_hang_reported[tc] == 0) {
pdb_msg += sprintf(pdb_msg, pdb_msg += sprintf(pdb_msg,
"TC %d looks hung with timer latch at %d\n", "TC %d looks hung with timer latch at %d\n",
tc, ipi_timer_latch[tc]); tc, atomic_read(&ipi_timer_latch[tc]));
clock_hang_reported[tc]++; clock_hang_reported[tc]++;
} }
} }
...@@ -1225,7 +1222,7 @@ void smtc_soft_dump(void) ...@@ -1225,7 +1222,7 @@ void smtc_soft_dump(void)
smtc_ipi_qdump(); smtc_ipi_qdump();
printk("Timer IPI Backlogs:\n"); printk("Timer IPI Backlogs:\n");
for (i=0; i < NR_CPUS; i++) { for (i=0; i < NR_CPUS; i++) {
printk("%d: %d\n", i, ipi_timer_latch[i]); printk("%d: %d\n", i, atomic_read(&ipi_timer_latch[i]));
} }
printk("%d Recoveries of \"stolen\" FPU\n", printk("%d Recoveries of \"stolen\" FPU\n",
atomic_read(&smtc_fpu_recoveries)); atomic_read(&smtc_fpu_recoveries));
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/kallsyms.h>
#include <asm/bootinfo.h> #include <asm/bootinfo.h>
#include <asm/cache.h> #include <asm/cache.h>
...@@ -33,6 +34,7 @@ ...@@ -33,6 +34,7 @@
#include <asm/cpu-features.h> #include <asm/cpu-features.h>
#include <asm/div64.h> #include <asm/div64.h>
#include <asm/sections.h> #include <asm/sections.h>
#include <asm/smtc_ipi.h>
#include <asm/time.h> #include <asm/time.h>
#include <irq.h> #include <irq.h>
...@@ -230,12 +232,24 @@ static int mips_next_event(unsigned long delta, ...@@ -230,12 +232,24 @@ static int mips_next_event(unsigned long delta,
struct clock_event_device *evt) struct clock_event_device *evt)
{ {
unsigned int cnt; unsigned int cnt;
int res;
#ifdef CONFIG_MIPS_MT_SMTC
{
unsigned long flags, vpflags;
local_irq_save(flags);
vpflags = dvpe();
#endif
cnt = read_c0_count(); cnt = read_c0_count();
cnt += delta; cnt += delta;
write_c0_compare(cnt); write_c0_compare(cnt);
res = ((long)(read_c0_count() - cnt ) > 0) ? -ETIME : 0;
return ((long)(read_c0_count() - cnt ) > 0) ? -ETIME : 0; #ifdef CONFIG_MIPS_MT_SMTC
evpe(vpflags);
local_irq_restore(flags);
}
#endif
return res;
} }
static void mips_set_mode(enum clock_event_mode mode, static void mips_set_mode(enum clock_event_mode mode,
...@@ -244,9 +258,7 @@ static void mips_set_mode(enum clock_event_mode mode, ...@@ -244,9 +258,7 @@ static void mips_set_mode(enum clock_event_mode mode,
/* Nothing to do ... */ /* Nothing to do ... */
} }
struct clock_event_device mips_clockevent; static DEFINE_PER_CPU(struct clock_event_device, mips_clockevent_device);
static struct clock_event_device *global_cd[NR_CPUS];
static int cp0_timer_irq_installed; static int cp0_timer_irq_installed;
static irqreturn_t timer_interrupt(int irq, void *dev_id) static irqreturn_t timer_interrupt(int irq, void *dev_id)
...@@ -271,7 +283,12 @@ static irqreturn_t timer_interrupt(int irq, void *dev_id) ...@@ -271,7 +283,12 @@ static irqreturn_t timer_interrupt(int irq, void *dev_id)
*/ */
if (!r2 || (read_c0_cause() & (1 << 30))) { if (!r2 || (read_c0_cause() & (1 << 30))) {
c0_timer_ack(); c0_timer_ack();
cd = global_cd[cpu]; #ifdef CONFIG_MIPS_MT_SMTC
if (cpu_data[cpu].vpe_id)
goto out;
cpu = 0;
#endif
cd = &per_cpu(mips_clockevent_device, cpu);
cd->event_handler(cd); cd->event_handler(cd);
} }
...@@ -281,7 +298,11 @@ static irqreturn_t timer_interrupt(int irq, void *dev_id) ...@@ -281,7 +298,11 @@ static irqreturn_t timer_interrupt(int irq, void *dev_id)
static struct irqaction timer_irqaction = { static struct irqaction timer_irqaction = {
.handler = timer_interrupt, .handler = timer_interrupt,
#ifdef CONFIG_MIPS_MT_SMTC
.flags = IRQF_DISABLED,
#else
.flags = IRQF_DISABLED | IRQF_PERCPU, .flags = IRQF_DISABLED | IRQF_PERCPU,
#endif
.name = "timer", .name = "timer",
}; };
...@@ -316,6 +337,60 @@ void __init __weak plat_timer_setup(struct irqaction *irq) ...@@ -316,6 +337,60 @@ void __init __weak plat_timer_setup(struct irqaction *irq)
{ {
} }
#ifdef CONFIG_MIPS_MT_SMTC
DEFINE_PER_CPU(struct clock_event_device, smtc_dummy_clockevent_device);
static void smtc_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt)
{
}
int dummycnt[NR_CPUS];
static void mips_broadcast(cpumask_t mask)
{
unsigned int cpu;
for_each_cpu_mask(cpu, mask)
smtc_send_ipi(cpu, SMTC_CLOCK_TICK, 0);
}
static void setup_smtc_dummy_clockevent_device(void)
{
//uint64_t mips_freq = mips_hpt_^frequency;
unsigned int cpu = smp_processor_id();
struct clock_event_device *cd;
cd = &per_cpu(smtc_dummy_clockevent_device, cpu);
cd->name = "SMTC";
cd->features = CLOCK_EVT_FEAT_DUMMY;
/* Calculate the min / max delta */
cd->mult = 0; //div_sc((unsigned long) mips_freq, NSEC_PER_SEC, 32);
cd->shift = 0; //32;
cd->max_delta_ns = 0; //clockevent_delta2ns(0x7fffffff, cd);
cd->min_delta_ns = 0; //clockevent_delta2ns(0x30, cd);
cd->rating = 200;
cd->irq = 17; //-1;
// if (cpu)
// cd->cpumask = CPU_MASK_ALL; // cpumask_of_cpu(cpu);
// else
cd->cpumask = cpumask_of_cpu(cpu);
cd->set_mode = smtc_set_mode;
cd->broadcast = mips_broadcast;
clockevents_register_device(cd);
}
#endif
static void mips_event_handler(struct clock_event_device *dev)
{
}
void __cpuinit mips_clockevent_init(void) void __cpuinit mips_clockevent_init(void)
{ {
uint64_t mips_freq = mips_hpt_frequency; uint64_t mips_freq = mips_hpt_frequency;
...@@ -326,12 +401,18 @@ void __cpuinit mips_clockevent_init(void) ...@@ -326,12 +401,18 @@ void __cpuinit mips_clockevent_init(void)
if (!cpu_has_counter) if (!cpu_has_counter)
return; return;
if (cpu == 0) #ifdef CONFIG_MIPS_MT_SMTC
cd = &mips_clockevent; setup_smtc_dummy_clockevent_device();
else
cd = kzalloc(sizeof(*cd), GFP_ATOMIC); /*
if (!cd) * On SMTC we only register VPE0's compare interrupt as clockevent
return; /* We're probably roadkill ... */ * device.
*/
if (cpu)
return;
#endif
cd = &per_cpu(mips_clockevent_device, cpu);
cd->name = "MIPS"; cd->name = "MIPS";
cd->features = CLOCK_EVT_FEAT_ONESHOT; cd->features = CLOCK_EVT_FEAT_ONESHOT;
...@@ -344,11 +425,15 @@ void __cpuinit mips_clockevent_init(void) ...@@ -344,11 +425,15 @@ void __cpuinit mips_clockevent_init(void)
cd->rating = 300; cd->rating = 300;
cd->irq = irq; cd->irq = irq;
#ifdef CONFIG_MIPS_MT_SMTC
cd->cpumask = CPU_MASK_ALL;
#else
cd->cpumask = cpumask_of_cpu(cpu); cd->cpumask = cpumask_of_cpu(cpu);
#endif
cd->set_next_event = mips_next_event; cd->set_next_event = mips_next_event;
cd->set_mode = mips_set_mode; cd->set_mode = mips_set_mode;
cd->event_handler = mips_event_handler;
global_cd[cpu] = cd;
clockevents_register_device(cd); clockevents_register_device(cd);
if (!cp0_timer_irq_installed) { if (!cp0_timer_irq_installed) {
......
...@@ -55,7 +55,6 @@ unsigned long cpu_khz; ...@@ -55,7 +55,6 @@ unsigned long cpu_khz;
static int mips_cpu_timer_irq; static int mips_cpu_timer_irq;
extern int cp0_perfcount_irq; extern int cp0_perfcount_irq;
extern void smtc_timer_broadcast(void);
static void mips_timer_dispatch(void) static void mips_timer_dispatch(void)
{ {
......
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