Commit | Line | Data |
---|---|---|
d852e62a MS |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * RDA8810PL SoC irqchip driver | |
4 | * | |
5 | * Copyright RDA Microelectronics Company Limited | |
6 | * Copyright (c) 2017 Andreas Färber | |
7 | * Copyright (c) 2018 Manivannan Sadhasivam | |
8 | */ | |
9 | ||
10 | #include <linux/init.h> | |
11 | #include <linux/interrupt.h> | |
12 | #include <linux/irq.h> | |
13 | #include <linux/irqchip.h> | |
14 | #include <linux/irqdomain.h> | |
15 | #include <linux/of_address.h> | |
16 | ||
17 | #include <asm/exception.h> | |
18 | ||
19 | #define RDA_INTC_FINALSTATUS 0x00 | |
20 | #define RDA_INTC_MASK_SET 0x08 | |
21 | #define RDA_INTC_MASK_CLR 0x0c | |
22 | ||
23 | #define RDA_IRQ_MASK_ALL 0xFFFFFFFF | |
24 | ||
25 | #define RDA_NR_IRQS 32 | |
26 | ||
27 | static void __iomem *rda_intc_base; | |
28 | static struct irq_domain *rda_irq_domain; | |
29 | ||
30 | static void rda_intc_mask_irq(struct irq_data *d) | |
31 | { | |
32 | writel_relaxed(BIT(d->hwirq), rda_intc_base + RDA_INTC_MASK_CLR); | |
33 | } | |
34 | ||
35 | static void rda_intc_unmask_irq(struct irq_data *d) | |
36 | { | |
37 | writel_relaxed(BIT(d->hwirq), rda_intc_base + RDA_INTC_MASK_SET); | |
38 | } | |
39 | ||
40 | static int rda_intc_set_type(struct irq_data *data, unsigned int flow_type) | |
41 | { | |
42 | /* Hardware supports only level triggered interrupts */ | |
43 | if ((flow_type & (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW)) == flow_type) | |
44 | return 0; | |
45 | ||
46 | return -EINVAL; | |
47 | } | |
48 | ||
49 | static void __exception_irq_entry rda_handle_irq(struct pt_regs *regs) | |
50 | { | |
51 | u32 stat = readl_relaxed(rda_intc_base + RDA_INTC_FINALSTATUS); | |
52 | u32 hwirq; | |
53 | ||
54 | while (stat) { | |
55 | hwirq = __fls(stat); | |
0953fb26 | 56 | generic_handle_domain_irq(rda_irq_domain, hwirq); |
d852e62a MS |
57 | stat &= ~BIT(hwirq); |
58 | } | |
59 | } | |
60 | ||
61 | static struct irq_chip rda_irq_chip = { | |
62 | .name = "rda-intc", | |
63 | .irq_mask = rda_intc_mask_irq, | |
64 | .irq_unmask = rda_intc_unmask_irq, | |
65 | .irq_set_type = rda_intc_set_type, | |
66 | }; | |
67 | ||
68 | static int rda_irq_map(struct irq_domain *d, | |
69 | unsigned int virq, irq_hw_number_t hw) | |
70 | { | |
71 | irq_set_status_flags(virq, IRQ_LEVEL); | |
72 | irq_set_chip_and_handler(virq, &rda_irq_chip, handle_level_irq); | |
73 | irq_set_chip_data(virq, d->host_data); | |
74 | irq_set_probe(virq); | |
75 | ||
76 | return 0; | |
77 | } | |
78 | ||
79 | static const struct irq_domain_ops rda_irq_domain_ops = { | |
80 | .map = rda_irq_map, | |
81 | .xlate = irq_domain_xlate_onecell, | |
82 | }; | |
83 | ||
84 | static int __init rda8810_intc_init(struct device_node *node, | |
85 | struct device_node *parent) | |
86 | { | |
87 | rda_intc_base = of_io_request_and_map(node, 0, "rda-intc"); | |
1fb51c97 WY |
88 | if (IS_ERR(rda_intc_base)) |
89 | return PTR_ERR(rda_intc_base); | |
d852e62a MS |
90 | |
91 | /* Mask all interrupt sources */ | |
92 | writel_relaxed(RDA_IRQ_MASK_ALL, rda_intc_base + RDA_INTC_MASK_CLR); | |
93 | ||
94 | rda_irq_domain = irq_domain_create_linear(&node->fwnode, RDA_NR_IRQS, | |
95 | &rda_irq_domain_ops, | |
96 | rda_intc_base); | |
97 | if (!rda_irq_domain) { | |
98 | iounmap(rda_intc_base); | |
99 | return -ENOMEM; | |
100 | } | |
101 | ||
102 | set_handle_irq(rda_handle_irq); | |
103 | ||
104 | return 0; | |
105 | } | |
106 | ||
107 | IRQCHIP_DECLARE(rda_intc, "rda,8810pl-intc", rda8810_intc_init); |