linux/drivers/irqchip/irq-goldfish-pic.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Driver for MIPS Goldfish Programmable Interrupt Controller.
   4 *
   5 * Author: Miodrag Dinic <miodrag.dinic@mips.com>
   6 */
   7
   8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
   9
  10#include <linux/interrupt.h>
  11#include <linux/irq.h>
  12#include <linux/irqchip.h>
  13#include <linux/irqchip/chained_irq.h>
  14#include <linux/irqdomain.h>
  15#include <linux/of_address.h>
  16#include <linux/of_irq.h>
  17
  18#define GFPIC_NR_IRQS                   32
  19
  20/* 8..39 Cascaded Goldfish PIC interrupts */
  21#define GFPIC_IRQ_BASE                  8
  22
  23#define GFPIC_REG_IRQ_PENDING           0x04
  24#define GFPIC_REG_IRQ_DISABLE_ALL       0x08
  25#define GFPIC_REG_IRQ_DISABLE           0x0c
  26#define GFPIC_REG_IRQ_ENABLE            0x10
  27
  28struct goldfish_pic_data {
  29        void __iomem *base;
  30        struct irq_domain *irq_domain;
  31};
  32
  33static void goldfish_pic_cascade(struct irq_desc *desc)
  34{
  35        struct goldfish_pic_data *gfpic = irq_desc_get_handler_data(desc);
  36        struct irq_chip *host_chip = irq_desc_get_chip(desc);
  37        u32 pending, hwirq, virq;
  38
  39        chained_irq_enter(host_chip, desc);
  40
  41        pending = readl(gfpic->base + GFPIC_REG_IRQ_PENDING);
  42        while (pending) {
  43                hwirq = __fls(pending);
  44                virq = irq_linear_revmap(gfpic->irq_domain, hwirq);
  45                generic_handle_irq(virq);
  46                pending &= ~(1 << hwirq);
  47        }
  48
  49        chained_irq_exit(host_chip, desc);
  50}
  51
  52static const struct irq_domain_ops goldfish_irq_domain_ops = {
  53        .xlate = irq_domain_xlate_onecell,
  54};
  55
  56static int __init goldfish_pic_of_init(struct device_node *of_node,
  57                                       struct device_node *parent)
  58{
  59        struct goldfish_pic_data *gfpic;
  60        struct irq_chip_generic *gc;
  61        struct irq_chip_type *ct;
  62        unsigned int parent_irq;
  63        int ret = 0;
  64
  65        gfpic = kzalloc(sizeof(*gfpic), GFP_KERNEL);
  66        if (!gfpic) {
  67                ret = -ENOMEM;
  68                goto out_err;
  69        }
  70
  71        parent_irq = irq_of_parse_and_map(of_node, 0);
  72        if (!parent_irq) {
  73                pr_err("Failed to map parent IRQ!\n");
  74                ret = -EINVAL;
  75                goto out_free;
  76        }
  77
  78        gfpic->base = of_iomap(of_node, 0);
  79        if (!gfpic->base) {
  80                pr_err("Failed to map base address!\n");
  81                ret = -ENOMEM;
  82                goto out_unmap_irq;
  83        }
  84
  85        /* Mask interrupts. */
  86        writel(1, gfpic->base + GFPIC_REG_IRQ_DISABLE_ALL);
  87
  88        gc = irq_alloc_generic_chip("GFPIC", 1, GFPIC_IRQ_BASE, gfpic->base,
  89                                    handle_level_irq);
  90        if (!gc) {
  91                pr_err("Failed to allocate chip structures!\n");
  92                ret = -ENOMEM;
  93                goto out_iounmap;
  94        }
  95
  96        ct = gc->chip_types;
  97        ct->regs.enable = GFPIC_REG_IRQ_ENABLE;
  98        ct->regs.disable = GFPIC_REG_IRQ_DISABLE;
  99        ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
 100        ct->chip.irq_mask = irq_gc_mask_disable_reg;
 101
 102        irq_setup_generic_chip(gc, IRQ_MSK(GFPIC_NR_IRQS), 0,
 103                               IRQ_NOPROBE | IRQ_LEVEL, 0);
 104
 105        gfpic->irq_domain = irq_domain_add_legacy(of_node, GFPIC_NR_IRQS,
 106                                                  GFPIC_IRQ_BASE, 0,
 107                                                  &goldfish_irq_domain_ops,
 108                                                  NULL);
 109        if (!gfpic->irq_domain) {
 110                pr_err("Failed to add irqdomain!\n");
 111                ret = -ENOMEM;
 112                goto out_destroy_generic_chip;
 113        }
 114
 115        irq_set_chained_handler_and_data(parent_irq,
 116                                         goldfish_pic_cascade, gfpic);
 117
 118        pr_info("Successfully registered.\n");
 119        return 0;
 120
 121out_destroy_generic_chip:
 122        irq_destroy_generic_chip(gc, IRQ_MSK(GFPIC_NR_IRQS),
 123                                 IRQ_NOPROBE | IRQ_LEVEL, 0);
 124out_iounmap:
 125        iounmap(gfpic->base);
 126out_unmap_irq:
 127        irq_dispose_mapping(parent_irq);
 128out_free:
 129        kfree(gfpic);
 130out_err:
 131        pr_err("Failed to initialize! (errno = %d)\n", ret);
 132        return ret;
 133}
 134
 135IRQCHIP_DECLARE(google_gf_pic, "google,goldfish-pic", goldfish_pic_of_init);
 136