summaryrefslogtreecommitdiff
path: root/arch/powerpc/sysdev/xics/icp-native.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/sysdev/xics/icp-native.c')
-rw-r--r--arch/powerpc/sysdev/xics/icp-native.c79
1 files changed, 47 insertions, 32 deletions
diff --git a/arch/powerpc/sysdev/xics/icp-native.c b/arch/powerpc/sysdev/xics/icp-native.c
index 7cd728b3b5e4..4e89158a577c 100644
--- a/arch/powerpc/sysdev/xics/icp-native.c
+++ b/arch/powerpc/sysdev/xics/icp-native.c
@@ -1,31 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2011 IBM Corporation.
- *
- * 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.
- *
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/irq.h>
+#include <linux/irqdomain.h>
#include <linux/smp.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/cpu.h>
#include <linux/of.h>
+#include <linux/of_address.h>
#include <linux/spinlock.h>
#include <linux/module.h>
-#include <asm/prom.h>
#include <asm/io.h>
#include <asm/smp.h>
#include <asm/irq.h>
#include <asm/errno.h>
#include <asm/xics.h>
#include <asm/kvm_ppc.h>
+#include <asm/dbell.h>
struct icp_ipl {
union {
@@ -123,10 +120,10 @@ static unsigned int icp_native_get_irq(void)
unsigned int irq;
if (vec == XICS_IRQ_SPURIOUS)
- return NO_IRQ;
+ return 0;
irq = irq_find_mapping(xics_host, vec);
- if (likely(irq != NO_IRQ)) {
+ if (likely(irq)) {
xics_push_cppr(vec);
return irq;
}
@@ -137,17 +134,42 @@ static unsigned int icp_native_get_irq(void)
/* We might learn about it later, so EOI it */
icp_native_set_xirr(xirr);
- return NO_IRQ;
+ return 0;
}
#ifdef CONFIG_SMP
-static void icp_native_cause_ipi(int cpu, unsigned long data)
+static void icp_native_cause_ipi(int cpu)
{
- kvmppc_set_host_ipi(cpu, 1);
+ kvmppc_set_host_ipi(cpu);
icp_native_set_qirr(cpu, IPI_PRIORITY);
}
+/*
+ * Called when an interrupt is received on an off-line CPU to
+ * clear the interrupt, so that the CPU can go back to nap mode.
+ */
+void icp_native_flush_interrupt(void)
+{
+ unsigned int xirr = icp_native_get_xirr();
+ unsigned int vec = xirr & 0x00ffffff;
+
+ if (vec == XICS_IRQ_SPURIOUS)
+ return;
+ if (vec == XICS_IPI) {
+ /* Clear pending IPI */
+ int cpu = smp_processor_id();
+ kvmppc_clear_host_ipi(cpu);
+ icp_native_set_qirr(cpu, 0xff);
+ } else {
+ pr_err("XICS: hw interrupt 0x%x to offline cpu, disabling\n",
+ vec);
+ xics_mask_unknown_vec(vec);
+ }
+ /* EOI the interrupt */
+ icp_native_set_xirr(xirr);
+}
+
void xics_wake_cpu(int cpu)
{
icp_native_set_qirr(cpu, IPI_PRIORITY);
@@ -158,7 +180,7 @@ static irqreturn_t icp_native_ipi_action(int irq, void *dev_id)
{
int cpu = smp_processor_id();
- kvmppc_set_host_ipi(cpu, 0);
+ kvmppc_clear_host_ipi(cpu);
icp_native_set_qirr(cpu, 0xff);
return smp_ipi_demux();
@@ -193,19 +215,19 @@ static int __init icp_native_map_one_cpu(int hw_id, unsigned long addr,
rname = kasprintf(GFP_KERNEL, "CPU %d [0x%x] Interrupt Presentation",
cpu, hw_id);
+ if (!rname)
+ return -ENOMEM;
if (!request_mem_region(addr, size, rname)) {
- pr_warning("icp_native: Could not reserve ICP MMIO"
- " for CPU %d, interrupt server #0x%x\n",
- cpu, hw_id);
+ pr_warn("icp_native: Could not reserve ICP MMIO for CPU %d, interrupt server #0x%x\n",
+ cpu, hw_id);
return -EBUSY;
}
icp_native_regs[cpu] = ioremap(addr, size);
kvmppc_set_xics_phys(cpu, addr);
if (!icp_native_regs[cpu]) {
- pr_warning("icp_native: Failed ioremap for CPU %d, "
- "interrupt server #0x%x, addr %#lx\n",
- cpu, hw_id, addr);
+ pr_warn("icp_native: Failed ioremap for CPU %d, interrupt server #0x%x, addr %#lx\n",
+ cpu, hw_id, addr);
release_mem_region(addr, size);
return -ENOMEM;
}
@@ -216,9 +238,9 @@ static int __init icp_native_init_one_node(struct device_node *np,
unsigned int *indx)
{
unsigned int ilen;
- const u32 *ireg;
+ const __be32 *ireg;
int i;
- int reg_tuple_size;
+ int num_reg;
int num_servers = 0;
/* This code does the theorically broken assumption that the interrupt
@@ -239,21 +261,14 @@ static int __init icp_native_init_one_node(struct device_node *np,
num_servers = of_read_number(ireg + 1, 1);
}
- ireg = of_get_property(np, "reg", &ilen);
- if (!ireg) {
- pr_err("icp_native: Can't find interrupt reg property");
- return -1;
- }
-
- reg_tuple_size = (of_n_addr_cells(np) + of_n_size_cells(np)) * 4;
- if (((ilen % reg_tuple_size) != 0)
- || (num_servers && (num_servers != (ilen / reg_tuple_size)))) {
+ num_reg = of_address_count(np);
+ if (num_servers && (num_servers != num_reg)) {
pr_err("icp_native: ICP reg len (%d) != num servers (%d)",
- ilen / reg_tuple_size, num_servers);
+ num_reg, num_servers);
return -1;
}
- for (i = 0; i < (ilen / reg_tuple_size); i++) {
+ for (i = 0; i < num_reg; i++) {
struct resource r;
int err;