linux/drivers/irqchip/irq-idt3243x.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Driver for IDT/Renesas 79RC3243x Interrupt Controller.
   4 */
   5
   6#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
   7
   8#include <linux/interrupt.h>
   9#include <linux/irq.h>
  10#include <linux/irqchip.h>
  11#include <linux/irqchip/chained_irq.h>
  12#include <linux/irqdomain.h>
  13#include <linux/of_address.h>
  14#include <linux/of_irq.h>
  15
  16#define IDT_PIC_NR_IRQS         32
  17
  18#define IDT_PIC_IRQ_PEND                0x00
  19#define IDT_PIC_IRQ_MASK                0x08
  20
  21struct idt_pic_data {
  22        void __iomem *base;
  23        struct irq_domain *irq_domain;
  24        struct irq_chip_generic *gc;
  25};
  26
  27static void idt_irq_dispatch(struct irq_desc *desc)
  28{
  29        struct idt_pic_data *idtpic = irq_desc_get_handler_data(desc);
  30        struct irq_chip *host_chip = irq_desc_get_chip(desc);
  31        u32 pending, hwirq, virq;
  32
  33        chained_irq_enter(host_chip, desc);
  34
  35        pending = irq_reg_readl(idtpic->gc, IDT_PIC_IRQ_PEND);
  36        pending &= ~idtpic->gc->mask_cache;
  37        while (pending) {
  38                hwirq = __fls(pending);
  39                virq = irq_linear_revmap(idtpic->irq_domain, hwirq);
  40                if (virq)
  41                        generic_handle_irq(virq);
  42                pending &= ~(1 << hwirq);
  43        }
  44
  45        chained_irq_exit(host_chip, desc);
  46}
  47
  48static int idt_pic_init(struct device_node *of_node, struct device_node *parent)
  49{
  50        struct irq_domain *domain;
  51        struct idt_pic_data *idtpic;
  52        struct irq_chip_generic *gc;
  53        struct irq_chip_type *ct;
  54        unsigned int parent_irq;
  55        int ret = 0;
  56
  57        idtpic = kzalloc(sizeof(*idtpic), GFP_KERNEL);
  58        if (!idtpic) {
  59                ret = -ENOMEM;
  60                goto out_err;
  61        }
  62
  63        parent_irq = irq_of_parse_and_map(of_node, 0);
  64        if (!parent_irq) {
  65                pr_err("Failed to map parent IRQ!\n");
  66                ret = -EINVAL;
  67                goto out_free;
  68        }
  69
  70        idtpic->base = of_iomap(of_node, 0);
  71        if (!idtpic->base) {
  72                pr_err("Failed to map base address!\n");
  73                ret = -ENOMEM;
  74                goto out_unmap_irq;
  75        }
  76
  77        domain = irq_domain_add_linear(of_node, IDT_PIC_NR_IRQS,
  78                                       &irq_generic_chip_ops, NULL);
  79        if (!domain) {
  80                pr_err("Failed to add irqdomain!\n");
  81                ret = -ENOMEM;
  82                goto out_iounmap;
  83        }
  84        idtpic->irq_domain = domain;
  85
  86        ret = irq_alloc_domain_generic_chips(domain, 32, 1, "IDTPIC",
  87                                             handle_level_irq, 0,
  88                                             IRQ_NOPROBE | IRQ_LEVEL, 0);
  89        if (ret)
  90                goto out_domain_remove;
  91
  92        gc = irq_get_domain_generic_chip(domain, 0);
  93        gc->reg_base = idtpic->base;
  94        gc->private = idtpic;
  95
  96        ct = gc->chip_types;
  97        ct->regs.mask = IDT_PIC_IRQ_MASK;
  98        ct->chip.irq_mask = irq_gc_mask_set_bit;
  99        ct->chip.irq_unmask = irq_gc_mask_clr_bit;
 100        idtpic->gc = gc;
 101
 102        /* Mask interrupts. */
 103        writel(0xffffffff, idtpic->base + IDT_PIC_IRQ_MASK);
 104        gc->mask_cache = 0xffffffff;
 105
 106        irq_set_chained_handler_and_data(parent_irq,
 107                                         idt_irq_dispatch, idtpic);
 108
 109        return 0;
 110
 111out_domain_remove:
 112        irq_domain_remove(domain);
 113out_iounmap:
 114        iounmap(idtpic->base);
 115out_unmap_irq:
 116        irq_dispose_mapping(parent_irq);
 117out_free:
 118        kfree(idtpic);
 119out_err:
 120        pr_err("Failed to initialize! (errno = %d)\n", ret);
 121        return ret;
 122}
 123
 124IRQCHIP_DECLARE(idt_pic, "idt,32434-pic", idt_pic_init);
 125