2011-04-04 11:46:58 +08:00
|
|
|
/*
|
|
|
|
* Common definitions accross all variants of ICP and ICS interrupt
|
|
|
|
* controllers.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef _XICS_H
|
|
|
|
#define _XICS_H
|
|
|
|
|
2011-04-15 06:31:58 +08:00
|
|
|
#include <linux/interrupt.h>
|
|
|
|
|
2011-04-04 11:46:58 +08:00
|
|
|
#define XICS_IPI 2
|
|
|
|
#define XICS_IRQ_SPURIOUS 0
|
|
|
|
|
|
|
|
/* Want a priority other than 0. Various HW issues require this. */
|
|
|
|
#define DEFAULT_PRIORITY 5
|
|
|
|
|
|
|
|
/*
|
2011-10-22 07:56:27 +08:00
|
|
|
* Mark IPIs as higher priority so we can take them inside interrupts
|
|
|
|
* FIXME: still true now?
|
2011-04-04 11:46:58 +08:00
|
|
|
*/
|
|
|
|
#define IPI_PRIORITY 4
|
|
|
|
|
|
|
|
/* The least favored priority */
|
|
|
|
#define LOWEST_PRIORITY 0xFF
|
|
|
|
|
|
|
|
/* The number of priorities defined above */
|
|
|
|
#define MAX_NUM_PRIORITIES 3
|
|
|
|
|
|
|
|
/* Native ICP */
|
2011-09-20 01:45:02 +08:00
|
|
|
#ifdef CONFIG_PPC_ICP_NATIVE
|
2011-04-04 11:46:58 +08:00
|
|
|
extern int icp_native_init(void);
|
powerpc/powernv: Don't call generic code on offline cpus
On PowerNV platforms, when a CPU is offline, we put it into nap mode.
It's possible that the CPU wakes up from nap mode while it is still
offline due to a stray IPI. A misdirected device interrupt could also
potentially cause it to wake up. In that circumstance, we need to clear
the interrupt so that the CPU can go back to nap mode.
In the past the clearing of the interrupt was accomplished by briefly
enabling interrupts and allowing the normal interrupt handling code
(do_IRQ() etc.) to handle the interrupt. This has the problem that
this code calls irq_enter() and irq_exit(), which call functions such
as account_system_vtime() which use RCU internally. Use of RCU is not
permitted on offline CPUs and will trigger errors if RCU checking is
enabled.
To avoid calling into any generic code which might use RCU, we adopt
a different method of clearing interrupts on offline CPUs. Since we
are on the PowerNV platform, we know that the system interrupt
controller is a XICS being driven directly (i.e. not via hcalls) by
the kernel. Hence this adds a new icp_native_flush_interrupt()
function to the native-mode XICS driver and arranges to call that
when an offline CPU is woken from nap. This new function reads the
interrupt from the XICS. If it is an IPI, it clears the IPI; if it
is a device interrupt, it prints a warning and disables the source.
Then it does the end-of-interrupt processing for the interrupt.
The other thing that briefly enabling interrupts did was to check and
clear the irq_happened flag in this CPU's PACA. Therefore, after
flushing the interrupt from the XICS, we also clear all bits except
the PACA_IRQ_HARD_DIS (interrupts are hard disabled) bit from the
irq_happened flag. The PACA_IRQ_HARD_DIS flag is set by power7_nap()
and is left set to indicate that interrupts are hard disabled. This
means we then have to ignore that flag in power7_nap(), which is
reasonable since it doesn't indicate that any interrupt event needs
servicing.
Signed-off-by: Paul Mackerras <paulus@samba.org>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
2014-09-02 12:23:16 +08:00
|
|
|
extern void icp_native_flush_interrupt(void);
|
2011-09-20 01:45:02 +08:00
|
|
|
#else
|
|
|
|
static inline int icp_native_init(void) { return -ENODEV; }
|
|
|
|
#endif
|
2011-04-04 11:46:58 +08:00
|
|
|
|
|
|
|
/* PAPR ICP */
|
2011-09-20 01:45:02 +08:00
|
|
|
#ifdef CONFIG_PPC_ICP_HV
|
2011-04-04 11:46:58 +08:00
|
|
|
extern int icp_hv_init(void);
|
2011-09-20 01:45:02 +08:00
|
|
|
#else
|
|
|
|
static inline int icp_hv_init(void) { return -ENODEV; }
|
|
|
|
#endif
|
2011-04-04 11:46:58 +08:00
|
|
|
|
|
|
|
/* ICP ops */
|
|
|
|
struct icp_ops {
|
|
|
|
unsigned int (*get_irq)(void);
|
|
|
|
void (*eoi)(struct irq_data *d);
|
|
|
|
void (*set_priority)(unsigned char prio);
|
|
|
|
void (*teardown_cpu)(void);
|
|
|
|
void (*flush_ipi)(void);
|
|
|
|
#ifdef CONFIG_SMP
|
powerpc: Consolidate ipi message mux and demux
Consolidate the mux and demux of ipi messages into smp.c and call
a new smp_ops callback to actually trigger the ipi.
The powerpc architecture code is optimised for having 4 distinct
ipi triggers, which are mapped to 4 distinct messages (ipi many, ipi
single, scheduler ipi, and enter debugger). However, several interrupt
controllers only provide a single software triggered interrupt that
can be delivered to each cpu. To resolve this limitation, each smp_ops
implementation created a per-cpu variable that is manipulated with atomic
bitops. Since these lines will be contended they are optimialy marked as
shared_aligned and take a full cache line for each cpu. Distro kernels
may have 2 or 3 of these in their config, each taking per-cpu space
even though at most one will be in use.
This consolidation removes smp_message_recv and replaces the single call
actions cases with direct calls from the common message recognition loop.
The complicated debugger ipi case with its muxed crash handling code is
moved to debug_ipi_action which is now called from the demux code (instead
of the multi-message action calling smp_message_recv).
I put a call to reschedule_action to increase the likelyhood of correctly
merging the anticipated scheduler_ipi() hook coming from the scheduler
tree; that single required call can be inlined later.
The actual message decode is a copy of the old pseries xics code with its
memory barriers and cache line spacing, augmented with a per-cpu unsigned
long based on the book-e doorbell code. The optional data is set via a
callback from the implementation and is passed to the new cause-ipi hook
along with the logical cpu number. While currently only the doorbell
implemntation uses this data it should be almost zero cost to retrieve and
pass it -- it adds a single register load for the argument from the same
cache line to which we just completed a store and the register is dead
on return from the call. I extended the data element from unsigned int
to unsigned long in case some other code wanted to associate a pointer.
The doorbell check_self is replaced by a call to smp_muxed_ipi_resend,
conditioned on the CPU_DBELL feature. The ifdef guard could be relaxed
to CONFIG_SMP but I left it with BOOKE for now.
Also, the doorbell interrupt vector for book-e was not calling irq_enter
and irq_exit, which throws off cpu accounting and causes code to not
realize it is running in interrupt context. Add the missing calls.
Signed-off-by: Milton Miller <miltonm@bga.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2011-05-11 03:29:39 +08:00
|
|
|
void (*cause_ipi)(int cpu, unsigned long data);
|
2011-04-04 11:46:58 +08:00
|
|
|
irq_handler_t ipi_action;
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
extern const struct icp_ops *icp_ops;
|
|
|
|
|
|
|
|
/* Native ICS */
|
|
|
|
extern int ics_native_init(void);
|
|
|
|
|
|
|
|
/* RTAS ICS */
|
2011-09-20 01:45:02 +08:00
|
|
|
#ifdef CONFIG_PPC_ICS_RTAS
|
2011-04-04 11:46:58 +08:00
|
|
|
extern int ics_rtas_init(void);
|
2011-09-20 01:45:02 +08:00
|
|
|
#else
|
|
|
|
static inline int ics_rtas_init(void) { return -ENODEV; }
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* HAL ICS */
|
|
|
|
#ifdef CONFIG_PPC_POWERNV
|
|
|
|
extern int ics_opal_init(void);
|
|
|
|
#else
|
|
|
|
static inline int ics_opal_init(void) { return -ENODEV; }
|
|
|
|
#endif
|
2011-04-04 11:46:58 +08:00
|
|
|
|
|
|
|
/* ICS instance, hooked up to chip_data of an irq */
|
|
|
|
struct ics {
|
|
|
|
struct list_head link;
|
|
|
|
int (*map)(struct ics *ics, unsigned int virq);
|
|
|
|
void (*mask_unknown)(struct ics *ics, unsigned long vec);
|
|
|
|
long (*get_server)(struct ics *ics, unsigned long vec);
|
2011-04-15 06:31:59 +08:00
|
|
|
int (*host_match)(struct ics *ics, struct device_node *node);
|
2011-04-04 11:46:58 +08:00
|
|
|
char data[];
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Commons */
|
|
|
|
extern unsigned int xics_default_server;
|
|
|
|
extern unsigned int xics_default_distrib_server;
|
|
|
|
extern unsigned int xics_interrupt_server_size;
|
2012-02-15 05:06:50 +08:00
|
|
|
extern struct irq_domain *xics_host;
|
2011-04-04 11:46:58 +08:00
|
|
|
|
|
|
|
struct xics_cppr {
|
|
|
|
unsigned char stack[MAX_NUM_PRIORITIES];
|
|
|
|
int index;
|
|
|
|
};
|
|
|
|
|
|
|
|
DECLARE_PER_CPU(struct xics_cppr, xics_cppr);
|
|
|
|
|
|
|
|
static inline void xics_push_cppr(unsigned int vec)
|
|
|
|
{
|
|
|
|
struct xics_cppr *os_cppr = &__get_cpu_var(xics_cppr);
|
|
|
|
|
|
|
|
if (WARN_ON(os_cppr->index >= MAX_NUM_PRIORITIES - 1))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (vec == XICS_IPI)
|
|
|
|
os_cppr->stack[++os_cppr->index] = IPI_PRIORITY;
|
|
|
|
else
|
|
|
|
os_cppr->stack[++os_cppr->index] = DEFAULT_PRIORITY;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline unsigned char xics_pop_cppr(void)
|
|
|
|
{
|
|
|
|
struct xics_cppr *os_cppr = &__get_cpu_var(xics_cppr);
|
|
|
|
|
|
|
|
if (WARN_ON(os_cppr->index < 1))
|
|
|
|
return LOWEST_PRIORITY;
|
|
|
|
|
|
|
|
return os_cppr->stack[--os_cppr->index];
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void xics_set_base_cppr(unsigned char cppr)
|
|
|
|
{
|
|
|
|
struct xics_cppr *os_cppr = &__get_cpu_var(xics_cppr);
|
|
|
|
|
|
|
|
/* we only really want to set the priority when there's
|
|
|
|
* just one cppr value on the stack
|
|
|
|
*/
|
|
|
|
WARN_ON(os_cppr->index != 0);
|
|
|
|
|
|
|
|
os_cppr->stack[0] = cppr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline unsigned char xics_cppr_top(void)
|
|
|
|
{
|
|
|
|
struct xics_cppr *os_cppr = &__get_cpu_var(xics_cppr);
|
|
|
|
|
|
|
|
return os_cppr->stack[os_cppr->index];
|
|
|
|
}
|
|
|
|
|
|
|
|
DECLARE_PER_CPU_SHARED_ALIGNED(unsigned long, xics_ipi_message);
|
|
|
|
|
|
|
|
extern void xics_init(void);
|
|
|
|
extern void xics_setup_cpu(void);
|
|
|
|
extern void xics_update_irq_servers(void);
|
|
|
|
extern void xics_set_cpu_giq(unsigned int gserver, unsigned int join);
|
|
|
|
extern void xics_mask_unknown_vec(unsigned int vec);
|
|
|
|
extern irqreturn_t xics_ipi_dispatch(int cpu);
|
|
|
|
extern int xics_smp_probe(void);
|
|
|
|
extern void xics_register_ics(struct ics *ics);
|
|
|
|
extern void xics_teardown_cpu(void);
|
|
|
|
extern void xics_kexec_teardown_cpu(int secondary);
|
|
|
|
extern void xics_migrate_irqs_away(void);
|
2013-04-26 03:20:59 +08:00
|
|
|
extern void icp_native_eoi(struct irq_data *d);
|
2011-04-04 11:46:58 +08:00
|
|
|
#ifdef CONFIG_SMP
|
|
|
|
extern int xics_get_irq_server(unsigned int virq, const struct cpumask *cpumask,
|
|
|
|
unsigned int strict_check);
|
|
|
|
#else
|
|
|
|
#define xics_get_irq_server(virq, cpumask, strict_check) (xics_default_server)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#endif /* _XICS_H */
|