Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * This file implements an irqchip for OPAL events. Whenever there is
0004  * an interrupt that is handled by OPAL we get passed a list of events
0005  * that Linux needs to do something about. These basically look like
0006  * interrupts to Linux so we implement an irqchip to handle them.
0007  *
0008  * Copyright Alistair Popple, IBM Corporation 2014.
0009  */
0010 #include <linux/bitops.h>
0011 #include <linux/irq.h>
0012 #include <linux/irqchip.h>
0013 #include <linux/irqdomain.h>
0014 #include <linux/interrupt.h>
0015 #include <linux/module.h>
0016 #include <linux/of.h>
0017 #include <linux/platform_device.h>
0018 #include <linux/kthread.h>
0019 #include <linux/delay.h>
0020 #include <linux/slab.h>
0021 #include <linux/of_irq.h>
0022 
0023 #include <asm/machdep.h>
0024 #include <asm/opal.h>
0025 
0026 #include "powernv.h"
0027 
0028 /* Maximum number of events supported by OPAL firmware */
0029 #define MAX_NUM_EVENTS 64
0030 
0031 struct opal_event_irqchip {
0032     struct irq_chip irqchip;
0033     struct irq_domain *domain;
0034     unsigned long mask;
0035 };
0036 static struct opal_event_irqchip opal_event_irqchip;
0037 static u64 last_outstanding_events;
0038 static int opal_irq_count;
0039 static struct resource *opal_irqs;
0040 
0041 void opal_handle_events(void)
0042 {
0043     __be64 events = 0;
0044     u64 e;
0045 
0046     e = READ_ONCE(last_outstanding_events) & opal_event_irqchip.mask;
0047 again:
0048     while (e) {
0049         int hwirq;
0050 
0051         hwirq = fls64(e) - 1;
0052         e &= ~BIT_ULL(hwirq);
0053 
0054         local_irq_disable();
0055         irq_enter();
0056         generic_handle_domain_irq(opal_event_irqchip.domain, hwirq);
0057         irq_exit();
0058         local_irq_enable();
0059 
0060         cond_resched();
0061     }
0062     last_outstanding_events = 0;
0063     if (opal_poll_events(&events) != OPAL_SUCCESS)
0064         return;
0065     e = be64_to_cpu(events) & opal_event_irqchip.mask;
0066     if (e)
0067         goto again;
0068 }
0069 
0070 bool opal_have_pending_events(void)
0071 {
0072     if (last_outstanding_events & opal_event_irqchip.mask)
0073         return true;
0074     return false;
0075 }
0076 
0077 static void opal_event_mask(struct irq_data *d)
0078 {
0079     clear_bit(d->hwirq, &opal_event_irqchip.mask);
0080 }
0081 
0082 static void opal_event_unmask(struct irq_data *d)
0083 {
0084     set_bit(d->hwirq, &opal_event_irqchip.mask);
0085     if (opal_have_pending_events())
0086         opal_wake_poller();
0087 }
0088 
0089 static int opal_event_set_type(struct irq_data *d, unsigned int flow_type)
0090 {
0091     /*
0092      * For now we only support level triggered events. The irq
0093      * handler will be called continuously until the event has
0094      * been cleared in OPAL.
0095      */
0096     if (flow_type != IRQ_TYPE_LEVEL_HIGH)
0097         return -EINVAL;
0098 
0099     return 0;
0100 }
0101 
0102 static struct opal_event_irqchip opal_event_irqchip = {
0103     .irqchip = {
0104         .name = "OPAL EVT",
0105         .irq_mask = opal_event_mask,
0106         .irq_unmask = opal_event_unmask,
0107         .irq_set_type = opal_event_set_type,
0108     },
0109     .mask = 0,
0110 };
0111 
0112 static int opal_event_map(struct irq_domain *d, unsigned int irq,
0113             irq_hw_number_t hwirq)
0114 {
0115     irq_set_chip_data(irq, &opal_event_irqchip);
0116     irq_set_chip_and_handler(irq, &opal_event_irqchip.irqchip,
0117                 handle_level_irq);
0118 
0119     return 0;
0120 }
0121 
0122 static irqreturn_t opal_interrupt(int irq, void *data)
0123 {
0124     __be64 events;
0125 
0126     opal_handle_interrupt(virq_to_hw(irq), &events);
0127     last_outstanding_events = be64_to_cpu(events);
0128     if (opal_have_pending_events())
0129         opal_wake_poller();
0130 
0131     return IRQ_HANDLED;
0132 }
0133 
0134 static int opal_event_match(struct irq_domain *h, struct device_node *node,
0135                 enum irq_domain_bus_token bus_token)
0136 {
0137     return irq_domain_get_of_node(h) == node;
0138 }
0139 
0140 static int opal_event_xlate(struct irq_domain *h, struct device_node *np,
0141                const u32 *intspec, unsigned int intsize,
0142                irq_hw_number_t *out_hwirq, unsigned int *out_flags)
0143 {
0144     *out_hwirq = intspec[0];
0145     *out_flags = IRQ_TYPE_LEVEL_HIGH;
0146 
0147     return 0;
0148 }
0149 
0150 static const struct irq_domain_ops opal_event_domain_ops = {
0151     .match  = opal_event_match,
0152     .map    = opal_event_map,
0153     .xlate  = opal_event_xlate,
0154 };
0155 
0156 void opal_event_shutdown(void)
0157 {
0158     unsigned int i;
0159 
0160     /* First free interrupts, which will also mask them */
0161     for (i = 0; i < opal_irq_count; i++) {
0162         if (!opal_irqs || !opal_irqs[i].start)
0163             continue;
0164 
0165         if (in_interrupt() || irqs_disabled())
0166             disable_irq_nosync(opal_irqs[i].start);
0167         else
0168             free_irq(opal_irqs[i].start, NULL);
0169 
0170         opal_irqs[i].start = 0;
0171     }
0172 }
0173 
0174 int __init opal_event_init(void)
0175 {
0176     struct device_node *dn, *opal_node;
0177     bool old_style = false;
0178     int i, rc = 0;
0179 
0180     opal_node = of_find_node_by_path("/ibm,opal");
0181     if (!opal_node) {
0182         pr_warn("opal: Node not found\n");
0183         return -ENODEV;
0184     }
0185 
0186     /* If dn is NULL it means the domain won't be linked to a DT
0187      * node so therefore irq_of_parse_and_map(...) wont work. But
0188      * that shouldn't be problem because if we're running a
0189      * version of skiboot that doesn't have the dn then the
0190      * devices won't have the correct properties and will have to
0191      * fall back to the legacy method (opal_event_request(...))
0192      * anyway. */
0193     dn = of_find_compatible_node(NULL, NULL, "ibm,opal-event");
0194     opal_event_irqchip.domain = irq_domain_add_linear(dn, MAX_NUM_EVENTS,
0195                 &opal_event_domain_ops, &opal_event_irqchip);
0196     of_node_put(dn);
0197     if (!opal_event_irqchip.domain) {
0198         pr_warn("opal: Unable to create irq domain\n");
0199         rc = -ENOMEM;
0200         goto out;
0201     }
0202 
0203     /* Look for new-style (standard) "interrupts" property */
0204     opal_irq_count = of_irq_count(opal_node);
0205 
0206     /* Absent ? Look for the old one */
0207     if (opal_irq_count < 1) {
0208         /* Get opal-interrupts property and names if present */
0209         rc = of_property_count_u32_elems(opal_node, "opal-interrupts");
0210         if (rc > 0)
0211             opal_irq_count = rc;
0212         old_style = true;
0213     }
0214 
0215     /* No interrupts ? Bail out */
0216     if (!opal_irq_count)
0217         goto out;
0218 
0219     pr_debug("OPAL: Found %d interrupts reserved for OPAL using %s scheme\n",
0220          opal_irq_count, old_style ? "old" : "new");
0221 
0222     /* Allocate an IRQ resources array */
0223     opal_irqs = kcalloc(opal_irq_count, sizeof(struct resource), GFP_KERNEL);
0224     if (WARN_ON(!opal_irqs)) {
0225         rc = -ENOMEM;
0226         goto out;
0227     }
0228 
0229     /* Build the resources array */
0230     if (old_style) {
0231         /* Old style "opal-interrupts" property */
0232         for (i = 0; i < opal_irq_count; i++) {
0233             struct resource *r = &opal_irqs[i];
0234             const char *name = NULL;
0235             u32 hw_irq;
0236             int virq;
0237 
0238             rc = of_property_read_u32_index(opal_node, "opal-interrupts",
0239                             i, &hw_irq);
0240             if (WARN_ON(rc < 0)) {
0241                 opal_irq_count = i;
0242                 break;
0243             }
0244             of_property_read_string_index(opal_node, "opal-interrupts-names",
0245                               i, &name);
0246             virq = irq_create_mapping(NULL, hw_irq);
0247             if (!virq) {
0248                 pr_warn("Failed to map OPAL irq 0x%x\n", hw_irq);
0249                 continue;
0250             }
0251             r->start = r->end = virq;
0252             r->flags = IORESOURCE_IRQ | IRQ_TYPE_LEVEL_LOW;
0253             r->name = name;
0254         }
0255     } else {
0256         /* new style standard "interrupts" property */
0257         rc = of_irq_to_resource_table(opal_node, opal_irqs, opal_irq_count);
0258         if (WARN_ON(rc < 0)) {
0259             opal_irq_count = 0;
0260             kfree(opal_irqs);
0261             goto out;
0262         }
0263         if (WARN_ON(rc < opal_irq_count))
0264             opal_irq_count = rc;
0265     }
0266 
0267     /* Install interrupt handlers */
0268     for (i = 0; i < opal_irq_count; i++) {
0269         struct resource *r = &opal_irqs[i];
0270         const char *name;
0271 
0272         /* Prefix name */
0273         if (r->name && strlen(r->name))
0274             name = kasprintf(GFP_KERNEL, "opal-%s", r->name);
0275         else
0276             name = kasprintf(GFP_KERNEL, "opal");
0277 
0278         /* Install interrupt handler */
0279         rc = request_irq(r->start, opal_interrupt, r->flags & IRQD_TRIGGER_MASK,
0280                  name, NULL);
0281         if (rc) {
0282             pr_warn("Error %d requesting OPAL irq %d\n", rc, (int)r->start);
0283             continue;
0284         }
0285     }
0286     rc = 0;
0287  out:
0288     of_node_put(opal_node);
0289     return rc;
0290 }
0291 machine_arch_initcall(powernv, opal_event_init);
0292 
0293 /**
0294  * opal_event_request(unsigned int opal_event_nr) - Request an event
0295  * @opal_event_nr: the opal event number to request
0296  *
0297  * This routine can be used to find the linux virq number which can
0298  * then be passed to request_irq to assign a handler for a particular
0299  * opal event. This should only be used by legacy devices which don't
0300  * have proper device tree bindings. Most devices should use
0301  * irq_of_parse_and_map() instead.
0302  */
0303 int opal_event_request(unsigned int opal_event_nr)
0304 {
0305     if (WARN_ON_ONCE(!opal_event_irqchip.domain))
0306         return 0;
0307 
0308     return irq_create_mapping(opal_event_irqchip.domain, opal_event_nr);
0309 }
0310 EXPORT_SYMBOL(opal_event_request);