Commit | Line | Data |
---|---|---|
4db8e6d2 SK |
1 | /* |
2 | * Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se> | |
3 | * Copyright (C) 2014 Stefan Kristansson <stefan.kristiansson@saunalahti.fi> | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or | |
6 | * modify it under the terms of the GNU General Public License | |
7 | * as published by the Free Software Foundation; either version | |
8 | * 2 of the License, or (at your option) any later version. | |
9 | */ | |
10 | ||
11 | #include <linux/irq.h> | |
41a83e06 | 12 | #include <linux/irqchip.h> |
4db8e6d2 SK |
13 | #include <linux/of.h> |
14 | #include <linux/of_irq.h> | |
15 | #include <linux/of_address.h> | |
16 | ||
4db8e6d2 SK |
17 | /* OR1K PIC implementation */ |
18 | ||
19 | struct or1k_pic_dev { | |
20 | struct irq_chip chip; | |
21 | irq_flow_handler_t handle; | |
22 | unsigned long flags; | |
23 | }; | |
24 | ||
25 | /* | |
26 | * We're a couple of cycles faster than the generic implementations with | |
27 | * these 'fast' versions. | |
28 | */ | |
29 | ||
30 | static void or1k_pic_mask(struct irq_data *data) | |
31 | { | |
32 | mtspr(SPR_PICMR, mfspr(SPR_PICMR) & ~(1UL << data->hwirq)); | |
33 | } | |
34 | ||
35 | static void or1k_pic_unmask(struct irq_data *data) | |
36 | { | |
37 | mtspr(SPR_PICMR, mfspr(SPR_PICMR) | (1UL << data->hwirq)); | |
38 | } | |
39 | ||
40 | static void or1k_pic_ack(struct irq_data *data) | |
41 | { | |
42 | mtspr(SPR_PICSR, (1UL << data->hwirq)); | |
43 | } | |
44 | ||
45 | static void or1k_pic_mask_ack(struct irq_data *data) | |
46 | { | |
47 | mtspr(SPR_PICMR, mfspr(SPR_PICMR) & ~(1UL << data->hwirq)); | |
48 | mtspr(SPR_PICSR, (1UL << data->hwirq)); | |
49 | } | |
50 | ||
51 | /* | |
52 | * There are two oddities with the OR1200 PIC implementation: | |
53 | * i) LEVEL-triggered interrupts are latched and need to be cleared | |
54 | * ii) the interrupt latch is cleared by writing a 0 to the bit, | |
55 | * as opposed to a 1 as mandated by the spec | |
56 | */ | |
57 | static void or1k_pic_or1200_ack(struct irq_data *data) | |
58 | { | |
59 | mtspr(SPR_PICSR, mfspr(SPR_PICSR) & ~(1UL << data->hwirq)); | |
60 | } | |
61 | ||
62 | static void or1k_pic_or1200_mask_ack(struct irq_data *data) | |
63 | { | |
64 | mtspr(SPR_PICMR, mfspr(SPR_PICMR) & ~(1UL << data->hwirq)); | |
65 | mtspr(SPR_PICSR, mfspr(SPR_PICSR) & ~(1UL << data->hwirq)); | |
66 | } | |
67 | ||
68 | static struct or1k_pic_dev or1k_pic_level = { | |
69 | .chip = { | |
70 | .name = "or1k-PIC-level", | |
71 | .irq_unmask = or1k_pic_unmask, | |
72 | .irq_mask = or1k_pic_mask, | |
73 | .irq_mask_ack = or1k_pic_mask, | |
74 | }, | |
75 | .handle = handle_level_irq, | |
76 | .flags = IRQ_LEVEL | IRQ_NOPROBE, | |
77 | }; | |
78 | ||
79 | static struct or1k_pic_dev or1k_pic_edge = { | |
80 | .chip = { | |
81 | .name = "or1k-PIC-edge", | |
82 | .irq_unmask = or1k_pic_unmask, | |
83 | .irq_mask = or1k_pic_mask, | |
84 | .irq_ack = or1k_pic_ack, | |
85 | .irq_mask_ack = or1k_pic_mask_ack, | |
86 | }, | |
87 | .handle = handle_edge_irq, | |
88 | .flags = IRQ_LEVEL | IRQ_NOPROBE, | |
89 | }; | |
90 | ||
91 | static struct or1k_pic_dev or1k_pic_or1200 = { | |
92 | .chip = { | |
93 | .name = "or1200-PIC", | |
94 | .irq_unmask = or1k_pic_unmask, | |
95 | .irq_mask = or1k_pic_mask, | |
96 | .irq_ack = or1k_pic_or1200_ack, | |
97 | .irq_mask_ack = or1k_pic_or1200_mask_ack, | |
98 | }, | |
99 | .handle = handle_level_irq, | |
100 | .flags = IRQ_LEVEL | IRQ_NOPROBE, | |
101 | }; | |
102 | ||
103 | static struct irq_domain *root_domain; | |
104 | ||
105 | static inline int pic_get_irq(int first) | |
106 | { | |
107 | int hwirq; | |
108 | ||
109 | hwirq = ffs(mfspr(SPR_PICSR) >> first); | |
110 | if (!hwirq) | |
111 | return NO_IRQ; | |
112 | else | |
113 | hwirq = hwirq + first - 1; | |
114 | ||
b0fee1dc | 115 | return hwirq; |
4db8e6d2 SK |
116 | } |
117 | ||
118 | static void or1k_pic_handle_irq(struct pt_regs *regs) | |
119 | { | |
120 | int irq = -1; | |
121 | ||
122 | while ((irq = pic_get_irq(irq + 1)) != NO_IRQ) | |
b0fee1dc | 123 | handle_domain_irq(root_domain, irq, regs); |
4db8e6d2 SK |
124 | } |
125 | ||
126 | static int or1k_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) | |
127 | { | |
128 | struct or1k_pic_dev *pic = d->host_data; | |
129 | ||
130 | irq_set_chip_and_handler(irq, &pic->chip, pic->handle); | |
131 | irq_set_status_flags(irq, pic->flags); | |
132 | ||
133 | return 0; | |
134 | } | |
135 | ||
136 | static const struct irq_domain_ops or1k_irq_domain_ops = { | |
137 | .xlate = irq_domain_xlate_onecell, | |
138 | .map = or1k_map, | |
139 | }; | |
140 | ||
141 | /* | |
142 | * This sets up the IRQ domain for the PIC built in to the OpenRISC | |
143 | * 1000 CPU. This is the "root" domain as these are the interrupts | |
144 | * that directly trigger an exception in the CPU. | |
145 | */ | |
146 | static int __init or1k_pic_init(struct device_node *node, | |
147 | struct or1k_pic_dev *pic) | |
148 | { | |
149 | /* Disable all interrupts until explicitly requested */ | |
150 | mtspr(SPR_PICMR, (0UL)); | |
151 | ||
152 | root_domain = irq_domain_add_linear(node, 32, &or1k_irq_domain_ops, | |
153 | pic); | |
154 | ||
155 | set_handle_irq(or1k_pic_handle_irq); | |
156 | ||
157 | return 0; | |
158 | } | |
159 | ||
160 | static int __init or1k_pic_or1200_init(struct device_node *node, | |
161 | struct device_node *parent) | |
162 | { | |
163 | return or1k_pic_init(node, &or1k_pic_or1200); | |
164 | } | |
165 | IRQCHIP_DECLARE(or1k_pic_or1200, "opencores,or1200-pic", or1k_pic_or1200_init); | |
166 | IRQCHIP_DECLARE(or1k_pic, "opencores,or1k-pic", or1k_pic_or1200_init); | |
167 | ||
168 | static int __init or1k_pic_level_init(struct device_node *node, | |
169 | struct device_node *parent) | |
170 | { | |
171 | return or1k_pic_init(node, &or1k_pic_level); | |
172 | } | |
173 | IRQCHIP_DECLARE(or1k_pic_level, "opencores,or1k-pic-level", | |
174 | or1k_pic_level_init); | |
175 | ||
176 | static int __init or1k_pic_edge_init(struct device_node *node, | |
177 | struct device_node *parent) | |
178 | { | |
179 | return or1k_pic_init(node, &or1k_pic_edge); | |
180 | } | |
181 | IRQCHIP_DECLARE(or1k_pic_edge, "opencores,or1k-pic-edge", or1k_pic_edge_init); |