forked from Minki/linux
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
This commit is contained in:
commit
6ddb4518c7
@ -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
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -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>;
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -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";
|
||||
};
|
||||
|
@ -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 */
|
||||
|
@ -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__
|
||||
|
||||
|
@ -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__ */
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
|
@ -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]);
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
276
arch/powerpc/sysdev/ppc4xx_msi.c
Normal file
276
arch/powerpc/sysdev/ppc4xx_msi.c
Normal file
@ -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);
|
@ -98,16 +98,6 @@ void ipi_call_unlock_irq(void);
|
||||
*/
|
||||
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
|
||||
* printk() and can access its per-cpu storage.
|
||||
|
Loading…
Reference in New Issue
Block a user