Skip to content

Commit

Permalink
[MIPS] Dyntick support for SMTC:
Browse files Browse the repository at this point in the history
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: Ralf Baechle <ralf@linux-mips.org>
  • Loading branch information
Ralf Baechle committed Oct 11, 2007
1 parent 7bcf771 commit ea58040
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 49 deletions.
4 changes: 4 additions & 0 deletions arch/mips/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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/:
#
Expand Down
67 changes: 32 additions & 35 deletions arch/mips/kernel/smtc.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* Copyright (C) 2004 Mips Technologies, Inc */

#include <linux/clockchips.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/cpumask.h>
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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... */

Expand Down Expand Up @@ -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;

Expand All @@ -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;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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();
Expand All @@ -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();
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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]++;
}
}
Expand Down Expand Up @@ -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));
Expand Down
111 changes: 98 additions & 13 deletions arch/mips/kernel/time.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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>
Expand Down Expand Up @@ -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,
Expand All @@ -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)
Expand All @@ -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);
}

Expand All @@ -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",
};

Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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) {
Expand Down
1 change: 0 additions & 1 deletion arch/mips/mips-boards/generic/time.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down

0 comments on commit ea58040

Please sign in to comment.