0001
0002
0003
0004
0005
0006
0007 #include <linux/init.h>
0008 #include <linux/of_address.h>
0009 #include <linux/of_irq.h>
0010 #include <linux/irqchip.h>
0011 #include <linux/irqchip/chained_irq.h>
0012 #include <linux/irq.h>
0013 #include <linux/io.h>
0014 #include <linux/syscore_ops.h>
0015
0016 #include <asm/i8259.h>
0017
0018 #define HTPIC_MAX_PARENT_IRQ 4
0019 #define HTINT_NUM_VECTORS 8
0020 #define HTINT_EN_OFF 0x20
0021
0022 struct loongson_htpic {
0023 void __iomem *base;
0024 struct irq_domain *domain;
0025 };
0026
0027 static struct loongson_htpic *htpic;
0028
0029 static void htpic_irq_dispatch(struct irq_desc *desc)
0030 {
0031 struct loongson_htpic *priv = irq_desc_get_handler_data(desc);
0032 struct irq_chip *chip = irq_desc_get_chip(desc);
0033 uint32_t pending;
0034
0035 chained_irq_enter(chip, desc);
0036 pending = readl(priv->base);
0037
0038 writel(pending, priv->base);
0039
0040 if (!pending)
0041 spurious_interrupt();
0042
0043 while (pending) {
0044 int bit = __ffs(pending);
0045
0046 if (unlikely(bit > 15)) {
0047 spurious_interrupt();
0048 break;
0049 }
0050
0051 generic_handle_domain_irq(priv->domain, bit);
0052 pending &= ~BIT(bit);
0053 }
0054 chained_irq_exit(chip, desc);
0055 }
0056
0057 static void htpic_reg_init(void)
0058 {
0059 int i;
0060
0061 for (i = 0; i < HTINT_NUM_VECTORS; i++) {
0062
0063 writel(0x0, htpic->base + HTINT_EN_OFF + i * 0x4);
0064
0065 (void) readl(htpic->base + i * 0x4);
0066
0067 writel(GENMASK(31, 0), htpic->base + i * 0x4);
0068 }
0069
0070
0071 writel(0xffff, htpic->base + HTINT_EN_OFF);
0072 }
0073
0074 static void htpic_resume(void)
0075 {
0076 htpic_reg_init();
0077 }
0078
0079 struct syscore_ops htpic_syscore_ops = {
0080 .resume = htpic_resume,
0081 };
0082
0083 static int __init htpic_of_init(struct device_node *node, struct device_node *parent)
0084 {
0085 unsigned int parent_irq[4];
0086 int i, err;
0087 int num_parents = 0;
0088
0089 if (htpic) {
0090 pr_err("loongson-htpic: Only one HTPIC is allowed in the system\n");
0091 return -ENODEV;
0092 }
0093
0094 htpic = kzalloc(sizeof(*htpic), GFP_KERNEL);
0095 if (!htpic)
0096 return -ENOMEM;
0097
0098 htpic->base = of_iomap(node, 0);
0099 if (!htpic->base) {
0100 err = -ENODEV;
0101 goto out_free;
0102 }
0103
0104 htpic->domain = __init_i8259_irqs(node);
0105 if (!htpic->domain) {
0106 pr_err("loongson-htpic: Failed to initialize i8259 IRQs\n");
0107 err = -ENOMEM;
0108 goto out_iounmap;
0109 }
0110
0111
0112 for (i = 0; i < HTPIC_MAX_PARENT_IRQ; i++) {
0113 parent_irq[i] = irq_of_parse_and_map(node, i);
0114 if (parent_irq[i] <= 0)
0115 break;
0116
0117 num_parents++;
0118 }
0119
0120 if (!num_parents) {
0121 pr_err("loongson-htpic: Failed to get parent irqs\n");
0122 err = -ENODEV;
0123 goto out_remove_domain;
0124 }
0125
0126 htpic_reg_init();
0127
0128 for (i = 0; i < num_parents; i++) {
0129 irq_set_chained_handler_and_data(parent_irq[i],
0130 htpic_irq_dispatch, htpic);
0131 }
0132
0133 register_syscore_ops(&htpic_syscore_ops);
0134
0135 return 0;
0136
0137 out_remove_domain:
0138 irq_domain_remove(htpic->domain);
0139 out_iounmap:
0140 iounmap(htpic->base);
0141 out_free:
0142 kfree(htpic);
0143 return err;
0144 }
0145
0146 IRQCHIP_DECLARE(loongson_htpic, "loongson,htpic-1.0", htpic_of_init);