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 1270547dc78SZubair Lutfullah Kakakhel static int xintc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) 1280547dc78SZubair Lutfullah Kakakhel { 12967862a3cSMubin Sayyed struct xintc_irq_chip *irqc = d->host_data; 13067862a3cSMubin Sayyed 13167862a3cSMubin Sayyed if (irqc->intr_mask & BIT(hw)) { 1320547dc78SZubair Lutfullah Kakakhel irq_set_chip_and_handler_name(irq, &intc_dev, 1330547dc78SZubair Lutfullah Kakakhel handle_edge_irq, "edge"); 1340547dc78SZubair Lutfullah Kakakhel irq_clear_status_flags(irq, IRQ_LEVEL); 1350547dc78SZubair Lutfullah Kakakhel } else { 1360547dc78SZubair Lutfullah Kakakhel irq_set_chip_and_handler_name(irq, &intc_dev, 1370547dc78SZubair Lutfullah Kakakhel handle_level_irq, "level"); 1380547dc78SZubair Lutfullah Kakakhel irq_set_status_flags(irq, IRQ_LEVEL); 1390547dc78SZubair Lutfullah Kakakhel } 14067862a3cSMubin Sayyed irq_set_chip_data(irq, irqc); 1410547dc78SZubair Lutfullah Kakakhel return 0; 1420547dc78SZubair Lutfullah Kakakhel } 1430547dc78SZubair Lutfullah Kakakhel 1440547dc78SZubair Lutfullah Kakakhel static const struct irq_domain_ops xintc_irq_domain_ops = { 1450547dc78SZubair Lutfullah Kakakhel .xlate = irq_domain_xlate_onetwocell, 1460547dc78SZubair Lutfullah Kakakhel .map = xintc_map, 1470547dc78SZubair Lutfullah Kakakhel }; 1480547dc78SZubair Lutfullah Kakakhel 1499689c99eSZubair Lutfullah Kakakhel static void xil_intc_irq_handler(struct irq_desc *desc) 1509689c99eSZubair Lutfullah Kakakhel { 1519689c99eSZubair Lutfullah Kakakhel struct irq_chip *chip = irq_desc_get_chip(desc); 15267862a3cSMubin Sayyed struct xintc_irq_chip *irqc; 1539689c99eSZubair Lutfullah Kakakhel u32 pending; 1549689c99eSZubair Lutfullah Kakakhel 15567862a3cSMubin Sayyed irqc = irq_data_get_irq_handler_data(&desc->irq_data); 1569689c99eSZubair Lutfullah Kakakhel chained_irq_enter(chip, desc); 1579689c99eSZubair Lutfullah Kakakhel do { 15867862a3cSMubin Sayyed pending = xintc_get_irq_local(irqc); 15967862a3cSMubin Sayyed if (pending == 0) 1609689c99eSZubair Lutfullah Kakakhel break; 1619689c99eSZubair Lutfullah Kakakhel generic_handle_irq(pending); 1629689c99eSZubair Lutfullah Kakakhel } while (true); 1639689c99eSZubair Lutfullah Kakakhel chained_irq_exit(chip, desc); 1649689c99eSZubair Lutfullah Kakakhel } 1659689c99eSZubair Lutfullah Kakakhel 166a0789993SMichal Simek static void xil_intc_handle_irq(struct pt_regs *regs) 167a0789993SMichal Simek { 168a0789993SMichal Simek u32 hwirq; 169a0789993SMichal Simek struct xintc_irq_chip *irqc = primary_intc; 170a0789993SMichal Simek 171a0789993SMichal Simek do { 172a0789993SMichal Simek hwirq = xintc_read(irqc, IVR); 173a0789993SMichal Simek if (likely(hwirq != -1U)) { 174a0789993SMichal Simek int ret; 175a0789993SMichal Simek 176a0789993SMichal Simek ret = handle_domain_irq(irqc->root_domain, hwirq, regs); 177a0789993SMichal Simek WARN_ONCE(ret, "Unhandled HWIRQ %d\n", hwirq); 178a0789993SMichal Simek continue; 179a0789993SMichal Simek } 180a0789993SMichal Simek 181a0789993SMichal Simek break; 182a0789993SMichal Simek } while (1); 183a0789993SMichal Simek } 184a0789993SMichal Simek 1850547dc78SZubair Lutfullah Kakakhel static int __init xilinx_intc_of_init(struct device_node *intc, 1860547dc78SZubair Lutfullah Kakakhel struct device_node *parent) 1870547dc78SZubair Lutfullah Kakakhel { 188591db74bSZubair Lutfullah Kakakhel struct xintc_irq_chip *irqc; 18967862a3cSMubin Sayyed int ret, irq; 190591db74bSZubair Lutfullah Kakakhel 191591db74bSZubair Lutfullah Kakakhel irqc = kzalloc(sizeof(*irqc), GFP_KERNEL); 192591db74bSZubair Lutfullah Kakakhel if (!irqc) 193591db74bSZubair Lutfullah Kakakhel return -ENOMEM; 194591db74bSZubair Lutfullah Kakakhel irqc->base = of_iomap(intc, 0); 195591db74bSZubair Lutfullah Kakakhel BUG_ON(!irqc->base); 1960547dc78SZubair Lutfullah Kakakhel 19767862a3cSMubin Sayyed ret = of_property_read_u32(intc, "xlnx,num-intr-inputs", &irqc->nr_irq); 1980547dc78SZubair Lutfullah Kakakhel if (ret < 0) { 199a5734de2SZubair Lutfullah Kakakhel pr_err("irq-xilinx: unable to read xlnx,num-intr-inputs\n"); 20067862a3cSMubin Sayyed goto error; 2010547dc78SZubair Lutfullah Kakakhel } 2020547dc78SZubair Lutfullah Kakakhel 203591db74bSZubair Lutfullah Kakakhel ret = of_property_read_u32(intc, "xlnx,kind-of-intr", &irqc->intr_mask); 2040547dc78SZubair Lutfullah Kakakhel if (ret < 0) { 2058a11da59SZubair Lutfullah Kakakhel pr_warn("irq-xilinx: unable to read xlnx,kind-of-intr\n"); 2068a11da59SZubair Lutfullah Kakakhel irqc->intr_mask = 0; 2070547dc78SZubair Lutfullah Kakakhel } 2080547dc78SZubair Lutfullah Kakakhel 20967862a3cSMubin Sayyed if (irqc->intr_mask >> irqc->nr_irq) 210a5734de2SZubair Lutfullah Kakakhel pr_warn("irq-xilinx: mismatch in kind-of-intr param\n"); 2110547dc78SZubair Lutfullah Kakakhel 212e81f54c6SRob Herring pr_info("irq-xilinx: %pOF: num_irq=%d, edge=0x%x\n", 21367862a3cSMubin Sayyed intc, irqc->nr_irq, irqc->intr_mask); 2140547dc78SZubair Lutfullah Kakakhel 2150547dc78SZubair Lutfullah Kakakhel 2160547dc78SZubair Lutfullah Kakakhel /* 2170547dc78SZubair Lutfullah Kakakhel * Disable all external interrupts until they are 2180547dc78SZubair Lutfullah Kakakhel * explicity requested. 2190547dc78SZubair Lutfullah Kakakhel */ 22067862a3cSMubin Sayyed xintc_write(irqc, IER, 0); 2210547dc78SZubair Lutfullah Kakakhel 2220547dc78SZubair Lutfullah Kakakhel /* Acknowledge any pending interrupts just in case. */ 22367862a3cSMubin Sayyed xintc_write(irqc, IAR, 0xffffffff); 2240547dc78SZubair Lutfullah Kakakhel 2250547dc78SZubair Lutfullah Kakakhel /* Turn on the Master Enable. */ 22667862a3cSMubin Sayyed xintc_write(irqc, MER, MER_HIE | MER_ME); 22767862a3cSMubin Sayyed if (xintc_read(irqc, MER) != (MER_HIE | MER_ME)) { 228591db74bSZubair Lutfullah Kakakhel static_branch_enable(&xintc_is_be); 22967862a3cSMubin Sayyed xintc_write(irqc, MER, MER_HIE | MER_ME); 2300547dc78SZubair Lutfullah Kakakhel } 2310547dc78SZubair Lutfullah Kakakhel 23267862a3cSMubin Sayyed irqc->root_domain = irq_domain_add_linear(intc, irqc->nr_irq, 233591db74bSZubair Lutfullah Kakakhel &xintc_irq_domain_ops, irqc); 234591db74bSZubair Lutfullah Kakakhel if (!irqc->root_domain) { 235591db74bSZubair Lutfullah Kakakhel pr_err("irq-xilinx: Unable to create IRQ domain\n"); 236c74038baSMichal Simek ret = -EINVAL; 23767862a3cSMubin Sayyed goto error; 238591db74bSZubair Lutfullah Kakakhel } 2390547dc78SZubair Lutfullah Kakakhel 2409689c99eSZubair Lutfullah Kakakhel if (parent) { 2419689c99eSZubair Lutfullah Kakakhel irq = irq_of_parse_and_map(intc, 0); 2429689c99eSZubair Lutfullah Kakakhel if (irq) { 2439689c99eSZubair Lutfullah Kakakhel irq_set_chained_handler_and_data(irq, 2449689c99eSZubair Lutfullah Kakakhel xil_intc_irq_handler, 2459689c99eSZubair Lutfullah Kakakhel irqc); 2469689c99eSZubair Lutfullah Kakakhel } else { 2479689c99eSZubair Lutfullah Kakakhel pr_err("irq-xilinx: interrupts property not in DT\n"); 2489689c99eSZubair Lutfullah Kakakhel ret = -EINVAL; 24967862a3cSMubin Sayyed goto error; 2509689c99eSZubair Lutfullah Kakakhel } 2519689c99eSZubair Lutfullah Kakakhel } else { 25267862a3cSMubin Sayyed primary_intc = irqc; 253*e02f6c01SMarc Zyngier irq_set_default_host(primary_intc->root_domain); 254a0789993SMichal Simek set_handle_irq(xil_intc_handle_irq); 2559689c99eSZubair Lutfullah Kakakhel } 2560547dc78SZubair Lutfullah Kakakhel 2570547dc78SZubair Lutfullah Kakakhel return 0; 258591db74bSZubair Lutfullah Kakakhel 25967862a3cSMubin Sayyed error: 26067862a3cSMubin Sayyed iounmap(irqc->base); 261591db74bSZubair Lutfullah Kakakhel kfree(irqc); 262591db74bSZubair Lutfullah Kakakhel return ret; 263591db74bSZubair Lutfullah Kakakhel 2640547dc78SZubair Lutfullah Kakakhel } 2650547dc78SZubair Lutfullah Kakakhel 2668328255fSZubair Lutfullah Kakakhel IRQCHIP_DECLARE(xilinx_intc_xps, "xlnx,xps-intc-1.00.a", xilinx_intc_of_init); 2678328255fSZubair Lutfullah Kakakhel IRQCHIP_DECLARE(xilinx_intc_opb, "xlnx,opb-intc-1.00.c", xilinx_intc_of_init); 268