Commit 568123f8 authored by Steven Seeger's avatar Steven Seeger
Browse files

add ipipe files

parent c5b069e0
......@@ -220,6 +220,7 @@ config PPC
select HAVE_SYSCALL_TRACEPOINTS
select HAVE_VIRT_CPU_ACCOUNTING
select HAVE_IRQ_TIME_ACCOUNTING
select HAVE_IPIPE_SUPPORT
select IRQ_DOMAIN
select IRQ_FORCED_THREADING
select MODULES_USE_ELF_RELA
......
/*
* include/asm-powerpc/ipipe.h
*
* I-pipe 32/64bit merge - Copyright (C) 2007 Philippe Gerum.
* I-pipe PA6T support - Copyright (C) 2007 Philippe Gerum.
* I-pipe 64-bit PowerPC port - Copyright (C) 2005 Heikki Lindholm.
* I-pipe PowerPC support - Copyright (C) 2002-2005 Philippe Gerum.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
* USA; either version 2 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __ASM_POWERPC_IPIPE_H
#define __ASM_POWERPC_IPIPE_H
#ifdef CONFIG_IPIPE
#include <asm/ptrace.h>
#include <asm/hw_irq.h>
#include <asm/irq.h>
#include <asm/bitops.h>
#include <asm/time.h>
#include <linux/ipipe_domain.h>
#include <linux/irq.h>
#include <linux/list.h>
#include <linux/cpumask.h>
#include <linux/cache.h>
#include <linux/threads.h>
#define IPIPE_CORE_RELEASE 2
struct ipipe_domain;
struct ipipe_arch_sysinfo {
};
#if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC)
extern cpumask_t __ipipe_dbrk_pending;
#endif
extern unsigned long __ipipe_hrtimer_freq;
#define __ipipe_hrclock_freq ppc_tb_freq
#define __ipipe_cpu_freq ppc_proc_freq
#define ipipe_read_tsc(t) \
({ \
unsigned long __tbu; \
__asm__ __volatile__ ("1: mftbu %0\n" \
"mftb %1\n" \
"mftbu %2\n" \
"cmpw %2,%0\n" \
"bne- 1b\n" \
:"=r" (((unsigned long *)&t)[0]), \
"=r" (((unsigned long *)&t)[1]), \
"=r" (__tbu)); \
t; \
})
#define ipipe_tsc2ns(t) \
((((unsigned long)(t)) * 1000) / (ppc_tb_freq / 1000000))
#define ipipe_tsc2us(t) \
({ \
unsigned long long delta = (t); \
do_div(delta, ppc_tb_freq/1000000+1); \
(unsigned long)delta; \
})
static inline const char *ipipe_clock_name(void)
{
return "timebase";
}
/* Private interface -- Internal use only */
#define __ipipe_enable_irq(irq) enable_irq(irq)
#define __ipipe_disable_irq(irq) disable_irq(irq)
#define __ipipe_enable_irqdesc(ipd, irq) do { } while(0)
#define __ipipe_disable_irqdesc(ipd, irq) do { } while(0)
void __ipipe_early_core_setup(void);
void __ipipe_enable_pipeline(void);
#ifdef CONFIG_SMP
struct ipipe_ipi_struct {
volatile unsigned long value;
} ____cacheline_aligned;
void __ipipe_hook_critical_ipi(struct ipipe_domain *ipd);
void __ipipe_register_mux_ipi(unsigned int irq);
void __ipipe_finish_ipi_demux(unsigned int irq);
#else
#define __ipipe_hook_critical_ipi(ipd) do { } while(0)
#endif /* CONFIG_SMP */
void __ipipe_dispatch_irq(unsigned int irq, int flags);
void __ipipe_handle_irq(unsigned int irq, struct pt_regs *regs);
struct irq_desc;
void __ipipe_ack_level_irq(struct irq_desc *desc);
void __ipipe_end_level_irq(struct irq_desc *desc);
void __ipipe_ack_edge_irq(struct irq_desc *desc);
void __ipipe_end_edge_irq(struct irq_desc *desc);
static inline unsigned long __ipipe_ffnz(unsigned long ul)
{
__asm__ __volatile__("cntlzw %0, %1":"=r"(ul):"r"(ul & (-ul)));
return 31 - ul;
}
#define __ipipe_root_tick_p(regs) ((regs)->msr & MSR_EE)
static inline void ipipe_notify_root_preemption(void) { }
#endif /* !CONFIG_IPIPE */
#endif /* !__ASM_POWERPC_IPIPE_H */
/* -*- linux-c -*-
* include/asm-powerpc/ipipe_base.h
*
* Copyright (C) 2007-2012 Philippe Gerum.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
* USA; either version 2 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __ASM_POWERPC_IPIPE_BASE_H
#define __ASM_POWERPC_IPIPE_BASE_H
#include <asm-generic/ipipe.h>
#ifdef CONFIG_IPIPE
#define IPIPE_NR_ROOT_IRQS CONFIG_NR_IRQS
#define IPIPE_NR_XIRQS IPIPE_NR_ROOT_IRQS
#define IPIPE_IRQ_ISHIFT 5 /* 32-bit arch. */
/*
* The first virtual interrupt is reserved for the timer (see
* __ipipe_early_core_setup).
*/
#define IPIPE_TIMER_VIRQ (IPIPE_VIRQ_BASE + 0)
#define IPIPE_DOORBELL_VIRQ (IPIPE_VIRQ_BASE + 1)
#ifdef CONFIG_SMP
/*
* These are virtual IPI numbers. The OpenPIC supports only 4 IPIs and
* all are already used by Linux. The virtualization layer is
* implemented by piggybacking the debugger break IPI 0x3,
* which is demultiplexed in __ipipe_ipi_demux().
*/
#define IPIPE_CRITICAL_IPI (IPIPE_VIRQ_BASE + 2)
#define IPIPE_HRTIMER_IPI (IPIPE_VIRQ_BASE + 3)
#define IPIPE_RESCHEDULE_IPI (IPIPE_VIRQ_BASE + 4)
#define IPIPE_BASE_IPI_OFFSET IPIPE_CRITICAL_IPI
/* these are bit numbers in practice */
#define IPIPE_MSG_CRITICAL_IPI 0
#define IPIPE_MSG_HRTIMER_IPI (IPIPE_MSG_CRITICAL_IPI + 1)
#define IPIPE_MSG_RESCHEDULE_IPI (IPIPE_MSG_CRITICAL_IPI + 2)
#define IPIPE_MSG_IPI_MASK ((1UL << IPIPE_MSG_CRITICAL_IPI) | \
(1UL << IPIPE_MSG_HRTIMER_IPI) | \
(1UL << IPIPE_MSG_RESCHEDULE_IPI))
#define ipipe_processor_id() raw_smp_processor_id()
#else /* !CONFIG_SMP */
#define ipipe_processor_id() 0
#endif /* CONFIG_SMP */
/* traps */
#define IPIPE_TRAP_ACCESS 0 /* Data or instruction access exception */
#define IPIPE_TRAP_ALIGNMENT 1 /* Alignment exception */
#define IPIPE_TRAP_ALTUNAVAIL 2 /* Altivec unavailable */
#define IPIPE_TRAP_PCE 3 /* Program check exception */
#define IPIPE_TRAP_MCE 4 /* Machine check exception */
#define IPIPE_TRAP_UNKNOWN 5 /* Unknown exception */
#define IPIPE_TRAP_IABR 6 /* Instruction breakpoint */
#define IPIPE_TRAP_RM 7 /* Run mode exception */
#define IPIPE_TRAP_SSTEP 8 /* Single-step exception */
#define IPIPE_TRAP_NREC 9 /* Non-recoverable exception */
#define IPIPE_TRAP_SOFTEMU 10 /* Software emulation */
#define IPIPE_TRAP_DEBUG 11 /* Debug exception */
#define IPIPE_TRAP_SPE 12 /* SPE exception */
#define IPIPE_TRAP_ALTASSIST 13 /* Altivec assist exception */
#define IPIPE_TRAP_CACHE 14 /* Cache-locking exception (FSL) */
#define IPIPE_TRAP_KFPUNAVAIL 15 /* FP unavailable exception */
#define IPIPE_TRAP_MAYDAY 16 /* Internal recovery trap */
#define IPIPE_NR_FAULTS 17
#ifndef __ASSEMBLY__
#ifdef CONFIG_SMP
#else /* !CONFIG_SMP */
#include <linux/bitops.h>
extern unsigned long __ipipe_root_status;
#endif /* !CONFIG_SMP */
#endif /* !__ASSEMBLY__ */
#endif /* !CONFIG_IPIPE */
#endif /* !__ASM_POWERPC_IPIPE_BASE_H */
/* -*- linux-c -*-
* include/asm-powerpc/ipipe_hwirq.h
*
* Copyright (C) 2009 Philippe Gerum.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
* USA; either version 2 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _ASM_POWERPC_IPIPE_HWIRQ_H
#define _ASM_POWERPC_IPIPE_HWIRQ_H
#include <asm-generic/ipipe.h>
#ifdef CONFIG_IPIPE
#include <linux/ipipe_trace.h>
#if defined(CONFIG_BOOKE)
#define hard_local_irq_restore_notrace(x) __asm__ __volatile__("wrtee %0" : : "r" (x) : "memory")
#else
#define hard_local_irq_restore_notrace(x) mtmsr(x)
#endif
static inline void hard_local_irq_disable_notrace(void)
{
#ifdef CONFIG_BOOKE
__asm__ __volatile__("wrteei 0": : :"memory");
#else
unsigned long msr = mfmsr();
mtmsr(msr & ~MSR_EE);
#endif
}
static inline void hard_local_irq_enable_notrace(void)
{
#ifdef CONFIG_BOOKE
__asm__ __volatile__("wrteei 1": : :"memory");
#else
unsigned long msr = mfmsr();
mtmsr(msr | MSR_EE);
#endif
}
static inline unsigned long hard_local_irq_save_notrace(void)
{
unsigned long msr = mfmsr();
#ifdef CONFIG_BOOKE
__asm__ __volatile__("wrteei 0": : :"memory");
#else
mtmsr(msr & ~MSR_EE);
#endif
return msr;
}
static inline int arch_irqs_disabled_flags(unsigned long flags)
{
return (flags & MSR_EE) == 0;
}
static inline unsigned long arch_local_irq_disable(void)
{
unsigned long flags;
flags = (!ipipe_test_and_stall_root()) << MSR_EE_LG;
barrier();
return flags;
}
static inline void arch_local_irq_enable(void)
{
barrier();
ipipe_unstall_root();
}
static inline void arch_local_irq_restore(unsigned long flags)
{
barrier();
if (!arch_irqs_disabled_flags(flags))
ipipe_unstall_root();
}
static inline unsigned long arch_local_irq_save(void)
{
return arch_local_irq_disable();
}
static inline unsigned long arch_local_save_flags(void)
{
return (!ipipe_test_root()) << MSR_EE_LG;
}
static inline int arch_irqs_disabled(void)
{
unsigned long flags = arch_local_save_flags();
return arch_irqs_disabled_flags(flags);
}
static inline unsigned long arch_mangle_irq_bits(int stalled, unsigned long msr)
{
/* Merge virtual and real interrupt mask bits. */
return (msr & ~MSR_VIRTEE) | ((long)(stalled == 0) << MSR_VIRTEE_LG);
}
static inline int arch_demangle_irq_bits(unsigned long *flags)
{
int stalled = (*flags & MSR_VIRTEE) == 0;
*flags &= ~MSR_VIRTEE;
return stalled;
}
static inline unsigned long hard_local_save_flags(void)
{
return mfmsr();
}
static inline int hard_irqs_disabled_flags(unsigned long flags)
{
return (flags & MSR_EE) == 0;
}
static inline int hard_irqs_disabled(void)
{
unsigned long flags = hard_local_save_flags();
return hard_irqs_disabled_flags(flags);
}
#ifdef CONFIG_IPIPE_TRACE_IRQSOFF
static inline void hard_local_irq_disable(void)
{
if (!hard_irqs_disabled()) {
hard_local_irq_disable_notrace();
ipipe_trace_begin(0x80000000);
}
}
static inline void hard_local_irq_enable(void)
{
if (hard_irqs_disabled()) {
ipipe_trace_end(0x80000000);
hard_local_irq_enable_notrace();
}
}
static inline unsigned long hard_local_irq_save(void)
{
unsigned long flags;
flags = hard_local_irq_save_notrace();
if (flags & MSR_EE)
ipipe_trace_begin(0x80000001);
return flags;
}
static inline void hard_local_irq_restore(unsigned long flags)
{
if (flags & MSR_EE)
ipipe_trace_end(0x80000001);
hard_local_irq_restore_notrace(flags);
}
#else /* !CONFIG_IPIPE_TRACE_IRQSOFF */
#define hard_local_irq_disable hard_local_irq_disable_notrace
#define hard_local_irq_enable hard_local_irq_enable_notrace
#define hard_local_irq_save hard_local_irq_save_notrace
#define hard_local_irq_restore hard_local_irq_restore_notrace
#endif /* CONFIG_IPIPE_TRACE_IRQSOFF */
#endif /* !CONFIG_IPIPE */
#endif /* !_ASM_POWERPC_IPIPE_HWIRQ_H */
/* -*- linux-c -*-
* linux/arch/powerpc/kernel/ipipe.c
*
* Copyright (C) 2005 Heikki Lindholm (PPC64 port).
* Copyright (C) 2004 Wolfgang Grandegger (Adeos/ppc port over 2.4).
* Copyright (C) 2002-2012 Philippe Gerum.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
* USA; either version 2 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Architecture-dependent I-PIPE core support for PowerPC 32/64bit.
*/
#include <linux/kernel.h>
#include <linux/smp.h>
#include <linux/sched.h>
#include <linux/ipipe.h>
#include <linux/slab.h>
#include <linux/bitops.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/kernel_stat.h>
#include <linux/ipipe_tickdev.h>
#include <asm/reg.h>
#include <asm/switch_to.h>
#include <asm/mmu_context.h>
#include <asm/unistd.h>
#include <asm/machdep.h>
#include <asm/atomic.h>
#include <asm/hardirq.h>
#include <asm/io.h>
#include <asm/time.h>
#include <asm/runlatch.h>
#include <asm/debug.h>
#include <asm/dbell.h>
#include <linux/sched/debug.h>
static void __ipipe_do_IRQ(unsigned int irq, void *cookie);
static void __ipipe_do_timer(unsigned int irq, void *cookie);
#ifdef CONFIG_PPC_DOORBELL
static void __ipipe_do_doorbell(unsigned int irq, void *cookie);
#endif
#define DECREMENTER_MAX 0x7fffffff
void __ipipe_handle_irq(unsigned int irq, struct pt_regs *regs)
{
/* NULL regs means software-triggered, no ack needed. */
__ipipe_dispatch_irq(irq, regs ? 0 : IPIPE_IRQF_NOACK);
}
#ifdef CONFIG_SMP
static DEFINE_PER_CPU(struct ipipe_ipi_struct, ipipe_ipi_message);
#if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC)
cpumask_t __ipipe_dbrk_pending; /* pending debugger break IPIs */
#endif
static unsigned int mux_ipi;
void __ipipe_register_mux_ipi(unsigned int irq)
{
mux_ipi = irq;
}
void __ipipe_hook_critical_ipi(struct ipipe_domain *ipd)
{
unsigned int ipi = IPIPE_CRITICAL_IPI;
ipd->irqs[ipi].ackfn = NULL;
ipd->irqs[ipi].handler = __ipipe_do_critical_sync;
ipd->irqs[ipi].cookie = NULL;
ipd->irqs[ipi].control = IPIPE_HANDLE_MASK|IPIPE_STICKY_MASK;
}
static void do_ipi_demux(int irq, struct pt_regs *regs)
{
int cpu __maybe_unused = ipipe_processor_id(), ipi;
while (this_cpu_ptr(&ipipe_ipi_message)->value & IPIPE_MSG_IPI_MASK) {
for (ipi = IPIPE_MSG_CRITICAL_IPI;
ipi <= IPIPE_MSG_RESCHEDULE_IPI; ++ipi) {
if (test_and_clear_bit(ipi,
&this_cpu_ptr(&ipipe_ipi_message)->value)) {
mb();
__ipipe_handle_irq(ipi + IPIPE_BASE_IPI_OFFSET, NULL);
}
}
}
#if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC)
/*
* The debugger IPI handler should be NMI-safe, so let's call
* it immediately in case the IPI is pending.
*/
if (cpumask_test_cpu(cpu, &__ipipe_dbrk_pending)) {
cpumask_clear_cpu(cpu, &__ipipe_dbrk_pending);
debugger_ipi(regs);
}
#endif /* CONFIG_DEBUGGER || CONFIG_KEXEC */
__ipipe_finish_ipi_demux(irq);
}
void ipipe_set_irq_affinity(unsigned int irq, cpumask_t cpumask)
{
if (ipipe_virtual_irq_p(irq) ||
irq_get_chip(irq)->irq_set_affinity == NULL)
return;
if (WARN_ON_ONCE(cpumask_any_and(&cpumask, cpu_online_mask) >= nr_cpu_ids))
return;
irq_get_chip(irq)->irq_set_affinity(irq_get_irq_data(irq), &cpumask, true);
}
EXPORT_SYMBOL_GPL(ipipe_set_irq_affinity);
void ipipe_send_ipi(unsigned int ipi, cpumask_t cpumask)
{
unsigned long flags;
int cpu, me;
flags = hard_local_irq_save();
me = ipipe_processor_id();
ipi -= IPIPE_BASE_IPI_OFFSET;
for_each_cpu(cpu, &cpumask) {
if (cpu == me)
continue;
set_bit(ipi, &per_cpu(ipipe_ipi_message, cpu).value);
if (smp_ops->message_pass)
smp_ops->message_pass(cpu, PPC_MSG_IPIPE_DEMUX);
#ifdef CONFIG_PPC_SMP_MUXED_IPI
else
smp_muxed_ipi_message_pass(cpu, PPC_MSG_IPIPE_DEMUX);
#endif
}
hard_local_irq_restore(flags);
}
EXPORT_SYMBOL_GPL(ipipe_send_ipi);
#endif /* !CONFIG_SMP */
void __ipipe_early_core_setup(void)
{
unsigned int virq;
/*
* Allocate all the virtual IRQs we need. We expect fixed virq
* numbers starting at IPIPE_VIRQ_BASE, so we request them
* early.
*/
virq = ipipe_alloc_virq();
BUG_ON(virq != IPIPE_TIMER_VIRQ);
/*
* Although not all CPUs define the doorbell event, we always
* allocate the corresponding VIRQ, so that we can keep fixed
* values for all VIRQ numbers.
*/
virq = ipipe_alloc_virq();
BUG_ON(virq != IPIPE_DOORBELL_VIRQ);
#ifdef CONFIG_SMP
virq = ipipe_alloc_virq();
BUG_ON(virq != IPIPE_CRITICAL_IPI);
virq = ipipe_alloc_virq();
BUG_ON(virq != IPIPE_HRTIMER_IPI);
virq = ipipe_alloc_virq();
BUG_ON(virq != IPIPE_RESCHEDULE_IPI);
#endif
}
/*
* __ipipe_enable_pipeline() -- We are running on the boot CPU, hw
* interrupts are off, and secondary CPUs are still lost in space.
*/
void __ipipe_enable_pipeline(void)