IP30: SMP Help

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



I've recently acquired a dual R14000 CPU for the Octane, so I am trying to get
SMP working again, but I can't get things setup properly.  I've attached both
ip30-irq.c and ip30-smp.c -- does anyone see any immediate problems (or just
where I am doing it wrong)?

Most reboot cycles with this code panics because init exited with a code of 0xa
or 0xb (which matches w/ SIGSEGV or SIGBUS).  Randomly, I can acquire a dash
shell by passing init=/bin/dash.  I can't do much in it, though.  A basic 'ls'
either segfaults or triggers a SIGBUS.  If I execute 'ls' enough times, it
eventually works.  Can't get much farther beyond that.

-- 
Joshua Kinard
Gentoo/MIPS
kumba@xxxxxxxxxx
4096R/D25D95E3 2011-03-28

"The past tempts us, the present confuses us, the future frightens us.  And our
lives slip away, moment by moment, lost in that vast, terrible in-between."

--Emperor Turhan, Centauri Republic
/*
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
 * ip30-irq.c: Highlevel interrupt handling for IP30 architecture.
 *
 * Copyright (C) 2004-2007 Stanislaw Skowronek <skylark@xxxxxxxxxxxxxx>
 *		 2009 Johannes Dickgreber <tanzy@xxxxxx>
 *		 2007-2014 Joshua Kinard <kumba@xxxxxxxxxx>
 *
 * Inspired by ip27-irq.c and ip32-irq.c
 */

#include <linux/init.h>
#include <linux/irq.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/tick.h>

#include <asm/irq_cpu.h>
#include <asm/pci/bridge.h>

#include <asm/mach-ip30/heart.h>
#include <asm/mach-ip30/pcibr.h>
#include <asm/mach-ip30/racermp.h>
#include <asm/mach-ip30/addrs.h>

#undef DEBUG_IRQ

static DEFINE_SPINLOCK(heart_lock);
static int heart_irq_owner[NR_IRQS];
bridge_t *ip30_irq_to_bridge[NR_IRQS];
unsigned int ip30_irq_to_slot[NR_IRQS];

//extern void ip30_clockevent_broadcast(const int cpu);
//extern void ip30_clockevent_broadcast(const cpumask_t mask);

/* ----------------------------------------------------------------------- */

/**
 * ip30_do_error_irq - IRQ dispatch for all HEART error IRQs (51 - 63).
 */
static noinline void
ip30_do_error_irq(void)
{
	unsigned long errors;
	unsigned long cause;
	int i;

	irq_enter();
	errors = readq(HEART_ISR);
	cause = readq(HEART_CAUSE);
	writeq(HEART_INT_LEVEL4, HEART_CLR_ISR);

	/* Refer to ip30-heart.h for the HC_* macros to know what the cause was */
	if (cause > 0) {
		printk(KERN_WARNING "IP30: HEART ATTACK! ISR = 0x%.16llx "
		       "CAUSE = 0x%.16llx\n", (u64)(errors),(u64)(cause));

		/* i = 63; i >= 51; i-- */
		for (i = HEART_ERR_MASK_END; i >= HEART_ERR_MASK_START; i--)
			if ((errors >> i) & 1)
				printk(KERN_CONT "  HEART Error IRQ #%d\n", i);
	}
	irq_exit();
}

/**
 * ip30_do_heart_irq - IRQ dispatch for all HEART L0, L1, L2 IRQs (0 - 49).
 */
static noinline void
ip30_do_heart_irq(void)
{
	int irqnum = 49;
	int cpu = smp_processor_id();
	u64 heart_isr = readq(HEART_ISR);
	u64 heart_imr = readq(HEART_IMR(cpu));
	u64 irqs = (heart_isr &
	            (HEART_INT_LEVEL0 | HEART_INT_LEVEL1 | HEART_INT_LEVEL2) &
	            heart_imr);

#ifdef DEBUG_IRQ
	bridge_t *bvma = (bridge_t *)RAW_NODE_SWIN_BASE(0, 15);

	printk(KERN_INFO "IP30: received HEART IRQs: 0x%016llx "
	                 "(mask 0x%016llx) CPU%d BRIDGE %08x\n",
	                 heart_isr, heart_imr, cpu, bvma->b_int_status);
#endif

	/* Poll all IRQs in decreasing priority order */
	do {
		if (irqs & (1UL << irqnum))
			do_IRQ(irqnum);
		irqnum--;
	} while (likely(irqnum >= 0));
}

