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
depends on CPU_MIPS32_R2
#depends on CPU_MIPS64_R2 # once there is hardware ...
depends on SYS_SUPPORTS_MULTITHREADING
select GENERIC_CLOCKEVENTS_BROADCAST
select CPU_MIPSR2_IRQ_VI
select CPU_MIPSR2_IRQ_EI
select CPU_MIPSR2_SRS
......@@ -1537,6 +1538,9 @@ config CPU_HAS_SYNC
depends on !CPU_R3000
default y
config GENERIC_CLOCKEVENTS_BROADCAST
bool
#
# Use the generic interrupt handling code in kernel/irq/:
#
......
/* Copyright (C) 2004 Mips Technologies, Inc */
#include <linux/clockchips.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/cpumask.h>
......@@ -62,7 +63,7 @@ asiduse smtc_live_asid[MAX_SMTC_TLBS][MAX_SMTC_ASIDS];
* 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
......@@ -296,8 +297,10 @@ int __init mipsmt_build_cpu_map(int start_cpu_slot)
__cpu_number_map[i] = i;
__cpu_logical_map[i] = i;
}
#ifdef CONFIG_MIPS_MT_FPAFF
/* Initialize map of CPUs with FPUs */
cpus_clear(mt_fpu_cpumask);
#endif
/* One of those TC's is the one booting, and not a secondary... */
printk("%i available secondary CPU TC(s)\n", i - 1);
......@@ -359,7 +362,7 @@ void mipsmt_prepare_cpus(void)
IPIQ[i].head = IPIQ[i].tail = NULL;
spin_lock_init(&IPIQ[i].lock);
IPIQ[i].depth = 0;
ipi_timer_latch[i] = 0;
atomic_set(&ipi_timer_latch[i], 0);
}
/* cpu_data index starts at zero */
......@@ -482,10 +485,12 @@ void mipsmt_prepare_cpus(void)
/* Set up coprocessor affinity CPU mask(s) */
#ifdef CONFIG_MIPS_MT_FPAFF
for (tc = 0; tc < ntc; tc++) {
if (cpu_data[tc].options & MIPS_CPU_FPU)
cpu_set(tc, mt_fpu_cpumask);
}
#endif
/* set up ipi interrupts... */
......@@ -702,7 +707,7 @@ static void smtc_ipi_qdump(void)
* be done with the atomic.h primitives). And since this is
* 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;
......@@ -714,8 +719,8 @@ static __inline__ int atomic_postincrement(unsigned int *pv)
" sc %1, %2 \n"
" beqz %1, 1b \n"
__WEAK_LLSC_MB
: "=&r" (result), "=&r" (temp), "=m" (*pv)
: "m" (*pv)
: "=&r" (result), "=&r" (temp), "=m" (v->counter)
: "m" (v->counter)
: "memory");
return result;
......@@ -743,6 +748,8 @@ void smtc_send_ipi(int cpu, int type, unsigned int action)
pipi->arg = (void *)action;
pipi->dest = cpu;
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 */
smtc_ipi_nq(&IPIQ[cpu], pipi);
LOCK_CORE_PRA();
......@@ -784,6 +791,8 @@ void smtc_send_ipi(int cpu, int type, unsigned int action)
}
smtc_ipi_nq(&IPIQ[cpu], pipi);
} else {
if (type == SMTC_CLOCK_TICK)
atomic_inc(&ipi_timer_latch[cpu]);
post_direct_ipi(cpu, pipi);
write_tc_c0_tchalt(0);
UNLOCK_CORE_PRA();
......@@ -801,6 +810,7 @@ static void post_direct_ipi(int cpu, struct smtc_ipi *pipi)
unsigned long tcrestart;
extern u32 kernelsp[NR_CPUS];
extern void __smtc_ipi_vector(void);
//printk("%s: on %d for %d\n", __func__, smp_processor_id(), cpu);
/* Extract Status, EPC from halted TC */
tcstatus = read_tc_c0_tcstatus();
......@@ -851,25 +861,31 @@ static void ipi_call_interrupt(void)
smp_call_function_interrupt();
}
DECLARE_PER_CPU(struct clock_event_device, smtc_dummy_clockevent_device);
void ipi_decode(struct smtc_ipi *pipi)
{
unsigned int cpu = smp_processor_id();
struct clock_event_device *cd;
void *arg_copy = pipi->arg;
int type_copy = pipi->type;
int dest_copy = pipi->dest;
int ticks;
smtc_ipi_nq(&freeIPIq, pipi);
switch (type_copy) {
case SMTC_CLOCK_TICK:
irq_enter();
kstat_this_cpu.irqs[MIPS_CPU_IRQ_BASE + cp0_compare_irq]++;
/* Invoke Clock "Interrupt" */
ipi_timer_latch[dest_copy] = 0;
#ifdef CONFIG_SMTC_IDLE_HOOK_DEBUG
clock_hang_reported[dest_copy] = 0;
#endif /* CONFIG_SMTC_IDLE_HOOK_DEBUG */
local_timer_interrupt(0, NULL);
kstat_this_cpu.irqs[MIPS_CPU_IRQ_BASE + 1]++;
cd = &per_cpu(smtc_dummy_clockevent_device, cpu);
ticks = atomic_read(&ipi_timer_latch[cpu]);
atomic_sub(ticks, &ipi_timer_latch[cpu]);
while (ticks) {
cd->event_handler(cd);
ticks--;
}
irq_exit();
break;
case LINUX_SMP_IPI:
switch ((int)arg_copy) {
case SMP_RESCHEDULE_YOURSELF:
......@@ -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"
* set via cross-VPE MTTR manipulation of the Cause register. It would be
......@@ -1180,11 +1177,11 @@ void smtc_idle_loop_hook(void)
for (tc = 0; tc < NR_CPUS; tc++) {
/* Don't check ourself - we'll dequeue IPIs just below */
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) {
pdb_msg += sprintf(pdb_msg,
"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]++;
}
}
......@@ -1225,7 +1222,7 @@ void smtc_soft_dump(void)
smtc_ipi_qdump();
printk("Timer IPI Backlogs:\n");
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",
atomic_read(&smtc_fpu_recoveries));
......
......@@ -25,6 +25,7 @@
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/kallsyms.h>
#include <asm/bootinfo.h>
#include <asm/cache.h>
......@@ -33,6 +34,7 @@
#include <asm/cpu-features.h>
#include <asm/div64.h>
#include <asm/sections.h>
#include <asm/smtc_ipi.h>
#include <asm/time.h>
#include <irq.h>
......@@ -230,12 +232,24 @@ static int mips_next_event(unsigned long delta,
struct clock_event_device *evt)
{
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 += delta;
write_c0_compare(cnt);
return ((long)(read_c0_count() - cnt ) > 0) ? -ETIME : 0;
res = ((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,
......@@ -244,9 +258,7 @@ static void mips_set_mode(enum clock_event_mode mode,
/* Nothing to do ... */
}
struct clock_event_device mips_clockevent;
static struct clock_event_device *global_cd[NR_CPUS];
static DEFINE_PER_CPU(struct clock_event_device, mips_clockevent_device);
static int cp0_timer_irq_installed;
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))) {
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);
}
......@@ -281,7 +298,11 @@ static irqreturn_t timer_interrupt(int irq, void *dev_id)
static struct irqaction timer_irqaction = {
.handler = timer_interrupt,
#ifdef CONFIG_MIPS_MT_SMTC
.flags = IRQF_DISABLED,
#else
.flags = IRQF_DISABLED | IRQF_PERCPU,
#endif
.name = "timer",
};
......@@ -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)
{
uint64_t mips_freq = mips_hpt_frequency;
......@@ -326,12 +401,18 @@ void __cpuinit mips_clockevent_init(void)
if (!cpu_has_counter)
return;
if (cpu == 0)
cd = &mips_clockevent;
else
cd = kzalloc(sizeof(*cd), GFP_ATOMIC);
if (!cd)
return; /* We're probably roadkill ... */
#ifdef CONFIG_MIPS_MT_SMTC
setup_smtc_dummy_clockevent_device();
/*
* On SMTC we only register VPE0's compare interrupt as clockevent
* device.
*/
if (cpu)
return;
#endif
cd = &per_cpu(mips_clockevent_device, cpu);
cd->name = "MIPS";
cd->features = CLOCK_EVT_FEAT_ONESHOT;
......@@ -344,11 +425,15 @@ void __cpuinit mips_clockevent_init(void)
cd->rating = 300;
cd->irq = irq;
#ifdef CONFIG_MIPS_MT_SMTC
cd->cpumask = CPU_MASK_ALL;
#else
cd->cpumask = cpumask_of_cpu(cpu);
#endif
cd->set_next_event = mips_next_event;
cd->set_mode = mips_set_mode;
cd->event_handler = mips_event_handler;
global_cd[cpu] = cd;
clockevents_register_device(cd);
if (!cp0_timer_irq_installed) {
......
......@@ -55,7 +55,6 @@ unsigned long cpu_khz;
static int mips_cpu_timer_irq;
extern int cp0_perfcount_irq;
extern void smtc_timer_broadcast(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