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, };