19f0fd049SAlistair Popple /* 29f0fd049SAlistair Popple * This file implements an irqchip for OPAL events. Whenever there is 39f0fd049SAlistair Popple * an interrupt that is handled by OPAL we get passed a list of events 49f0fd049SAlistair Popple * that Linux needs to do something about. These basically look like 59f0fd049SAlistair Popple * interrupts to Linux so we implement an irqchip to handle them. 69f0fd049SAlistair Popple * 79f0fd049SAlistair Popple * Copyright Alistair Popple, IBM Corporation 2014. 89f0fd049SAlistair Popple * 99f0fd049SAlistair Popple * This program is free software; you can redistribute it and/or modify it 109f0fd049SAlistair Popple * under the terms of the GNU General Public License as published by the 119f0fd049SAlistair Popple * Free Software Foundation; either version 2 of the License, or (at your 129f0fd049SAlistair Popple * option) any later version. 139f0fd049SAlistair Popple */ 149f0fd049SAlistair Popple #include <linux/bitops.h> 159f0fd049SAlistair Popple #include <linux/irq.h> 169f0fd049SAlistair Popple #include <linux/irqchip.h> 179f0fd049SAlistair Popple #include <linux/irqdomain.h> 189f0fd049SAlistair Popple #include <linux/interrupt.h> 199f0fd049SAlistair Popple #include <linux/module.h> 209f0fd049SAlistair Popple #include <linux/of.h> 219f0fd049SAlistair Popple #include <linux/platform_device.h> 229f0fd049SAlistair Popple #include <linux/kthread.h> 239f0fd049SAlistair Popple #include <linux/delay.h> 249f0fd049SAlistair Popple #include <linux/slab.h> 259f0fd049SAlistair Popple 269f0fd049SAlistair Popple #include <asm/machdep.h> 279f0fd049SAlistair Popple #include <asm/opal.h> 289f0fd049SAlistair Popple 299f0fd049SAlistair Popple #include "powernv.h" 309f0fd049SAlistair Popple 319f0fd049SAlistair Popple /* Maximum number of events supported by OPAL firmware */ 329f0fd049SAlistair Popple #define MAX_NUM_EVENTS 64 339f0fd049SAlistair Popple 349f0fd049SAlistair Popple struct opal_event_irqchip { 359f0fd049SAlistair Popple struct irq_chip irqchip; 369f0fd049SAlistair Popple struct irq_domain *domain; 379f0fd049SAlistair Popple unsigned long mask; 389f0fd049SAlistair Popple }; 399f0fd049SAlistair Popple static struct opal_event_irqchip opal_event_irqchip; 4056c0b48bSNicholas Piggin static u64 last_outstanding_events; 419f0fd049SAlistair Popple static unsigned int opal_irq_count; 429f0fd049SAlistair Popple static unsigned int *opal_irqs; 439f0fd049SAlistair Popple 4456c0b48bSNicholas Piggin void opal_handle_events(void) 4525642e14SAlistair Popple { 4656c0b48bSNicholas Piggin __be64 events = 0; 4756c0b48bSNicholas Piggin u64 e; 4825642e14SAlistair Popple 4956c0b48bSNicholas Piggin e = READ_ONCE(last_outstanding_events) & opal_event_irqchip.mask; 5056c0b48bSNicholas Piggin again: 5156c0b48bSNicholas Piggin while (e) { 5256c0b48bSNicholas Piggin int virq, hwirq; 5325642e14SAlistair Popple 5456c0b48bSNicholas Piggin hwirq = fls64(e) - 1; 5556c0b48bSNicholas Piggin e &= ~BIT_ULL(hwirq); 5656c0b48bSNicholas Piggin 5756c0b48bSNicholas Piggin local_irq_disable(); 5856c0b48bSNicholas Piggin virq = irq_find_mapping(opal_event_irqchip.domain, hwirq); 5956c0b48bSNicholas Piggin if (virq) { 6056c0b48bSNicholas Piggin irq_enter(); 6125642e14SAlistair Popple generic_handle_irq(virq); 6256c0b48bSNicholas Piggin irq_exit(); 6325642e14SAlistair Popple } 6456c0b48bSNicholas Piggin local_irq_enable(); 6556c0b48bSNicholas Piggin 6656c0b48bSNicholas Piggin cond_resched(); 6725642e14SAlistair Popple } 6856c0b48bSNicholas Piggin last_outstanding_events = 0; 6956c0b48bSNicholas Piggin if (opal_poll_events(&events) != OPAL_SUCCESS) 7056c0b48bSNicholas Piggin return; 7156c0b48bSNicholas Piggin e = be64_to_cpu(events) & opal_event_irqchip.mask; 7256c0b48bSNicholas Piggin if (e) 7356c0b48bSNicholas Piggin goto again; 7456c0b48bSNicholas Piggin } 7556c0b48bSNicholas Piggin 7656c0b48bSNicholas Piggin bool opal_have_pending_events(void) 7756c0b48bSNicholas Piggin { 7856c0b48bSNicholas Piggin if (last_outstanding_events & opal_event_irqchip.mask) 7956c0b48bSNicholas Piggin return true; 8056c0b48bSNicholas Piggin return false; 8125642e14SAlistair Popple } 8225642e14SAlistair Popple 839f0fd049SAlistair Popple static void opal_event_mask(struct irq_data *d) 849f0fd049SAlistair Popple { 859f0fd049SAlistair Popple clear_bit(d->hwirq, &opal_event_irqchip.mask); 869f0fd049SAlistair Popple } 879f0fd049SAlistair Popple 889f0fd049SAlistair Popple static void opal_event_unmask(struct irq_data *d) 899f0fd049SAlistair Popple { 909f0fd049SAlistair Popple set_bit(d->hwirq, &opal_event_irqchip.mask); 9156c0b48bSNicholas Piggin if (opal_have_pending_events()) 9256c0b48bSNicholas Piggin opal_wake_poller(); 939f0fd049SAlistair Popple } 949f0fd049SAlistair Popple 959f0fd049SAlistair Popple static int opal_event_set_type(struct irq_data *d, unsigned int flow_type) 969f0fd049SAlistair Popple { 979f0fd049SAlistair Popple /* 989f0fd049SAlistair Popple * For now we only support level triggered events. The irq 999f0fd049SAlistair Popple * handler will be called continuously until the event has 1009f0fd049SAlistair Popple * been cleared in OPAL. 1019f0fd049SAlistair Popple */ 1029f0fd049SAlistair Popple if (flow_type != IRQ_TYPE_LEVEL_HIGH) 1039f0fd049SAlistair Popple return -EINVAL; 1049f0fd049SAlistair Popple 1059f0fd049SAlistair Popple return 0; 1069f0fd049SAlistair Popple } 1079f0fd049SAlistair Popple 1089f0fd049SAlistair Popple static struct opal_event_irqchip opal_event_irqchip = { 1099f0fd049SAlistair Popple .irqchip = { 1109f0fd049SAlistair Popple .name = "OPAL EVT", 1119f0fd049SAlistair Popple .irq_mask = opal_event_mask, 1129f0fd049SAlistair Popple .irq_unmask = opal_event_unmask, 1139f0fd049SAlistair Popple .irq_set_type = opal_event_set_type, 1149f0fd049SAlistair Popple }, 1159f0fd049SAlistair Popple .mask = 0, 1169f0fd049SAlistair Popple }; 1179f0fd049SAlistair Popple 1189f0fd049SAlistair Popple static int opal_event_map(struct irq_domain *d, unsigned int irq, 1199f0fd049SAlistair Popple irq_hw_number_t hwirq) 1209f0fd049SAlistair Popple { 1219f0fd049SAlistair Popple irq_set_chip_data(irq, &opal_event_irqchip); 1229f0fd049SAlistair Popple irq_set_chip_and_handler(irq, &opal_event_irqchip.irqchip, 1239f0fd049SAlistair Popple handle_level_irq); 1249f0fd049SAlistair Popple 1259f0fd049SAlistair Popple return 0; 1269f0fd049SAlistair Popple } 1279f0fd049SAlistair Popple 1289f0fd049SAlistair Popple static irqreturn_t opal_interrupt(int irq, void *data) 1299f0fd049SAlistair Popple { 1309f0fd049SAlistair Popple __be64 events; 1319f0fd049SAlistair Popple 1329f0fd049SAlistair Popple opal_handle_interrupt(virq_to_hw(irq), &events); 13356c0b48bSNicholas Piggin last_outstanding_events = be64_to_cpu(events); 13456c0b48bSNicholas Piggin if (opal_have_pending_events()) 13556c0b48bSNicholas Piggin opal_wake_poller(); 1369f0fd049SAlistair Popple 1379f0fd049SAlistair Popple return IRQ_HANDLED; 1389f0fd049SAlistair Popple } 1399f0fd049SAlistair Popple 140ad3aedfbSMarc Zyngier static int opal_event_match(struct irq_domain *h, struct device_node *node, 141ad3aedfbSMarc Zyngier enum irq_domain_bus_token bus_token) 1429f0fd049SAlistair Popple { 1435d4c9bc7SMarc Zyngier return irq_domain_get_of_node(h) == node; 1449f0fd049SAlistair Popple } 1459f0fd049SAlistair Popple 1469f0fd049SAlistair Popple static int opal_event_xlate(struct irq_domain *h, struct device_node *np, 1479f0fd049SAlistair Popple const u32 *intspec, unsigned int intsize, 1489f0fd049SAlistair Popple irq_hw_number_t *out_hwirq, unsigned int *out_flags) 1499f0fd049SAlistair Popple { 1509f0fd049SAlistair Popple *out_hwirq = intspec[0]; 1519f0fd049SAlistair Popple *out_flags = IRQ_TYPE_LEVEL_HIGH; 1529f0fd049SAlistair Popple 1539f0fd049SAlistair Popple return 0; 1549f0fd049SAlistair Popple } 1559f0fd049SAlistair Popple 1569f0fd049SAlistair Popple static const struct irq_domain_ops opal_event_domain_ops = { 1579f0fd049SAlistair Popple .match = opal_event_match, 1589f0fd049SAlistair Popple .map = opal_event_map, 1599f0fd049SAlistair Popple .xlate = opal_event_xlate, 1609f0fd049SAlistair Popple }; 1619f0fd049SAlistair Popple 1629f0fd049SAlistair Popple void opal_event_shutdown(void) 1639f0fd049SAlistair Popple { 1649f0fd049SAlistair Popple unsigned int i; 1659f0fd049SAlistair Popple 1669f0fd049SAlistair Popple /* First free interrupts, which will also mask them */ 1679f0fd049SAlistair Popple for (i = 0; i < opal_irq_count; i++) { 168c6baa077SMichael Ellerman if (!opal_irqs[i]) 169c6baa077SMichael Ellerman continue; 170c6baa077SMichael Ellerman 171c0beffc4SNicholas Piggin if (in_interrupt() || irqs_disabled()) 172c6baa077SMichael Ellerman disable_irq_nosync(opal_irqs[i]); 173c6baa077SMichael Ellerman else 1749f0fd049SAlistair Popple free_irq(opal_irqs[i], NULL); 175c6baa077SMichael Ellerman 1769f0fd049SAlistair Popple opal_irqs[i] = 0; 1779f0fd049SAlistair Popple } 1789f0fd049SAlistair Popple } 1799f0fd049SAlistair Popple 1809f0fd049SAlistair Popple int __init opal_event_init(void) 1819f0fd049SAlistair Popple { 1829f0fd049SAlistair Popple struct device_node *dn, *opal_node; 1832717a33dSBenjamin Herrenschmidt const char **names; 1842717a33dSBenjamin Herrenschmidt u32 *irqs; 1852717a33dSBenjamin Herrenschmidt int i, rc; 1869f0fd049SAlistair Popple 1879f0fd049SAlistair Popple opal_node = of_find_node_by_path("/ibm,opal"); 1889f0fd049SAlistair Popple if (!opal_node) { 1899f0fd049SAlistair Popple pr_warn("opal: Node not found\n"); 1909f0fd049SAlistair Popple return -ENODEV; 1919f0fd049SAlistair Popple } 1929f0fd049SAlistair Popple 1939f0fd049SAlistair Popple /* If dn is NULL it means the domain won't be linked to a DT 1949f0fd049SAlistair Popple * node so therefore irq_of_parse_and_map(...) wont work. But 1959f0fd049SAlistair Popple * that shouldn't be problem because if we're running a 1969f0fd049SAlistair Popple * version of skiboot that doesn't have the dn then the 1979f0fd049SAlistair Popple * devices won't have the correct properties and will have to 1989f0fd049SAlistair Popple * fall back to the legacy method (opal_event_request(...)) 1999f0fd049SAlistair Popple * anyway. */ 2009f0fd049SAlistair Popple dn = of_find_compatible_node(NULL, NULL, "ibm,opal-event"); 2019f0fd049SAlistair Popple opal_event_irqchip.domain = irq_domain_add_linear(dn, MAX_NUM_EVENTS, 2029f0fd049SAlistair Popple &opal_event_domain_ops, &opal_event_irqchip); 2039f0fd049SAlistair Popple of_node_put(dn); 2049f0fd049SAlistair Popple if (!opal_event_irqchip.domain) { 2059f0fd049SAlistair Popple pr_warn("opal: Unable to create irq domain\n"); 2069f0fd049SAlistair Popple rc = -ENOMEM; 2079f0fd049SAlistair Popple goto out; 2089f0fd049SAlistair Popple } 2099f0fd049SAlistair Popple 2102717a33dSBenjamin Herrenschmidt /* Get opal-interrupts property and names if present */ 2112717a33dSBenjamin Herrenschmidt rc = of_property_count_u32_elems(opal_node, "opal-interrupts"); 2122717a33dSBenjamin Herrenschmidt if (rc < 0) 2132717a33dSBenjamin Herrenschmidt goto out; 2142717a33dSBenjamin Herrenschmidt 2152717a33dSBenjamin Herrenschmidt opal_irq_count = rc; 2169f0fd049SAlistair Popple pr_debug("Found %d interrupts reserved for OPAL\n", opal_irq_count); 2179f0fd049SAlistair Popple 2182717a33dSBenjamin Herrenschmidt irqs = kcalloc(opal_irq_count, sizeof(*irqs), GFP_KERNEL); 2192717a33dSBenjamin Herrenschmidt names = kcalloc(opal_irq_count, sizeof(*names), GFP_KERNEL); 2209f0fd049SAlistair Popple opal_irqs = kcalloc(opal_irq_count, sizeof(*opal_irqs), GFP_KERNEL); 2212717a33dSBenjamin Herrenschmidt 2222717a33dSBenjamin Herrenschmidt if (WARN_ON(!irqs || !names || !opal_irqs)) 2232717a33dSBenjamin Herrenschmidt goto out_free; 2242717a33dSBenjamin Herrenschmidt 2252717a33dSBenjamin Herrenschmidt rc = of_property_read_u32_array(opal_node, "opal-interrupts", 2262717a33dSBenjamin Herrenschmidt irqs, opal_irq_count); 2272717a33dSBenjamin Herrenschmidt if (rc < 0) { 2282717a33dSBenjamin Herrenschmidt pr_err("Error %d reading opal-interrupts array\n", rc); 2292717a33dSBenjamin Herrenschmidt goto out_free; 2302717a33dSBenjamin Herrenschmidt } 2312717a33dSBenjamin Herrenschmidt 2322717a33dSBenjamin Herrenschmidt /* It's not an error for the names to be missing */ 2332717a33dSBenjamin Herrenschmidt of_property_read_string_array(opal_node, "opal-interrupts-names", 2342717a33dSBenjamin Herrenschmidt names, opal_irq_count); 2352717a33dSBenjamin Herrenschmidt 2362717a33dSBenjamin Herrenschmidt /* Install interrupt handlers */ 2372717a33dSBenjamin Herrenschmidt for (i = 0; i < opal_irq_count; i++) { 2382717a33dSBenjamin Herrenschmidt unsigned int virq; 2392717a33dSBenjamin Herrenschmidt char *name; 2409f0fd049SAlistair Popple 2419f0fd049SAlistair Popple /* Get hardware and virtual IRQ */ 2422717a33dSBenjamin Herrenschmidt virq = irq_create_mapping(NULL, irqs[i]); 243ef24ba70SMichael Ellerman if (!virq) { 2442717a33dSBenjamin Herrenschmidt pr_warn("Failed to map irq 0x%x\n", irqs[i]); 2459f0fd049SAlistair Popple continue; 2469f0fd049SAlistair Popple } 2479f0fd049SAlistair Popple 2482717a33dSBenjamin Herrenschmidt if (names[i] && strlen(names[i])) 2492717a33dSBenjamin Herrenschmidt name = kasprintf(GFP_KERNEL, "opal-%s", names[i]); 2502717a33dSBenjamin Herrenschmidt else 2512717a33dSBenjamin Herrenschmidt name = kasprintf(GFP_KERNEL, "opal"); 2522717a33dSBenjamin Herrenschmidt 2539f0fd049SAlistair Popple /* Install interrupt handler */ 254880a3d6aSBenjamin Herrenschmidt rc = request_irq(virq, opal_interrupt, IRQF_TRIGGER_LOW, 2552717a33dSBenjamin Herrenschmidt name, NULL); 2569f0fd049SAlistair Popple if (rc) { 2579f0fd049SAlistair Popple irq_dispose_mapping(virq); 2589f0fd049SAlistair Popple pr_warn("Error %d requesting irq %d (0x%x)\n", 2592717a33dSBenjamin Herrenschmidt rc, virq, irqs[i]); 2609f0fd049SAlistair Popple continue; 2619f0fd049SAlistair Popple } 2629f0fd049SAlistair Popple 2639f0fd049SAlistair Popple /* Cache IRQ */ 2649f0fd049SAlistair Popple opal_irqs[i] = virq; 2659f0fd049SAlistair Popple } 2669f0fd049SAlistair Popple 2672717a33dSBenjamin Herrenschmidt out_free: 2682717a33dSBenjamin Herrenschmidt kfree(irqs); 2692717a33dSBenjamin Herrenschmidt kfree(names); 2709f0fd049SAlistair Popple out: 2719f0fd049SAlistair Popple of_node_put(opal_node); 2729f0fd049SAlistair Popple return rc; 2739f0fd049SAlistair Popple } 27402b6505cSAlistair Popple machine_arch_initcall(powernv, opal_event_init); 2759f0fd049SAlistair Popple 2769f0fd049SAlistair Popple /** 2779f0fd049SAlistair Popple * opal_event_request(unsigned int opal_event_nr) - Request an event 2789f0fd049SAlistair Popple * @opal_event_nr: the opal event number to request 2799f0fd049SAlistair Popple * 2809f0fd049SAlistair Popple * This routine can be used to find the linux virq number which can 2819f0fd049SAlistair Popple * then be passed to request_irq to assign a handler for a particular 2829f0fd049SAlistair Popple * opal event. This should only be used by legacy devices which don't 2839f0fd049SAlistair Popple * have proper device tree bindings. Most devices should use 2849f0fd049SAlistair Popple * irq_of_parse_and_map() instead. 2859f0fd049SAlistair Popple */ 2869f0fd049SAlistair Popple int opal_event_request(unsigned int opal_event_nr) 2879f0fd049SAlistair Popple { 28802b6505cSAlistair Popple if (WARN_ON_ONCE(!opal_event_irqchip.domain)) 289ef24ba70SMichael Ellerman return 0; 29002b6505cSAlistair Popple 2919f0fd049SAlistair Popple return irq_create_mapping(opal_event_irqchip.domain, opal_event_nr); 2929f0fd049SAlistair Popple } 2939f0fd049SAlistair Popple EXPORT_SYMBOL(opal_event_request); 294