1529ea368SThomas Bogendoerfer // SPDX-License-Identifier: GPL-2.0 2529ea368SThomas Bogendoerfer /* 3529ea368SThomas Bogendoerfer * Driver for IDT/Renesas 79RC3243x Interrupt Controller. 4529ea368SThomas Bogendoerfer */ 5529ea368SThomas Bogendoerfer 6529ea368SThomas Bogendoerfer #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 7529ea368SThomas Bogendoerfer 8529ea368SThomas Bogendoerfer #include <linux/interrupt.h> 9529ea368SThomas Bogendoerfer #include <linux/irq.h> 10529ea368SThomas Bogendoerfer #include <linux/irqchip.h> 11529ea368SThomas Bogendoerfer #include <linux/irqchip/chained_irq.h> 12529ea368SThomas Bogendoerfer #include <linux/irqdomain.h> 13529ea368SThomas Bogendoerfer #include <linux/of_address.h> 14529ea368SThomas Bogendoerfer #include <linux/of_irq.h> 15529ea368SThomas Bogendoerfer 16529ea368SThomas Bogendoerfer #define IDT_PIC_NR_IRQS 32 17529ea368SThomas Bogendoerfer 18529ea368SThomas Bogendoerfer #define IDT_PIC_IRQ_PEND 0x00 19529ea368SThomas Bogendoerfer #define IDT_PIC_IRQ_MASK 0x08 20529ea368SThomas Bogendoerfer 21529ea368SThomas Bogendoerfer struct idt_pic_data { 22529ea368SThomas Bogendoerfer void __iomem *base; 23529ea368SThomas Bogendoerfer struct irq_domain *irq_domain; 24529ea368SThomas Bogendoerfer struct irq_chip_generic *gc; 25529ea368SThomas Bogendoerfer }; 26529ea368SThomas Bogendoerfer 27529ea368SThomas Bogendoerfer static void idt_irq_dispatch(struct irq_desc *desc) 28529ea368SThomas Bogendoerfer { 29529ea368SThomas Bogendoerfer struct idt_pic_data *idtpic = irq_desc_get_handler_data(desc); 30529ea368SThomas Bogendoerfer struct irq_chip *host_chip = irq_desc_get_chip(desc); 31*046a6ee2SMarc Zyngier u32 pending, hwirq; 32529ea368SThomas Bogendoerfer 33529ea368SThomas Bogendoerfer chained_irq_enter(host_chip, desc); 34529ea368SThomas Bogendoerfer 35529ea368SThomas Bogendoerfer pending = irq_reg_readl(idtpic->gc, IDT_PIC_IRQ_PEND); 36529ea368SThomas Bogendoerfer pending &= ~idtpic->gc->mask_cache; 37529ea368SThomas Bogendoerfer while (pending) { 38529ea368SThomas Bogendoerfer hwirq = __fls(pending); 39*046a6ee2SMarc Zyngier generic_handle_domain_irq(idtpic->irq_domain, hwirq); 40529ea368SThomas Bogendoerfer pending &= ~(1 << hwirq); 41529ea368SThomas Bogendoerfer } 42529ea368SThomas Bogendoerfer 43529ea368SThomas Bogendoerfer chained_irq_exit(host_chip, desc); 44529ea368SThomas Bogendoerfer } 45529ea368SThomas Bogendoerfer 46529ea368SThomas Bogendoerfer static int idt_pic_init(struct device_node *of_node, struct device_node *parent) 47529ea368SThomas Bogendoerfer { 48529ea368SThomas Bogendoerfer struct irq_domain *domain; 49529ea368SThomas Bogendoerfer struct idt_pic_data *idtpic; 50529ea368SThomas Bogendoerfer struct irq_chip_generic *gc; 51529ea368SThomas Bogendoerfer struct irq_chip_type *ct; 52529ea368SThomas Bogendoerfer unsigned int parent_irq; 53529ea368SThomas Bogendoerfer int ret = 0; 54529ea368SThomas Bogendoerfer 55529ea368SThomas Bogendoerfer idtpic = kzalloc(sizeof(*idtpic), GFP_KERNEL); 56529ea368SThomas Bogendoerfer if (!idtpic) { 57529ea368SThomas Bogendoerfer ret = -ENOMEM; 58529ea368SThomas Bogendoerfer goto out_err; 59529ea368SThomas Bogendoerfer } 60529ea368SThomas Bogendoerfer 61529ea368SThomas Bogendoerfer parent_irq = irq_of_parse_and_map(of_node, 0); 62529ea368SThomas Bogendoerfer if (!parent_irq) { 63529ea368SThomas Bogendoerfer pr_err("Failed to map parent IRQ!\n"); 64529ea368SThomas Bogendoerfer ret = -EINVAL; 65529ea368SThomas Bogendoerfer goto out_free; 66529ea368SThomas Bogendoerfer } 67529ea368SThomas Bogendoerfer 68529ea368SThomas Bogendoerfer idtpic->base = of_iomap(of_node, 0); 69529ea368SThomas Bogendoerfer if (!idtpic->base) { 70529ea368SThomas Bogendoerfer pr_err("Failed to map base address!\n"); 71529ea368SThomas Bogendoerfer ret = -ENOMEM; 72529ea368SThomas Bogendoerfer goto out_unmap_irq; 73529ea368SThomas Bogendoerfer } 74529ea368SThomas Bogendoerfer 75529ea368SThomas Bogendoerfer domain = irq_domain_add_linear(of_node, IDT_PIC_NR_IRQS, 76529ea368SThomas Bogendoerfer &irq_generic_chip_ops, NULL); 77529ea368SThomas Bogendoerfer if (!domain) { 78529ea368SThomas Bogendoerfer pr_err("Failed to add irqdomain!\n"); 79529ea368SThomas Bogendoerfer ret = -ENOMEM; 80529ea368SThomas Bogendoerfer goto out_iounmap; 81529ea368SThomas Bogendoerfer } 82529ea368SThomas Bogendoerfer idtpic->irq_domain = domain; 83529ea368SThomas Bogendoerfer 84529ea368SThomas Bogendoerfer ret = irq_alloc_domain_generic_chips(domain, 32, 1, "IDTPIC", 85529ea368SThomas Bogendoerfer handle_level_irq, 0, 86529ea368SThomas Bogendoerfer IRQ_NOPROBE | IRQ_LEVEL, 0); 87529ea368SThomas Bogendoerfer if (ret) 88529ea368SThomas Bogendoerfer goto out_domain_remove; 89529ea368SThomas Bogendoerfer 90529ea368SThomas Bogendoerfer gc = irq_get_domain_generic_chip(domain, 0); 91529ea368SThomas Bogendoerfer gc->reg_base = idtpic->base; 92529ea368SThomas Bogendoerfer gc->private = idtpic; 93529ea368SThomas Bogendoerfer 94529ea368SThomas Bogendoerfer ct = gc->chip_types; 95529ea368SThomas Bogendoerfer ct->regs.mask = IDT_PIC_IRQ_MASK; 96529ea368SThomas Bogendoerfer ct->chip.irq_mask = irq_gc_mask_set_bit; 97529ea368SThomas Bogendoerfer ct->chip.irq_unmask = irq_gc_mask_clr_bit; 98529ea368SThomas Bogendoerfer idtpic->gc = gc; 99529ea368SThomas Bogendoerfer 100529ea368SThomas Bogendoerfer /* Mask interrupts. */ 101529ea368SThomas Bogendoerfer writel(0xffffffff, idtpic->base + IDT_PIC_IRQ_MASK); 102529ea368SThomas Bogendoerfer gc->mask_cache = 0xffffffff; 103529ea368SThomas Bogendoerfer 104529ea368SThomas Bogendoerfer irq_set_chained_handler_and_data(parent_irq, 105529ea368SThomas Bogendoerfer idt_irq_dispatch, idtpic); 106529ea368SThomas Bogendoerfer 107529ea368SThomas Bogendoerfer return 0; 108529ea368SThomas Bogendoerfer 109529ea368SThomas Bogendoerfer out_domain_remove: 110529ea368SThomas Bogendoerfer irq_domain_remove(domain); 111529ea368SThomas Bogendoerfer out_iounmap: 112529ea368SThomas Bogendoerfer iounmap(idtpic->base); 113529ea368SThomas Bogendoerfer out_unmap_irq: 114529ea368SThomas Bogendoerfer irq_dispose_mapping(parent_irq); 115529ea368SThomas Bogendoerfer out_free: 116529ea368SThomas Bogendoerfer kfree(idtpic); 117529ea368SThomas Bogendoerfer out_err: 118529ea368SThomas Bogendoerfer pr_err("Failed to initialize! (errno = %d)\n", ret); 119529ea368SThomas Bogendoerfer return ret; 120529ea368SThomas Bogendoerfer } 121529ea368SThomas Bogendoerfer 122529ea368SThomas Bogendoerfer IRQCHIP_DECLARE(idt_pic, "idt,32434-pic", idt_pic_init); 123