123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*
- * ICS backend for OPAL managed interrupts.
- *
- * Copyright 2011 IBM Corp.
- */
- //#define DEBUG
- #include <linux/types.h>
- #include <linux/kernel.h>
- #include <linux/irq.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/msi.h>
- #include <linux/list.h>
- #include <asm/smp.h>
- #include <asm/machdep.h>
- #include <asm/irq.h>
- #include <asm/errno.h>
- #include <asm/xics.h>
- #include <asm/opal.h>
- #include <asm/firmware.h>
- struct ics_native {
- struct ics ics;
- struct device_node *node;
- void __iomem *base;
- u32 ibase;
- u32 icount;
- };
- #define to_ics_native(_ics) container_of(_ics, struct ics_native, ics)
- static void __iomem *ics_native_xive(struct ics_native *in, unsigned int vec)
- {
- return in->base + 0x800 + ((vec - in->ibase) << 2);
- }
- static void ics_native_unmask_irq(struct irq_data *d)
- {
- unsigned int vec = (unsigned int)irqd_to_hwirq(d);
- struct ics *ics = irq_data_get_irq_chip_data(d);
- struct ics_native *in = to_ics_native(ics);
- unsigned int server;
- pr_devel("ics-native: unmask virq %d [hw 0x%x]\n", d->irq, vec);
- if (vec < in->ibase || vec >= (in->ibase + in->icount))
- return;
- server = xics_get_irq_server(d->irq, irq_data_get_affinity_mask(d), 0);
- out_be32(ics_native_xive(in, vec), (server << 8) | DEFAULT_PRIORITY);
- }
- static unsigned int ics_native_startup(struct irq_data *d)
- {
- #ifdef CONFIG_PCI_MSI
- /*
- * The generic MSI code returns with the interrupt disabled on the
- * card, using the MSI mask bits. Firmware doesn't appear to unmask
- * at that level, so we do it here by hand.
- */
- if (irq_data_get_msi_desc(d))
- pci_msi_unmask_irq(d);
- #endif
- /* unmask it */
- ics_native_unmask_irq(d);
- return 0;
- }
- static void ics_native_do_mask(struct ics_native *in, unsigned int vec)
- {
- out_be32(ics_native_xive(in, vec), 0xff);
- }
- static void ics_native_mask_irq(struct irq_data *d)
- {
- unsigned int vec = (unsigned int)irqd_to_hwirq(d);
- struct ics *ics = irq_data_get_irq_chip_data(d);
- struct ics_native *in = to_ics_native(ics);
- pr_devel("ics-native: mask virq %d [hw 0x%x]\n", d->irq, vec);
- if (vec < in->ibase || vec >= (in->ibase + in->icount))
- return;
- ics_native_do_mask(in, vec);
- }
- static int ics_native_set_affinity(struct irq_data *d,
- const struct cpumask *cpumask,
- bool force)
- {
- unsigned int vec = (unsigned int)irqd_to_hwirq(d);
- struct ics *ics = irq_data_get_irq_chip_data(d);
- struct ics_native *in = to_ics_native(ics);
- int server;
- u32 xive;
- if (vec < in->ibase || vec >= (in->ibase + in->icount))
- return -EINVAL;
- server = xics_get_irq_server(d->irq, cpumask, 1);
- if (server == -1) {
- pr_warn("%s: No online cpus in the mask %*pb for irq %d\n",
- __func__, cpumask_pr_args(cpumask), d->irq);
- return -1;
- }
- xive = in_be32(ics_native_xive(in, vec));
- xive = (xive & 0xff) | (server << 8);
- out_be32(ics_native_xive(in, vec), xive);
- return IRQ_SET_MASK_OK;
- }
- static struct irq_chip ics_native_irq_chip = {
- .name = "ICS",
- .irq_startup = ics_native_startup,
- .irq_mask = ics_native_mask_irq,
- .irq_unmask = ics_native_unmask_irq,
- .irq_eoi = NULL, /* Patched at init time */
- .irq_set_affinity = ics_native_set_affinity,
- .irq_set_type = xics_set_irq_type,
- .irq_retrigger = xics_retrigger,
- };
- static int ics_native_check(struct ics *ics, unsigned int hw_irq)
- {
- struct ics_native *in = to_ics_native(ics);
- pr_devel("%s: hw_irq=0x%x\n", __func__, hw_irq);
- if (hw_irq < in->ibase || hw_irq >= (in->ibase + in->icount))
- return -EINVAL;
- return 0;
- }
- static void ics_native_mask_unknown(struct ics *ics, unsigned long vec)
- {
- struct ics_native *in = to_ics_native(ics);
- if (vec < in->ibase || vec >= (in->ibase + in->icount))
- return;
- ics_native_do_mask(in, vec);
- }
- static long ics_native_get_server(struct ics *ics, unsigned long vec)
- {
- struct ics_native *in = to_ics_native(ics);
- u32 xive;
- if (vec < in->ibase || vec >= (in->ibase + in->icount))
- return -EINVAL;
- xive = in_be32(ics_native_xive(in, vec));
- return (xive >> 8) & 0xfff;
- }
- static int ics_native_host_match(struct ics *ics, struct device_node *node)
- {
- struct ics_native *in = to_ics_native(ics);
- return in->node == node;
- }
- static struct ics ics_native_template = {
- .check = ics_native_check,
- .mask_unknown = ics_native_mask_unknown,
- .get_server = ics_native_get_server,
- .host_match = ics_native_host_match,
- .chip = &ics_native_irq_chip,
- };
- static int __init ics_native_add_one(struct device_node *np)
- {
- struct ics_native *ics;
- u32 ranges[2];
- int rc, count;
- ics = kzalloc(sizeof(struct ics_native), GFP_KERNEL);
- if (!ics)
- return -ENOMEM;
- ics->node = of_node_get(np);
- memcpy(&ics->ics, &ics_native_template, sizeof(struct ics));
- ics->base = of_iomap(np, 0);
- if (!ics->base) {
- pr_err("Failed to map %pOFP\n", np);
- rc = -ENOMEM;
- goto fail;
- }
- count = of_property_count_u32_elems(np, "interrupt-ranges");
- if (count < 2 || count & 1) {
- pr_err("Failed to read interrupt-ranges of %pOFP\n", np);
- rc = -EINVAL;
- goto fail;
- }
- if (count > 2) {
- pr_warn("ICS %pOFP has %d ranges, only one supported\n",
- np, count >> 1);
- }
- rc = of_property_read_u32_array(np, "interrupt-ranges",
- ranges, 2);
- if (rc) {
- pr_err("Failed to read interrupt-ranges of %pOFP\n", np);
- goto fail;
- }
- ics->ibase = ranges[0];
- ics->icount = ranges[1];
- pr_info("ICS native initialized for sources %d..%d\n",
- ics->ibase, ics->ibase + ics->icount - 1);
- /* Register ourselves */
- xics_register_ics(&ics->ics);
- return 0;
- fail:
- of_node_put(ics->node);
- kfree(ics);
- return rc;
- }
- int __init ics_native_init(void)
- {
- struct device_node *ics;
- bool found_one = false;
- /* We need to patch our irq chip's EOI to point to the
- * right ICP
- */
- ics_native_irq_chip.irq_eoi = icp_ops->eoi;
- /* Find native ICS in the device-tree */
- for_each_compatible_node(ics, NULL, "openpower,xics-sources") {
- if (ics_native_add_one(ics) == 0)
- found_one = true;
- }
- if (found_one)
- pr_info("ICS native backend registered\n");
- return found_one ? 0 : -ENODEV;
- }
|