Path: blob/master/arch/powerpc/sysdev/xics/ics-native.c
26493 views
// SPDX-License-Identifier: GPL-2.0-or-later1/*2* ICS backend for OPAL managed interrupts.3*4* Copyright 2011 IBM Corp.5*/67//#define DEBUG89#include <linux/types.h>10#include <linux/kernel.h>11#include <linux/irq.h>12#include <linux/smp.h>13#include <linux/interrupt.h>14#include <linux/init.h>15#include <linux/cpu.h>16#include <linux/of.h>17#include <linux/of_address.h>18#include <linux/spinlock.h>19#include <linux/msi.h>20#include <linux/list.h>2122#include <asm/smp.h>23#include <asm/machdep.h>24#include <asm/irq.h>25#include <asm/errno.h>26#include <asm/xics.h>27#include <asm/opal.h>28#include <asm/firmware.h>2930struct ics_native {31struct ics ics;32struct device_node *node;33void __iomem *base;34u32 ibase;35u32 icount;36};37#define to_ics_native(_ics) container_of(_ics, struct ics_native, ics)3839static void __iomem *ics_native_xive(struct ics_native *in, unsigned int vec)40{41return in->base + 0x800 + ((vec - in->ibase) << 2);42}4344static void ics_native_unmask_irq(struct irq_data *d)45{46unsigned int vec = (unsigned int)irqd_to_hwirq(d);47struct ics *ics = irq_data_get_irq_chip_data(d);48struct ics_native *in = to_ics_native(ics);49unsigned int server;5051pr_devel("ics-native: unmask virq %d [hw 0x%x]\n", d->irq, vec);5253if (vec < in->ibase || vec >= (in->ibase + in->icount))54return;5556server = xics_get_irq_server(d->irq, irq_data_get_affinity_mask(d), 0);57out_be32(ics_native_xive(in, vec), (server << 8) | DEFAULT_PRIORITY);58}5960static unsigned int ics_native_startup(struct irq_data *d)61{62#ifdef CONFIG_PCI_MSI63/*64* The generic MSI code returns with the interrupt disabled on the65* card, using the MSI mask bits. Firmware doesn't appear to unmask66* at that level, so we do it here by hand.67*/68if (irq_data_get_msi_desc(d))69pci_msi_unmask_irq(d);70#endif7172/* unmask it */73ics_native_unmask_irq(d);74return 0;75}7677static void ics_native_do_mask(struct ics_native *in, unsigned int vec)78{79out_be32(ics_native_xive(in, vec), 0xff);80}8182static void ics_native_mask_irq(struct irq_data *d)83{84unsigned int vec = (unsigned int)irqd_to_hwirq(d);85struct ics *ics = irq_data_get_irq_chip_data(d);86struct ics_native *in = to_ics_native(ics);8788pr_devel("ics-native: mask virq %d [hw 0x%x]\n", d->irq, vec);8990if (vec < in->ibase || vec >= (in->ibase + in->icount))91return;92ics_native_do_mask(in, vec);93}9495static int ics_native_set_affinity(struct irq_data *d,96const struct cpumask *cpumask,97bool force)98{99unsigned int vec = (unsigned int)irqd_to_hwirq(d);100struct ics *ics = irq_data_get_irq_chip_data(d);101struct ics_native *in = to_ics_native(ics);102int server;103u32 xive;104105if (vec < in->ibase || vec >= (in->ibase + in->icount))106return -EINVAL;107108server = xics_get_irq_server(d->irq, cpumask, 1);109if (server == -1) {110pr_warn("%s: No online cpus in the mask %*pb for irq %d\n",111__func__, cpumask_pr_args(cpumask), d->irq);112return -1;113}114115xive = in_be32(ics_native_xive(in, vec));116xive = (xive & 0xff) | (server << 8);117out_be32(ics_native_xive(in, vec), xive);118119return IRQ_SET_MASK_OK;120}121122static struct irq_chip ics_native_irq_chip = {123.name = "ICS",124.irq_startup = ics_native_startup,125.irq_mask = ics_native_mask_irq,126.irq_unmask = ics_native_unmask_irq,127.irq_eoi = NULL, /* Patched at init time */128.irq_set_affinity = ics_native_set_affinity,129.irq_set_type = xics_set_irq_type,130.irq_retrigger = xics_retrigger,131};132133static int ics_native_check(struct ics *ics, unsigned int hw_irq)134{135struct ics_native *in = to_ics_native(ics);136137pr_devel("%s: hw_irq=0x%x\n", __func__, hw_irq);138139if (hw_irq < in->ibase || hw_irq >= (in->ibase + in->icount))140return -EINVAL;141142return 0;143}144145static void ics_native_mask_unknown(struct ics *ics, unsigned long vec)146{147struct ics_native *in = to_ics_native(ics);148149if (vec < in->ibase || vec >= (in->ibase + in->icount))150return;151152ics_native_do_mask(in, vec);153}154155static long ics_native_get_server(struct ics *ics, unsigned long vec)156{157struct ics_native *in = to_ics_native(ics);158u32 xive;159160if (vec < in->ibase || vec >= (in->ibase + in->icount))161return -EINVAL;162163xive = in_be32(ics_native_xive(in, vec));164return (xive >> 8) & 0xfff;165}166167static int ics_native_host_match(struct ics *ics, struct device_node *node)168{169struct ics_native *in = to_ics_native(ics);170171return in->node == node;172}173174static struct ics ics_native_template = {175.check = ics_native_check,176.mask_unknown = ics_native_mask_unknown,177.get_server = ics_native_get_server,178.host_match = ics_native_host_match,179.chip = &ics_native_irq_chip,180};181182static int __init ics_native_add_one(struct device_node *np)183{184struct ics_native *ics;185u32 ranges[2];186int rc, count;187188ics = kzalloc(sizeof(struct ics_native), GFP_KERNEL);189if (!ics)190return -ENOMEM;191ics->node = of_node_get(np);192memcpy(&ics->ics, &ics_native_template, sizeof(struct ics));193194ics->base = of_iomap(np, 0);195if (!ics->base) {196pr_err("Failed to map %pOFP\n", np);197rc = -ENOMEM;198goto fail;199}200201count = of_property_count_u32_elems(np, "interrupt-ranges");202if (count < 2 || count & 1) {203pr_err("Failed to read interrupt-ranges of %pOFP\n", np);204rc = -EINVAL;205goto fail;206}207if (count > 2) {208pr_warn("ICS %pOFP has %d ranges, only one supported\n",209np, count >> 1);210}211rc = of_property_read_u32_array(np, "interrupt-ranges",212ranges, 2);213if (rc) {214pr_err("Failed to read interrupt-ranges of %pOFP\n", np);215goto fail;216}217ics->ibase = ranges[0];218ics->icount = ranges[1];219220pr_info("ICS native initialized for sources %d..%d\n",221ics->ibase, ics->ibase + ics->icount - 1);222223/* Register ourselves */224xics_register_ics(&ics->ics);225226return 0;227fail:228of_node_put(ics->node);229kfree(ics);230return rc;231}232233int __init ics_native_init(void)234{235struct device_node *ics;236bool found_one = false;237238/* We need to patch our irq chip's EOI to point to the239* right ICP240*/241ics_native_irq_chip.irq_eoi = icp_ops->eoi;242243/* Find native ICS in the device-tree */244for_each_compatible_node(ics, NULL, "openpower,xics-sources") {245if (ics_native_add_one(ics) == 0)246found_one = true;247}248249if (found_one)250pr_info("ICS native backend registered\n");251252return found_one ? 0 : -ENODEV;253}254255256