10547dc78SZubair Lutfullah Kakakhel /* 20547dc78SZubair Lutfullah Kakakhel * Copyright (C) 2007-2013 Michal Simek <monstr@monstr.eu> 30547dc78SZubair Lutfullah Kakakhel * Copyright (C) 2012-2013 Xilinx, Inc. 40547dc78SZubair Lutfullah Kakakhel * Copyright (C) 2007-2009 PetaLogix 50547dc78SZubair Lutfullah Kakakhel * Copyright (C) 2006 Atmark Techno, Inc. 60547dc78SZubair Lutfullah Kakakhel * 70547dc78SZubair Lutfullah Kakakhel * This file is subject to the terms and conditions of the GNU General Public 80547dc78SZubair Lutfullah Kakakhel * License. See the file "COPYING" in the main directory of this archive 90547dc78SZubair Lutfullah Kakakhel * for more details. 100547dc78SZubair Lutfullah Kakakhel */ 110547dc78SZubair Lutfullah Kakakhel 120547dc78SZubair Lutfullah Kakakhel #include <linux/irqdomain.h> 130547dc78SZubair Lutfullah Kakakhel #include <linux/irq.h> 140547dc78SZubair Lutfullah Kakakhel #include <linux/irqchip.h> 159689c99eSZubair Lutfullah Kakakhel #include <linux/irqchip/chained_irq.h> 160547dc78SZubair Lutfullah Kakakhel #include <linux/of_address.h> 170547dc78SZubair Lutfullah Kakakhel #include <linux/io.h> 18591db74bSZubair Lutfullah Kakakhel #include <linux/jump_label.h> 190547dc78SZubair Lutfullah Kakakhel #include <linux/bug.h> 209689c99eSZubair Lutfullah Kakakhel #include <linux/of_irq.h> 210547dc78SZubair Lutfullah Kakakhel 220547dc78SZubair Lutfullah Kakakhel /* No one else should require these constants, so define them locally here. */ 230547dc78SZubair Lutfullah Kakakhel #define ISR 0x00 /* Interrupt Status Register */ 240547dc78SZubair Lutfullah Kakakhel #define IPR 0x04 /* Interrupt Pending Register */ 250547dc78SZubair Lutfullah Kakakhel #define IER 0x08 /* Interrupt Enable Register */ 260547dc78SZubair Lutfullah Kakakhel #define IAR 0x0c /* Interrupt Acknowledge Register */ 270547dc78SZubair Lutfullah Kakakhel #define SIE 0x10 /* Set Interrupt Enable bits */ 280547dc78SZubair Lutfullah Kakakhel #define CIE 0x14 /* Clear Interrupt Enable bits */ 290547dc78SZubair Lutfullah Kakakhel #define IVR 0x18 /* Interrupt Vector Register */ 300547dc78SZubair Lutfullah Kakakhel #define MER 0x1c /* Master Enable Register */ 310547dc78SZubair Lutfullah Kakakhel 320547dc78SZubair Lutfullah Kakakhel #define MER_ME (1<<0) 330547dc78SZubair Lutfullah Kakakhel #define MER_HIE (1<<1) 340547dc78SZubair Lutfullah Kakakhel 35591db74bSZubair Lutfullah Kakakhel static DEFINE_STATIC_KEY_FALSE(xintc_is_be); 360547dc78SZubair Lutfullah Kakakhel 37591db74bSZubair Lutfullah Kakakhel struct xintc_irq_chip { 38591db74bSZubair Lutfullah Kakakhel void __iomem *base; 39591db74bSZubair Lutfullah Kakakhel struct irq_domain *root_domain; 40591db74bSZubair Lutfullah Kakakhel u32 intr_mask; 4167862a3cSMubin Sayyed u32 nr_irq; 42591db74bSZubair Lutfullah Kakakhel }; 43591db74bSZubair Lutfullah Kakakhel 4467862a3cSMubin Sayyed static struct xintc_irq_chip *primary_intc; 45591db74bSZubair Lutfullah Kakakhel 4667862a3cSMubin Sayyed static void xintc_write(struct xintc_irq_chip *irqc, int reg, u32 data) 470547dc78SZubair Lutfullah Kakakhel { 48591db74bSZubair Lutfullah Kakakhel if (static_branch_unlikely(&xintc_is_be)) 4967862a3cSMubin Sayyed iowrite32be(data, irqc->base + reg); 50591db74bSZubair Lutfullah Kakakhel else 5167862a3cSMubin Sayyed iowrite32(data, irqc->base + reg); 520547dc78SZubair Lutfullah Kakakhel } 530547dc78SZubair Lutfullah Kakakhel 5467862a3cSMubin Sayyed static u32 xintc_read(struct xintc_irq_chip *irqc, int reg) 550547dc78SZubair Lutfullah Kakakhel { 56591db74bSZubair Lutfullah Kakakhel if (static_branch_unlikely(&xintc_is_be)) 5767862a3cSMubin Sayyed return ioread32be(irqc->base + reg); 58591db74bSZubair Lutfullah Kakakhel else 5967862a3cSMubin Sayyed return ioread32(irqc->base + reg); 600547dc78SZubair Lutfullah Kakakhel } 610547dc78SZubair Lutfullah Kakakhel 620547dc78SZubair Lutfullah Kakakhel static void intc_enable_or_unmask(struct irq_data *d) 630547dc78SZubair Lutfullah Kakakhel { 6467862a3cSMubin Sayyed struct xintc_irq_chip *irqc = irq_data_get_irq_chip_data(d); 6567862a3cSMubin Sayyed unsigned long mask = BIT(d->hwirq); 660547dc78SZubair Lutfullah Kakakhel 67a5734de2SZubair Lutfullah Kakakhel pr_debug("irq-xilinx: enable_or_unmask: %ld\n", d->hwirq); 680547dc78SZubair Lutfullah Kakakhel 690547dc78SZubair Lutfullah Kakakhel /* ack level irqs because they can't be acked during 700547dc78SZubair Lutfullah Kakakhel * ack function since the handle_level_irq function 710547dc78SZubair Lutfullah Kakakhel * acks the irq before calling the interrupt handler 720547dc78SZubair Lutfullah Kakakhel */ 730547dc78SZubair Lutfullah Kakakhel if (irqd_is_level_type(d)) 7467862a3cSMubin Sayyed xintc_write(irqc, IAR, mask); 750547dc78SZubair Lutfullah Kakakhel 7667862a3cSMubin Sayyed xintc_write(irqc, SIE, mask); 770547dc78SZubair Lutfullah Kakakhel } 780547dc78SZubair Lutfullah Kakakhel 790547dc78SZubair Lutfullah Kakakhel static void intc_disable_or_mask(struct irq_data *d) 800547dc78SZubair Lutfullah Kakakhel { 8167862a3cSMubin Sayyed struct xintc_irq_chip *irqc = irq_data_get_irq_chip_data(d); 8267862a3cSMubin Sayyed 83a5734de2SZubair Lutfullah Kakakhel pr_debug("irq-xilinx: disable: %ld\n", d->hwirq); 8467862a3cSMubin Sayyed xintc_write(irqc, CIE, BIT(d->hwirq)); 850547dc78SZubair Lutfullah Kakakhel } 860547dc78SZubair Lutfullah Kakakhel 870547dc78SZubair Lutfullah Kakakhel static void intc_ack(struct irq_data *d) 880547dc78SZubair Lutfullah Kakakhel { 8967862a3cSMubin Sayyed struct xintc_irq_chip *irqc = irq_data_get_irq_chip_data(d); 9067862a3cSMubin Sayyed 91a5734de2SZubair Lutfullah Kakakhel pr_debug("irq-xilinx: ack: %ld\n", d->hwirq); 9267862a3cSMubin Sayyed xintc_write(irqc, IAR, BIT(d->hwirq)); 930547dc78SZubair Lutfullah Kakakhel } 940547dc78SZubair Lutfullah Kakakhel 950547dc78SZubair Lutfullah Kakakhel static void intc_mask_ack(struct irq_data *d) 960547dc78SZubair Lutfullah Kakakhel { 9767862a3cSMubin Sayyed struct xintc_irq_chip *irqc = irq_data_get_irq_chip_data(d); 9867862a3cSMubin Sayyed unsigned long mask = BIT(d->hwirq); 990547dc78SZubair Lutfullah Kakakhel 100a5734de2SZubair Lutfullah Kakakhel pr_debug("irq-xilinx: disable_and_ack: %ld\n", d->hwirq); 10167862a3cSMubin Sayyed xintc_write(irqc, CIE, mask); 10267862a3cSMubin Sayyed xintc_write(irqc, IAR, mask); 1030547dc78SZubair Lutfullah Kakakhel } 1040547dc78SZubair Lutfullah Kakakhel 1050547dc78SZubair Lutfullah Kakakhel static struct irq_chip intc_dev = { 1060547dc78SZubair Lutfullah Kakakhel .name = "Xilinx INTC", 1070547dc78SZubair Lutfullah Kakakhel .irq_unmask = intc_enable_or_unmask, 1080547dc78SZubair Lutfullah Kakakhel .irq_mask = intc_disable_or_mask, 1090547dc78SZubair Lutfullah Kakakhel .irq_ack = intc_ack, 1100547dc78SZubair Lutfullah Kakakhel .irq_mask_ack = intc_mask_ack, 1110547dc78SZubair Lutfullah Kakakhel }; 1120547dc78SZubair Lutfullah Kakakhel 11367862a3cSMubin Sayyed static unsigned int xintc_get_irq_local(struct xintc_irq_chip *irqc) 11467862a3cSMubin Sayyed { 11567862a3cSMubin Sayyed unsigned int irq = 0; 11667862a3cSMubin Sayyed u32 hwirq; 11767862a3cSMubin Sayyed 11867862a3cSMubin Sayyed hwirq = xintc_read(irqc, IVR); 11967862a3cSMubin Sayyed if (hwirq != -1U) 12067862a3cSMubin Sayyed irq = irq_find_mapping(irqc->root_domain, hwirq); 12167862a3cSMubin Sayyed 12267862a3cSMubin Sayyed pr_debug("irq-xilinx: hwirq=%d, irq=%d\n", hwirq, irq); 12367862a3cSMubin Sayyed 12467862a3cSMubin Sayyed return irq; 12567862a3cSMubin Sayyed } 12667862a3cSMubin Sayyed 1274cea749dSMarc Zyngier unsigned int xintc_get_irq(void) 1284cea749dSMarc Zyngier { 1294cea749dSMarc Zyngier unsigned int irq = -1; 1304cea749dSMarc Zyngier u32 hwirq; 1314cea749dSMarc Zyngier 1324cea749dSMarc Zyngier hwirq = xintc_read(primary_intc, IVR); 1334cea749dSMarc Zyngier if (hwirq != -1U) 1344cea749dSMarc Zyngier irq = irq_find_mapping(primary_intc->root_domain, hwirq); 1354cea749dSMarc Zyngier 1364cea749dSMarc Zyngier pr_debug("irq-xilinx: hwirq=%d, irq=%d\n", hwirq, irq); 1374cea749dSMarc Zyngier 1384cea749dSMarc Zyngier return irq; 1394cea749dSMarc Zyngier } 1404cea749dSMarc Zyngier 1410547dc78SZubair Lutfullah Kakakhel static int xintc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) 1420547dc78SZubair Lutfullah Kakakhel { 14367862a3cSMubin Sayyed struct xintc_irq_chip *irqc = d->host_data; 14467862a3cSMubin Sayyed 14567862a3cSMubin Sayyed if (irqc->intr_mask & BIT(hw)) { 1460547dc78SZubair Lutfullah Kakakhel irq_set_chip_and_handler_name(irq, &intc_dev, 1470547dc78SZubair Lutfullah Kakakhel handle_edge_irq, "edge"); 1480547dc78SZubair Lutfullah Kakakhel irq_clear_status_flags(irq, IRQ_LEVEL); 1490547dc78SZubair Lutfullah Kakakhel } else { 1500547dc78SZubair Lutfullah Kakakhel irq_set_chip_and_handler_name(irq, &intc_dev, 1510547dc78SZubair Lutfullah Kakakhel handle_level_irq, "level"); 1520547dc78SZubair Lutfullah Kakakhel irq_set_status_flags(irq, IRQ_LEVEL); 1530547dc78SZubair Lutfullah Kakakhel } 15467862a3cSMubin Sayyed irq_set_chip_data(irq, irqc); 1550547dc78SZubair Lutfullah Kakakhel return 0; 1560547dc78SZubair Lutfullah Kakakhel } 1570547dc78SZubair Lutfullah Kakakhel 1580547dc78SZubair Lutfullah Kakakhel static const struct irq_domain_ops xintc_irq_domain_ops = { 1590547dc78SZubair Lutfullah Kakakhel .xlate = irq_domain_xlate_onetwocell, 1600547dc78SZubair Lutfullah Kakakhel .map = xintc_map, 1610547dc78SZubair Lutfullah Kakakhel }; 1620547dc78SZubair Lutfullah Kakakhel 1639689c99eSZubair Lutfullah Kakakhel static void xil_intc_irq_handler(struct irq_desc *desc) 1649689c99eSZubair Lutfullah Kakakhel { 1659689c99eSZubair Lutfullah Kakakhel struct irq_chip *chip = irq_desc_get_chip(desc); 16667862a3cSMubin Sayyed struct xintc_irq_chip *irqc; 1679689c99eSZubair Lutfullah Kakakhel u32 pending; 1689689c99eSZubair Lutfullah Kakakhel 16967862a3cSMubin Sayyed irqc = irq_data_get_irq_handler_data(&desc->irq_data); 1709689c99eSZubair Lutfullah Kakakhel chained_irq_enter(chip, desc); 1719689c99eSZubair Lutfullah Kakakhel do { 17267862a3cSMubin Sayyed pending = xintc_get_irq_local(irqc); 17367862a3cSMubin Sayyed if (pending == 0) 1749689c99eSZubair Lutfullah Kakakhel break; 1759689c99eSZubair Lutfullah Kakakhel generic_handle_irq(pending); 1769689c99eSZubair Lutfullah Kakakhel } while (true); 1779689c99eSZubair Lutfullah Kakakhel chained_irq_exit(chip, desc); 1789689c99eSZubair Lutfullah Kakakhel } 1799689c99eSZubair Lutfullah Kakakhel 1800547dc78SZubair Lutfullah Kakakhel static int __init xilinx_intc_of_init(struct device_node *intc, 1810547dc78SZubair Lutfullah Kakakhel struct device_node *parent) 1820547dc78SZubair Lutfullah Kakakhel { 183591db74bSZubair Lutfullah Kakakhel struct xintc_irq_chip *irqc; 18467862a3cSMubin Sayyed int ret, irq; 185591db74bSZubair Lutfullah Kakakhel 186591db74bSZubair Lutfullah Kakakhel irqc = kzalloc(sizeof(*irqc), GFP_KERNEL); 187591db74bSZubair Lutfullah Kakakhel if (!irqc) 188591db74bSZubair Lutfullah Kakakhel return -ENOMEM; 189591db74bSZubair Lutfullah Kakakhel irqc->base = of_iomap(intc, 0); 190591db74bSZubair Lutfullah Kakakhel BUG_ON(!irqc->base); 1910547dc78SZubair Lutfullah Kakakhel 19267862a3cSMubin Sayyed ret = of_property_read_u32(intc, "xlnx,num-intr-inputs", &irqc->nr_irq); 1930547dc78SZubair Lutfullah Kakakhel if (ret < 0) { 194a5734de2SZubair Lutfullah Kakakhel pr_err("irq-xilinx: unable to read xlnx,num-intr-inputs\n"); 19567862a3cSMubin Sayyed goto error; 1960547dc78SZubair Lutfullah Kakakhel } 1970547dc78SZubair Lutfullah Kakakhel 198591db74bSZubair Lutfullah Kakakhel ret = of_property_read_u32(intc, "xlnx,kind-of-intr", &irqc->intr_mask); 1990547dc78SZubair Lutfullah Kakakhel if (ret < 0) { 2008a11da59SZubair Lutfullah Kakakhel pr_warn("irq-xilinx: unable to read xlnx,kind-of-intr\n"); 2018a11da59SZubair Lutfullah Kakakhel irqc->intr_mask = 0; 2020547dc78SZubair Lutfullah Kakakhel } 2030547dc78SZubair Lutfullah Kakakhel 20467862a3cSMubin Sayyed if (irqc->intr_mask >> irqc->nr_irq) 205a5734de2SZubair Lutfullah Kakakhel pr_warn("irq-xilinx: mismatch in kind-of-intr param\n"); 2060547dc78SZubair Lutfullah Kakakhel 207e81f54c6SRob Herring pr_info("irq-xilinx: %pOF: num_irq=%d, edge=0x%x\n", 20867862a3cSMubin Sayyed intc, irqc->nr_irq, irqc->intr_mask); 2090547dc78SZubair Lutfullah Kakakhel 2100547dc78SZubair Lutfullah Kakakhel 2110547dc78SZubair Lutfullah Kakakhel /* 2120547dc78SZubair Lutfullah Kakakhel * Disable all external interrupts until they are 213*a359f757SIngo Molnar * explicitly requested. 2140547dc78SZubair Lutfullah Kakakhel */ 21567862a3cSMubin Sayyed xintc_write(irqc, IER, 0); 2160547dc78SZubair Lutfullah Kakakhel 2170547dc78SZubair Lutfullah Kakakhel /* Acknowledge any pending interrupts just in case. */ 21867862a3cSMubin Sayyed xintc_write(irqc, IAR, 0xffffffff); 2190547dc78SZubair Lutfullah Kakakhel 2200547dc78SZubair Lutfullah Kakakhel /* Turn on the Master Enable. */ 22167862a3cSMubin Sayyed xintc_write(irqc, MER, MER_HIE | MER_ME); 22267862a3cSMubin Sayyed if (xintc_read(irqc, MER) != (MER_HIE | MER_ME)) { 223591db74bSZubair Lutfullah Kakakhel static_branch_enable(&xintc_is_be); 22467862a3cSMubin Sayyed xintc_write(irqc, MER, MER_HIE | MER_ME); 2250547dc78SZubair Lutfullah Kakakhel } 2260547dc78SZubair Lutfullah Kakakhel 22767862a3cSMubin Sayyed irqc->root_domain = irq_domain_add_linear(intc, irqc->nr_irq, 228591db74bSZubair Lutfullah Kakakhel &xintc_irq_domain_ops, irqc); 229591db74bSZubair Lutfullah Kakakhel if (!irqc->root_domain) { 230591db74bSZubair Lutfullah Kakakhel pr_err("irq-xilinx: Unable to create IRQ domain\n"); 231c74038baSMichal Simek ret = -EINVAL; 23267862a3cSMubin Sayyed goto error; 233591db74bSZubair Lutfullah Kakakhel } 2340547dc78SZubair Lutfullah Kakakhel 2359689c99eSZubair Lutfullah Kakakhel if (parent) { 2369689c99eSZubair Lutfullah Kakakhel irq = irq_of_parse_and_map(intc, 0); 2379689c99eSZubair Lutfullah Kakakhel if (irq) { 2389689c99eSZubair Lutfullah Kakakhel irq_set_chained_handler_and_data(irq, 2399689c99eSZubair Lutfullah Kakakhel xil_intc_irq_handler, 2409689c99eSZubair Lutfullah Kakakhel irqc); 2419689c99eSZubair Lutfullah Kakakhel } else { 2429689c99eSZubair Lutfullah Kakakhel pr_err("irq-xilinx: interrupts property not in DT\n"); 2439689c99eSZubair Lutfullah Kakakhel ret = -EINVAL; 24467862a3cSMubin Sayyed goto error; 2459689c99eSZubair Lutfullah Kakakhel } 2469689c99eSZubair Lutfullah Kakakhel } else { 24767862a3cSMubin Sayyed primary_intc = irqc; 248e02f6c01SMarc Zyngier irq_set_default_host(primary_intc->root_domain); 2499689c99eSZubair Lutfullah Kakakhel } 2500547dc78SZubair Lutfullah Kakakhel 2510547dc78SZubair Lutfullah Kakakhel return 0; 252591db74bSZubair Lutfullah Kakakhel 25367862a3cSMubin Sayyed error: 25467862a3cSMubin Sayyed iounmap(irqc->base); 255591db74bSZubair Lutfullah Kakakhel kfree(irqc); 256591db74bSZubair Lutfullah Kakakhel return ret; 257591db74bSZubair Lutfullah Kakakhel 2580547dc78SZubair Lutfullah Kakakhel } 2590547dc78SZubair Lutfullah Kakakhel 2608328255fSZubair Lutfullah Kakakhel IRQCHIP_DECLARE(xilinx_intc_xps, "xlnx,xps-intc-1.00.a", xilinx_intc_of_init); 2618328255fSZubair Lutfullah Kakakhel IRQCHIP_DECLARE(xilinx_intc_opb, "xlnx,opb-intc-1.00.c", xilinx_intc_of_init); 262