/* ----------------------------------------------------------------------- */

/**
 * plat_irq_dispatch - platform IRQ dispatch.
 */
asmlinkage void
plat_irq_dispatch(void)
{
	unsigned long pending;

	pending = (read_c0_cause() & read_c0_status() & ST0_IM);

	/* L5, CPU Counter/Compare */
	if (pending & CAUSEF_IP7)
		do_IRQ(MIPS_CPU_IRQ_BASE);
	/* L3, HEART Counter/Compare */
	else if (likely(pending & CAUSEF_IP5))
		do_IRQ(HEART_L3_INT_TIMER);
	/* L0-L2, HEART normal IRQs + IPI/SMP IRQs*/
	else if (likely(pending & (CAUSEF_IP2 | CAUSEF_IP3 | CAUSEF_IP4)))
		ip30_do_heart_irq();
	/* L4, HEART Errors */
	else if (unlikely(pending & CAUSEF_IP6))
		ip30_do_error_irq();
}

/* ----------------------------------------------------------------------- */



/* ----------------------------------------------------------------------- */
/* HEART IRQ Ops */

/**
 * startup_heart_irq - assigns a HEART IRQ to a CPU and/or Bridge slot.
 * @d: struct irq_data containing IRQ information.
 *
 * Returns 0 for now (XXX: this may need review, possible pending interrupts).
 */
static unsigned int
startup_heart_irq(struct irq_data *d)
{
	bridge_t *bridge;
	int cpu = smp_processor_id();
	u32 device, slot;
	u64 *imr;

	spin_lock(&heart_lock);

	if (heart_irq_owner[d->irq] != -1) {
		printk(KERN_ERR "DEBUG: startup_heart_irq: bad IRQ "
		       "startup request for IRQ %d on CPU %d "
		       "(already assigned to %d)!\n",
		       d->irq, cpu, heart_irq_owner[d->irq]);
		goto out;
	}

	/* store which CPU owns this IRQ */
	heart_irq_owner[d->irq] = cpu;

#ifdef DEBUG_IRQ
	printk(KERN_INFO "DEBUG: IP30: IRQ: startup_heart_irq: "
	       "IRQ %d on CPU %d!\n", d->irq, heart_irq_owner[d->irq]);
#endif

	/* clear IRQ flag */
	writeq(HEART_VEC_TO_IBIT(d->irq), HEART_CLR_ISR);

	/* unmask IRQ */
	imr = HEART_IMR(cpu);
	writeq(readq(imr) | HEART_VEC_TO_IBIT(d->irq), imr);

	/* Handle BRIDGE IRQs. */
	bridge = ip30_irq_to_bridge[d->irq];
	if (bridge) {
		slot = ip30_irq_to_slot[d->irq];
		bridge->b_int_enable |= (1 << slot);
		bridge->b_int_mode |= (1 << slot);
		device = bridge->b_int_device;
		device &= ~BRIDGE_INT_DEV_MASK(slot);
		device |=  BRIDGE_INT_DEV_SET(slot, slot);
		bridge->b_int_device = device;
		bridge->b_widget.w_tflush;
	}

out:
	spin_unlock(&heart_lock);

	/* XXX: This is probably not right; we could have pending irqs */
	return 0;
}

/**
 * shutdown_heart_irq - removes a HEART IRQ from a CPU and/or Bridge slot.
 * @d: struct irq_data containing IRQ information.
 */
static void
shutdown_heart_irq(struct irq_data *d)
{
	bridge_t *bridge;
	u64 *imr;

	spin_lock(&heart_lock);

	imr = HEART_IMR(heart_irq_owner[d->irq]);
	writeq(readq(imr) & ~(HEART_VEC_TO_IBIT(d->irq)), imr);

	bridge = ip30_irq_to_bridge[d->irq];
	if (bridge)
		bridge->b_int_enable &= ~(1UL << ip30_irq_to_slot[d->irq]);

	heart_irq_owner[d->irq] = -1;

	spin_unlock(&heart_lock);
}

