Commit 6ddb4518 authored by Linus Torvalds's avatar Linus Torvalds

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
parents be93d8cf 3fb79338
...@@ -141,6 +141,7 @@ config PPC ...@@ -141,6 +141,7 @@ config PPC
select GENERIC_IRQ_SHOW select GENERIC_IRQ_SHOW
select GENERIC_IRQ_SHOW_LEVEL select GENERIC_IRQ_SHOW_LEVEL
select HAVE_RCU_TABLE_FREE if SMP select HAVE_RCU_TABLE_FREE if SMP
select HAVE_SYSCALL_TRACEPOINTS
config EARLY_PRINTK config EARLY_PRINTK
bool bool
......
...@@ -530,5 +530,23 @@ PCIE1: pciex@d20000000 { ...@@ -530,5 +530,23 @@ PCIE1: pciex@d20000000 {
0x0 0x0 0x0 0x3 &UIC3 0x12 0x4 /* swizzled int C */ 0x0 0x0 0x0 0x3 &UIC3 0x12 0x4 /* swizzled int C */
0x0 0x0 0x0 0x4 &UIC3 0x13 0x4 /* swizzled int D */>; 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>;
};
}; };
}; };
...@@ -442,6 +442,24 @@ PCIE2: pciex@d40000000 { ...@@ -442,6 +442,24 @@ PCIE2: pciex@d40000000 {
0x0 0x0 0x0 0x4 &UIC3 0xb 0x4 /* swizzled int D */>; 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 { I2O: i2o@400100000 {
compatible = "ibm,i2o-440spe"; compatible = "ibm,i2o-440spe";
reg = <0x00000004 0x00100000 0x100>; reg = <0x00000004 0x00100000 0x100>;
......
...@@ -403,5 +403,33 @@ PCIE1: pciex@0c0000000 { ...@@ -403,5 +403,33 @@ PCIE1: pciex@0c0000000 {
0x0 0x0 0x0 0x3 &UIC2 0xd 0x4 /* swizzled int C */ 0x0 0x0 0x0 0x3 &UIC2 0xd 0x4 /* swizzled int C */
0x0 0x0 0x0 0x4 &UIC2 0xe 0x4 /* swizzled int D */>; 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>;
};
}; };
}; };
...@@ -358,8 +358,28 @@ PCIE2: pciex@d40000000 { ...@@ -358,8 +358,28 @@ PCIE2: pciex@d40000000 {
0x0 0x0 0x0 0x4 &UIC3 0xb 0x4 /* swizzled int D */>; 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 { chosen {
linux,stdout-path = "/plb/opb/serial@ef600200"; linux,stdout-path = "/plb/opb/serial@ef600200";
}; };
......
...@@ -60,4 +60,18 @@ struct dyn_arch_ftrace { ...@@ -60,4 +60,18 @@ struct dyn_arch_ftrace {
#endif #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 */ #endif /* _ASM_POWERPC_FTRACE */
...@@ -236,7 +236,7 @@ ...@@ -236,7 +236,7 @@
#define H_HOME_NODE_ASSOCIATIVITY 0x2EC #define H_HOME_NODE_ASSOCIATIVITY 0x2EC
#define H_BEST_ENERGY 0x2F4 #define H_BEST_ENERGY 0x2F4
#define H_GET_MPP_X 0x314 #define H_GET_MPP_X 0x314
#define MAX_HCALL_OPCODE H_BEST_ENERGY #define MAX_HCALL_OPCODE H_GET_MPP_X
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
......
...@@ -191,8 +191,6 @@ extern unsigned long __secondary_hold_spinloop; ...@@ -191,8 +191,6 @@ extern unsigned long __secondary_hold_spinloop;
extern unsigned long __secondary_hold_acknowledge; extern unsigned long __secondary_hold_acknowledge;
extern char __secondary_hold; extern char __secondary_hold;
extern irqreturn_t debug_ipi_action(int irq, void *data);
#endif /* __ASSEMBLY__ */ #endif /* __ASSEMBLY__ */
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
......
...@@ -15,6 +15,11 @@ ...@@ -15,6 +15,11 @@
#include <linux/sched.h> #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, static inline long syscall_get_nr(struct task_struct *task,
struct pt_regs *regs) struct pt_regs *regs)
{ {
......
...@@ -110,7 +110,8 @@ static inline struct thread_info *current_thread_info(void) ...@@ -110,7 +110,8 @@ static inline struct thread_info *current_thread_info(void)
#define TIF_NOERROR 12 /* Force successful syscall return */ #define TIF_NOERROR 12 /* Force successful syscall return */
#define TIF_NOTIFY_RESUME 13 /* callback before returning to user */ #define TIF_NOTIFY_RESUME 13 /* callback before returning to user */
#define TIF_FREEZE 14 /* Freezing for suspend */ #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 */ /* as above, but as bit values */
#define _TIF_SYSCALL_TRACE (1<<TIF_SYSCALL_TRACE) #define _TIF_SYSCALL_TRACE (1<<TIF_SYSCALL_TRACE)
...@@ -127,8 +128,10 @@ static inline struct thread_info *current_thread_info(void) ...@@ -127,8 +128,10 @@ static inline struct thread_info *current_thread_info(void)
#define _TIF_NOERROR (1<<TIF_NOERROR) #define _TIF_NOERROR (1<<TIF_NOERROR)
#define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME) #define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME)
#define _TIF_FREEZE (1<<TIF_FREEZE) #define _TIF_FREEZE (1<<TIF_FREEZE)
#define _TIF_SYSCALL_TRACEPOINT (1<<TIF_SYSCALL_TRACEPOINT)
#define _TIF_RUNLATCH (1<<TIF_RUNLATCH) #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 | \ #define _TIF_USER_WORK_MASK (_TIF_SIGPENDING | _TIF_NEED_RESCHED | \
_TIF_NOTIFY_RESUME) _TIF_NOTIFY_RESUME)
......
...@@ -109,6 +109,7 @@ obj-$(CONFIG_PPC_IO_WORKAROUNDS) += io-workarounds.o ...@@ -109,6 +109,7 @@ obj-$(CONFIG_PPC_IO_WORKAROUNDS) += io-workarounds.o
obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o
obj-$(CONFIG_FTRACE_SYSCALLS) += ftrace.o
obj-$(CONFIG_PERF_EVENTS) += perf_callchain.o obj-$(CONFIG_PERF_EVENTS) += perf_callchain.o
obj-$(CONFIG_PPC_PERF_CTRS) += perf_event.o obj-$(CONFIG_PPC_PERF_CTRS) += perf_event.o
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#include <asm/code-patching.h> #include <asm/code-patching.h>
#include <asm/ftrace.h> #include <asm/ftrace.h>
#include <asm/syscall.h>
#ifdef CONFIG_DYNAMIC_FTRACE #ifdef CONFIG_DYNAMIC_FTRACE
...@@ -600,3 +601,10 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr) ...@@ -600,3 +601,10 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr)
} }
} }
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ #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 */
...@@ -295,17 +295,20 @@ static inline void handle_one_irq(unsigned int irq) ...@@ -295,17 +295,20 @@ static inline void handle_one_irq(unsigned int irq)
unsigned long saved_sp_limit; unsigned long saved_sp_limit;
struct irq_desc *desc; struct irq_desc *desc;
desc = irq_to_desc(irq);
if (!desc)
return;
/* Switch to the irq stack to handle this */ /* Switch to the irq stack to handle this */
curtp = current_thread_info(); curtp = current_thread_info();
irqtp = hardirq_ctx[smp_processor_id()]; irqtp = hardirq_ctx[smp_processor_id()];
if (curtp == irqtp) { if (curtp == irqtp) {
/* We're already on the irq stack, just handle it */ /* We're already on the irq stack, just handle it */
generic_handle_irq(irq); desc->handle_irq(irq, desc);
return; return;
} }
desc = irq_to_desc(irq);
saved_sp_limit = current->thread.ksp_limit; saved_sp_limit = current->thread.ksp_limit;
irqtp->task = curtp->task; irqtp->task = curtp->task;
...@@ -557,15 +560,8 @@ struct irq_host *irq_alloc_host(struct device_node *of_node, ...@@ -557,15 +560,8 @@ struct irq_host *irq_alloc_host(struct device_node *of_node,
if (revmap_type == IRQ_HOST_MAP_LEGACY) { if (revmap_type == IRQ_HOST_MAP_LEGACY) {
if (irq_map[0].host != NULL) { if (irq_map[0].host != NULL) {
raw_spin_unlock_irqrestore(&irq_big_lock, flags); raw_spin_unlock_irqrestore(&irq_big_lock, flags);
/* If we are early boot, we can't free the structure, of_node_put(host->of_node);
* too bad... kfree(host);
* 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);
}
return NULL; return NULL;
} }
irq_map[0].host = host; irq_map[0].host = host;
...@@ -727,9 +723,7 @@ unsigned int irq_create_mapping(struct irq_host *host, ...@@ -727,9 +723,7 @@ unsigned int irq_create_mapping(struct irq_host *host,
} }
pr_debug("irq: -> using host @%p\n", host); pr_debug("irq: -> using host @%p\n", host);
/* Check if mapping already exist, if it does, call /* Check if mapping already exists */
* host->ops->map() to update the flags
*/
virq = irq_find_mapping(host, hwirq); virq = irq_find_mapping(host, hwirq);
if (virq != NO_IRQ) { if (virq != NO_IRQ) {
pr_debug("irq: -> existing mapping on virq %d\n", virq); pr_debug("irq: -> existing mapping on virq %d\n", virq);
...@@ -899,10 +893,13 @@ unsigned int irq_radix_revmap_lookup(struct irq_host *host, ...@@ -899,10 +893,13 @@ unsigned int irq_radix_revmap_lookup(struct irq_host *host,
return irq_find_mapping(host, hwirq); return irq_find_mapping(host, hwirq);
/* /*
* No rcu_read_lock(ing) needed, the ptr returned can't go under us * The ptr returned references the static global irq_map.
* as it's referencing an entry in the static irq_map table. * 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); ptr = radix_tree_lookup(&host->revmap_data.tree, hwirq);
rcu_read_unlock();
/* /*
* If found in radix tree, then fine. * If found in radix tree, then fine.
...@@ -1010,14 +1007,23 @@ void irq_free_virt(unsigned int virq, unsigned int count) ...@@ -1010,14 +1007,23 @@ void irq_free_virt(unsigned int virq, unsigned int count)
WARN_ON (virq < NUM_ISA_INTERRUPTS); WARN_ON (virq < NUM_ISA_INTERRUPTS);
WARN_ON (count == 0 || (virq + count) > irq_virq_count); 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); raw_spin_lock_irqsave(&irq_big_lock, flags);
for (i = virq; i < (virq + count); i++) { for (i = virq; i < (virq + count); i++) {
struct irq_host *host; struct irq_host *host;
if (i < NUM_ISA_INTERRUPTS ||
(virq + count) > irq_virq_count)
continue;
host = irq_map[i].host; host = irq_map[i].host;
irq_map[i].hwirq = host->inval_irq; irq_map[i].hwirq = host->inval_irq;
smp_wmb(); smp_wmb();
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <linux/signal.h> #include <linux/signal.h>
#include <linux/seccomp.h> #include <linux/seccomp.h>
#include <linux/audit.h> #include <linux/audit.h>
#include <trace/syscall.h>
#ifdef CONFIG_PPC32 #ifdef CONFIG_PPC32
#include <linux/module.h> #include <linux/module.h>
#endif #endif
...@@ -40,6 +41,9 @@ ...@@ -40,6 +41,9 @@
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/system.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 * 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. * 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) ...@@ -1710,6 +1714,9 @@ long do_syscall_trace_enter(struct pt_regs *regs)
*/ */
ret = -1L; ret = -1L;
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
trace_sys_enter(regs, regs->gpr[0]);
if (unlikely(current->audit_context)) { if (unlikely(current->audit_context)) {
#ifdef CONFIG_PPC64 #ifdef CONFIG_PPC64
if (!is_32bit_task()) if (!is_32bit_task())
...@@ -1738,6 +1745,9 @@ void do_syscall_trace_leave(struct pt_regs *regs) ...@@ -1738,6 +1745,9 @@ void do_syscall_trace_leave(struct pt_regs *regs)
audit_syscall_exit((regs->ccr&0x10000000)?AUDITSC_FAILURE:AUDITSC_SUCCESS, audit_syscall_exit((regs->ccr&0x10000000)?AUDITSC_FAILURE:AUDITSC_SUCCESS,
regs->result); regs->result);
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
trace_sys_exit(regs, regs->result);
step = test_thread_flag(TIF_SINGLESTEP); step = test_thread_flag(TIF_SINGLESTEP);
if (step || test_thread_flag(TIF_SYSCALL_TRACE)) if (step || test_thread_flag(TIF_SYSCALL_TRACE))
tracehook_report_syscall_exit(regs, step); tracehook_report_syscall_exit(regs, step);
......
...@@ -129,7 +129,7 @@ static irqreturn_t call_function_single_action(int irq, void *data) ...@@ -129,7 +129,7 @@ static irqreturn_t call_function_single_action(int irq, void *data)
return IRQ_HANDLED; 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) { if (crash_ipi_function_ptr) {
crash_ipi_function_ptr(get_irq_regs()); crash_ipi_function_ptr(get_irq_regs());
......
...@@ -261,6 +261,28 @@ static int get_kernel(unsigned long pc, unsigned long mmcra) ...@@ -261,6 +261,28 @@ static int get_kernel(unsigned long pc, unsigned long mmcra)
return is_kernel; 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, static void power4_handle_interrupt(struct pt_regs *regs,
struct op_counter_config *ctr) struct op_counter_config *ctr)
{ {
...@@ -281,7 +303,7 @@ static void power4_handle_interrupt(struct pt_regs *regs, ...@@ -281,7 +303,7 @@ static void power4_handle_interrupt(struct pt_regs *regs,
for (i = 0; i < cur_cpu_spec->num_pmcs; ++i) { for (i = 0; i < cur_cpu_spec->num_pmcs; ++i) {
val = classic_ctr_read(i); val = classic_ctr_read(i);
if (val < 0) { if (pmc_overflow(val)) {
if (oprofile_running && ctr[i].enabled) { if (oprofile_running && ctr[i].enabled) {
oprofile_add_ext_sample(pc, regs, i, is_kernel); oprofile_add_ext_sample(pc, regs, i, is_kernel);
classic_ctr_write(i, reset_value[i]); classic_ctr_write(i, reset_value[i]);
......
...@@ -57,6 +57,8 @@ config KILAUEA ...@@ -57,6 +57,8 @@ config KILAUEA
select 405EX select 405EX
select PPC40x_SIMPLE select PPC40x_SIMPLE
select PPC4xx_PCI_EXPRESS select PPC4xx_PCI_EXPRESS
select PCI_MSI
select PPC4xx_MSI
help help
This option enables support for the AMCC PPC405EX evaluation board. This option enables support for the AMCC PPC405EX evaluation board.
......
...@@ -74,6 +74,8 @@ config KATMAI ...@@ -74,6 +74,8 @@ config KATMAI
select 440SPe select 440SPe
select PCI select PCI
select PPC4xx_PCI_EXPRESS select PPC4xx_PCI_EXPRESS
select PCI_MSI
select PCC4xx_MSI
help help
This option enables support for the AMCC PPC440SPe evaluation board. This option enables support for the AMCC PPC440SPe evaluation board.
...@@ -118,6 +120,8 @@ config CANYONLANDS ...@@ -118,6 +120,8 @@ config CANYONLANDS
select 460EX select 460EX
select PCI select PCI
select PPC4xx_PCI_EXPRESS select PPC4xx_PCI_EXPRESS
select PCI_MSI
select PPC4xx_MSI
select IBM_NEW_EMAC_RGMII select IBM_NEW_EMAC_RGMII
select IBM_NEW_EMAC_ZMII select IBM_NEW_EMAC_ZMII
help help
...@@ -144,6 +148,8 @@ config REDWOOD ...@@ -144,6 +148,8 @@ config REDWOOD
select 460SX select 460SX
select PCI select PCI
select PPC4xx_PCI_EXPRESS select PPC4xx_PCI_EXPRESS
select PCI_MSI
select PPC4xx_MSI
help help
This option enables support for the AMCC PPC460SX Redwood board. This option enables support for the AMCC PPC460SX Redwood board.
......
...@@ -176,14 +176,14 @@ EXPORT_SYMBOL_GPL(iic_get_target_id); ...@@ -176,14 +176,14 @@ EXPORT_SYMBOL_GPL(iic_get_target_id);
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
/* Use the highest interrupt priorities for IPI */ /* 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) struct irq_host *iic_get_irq_host(int node)
...@@ -192,50 +192,31 @@ 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); EXPORT_SYMBOL_GPL(iic_get_irq_host);
static irqreturn_t iic_ipi_action(int irq, void *dev_id) static void iic_request_ipi(int msg)
{
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)
{ {
int virq; 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) { if (virq == NO_IRQ) {
printk(KERN_ERR printk(KERN_ERR
"iic: failed to map IPI %s\n", name); "iic: failed to map IPI %s\n", smp_ipi_name[msg]);
return; return;
} }
if (request_irq(virq, iic_ipi_action, IRQF_DISABLED, name,
(void *)(long)ipi)) /*
printk(KERN_ERR * If smp_request_message_ipi encounters an error it will notify
"iic: failed to request IPI %s\n", name); * 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) void iic_request_IPIs(void)
{ {
iic_request_ipi(PPC_MSG_CALL_FUNCTION, "IPI-call"); iic_request_ipi(PPC_MSG_CALL_FUNCTION);
iic_request_ipi(PPC_MSG_RESCHEDULE, "IPI-resched"); iic_request_ipi(PPC_MSG_RESCHEDULE);
iic_request_ipi(PPC_MSG_CALL_FUNC_SINGLE, "IPI-call-single"); iic_request_ipi(PPC_MSG_CALL_FUNC_SINGLE);
#ifdef CONFIG_DEBUGGER iic_request_ipi(PPC_MSG_DEBUGGER_BREAK);
iic_request_ipi(PPC_MSG_DEBUGGER_BREAK, "IPI-debug");
#endif /* CONFIG_DEBUGGER */
} }
#endif /* CONFIG_SMP */ #endif /* CONFIG_SMP */
......
...@@ -75,7 +75,7 @@ enum { ...@@ -75,7 +75,7 @@ enum {
}; };
extern void iic_init_IRQ(void); 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_request_IPIs(void);
extern void iic_setup_cpu(void); extern void iic_setup_cpu(void);
......
...@@ -152,7 +152,7 @@ static int smp_cell_cpu_bootable(unsigned int nr) ...@@ -152,7 +152,7 @@ static int smp_cell_cpu_bootable(unsigned int nr)
return 1; return 1;
} }
static struct smp_ops_t bpa_iic_smp_ops = { static struct smp_ops_t bpa_iic_smp_ops = {
.message_pass = iic_cause_IPI, .message_pass = iic_message_pass,
.probe = smp_iic_probe, .probe = smp_iic_probe,
.kick_cpu = smp_cell_kick_cpu, .kick_cpu = smp_cell_kick_cpu,
.setup_cpu = smp_cell_setup_cpu, .setup_cpu = smp_cell_setup_cpu,
......
...@@ -7,11 +7,18 @@ config PPC4xx_PCI_EXPRESS ...@@ -7,11 +7,18 @@ config PPC4xx_PCI_EXPRESS
depends on PCI && 4xx depends on PCI && 4xx
default n default n
config PPC4xx_MSI
bool
depends on PCI_MSI
depends on PCI && 4xx
default n
config PPC_MSI_BITMAP config PPC_MSI_BITMAP
bool bool
depends on PCI_MSI depends on PCI_MSI
default y if MPIC default y if MPIC
default y if FSL_PCI default y if FSL_PCI
default y if PPC4xx_MSI
source "arch/powerpc/sysdev/xics/Kconfig" source "arch/powerpc/sysdev/xics/Kconfig"
......
...@@ -41,6 +41,7 @@ obj-$(CONFIG_OF_RTC) += of_rtc.o ...@@ -41,6 +41,7 @@ obj-$(CONFIG_OF_RTC) += of_rtc.o
ifeq ($(CONFIG_PCI),y) ifeq ($(CONFIG_PCI),y)
obj-$(CONFIG_4xx) += ppc4xx_pci.o obj-$(CONFIG_4xx) += ppc4xx_pci.o
endif endif
obj-$(CONFIG_PPC4xx_MSI) += ppc4xx_msi.o
obj-$(CONFIG_PPC4xx_CPM) += ppc4xx_cpm.o obj-$(CONFIG_PPC4xx_CPM) += ppc4xx_cpm.o
obj-$(CONFIG_PPC4xx_GPIO) += ppc4xx_gpio.o obj-$(CONFIG_PPC4xx_GPIO) += ppc4xx_gpio.o
......
/*
* 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);
...@@ -98,16 +98,6 @@ void ipi_call_unlock_irq(void); ...@@ -98,16 +98,6 @@ void ipi_call_unlock_irq(void);
*/ */
int on_each_cpu(smp_call_func_t func, void *info, int wait); int on_each_cpu(smp_call_func_t func, void *info, int wait);
#define MSG_ALL_BUT_SELF 0x8000 /* Assume <32768 CPU's */
#define MSG_ALL 0x8001
#define MSG_INVALIDATE_TLB 0x0001 /* Remote processor TLB invalidate */
#define MSG_STOP_CPU 0x0002 /* Sent to shut down slave CPU's
* when rebooting
*/
#define MSG_RESCHEDULE 0x0003 /* Reschedule request from master CPU*/
#define MSG_CALL_FUNCTION 0x0004 /* Call function on all other CPUs */
/* /*
* Mark the boot cpu "online" so that it can call console drivers in * Mark the boot cpu "online" so that it can call console drivers in
* printk() and can access its per-cpu storage. * printk() and can access its per-cpu storage.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment