Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ea580401 authored by Ralf Baechle's avatar Ralf Baechle
Browse files

[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
Loading
Loading
Loading
Loading
+4 −0
Original line number Original line Diff line number Diff line
@@ -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
	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/:
#
#
+32 −35
Original line number Original line Diff line number Diff line
/* 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];
 * 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)
		__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)
		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)


	/* 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)
 * 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)
	"	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)
	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)
			}
			}
			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)
	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)
	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)
	}
	}
}
}


/*
 * 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)
	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)
	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));
+98 −13
Original line number Original line Diff line number Diff line
@@ -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 @@
#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,
                           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,
	/* 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)
	 */
	 */
	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)


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)
{
{
}
}


#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)
	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)


	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) {
+0 −1
Original line number Original line Diff line number Diff line
@@ -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)
{
{