/**
 * ack_heart_irq - acks a HEART IRQ.
 * @d: struct irq_data containing IRQ information.
 */
static void
ack_heart_irq(struct irq_data *d)
{

	spin_lock(&heart_lock);
	writeq(HEART_VEC_TO_IBIT(d->irq), HEART_CLR_ISR);
	spin_unlock(&heart_lock);
}

/**
 * mask_heart_irq - masks a HEART IRQ.
 * @d: struct irq_data containing IRQ information.
 */
static void
mask_heart_irq(struct irq_data *d)
{
	u64 *imr;

	spin_lock(&heart_lock);
	imr = HEART_IMR(heart_irq_owner[d->irq]);
	writeq(readq(imr) & ~(HEART_VEC_TO_IBIT(d->irq)), imr);
	spin_unlock(&heart_lock);
}

/**
 * mask_and_ack_heart_irq - masks and acks a HEART IRQ.
 * @d: struct irq_data containing IRQ information.
 */
static void
mask_and_ack_heart_irq(struct irq_data *d)
{
	u64 *imr;

	spin_lock(&heart_lock);
	writeq(HEART_VEC_TO_IBIT(d->irq), HEART_CLR_ISR);
	imr = HEART_IMR(heart_irq_owner[d->irq]);
	writeq(readq(imr) & ~(HEART_VEC_TO_IBIT(d->irq)), imr);
	spin_unlock(&heart_lock);
}

/**
 * unmask_heart_irq - unmasks a HEART IRQ.
 * @d: struct irq_data containing IRQ information.
 */
static void
unmask_heart_irq(struct irq_data *d)
{
	u64 *imr;

	spin_lock(&heart_lock);
	imr = HEART_IMR(heart_irq_owner[d->irq]);
	writeq(readq(imr) | HEART_VEC_TO_IBIT(d->irq), imr);
	spin_unlock(&heart_lock);
}

/**
 * struct ip30_heart_irq - HEART struct irq_chip ops.
 * @irq_startup: startup function.
 * @irq_shutdown: shutdown function.
 * @irq_ack: ack function.
 * @irq_mask: mask function.
 * @irq_mask_ack: mask & ack function.
 * @irq_unmask: unmask function.
 */
static struct
irq_chip ip30_heart_irq = {
	.name = "HEART",
	.irq_startup = startup_heart_irq,
	.irq_shutdown = shutdown_heart_irq,
	.irq_ack = ack_heart_irq,
	.irq_mask = mask_heart_irq,
	.irq_mask_ack = mask_and_ack_heart_irq,
	.irq_unmask = unmask_heart_irq,
};
/* ----------------------------------------------------------------------- */

static inline void ip30_unmask_boot_cpu_irq(struct irq_data *d)
{
	set_c0_status(0x100 << (d->irq - MIPS_CPU_IRQ_BASE));
}

static inline void ip30_mask_boot_cpu_irq(struct irq_data *d)
{
	clear_c0_status(0x100 << (d->irq - MIPS_CPU_IRQ_BASE));
}

static struct irq_chip ip30_boot_cpu_irq_controller = {
	.name		= "IP30-CPU0-IRQ",
	.irq_ack	= ip30_mask_boot_cpu_irq,
	.irq_mask	= ip30_mask_boot_cpu_irq,
	.irq_mask_ack	= ip30_mask_boot_cpu_irq,
	.irq_unmask	= ip30_unmask_boot_cpu_irq,
	.irq_eoi	= ip30_unmask_boot_cpu_irq,
};

/* ----------------------------------------------------------------------- */
/* Arch IRQ initialization - runs on CPU0 only. */

/**
 * arch_init_irq - arch initialization function.
 */
