linux/arch/powerpc/sysdev/xics/icp-hv.c
<<
>>
Prefs
   1/*
   2 * Copyright 2011 IBM Corporation.
   3 *
   4 *  This program is free software; you can redistribute it and/or
   5 *  modify it under the terms of the GNU General Public License
   6 *  as published by the Free Software Foundation; either version
   7 *  2 of the License, or (at your option) any later version.
   8 *
   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/cpu.h>
  16#include <linux/of.h>
  17
  18#include <asm/smp.h>
  19#include <asm/irq.h>
  20#include <asm/errno.h>
  21#include <asm/xics.h>
  22#include <asm/io.h>
  23#include <asm/hvcall.h>
  24
  25static inline unsigned int icp_hv_get_xirr(unsigned char cppr)
  26{
  27        unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
  28        long rc;
  29        unsigned int ret = XICS_IRQ_SPURIOUS;
  30
  31        rc = plpar_hcall(H_XIRR, retbuf, cppr);
  32        if (rc == H_SUCCESS) {
  33                ret = (unsigned int)retbuf[0];
  34        } else {
  35                pr_err("%s: bad return code xirr cppr=0x%x returned %ld\n",
  36                        __func__, cppr, rc);
  37                WARN_ON_ONCE(1);
  38        }
  39
  40        return ret;
  41}
  42
  43static inline void icp_hv_set_cppr(u8 value)
  44{
  45        long rc = plpar_hcall_norets(H_CPPR, value);
  46        if (rc != H_SUCCESS) {
  47                pr_err("%s: bad return code cppr cppr=0x%x returned %ld\n",
  48                        __func__, value, rc);
  49                WARN_ON_ONCE(1);
  50        }
  51}
  52
  53static inline void icp_hv_set_xirr(unsigned int value)
  54{
  55        long rc = plpar_hcall_norets(H_EOI, value);
  56        if (rc != H_SUCCESS) {
  57                pr_err("%s: bad return code eoi xirr=0x%x returned %ld\n",
  58                        __func__, value, rc);
  59                WARN_ON_ONCE(1);
  60                icp_hv_set_cppr(value >> 24);
  61        }
  62}
  63
  64static inline void icp_hv_set_qirr(int n_cpu , u8 value)
  65{
  66        int hw_cpu = get_hard_smp_processor_id(n_cpu);
  67        long rc;
  68
  69        /* Make sure all previous accesses are ordered before IPI sending */
  70        mb();
  71        rc = plpar_hcall_norets(H_IPI, hw_cpu, value);
  72        if (rc != H_SUCCESS) {
  73                pr_err("%s: bad return code qirr cpu=%d hw_cpu=%d mfrr=0x%x "
  74                        "returned %ld\n", __func__, n_cpu, hw_cpu, value, rc);
  75                WARN_ON_ONCE(1);
  76        }
  77}
  78
  79static void icp_hv_eoi(struct irq_data *d)
  80{
  81        unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
  82
  83        iosync();
  84        icp_hv_set_xirr((xics_pop_cppr() << 24) | hw_irq);
  85}
  86
  87static void icp_hv_teardown_cpu(void)
  88{
  89        int cpu = smp_processor_id();
  90
  91        /* Clear any pending IPI */
  92        icp_hv_set_qirr(cpu, 0xff);
  93}
  94
  95static void icp_hv_flush_ipi(void)
  96{
  97        /* We take the ipi irq but and never return so we
  98         * need to EOI the IPI, but want to leave our priority 0
  99         *
 100         * should we check all the other interrupts too?
 101         * should we be flagging idle loop instead?
 102         * or creating some task to be scheduled?
 103         */
 104
 105        icp_hv_set_xirr((0x00 << 24) | XICS_IPI);
 106}
 107
 108static unsigned int icp_hv_get_irq(void)
 109{
 110        unsigned int xirr = icp_hv_get_xirr(xics_cppr_top());
 111        unsigned int vec = xirr & 0x00ffffff;
 112        unsigned int irq;
 113
 114        if (vec == XICS_IRQ_SPURIOUS)
 115                return NO_IRQ;
 116
 117        irq = irq_find_mapping(xics_host, vec);
 118        if (likely(irq != NO_IRQ)) {
 119                xics_push_cppr(vec);
 120                return irq;
 121        }
 122
 123        /* We don't have a linux mapping, so have rtas mask it. */
 124        xics_mask_unknown_vec(vec);
 125
 126        /* We might learn about it later, so EOI it */
 127        icp_hv_set_xirr(xirr);
 128
 129        return NO_IRQ;
 130}
 131
 132static void icp_hv_set_cpu_priority(unsigned char cppr)
 133{
 134        xics_set_base_cppr(cppr);
 135        icp_hv_set_cppr(cppr);
 136        iosync();
 137}
 138
 139#ifdef CONFIG_SMP
 140
 141static void icp_hv_cause_ipi(int cpu, unsigned long data)
 142{
 143        icp_hv_set_qirr(cpu, IPI_PRIORITY);
 144}
 145
 146static irqreturn_t icp_hv_ipi_action(int irq, void *dev_id)
 147{
 148        int cpu = smp_processor_id();
 149
 150        icp_hv_set_qirr(cpu, 0xff);
 151
 152        return smp_ipi_demux();
 153}
 154
 155#endif /* CONFIG_SMP */
 156
 157static const struct icp_ops icp_hv_ops = {
 158        .get_irq        = icp_hv_get_irq,
 159        .eoi            = icp_hv_eoi,
 160        .set_priority   = icp_hv_set_cpu_priority,
 161        .teardown_cpu   = icp_hv_teardown_cpu,
 162        .flush_ipi      = icp_hv_flush_ipi,
 163#ifdef CONFIG_SMP
 164        .ipi_action     = icp_hv_ipi_action,
 165        .cause_ipi      = icp_hv_cause_ipi,
 166#endif
 167};
 168
 169int icp_hv_init(void)
 170{
 171        struct device_node *np;
 172
 173        np = of_find_compatible_node(NULL, NULL, "ibm,ppc-xicp");
 174        if (!np)
 175                np = of_find_node_by_type(NULL,
 176                                    "PowerPC-External-Interrupt-Presentation");
 177        if (!np)
 178                return -ENODEV;
 179
 180        icp_ops = &icp_hv_ops;
 181
 182        return 0;
 183}
 184
 185