• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 Imagination Technologies Ltd.	All rights reserved
3  *	Author: Miodrag Dinic <miodrag.dinic@imgtec.com>
4  *
5  * This file implements interrupt controller driver for MIPS Goldfish PIC.
6  *
7  * This program is free software; you can redistribute	it and/or modify it
8  * under  the terms of	the GNU General	 Public License as published by the
9  * Free Software Foundation;  either version 2 of the  License, or (at your
10  * option) any later version.
11  */
12 
13 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
14 
15 #include <linux/interrupt.h>
16 #include <linux/irq.h>
17 #include <linux/irqchip.h>
18 #include <linux/irqchip/chained_irq.h>
19 #include <linux/irqdomain.h>
20 #include <linux/of_address.h>
21 #include <linux/of_irq.h>
22 #include <linux/slab.h>
23 
24 #include <asm/irq_cpu.h>
25 
26 #define GFPIC_NR_IRQS			32
27 
28 /* 8..39 Cascaded Goldfish PIC interrupts */
29 #define GFPIC_IRQ_BASE			8
30 
31 #define GFPIC_REG_IRQ_PENDING		0x04
32 #define GFPIC_REG_IRQ_DISABLE_ALL	0x08
33 #define GFPIC_REG_IRQ_DISABLE		0x0c
34 #define GFPIC_REG_IRQ_ENABLE		0x10
35 
36 struct goldfish_pic_data {
37 	void __iomem *base;
38 	struct irq_domain *irq_domain;
39 };
40 
goldfish_pic_cascade(unsigned irq,struct irq_desc * desc)41 static void goldfish_pic_cascade(unsigned irq, struct irq_desc *desc)
42 {
43 	struct goldfish_pic_data *gfpic = irq_desc_get_handler_data(desc);
44 	struct irq_chip *host_chip = irq_desc_get_chip(desc);
45 	u32 pending, hwirq, virq;
46 
47 	pr_debug("Cascaded IRQ %d\n", irq);
48 
49 	chained_irq_enter(host_chip, desc);
50 
51 	pending = readl(gfpic->base + GFPIC_REG_IRQ_PENDING);
52 	while (pending) {
53 		hwirq = __fls(pending);
54 		virq = irq_linear_revmap(gfpic->irq_domain, hwirq);
55 		generic_handle_irq(virq);
56 		pending &= ~(1 << hwirq);
57 	}
58 
59 	chained_irq_exit(host_chip, desc);
60 }
61 
62 static const struct irq_domain_ops goldfish_irq_domain_ops = {
63 	.xlate = irq_domain_xlate_onecell,
64 };
65 
goldfish_pic_of_init(struct device_node * of_node,struct device_node * parent)66 static int __init goldfish_pic_of_init(struct device_node *of_node,
67 				       struct device_node *parent)
68 {
69 	struct goldfish_pic_data *gfpic;
70 	struct irq_chip_generic *gc;
71 	struct irq_chip_type *ct;
72 	unsigned int parent_irq;
73 	int ret = 0;
74 
75 	gfpic = kzalloc(sizeof(*gfpic), GFP_KERNEL);
76 	if (!gfpic) {
77 		ret = -ENOMEM;
78 		goto out_err;
79 	}
80 
81 	parent_irq = irq_of_parse_and_map(of_node, 0);
82 	if (!parent_irq) {
83 		pr_err("Failed to map parent IRQ!\n");
84 		ret = -EINVAL;
85 		goto out_free;
86 	}
87 
88 	gfpic->base = of_iomap(of_node, 0);
89 	if (!gfpic->base) {
90 		pr_err("Failed to map base address!\n");
91 		ret = -ENOMEM;
92 		goto out_unmap_irq;
93 	}
94 
95 	/* Mask interrupts. */
96 	writel(1, gfpic->base + GFPIC_REG_IRQ_DISABLE_ALL);
97 
98 	gc = irq_alloc_generic_chip("GFPIC", 1, GFPIC_IRQ_BASE, gfpic->base,
99 				    handle_level_irq);
100 	if (!gc) {
101 		pr_err("Failed to allocate chip structures!\n");
102 		ret = -ENOMEM;
103 		goto out_iounmap;
104 	}
105 
106 	ct = gc->chip_types;
107 	ct->regs.enable = GFPIC_REG_IRQ_ENABLE;
108 	ct->regs.disable = GFPIC_REG_IRQ_DISABLE;
109 	ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
110 	ct->chip.irq_mask = irq_gc_mask_disable_reg;
111 
112 	irq_setup_generic_chip(gc, IRQ_MSK(GFPIC_NR_IRQS), 0,
113 			       IRQ_NOPROBE | IRQ_LEVEL, 0);
114 
115 	gfpic->irq_domain = irq_domain_add_legacy(of_node, GFPIC_NR_IRQS,
116 						  GFPIC_IRQ_BASE, 0,
117 						  &goldfish_irq_domain_ops,
118 						  NULL);
119 	if (!gfpic->irq_domain) {
120 		pr_err("Failed to add irqdomain!\n");
121 		ret = -ENOMEM;
122 		goto out_iounmap;
123 	}
124 
125 	irq_set_chained_handler(parent_irq, goldfish_pic_cascade);
126 	irq_set_handler_data(parent_irq, gfpic);
127 
128 	pr_info("Successfully registered.\n");
129 	return 0;
130 
131 out_iounmap:
132 	iounmap(gfpic->base);
133 out_unmap_irq:
134 	irq_dispose_mapping(parent_irq);
135 out_free:
136 	kfree(gfpic);
137 out_err:
138 	pr_err("Failed to initialize! (errno = %d)\n", ret);
139 	return ret;
140 }
141 
142 static struct of_device_id __initdata irqchip_of_match_goldfish_pic[] = {
143 	{ .compatible = "mti,cpu-interrupt-controller", .data = mips_cpu_irq_of_init },
144 	{ .compatible = "generic,goldfish-pic", .data = goldfish_pic_of_init },
145 	{},
146 };
147 
arch_init_irq(void)148 void __init arch_init_irq(void)
149 {
150 	of_irq_init(irqchip_of_match_goldfish_pic);
151 }
152