Commit | Line | Data |
---|---|---|
abe45fd9 GH |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright (C) 2005-2017 Andes Technology Corporation | |
3 | ||
4 | #include <linux/irq.h> | |
5 | #include <linux/of.h> | |
6 | #include <linux/of_irq.h> | |
7 | #include <linux/of_address.h> | |
8 | #include <linux/interrupt.h> | |
9 | #include <linux/irqdomain.h> | |
10 | #include <linux/irqchip.h> | |
11 | #include <nds32_intrinsic.h> | |
12 | ||
7938e631 NH |
13 | unsigned long wake_mask; |
14 | ||
abe45fd9 GH |
15 | static void ativic32_ack_irq(struct irq_data *data) |
16 | { | |
17 | __nds32__mtsr_dsb(BIT(data->hwirq), NDS32_SR_INT_PEND2); | |
18 | } | |
19 | ||
20 | static void ativic32_mask_irq(struct irq_data *data) | |
21 | { | |
22 | unsigned long int_mask2 = __nds32__mfsr(NDS32_SR_INT_MASK2); | |
23 | __nds32__mtsr_dsb(int_mask2 & (~(BIT(data->hwirq))), NDS32_SR_INT_MASK2); | |
24 | } | |
25 | ||
26 | static void ativic32_unmask_irq(struct irq_data *data) | |
27 | { | |
28 | unsigned long int_mask2 = __nds32__mfsr(NDS32_SR_INT_MASK2); | |
29 | __nds32__mtsr_dsb(int_mask2 | (BIT(data->hwirq)), NDS32_SR_INT_MASK2); | |
30 | } | |
31 | ||
7938e631 NH |
32 | static int nointc_set_wake(struct irq_data *data, unsigned int on) |
33 | { | |
34 | unsigned long int_mask = __nds32__mfsr(NDS32_SR_INT_MASK); | |
35 | static unsigned long irq_orig_bit; | |
36 | u32 bit = 1 << data->hwirq; | |
37 | ||
38 | if (on) { | |
39 | if (int_mask & bit) | |
40 | __assign_bit(data->hwirq, &irq_orig_bit, true); | |
41 | else | |
42 | __assign_bit(data->hwirq, &irq_orig_bit, false); | |
43 | ||
44 | __assign_bit(data->hwirq, &int_mask, true); | |
45 | __assign_bit(data->hwirq, &wake_mask, true); | |
46 | ||
47 | } else { | |
48 | if (!(irq_orig_bit & bit)) | |
49 | __assign_bit(data->hwirq, &int_mask, false); | |
50 | ||
51 | __assign_bit(data->hwirq, &wake_mask, false); | |
52 | __assign_bit(data->hwirq, &irq_orig_bit, false); | |
53 | } | |
54 | ||
55 | __nds32__mtsr_dsb(int_mask, NDS32_SR_INT_MASK); | |
56 | ||
57 | return 0; | |
58 | } | |
59 | ||
abe45fd9 GH |
60 | static struct irq_chip ativic32_chip = { |
61 | .name = "ativic32", | |
62 | .irq_ack = ativic32_ack_irq, | |
63 | .irq_mask = ativic32_mask_irq, | |
64 | .irq_unmask = ativic32_unmask_irq, | |
7938e631 | 65 | .irq_set_wake = nointc_set_wake, |
abe45fd9 GH |
66 | }; |
67 | ||
68 | static unsigned int __initdata nivic_map[6] = { 6, 2, 10, 16, 24, 32 }; | |
69 | ||
70 | static struct irq_domain *root_domain; | |
71 | static int ativic32_irq_domain_map(struct irq_domain *id, unsigned int virq, | |
72 | irq_hw_number_t hw) | |
73 | { | |
74 | ||
75 | unsigned long int_trigger_type; | |
76 | u32 type; | |
77 | struct irq_data *irq_data; | |
78 | int_trigger_type = __nds32__mfsr(NDS32_SR_INT_TRIGGER); | |
79 | irq_data = irq_get_irq_data(virq); | |
80 | if (!irq_data) | |
81 | return -EINVAL; | |
82 | ||
83 | if (int_trigger_type & (BIT(hw))) { | |
84 | irq_set_chip_and_handler(virq, &ativic32_chip, handle_edge_irq); | |
85 | type = IRQ_TYPE_EDGE_RISING; | |
86 | } else { | |
87 | irq_set_chip_and_handler(virq, &ativic32_chip, handle_level_irq); | |
88 | type = IRQ_TYPE_LEVEL_HIGH; | |
89 | } | |
90 | ||
91 | irqd_set_trigger_type(irq_data, type); | |
92 | return 0; | |
93 | } | |
94 | ||
57a744e9 | 95 | static const struct irq_domain_ops ativic32_ops = { |
abe45fd9 GH |
96 | .map = ativic32_irq_domain_map, |
97 | .xlate = irq_domain_xlate_onecell | |
98 | }; | |
99 | ||
100 | static irq_hw_number_t get_intr_src(void) | |
101 | { | |
102 | return ((__nds32__mfsr(NDS32_SR_ITYPE) & ITYPE_mskVECTOR) >> ITYPE_offVECTOR) | |
103 | - NDS32_VECTOR_offINTERRUPT; | |
104 | } | |
105 | ||
106 | asmlinkage void asm_do_IRQ(struct pt_regs *regs) | |
107 | { | |
108 | irq_hw_number_t hwirq = get_intr_src(); | |
109 | handle_domain_irq(root_domain, hwirq, regs); | |
110 | } | |
111 | ||
112 | int __init ativic32_init_irq(struct device_node *node, struct device_node *parent) | |
113 | { | |
114 | unsigned long int_vec_base, nivic, nr_ints; | |
115 | ||
116 | if (WARN(parent, "non-root ativic32 are not supported")) | |
117 | return -EINVAL; | |
118 | ||
119 | int_vec_base = __nds32__mfsr(NDS32_SR_IVB); | |
120 | ||
121 | if (((int_vec_base & IVB_mskIVIC_VER) >> IVB_offIVIC_VER) == 0) | |
122 | panic("Unable to use atcivic32 for this cpu.\n"); | |
123 | ||
124 | nivic = (int_vec_base & IVB_mskNIVIC) >> IVB_offNIVIC; | |
125 | if (nivic >= ARRAY_SIZE(nivic_map)) | |
126 | panic("The number of input for ativic32 is not supported.\n"); | |
127 | ||
128 | nr_ints = nivic_map[nivic]; | |
129 | ||
130 | root_domain = irq_domain_add_linear(node, nr_ints, | |
131 | &ativic32_ops, NULL); | |
132 | ||
133 | if (!root_domain) | |
134 | panic("%s: unable to create IRQ domain\n", node->full_name); | |
135 | ||
136 | return 0; | |
137 | } | |
138 | IRQCHIP_DECLARE(ativic32, "andestech,ativic32", ativic32_init_irq); |