summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2011-05-26 23:11:17 +0400
committerLinus Torvalds <torvalds@linux-foundation.org>2011-05-26 23:11:17 +0400
commit6ddb4518c7af7b03fa322552d794f759cd5c26fa (patch)
tree4560d1425733243520c97aade1d69c6739b13b11 /arch
parentbe93d8cfbae1996052e91b2883d306a5d9d0fe18 (diff)
parent3fb7933850faf1017c59a675e895ed8f27fef4be (diff)
downloadlinux-6ddb4518c7af7b03fa322552d794f759cd5c26fa.tar.xz
Merge branch 'merge' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc
* 'merge' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc: powerpc/4xx: Adding PCIe MSI support powerpc: Fix irq_free_virt by adjusting bounds before loop powerpc/irq: Protect irq_radix_revmap_lookup against irq_free_virt powerpc/irq: Check desc in handle_one_irq and expand generic_handle_irq powerpc/irq: Always free duplicate IRQ_LEGACY hosts powerpc/irq: Remove stale and misleading comment powerpc/cell: Rename ipi functions to match current abstractions powerpc/cell: Use common smp ipi actions Remove unused MSG_ flags in linux/smp.h powerpc/pseries: Update MAX_HCALL_OPCODE to reflect page coalescing powerpc/oprofile: Handle events that raise an exception without overflowing powerpc/ftrace: Implement raw syscall tracepoints on PowerPC
Diffstat (limited to 'arch')
-rw-r--r--arch/powerpc/Kconfig1
-rw-r--r--arch/powerpc/boot/dts/canyonlands.dts18
-rw-r--r--arch/powerpc/boot/dts/katmai.dts18
-rw-r--r--arch/powerpc/boot/dts/kilauea.dts28
-rw-r--r--arch/powerpc/boot/dts/redwood.dts20
-rw-r--r--arch/powerpc/include/asm/ftrace.h14
-rw-r--r--arch/powerpc/include/asm/hvcall.h2
-rw-r--r--arch/powerpc/include/asm/smp.h2
-rw-r--r--arch/powerpc/include/asm/syscall.h5
-rw-r--r--arch/powerpc/include/asm/thread_info.h7
-rw-r--r--arch/powerpc/kernel/Makefile1
-rw-r--r--arch/powerpc/kernel/ftrace.c8
-rw-r--r--arch/powerpc/kernel/irq.c46
-rw-r--r--arch/powerpc/kernel/ptrace.c10
-rw-r--r--arch/powerpc/kernel/smp.c2
-rw-r--r--arch/powerpc/oprofile/op_model_power4.c24
-rw-r--r--arch/powerpc/platforms/40x/Kconfig2
-rw-r--r--arch/powerpc/platforms/44x/Kconfig6
-rw-r--r--arch/powerpc/platforms/cell/interrupt.c55
-rw-r--r--arch/powerpc/platforms/cell/interrupt.h2
-rw-r--r--arch/powerpc/platforms/cell/smp.c2
-rw-r--r--arch/powerpc/sysdev/Kconfig7
-rw-r--r--arch/powerpc/sysdev/Makefile1
-rw-r--r--arch/powerpc/sysdev/ppc4xx_msi.c276
24 files changed, 491 insertions, 66 deletions
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 423145a6f7ba..2f6a22e8e935 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -141,6 +141,7 @@ config PPC
select GENERIC_IRQ_SHOW
select GENERIC_IRQ_SHOW_LEVEL
select HAVE_RCU_TABLE_FREE if SMP
+ select HAVE_SYSCALL_TRACEPOINTS
config EARLY_PRINTK
bool
diff --git a/arch/powerpc/boot/dts/canyonlands.dts b/arch/powerpc/boot/dts/canyonlands.dts
index 2779f08313a5..22dd6ae84da0 100644
--- a/arch/powerpc/boot/dts/canyonlands.dts
+++ b/arch/powerpc/boot/dts/canyonlands.dts
@@ -530,5 +530,23 @@
0x0 0x0 0x0 0x3 &UIC3 0x12 0x4 /* swizzled int C */
0x0 0x0 0x0 0x4 &UIC3 0x13 0x4 /* swizzled int D */>;
};
+
+ MSI: ppc4xx-msi@C10000000 {
+ compatible = "amcc,ppc4xx-msi", "ppc4xx-msi";
+ reg = < 0xC 0x10000000 0x100>;
+ sdr-base = <0x36C>;
+ msi-data = <0x00000000>;
+ msi-mask = <0x44440000>;
+ interrupt-count = <3>;
+ interrupts = <0 1 2 3>;
+ interrupt-parent = <&UIC3>;
+ #interrupt-cells = <1>;
+ #address-cells = <0>;
+ #size-cells = <0>;
+ interrupt-map = <0 &UIC3 0x18 1
+ 1 &UIC3 0x19 1
+ 2 &UIC3 0x1A 1
+ 3 &UIC3 0x1B 1>;
+ };
};
};
diff --git a/arch/powerpc/boot/dts/katmai.dts b/arch/powerpc/boot/dts/katmai.dts
index 7c3be5e45748..f913dbe25d35 100644
--- a/arch/powerpc/boot/dts/katmai.dts
+++ b/arch/powerpc/boot/dts/katmai.dts
@@ -442,6 +442,24 @@
0x0 0x0 0x0 0x4 &UIC3 0xb 0x4 /* swizzled int D */>;
};
+ MSI: ppc4xx-msi@400300000 {
+ compatible = "amcc,ppc4xx-msi", "ppc4xx-msi";
+ reg = < 0x4 0x00300000 0x100>;
+ sdr-base = <0x3B0>;
+ msi-data = <0x00000000>;
+ msi-mask = <0x44440000>;
+ interrupt-count = <3>;
+ interrupts =<0 1 2 3>;
+ interrupt-parent = <&UIC0>;
+ #interrupt-cells = <1>;
+ #address-cells = <0>;
+ #size-cells = <0>;
+ interrupt-map = <0 &UIC0 0xC 1
+ 1 &UIC0 0x0D 1
+ 2 &UIC0 0x0E 1
+ 3 &UIC0 0x0F 1>;
+ };
+
I2O: i2o@400100000 {
compatible = "ibm,i2o-440spe";
reg = <0x00000004 0x00100000 0x100>;
diff --git a/arch/powerpc/boot/dts/kilauea.dts b/arch/powerpc/boot/dts/kilauea.dts
index 89edb16649c3..1613d6e4049e 100644
--- a/arch/powerpc/boot/dts/kilauea.dts
+++ b/arch/powerpc/boot/dts/kilauea.dts
@@ -403,5 +403,33 @@
0x0 0x0 0x0 0x3 &UIC2 0xd 0x4 /* swizzled int C */
0x0 0x0 0x0 0x4 &UIC2 0xe 0x4 /* swizzled int D */>;
};
+
+ MSI: ppc4xx-msi@C10000000 {
+ compatible = "amcc,ppc4xx-msi", "ppc4xx-msi";
+ reg = < 0x0 0xEF620000 0x100>;
+ sdr-base = <0x4B0>;
+ msi-data = <0x00000000>;
+ msi-mask = <0x44440000>;
+ interrupt-count = <12>;
+ interrupts = <0 1 2 3 4 5 6 7 8 9 0xA 0xB 0xC 0xD>;
+ interrupt-parent = <&UIC2>;
+ #interrupt-cells = <1>;
+ #address-cells = <0>;
+ #size-cells = <0>;
+ interrupt-map = <0 &UIC2 0x10 1
+ 1 &UIC2 0x11 1
+ 2 &UIC2 0x12 1
+ 2 &UIC2 0x13 1
+ 2 &UIC2 0x14 1
+ 2 &UIC2 0x15 1
+ 2 &UIC2 0x16 1
+ 2 &UIC2 0x17 1
+ 2 &UIC2 0x18 1
+ 2 &UIC2 0x19 1
+ 2 &UIC2 0x1A 1
+ 2 &UIC2 0x1B 1
+ 2 &UIC2 0x1C 1
+ 3 &UIC2 0x1D 1>;
+ };
};
};
diff --git a/arch/powerpc/boot/dts/redwood.dts b/arch/powerpc/boot/dts/redwood.dts
index 81636c01d906..d86a3a498118 100644
--- a/arch/powerpc/boot/dts/redwood.dts
+++ b/arch/powerpc/boot/dts/redwood.dts
@@ -358,8 +358,28 @@
0x0 0x0 0x0 0x4 &UIC3 0xb 0x4 /* swizzled int D */>;
};
+ MSI: ppc4xx-msi@400300000 {
+ compatible = "amcc,ppc4xx-msi", "ppc4xx-msi";
+ reg = < 0x4 0x00300000 0x100
+ 0x4 0x00300000 0x100>;
+ sdr-base = <0x3B0>;
+ msi-data = <0x00000000>;
+ msi-mask = <0x44440000>;
+ interrupt-count = <3>;
+ interrupts =<0 1 2 3>;
+ interrupt-parent = <&UIC0>;
+ #interrupt-cells = <1>;
+ #address-cells = <0>;
+ #size-cells = <0>;
+ interrupt-map = <0 &UIC0 0xC 1
+ 1 &UIC0 0x0D 1
+ 2 &UIC0 0x0E 1
+ 3 &UIC0 0x0F 1>;
+ };
+
};
+
chosen {
linux,stdout-path = "/plb/opb/serial@ef600200";
};
diff --git a/arch/powerpc/include/asm/ftrace.h b/arch/powerpc/include/asm/ftrace.h
index dde1296b8b41..169d039ed402 100644
--- a/arch/powerpc/include/asm/ftrace.h
+++ b/arch/powerpc/include/asm/ftrace.h
@@ -60,4 +60,18 @@ struct dyn_arch_ftrace {
#endif
+#if defined(CONFIG_FTRACE_SYSCALLS) && defined(CONFIG_PPC64) && !defined(__ASSEMBLY__)
+#define ARCH_HAS_SYSCALL_MATCH_SYM_NAME
+static inline bool arch_syscall_match_sym_name(const char *sym, const char *name)
+{
+ /*
+ * Compare the symbol name with the system call name. Skip the .sys or .SyS
+ * prefix from the symbol name and the sys prefix from the system call name and
+ * just match the rest. This is only needed on ppc64 since symbol names on
+ * 32bit do not start with a period so the generic function will work.
+ */
+ return !strcmp(sym + 4, name + 3);
+}
+#endif /* CONFIG_FTRACE_SYSCALLS && CONFIG_PPC64 && !__ASSEMBLY__ */
+
#endif /* _ASM_POWERPC_FTRACE */
diff --git a/arch/powerpc/include/asm/hvcall.h b/arch/powerpc/include/asm/hvcall.h
index 852b8c1c09db..fd8201dddd4b 100644
--- a/arch/powerpc/include/asm/hvcall.h
+++ b/arch/powerpc/include/asm/hvcall.h
@@ -236,7 +236,7 @@
#define H_HOME_NODE_ASSOCIATIVITY 0x2EC
#define H_BEST_ENERGY 0x2F4
#define H_GET_MPP_X 0x314
-#define MAX_HCALL_OPCODE H_BEST_ENERGY
+#define MAX_HCALL_OPCODE H_GET_MPP_X
#ifndef __ASSEMBLY__
diff --git a/arch/powerpc/include/asm/smp.h b/arch/powerpc/include/asm/smp.h
index 880b8c1e6e53..11eb404b5606 100644
--- a/arch/powerpc/include/asm/smp.h
+++ b/arch/powerpc/include/asm/smp.h
@@ -191,8 +191,6 @@ extern unsigned long __secondary_hold_spinloop;
extern unsigned long __secondary_hold_acknowledge;
extern char __secondary_hold;
-extern irqreturn_t debug_ipi_action(int irq, void *data);
-
#endif /* __ASSEMBLY__ */
#endif /* __KERNEL__ */
diff --git a/arch/powerpc/include/asm/syscall.h b/arch/powerpc/include/asm/syscall.h
index 23913e902fc3..b54b2add07be 100644
--- a/arch/powerpc/include/asm/syscall.h
+++ b/arch/powerpc/include/asm/syscall.h
@@ -15,6 +15,11 @@
#include <linux/sched.h>
+/* ftrace syscalls requires exporting the sys_call_table */
+#ifdef CONFIG_FTRACE_SYSCALLS
+extern const unsigned long *sys_call_table;
+#endif /* CONFIG_FTRACE_SYSCALLS */
+
static inline long syscall_get_nr(struct task_struct *task,
struct pt_regs *regs)
{
diff --git a/arch/powerpc/include/asm/thread_info.h b/arch/powerpc/include/asm/thread_info.h
index 37c353e8af7c..836f231ec1f0 100644
--- a/arch/powerpc/include/asm/thread_info.h
+++ b/arch/powerpc/include/asm/thread_info.h
@@ -110,7 +110,8 @@ static inline struct thread_info *current_thread_info(void)
#define TIF_NOERROR 12 /* Force successful syscall return */
#define TIF_NOTIFY_RESUME 13 /* callback before returning to user */
#define TIF_FREEZE 14 /* Freezing for suspend */
-#define TIF_RUNLATCH 15 /* Is the runlatch enabled? */
+#define TIF_SYSCALL_TRACEPOINT 15 /* syscall tracepoint instrumentation */
+#define TIF_RUNLATCH 16 /* Is the runlatch enabled? */
/* as above, but as bit values */
#define _TIF_SYSCALL_TRACE (1<<TIF_SYSCALL_TRACE)
@@ -127,8 +128,10 @@ static inline struct thread_info *current_thread_info(void)
#define _TIF_NOERROR (1<<TIF_NOERROR)
#define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME)
#define _TIF_FREEZE (1<<TIF_FREEZE)
+#define _TIF_SYSCALL_TRACEPOINT (1<<TIF_SYSCALL_TRACEPOINT)
#define _TIF_RUNLATCH (1<<TIF_RUNLATCH)
-#define _TIF_SYSCALL_T_OR_A (_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SECCOMP)
+#define _TIF_SYSCALL_T_OR_A (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | \
+ _TIF_SECCOMP | _TIF_SYSCALL_TRACEPOINT)
#define _TIF_USER_WORK_MASK (_TIF_SIGPENDING | _TIF_NEED_RESCHED | \
_TIF_NOTIFY_RESUME)
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index 9aab36312572..e8b981897d44 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -109,6 +109,7 @@ obj-$(CONFIG_PPC_IO_WORKAROUNDS) += io-workarounds.o
obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o
+obj-$(CONFIG_FTRACE_SYSCALLS) += ftrace.o
obj-$(CONFIG_PERF_EVENTS) += perf_callchain.o
obj-$(CONFIG_PPC_PERF_CTRS) += perf_event.o
diff --git a/arch/powerpc/kernel/ftrace.c b/arch/powerpc/kernel/ftrace.c
index ce1f3e44c24f..bf99cfa6bbfe 100644
--- a/arch/powerpc/kernel/ftrace.c
+++ b/arch/powerpc/kernel/ftrace.c
@@ -22,6 +22,7 @@
#include <asm/cacheflush.h>
#include <asm/code-patching.h>
#include <asm/ftrace.h>
+#include <asm/syscall.h>
#ifdef CONFIG_DYNAMIC_FTRACE
@@ -600,3 +601,10 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr)
}
}
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
+
+#if defined(CONFIG_FTRACE_SYSCALLS) && defined(CONFIG_PPC64)
+unsigned long __init arch_syscall_addr(int nr)
+{
+ return sys_call_table[nr*2];
+}
+#endif /* CONFIG_FTRACE_SYSCALLS && CONFIG_PPC64 */
diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c
index a24d37d4cf51..5b428e308666 100644
--- a/arch/powerpc/kernel/irq.c
+++ b/arch/powerpc/kernel/irq.c
@@ -295,17 +295,20 @@ static inline void handle_one_irq(unsigned int irq)
unsigned long saved_sp_limit;
struct irq_desc *desc;
+ desc = irq_to_desc(irq);
+ if (!desc)
+ return;
+
/* Switch to the irq stack to handle this */
curtp = current_thread_info();
irqtp = hardirq_ctx[smp_processor_id()];
if (curtp == irqtp) {
/* We're already on the irq stack, just handle it */
- generic_handle_irq(irq);
+ desc->handle_irq(irq, desc);
return;
}
- desc = irq_to_desc(irq);
saved_sp_limit = current->thread.ksp_limit;
irqtp->task = curtp->task;
@@ -557,15 +560,8 @@ struct irq_host *irq_alloc_host(struct device_node *of_node,
if (revmap_type == IRQ_HOST_MAP_LEGACY) {
if (irq_map[0].host != NULL) {
raw_spin_unlock_irqrestore(&irq_big_lock, flags);
- /* If we are early boot, we can't free the structure,
- * too bad...
- * this will be fixed once slab is made available early
- * instead of the current cruft
- */
- if (mem_init_done) {
- of_node_put(host->of_node);
- kfree(host);
- }
+ of_node_put(host->of_node);
+ kfree(host);
return NULL;
}
irq_map[0].host = host;
@@ -727,9 +723,7 @@ unsigned int irq_create_mapping(struct irq_host *host,
}
pr_debug("irq: -> using host @%p\n", host);
- /* Check if mapping already exist, if it does, call
- * host->ops->map() to update the flags
- */
+ /* Check if mapping already exists */
virq = irq_find_mapping(host, hwirq);
if (virq != NO_IRQ) {
pr_debug("irq: -> existing mapping on virq %d\n", virq);
@@ -899,10 +893,13 @@ unsigned int irq_radix_revmap_lookup(struct irq_host *host,
return irq_find_mapping(host, hwirq);
/*
- * No rcu_read_lock(ing) needed, the ptr returned can't go under us
- * as it's referencing an entry in the static irq_map table.
+ * The ptr returned references the static global irq_map.
+ * but freeing an irq can delete nodes along the path to
+ * do the lookup via call_rcu.
*/
+ rcu_read_lock();
ptr = radix_tree_lookup(&host->revmap_data.tree, hwirq);
+ rcu_read_unlock();
/*
* If found in radix tree, then fine.
@@ -1010,14 +1007,23 @@ void irq_free_virt(unsigned int virq, unsigned int count)
WARN_ON (virq < NUM_ISA_INTERRUPTS);
WARN_ON (count == 0 || (virq + count) > irq_virq_count);
+ if (virq < NUM_ISA_INTERRUPTS) {
+ if (virq + count < NUM_ISA_INTERRUPTS)
+ return;
+ count =- NUM_ISA_INTERRUPTS - virq;
+ virq = NUM_ISA_INTERRUPTS;
+ }
+
+ if (count > irq_virq_count || virq > irq_virq_count - count) {
+ if (virq > irq_virq_count)
+ return;
+ count = irq_virq_count - virq;
+ }
+
raw_spin_lock_irqsave(&irq_big_lock, flags);
for (i = virq; i < (virq + count); i++) {
struct irq_host *host;
- if (i < NUM_ISA_INTERRUPTS ||
- (virq + count) > irq_virq_count)
- continue;
-
host = irq_map[i].host;
irq_map[i].hwirq = host->inval_irq;
smp_wmb();
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index a6ae1cfad86c..cb22024f2b42 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -29,6 +29,7 @@
#include <linux/signal.h>
#include <linux/seccomp.h>
#include <linux/audit.h>
+#include <trace/syscall.h>
#ifdef CONFIG_PPC32
#include <linux/module.h>
#endif
@@ -40,6 +41,9 @@
#include <asm/pgtable.h>
#include <asm/system.h>
+#define CREATE_TRACE_POINTS
+#include <trace/events/syscalls.h>
+
/*
* The parameter save area on the stack is used to store arguments being passed
* to callee function and is located at fixed offset from stack pointer.
@@ -1710,6 +1714,9 @@ long do_syscall_trace_enter(struct pt_regs *regs)
*/
ret = -1L;
+ if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
+ trace_sys_enter(regs, regs->gpr[0]);
+
if (unlikely(current->audit_context)) {
#ifdef CONFIG_PPC64
if (!is_32bit_task())
@@ -1738,6 +1745,9 @@ void do_syscall_trace_leave(struct pt_regs *regs)
audit_syscall_exit((regs->ccr&0x10000000)?AUDITSC_FAILURE:AUDITSC_SUCCESS,
regs->result);
+ if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
+ trace_sys_exit(regs, regs->result);
+
step = test_thread_flag(TIF_SINGLESTEP);
if (step || test_thread_flag(TIF_SYSCALL_TRACE))
tracehook_report_syscall_exit(regs, step);
diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c
index 4a6f2ec7e761..8ebc6700b98d 100644
--- a/arch/powerpc/kernel/smp.c
+++ b/arch/powerpc/kernel/smp.c
@@ -129,7 +129,7 @@ static irqreturn_t call_function_single_action(int irq, void *data)
return IRQ_HANDLED;
}
-irqreturn_t debug_ipi_action(int irq, void *data)
+static irqreturn_t debug_ipi_action(int irq, void *data)
{
if (crash_ipi_function_ptr) {
crash_ipi_function_ptr(get_irq_regs());
diff --git a/arch/powerpc/oprofile/op_model_power4.c b/arch/powerpc/oprofile/op_model_power4.c
index 8ee51a252cf1..e6bec74be131 100644
--- a/arch/powerpc/oprofile/op_model_power4.c
+++ b/arch/powerpc/oprofile/op_model_power4.c
@@ -261,6 +261,28 @@ static int get_kernel(unsigned long pc, unsigned long mmcra)
return is_kernel;
}
+static bool pmc_overflow(unsigned long val)
+{
+ if ((int)val < 0)
+ return true;
+
+ /*
+ * Events on POWER7 can roll back if a speculative event doesn't
+ * eventually complete. Unfortunately in some rare cases they will
+ * raise a performance monitor exception. We need to catch this to
+ * ensure we reset the PMC. In all cases the PMC will be 256 or less
+ * cycles from overflow.
+ *
+ * We only do this if the first pass fails to find any overflowing
+ * PMCs because a user might set a period of less than 256 and we
+ * don't want to mistakenly reset them.
+ */
+ if (__is_processor(PV_POWER7) && ((0x80000000 - val) <= 256))
+ return true;
+
+ return false;
+}
+
static void power4_handle_interrupt(struct pt_regs *regs,
struct op_counter_config *ctr)
{
@@ -281,7 +303,7 @@ static void power4_handle_interrupt(struct pt_regs *regs,
for (i = 0; i < cur_cpu_spec->num_pmcs; ++i) {
val = classic_ctr_read(i);
- if (val < 0) {
+ if (pmc_overflow(val)) {
if (oprofile_running && ctr[i].enabled) {
oprofile_add_ext_sample(pc, regs, i, is_kernel);
classic_ctr_write(i, reset_value[i]);
diff --git a/arch/powerpc/platforms/40x/Kconfig b/arch/powerpc/platforms/40x/Kconfig
index b72176434ebe..d733d7ca939c 100644
--- a/arch/powerpc/platforms/40x/Kconfig
+++ b/arch/powerpc/platforms/40x/Kconfig
@@ -57,6 +57,8 @@ config KILAUEA
select 405EX
select PPC40x_SIMPLE
select PPC4xx_PCI_EXPRESS
+ select PCI_MSI
+ select PPC4xx_MSI
help
This option enables support for the AMCC PPC405EX evaluation board.
diff --git a/arch/powerpc/platforms/44x/Kconfig b/arch/powerpc/platforms/44x/Kconfig
index f485fc5f6d5e..e958b6f48ec2 100644
--- a/arch/powerpc/platforms/44x/Kconfig
+++ b/arch/powerpc/platforms/44x/Kconfig
@@ -74,6 +74,8 @@ config KATMAI
select 440SPe
select PCI
select PPC4xx_PCI_EXPRESS
+ select PCI_MSI
+ select PCC4xx_MSI
help
This option enables support for the AMCC PPC440SPe evaluation board.
@@ -118,6 +120,8 @@ config CANYONLANDS
select 460EX
select PCI
select PPC4xx_PCI_EXPRESS
+ select PCI_MSI
+ select PPC4xx_MSI
select IBM_NEW_EMAC_RGMII
select IBM_NEW_EMAC_ZMII
help
@@ -144,6 +148,8 @@ config REDWOOD
select 460SX
select PCI
select PPC4xx_PCI_EXPRESS
+ select PCI_MSI
+ select PPC4xx_MSI
help
This option enables support for the AMCC PPC460SX Redwood board.
diff --git a/arch/powerpc/platforms/cell/interrupt.c b/arch/powerpc/platforms/cell/interrupt.c
index 449c08c15862..3e4eba603e6b 100644
--- a/arch/powerpc/platforms/cell/interrupt.c
+++ b/arch/powerpc/platforms/cell/interrupt.c
@@ -176,14 +176,14 @@ EXPORT_SYMBOL_GPL(iic_get_target_id);
#ifdef CONFIG_SMP
/* Use the highest interrupt priorities for IPI */
-static inline int iic_ipi_to_irq(int ipi)
+static inline int iic_msg_to_irq(int msg)
{
- return IIC_IRQ_TYPE_IPI + 0xf - ipi;
+ return IIC_IRQ_TYPE_IPI + 0xf - msg;
}
-void iic_cause_IPI(int cpu, int mesg)
+void iic_message_pass(int cpu, int msg)
{
- out_be64(&per_cpu(cpu_iic, cpu).regs->generate, (0xf - mesg) << 4);
+ out_be64(&per_cpu(cpu_iic, cpu).regs->generate, (0xf - msg) << 4);
}
struct irq_host *iic_get_irq_host(int node)
@@ -192,50 +192,31 @@ struct irq_host *iic_get_irq_host(int node)
}
EXPORT_SYMBOL_GPL(iic_get_irq_host);
-static irqreturn_t iic_ipi_action(int irq, void *dev_id)
-{
- int ipi = (int)(long)dev_id;
-
- switch(ipi) {
- case PPC_MSG_CALL_FUNCTION:
- generic_smp_call_function_interrupt();
- break;
- case PPC_MSG_RESCHEDULE:
- scheduler_ipi();
- break;
- case PPC_MSG_CALL_FUNC_SINGLE:
- generic_smp_call_function_single_interrupt();
- break;
- case PPC_MSG_DEBUGGER_BREAK:
- debug_ipi_action(0, NULL);
- break;
- }
- return IRQ_HANDLED;
-}
-static void iic_request_ipi(int ipi, const char *name)
+static void iic_request_ipi(int msg)
{
int virq;
- virq = irq_create_mapping(iic_host, iic_ipi_to_irq(ipi));
+ virq = irq_create_mapping(iic_host, iic_msg_to_irq(msg));
if (virq == NO_IRQ) {
printk(KERN_ERR
- "iic: failed to map IPI %s\n", name);
+ "iic: failed to map IPI %s\n", smp_ipi_name[msg]);
return;
}
- if (request_irq(virq, iic_ipi_action, IRQF_DISABLED, name,
- (void *)(long)ipi))
- printk(KERN_ERR
- "iic: failed to request IPI %s\n", name);
+
+ /*
+ * If smp_request_message_ipi encounters an error it will notify
+ * the error. If a message is not needed it will return non-zero.
+ */
+ if (smp_request_message_ipi(virq, msg))
+ irq_dispose_mapping(virq);
}
void iic_request_IPIs(void)
{
- iic_request_ipi(PPC_MSG_CALL_FUNCTION, "IPI-call");
- iic_request_ipi(PPC_MSG_RESCHEDULE, "IPI-resched");
- iic_request_ipi(PPC_MSG_CALL_FUNC_SINGLE, "IPI-call-single");
-#ifdef CONFIG_DEBUGGER
- iic_request_ipi(PPC_MSG_DEBUGGER_BREAK, "IPI-debug");
-#endif /* CONFIG_DEBUGGER */
+ iic_request_ipi(PPC_MSG_CALL_FUNCTION);
+ iic_request_ipi(PPC_MSG_RESCHEDULE);
+ iic_request_ipi(PPC_MSG_CALL_FUNC_SINGLE);
+ iic_request_ipi(PPC_MSG_DEBUGGER_BREAK);
}
#endif /* CONFIG_SMP */
diff --git a/arch/powerpc/platforms/cell/interrupt.h b/arch/powerpc/platforms/cell/interrupt.h
index 942dc39d6045..4f60ae6ca358 100644
--- a/arch/powerpc/platforms/cell/interrupt.h
+++ b/arch/powerpc/platforms/cell/interrupt.h
@@ -75,7 +75,7 @@ enum {
};
extern void iic_init_IRQ(void);
-extern void iic_cause_IPI(int cpu, int mesg);
+extern void iic_message_pass(int cpu, int msg);
extern void iic_request_IPIs(void);
extern void iic_setup_cpu(void);
diff --git a/arch/powerpc/platforms/cell/smp.c b/arch/powerpc/platforms/cell/smp.c
index d176e6148e3f..dbb641ea90dd 100644
--- a/arch/powerpc/platforms/cell/smp.c
+++ b/arch/powerpc/platforms/cell/smp.c
@@ -152,7 +152,7 @@ static int smp_cell_cpu_bootable(unsigned int nr)
return 1;
}
static struct smp_ops_t bpa_iic_smp_ops = {
- .message_pass = iic_cause_IPI,
+ .message_pass = iic_message_pass,
.probe = smp_iic_probe,
.kick_cpu = smp_cell_kick_cpu,
.setup_cpu = smp_cell_setup_cpu,
diff --git a/arch/powerpc/sysdev/Kconfig b/arch/powerpc/sysdev/Kconfig
index d775fd148d13..7b4df37ac381 100644
--- a/arch/powerpc/sysdev/Kconfig
+++ b/arch/powerpc/sysdev/Kconfig
@@ -7,11 +7,18 @@ config PPC4xx_PCI_EXPRESS
depends on PCI && 4xx
default n
+config PPC4xx_MSI
+ bool
+ depends on PCI_MSI
+ depends on PCI && 4xx
+ default n
+
config PPC_MSI_BITMAP
bool
depends on PCI_MSI
default y if MPIC
default y if FSL_PCI
+ default y if PPC4xx_MSI
source "arch/powerpc/sysdev/xics/Kconfig"
diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile
index 6076e0074a87..0efa990e3344 100644
--- a/arch/powerpc/sysdev/Makefile
+++ b/arch/powerpc/sysdev/Makefile
@@ -41,6 +41,7 @@ obj-$(CONFIG_OF_RTC) += of_rtc.o
ifeq ($(CONFIG_PCI),y)
obj-$(CONFIG_4xx) += ppc4xx_pci.o
endif
+obj-$(CONFIG_PPC4xx_MSI) += ppc4xx_msi.o
obj-$(CONFIG_PPC4xx_CPM) += ppc4xx_cpm.o
obj-$(CONFIG_PPC4xx_GPIO) += ppc4xx_gpio.o
diff --git a/arch/powerpc/sysdev/ppc4xx_msi.c b/arch/powerpc/sysdev/ppc4xx_msi.c
new file mode 100644
index 000000000000..367af0241851
--- /dev/null
+++ b/arch/powerpc/sysdev/ppc4xx_msi.c
@@ -0,0 +1,276 @@
+/*
+ * Adding PCI-E MSI support for PPC4XX SoCs.
+ *
+ * Copyright (c) 2010, Applied Micro Circuits Corporation
+ * Authors: Tirumala R Marri <tmarri@apm.com>
+ * Feng Kan <fkan@apm.com>
+ *
+ * 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; 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
+ */
+
+#include <linux/irq.h>
+#include <linux/bootmem.h>
+#include <linux/pci.h>
+#include <linux/msi.h>
+#include <linux/of_platform.h>
+#include <linux/interrupt.h>
+#include <asm/prom.h>
+#include <asm/hw_irq.h>
+#include <asm/ppc-pci.h>
+#include <boot/dcr.h>
+#include <asm/dcr-regs.h>
+#include <asm/msi_bitmap.h>
+
+#define PEIH_TERMADH 0x00
+#define PEIH_TERMADL 0x08
+#define PEIH_MSIED 0x10
+#define PEIH_MSIMK 0x18
+#define PEIH_MSIASS 0x20
+#define PEIH_FLUSH0 0x30
+#define PEIH_FLUSH1 0x38
+#define PEIH_CNTRST 0x48
+#define NR_MSI_IRQS 4
+
+struct ppc4xx_msi {
+ u32 msi_addr_lo;
+ u32 msi_addr_hi;
+ void __iomem *msi_regs;
+ int msi_virqs[NR_MSI_IRQS];
+ struct msi_bitmap bitmap;
+ struct device_node *msi_dev;
+};
+
+static struct ppc4xx_msi ppc4xx_msi;
+
+static int ppc4xx_msi_init_allocator(struct platform_device *dev,
+ struct ppc4xx_msi *msi_data)
+{
+ int err;
+
+ err = msi_bitmap_alloc(&msi_data->bitmap, NR_MSI_IRQS,
+ dev->dev.of_node);
+ if (err)
+ return err;
+
+ err = msi_bitmap_reserve_dt_hwirqs(&msi_data->bitmap);
+ if (err < 0) {
+ msi_bitmap_free(&msi_data->bitmap);
+ return err;
+ }
+
+ return 0;
+}
+
+static int ppc4xx_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
+{
+ int int_no = -ENOMEM;
+ unsigned int virq;
+ struct msi_msg msg;
+ struct msi_desc *entry;
+ struct ppc4xx_msi *msi_data = &ppc4xx_msi;
+
+ list_for_each_entry(entry, &dev->msi_list, list) {
+ int_no = msi_bitmap_alloc_hwirqs(&msi_data->bitmap, 1);
+ if (int_no >= 0)
+ break;
+ if (int_no < 0) {
+ pr_debug("%s: fail allocating msi interrupt\n",
+ __func__);
+ }
+ virq = irq_of_parse_and_map(msi_data->msi_dev, int_no);
+ if (virq == NO_IRQ) {
+ dev_err(&dev->dev, "%s: fail mapping irq\n", __func__);
+ msi_bitmap_free_hwirqs(&msi_data->bitmap, int_no, 1);
+ return -ENOSPC;
+ }
+ dev_dbg(&dev->dev, "%s: virq = %d\n", __func__, virq);
+
+ /* Setup msi address space */
+ msg.address_hi = msi_data->msi_addr_hi;
+ msg.address_lo = msi_data->msi_addr_lo;
+
+ irq_set_msi_desc(virq, entry);
+ msg.data = int_no;
+ write_msi_msg(virq, &msg);
+ }
+ return 0;
+}
+
+void ppc4xx_teardown_msi_irqs(struct pci_dev *dev)
+{
+ struct msi_desc *entry;
+ struct ppc4xx_msi *msi_data = &ppc4xx_msi;
+
+ dev_dbg(&dev->dev, "PCIE-MSI: tearing down msi irqs\n");
+
+ list_for_each_entry(entry, &dev->msi_list, list) {
+ if (entry->irq == NO_IRQ)
+ continue;
+ irq_set_msi_desc(entry->irq, NULL);
+ msi_bitmap_free_hwirqs(&msi_data->bitmap,
+ virq_to_hw(entry->irq), 1);
+ irq_dispose_mapping(entry->irq);
+ }
+}
+
+static int ppc4xx_msi_check_device(struct pci_dev *pdev, int nvec, int type)
+{
+ dev_dbg(&pdev->dev, "PCIE-MSI:%s called. vec %x type %d\n",
+ __func__, nvec, type);
+ if (type == PCI_CAP_ID_MSIX)
+ pr_debug("ppc4xx msi: MSI-X untested, trying anyway.\n");
+
+ return 0;
+}
+
+static int ppc4xx_setup_pcieh_hw(struct platform_device *dev,
+ struct resource res, struct ppc4xx_msi *msi)
+{
+ const u32 *msi_data;
+ const u32 *msi_mask;
+ const u32 *sdr_addr;
+ dma_addr_t msi_phys;
+ void *msi_virt;
+
+ sdr_addr = of_get_property(dev->dev.of_node, "sdr-base", NULL);
+ if (!sdr_addr)
+ return -1;
+
+ SDR0_WRITE(sdr_addr, (u64)res.start >> 32); /*HIGH addr */
+ SDR0_WRITE(sdr_addr + 1, res.start & 0xFFFFFFFF); /* Low addr */
+
+
+ msi->msi_dev = of_find_node_by_name(NULL, "ppc4xx-msi");
+ if (msi->msi_dev)
+ return -ENODEV;
+
+ msi->msi_regs = of_iomap(msi->msi_dev, 0);
+ if (!msi->msi_regs) {
+ dev_err(&dev->dev, "of_iomap problem failed\n");
+ return -ENOMEM;
+ }
+ dev_dbg(&dev->dev, "PCIE-MSI: msi register mapped 0x%x 0x%x\n",
+ (u32) (msi->msi_regs + PEIH_TERMADH), (u32) (msi->msi_regs));
+
+ msi_virt = dma_alloc_coherent(&dev->dev, 64, &msi_phys, GFP_KERNEL);
+ msi->msi_addr_hi = 0x0;
+ msi->msi_addr_lo = (u32) msi_phys;
+ dev_dbg(&dev->dev, "PCIE-MSI: msi address 0x%x\n", msi->msi_addr_lo);
+
+ /* Progam the Interrupt handler Termination addr registers */
+ out_be32(msi->msi_regs + PEIH_TERMADH, msi->msi_addr_hi);
+ out_be32(msi->msi_regs + PEIH_TERMADL, msi->msi_addr_lo);
+
+ msi_data = of_get_property(dev->dev.of_node, "msi-data", NULL);
+ if (!msi_data)
+ return -1;
+ msi_mask = of_get_property(dev->dev.of_node, "msi-mask", NULL);
+ if (!msi_mask)
+ return -1;
+ /* Program MSI Expected data and Mask bits */
+ out_be32(msi->msi_regs + PEIH_MSIED, *msi_data);
+ out_be32(msi->msi_regs + PEIH_MSIMK, *msi_mask);
+
+ return 0;
+}
+
+static int ppc4xx_of_msi_remove(struct platform_device *dev)
+{
+ struct ppc4xx_msi *msi = dev->dev.platform_data;
+ int i;
+ int virq;
+
+ for (i = 0; i < NR_MSI_IRQS; i++) {
+ virq = msi->msi_virqs[i];
+ if (virq != NO_IRQ)
+ irq_dispose_mapping(virq);
+ }
+
+ if (msi->bitmap.bitmap)
+ msi_bitmap_free(&msi->bitmap);
+ iounmap(msi->msi_regs);
+ of_node_put(msi->msi_dev);
+ kfree(msi);
+
+ return 0;
+}
+
+static int __devinit ppc4xx_msi_probe(struct platform_device *dev)
+{
+ struct ppc4xx_msi *msi;
+ struct resource res;
+ int err = 0;
+
+ msi = &ppc4xx_msi;/*keep the msi data for further use*/
+
+ dev_dbg(&dev->dev, "PCIE-MSI: Setting up MSI support...\n");
+
+ msi = kzalloc(sizeof(struct ppc4xx_msi), GFP_KERNEL);
+ if (!msi) {
+ dev_err(&dev->dev, "No memory for MSI structure\n");
+ return -ENOMEM;
+ }
+ dev->dev.platform_data = msi;
+
+ /* Get MSI ranges */
+ err = of_address_to_resource(dev->dev.of_node, 0, &res);
+ if (err) {
+ dev_err(&dev->dev, "%s resource error!\n",
+ dev->dev.of_node->full_name);
+ goto error_out;
+ }
+
+ if (ppc4xx_setup_pcieh_hw(dev, res, msi))
+ goto error_out;
+
+ err = ppc4xx_msi_init_allocator(dev, msi);
+ if (err) {
+ dev_err(&dev->dev, "Error allocating MSI bitmap\n");
+ goto error_out;
+ }
+
+ ppc_md.setup_msi_irqs = ppc4xx_setup_msi_irqs;
+ ppc_md.teardown_msi_irqs = ppc4xx_teardown_msi_irqs;
+ ppc_md.msi_check_device = ppc4xx_msi_check_device;
+ return err;
+
+error_out:
+ ppc4xx_of_msi_remove(dev);
+ return err;
+}
+static const struct of_device_id ppc4xx_msi_ids[] = {
+ {
+ .compatible = "amcc,ppc4xx-msi",
+ },
+ {}
+};
+static struct platform_driver ppc4xx_msi_driver = {
+ .probe = ppc4xx_msi_probe,
+ .remove = ppc4xx_of_msi_remove,
+ .driver = {
+ .name = "ppc4xx-msi",
+ .owner = THIS_MODULE,
+ .of_match_table = ppc4xx_msi_ids,
+ },
+
+};
+
+static __init int ppc4xx_msi_init(void)
+{
+ return platform_driver_register(&ppc4xx_msi_driver);
+}
+
+subsys_initcall(ppc4xx_msi_init);