void __init
arch_init_irq(void)
{
	int irq;

	/* Ack everything */
	writeq(HEART_ACK_ALL_MASK, HEART_CLR_ISR);

	/* Mask all IRQs */
	writeq(HEART_CLR_ALL_MASK, HEART_IMR(0));
	writeq(HEART_CLR_ALL_MASK, HEART_IMR(1));
	writeq(HEART_CLR_ALL_MASK, HEART_IMR(2));
	writeq(HEART_CLR_ALL_MASK, HEART_IMR(3));

	/* Leave HEART errors enabled */
	writeq(HEART_BR_ERR_MASK, HEART_IMR(0));

	for (irq = HEART_IRQ_BASE; irq < HEART_IRQS; irq++) {
		heart_irq_owner[irq] = -1;
		irq_set_chip_and_handler(irq, &ip30_heart_irq, handle_level_irq);
	}

	/* Init CPU0 IRQs */
//	mips_cpu_irq_init();
	irq_set_chip_and_handler(MIPS_CPU_IRQ_BASE,
				 &ip30_boot_cpu_irq_controller,
				 handle_percpu_irq);
	change_c0_status(ST0_IM, (STATUSF_IP2 | STATUSF_IP3 | STATUSF_IP4 |
	                          STATUSF_IP5 | STATUSF_IP6 | STATUSF_IP7));

	printk(KERN_INFO "IP30: HEART interrupt controller initialized.\n");
}
/* ----------------------------------------------------------------------- */
/*
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
 * ip30-smp.c: SMP on IP30 architecture.
 *
 * Copyright (C) 2005-2007 Stanislaw Skowronek <skylark@xxxxxxxxxxxxxx>
 *               2006-2007 Joshua Kinard <kumba@xxxxxxxxxx>
 *               2009 Johannes Dickgreber <tanzy@xxxxxx>
 */

#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/tick.h>
#include <linux/spinlock_types.h>

#include <asm/time.h>
#include <asm/cacheflush.h>

#include <asm/mach-ip30/heart.h>
#include <asm/mach-ip30/racermp.h>

static DEFINE_SPINLOCK(ip30_ipi_lock);
//static DEFINE_SPINLOCK(ip30_timer_lock);
static DEFINE_PER_CPU(int, ip30_ipi_action_mask);
static DEFINE_PER_CPU(char[15], ip30_ipi_name);
//static DEFINE_PER_CPU(char[17], ip30_timer_name);

//void ip30_clockevent_broadcast(const int cpu)
//{
//#ifdef CONFIG_SMP
//	/* There can only ever be 2 CPUs in an Octane, so keep it simple. */
//#endif
//}

/* ----------------------------------------------------------------------- */
/* SMP IPI Ops */

/* Runs on CPUx and sends an IPI to CPUy. */
static void ip30_send_ipi_single(int cpu, unsigned int action)
{
//	unsigned long flags;

//	printk(KERN_INFO "DEBUG: IP30: SMP: ip30_send_ipi_single CPU%d -> CPU%d: action: 0x%.8x\n", smp_processor_id(), cpu, action);
//	spin_lock_irqsave(&ip30_ipi_lock, flags);
	spin_lock(&ip30_ipi_lock);
	per_cpu(ip30_ipi_action_mask, cpu) |= action;
	writeq(HEART_VEC_TO_IBIT(HEART_L2_INT_IPI_CPU(cpu)), HEART_SET_ISR);
	spin_unlock(&ip30_ipi_lock);
//	spin_unlock_irqrestore(&ip30_ipi_lock, flags);
}

/* IRQ handler that runs on CPUy and services the IPI. */
static irqreturn_t ip30_ipi_irq(int irq, void *dev_id)
{
	int action;
	int cpu = smp_processor_id();
//	unsigned long flags;

//	spin_lock_irqsave(&ip30_ipi_lock, flags);
	spin_lock(&ip30_ipi_lock);
	action = __get_cpu_var(ip30_ipi_action_mask);
	per_cpu(ip30_ipi_action_mask, cpu) = 0;
	writeq(HEART_VEC_TO_IBIT(HEART_L2_INT_IPI_CPU(cpu)), HEART_CLR_ISR);
	spin_unlock(&ip30_ipi_lock);
//	spin_unlock_irqrestore(&ip30_ipi_lock, flags);

//	printk(KERN_INFO "DEBUG: IP30: SMP: ip30_ipi_irq: cpu%d: action: 0x%.8x\n", cpu, action);

	if (action & SMP_RESCHEDULE_YOURSELF)
		scheduler_ipi();

	if (action & SMP_CALL_FUNCTION)
		smp_call_function_interrupt();

	return IRQ_HANDLED;
}

