0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 #include <linux/interrupt.h>
0013 #include <linux/io.h>
0014 #include <linux/irq.h>
0015 #include <linux/irqchip.h>
0016 #include <linux/irqchip/chained_irq.h>
0017 #include <linux/irqdomain.h>
0018 #include <linux/module.h>
0019 #include <linux/of_irq.h>
0020 #include <linux/platform_device.h>
0021 #include <linux/seq_file.h>
0022
0023 #define PIC_CAUSE 0x0
0024 #define PIC_MASK 0x4
0025
0026 #define PIC_MAX_IRQS 32
0027 #define PIC_MAX_IRQ_MASK ((1UL << PIC_MAX_IRQS) - 1)
0028
0029 struct mvebu_pic {
0030 void __iomem *base;
0031 u32 parent_irq;
0032 struct irq_domain *domain;
0033 struct platform_device *pdev;
0034 };
0035
0036 static void mvebu_pic_reset(struct mvebu_pic *pic)
0037 {
0038
0039 writel(0, pic->base + PIC_MASK);
0040 writel(PIC_MAX_IRQ_MASK, pic->base + PIC_CAUSE);
0041 }
0042
0043 static void mvebu_pic_eoi_irq(struct irq_data *d)
0044 {
0045 struct mvebu_pic *pic = irq_data_get_irq_chip_data(d);
0046
0047 writel(1 << d->hwirq, pic->base + PIC_CAUSE);
0048 }
0049
0050 static void mvebu_pic_mask_irq(struct irq_data *d)
0051 {
0052 struct mvebu_pic *pic = irq_data_get_irq_chip_data(d);
0053 u32 reg;
0054
0055 reg = readl(pic->base + PIC_MASK);
0056 reg |= (1 << d->hwirq);
0057 writel(reg, pic->base + PIC_MASK);
0058 }
0059
0060 static void mvebu_pic_unmask_irq(struct irq_data *d)
0061 {
0062 struct mvebu_pic *pic = irq_data_get_irq_chip_data(d);
0063 u32 reg;
0064
0065 reg = readl(pic->base + PIC_MASK);
0066 reg &= ~(1 << d->hwirq);
0067 writel(reg, pic->base + PIC_MASK);
0068 }
0069
0070 static void mvebu_pic_print_chip(struct irq_data *d, struct seq_file *p)
0071 {
0072 struct mvebu_pic *pic = irq_data_get_irq_chip_data(d);
0073
0074 seq_printf(p, dev_name(&pic->pdev->dev));
0075 }
0076
0077 static const struct irq_chip mvebu_pic_chip = {
0078 .irq_mask = mvebu_pic_mask_irq,
0079 .irq_unmask = mvebu_pic_unmask_irq,
0080 .irq_eoi = mvebu_pic_eoi_irq,
0081 .irq_print_chip = mvebu_pic_print_chip,
0082 };
0083
0084 static int mvebu_pic_irq_map(struct irq_domain *domain, unsigned int virq,
0085 irq_hw_number_t hwirq)
0086 {
0087 struct mvebu_pic *pic = domain->host_data;
0088
0089 irq_set_percpu_devid(virq);
0090 irq_set_chip_data(virq, pic);
0091 irq_set_chip_and_handler(virq, &mvebu_pic_chip, handle_percpu_devid_irq);
0092 irq_set_status_flags(virq, IRQ_LEVEL);
0093 irq_set_probe(virq);
0094
0095 return 0;
0096 }
0097
0098 static const struct irq_domain_ops mvebu_pic_domain_ops = {
0099 .map = mvebu_pic_irq_map,
0100 .xlate = irq_domain_xlate_onecell,
0101 };
0102
0103 static void mvebu_pic_handle_cascade_irq(struct irq_desc *desc)
0104 {
0105 struct mvebu_pic *pic = irq_desc_get_handler_data(desc);
0106 struct irq_chip *chip = irq_desc_get_chip(desc);
0107 unsigned long irqmap, irqn;
0108
0109 irqmap = readl_relaxed(pic->base + PIC_CAUSE);
0110 chained_irq_enter(chip, desc);
0111
0112 for_each_set_bit(irqn, &irqmap, BITS_PER_LONG)
0113 generic_handle_domain_irq(pic->domain, irqn);
0114
0115 chained_irq_exit(chip, desc);
0116 }
0117
0118 static void mvebu_pic_enable_percpu_irq(void *data)
0119 {
0120 struct mvebu_pic *pic = data;
0121
0122 mvebu_pic_reset(pic);
0123 enable_percpu_irq(pic->parent_irq, IRQ_TYPE_NONE);
0124 }
0125
0126 static void mvebu_pic_disable_percpu_irq(void *data)
0127 {
0128 struct mvebu_pic *pic = data;
0129
0130 disable_percpu_irq(pic->parent_irq);
0131 }
0132
0133 static int mvebu_pic_probe(struct platform_device *pdev)
0134 {
0135 struct device_node *node = pdev->dev.of_node;
0136 struct mvebu_pic *pic;
0137
0138 pic = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_pic), GFP_KERNEL);
0139 if (!pic)
0140 return -ENOMEM;
0141
0142 pic->pdev = pdev;
0143 pic->base = devm_platform_ioremap_resource(pdev, 0);
0144 if (IS_ERR(pic->base))
0145 return PTR_ERR(pic->base);
0146
0147 pic->parent_irq = irq_of_parse_and_map(node, 0);
0148 if (pic->parent_irq <= 0) {
0149 dev_err(&pdev->dev, "Failed to parse parent interrupt\n");
0150 return -EINVAL;
0151 }
0152
0153 pic->domain = irq_domain_add_linear(node, PIC_MAX_IRQS,
0154 &mvebu_pic_domain_ops, pic);
0155 if (!pic->domain) {
0156 dev_err(&pdev->dev, "Failed to allocate irq domain\n");
0157 return -ENOMEM;
0158 }
0159
0160 irq_set_chained_handler(pic->parent_irq, mvebu_pic_handle_cascade_irq);
0161 irq_set_handler_data(pic->parent_irq, pic);
0162
0163 on_each_cpu(mvebu_pic_enable_percpu_irq, pic, 1);
0164
0165 platform_set_drvdata(pdev, pic);
0166
0167 return 0;
0168 }
0169
0170 static int mvebu_pic_remove(struct platform_device *pdev)
0171 {
0172 struct mvebu_pic *pic = platform_get_drvdata(pdev);
0173
0174 on_each_cpu(mvebu_pic_disable_percpu_irq, pic, 1);
0175 irq_domain_remove(pic->domain);
0176
0177 return 0;
0178 }
0179
0180 static const struct of_device_id mvebu_pic_of_match[] = {
0181 { .compatible = "marvell,armada-8k-pic", },
0182 {},
0183 };
0184 MODULE_DEVICE_TABLE(of, mvebu_pic_of_match);
0185
0186 static struct platform_driver mvebu_pic_driver = {
0187 .probe = mvebu_pic_probe,
0188 .remove = mvebu_pic_remove,
0189 .driver = {
0190 .name = "mvebu-pic",
0191 .of_match_table = mvebu_pic_of_match,
0192 },
0193 };
0194 module_platform_driver(mvebu_pic_driver);
0195
0196 MODULE_AUTHOR("Yehuda Yitschak <yehuday@marvell.com>");
0197 MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>");
0198 MODULE_LICENSE("GPL v2");
0199 MODULE_ALIAS("platform:mvebu_pic");
0200