linux/arch/powerpc/sysdev/xics/ics-opal.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * ICS backend for OPAL managed interrupts.
   4 *
   5 * Copyright 2011 IBM Corp.
   6 */
   7
   8#undef DEBUG
   9
  10#include <linux/types.h>
  11#include <linux/kernel.h>
  12#include <linux/irq.h>
  13#include <linux/smp.h>
  14#include <linux/interrupt.h>
  15#include <linux/init.h>
  16#include <linux/cpu.h>
  17#include <linux/of.h>
  18#include <linux/spinlock.h>
  19#include <linux/msi.h>
  20
  21#include <asm/prom.h>
  22#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>
  29
  30static int ics_opal_mangle_server(int server)
  31{
  32        /* No link for now */
  33        return server << 2;
  34}
  35
  36static int ics_opal_unmangle_server(int server)
  37{
  38        /* No link for now */
  39        return server >> 2;
  40}
  41
  42static void ics_opal_unmask_irq(struct irq_data *d)
  43{
  44        unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
  45        int64_t rc;
  46        int server;
  47
  48        pr_devel("ics-hal: unmask virq %d [hw 0x%x]\n", d->irq, hw_irq);
  49
  50        if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS)
  51                return;
  52
  53        server = xics_get_irq_server(d->irq, irq_data_get_affinity_mask(d), 0);
  54        server = ics_opal_mangle_server(server);
  55
  56        rc = opal_set_xive(hw_irq, server, DEFAULT_PRIORITY);
  57        if (rc != OPAL_SUCCESS)
  58                pr_err("%s: opal_set_xive(irq=%d [hw 0x%x] server=%x)"
  59                       " error %lld\n",
  60                       __func__, d->irq, hw_irq, server, rc);
  61}
  62
  63static unsigned int ics_opal_startup(struct irq_data *d)
  64{
  65#ifdef CONFIG_PCI_MSI
  66        /*
  67         * The generic MSI code returns with the interrupt disabled on the
  68         * card, using the MSI mask bits. Firmware doesn't appear to unmask
  69         * at that level, so we do it here by hand.
  70         */
  71        if (irq_data_get_msi_desc(d))
  72                pci_msi_unmask_irq(d);
  73#endif
  74
  75        /* unmask it */
  76        ics_opal_unmask_irq(d);
  77        return 0;
  78}
  79
  80static void ics_opal_mask_real_irq(unsigned int hw_irq)
  81{
  82        int server = ics_opal_mangle_server(xics_default_server);
  83        int64_t rc;
  84
  85        if (hw_irq == XICS_IPI)
  86                return;
  87
  88        /* Have to set XIVE to 0xff to be able to remove a slot */
  89        rc = opal_set_xive(hw_irq, server, 0xff);
  90        if (rc != OPAL_SUCCESS)
  91                pr_err("%s: opal_set_xive(0xff) irq=%u returned %lld\n",
  92                       __func__, hw_irq, rc);
  93}
  94
  95static void ics_opal_mask_irq(struct irq_data *d)
  96{
  97        unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
  98
  99        pr_devel("ics-hal: mask virq %d [hw 0x%x]\n", d->irq, hw_irq);
 100
 101        if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS)
 102                return;
 103        ics_opal_mask_real_irq(hw_irq);
 104}
 105
 106static int ics_opal_set_affinity(struct irq_data *d,
 107                                 const struct cpumask *cpumask,
 108                                 bool force)
 109{
 110        unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
 111        __be16 oserver;
 112        int16_t server;
 113        int8_t priority;
 114        int64_t rc;
 115        int wanted_server;
 116
 117        if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS)
 118                return -1;
 119
 120        rc = opal_get_xive(hw_irq, &oserver, &priority);
 121        if (rc != OPAL_SUCCESS) {
 122                pr_err("%s: opal_get_xive(irq=%d [hw 0x%x]) error %lld\n",
 123                       __func__, d->irq, hw_irq, rc);
 124                return -1;
 125        }
 126        server = be16_to_cpu(oserver);
 127
 128        wanted_server = xics_get_irq_server(d->irq, cpumask, 1);
 129        if (wanted_server < 0) {
 130                pr_warn("%s: No online cpus in the mask %*pb for irq %d\n",
 131                        __func__, cpumask_pr_args(cpumask), d->irq);
 132                return -1;
 133        }
 134        server = ics_opal_mangle_server(wanted_server);
 135
 136        pr_devel("ics-hal: set-affinity irq %d [hw 0x%x] server: 0x%x/0x%x\n",
 137                 d->irq, hw_irq, wanted_server, server);
 138
 139        rc = opal_set_xive(hw_irq, server, priority);
 140        if (rc != OPAL_SUCCESS) {
 141                pr_err("%s: opal_set_xive(irq=%d [hw 0x%x] server=%x)"
 142                       " error %lld\n",
 143                       __func__, d->irq, hw_irq, server, rc);
 144                return -1;
 145        }
 146        return IRQ_SET_MASK_OK;
 147}
 148
 149static struct irq_chip ics_opal_irq_chip = {
 150        .name = "OPAL ICS",
 151        .irq_startup = ics_opal_startup,
 152        .irq_mask = ics_opal_mask_irq,
 153        .irq_unmask = ics_opal_unmask_irq,
 154        .irq_eoi = NULL, /* Patched at init time */
 155        .irq_set_affinity = ics_opal_set_affinity,
 156        .irq_set_type = xics_set_irq_type,
 157        .irq_retrigger = xics_retrigger,
 158};
 159
 160static int ics_opal_map(struct ics *ics, unsigned int virq);
 161static void ics_opal_mask_unknown(struct ics *ics, unsigned long vec);
 162static long ics_opal_get_server(struct ics *ics, unsigned long vec);
 163
 164static int ics_opal_host_match(struct ics *ics, struct device_node *node)
 165{
 166        return 1;
 167}
 168
 169/* Only one global & state struct ics */
 170static struct ics ics_hal = {
 171        .map            = ics_opal_map,
 172        .mask_unknown   = ics_opal_mask_unknown,
 173        .get_server     = ics_opal_get_server,
 174        .host_match     = ics_opal_host_match,
 175};
 176
 177static int ics_opal_map(struct ics *ics, unsigned int virq)
 178{
 179        unsigned int hw_irq = (unsigned int)virq_to_hw(virq);
 180        int64_t rc;
 181        __be16 server;
 182        int8_t priority;
 183
 184        if (WARN_ON(hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS))
 185                return -EINVAL;
 186
 187        /* Check if HAL knows about this interrupt */
 188        rc = opal_get_xive(hw_irq, &server, &priority);
 189        if (rc != OPAL_SUCCESS)
 190                return -ENXIO;
 191
 192        irq_set_chip_and_handler(virq, &ics_opal_irq_chip, handle_fasteoi_irq);
 193        irq_set_chip_data(virq, &ics_hal);
 194
 195        return 0;
 196}
 197
 198static void ics_opal_mask_unknown(struct ics *ics, unsigned long vec)
 199{
 200        int64_t rc;
 201        __be16 server;
 202        int8_t priority;
 203
 204        /* Check if HAL knows about this interrupt */
 205        rc = opal_get_xive(vec, &server, &priority);
 206        if (rc != OPAL_SUCCESS)
 207                return;
 208
 209        ics_opal_mask_real_irq(vec);
 210}
 211
 212static long ics_opal_get_server(struct ics *ics, unsigned long vec)
 213{
 214        int64_t rc;
 215        __be16 server;
 216        int8_t priority;
 217
 218        /* Check if HAL knows about this interrupt */
 219        rc = opal_get_xive(vec, &server, &priority);
 220        if (rc != OPAL_SUCCESS)
 221                return -1;
 222        return ics_opal_unmangle_server(be16_to_cpu(server));
 223}
 224
 225int __init ics_opal_init(void)
 226{
 227        if (!firmware_has_feature(FW_FEATURE_OPAL))
 228                return -ENODEV;
 229
 230        /* We need to patch our irq chip's EOI to point to the
 231         * right ICP
 232         */
 233        ics_opal_irq_chip.irq_eoi = icp_ops->eoi;
 234
 235        /* Register ourselves */
 236        xics_register_ics(&ics_hal);
 237
 238        pr_info("ICS OPAL backend registered\n");
 239
 240        return 0;
 241}
 242