///* IRQ handler that runs on CPUy and services the CPU Timer. */
//static irqreturn_t ip30_timer_irq(int irq, void *dev_id)
//{
//	int cpu = smp_processor_id();
//	struct clock_event_device *cd;
//
//	spin_lock(&ip30_timer_lock);
//	cd = tick_get_device(cpu)->evtdev;
//	cd->event_handler(cd);
////	printk(KERN_INFO "DEBUG: IP30: SMP: ip30_timer_irq: CPU%d: %s\n", cpu, cd->name);
//	spin_unlock(&ip30_timer_lock);
//	writeq(HEART_VEC_TO_IBIT(HEART_L2_INT_TIMER_CPU(cpu)), HEART_CLR_ISR);
//
//	return IRQ_HANDLED;
//}

static void ip30_send_ipi_mask(const struct cpumask *mask, unsigned int action)
{
	u32 i;

	for_each_cpu(i, mask)
		ip30_send_ipi_single(i, action);
}

static void __init ip30_smp_setup(void)
{
	int i;
	int ncpu = 0;
	int vcpu;

	for (i = 0; i < NR_CPUS; i++) {
		if (readl(MP_MAGIC(i)) == MPCONF_MAGIC) {
			vcpu = readl(MP_VIRTID(i));
			__cpu_number_map[i] = vcpu;
			__cpu_logical_map[vcpu] = i;
			set_cpu_possible(i, true);
			set_cpu_present(i, true);
			printk(KERN_INFO "IP30: Slot: %d, PrID: %.8x, PhyID: %d, VirtID: %d\n",
			       i, readl(MP_PRID(i)), readl(MP_PHYSID(i)), vcpu);
			ncpu++;
		}
	}
	printk(KERN_INFO "IP30: Detected %d CPU(s) present.\n", ncpu);
}

/* Runs on CPU0 */
static void __init ip30_prepare_cpus(unsigned int max_cpus)
{
	int cpu = smp_processor_id();
	unsigned char *ipi_name = per_cpu(ip30_ipi_name, cpu);
//	unsigned char *timer_name = per_cpu(ip30_timer_name, cpu);

	printk(KERN_INFO "DEBUG: IP30: SMP: ip30_prepare_cpus\n");

	/* Request an IRQ number for CPU0 IPI. */
	snprintf(ipi_name, 15, "ip30-cpu%d-ipi", cpu);
	if (request_irq(HEART_L2_INT_IPI_CPU(cpu), ip30_ipi_irq, IRQF_PERCPU,
	                ipi_name, NULL))
		panic("Can't request CPU%d IPI interrupt", cpu);

//	snprintf(timer_name, 17, "ip30-cpu%d-timer", cpu);
//	if (request_irq(HEART_L2_INT_TIMER_CPU(cpu), ip30_timer_irq, IRQF_PERCPU,
//	                timer_name, NULL))
//		panic("Can't request CPU%d Broadcast Timer interrupt", cpu);

	/* Enable the IPI interrupt on CPU0 IP4. */
//	change_c0_status(ST0_IM, STATUSF_IP4);
}

/* Runs on CPU0 and boots CPUx, where x > 0 */
static void ip30_boot_secondary(int cpu, struct task_struct *idle)
{
	int boot_cpu = smp_processor_id();
//	struct thread_info *gp = task_thread_info(idle);

	printk(KERN_INFO "DEBUG: IP30: SMP: ip30_boot_secondary: CPU%d -> CPU%d\n", boot_cpu, cpu);

	/* Stack pointer (sp). */
	writeq(__KSTK_TOS(idle), MP_STACKADDR(cpu));

	/* Global pointer (gp). */
	writeq((unsigned long)task_thread_info(idle), MP_LPARM(cpu));
//	writeq((unsigned long)gp, MP_LPARM(cpu));

	/* XXX: I don't know if this is needed or not.  Copied from elsewhere. */
//	flush_icache_range((unsigned long)gp,
//	                   (unsigned long)(gp + sizeof(struct thread_info)));

	/* Boot CPU1. */
	writeq((unsigned long)ip30_smp_bootstrap, MP_LAUNCH(cpu));
//	mb();

	/* XXX: This is needed to "kick" the CPU awake.  Bug? */
	pr_info("SMP: Booting CPU%d...\n", cpu);
}

