forked from Minki/linux
2874c5fd28
Based on 1 normalized pattern(s): this program is free software you can redistribute it and or modify it under the terms of the gnu general public license as published by the free software foundation either version 2 of the license or at your option any later version extracted by the scancode license scanner the SPDX license identifier GPL-2.0-or-later has been chosen to replace the boilerplate/reference in 3029 file(s). Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Allison Randal <allison@lohutok.net> Cc: linux-spdx@vger.kernel.org Link: https://lkml.kernel.org/r/20190527070032.746973796@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
136 lines
3.3 KiB
C
136 lines
3.3 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Driver for MIPS Goldfish Programmable Interrupt Controller.
|
|
*
|
|
* Author: Miodrag Dinic <miodrag.dinic@mips.com>
|
|
*/
|
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
#include <linux/interrupt.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/irqchip.h>
|
|
#include <linux/irqchip/chained_irq.h>
|
|
#include <linux/irqdomain.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_irq.h>
|
|
|
|
#define GFPIC_NR_IRQS 32
|
|
|
|
/* 8..39 Cascaded Goldfish PIC interrupts */
|
|
#define GFPIC_IRQ_BASE 8
|
|
|
|
#define GFPIC_REG_IRQ_PENDING 0x04
|
|
#define GFPIC_REG_IRQ_DISABLE_ALL 0x08
|
|
#define GFPIC_REG_IRQ_DISABLE 0x0c
|
|
#define GFPIC_REG_IRQ_ENABLE 0x10
|
|
|
|
struct goldfish_pic_data {
|
|
void __iomem *base;
|
|
struct irq_domain *irq_domain;
|
|
};
|
|
|
|
static void goldfish_pic_cascade(struct irq_desc *desc)
|
|
{
|
|
struct goldfish_pic_data *gfpic = irq_desc_get_handler_data(desc);
|
|
struct irq_chip *host_chip = irq_desc_get_chip(desc);
|
|
u32 pending, hwirq, virq;
|
|
|
|
chained_irq_enter(host_chip, desc);
|
|
|
|
pending = readl(gfpic->base + GFPIC_REG_IRQ_PENDING);
|
|
while (pending) {
|
|
hwirq = __fls(pending);
|
|
virq = irq_linear_revmap(gfpic->irq_domain, hwirq);
|
|
generic_handle_irq(virq);
|
|
pending &= ~(1 << hwirq);
|
|
}
|
|
|
|
chained_irq_exit(host_chip, desc);
|
|
}
|
|
|
|
static const struct irq_domain_ops goldfish_irq_domain_ops = {
|
|
.xlate = irq_domain_xlate_onecell,
|
|
};
|
|
|
|
static int __init goldfish_pic_of_init(struct device_node *of_node,
|
|
struct device_node *parent)
|
|
{
|
|
struct goldfish_pic_data *gfpic;
|
|
struct irq_chip_generic *gc;
|
|
struct irq_chip_type *ct;
|
|
unsigned int parent_irq;
|
|
int ret = 0;
|
|
|
|
gfpic = kzalloc(sizeof(*gfpic), GFP_KERNEL);
|
|
if (!gfpic) {
|
|
ret = -ENOMEM;
|
|
goto out_err;
|
|
}
|
|
|
|
parent_irq = irq_of_parse_and_map(of_node, 0);
|
|
if (!parent_irq) {
|
|
pr_err("Failed to map parent IRQ!\n");
|
|
ret = -EINVAL;
|
|
goto out_free;
|
|
}
|
|
|
|
gfpic->base = of_iomap(of_node, 0);
|
|
if (!gfpic->base) {
|
|
pr_err("Failed to map base address!\n");
|
|
ret = -ENOMEM;
|
|
goto out_unmap_irq;
|
|
}
|
|
|
|
/* Mask interrupts. */
|
|
writel(1, gfpic->base + GFPIC_REG_IRQ_DISABLE_ALL);
|
|
|
|
gc = irq_alloc_generic_chip("GFPIC", 1, GFPIC_IRQ_BASE, gfpic->base,
|
|
handle_level_irq);
|
|
if (!gc) {
|
|
pr_err("Failed to allocate chip structures!\n");
|
|
ret = -ENOMEM;
|
|
goto out_iounmap;
|
|
}
|
|
|
|
ct = gc->chip_types;
|
|
ct->regs.enable = GFPIC_REG_IRQ_ENABLE;
|
|
ct->regs.disable = GFPIC_REG_IRQ_DISABLE;
|
|
ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
|
|
ct->chip.irq_mask = irq_gc_mask_disable_reg;
|
|
|
|
irq_setup_generic_chip(gc, IRQ_MSK(GFPIC_NR_IRQS), 0,
|
|
IRQ_NOPROBE | IRQ_LEVEL, 0);
|
|
|
|
gfpic->irq_domain = irq_domain_add_legacy(of_node, GFPIC_NR_IRQS,
|
|
GFPIC_IRQ_BASE, 0,
|
|
&goldfish_irq_domain_ops,
|
|
NULL);
|
|
if (!gfpic->irq_domain) {
|
|
pr_err("Failed to add irqdomain!\n");
|
|
ret = -ENOMEM;
|
|
goto out_destroy_generic_chip;
|
|
}
|
|
|
|
irq_set_chained_handler_and_data(parent_irq,
|
|
goldfish_pic_cascade, gfpic);
|
|
|
|
pr_info("Successfully registered.\n");
|
|
return 0;
|
|
|
|
out_destroy_generic_chip:
|
|
irq_destroy_generic_chip(gc, IRQ_MSK(GFPIC_NR_IRQS),
|
|
IRQ_NOPROBE | IRQ_LEVEL, 0);
|
|
out_iounmap:
|
|
iounmap(gfpic->base);
|
|
out_unmap_irq:
|
|
irq_dispose_mapping(parent_irq);
|
|
out_free:
|
|
kfree(gfpic);
|
|
out_err:
|
|
pr_err("Failed to initialize! (errno = %d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
IRQCHIP_DECLARE(google_gf_pic, "google,goldfish-pic", goldfish_pic_of_init);
|