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