diff options
Diffstat (limited to 'arch/powerpc')
34 files changed, 599 insertions, 106 deletions
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 423145a6f7ba..2729c6663d8a 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -91,14 +91,6 @@ config GENERIC_HWEIGHT bool default y -config GENERIC_FIND_NEXT_BIT - bool - default y - -config GENERIC_FIND_BIT_LE - bool - default y - config GENERIC_GPIO bool help @@ -141,6 +133,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/configs/ppc6xx_defconfig b/arch/powerpc/configs/ppc6xx_defconfig index 214208924a9c..04360f9b0109 100644 --- a/arch/powerpc/configs/ppc6xx_defconfig +++ b/arch/powerpc/configs/ppc6xx_defconfig @@ -10,7 +10,6 @@ CONFIG_TASK_XACCT=y CONFIG_TASK_IO_ACCOUNTING=y CONFIG_AUDIT=y CONFIG_CGROUPS=y -CONFIG_CGROUP_NS=y CONFIG_CGROUP_DEVICE=y CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y diff --git a/arch/powerpc/configs/pseries_defconfig b/arch/powerpc/configs/pseries_defconfig index 7de13865508c..c9f212b5f3de 100644 --- a/arch/powerpc/configs/pseries_defconfig +++ b/arch/powerpc/configs/pseries_defconfig @@ -15,7 +15,6 @@ CONFIG_AUDITSYSCALL=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_CGROUPS=y -CONFIG_CGROUP_NS=y CONFIG_CGROUP_FREEZER=y CONFIG_CGROUP_DEVICE=y CONFIG_CPUSETS=y diff --git a/arch/powerpc/include/asm/fsl_lbc.h b/arch/powerpc/include/asm/fsl_lbc.h index 5c1bf3466749..8a0b5ece8f76 100644 --- a/arch/powerpc/include/asm/fsl_lbc.h +++ b/arch/powerpc/include/asm/fsl_lbc.h @@ -157,6 +157,8 @@ struct fsl_lbc_regs { #define LBCR_EPAR_SHIFT 16 #define LBCR_BMT 0x0000FF00 #define LBCR_BMT_SHIFT 8 +#define LBCR_BMTPS 0x0000000F +#define LBCR_BMTPS_SHIFT 0 #define LBCR_INIT 0x00040000 __be32 lcrr; /**< Clock Ratio Register */ #define LCRR_DBYP 0x80000000 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/rio.h b/arch/powerpc/include/asm/rio.h index 0018bf80cb25..d902abd33995 100644 --- a/arch/powerpc/include/asm/rio.h +++ b/arch/powerpc/include/asm/rio.h @@ -14,5 +14,10 @@ #define ASM_PPC_RIO_H extern void platform_rio_init(void); +#ifdef CONFIG_RAPIDIO +extern int fsl_rio_mcheck_exception(struct pt_regs *); +#else +static inline int fsl_rio_mcheck_exception(struct pt_regs *regs) {return 0; } +#endif #endif /* ASM_PPC_RIO_H */ 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/suspend.h b/arch/powerpc/include/asm/suspend.h deleted file mode 100644 index c6efc3466aa6..000000000000 --- a/arch/powerpc/include/asm/suspend.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __ASM_POWERPC_SUSPEND_H -#define __ASM_POWERPC_SUSPEND_H - -static inline int arch_prepare_suspend(void) { return 0; } - -#endif /* __ASM_POWERPC_SUSPEND_H */ 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/prom.c b/arch/powerpc/kernel/prom.c index 48aeb55faae9..f2c906b1d8d3 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c @@ -694,7 +694,7 @@ void __init early_init_devtree(void *params) * device-tree, including the platform type, initrd location and * size, TCE reserve, and more ... */ - of_scan_flat_dt(early_init_dt_scan_chosen_ppc, NULL); + of_scan_flat_dt(early_init_dt_scan_chosen_ppc, cmd_line); /* Scan memory nodes and rebuild MEMBLOCKs */ memblock_init(); 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/kernel/swsusp.c b/arch/powerpc/kernel/swsusp.c index 560c96119501..aa17b76dd427 100644 --- a/arch/powerpc/kernel/swsusp.c +++ b/arch/powerpc/kernel/swsusp.c @@ -10,7 +10,6 @@ */ #include <linux/sched.h> -#include <asm/suspend.h> #include <asm/system.h> #include <asm/current.h> #include <asm/mmu_context.h> diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index b13306b0d925..0ff4ab98d50c 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -55,6 +55,7 @@ #endif #include <asm/kexec.h> #include <asm/ppc-opcode.h> +#include <asm/rio.h> #if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC) int (*__debugger)(struct pt_regs *regs) __read_mostly; @@ -424,6 +425,12 @@ int machine_check_e500mc(struct pt_regs *regs) unsigned long reason = mcsr; int recoverable = 1; + if (reason & MCSR_BUS_RBERR) { + recoverable = fsl_rio_mcheck_exception(regs); + if (recoverable == 1) + goto silent_out; + } + printk("Machine check in kernel mode.\n"); printk("Caused by (from MCSR=%lx): ", reason); @@ -499,6 +506,7 @@ int machine_check_e500mc(struct pt_regs *regs) reason & MCSR_MEA ? "Effective" : "Physical", addr); } +silent_out: mtspr(SPRN_MCSR, mcsr); return mfspr(SPRN_MCSR) == 0 && recoverable; } @@ -507,6 +515,11 @@ int machine_check_e500(struct pt_regs *regs) { unsigned long reason = get_mc_reason(regs); + if (reason & MCSR_BUS_RBERR) { + if (fsl_rio_mcheck_exception(regs)) + return 1; + } + printk("Machine check in kernel mode.\n"); printk("Caused by (from MCSR=%lx): ", reason); 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/fsl_lbc.c b/arch/powerpc/sysdev/fsl_lbc.c index 4fcb5a4e60dd..0608b1657da4 100644 --- a/arch/powerpc/sysdev/fsl_lbc.c +++ b/arch/powerpc/sysdev/fsl_lbc.c @@ -184,7 +184,8 @@ int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base, u32 mar) } EXPORT_SYMBOL(fsl_upm_run_pattern); -static int __devinit fsl_lbc_ctrl_init(struct fsl_lbc_ctrl *ctrl) +static int __devinit fsl_lbc_ctrl_init(struct fsl_lbc_ctrl *ctrl, + struct device_node *node) { struct fsl_lbc_regs __iomem *lbc = ctrl->regs; @@ -198,6 +199,10 @@ static int __devinit fsl_lbc_ctrl_init(struct fsl_lbc_ctrl *ctrl) /* Enable interrupts for any detected events */ out_be32(&lbc->lteir, LTEIR_ENABLE); + /* Set the monitor timeout value to the maximum for erratum A001 */ + if (of_device_is_compatible(node, "fsl,elbc")) + clrsetbits_be32(&lbc->lbcr, LBCR_BMT, LBCR_BMTPS); + return 0; } @@ -304,7 +309,7 @@ static int __devinit fsl_lbc_ctrl_probe(struct platform_device *dev) fsl_lbc_ctrl_dev->dev = &dev->dev; - ret = fsl_lbc_ctrl_init(fsl_lbc_ctrl_dev); + ret = fsl_lbc_ctrl_init(fsl_lbc_ctrl_dev, dev->dev.of_node); if (ret < 0) goto err; diff --git a/arch/powerpc/sysdev/fsl_rio.c b/arch/powerpc/sysdev/fsl_rio.c index 49798532b477..5b206a2fe17c 100644 --- a/arch/powerpc/sysdev/fsl_rio.c +++ b/arch/powerpc/sysdev/fsl_rio.c @@ -10,7 +10,7 @@ * - Added Port-Write message handling * - Added Machine Check exception handling * - * Copyright (C) 2007, 2008 Freescale Semiconductor, Inc. + * Copyright (C) 2007, 2008, 2010 Freescale Semiconductor, Inc. * Zhang Wei <wei.zhang@freescale.com> * * Copyright 2005 MontaVista Software, Inc. @@ -47,15 +47,33 @@ #define IRQ_RIO_RX(m) (((struct rio_priv *)(m->priv))->rxirq) #define IRQ_RIO_PW(m) (((struct rio_priv *)(m->priv))->pwirq) +#define IPWSR_CLEAR 0x98 +#define OMSR_CLEAR 0x1cb3 +#define IMSR_CLEAR 0x491 +#define IDSR_CLEAR 0x91 +#define ODSR_CLEAR 0x1c00 +#define LTLEECSR_ENABLE_ALL 0xFFC000FC +#define ESCSR_CLEAR 0x07120204 + +#define RIO_PORT1_EDCSR 0x0640 +#define RIO_PORT2_EDCSR 0x0680 +#define RIO_PORT1_IECSR 0x10130 +#define RIO_PORT2_IECSR 0x101B0 +#define RIO_IM0SR 0x13064 +#define RIO_IM1SR 0x13164 +#define RIO_OM0SR 0x13004 +#define RIO_OM1SR 0x13104 + #define RIO_ATMU_REGS_OFFSET 0x10c00 #define RIO_P_MSG_REGS_OFFSET 0x11000 #define RIO_S_MSG_REGS_OFFSET 0x13000 #define RIO_GCCSR 0x13c #define RIO_ESCSR 0x158 +#define RIO_PORT2_ESCSR 0x178 #define RIO_CCSR 0x15c #define RIO_LTLEDCSR 0x0608 -#define RIO_LTLEDCSR_IER 0x80000000 -#define RIO_LTLEDCSR_PRT 0x01000000 +#define RIO_LTLEDCSR_IER 0x80000000 +#define RIO_LTLEDCSR_PRT 0x01000000 #define RIO_LTLEECSR 0x060c #define RIO_EPWISR 0x10010 #define RIO_ISR_AACR 0x10120 @@ -88,7 +106,10 @@ #define RIO_IPWSR_PWD 0x00000008 #define RIO_IPWSR_PWB 0x00000004 -#define RIO_EPWISR_PINT 0x80000000 +/* EPWISR Error match value */ +#define RIO_EPWISR_PINT1 0x80000000 +#define RIO_EPWISR_PINT2 0x40000000 +#define RIO_EPWISR_MU 0x00000002 #define RIO_EPWISR_PW 0x00000001 #define RIO_MSG_DESC_SIZE 32 @@ -260,9 +281,7 @@ struct rio_priv { static void __iomem *rio_regs_win; #ifdef CONFIG_E500 -static int (*saved_mcheck_exception)(struct pt_regs *regs); - -static int fsl_rio_mcheck_exception(struct pt_regs *regs) +int fsl_rio_mcheck_exception(struct pt_regs *regs) { const struct exception_table_entry *entry = NULL; unsigned long reason = mfspr(SPRN_MCSR); @@ -284,11 +303,9 @@ static int fsl_rio_mcheck_exception(struct pt_regs *regs) } } - if (saved_mcheck_exception) - return saved_mcheck_exception(regs); - else - return cur_cpu_spec->machine_check(regs); + return 0; } +EXPORT_SYMBOL_GPL(fsl_rio_mcheck_exception); #endif /** @@ -1064,6 +1081,40 @@ static int fsl_rio_doorbell_init(struct rio_mport *mport) return rc; } +static void port_error_handler(struct rio_mport *port, int offset) +{ + /*XXX: Error recovery is not implemented, we just clear errors */ + out_be32((u32 *)(rio_regs_win + RIO_LTLEDCSR), 0); + + if (offset == 0) { + out_be32((u32 *)(rio_regs_win + RIO_PORT1_EDCSR), 0); + out_be32((u32 *)(rio_regs_win + RIO_PORT1_IECSR), 0); + out_be32((u32 *)(rio_regs_win + RIO_ESCSR), ESCSR_CLEAR); + } else { + out_be32((u32 *)(rio_regs_win + RIO_PORT2_EDCSR), 0); + out_be32((u32 *)(rio_regs_win + RIO_PORT2_IECSR), 0); + out_be32((u32 *)(rio_regs_win + RIO_PORT2_ESCSR), ESCSR_CLEAR); + } +} + +static void msg_unit_error_handler(struct rio_mport *port) +{ + struct rio_priv *priv = port->priv; + + /*XXX: Error recovery is not implemented, we just clear errors */ + out_be32((u32 *)(rio_regs_win + RIO_LTLEDCSR), 0); + + out_be32((u32 *)(rio_regs_win + RIO_IM0SR), IMSR_CLEAR); + out_be32((u32 *)(rio_regs_win + RIO_IM1SR), IMSR_CLEAR); + out_be32((u32 *)(rio_regs_win + RIO_OM0SR), OMSR_CLEAR); + out_be32((u32 *)(rio_regs_win + RIO_OM1SR), OMSR_CLEAR); + + out_be32(&priv->msg_regs->odsr, ODSR_CLEAR); + out_be32(&priv->msg_regs->dsr, IDSR_CLEAR); + + out_be32(&priv->msg_regs->pwsr, IPWSR_CLEAR); +} + /** * fsl_rio_port_write_handler - MPC85xx port write interrupt handler * @irq: Linux interrupt number @@ -1144,10 +1195,22 @@ fsl_rio_port_write_handler(int irq, void *dev_instance) } pw_done: - if (epwisr & RIO_EPWISR_PINT) { + if (epwisr & RIO_EPWISR_PINT1) { + tmp = in_be32(priv->regs_win + RIO_LTLEDCSR); + pr_debug("RIO_LTLEDCSR = 0x%x\n", tmp); + port_error_handler(port, 0); + } + + if (epwisr & RIO_EPWISR_PINT2) { tmp = in_be32(priv->regs_win + RIO_LTLEDCSR); pr_debug("RIO_LTLEDCSR = 0x%x\n", tmp); - out_be32(priv->regs_win + RIO_LTLEDCSR, 0); + port_error_handler(port, 1); + } + + if (epwisr & RIO_EPWISR_MU) { + tmp = in_be32(priv->regs_win + RIO_LTLEDCSR); + pr_debug("RIO_LTLEDCSR = 0x%x\n", tmp); + msg_unit_error_handler(port); } return IRQ_HANDLED; @@ -1258,12 +1321,14 @@ static int fsl_rio_port_write_init(struct rio_mport *mport) /* Hook up port-write handler */ - rc = request_irq(IRQ_RIO_PW(mport), fsl_rio_port_write_handler, 0, - "port-write", (void *)mport); + rc = request_irq(IRQ_RIO_PW(mport), fsl_rio_port_write_handler, + IRQF_SHARED, "port-write", (void *)mport); if (rc < 0) { pr_err("MPC85xx RIO: unable to request inbound doorbell irq"); goto err_out; } + /* Enable Error Interrupt */ + out_be32((u32 *)(rio_regs_win + RIO_LTLEECSR), LTLEECSR_ENABLE_ALL); INIT_WORK(&priv->pw_work, fsl_pw_dpc); spin_lock_init(&priv->pw_fifo_lock); @@ -1538,11 +1603,6 @@ int fsl_rio_setup(struct platform_device *dev) fsl_rio_doorbell_init(port); fsl_rio_port_write_init(port); -#ifdef CONFIG_E500 - saved_mcheck_exception = ppc_md.machine_check_exception; - ppc_md.machine_check_exception = fsl_rio_mcheck_exception; -#endif - return 0; err: iounmap(priv->regs_win); 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); |