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 #include <linux/irq_work.h> 269f0fd049SAlistair Popple 279f0fd049SAlistair Popple #include <asm/machdep.h> 289f0fd049SAlistair Popple #include <asm/opal.h> 299f0fd049SAlistair Popple 309f0fd049SAlistair Popple #include "powernv.h" 319f0fd049SAlistair Popple 329f0fd049SAlistair Popple /* Maximum number of events supported by OPAL firmware */ 339f0fd049SAlistair Popple #define MAX_NUM_EVENTS 64 349f0fd049SAlistair Popple 359f0fd049SAlistair Popple struct opal_event_irqchip { 369f0fd049SAlistair Popple struct irq_chip irqchip; 379f0fd049SAlistair Popple struct irq_domain *domain; 389f0fd049SAlistair Popple unsigned long mask; 399f0fd049SAlistair Popple }; 409f0fd049SAlistair Popple static struct opal_event_irqchip opal_event_irqchip; 419f0fd049SAlistair Popple 429f0fd049SAlistair Popple static unsigned int opal_irq_count; 439f0fd049SAlistair Popple static unsigned int *opal_irqs; 449f0fd049SAlistair Popple 459f0fd049SAlistair Popple static void opal_handle_irq_work(struct irq_work *work); 4625642e14SAlistair Popple static u64 last_outstanding_events; 479f0fd049SAlistair Popple static struct irq_work opal_event_irq_work = { 489f0fd049SAlistair Popple .func = opal_handle_irq_work, 499f0fd049SAlistair Popple }; 509f0fd049SAlistair Popple 5125642e14SAlistair Popple void opal_handle_events(uint64_t events) 5225642e14SAlistair Popple { 5325642e14SAlistair Popple int virq, hwirq = 0; 5425642e14SAlistair Popple u64 mask = opal_event_irqchip.mask; 5525642e14SAlistair Popple 5625642e14SAlistair Popple if (!in_irq() && (events & mask)) { 5725642e14SAlistair Popple last_outstanding_events = events; 5825642e14SAlistair Popple irq_work_queue(&opal_event_irq_work); 5925642e14SAlistair Popple return; 6025642e14SAlistair Popple } 6125642e14SAlistair Popple 6225642e14SAlistair Popple while (events & mask) { 6325642e14SAlistair Popple hwirq = fls64(events) - 1; 6425642e14SAlistair Popple if (BIT_ULL(hwirq) & mask) { 6525642e14SAlistair Popple virq = irq_find_mapping(opal_event_irqchip.domain, 6625642e14SAlistair Popple hwirq); 6725642e14SAlistair Popple if (virq) 6825642e14SAlistair Popple generic_handle_irq(virq); 6925642e14SAlistair Popple } 7025642e14SAlistair Popple events &= ~BIT_ULL(hwirq); 7125642e14SAlistair Popple } 7225642e14SAlistair Popple } 7325642e14SAlistair Popple 749f0fd049SAlistair Popple static void opal_event_mask(struct irq_data *d) 759f0fd049SAlistair Popple { 769f0fd049SAlistair Popple clear_bit(d->hwirq, &opal_event_irqchip.mask); 779f0fd049SAlistair Popple } 789f0fd049SAlistair Popple 799f0fd049SAlistair Popple static void opal_event_unmask(struct irq_data *d) 809f0fd049SAlistair Popple { 8125642e14SAlistair Popple __be64 events; 8225642e14SAlistair Popple 839f0fd049SAlistair Popple set_bit(d->hwirq, &opal_event_irqchip.mask); 849f0fd049SAlistair Popple 8525642e14SAlistair Popple opal_poll_events(&events); 86036592fbSAlistair Popple last_outstanding_events = be64_to_cpu(events); 87036592fbSAlistair Popple 88036592fbSAlistair Popple /* 89036592fbSAlistair Popple * We can't just handle the events now with opal_handle_events(). 90036592fbSAlistair Popple * If we did we would deadlock when opal_event_unmask() is called from 91036592fbSAlistair Popple * handle_level_irq() with the irq descriptor lock held, because 92036592fbSAlistair Popple * calling opal_handle_events() would call generic_handle_irq() and 93036592fbSAlistair Popple * then handle_level_irq() which would try to take the descriptor lock 94036592fbSAlistair Popple * again. Instead queue the events for later. 95036592fbSAlistair Popple */ 96036592fbSAlistair Popple if (last_outstanding_events & opal_event_irqchip.mask) 97036592fbSAlistair Popple /* Need to retrigger the interrupt */ 98036592fbSAlistair Popple irq_work_queue(&opal_event_irq_work); 999f0fd049SAlistair Popple } 1009f0fd049SAlistair Popple 1019f0fd049SAlistair Popple static int opal_event_set_type(struct irq_data *d, unsigned int flow_type) 1029f0fd049SAlistair Popple { 1039f0fd049SAlistair Popple /* 1049f0fd049SAlistair Popple * For now we only support level triggered events. The irq 1059f0fd049SAlistair Popple * handler will be called continuously until the event has 1069f0fd049SAlistair Popple * been cleared in OPAL. 1079f0fd049SAlistair Popple */ 1089f0fd049SAlistair Popple if (flow_type != IRQ_TYPE_LEVEL_HIGH) 1099f0fd049SAlistair Popple return -EINVAL; 1109f0fd049SAlistair Popple 1119f0fd049SAlistair Popple return 0; 1129f0fd049SAlistair Popple } 1139f0fd049SAlistair Popple 1149f0fd049SAlistair Popple static struct opal_event_irqchip opal_event_irqchip = { 1159f0fd049SAlistair Popple .irqchip = { 1169f0fd049SAlistair Popple .name = "OPAL EVT", 1179f0fd049SAlistair Popple .irq_mask = opal_event_mask, 1189f0fd049SAlistair Popple .irq_unmask = opal_event_unmask, 1199f0fd049SAlistair Popple .irq_set_type = opal_event_set_type, 1209f0fd049SAlistair Popple }, 1219f0fd049SAlistair Popple .mask = 0, 1229f0fd049SAlistair Popple }; 1239f0fd049SAlistair Popple 1249f0fd049SAlistair Popple static int opal_event_map(struct irq_domain *d, unsigned int irq, 1259f0fd049SAlistair Popple irq_hw_number_t hwirq) 1269f0fd049SAlistair Popple { 1279f0fd049SAlistair Popple irq_set_chip_data(irq, &opal_event_irqchip); 1289f0fd049SAlistair Popple irq_set_chip_and_handler(irq, &opal_event_irqchip.irqchip, 1299f0fd049SAlistair Popple handle_level_irq); 1309f0fd049SAlistair Popple 1319f0fd049SAlistair Popple return 0; 1329f0fd049SAlistair Popple } 1339f0fd049SAlistair Popple 1349f0fd049SAlistair Popple static irqreturn_t opal_interrupt(int irq, void *data) 1359f0fd049SAlistair Popple { 1369f0fd049SAlistair Popple __be64 events; 1379f0fd049SAlistair Popple 1389f0fd049SAlistair Popple opal_handle_interrupt(virq_to_hw(irq), &events); 1399f0fd049SAlistair Popple opal_handle_events(be64_to_cpu(events)); 1409f0fd049SAlistair Popple 1419f0fd049SAlistair Popple return IRQ_HANDLED; 1429f0fd049SAlistair Popple } 1439f0fd049SAlistair Popple 1449f0fd049SAlistair Popple static void opal_handle_irq_work(struct irq_work *work) 1459f0fd049SAlistair Popple { 14625642e14SAlistair Popple opal_handle_events(last_outstanding_events); 1479f0fd049SAlistair Popple } 1489f0fd049SAlistair Popple 149ad3aedfbSMarc Zyngier static int opal_event_match(struct irq_domain *h, struct device_node *node, 150ad3aedfbSMarc Zyngier enum irq_domain_bus_token bus_token) 1519f0fd049SAlistair Popple { 1525d4c9bc7SMarc Zyngier return irq_domain_get_of_node(h) == node; 1539f0fd049SAlistair Popple } 1549f0fd049SAlistair Popple 1559f0fd049SAlistair Popple static int opal_event_xlate(struct irq_domain *h, struct device_node *np, 1569f0fd049SAlistair Popple const u32 *intspec, unsigned int intsize, 1579f0fd049SAlistair Popple irq_hw_number_t *out_hwirq, unsigned int *out_flags) 1589f0fd049SAlistair Popple { 1599f0fd049SAlistair Popple *out_hwirq = intspec[0]; 1609f0fd049SAlistair Popple *out_flags = IRQ_TYPE_LEVEL_HIGH; 1619f0fd049SAlistair Popple 1629f0fd049SAlistair Popple return 0; 1639f0fd049SAlistair Popple } 1649f0fd049SAlistair Popple 1659f0fd049SAlistair Popple static const struct irq_domain_ops opal_event_domain_ops = { 1669f0fd049SAlistair Popple .match = opal_event_match, 1679f0fd049SAlistair Popple .map = opal_event_map, 1689f0fd049SAlistair Popple .xlate = opal_event_xlate, 1699f0fd049SAlistair Popple }; 1709f0fd049SAlistair Popple 1719f0fd049SAlistair Popple void opal_event_shutdown(void) 1729f0fd049SAlistair Popple { 1739f0fd049SAlistair Popple unsigned int i; 1749f0fd049SAlistair Popple 1759f0fd049SAlistair Popple /* First free interrupts, which will also mask them */ 1769f0fd049SAlistair Popple for (i = 0; i < opal_irq_count; i++) { 1779f0fd049SAlistair Popple if (opal_irqs[i]) 1789f0fd049SAlistair Popple free_irq(opal_irqs[i], NULL); 1799f0fd049SAlistair Popple opal_irqs[i] = 0; 1809f0fd049SAlistair Popple } 1819f0fd049SAlistair Popple } 1829f0fd049SAlistair Popple 1839f0fd049SAlistair Popple int __init opal_event_init(void) 1849f0fd049SAlistair Popple { 1859f0fd049SAlistair Popple struct device_node *dn, *opal_node; 1862717a33dSBenjamin Herrenschmidt const char **names; 1872717a33dSBenjamin Herrenschmidt u32 *irqs; 1882717a33dSBenjamin Herrenschmidt int i, rc; 1899f0fd049SAlistair Popple 1909f0fd049SAlistair Popple opal_node = of_find_node_by_path("/ibm,opal"); 1919f0fd049SAlistair Popple if (!opal_node) { 1929f0fd049SAlistair Popple pr_warn("opal: Node not found\n"); 1939f0fd049SAlistair Popple return -ENODEV; 1949f0fd049SAlistair Popple } 1959f0fd049SAlistair Popple 1969f0fd049SAlistair Popple /* If dn is NULL it means the domain won't be linked to a DT 1979f0fd049SAlistair Popple * node so therefore irq_of_parse_and_map(...) wont work. But 1989f0fd049SAlistair Popple * that shouldn't be problem because if we're running a 1999f0fd049SAlistair Popple * version of skiboot that doesn't have the dn then the 2009f0fd049SAlistair Popple * devices won't have the correct properties and will have to 2019f0fd049SAlistair Popple * fall back to the legacy method (opal_event_request(...)) 2029f0fd049SAlistair Popple * anyway. */ 2039f0fd049SAlistair Popple dn = of_find_compatible_node(NULL, NULL, "ibm,opal-event"); 2049f0fd049SAlistair Popple opal_event_irqchip.domain = irq_domain_add_linear(dn, MAX_NUM_EVENTS, 2059f0fd049SAlistair Popple &opal_event_domain_ops, &opal_event_irqchip); 2069f0fd049SAlistair Popple of_node_put(dn); 2079f0fd049SAlistair Popple if (!opal_event_irqchip.domain) { 2089f0fd049SAlistair Popple pr_warn("opal: Unable to create irq domain\n"); 2099f0fd049SAlistair Popple rc = -ENOMEM; 2109f0fd049SAlistair Popple goto out; 2119f0fd049SAlistair Popple } 2129f0fd049SAlistair Popple 2132717a33dSBenjamin Herrenschmidt /* Get opal-interrupts property and names if present */ 2142717a33dSBenjamin Herrenschmidt rc = of_property_count_u32_elems(opal_node, "opal-interrupts"); 2152717a33dSBenjamin Herrenschmidt if (rc < 0) 2162717a33dSBenjamin Herrenschmidt goto out; 2172717a33dSBenjamin Herrenschmidt 2182717a33dSBenjamin Herrenschmidt opal_irq_count = rc; 2199f0fd049SAlistair Popple pr_debug("Found %d interrupts reserved for OPAL\n", opal_irq_count); 2209f0fd049SAlistair Popple 2212717a33dSBenjamin Herrenschmidt irqs = kcalloc(opal_irq_count, sizeof(*irqs), GFP_KERNEL); 2222717a33dSBenjamin Herrenschmidt names = kcalloc(opal_irq_count, sizeof(*names), GFP_KERNEL); 2239f0fd049SAlistair Popple opal_irqs = kcalloc(opal_irq_count, sizeof(*opal_irqs), GFP_KERNEL); 2242717a33dSBenjamin Herrenschmidt 2252717a33dSBenjamin Herrenschmidt if (WARN_ON(!irqs || !names || !opal_irqs)) 2262717a33dSBenjamin Herrenschmidt goto out_free; 2272717a33dSBenjamin Herrenschmidt 2282717a33dSBenjamin Herrenschmidt rc = of_property_read_u32_array(opal_node, "opal-interrupts", 2292717a33dSBenjamin Herrenschmidt irqs, opal_irq_count); 2302717a33dSBenjamin Herrenschmidt if (rc < 0) { 2312717a33dSBenjamin Herrenschmidt pr_err("Error %d reading opal-interrupts array\n", rc); 2322717a33dSBenjamin Herrenschmidt goto out_free; 2332717a33dSBenjamin Herrenschmidt } 2342717a33dSBenjamin Herrenschmidt 2352717a33dSBenjamin Herrenschmidt /* It's not an error for the names to be missing */ 2362717a33dSBenjamin Herrenschmidt of_property_read_string_array(opal_node, "opal-interrupts-names", 2372717a33dSBenjamin Herrenschmidt names, opal_irq_count); 2382717a33dSBenjamin Herrenschmidt 2392717a33dSBenjamin Herrenschmidt /* Install interrupt handlers */ 2402717a33dSBenjamin Herrenschmidt for (i = 0; i < opal_irq_count; i++) { 2412717a33dSBenjamin Herrenschmidt unsigned int virq; 2422717a33dSBenjamin Herrenschmidt char *name; 2439f0fd049SAlistair Popple 2449f0fd049SAlistair Popple /* Get hardware and virtual IRQ */ 2452717a33dSBenjamin Herrenschmidt virq = irq_create_mapping(NULL, irqs[i]); 246ef24ba70SMichael Ellerman if (!virq) { 2472717a33dSBenjamin Herrenschmidt pr_warn("Failed to map irq 0x%x\n", irqs[i]); 2489f0fd049SAlistair Popple continue; 2499f0fd049SAlistair Popple } 2509f0fd049SAlistair Popple 2512717a33dSBenjamin Herrenschmidt if (names[i] && strlen(names[i])) 2522717a33dSBenjamin Herrenschmidt name = kasprintf(GFP_KERNEL, "opal-%s", names[i]); 2532717a33dSBenjamin Herrenschmidt else 2542717a33dSBenjamin Herrenschmidt name = kasprintf(GFP_KERNEL, "opal"); 2552717a33dSBenjamin Herrenschmidt 2569f0fd049SAlistair Popple /* Install interrupt handler */ 257880a3d6aSBenjamin Herrenschmidt rc = request_irq(virq, opal_interrupt, IRQF_TRIGGER_LOW, 2582717a33dSBenjamin Herrenschmidt name, NULL); 2599f0fd049SAlistair Popple if (rc) { 2609f0fd049SAlistair Popple irq_dispose_mapping(virq); 2619f0fd049SAlistair Popple pr_warn("Error %d requesting irq %d (0x%x)\n", 2622717a33dSBenjamin Herrenschmidt rc, virq, irqs[i]); 2639f0fd049SAlistair Popple continue; 2649f0fd049SAlistair Popple } 2659f0fd049SAlistair Popple 2669f0fd049SAlistair Popple /* Cache IRQ */ 2679f0fd049SAlistair Popple opal_irqs[i] = virq; 2689f0fd049SAlistair Popple } 2699f0fd049SAlistair Popple 2702717a33dSBenjamin Herrenschmidt out_free: 2712717a33dSBenjamin Herrenschmidt kfree(irqs); 2722717a33dSBenjamin Herrenschmidt kfree(names); 2739f0fd049SAlistair Popple out: 2749f0fd049SAlistair Popple of_node_put(opal_node); 2759f0fd049SAlistair Popple return rc; 2769f0fd049SAlistair Popple } 27702b6505cSAlistair Popple machine_arch_initcall(powernv, opal_event_init); 2789f0fd049SAlistair Popple 2799f0fd049SAlistair Popple /** 2809f0fd049SAlistair Popple * opal_event_request(unsigned int opal_event_nr) - Request an event 2819f0fd049SAlistair Popple * @opal_event_nr: the opal event number to request 2829f0fd049SAlistair Popple * 2839f0fd049SAlistair Popple * This routine can be used to find the linux virq number which can 2849f0fd049SAlistair Popple * then be passed to request_irq to assign a handler for a particular 2859f0fd049SAlistair Popple * opal event. This should only be used by legacy devices which don't 2869f0fd049SAlistair Popple * have proper device tree bindings. Most devices should use 2879f0fd049SAlistair Popple * irq_of_parse_and_map() instead. 2889f0fd049SAlistair Popple */ 2899f0fd049SAlistair Popple int opal_event_request(unsigned int opal_event_nr) 2909f0fd049SAlistair Popple { 29102b6505cSAlistair Popple if (WARN_ON_ONCE(!opal_event_irqchip.domain)) 292ef24ba70SMichael Ellerman return 0; 29302b6505cSAlistair Popple 2949f0fd049SAlistair Popple return irq_create_mapping(opal_event_irqchip.domain, opal_event_nr); 2959f0fd049SAlistair Popple } 2969f0fd049SAlistair Popple EXPORT_SYMBOL(opal_event_request); 297