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); 469f0fd049SAlistair Popple static __be64 last_outstanding_events; 479f0fd049SAlistair Popple static struct irq_work opal_event_irq_work = { 489f0fd049SAlistair Popple .func = opal_handle_irq_work, 499f0fd049SAlistair Popple }; 509f0fd049SAlistair Popple 519f0fd049SAlistair Popple static void opal_event_mask(struct irq_data *d) 529f0fd049SAlistair Popple { 539f0fd049SAlistair Popple clear_bit(d->hwirq, &opal_event_irqchip.mask); 549f0fd049SAlistair Popple } 559f0fd049SAlistair Popple 569f0fd049SAlistair Popple static void opal_event_unmask(struct irq_data *d) 579f0fd049SAlistair Popple { 589f0fd049SAlistair Popple set_bit(d->hwirq, &opal_event_irqchip.mask); 599f0fd049SAlistair Popple 609f0fd049SAlistair Popple opal_poll_events(&last_outstanding_events); 619f0fd049SAlistair Popple if (last_outstanding_events & opal_event_irqchip.mask) 629f0fd049SAlistair Popple /* Need to retrigger the interrupt */ 639f0fd049SAlistair Popple irq_work_queue(&opal_event_irq_work); 649f0fd049SAlistair Popple } 659f0fd049SAlistair Popple 669f0fd049SAlistair Popple static int opal_event_set_type(struct irq_data *d, unsigned int flow_type) 679f0fd049SAlistair Popple { 689f0fd049SAlistair Popple /* 699f0fd049SAlistair Popple * For now we only support level triggered events. The irq 709f0fd049SAlistair Popple * handler will be called continuously until the event has 719f0fd049SAlistair Popple * been cleared in OPAL. 729f0fd049SAlistair Popple */ 739f0fd049SAlistair Popple if (flow_type != IRQ_TYPE_LEVEL_HIGH) 749f0fd049SAlistair Popple return -EINVAL; 759f0fd049SAlistair Popple 769f0fd049SAlistair Popple return 0; 779f0fd049SAlistair Popple } 789f0fd049SAlistair Popple 799f0fd049SAlistair Popple static struct opal_event_irqchip opal_event_irqchip = { 809f0fd049SAlistair Popple .irqchip = { 819f0fd049SAlistair Popple .name = "OPAL EVT", 829f0fd049SAlistair Popple .irq_mask = opal_event_mask, 839f0fd049SAlistair Popple .irq_unmask = opal_event_unmask, 849f0fd049SAlistair Popple .irq_set_type = opal_event_set_type, 859f0fd049SAlistair Popple }, 869f0fd049SAlistair Popple .mask = 0, 879f0fd049SAlistair Popple }; 889f0fd049SAlistair Popple 899f0fd049SAlistair Popple static int opal_event_map(struct irq_domain *d, unsigned int irq, 909f0fd049SAlistair Popple irq_hw_number_t hwirq) 919f0fd049SAlistair Popple { 929f0fd049SAlistair Popple irq_set_chip_data(irq, &opal_event_irqchip); 939f0fd049SAlistair Popple irq_set_chip_and_handler(irq, &opal_event_irqchip.irqchip, 949f0fd049SAlistair Popple handle_level_irq); 959f0fd049SAlistair Popple 969f0fd049SAlistair Popple return 0; 979f0fd049SAlistair Popple } 989f0fd049SAlistair Popple 999f0fd049SAlistair Popple void opal_handle_events(uint64_t events) 1009f0fd049SAlistair Popple { 1019f0fd049SAlistair Popple int virq, hwirq = 0; 1029f0fd049SAlistair Popple u64 mask = opal_event_irqchip.mask; 1039f0fd049SAlistair Popple 1049f0fd049SAlistair Popple if (!in_irq() && (events & mask)) { 1059f0fd049SAlistair Popple last_outstanding_events = events; 1069f0fd049SAlistair Popple irq_work_queue(&opal_event_irq_work); 1079f0fd049SAlistair Popple return; 1089f0fd049SAlistair Popple } 1099f0fd049SAlistair Popple 11081f2f7ceSAlistair Popple while (events & mask) { 1119f0fd049SAlistair Popple hwirq = fls64(events) - 1; 11281f2f7ceSAlistair Popple if (BIT_ULL(hwirq) & mask) { 1139f0fd049SAlistair Popple virq = irq_find_mapping(opal_event_irqchip.domain, 1149f0fd049SAlistair Popple hwirq); 11581f2f7ceSAlistair Popple if (virq) 1169f0fd049SAlistair Popple generic_handle_irq(virq); 11781f2f7ceSAlistair Popple } 1189f0fd049SAlistair Popple events &= ~BIT_ULL(hwirq); 1199f0fd049SAlistair Popple } 1209f0fd049SAlistair Popple } 1219f0fd049SAlistair Popple 1229f0fd049SAlistair Popple static irqreturn_t opal_interrupt(int irq, void *data) 1239f0fd049SAlistair Popple { 1249f0fd049SAlistair Popple __be64 events; 1259f0fd049SAlistair Popple 1269f0fd049SAlistair Popple opal_handle_interrupt(virq_to_hw(irq), &events); 1279f0fd049SAlistair Popple opal_handle_events(be64_to_cpu(events)); 1289f0fd049SAlistair Popple 1299f0fd049SAlistair Popple return IRQ_HANDLED; 1309f0fd049SAlistair Popple } 1319f0fd049SAlistair Popple 1329f0fd049SAlistair Popple static void opal_handle_irq_work(struct irq_work *work) 1339f0fd049SAlistair Popple { 1349f0fd049SAlistair Popple opal_handle_events(be64_to_cpu(last_outstanding_events)); 1359f0fd049SAlistair Popple } 1369f0fd049SAlistair Popple 137ad3aedfbSMarc Zyngier static int opal_event_match(struct irq_domain *h, struct device_node *node, 138ad3aedfbSMarc Zyngier enum irq_domain_bus_token bus_token) 1399f0fd049SAlistair Popple { 1409f0fd049SAlistair Popple return h->of_node == node; 1419f0fd049SAlistair Popple } 1429f0fd049SAlistair Popple 1439f0fd049SAlistair Popple static int opal_event_xlate(struct irq_domain *h, struct device_node *np, 1449f0fd049SAlistair Popple const u32 *intspec, unsigned int intsize, 1459f0fd049SAlistair Popple irq_hw_number_t *out_hwirq, unsigned int *out_flags) 1469f0fd049SAlistair Popple { 1479f0fd049SAlistair Popple *out_hwirq = intspec[0]; 1489f0fd049SAlistair Popple *out_flags = IRQ_TYPE_LEVEL_HIGH; 1499f0fd049SAlistair Popple 1509f0fd049SAlistair Popple return 0; 1519f0fd049SAlistair Popple } 1529f0fd049SAlistair Popple 1539f0fd049SAlistair Popple static const struct irq_domain_ops opal_event_domain_ops = { 1549f0fd049SAlistair Popple .match = opal_event_match, 1559f0fd049SAlistair Popple .map = opal_event_map, 1569f0fd049SAlistair Popple .xlate = opal_event_xlate, 1579f0fd049SAlistair Popple }; 1589f0fd049SAlistair Popple 1599f0fd049SAlistair Popple void opal_event_shutdown(void) 1609f0fd049SAlistair Popple { 1619f0fd049SAlistair Popple unsigned int i; 1629f0fd049SAlistair Popple 1639f0fd049SAlistair Popple /* First free interrupts, which will also mask them */ 1649f0fd049SAlistair Popple for (i = 0; i < opal_irq_count; i++) { 1659f0fd049SAlistair Popple if (opal_irqs[i]) 1669f0fd049SAlistair Popple free_irq(opal_irqs[i], NULL); 1679f0fd049SAlistair Popple opal_irqs[i] = 0; 1689f0fd049SAlistair Popple } 1699f0fd049SAlistair Popple } 1709f0fd049SAlistair Popple 1719f0fd049SAlistair Popple int __init opal_event_init(void) 1729f0fd049SAlistair Popple { 1739f0fd049SAlistair Popple struct device_node *dn, *opal_node; 1749f0fd049SAlistair Popple const __be32 *irqs; 1759f0fd049SAlistair Popple int i, irqlen, rc = 0; 1769f0fd049SAlistair Popple 1779f0fd049SAlistair Popple opal_node = of_find_node_by_path("/ibm,opal"); 1789f0fd049SAlistair Popple if (!opal_node) { 1799f0fd049SAlistair Popple pr_warn("opal: Node not found\n"); 1809f0fd049SAlistair Popple return -ENODEV; 1819f0fd049SAlistair Popple } 1829f0fd049SAlistair Popple 1839f0fd049SAlistair Popple /* If dn is NULL it means the domain won't be linked to a DT 1849f0fd049SAlistair Popple * node so therefore irq_of_parse_and_map(...) wont work. But 1859f0fd049SAlistair Popple * that shouldn't be problem because if we're running a 1869f0fd049SAlistair Popple * version of skiboot that doesn't have the dn then the 1879f0fd049SAlistair Popple * devices won't have the correct properties and will have to 1889f0fd049SAlistair Popple * fall back to the legacy method (opal_event_request(...)) 1899f0fd049SAlistair Popple * anyway. */ 1909f0fd049SAlistair Popple dn = of_find_compatible_node(NULL, NULL, "ibm,opal-event"); 1919f0fd049SAlistair Popple opal_event_irqchip.domain = irq_domain_add_linear(dn, MAX_NUM_EVENTS, 1929f0fd049SAlistair Popple &opal_event_domain_ops, &opal_event_irqchip); 1939f0fd049SAlistair Popple of_node_put(dn); 1949f0fd049SAlistair Popple if (!opal_event_irqchip.domain) { 1959f0fd049SAlistair Popple pr_warn("opal: Unable to create irq domain\n"); 1969f0fd049SAlistair Popple rc = -ENOMEM; 1979f0fd049SAlistair Popple goto out; 1989f0fd049SAlistair Popple } 1999f0fd049SAlistair Popple 2009f0fd049SAlistair Popple /* Get interrupt property */ 2019f0fd049SAlistair Popple irqs = of_get_property(opal_node, "opal-interrupts", &irqlen); 2029f0fd049SAlistair Popple opal_irq_count = irqs ? (irqlen / 4) : 0; 2039f0fd049SAlistair Popple pr_debug("Found %d interrupts reserved for OPAL\n", opal_irq_count); 2049f0fd049SAlistair Popple 2059f0fd049SAlistair Popple /* Install interrupt handlers */ 2069f0fd049SAlistair Popple opal_irqs = kcalloc(opal_irq_count, sizeof(*opal_irqs), GFP_KERNEL); 2079f0fd049SAlistair Popple for (i = 0; irqs && i < opal_irq_count; i++, irqs++) { 2089f0fd049SAlistair Popple unsigned int irq, virq; 2099f0fd049SAlistair Popple 2109f0fd049SAlistair Popple /* Get hardware and virtual IRQ */ 2119f0fd049SAlistair Popple irq = be32_to_cpup(irqs); 2129f0fd049SAlistair Popple virq = irq_create_mapping(NULL, irq); 2139f0fd049SAlistair Popple if (virq == NO_IRQ) { 2149f0fd049SAlistair Popple pr_warn("Failed to map irq 0x%x\n", irq); 2159f0fd049SAlistair Popple continue; 2169f0fd049SAlistair Popple } 2179f0fd049SAlistair Popple 2189f0fd049SAlistair Popple /* Install interrupt handler */ 2199f0fd049SAlistair Popple rc = request_irq(virq, opal_interrupt, 0, "opal", NULL); 2209f0fd049SAlistair Popple if (rc) { 2219f0fd049SAlistair Popple irq_dispose_mapping(virq); 2229f0fd049SAlistair Popple pr_warn("Error %d requesting irq %d (0x%x)\n", 2239f0fd049SAlistair Popple rc, virq, irq); 2249f0fd049SAlistair Popple continue; 2259f0fd049SAlistair Popple } 2269f0fd049SAlistair Popple 2279f0fd049SAlistair Popple /* Cache IRQ */ 2289f0fd049SAlistair Popple opal_irqs[i] = virq; 2299f0fd049SAlistair Popple } 2309f0fd049SAlistair Popple 2319f0fd049SAlistair Popple out: 2329f0fd049SAlistair Popple of_node_put(opal_node); 2339f0fd049SAlistair Popple return rc; 2349f0fd049SAlistair Popple } 23502b6505cSAlistair Popple machine_arch_initcall(powernv, opal_event_init); 2369f0fd049SAlistair Popple 2379f0fd049SAlistair Popple /** 2389f0fd049SAlistair Popple * opal_event_request(unsigned int opal_event_nr) - Request an event 2399f0fd049SAlistair Popple * @opal_event_nr: the opal event number to request 2409f0fd049SAlistair Popple * 2419f0fd049SAlistair Popple * This routine can be used to find the linux virq number which can 2429f0fd049SAlistair Popple * then be passed to request_irq to assign a handler for a particular 2439f0fd049SAlistair Popple * opal event. This should only be used by legacy devices which don't 2449f0fd049SAlistair Popple * have proper device tree bindings. Most devices should use 2459f0fd049SAlistair Popple * irq_of_parse_and_map() instead. 2469f0fd049SAlistair Popple */ 2479f0fd049SAlistair Popple int opal_event_request(unsigned int opal_event_nr) 2489f0fd049SAlistair Popple { 24902b6505cSAlistair Popple if (WARN_ON_ONCE(!opal_event_irqchip.domain)) 25002b6505cSAlistair Popple return NO_IRQ; 25102b6505cSAlistair Popple 2529f0fd049SAlistair Popple return irq_create_mapping(opal_event_irqchip.domain, opal_event_nr); 2539f0fd049SAlistair Popple } 2549f0fd049SAlistair Popple EXPORT_SYMBOL(opal_event_request); 255