12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
24db8e6d2SStefan Kristiansson /*
34db8e6d2SStefan Kristiansson * Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se>
44db8e6d2SStefan Kristiansson * Copyright (C) 2014 Stefan Kristansson <stefan.kristiansson@saunalahti.fi>
54db8e6d2SStefan Kristiansson */
64db8e6d2SStefan Kristiansson
74db8e6d2SStefan Kristiansson #include <linux/irq.h>
841a83e06SJoel Porquet #include <linux/irqchip.h>
94db8e6d2SStefan Kristiansson #include <linux/of.h>
104db8e6d2SStefan Kristiansson #include <linux/of_irq.h>
114db8e6d2SStefan Kristiansson #include <linux/of_address.h>
124db8e6d2SStefan Kristiansson
134db8e6d2SStefan Kristiansson /* OR1K PIC implementation */
144db8e6d2SStefan Kristiansson
154db8e6d2SStefan Kristiansson struct or1k_pic_dev {
164db8e6d2SStefan Kristiansson struct irq_chip chip;
174db8e6d2SStefan Kristiansson irq_flow_handler_t handle;
184db8e6d2SStefan Kristiansson unsigned long flags;
194db8e6d2SStefan Kristiansson };
204db8e6d2SStefan Kristiansson
214db8e6d2SStefan Kristiansson /*
224db8e6d2SStefan Kristiansson * We're a couple of cycles faster than the generic implementations with
234db8e6d2SStefan Kristiansson * these 'fast' versions.
244db8e6d2SStefan Kristiansson */
254db8e6d2SStefan Kristiansson
or1k_pic_mask(struct irq_data * data)264db8e6d2SStefan Kristiansson static void or1k_pic_mask(struct irq_data *data)
274db8e6d2SStefan Kristiansson {
284db8e6d2SStefan Kristiansson mtspr(SPR_PICMR, mfspr(SPR_PICMR) & ~(1UL << data->hwirq));
294db8e6d2SStefan Kristiansson }
304db8e6d2SStefan Kristiansson
or1k_pic_unmask(struct irq_data * data)314db8e6d2SStefan Kristiansson static void or1k_pic_unmask(struct irq_data *data)
324db8e6d2SStefan Kristiansson {
334db8e6d2SStefan Kristiansson mtspr(SPR_PICMR, mfspr(SPR_PICMR) | (1UL << data->hwirq));
344db8e6d2SStefan Kristiansson }
354db8e6d2SStefan Kristiansson
or1k_pic_ack(struct irq_data * data)364db8e6d2SStefan Kristiansson static void or1k_pic_ack(struct irq_data *data)
374db8e6d2SStefan Kristiansson {
384db8e6d2SStefan Kristiansson mtspr(SPR_PICSR, (1UL << data->hwirq));
394db8e6d2SStefan Kristiansson }
404db8e6d2SStefan Kristiansson
or1k_pic_mask_ack(struct irq_data * data)414db8e6d2SStefan Kristiansson static void or1k_pic_mask_ack(struct irq_data *data)
424db8e6d2SStefan Kristiansson {
434db8e6d2SStefan Kristiansson mtspr(SPR_PICMR, mfspr(SPR_PICMR) & ~(1UL << data->hwirq));
444db8e6d2SStefan Kristiansson mtspr(SPR_PICSR, (1UL << data->hwirq));
454db8e6d2SStefan Kristiansson }
464db8e6d2SStefan Kristiansson
474db8e6d2SStefan Kristiansson /*
484db8e6d2SStefan Kristiansson * There are two oddities with the OR1200 PIC implementation:
494db8e6d2SStefan Kristiansson * i) LEVEL-triggered interrupts are latched and need to be cleared
504db8e6d2SStefan Kristiansson * ii) the interrupt latch is cleared by writing a 0 to the bit,
514db8e6d2SStefan Kristiansson * as opposed to a 1 as mandated by the spec
524db8e6d2SStefan Kristiansson */
or1k_pic_or1200_ack(struct irq_data * data)534db8e6d2SStefan Kristiansson static void or1k_pic_or1200_ack(struct irq_data *data)
544db8e6d2SStefan Kristiansson {
554db8e6d2SStefan Kristiansson mtspr(SPR_PICSR, mfspr(SPR_PICSR) & ~(1UL << data->hwirq));
564db8e6d2SStefan Kristiansson }
574db8e6d2SStefan Kristiansson
or1k_pic_or1200_mask_ack(struct irq_data * data)584db8e6d2SStefan Kristiansson static void or1k_pic_or1200_mask_ack(struct irq_data *data)
594db8e6d2SStefan Kristiansson {
604db8e6d2SStefan Kristiansson mtspr(SPR_PICMR, mfspr(SPR_PICMR) & ~(1UL << data->hwirq));
614db8e6d2SStefan Kristiansson mtspr(SPR_PICSR, mfspr(SPR_PICSR) & ~(1UL << data->hwirq));
624db8e6d2SStefan Kristiansson }
634db8e6d2SStefan Kristiansson
644db8e6d2SStefan Kristiansson static struct or1k_pic_dev or1k_pic_level = {
654db8e6d2SStefan Kristiansson .chip = {
664db8e6d2SStefan Kristiansson .name = "or1k-PIC-level",
674db8e6d2SStefan Kristiansson .irq_unmask = or1k_pic_unmask,
684db8e6d2SStefan Kristiansson .irq_mask = or1k_pic_mask,
694db8e6d2SStefan Kristiansson },
704db8e6d2SStefan Kristiansson .handle = handle_level_irq,
714db8e6d2SStefan Kristiansson .flags = IRQ_LEVEL | IRQ_NOPROBE,
724db8e6d2SStefan Kristiansson };
734db8e6d2SStefan Kristiansson
744db8e6d2SStefan Kristiansson static struct or1k_pic_dev or1k_pic_edge = {
754db8e6d2SStefan Kristiansson .chip = {
764db8e6d2SStefan Kristiansson .name = "or1k-PIC-edge",
774db8e6d2SStefan Kristiansson .irq_unmask = or1k_pic_unmask,
784db8e6d2SStefan Kristiansson .irq_mask = or1k_pic_mask,
794db8e6d2SStefan Kristiansson .irq_ack = or1k_pic_ack,
804db8e6d2SStefan Kristiansson .irq_mask_ack = or1k_pic_mask_ack,
814db8e6d2SStefan Kristiansson },
824db8e6d2SStefan Kristiansson .handle = handle_edge_irq,
834db8e6d2SStefan Kristiansson .flags = IRQ_LEVEL | IRQ_NOPROBE,
844db8e6d2SStefan Kristiansson };
854db8e6d2SStefan Kristiansson
864db8e6d2SStefan Kristiansson static struct or1k_pic_dev or1k_pic_or1200 = {
874db8e6d2SStefan Kristiansson .chip = {
884db8e6d2SStefan Kristiansson .name = "or1200-PIC",
894db8e6d2SStefan Kristiansson .irq_unmask = or1k_pic_unmask,
904db8e6d2SStefan Kristiansson .irq_mask = or1k_pic_mask,
914db8e6d2SStefan Kristiansson .irq_ack = or1k_pic_or1200_ack,
924db8e6d2SStefan Kristiansson .irq_mask_ack = or1k_pic_or1200_mask_ack,
934db8e6d2SStefan Kristiansson },
944db8e6d2SStefan Kristiansson .handle = handle_level_irq,
954db8e6d2SStefan Kristiansson .flags = IRQ_LEVEL | IRQ_NOPROBE,
964db8e6d2SStefan Kristiansson };
974db8e6d2SStefan Kristiansson
984db8e6d2SStefan Kristiansson static struct irq_domain *root_domain;
994db8e6d2SStefan Kristiansson
pic_get_irq(int first)1004db8e6d2SStefan Kristiansson static inline int pic_get_irq(int first)
1014db8e6d2SStefan Kristiansson {
1024db8e6d2SStefan Kristiansson int hwirq;
1034db8e6d2SStefan Kristiansson
1044db8e6d2SStefan Kristiansson hwirq = ffs(mfspr(SPR_PICSR) >> first);
1054db8e6d2SStefan Kristiansson if (!hwirq)
1064db8e6d2SStefan Kristiansson return NO_IRQ;
1074db8e6d2SStefan Kristiansson else
1084db8e6d2SStefan Kristiansson hwirq = hwirq + first - 1;
1094db8e6d2SStefan Kristiansson
110b0fee1dcSMarc Zyngier return hwirq;
1114db8e6d2SStefan Kristiansson }
1124db8e6d2SStefan Kristiansson
or1k_pic_handle_irq(struct pt_regs * regs)1134db8e6d2SStefan Kristiansson static void or1k_pic_handle_irq(struct pt_regs *regs)
1144db8e6d2SStefan Kristiansson {
1154db8e6d2SStefan Kristiansson int irq = -1;
1164db8e6d2SStefan Kristiansson
1174db8e6d2SStefan Kristiansson while ((irq = pic_get_irq(irq + 1)) != NO_IRQ)
118*0953fb26SMark Rutland generic_handle_domain_irq(root_domain, irq);
1194db8e6d2SStefan Kristiansson }
1204db8e6d2SStefan Kristiansson
or1k_map(struct irq_domain * d,unsigned int irq,irq_hw_number_t hw)1214db8e6d2SStefan Kristiansson static int or1k_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
1224db8e6d2SStefan Kristiansson {
1234db8e6d2SStefan Kristiansson struct or1k_pic_dev *pic = d->host_data;
1244db8e6d2SStefan Kristiansson
1254db8e6d2SStefan Kristiansson irq_set_chip_and_handler(irq, &pic->chip, pic->handle);
1264db8e6d2SStefan Kristiansson irq_set_status_flags(irq, pic->flags);
1274db8e6d2SStefan Kristiansson
1284db8e6d2SStefan Kristiansson return 0;
1294db8e6d2SStefan Kristiansson }
1304db8e6d2SStefan Kristiansson
1314db8e6d2SStefan Kristiansson static const struct irq_domain_ops or1k_irq_domain_ops = {
1324db8e6d2SStefan Kristiansson .xlate = irq_domain_xlate_onecell,
1334db8e6d2SStefan Kristiansson .map = or1k_map,
1344db8e6d2SStefan Kristiansson };
1354db8e6d2SStefan Kristiansson
1364db8e6d2SStefan Kristiansson /*
1374db8e6d2SStefan Kristiansson * This sets up the IRQ domain for the PIC built in to the OpenRISC
1384db8e6d2SStefan Kristiansson * 1000 CPU. This is the "root" domain as these are the interrupts
1394db8e6d2SStefan Kristiansson * that directly trigger an exception in the CPU.
1404db8e6d2SStefan Kristiansson */
or1k_pic_init(struct device_node * node,struct or1k_pic_dev * pic)1414db8e6d2SStefan Kristiansson static int __init or1k_pic_init(struct device_node *node,
1424db8e6d2SStefan Kristiansson struct or1k_pic_dev *pic)
1434db8e6d2SStefan Kristiansson {
1444db8e6d2SStefan Kristiansson /* Disable all interrupts until explicitly requested */
1454db8e6d2SStefan Kristiansson mtspr(SPR_PICMR, (0UL));
1464db8e6d2SStefan Kristiansson
1474db8e6d2SStefan Kristiansson root_domain = irq_domain_add_linear(node, 32, &or1k_irq_domain_ops,
1484db8e6d2SStefan Kristiansson pic);
1494db8e6d2SStefan Kristiansson
1504db8e6d2SStefan Kristiansson set_handle_irq(or1k_pic_handle_irq);
1514db8e6d2SStefan Kristiansson
1524db8e6d2SStefan Kristiansson return 0;
1534db8e6d2SStefan Kristiansson }
1544db8e6d2SStefan Kristiansson
or1k_pic_or1200_init(struct device_node * node,struct device_node * parent)1554db8e6d2SStefan Kristiansson static int __init or1k_pic_or1200_init(struct device_node *node,
1564db8e6d2SStefan Kristiansson struct device_node *parent)
1574db8e6d2SStefan Kristiansson {
1584db8e6d2SStefan Kristiansson return or1k_pic_init(node, &or1k_pic_or1200);
1594db8e6d2SStefan Kristiansson }
1604db8e6d2SStefan Kristiansson IRQCHIP_DECLARE(or1k_pic_or1200, "opencores,or1200-pic", or1k_pic_or1200_init);
1614db8e6d2SStefan Kristiansson IRQCHIP_DECLARE(or1k_pic, "opencores,or1k-pic", or1k_pic_or1200_init);
1624db8e6d2SStefan Kristiansson
or1k_pic_level_init(struct device_node * node,struct device_node * parent)1634db8e6d2SStefan Kristiansson static int __init or1k_pic_level_init(struct device_node *node,
1644db8e6d2SStefan Kristiansson struct device_node *parent)
1654db8e6d2SStefan Kristiansson {
1664db8e6d2SStefan Kristiansson return or1k_pic_init(node, &or1k_pic_level);
1674db8e6d2SStefan Kristiansson }
1684db8e6d2SStefan Kristiansson IRQCHIP_DECLARE(or1k_pic_level, "opencores,or1k-pic-level",
1694db8e6d2SStefan Kristiansson or1k_pic_level_init);
1704db8e6d2SStefan Kristiansson
or1k_pic_edge_init(struct device_node * node,struct device_node * parent)1714db8e6d2SStefan Kristiansson static int __init or1k_pic_edge_init(struct device_node *node,
1724db8e6d2SStefan Kristiansson struct device_node *parent)
1734db8e6d2SStefan Kristiansson {
1744db8e6d2SStefan Kristiansson return or1k_pic_init(node, &or1k_pic_edge);
1754db8e6d2SStefan Kristiansson }
1764db8e6d2SStefan Kristiansson IRQCHIP_DECLARE(or1k_pic_edge, "opencores,or1k-pic-edge", or1k_pic_edge_init);
177