/* Runs on CPUx, where x > 0 */
static void ip30_init_secondary(void)
{
	int cpu = smp_processor_id();

	printk(KERN_INFO "DEBUG: IP30: SMP: ip30_init_secondary on CPU%d\n", cpu);

}

static inline void ip30_unmask_2nd_cpu_irq(struct irq_data *d)
{
	set_c0_status(0x100 << (d->irq - (MIPS_CPU_IRQ_BASE + 1)));
}

static inline void ip30_mask_2nd_cpu_irq(struct irq_data *d)
{
	clear_c0_status(0x100 << (d->irq - (MIPS_CPU_IRQ_BASE + 1)));
}

static struct irq_chip ip30_2nd_cpu_irq_controller = {
	.name		= "IP30-CPU1-IRQ",
	.irq_ack	= ip30_mask_2nd_cpu_irq,
	.irq_mask	= ip30_mask_2nd_cpu_irq,
	.irq_mask_ack	= ip30_mask_2nd_cpu_irq,
	.irq_unmask	= ip30_unmask_2nd_cpu_irq,
	.irq_eoi	= ip30_unmask_2nd_cpu_irq,
};

static void ip30_smp_finish(void)
{
	int cpu = smp_processor_id();
	unsigned char *ipi_name = per_cpu(ip30_ipi_name, cpu);
//	unsigned char *timer_name = per_cpu(ip30_timer_name, cpu);

	printk(KERN_INFO "DEBUG: IP30: SMP: ip30_smp_finish: CPU%d\n", cpu);

	irq_set_chip_and_handler((MIPS_CPU_IRQ_BASE + 1),
				 &ip30_2nd_cpu_irq_controller,
				 handle_percpu_irq);
//	write_c0_compare(read_c0_count() + mips_hpt_frequency / HZ);
	change_c0_status(ST0_IM, (STATUSF_IP2 | STATUSF_IP3 | STATUSF_IP4 |
	                          STATUSF_IP5 | STATUSF_IP6 | STATUSF_IP7));
	local_irq_enable();

	/* Request an IRQ number for CPU1 IPI. */
	snprintf(ipi_name, 15, "ip30-cpu%d-ipi", cpu);
	if (request_irq(HEART_L2_INT_IPI_CPU(cpu), ip30_ipi_irq, IRQF_PERCPU,
	                ipi_name, NULL))
		panic("Can't request CPU%d IPI interrupt", cpu);

//	snprintf(timer_name, 17, "ip30-cpu%d-timer", cpu);
//	if (request_irq(HEART_L2_INT_TIMER_CPU(cpu), ip30_timer_irq, IRQF_PERCPU,
//	                timer_name, NULL))
//		panic("Can't request CPU%d Broadcast Timer interrupt", cpu);

	pr_info("SMP: CPU%d is running\n", smp_processor_id());
}

struct plat_smp_ops ip30_smp_ops = {
	.send_ipi_single	= ip30_send_ipi_single,
	.send_ipi_mask		= ip30_send_ipi_mask,
	.init_secondary		= ip30_init_secondary,
	.smp_finish		= ip30_smp_finish,
	.boot_secondary		= ip30_boot_secondary,
	.smp_setup		= ip30_smp_setup,
	.prepare_cpus		= ip30_prepare_cpus,
};

[Index of Archives]     [Linux MIPS Home]     [LKML Archive]     [Linux ARM Kernel]     [Linux ARM]     [Linux]     [Git]     [Yosemite News]     [Linux SCSI]     [Linux Hams]

  Powered by Linux