Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Copyright (C) 2016 Marvell
0003  *
0004  * Yehuda Yitschak <yehuday@marvell.com>
0005  * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
0006  *
0007  * This file is licensed under the terms of the GNU General Public
0008  * License version 2.  This program is licensed "as is" without any
0009  * warranty of any kind, whether express or implied.
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     /* ACK and mask all interrupts */
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