Path: blob/master/arch/powerpc/sysdev/xics/icp-native.c
26493 views
// SPDX-License-Identifier: GPL-2.0-or-later1/*2* Copyright 2011 IBM Corporation.3*/45#include <linux/types.h>6#include <linux/kernel.h>7#include <linux/irq.h>8#include <linux/irqdomain.h>9#include <linux/smp.h>10#include <linux/interrupt.h>11#include <linux/init.h>12#include <linux/cpu.h>13#include <linux/of.h>14#include <linux/of_address.h>15#include <linux/spinlock.h>16#include <linux/module.h>1718#include <asm/io.h>19#include <asm/smp.h>20#include <asm/irq.h>21#include <asm/errno.h>22#include <asm/xics.h>23#include <asm/kvm_ppc.h>24#include <asm/dbell.h>2526struct icp_ipl {27union {28u32 word;29u8 bytes[4];30} xirr_poll;31union {32u32 word;33u8 bytes[4];34} xirr;35u32 dummy;36union {37u32 word;38u8 bytes[4];39} qirr;40u32 link_a;41u32 link_b;42u32 link_c;43};4445static struct icp_ipl __iomem *icp_native_regs[NR_CPUS];4647static inline unsigned int icp_native_get_xirr(void)48{49int cpu = smp_processor_id();50unsigned int xirr;5152/* Handled an interrupt latched by KVM */53xirr = kvmppc_get_xics_latch();54if (xirr)55return xirr;5657return in_be32(&icp_native_regs[cpu]->xirr.word);58}5960static inline void icp_native_set_xirr(unsigned int value)61{62int cpu = smp_processor_id();6364out_be32(&icp_native_regs[cpu]->xirr.word, value);65}6667static inline void icp_native_set_cppr(u8 value)68{69int cpu = smp_processor_id();7071out_8(&icp_native_regs[cpu]->xirr.bytes[0], value);72}7374static inline void icp_native_set_qirr(int n_cpu, u8 value)75{76out_8(&icp_native_regs[n_cpu]->qirr.bytes[0], value);77}7879static void icp_native_set_cpu_priority(unsigned char cppr)80{81xics_set_base_cppr(cppr);82icp_native_set_cppr(cppr);83iosync();84}8586void icp_native_eoi(struct irq_data *d)87{88unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);8990iosync();91icp_native_set_xirr((xics_pop_cppr() << 24) | hw_irq);92}9394static void icp_native_teardown_cpu(void)95{96int cpu = smp_processor_id();9798/* Clear any pending IPI */99icp_native_set_qirr(cpu, 0xff);100}101102static void icp_native_flush_ipi(void)103{104/* We take the ipi irq but and never return so we105* need to EOI the IPI, but want to leave our priority 0106*107* should we check all the other interrupts too?108* should we be flagging idle loop instead?109* or creating some task to be scheduled?110*/111112icp_native_set_xirr((0x00 << 24) | XICS_IPI);113}114115static unsigned int icp_native_get_irq(void)116{117unsigned int xirr = icp_native_get_xirr();118unsigned int vec = xirr & 0x00ffffff;119unsigned int irq;120121if (vec == XICS_IRQ_SPURIOUS)122return 0;123124irq = irq_find_mapping(xics_host, vec);125if (likely(irq)) {126xics_push_cppr(vec);127return irq;128}129130/* We don't have a linux mapping, so have rtas mask it. */131xics_mask_unknown_vec(vec);132133/* We might learn about it later, so EOI it */134icp_native_set_xirr(xirr);135136return 0;137}138139#ifdef CONFIG_SMP140141static void icp_native_cause_ipi(int cpu)142{143kvmppc_set_host_ipi(cpu);144icp_native_set_qirr(cpu, IPI_PRIORITY);145}146147/*148* Called when an interrupt is received on an off-line CPU to149* clear the interrupt, so that the CPU can go back to nap mode.150*/151void icp_native_flush_interrupt(void)152{153unsigned int xirr = icp_native_get_xirr();154unsigned int vec = xirr & 0x00ffffff;155156if (vec == XICS_IRQ_SPURIOUS)157return;158if (vec == XICS_IPI) {159/* Clear pending IPI */160int cpu = smp_processor_id();161kvmppc_clear_host_ipi(cpu);162icp_native_set_qirr(cpu, 0xff);163} else {164pr_err("XICS: hw interrupt 0x%x to offline cpu, disabling\n",165vec);166xics_mask_unknown_vec(vec);167}168/* EOI the interrupt */169icp_native_set_xirr(xirr);170}171172void xics_wake_cpu(int cpu)173{174icp_native_set_qirr(cpu, IPI_PRIORITY);175}176EXPORT_SYMBOL_GPL(xics_wake_cpu);177178static irqreturn_t icp_native_ipi_action(int irq, void *dev_id)179{180int cpu = smp_processor_id();181182kvmppc_clear_host_ipi(cpu);183icp_native_set_qirr(cpu, 0xff);184185return smp_ipi_demux();186}187188#endif /* CONFIG_SMP */189190static int __init icp_native_map_one_cpu(int hw_id, unsigned long addr,191unsigned long size)192{193char *rname;194int i, cpu = -1;195196/* This may look gross but it's good enough for now, we don't quite197* have a hard -> linux processor id matching.198*/199for_each_possible_cpu(i) {200if (!cpu_present(i))201continue;202if (hw_id == get_hard_smp_processor_id(i)) {203cpu = i;204break;205}206}207208/* Fail, skip that CPU. Don't print, it's normal, some XICS come up209* with way more entries in there than you have CPUs210*/211if (cpu == -1)212return 0;213214rname = kasprintf(GFP_KERNEL, "CPU %d [0x%x] Interrupt Presentation",215cpu, hw_id);216217if (!rname)218return -ENOMEM;219if (!request_mem_region(addr, size, rname)) {220pr_warn("icp_native: Could not reserve ICP MMIO for CPU %d, interrupt server #0x%x\n",221cpu, hw_id);222return -EBUSY;223}224225icp_native_regs[cpu] = ioremap(addr, size);226kvmppc_set_xics_phys(cpu, addr);227if (!icp_native_regs[cpu]) {228pr_warn("icp_native: Failed ioremap for CPU %d, interrupt server #0x%x, addr %#lx\n",229cpu, hw_id, addr);230release_mem_region(addr, size);231return -ENOMEM;232}233return 0;234}235236static int __init icp_native_init_one_node(struct device_node *np,237unsigned int *indx)238{239unsigned int ilen;240const __be32 *ireg;241int i;242int num_reg;243int num_servers = 0;244245/* This code does the theorically broken assumption that the interrupt246* server numbers are the same as the hard CPU numbers.247* This happens to be the case so far but we are playing with fire...248* should be fixed one of these days. -BenH.249*/250ireg = of_get_property(np, "ibm,interrupt-server-ranges", &ilen);251252/* Do that ever happen ? we'll know soon enough... but even good'old253* f80 does have that property ..254*/255WARN_ON((ireg == NULL) || (ilen != 2*sizeof(u32)));256257if (ireg) {258*indx = of_read_number(ireg, 1);259if (ilen >= 2*sizeof(u32))260num_servers = of_read_number(ireg + 1, 1);261}262263num_reg = of_address_count(np);264if (num_servers && (num_servers != num_reg)) {265pr_err("icp_native: ICP reg len (%d) != num servers (%d)",266num_reg, num_servers);267return -1;268}269270for (i = 0; i < num_reg; i++) {271struct resource r;272int err;273274err = of_address_to_resource(np, i, &r);275if (err) {276pr_err("icp_native: Could not translate ICP MMIO"277" for interrupt server 0x%x (%d)\n", *indx, err);278return -1;279}280281if (icp_native_map_one_cpu(*indx, r.start, resource_size(&r)))282return -1;283284(*indx)++;285}286return 0;287}288289static const struct icp_ops icp_native_ops = {290.get_irq = icp_native_get_irq,291.eoi = icp_native_eoi,292.set_priority = icp_native_set_cpu_priority,293.teardown_cpu = icp_native_teardown_cpu,294.flush_ipi = icp_native_flush_ipi,295#ifdef CONFIG_SMP296.ipi_action = icp_native_ipi_action,297.cause_ipi = icp_native_cause_ipi,298#endif299};300301int __init icp_native_init(void)302{303struct device_node *np;304u32 indx = 0;305int found = 0;306307for_each_compatible_node(np, NULL, "ibm,ppc-xicp")308if (icp_native_init_one_node(np, &indx) == 0)309found = 1;310if (!found) {311for_each_node_by_type(np,312"PowerPC-External-Interrupt-Presentation") {313if (icp_native_init_one_node(np, &indx) == 0)314found = 1;315}316}317318if (found == 0)319return -ENODEV;320321icp_ops = &icp_native_ops;322323return 0;324}325326327