2 * Copyright (C) 2016 Marvell
4 * Yehuda Yitschak <yehuday@marvell.com>
5 * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
7 * This file is licensed under the terms of the GNU General Public
8 * License version 2. This program is licensed "as is" without any
9 * warranty of any kind, whether express or implied.
12 #include <linux/interrupt.h>
14 #include <linux/irq.h>
15 #include <linux/irqchip.h>
16 #include <linux/irqchip/chained_irq.h>
17 #include <linux/irqdomain.h>
18 #include <linux/module.h>
19 #include <linux/of_irq.h>
20 #include <linux/platform_device.h>
25 #define PIC_MAX_IRQS 32
26 #define PIC_MAX_IRQ_MASK ((1UL << PIC_MAX_IRQS) - 1)
31 struct irq_domain
*domain
;
32 struct irq_chip irq_chip
;
35 static void mvebu_pic_reset(struct mvebu_pic
*pic
)
37 /* ACK and mask all interrupts */
38 writel(0, pic
->base
+ PIC_MASK
);
39 writel(PIC_MAX_IRQ_MASK
, pic
->base
+ PIC_CAUSE
);
42 static void mvebu_pic_eoi_irq(struct irq_data
*d
)
44 struct mvebu_pic
*pic
= irq_data_get_irq_chip_data(d
);
46 writel(1 << d
->hwirq
, pic
->base
+ PIC_CAUSE
);
49 static void mvebu_pic_mask_irq(struct irq_data
*d
)
51 struct mvebu_pic
*pic
= irq_data_get_irq_chip_data(d
);
54 reg
= readl(pic
->base
+ PIC_MASK
);
55 reg
|= (1 << d
->hwirq
);
56 writel(reg
, pic
->base
+ PIC_MASK
);
59 static void mvebu_pic_unmask_irq(struct irq_data
*d
)
61 struct mvebu_pic
*pic
= irq_data_get_irq_chip_data(d
);
64 reg
= readl(pic
->base
+ PIC_MASK
);
65 reg
&= ~(1 << d
->hwirq
);
66 writel(reg
, pic
->base
+ PIC_MASK
);
69 static int mvebu_pic_irq_map(struct irq_domain
*domain
, unsigned int virq
,
70 irq_hw_number_t hwirq
)
72 struct mvebu_pic
*pic
= domain
->host_data
;
74 irq_set_percpu_devid(virq
);
75 irq_set_chip_data(virq
, pic
);
76 irq_set_chip_and_handler(virq
, &pic
->irq_chip
,
77 handle_percpu_devid_irq
);
78 irq_set_status_flags(virq
, IRQ_LEVEL
);
84 static const struct irq_domain_ops mvebu_pic_domain_ops
= {
85 .map
= mvebu_pic_irq_map
,
86 .xlate
= irq_domain_xlate_onecell
,
89 static void mvebu_pic_handle_cascade_irq(struct irq_desc
*desc
)
91 struct mvebu_pic
*pic
= irq_desc_get_handler_data(desc
);
92 struct irq_chip
*chip
= irq_desc_get_chip(desc
);
93 unsigned long irqmap
, irqn
;
94 unsigned int cascade_irq
;
96 irqmap
= readl_relaxed(pic
->base
+ PIC_CAUSE
);
97 chained_irq_enter(chip
, desc
);
99 for_each_set_bit(irqn
, &irqmap
, BITS_PER_LONG
) {
100 cascade_irq
= irq_find_mapping(pic
->domain
, irqn
);
101 generic_handle_irq(cascade_irq
);
104 chained_irq_exit(chip
, desc
);
107 static void mvebu_pic_enable_percpu_irq(void *data
)
109 struct mvebu_pic
*pic
= data
;
111 mvebu_pic_reset(pic
);
112 enable_percpu_irq(pic
->parent_irq
, IRQ_TYPE_NONE
);
115 static void mvebu_pic_disable_percpu_irq(void *data
)
117 struct mvebu_pic
*pic
= data
;
119 disable_percpu_irq(pic
->parent_irq
);
122 static int mvebu_pic_probe(struct platform_device
*pdev
)
124 struct device_node
*node
= pdev
->dev
.of_node
;
125 struct mvebu_pic
*pic
;
126 struct irq_chip
*irq_chip
;
127 struct resource
*res
;
129 pic
= devm_kzalloc(&pdev
->dev
, sizeof(struct mvebu_pic
), GFP_KERNEL
);
133 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
134 pic
->base
= devm_ioremap_resource(&pdev
->dev
, res
);
135 if (IS_ERR(pic
->base
))
136 return PTR_ERR(pic
->base
);
138 irq_chip
= &pic
->irq_chip
;
139 irq_chip
->name
= dev_name(&pdev
->dev
);
140 irq_chip
->irq_mask
= mvebu_pic_mask_irq
;
141 irq_chip
->irq_unmask
= mvebu_pic_unmask_irq
;
142 irq_chip
->irq_eoi
= mvebu_pic_eoi_irq
;
144 pic
->parent_irq
= irq_of_parse_and_map(node
, 0);
145 if (pic
->parent_irq
<= 0) {
146 dev_err(&pdev
->dev
, "Failed to parse parent interrupt\n");
150 pic
->domain
= irq_domain_add_linear(node
, PIC_MAX_IRQS
,
151 &mvebu_pic_domain_ops
, pic
);
153 dev_err(&pdev
->dev
, "Failed to allocate irq domain\n");
157 irq_set_chained_handler(pic
->parent_irq
, mvebu_pic_handle_cascade_irq
);
158 irq_set_handler_data(pic
->parent_irq
, pic
);
160 on_each_cpu(mvebu_pic_enable_percpu_irq
, pic
, 1);
162 platform_set_drvdata(pdev
, pic
);
167 static int mvebu_pic_remove(struct platform_device
*pdev
)
169 struct mvebu_pic
*pic
= platform_get_drvdata(pdev
);
171 on_each_cpu(mvebu_pic_disable_percpu_irq
, pic
, 1);
172 irq_domain_remove(pic
->domain
);
177 static const struct of_device_id mvebu_pic_of_match
[] = {
178 { .compatible
= "marvell,armada-8k-pic", },
181 MODULE_DEVICE_TABLE(of
, mvebu_pic_of_match
);
183 static struct platform_driver mvebu_pic_driver
= {
184 .probe
= mvebu_pic_probe
,
185 .remove
= mvebu_pic_remove
,
188 .of_match_table
= mvebu_pic_of_match
,
191 module_platform_driver(mvebu_pic_driver
);
193 MODULE_AUTHOR("Yehuda Yitschak <yehuday@marvell.com>");
194 MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>");
195 MODULE_LICENSE("GPL v2");
196 MODULE_ALIAS("platform:mvebu_pic");