summaryrefslogtreecommitdiffstats
path: root/drivers/irqchip/irq-mvebu-icu.c
diff options
context:
space:
mode:
authorMiquel Raynal <miquel.raynal@bootlin.com>2018-10-02 04:59:00 -0400
committerMarc Zyngier <marc.zyngier@arm.com>2018-10-02 07:00:30 -0400
commit175c98aa265b7ba45e81524075d28ff2725d8e83 (patch)
tree1786b8001f368b0254e0233aa538e26f0b33151f /drivers/irqchip/irq-mvebu-icu.c
parent228197c5694369b5d65a2b5f579fc443166bc3f8 (diff)
irqchip/irq-mvebu-icu: Add support for System Error Interrupts (SEI)
So far the ICU only handled NSR interrupts through GICP. An SEI driver provides an MSI domain through which it is possible to raise SEI, so let's add SEI support to the ICU driver. Handle the NSR probe function in a more generic way to support other type of interrupts. Each interrupt domain is a tree domain to avoid allocation the 207 entries each time. Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Diffstat (limited to 'drivers/irqchip/irq-mvebu-icu.c')
-rw-r--r--drivers/irqchip/irq-mvebu-icu.c139
1 files changed, 105 insertions, 34 deletions
diff --git a/drivers/irqchip/irq-mvebu-icu.c b/drivers/irqchip/irq-mvebu-icu.c
index d0b1994e2a6f..547045d89c4b 100644
--- a/drivers/irqchip/irq-mvebu-icu.c
+++ b/drivers/irqchip/irq-mvebu-icu.c
@@ -27,6 +27,10 @@
27#define ICU_SETSPI_NSR_AH 0x14 27#define ICU_SETSPI_NSR_AH 0x14
28#define ICU_CLRSPI_NSR_AL 0x18 28#define ICU_CLRSPI_NSR_AL 0x18
29#define ICU_CLRSPI_NSR_AH 0x1c 29#define ICU_CLRSPI_NSR_AH 0x1c
30#define ICU_SET_SEI_AL 0x50
31#define ICU_SET_SEI_AH 0x54
32#define ICU_CLR_SEI_AL 0x58
33#define ICU_CLR_SEI_AH 0x5C
30#define ICU_INT_CFG(x) (0x100 + 4 * (x)) 34#define ICU_INT_CFG(x) (0x100 + 4 * (x))
31#define ICU_INT_ENABLE BIT(24) 35#define ICU_INT_ENABLE BIT(24)
32#define ICU_IS_EDGE BIT(28) 36#define ICU_IS_EDGE BIT(28)
@@ -37,11 +41,23 @@
37#define ICU_SATA0_ICU_ID 109 41#define ICU_SATA0_ICU_ID 109
38#define ICU_SATA1_ICU_ID 107 42#define ICU_SATA1_ICU_ID 107
39 43
44struct mvebu_icu_subset_data {
45 unsigned int icu_group;
46 unsigned int offset_set_ah;
47 unsigned int offset_set_al;
48 unsigned int offset_clr_ah;
49 unsigned int offset_clr_al;
50};
51
40struct mvebu_icu { 52struct mvebu_icu {
41 struct irq_chip irq_chip;
42 void __iomem *base; 53 void __iomem *base;
43 struct device *dev; 54 struct device *dev;
55};
56
57struct mvebu_icu_msi_data {
58 struct mvebu_icu *icu;
44 atomic_t initialized; 59 atomic_t initialized;
60 const struct mvebu_icu_subset_data *subset_data;
45}; 61};
46 62
47struct mvebu_icu_irq_data { 63struct mvebu_icu_irq_data {
@@ -52,28 +68,38 @@ struct mvebu_icu_irq_data {
52 68
53DEFINE_STATIC_KEY_FALSE(legacy_bindings); 69DEFINE_STATIC_KEY_FALSE(legacy_bindings);
54 70
55static void mvebu_icu_init(struct mvebu_icu *icu, struct msi_msg *msg) 71static void mvebu_icu_init(struct mvebu_icu *icu,
72 struct mvebu_icu_msi_data *msi_data,
73 struct msi_msg *msg)
56{ 74{
57 if (atomic_cmpxchg(&icu->initialized, false, true)) 75 const struct mvebu_icu_subset_data *subset = msi_data->subset_data;
76
77 if (atomic_cmpxchg(&msi_data->initialized, false, true))
58 return; 78 return;
59 79
60 /* Set Clear/Set ICU SPI message address in AP */ 80 /* Set 'SET' ICU SPI message address in AP */
61 writel_relaxed(msg[0].address_hi, icu->base + ICU_SETSPI_NSR_AH); 81 writel_relaxed(msg[0].address_hi, icu->base + subset->offset_set_ah);
62 writel_relaxed(msg[0].address_lo, icu->base + ICU_SETSPI_NSR_AL); 82 writel_relaxed(msg[0].address_lo, icu->base + subset->offset_set_al);
63 writel_relaxed(msg[1].address_hi, icu->base + ICU_CLRSPI_NSR_AH); 83
64 writel_relaxed(msg[1].address_lo, icu->base + ICU_CLRSPI_NSR_AL); 84 if (subset->icu_group != ICU_GRP_NSR)
85 return;
86
87 /* Set 'CLEAR' ICU SPI message address in AP (level-MSI only) */
88 writel_relaxed(msg[1].address_hi, icu->base + subset->offset_clr_ah);
89 writel_relaxed(msg[1].address_lo, icu->base + subset->offset_clr_al);
65} 90}
66 91
67static void mvebu_icu_write_msg(struct msi_desc *desc, struct msi_msg *msg) 92static void mvebu_icu_write_msg(struct msi_desc *desc, struct msi_msg *msg)
68{ 93{
69 struct irq_data *d = irq_get_irq_data(desc->irq); 94 struct irq_data *d = irq_get_irq_data(desc->irq);
95 struct mvebu_icu_msi_data *msi_data = platform_msi_get_host_data(d->domain);
70 struct mvebu_icu_irq_data *icu_irqd = d->chip_data; 96 struct mvebu_icu_irq_data *icu_irqd = d->chip_data;
71 struct mvebu_icu *icu = icu_irqd->icu; 97 struct mvebu_icu *icu = icu_irqd->icu;
72 unsigned int icu_int; 98 unsigned int icu_int;
73 99
74 if (msg->address_lo || msg->address_hi) { 100 if (msg->address_lo || msg->address_hi) {
75 /* One off initialization */ 101 /* One off initialization per domain */
76 mvebu_icu_init(icu, msg); 102 mvebu_icu_init(icu, msi_data, msg);
77 /* Configure the ICU with irq number & type */ 103 /* Configure the ICU with irq number & type */
78 icu_int = msg->data | ICU_INT_ENABLE; 104 icu_int = msg->data | ICU_INT_ENABLE;
79 if (icu_irqd->type & IRQ_TYPE_EDGE_RISING) 105 if (icu_irqd->type & IRQ_TYPE_EDGE_RISING)
@@ -103,10 +129,29 @@ static void mvebu_icu_write_msg(struct msi_desc *desc, struct msi_msg *msg)
103 } 129 }
104} 130}
105 131
132static struct irq_chip mvebu_icu_nsr_chip = {
133 .name = "ICU-NSR",
134 .irq_mask = irq_chip_mask_parent,
135 .irq_unmask = irq_chip_unmask_parent,
136 .irq_eoi = irq_chip_eoi_parent,
137 .irq_set_type = irq_chip_set_type_parent,
138 .irq_set_affinity = irq_chip_set_affinity_parent,
139};
140
141static struct irq_chip mvebu_icu_sei_chip = {
142 .name = "ICU-SEI",
143 .irq_ack = irq_chip_ack_parent,
144 .irq_mask = irq_chip_mask_parent,
145 .irq_unmask = irq_chip_unmask_parent,
146 .irq_set_type = irq_chip_set_type_parent,
147 .irq_set_affinity = irq_chip_set_affinity_parent,
148};
149
106static int 150static int
107mvebu_icu_irq_domain_translate(struct irq_domain *d, struct irq_fwspec *fwspec, 151mvebu_icu_irq_domain_translate(struct irq_domain *d, struct irq_fwspec *fwspec,
108 unsigned long *hwirq, unsigned int *type) 152 unsigned long *hwirq, unsigned int *type)
109{ 153{
154 struct mvebu_icu_msi_data *msi_data = platform_msi_get_host_data(d);
110 struct mvebu_icu *icu = platform_msi_get_host_data(d); 155 struct mvebu_icu *icu = platform_msi_get_host_data(d);
111 unsigned int param_count = static_branch_unlikely(&legacy_bindings) ? 3 : 2; 156 unsigned int param_count = static_branch_unlikely(&legacy_bindings) ? 3 : 2;
112 157
@@ -128,6 +173,15 @@ mvebu_icu_irq_domain_translate(struct irq_domain *d, struct irq_fwspec *fwspec,
128 } else { 173 } else {
129 *hwirq = fwspec->param[0]; 174 *hwirq = fwspec->param[0];
130 *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; 175 *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
176
177 /*
178 * The ICU receives level interrupts. While the NSR are also
179 * level interrupts, SEI are edge interrupts. Force the type
180 * here in this case. Please note that this makes the interrupt
181 * handling unreliable.
182 */
183 if (msi_data->subset_data->icu_group == ICU_GRP_SEI)
184 *type = IRQ_TYPE_EDGE_RISING;
131 } 185 }
132 186
133 if (*hwirq >= ICU_MAX_IRQS) { 187 if (*hwirq >= ICU_MAX_IRQS) {
@@ -145,8 +199,10 @@ mvebu_icu_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
145 int err; 199 int err;
146 unsigned long hwirq; 200 unsigned long hwirq;
147 struct irq_fwspec *fwspec = args; 201 struct irq_fwspec *fwspec = args;
148 struct mvebu_icu *icu = platform_msi_get_host_data(domain); 202 struct mvebu_icu_msi_data *msi_data = platform_msi_get_host_data(domain);
203 struct mvebu_icu *icu = msi_data->icu;
149 struct mvebu_icu_irq_data *icu_irqd; 204 struct mvebu_icu_irq_data *icu_irqd;
205 struct irq_chip *chip = &mvebu_icu_nsr_chip;
150 206
151 icu_irqd = kmalloc(sizeof(*icu_irqd), GFP_KERNEL); 207 icu_irqd = kmalloc(sizeof(*icu_irqd), GFP_KERNEL);
152 if (!icu_irqd) 208 if (!icu_irqd)
@@ -162,7 +218,7 @@ mvebu_icu_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
162 if (static_branch_unlikely(&legacy_bindings)) 218 if (static_branch_unlikely(&legacy_bindings))
163 icu_irqd->icu_group = fwspec->param[0]; 219 icu_irqd->icu_group = fwspec->param[0];
164 else 220 else
165 icu_irqd->icu_group = ICU_GRP_NSR; 221 icu_irqd->icu_group = msi_data->subset_data->icu_group;
166 icu_irqd->icu = icu; 222 icu_irqd->icu = icu;
167 223
168 err = platform_msi_domain_alloc(domain, virq, nr_irqs); 224 err = platform_msi_domain_alloc(domain, virq, nr_irqs);
@@ -176,8 +232,11 @@ mvebu_icu_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
176 if (err) 232 if (err)
177 goto free_msi; 233 goto free_msi;
178 234
235 if (icu_irqd->icu_group == ICU_GRP_SEI)
236 chip = &mvebu_icu_sei_chip;
237
179 err = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, 238 err = irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
180 &icu->irq_chip, icu_irqd); 239 chip, icu_irqd);
181 if (err) { 240 if (err) {
182 dev_err(icu->dev, "failed to set the data to IRQ domain\n"); 241 dev_err(icu->dev, "failed to set the data to IRQ domain\n");
183 goto free_msi; 242 goto free_msi;
@@ -210,24 +269,50 @@ static const struct irq_domain_ops mvebu_icu_domain_ops = {
210 .free = mvebu_icu_irq_domain_free, 269 .free = mvebu_icu_irq_domain_free,
211}; 270};
212 271
272static const struct mvebu_icu_subset_data mvebu_icu_nsr_subset_data = {
273 .icu_group = ICU_GRP_NSR,
274 .offset_set_ah = ICU_SETSPI_NSR_AH,
275 .offset_set_al = ICU_SETSPI_NSR_AL,
276 .offset_clr_ah = ICU_CLRSPI_NSR_AH,
277 .offset_clr_al = ICU_CLRSPI_NSR_AL,
278};
279
280static const struct mvebu_icu_subset_data mvebu_icu_sei_subset_data = {
281 .icu_group = ICU_GRP_SEI,
282 .offset_set_ah = ICU_SET_SEI_AH,
283 .offset_set_al = ICU_SET_SEI_AL,
284};
285
213static const struct of_device_id mvebu_icu_subset_of_match[] = { 286static const struct of_device_id mvebu_icu_subset_of_match[] = {
214 { 287 {
215 .compatible = "marvell,cp110-icu-nsr", 288 .compatible = "marvell,cp110-icu-nsr",
289 .data = &mvebu_icu_nsr_subset_data,
290 },
291 {
292 .compatible = "marvell,cp110-icu-sei",
293 .data = &mvebu_icu_sei_subset_data,
216 }, 294 },
217 {}, 295 {},
218}; 296};
219 297
220static int mvebu_icu_subset_probe(struct platform_device *pdev) 298static int mvebu_icu_subset_probe(struct platform_device *pdev)
221{ 299{
300 struct mvebu_icu_msi_data *msi_data;
222 struct device_node *msi_parent_dn; 301 struct device_node *msi_parent_dn;
223 struct device *dev = &pdev->dev; 302 struct device *dev = &pdev->dev;
224 struct irq_domain *irq_domain; 303 struct irq_domain *irq_domain;
225 struct mvebu_icu *icu;
226 304
227 if (static_branch_unlikely(&legacy_bindings)) 305 msi_data = devm_kzalloc(dev, sizeof(*msi_data), GFP_KERNEL);
228 icu = dev_get_drvdata(dev); 306 if (!msi_data)
229 else 307 return -ENOMEM;
230 icu = dev_get_drvdata(dev->parent); 308
309 if (static_branch_unlikely(&legacy_bindings)) {
310 msi_data->icu = dev_get_drvdata(dev);
311 msi_data->subset_data = &mvebu_icu_nsr_subset_data;
312 } else {
313 msi_data->icu = dev_get_drvdata(dev->parent);
314 msi_data->subset_data = of_device_get_match_data(dev);
315 }
231 316
232 dev->msi_domain = of_msi_get_domain(dev, dev->of_node, 317 dev->msi_domain = of_msi_get_domain(dev, dev->of_node,
233 DOMAIN_BUS_PLATFORM_MSI); 318 DOMAIN_BUS_PLATFORM_MSI);
@@ -241,7 +326,7 @@ static int mvebu_icu_subset_probe(struct platform_device *pdev)
241 irq_domain = platform_msi_create_device_tree_domain(dev, ICU_MAX_IRQS, 326 irq_domain = platform_msi_create_device_tree_domain(dev, ICU_MAX_IRQS,
242 mvebu_icu_write_msg, 327 mvebu_icu_write_msg,
243 &mvebu_icu_domain_ops, 328 &mvebu_icu_domain_ops,
244 icu); 329 msi_data);
245 if (!irq_domain) { 330 if (!irq_domain) {
246 dev_err(dev, "Failed to create ICU MSI domain\n"); 331 dev_err(dev, "Failed to create ICU MSI domain\n");
247 return -ENOMEM; 332 return -ENOMEM;
@@ -279,12 +364,6 @@ static int mvebu_icu_probe(struct platform_device *pdev)
279 return PTR_ERR(icu->base); 364 return PTR_ERR(icu->base);
280 } 365 }
281 366
282 icu->irq_chip.name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
283 "ICU.%x",
284 (unsigned int)res->start);
285 if (!icu->irq_chip.name)
286 return -ENOMEM;
287
288 /* 367 /*
289 * Legacy bindings: ICU is one node with one MSI parent: force manually 368 * Legacy bindings: ICU is one node with one MSI parent: force manually
290 * the probe of the NSR interrupts side. 369 * the probe of the NSR interrupts side.
@@ -295,16 +374,8 @@ static int mvebu_icu_probe(struct platform_device *pdev)
295 if (!of_get_child_count(pdev->dev.of_node)) 374 if (!of_get_child_count(pdev->dev.of_node))
296 static_branch_enable(&legacy_bindings); 375 static_branch_enable(&legacy_bindings);
297 376
298 icu->irq_chip.irq_mask = irq_chip_mask_parent;
299 icu->irq_chip.irq_unmask = irq_chip_unmask_parent;
300 icu->irq_chip.irq_eoi = irq_chip_eoi_parent;
301 icu->irq_chip.irq_set_type = irq_chip_set_type_parent;
302#ifdef CONFIG_SMP
303 icu->irq_chip.irq_set_affinity = irq_chip_set_affinity_parent;
304#endif
305
306 /* 377 /*
307 * Clean all ICU interrupts with type SPI_NSR, required to 378 * Clean all ICU interrupts of type NSR and SEI, required to
308 * avoid unpredictable SPI assignments done by firmware. 379 * avoid unpredictable SPI assignments done by firmware.
309 */ 380 */
310 for (i = 0 ; i < ICU_MAX_IRQS ; i++) { 381 for (i = 0 ; i < ICU_MAX_IRQS ; i++) {