root/arch/powerpc/sysdev/xics/icp-opal.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. icp_opal_teardown_cpu
  2. icp_opal_flush_ipi
  3. icp_opal_get_xirr
  4. icp_opal_get_irq
  5. icp_opal_set_cpu_priority
  6. icp_opal_eoi
  7. icp_opal_cause_ipi
  8. icp_opal_ipi_action
  9. icp_opal_flush_interrupt
  10. icp_opal_init

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * Copyright 2016 IBM Corporation.
   4  */
   5 #include <linux/types.h>
   6 #include <linux/kernel.h>
   7 #include <linux/irq.h>
   8 #include <linux/smp.h>
   9 #include <linux/interrupt.h>
  10 #include <linux/cpu.h>
  11 #include <linux/of.h>
  12 
  13 #include <asm/smp.h>
  14 #include <asm/irq.h>
  15 #include <asm/errno.h>
  16 #include <asm/xics.h>
  17 #include <asm/io.h>
  18 #include <asm/opal.h>
  19 #include <asm/kvm_ppc.h>
  20 
  21 static void icp_opal_teardown_cpu(void)
  22 {
  23         int hw_cpu = hard_smp_processor_id();
  24 
  25         /* Clear any pending IPI */
  26         opal_int_set_mfrr(hw_cpu, 0xff);
  27 }
  28 
  29 static void icp_opal_flush_ipi(void)
  30 {
  31         /*
  32          * We take the ipi irq but and never return so we need to EOI the IPI,
  33          * but want to leave our priority 0.
  34          *
  35          * Should we check all the other interrupts too?
  36          * Should we be flagging idle loop instead?
  37          * Or creating some task to be scheduled?
  38          */
  39         if (opal_int_eoi((0x00 << 24) | XICS_IPI) > 0)
  40                 force_external_irq_replay();
  41 }
  42 
  43 static unsigned int icp_opal_get_xirr(void)
  44 {
  45         unsigned int kvm_xirr;
  46         __be32 hw_xirr;
  47         int64_t rc;
  48 
  49         /* Handle an interrupt latched by KVM first */
  50         kvm_xirr = kvmppc_get_xics_latch();
  51         if (kvm_xirr)
  52                 return kvm_xirr;
  53 
  54         /* Then ask OPAL */
  55         rc = opal_int_get_xirr(&hw_xirr, false);
  56         if (rc < 0)
  57                 return 0;
  58         return be32_to_cpu(hw_xirr);
  59 }
  60 
  61 static unsigned int icp_opal_get_irq(void)
  62 {
  63         unsigned int xirr;
  64         unsigned int vec;
  65         unsigned int irq;
  66 
  67         xirr = icp_opal_get_xirr();
  68         vec = xirr & 0x00ffffff;
  69         if (vec == XICS_IRQ_SPURIOUS)
  70                 return 0;
  71 
  72         irq = irq_find_mapping(xics_host, vec);
  73         if (likely(irq)) {
  74                 xics_push_cppr(vec);
  75                 return irq;
  76         }
  77 
  78         /* We don't have a linux mapping, so have rtas mask it. */
  79         xics_mask_unknown_vec(vec);
  80 
  81         /* We might learn about it later, so EOI it */
  82         if (opal_int_eoi(xirr) > 0)
  83                 force_external_irq_replay();
  84 
  85         return 0;
  86 }
  87 
  88 static void icp_opal_set_cpu_priority(unsigned char cppr)
  89 {
  90         /*
  91          * Here be dragons. The caller has asked to allow only IPI's and not
  92          * external interrupts. But OPAL XIVE doesn't support that. So instead
  93          * of allowing no interrupts allow all. That's still not right, but
  94          * currently the only caller who does this is xics_migrate_irqs_away()
  95          * and it works in that case.
  96          */
  97         if (cppr >= DEFAULT_PRIORITY)
  98                 cppr = LOWEST_PRIORITY;
  99 
 100         xics_set_base_cppr(cppr);
 101         opal_int_set_cppr(cppr);
 102         iosync();
 103 }
 104 
 105 static void icp_opal_eoi(struct irq_data *d)
 106 {
 107         unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
 108         int64_t rc;
 109 
 110         iosync();
 111         rc = opal_int_eoi((xics_pop_cppr() << 24) | hw_irq);
 112 
 113         /*
 114          * EOI tells us whether there are more interrupts to fetch.
 115          *
 116          * Some HW implementations might not be able to send us another
 117          * external interrupt in that case, so we force a replay.
 118          */
 119         if (rc > 0)
 120                 force_external_irq_replay();
 121 }
 122 
 123 #ifdef CONFIG_SMP
 124 
 125 static void icp_opal_cause_ipi(int cpu)
 126 {
 127         int hw_cpu = get_hard_smp_processor_id(cpu);
 128 
 129         kvmppc_set_host_ipi(cpu);
 130         opal_int_set_mfrr(hw_cpu, IPI_PRIORITY);
 131 }
 132 
 133 static irqreturn_t icp_opal_ipi_action(int irq, void *dev_id)
 134 {
 135         int cpu = smp_processor_id();
 136 
 137         kvmppc_clear_host_ipi(cpu);
 138         opal_int_set_mfrr(get_hard_smp_processor_id(cpu), 0xff);
 139 
 140         return smp_ipi_demux();
 141 }
 142 
 143 /*
 144  * Called when an interrupt is received on an off-line CPU to
 145  * clear the interrupt, so that the CPU can go back to nap mode.
 146  */
 147 void icp_opal_flush_interrupt(void)
 148 {
 149         unsigned int xirr;
 150         unsigned int vec;
 151 
 152         do {
 153                 xirr = icp_opal_get_xirr();
 154                 vec = xirr & 0x00ffffff;
 155                 if (vec == XICS_IRQ_SPURIOUS)
 156                         break;
 157                 if (vec == XICS_IPI) {
 158                         /* Clear pending IPI */
 159                         int cpu = smp_processor_id();
 160                         kvmppc_clear_host_ipi(cpu);
 161                         opal_int_set_mfrr(get_hard_smp_processor_id(cpu), 0xff);
 162                 } else {
 163                         pr_err("XICS: hw interrupt 0x%x to offline cpu, "
 164                                "disabling\n", vec);
 165                         xics_mask_unknown_vec(vec);
 166                 }
 167 
 168                 /* EOI the interrupt */
 169         } while (opal_int_eoi(xirr) > 0);
 170 }
 171 
 172 #endif /* CONFIG_SMP */
 173 
 174 static const struct icp_ops icp_opal_ops = {
 175         .get_irq        = icp_opal_get_irq,
 176         .eoi            = icp_opal_eoi,
 177         .set_priority   = icp_opal_set_cpu_priority,
 178         .teardown_cpu   = icp_opal_teardown_cpu,
 179         .flush_ipi      = icp_opal_flush_ipi,
 180 #ifdef CONFIG_SMP
 181         .ipi_action     = icp_opal_ipi_action,
 182         .cause_ipi      = icp_opal_cause_ipi,
 183 #endif
 184 };
 185 
 186 int icp_opal_init(void)
 187 {
 188         struct device_node *np;
 189 
 190         np = of_find_compatible_node(NULL, NULL, "ibm,opal-intc");
 191         if (!np)
 192                 return -ENODEV;
 193 
 194         icp_ops = &icp_opal_ops;
 195 
 196         printk("XICS: Using OPAL ICP fallbacks\n");
 197 
 198         return 0;
 199 }
 200 

/* [<][>][^][v][top][bottom][index][help] */