root/drivers/irqchip/irq-mvebu-pic.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. mvebu_pic_reset
  2. mvebu_pic_eoi_irq
  3. mvebu_pic_mask_irq
  4. mvebu_pic_unmask_irq
  5. mvebu_pic_irq_map
  6. mvebu_pic_handle_cascade_irq
  7. mvebu_pic_enable_percpu_irq
  8. mvebu_pic_disable_percpu_irq
  9. mvebu_pic_probe
  10. mvebu_pic_remove

   1 /*
   2  * Copyright (C) 2016 Marvell
   3  *
   4  * Yehuda Yitschak <yehuday@marvell.com>
   5  * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
   6  *
   7  * This file is licensed under the terms of the GNU General Public
   8  * License version 2.  This program is licensed "as is" without any
   9  * warranty of any kind, whether express or implied.
  10  */
  11 
  12 #include <linux/interrupt.h>
  13 #include <linux/io.h>
  14 #include <linux/irq.h>
  15 #include <linux/irqchip.h>
  16 #include <linux/irqchip/chained_irq.h>
  17 #include <linux/irqdomain.h>
  18 #include <linux/module.h>
  19 #include <linux/of_irq.h>
  20 #include <linux/platform_device.h>
  21 
  22 #define PIC_CAUSE              0x0
  23 #define PIC_MASK               0x4
  24 
  25 #define PIC_MAX_IRQS            32
  26 #define PIC_MAX_IRQ_MASK        ((1UL << PIC_MAX_IRQS) - 1)
  27 
  28 struct mvebu_pic {
  29         void __iomem *base;
  30         u32 parent_irq;
  31         struct irq_domain *domain;
  32         struct irq_chip irq_chip;
  33 };
  34 
  35 static void mvebu_pic_reset(struct mvebu_pic *pic)
  36 {
  37         /* ACK and mask all interrupts */
  38         writel(0, pic->base + PIC_MASK);
  39         writel(PIC_MAX_IRQ_MASK, pic->base + PIC_CAUSE);
  40 }
  41 
  42 static void mvebu_pic_eoi_irq(struct irq_data *d)
  43 {
  44         struct mvebu_pic *pic = irq_data_get_irq_chip_data(d);
  45 
  46         writel(1 << d->hwirq, pic->base + PIC_CAUSE);
  47 }
  48 
  49 static void mvebu_pic_mask_irq(struct irq_data *d)
  50 {
  51         struct mvebu_pic *pic = irq_data_get_irq_chip_data(d);
  52         u32 reg;
  53 
  54         reg =  readl(pic->base + PIC_MASK);
  55         reg |= (1 << d->hwirq);
  56         writel(reg, pic->base + PIC_MASK);
  57 }
  58 
  59 static void mvebu_pic_unmask_irq(struct irq_data *d)
  60 {
  61         struct mvebu_pic *pic = irq_data_get_irq_chip_data(d);
  62         u32 reg;
  63 
  64         reg = readl(pic->base + PIC_MASK);
  65         reg &= ~(1 << d->hwirq);
  66         writel(reg, pic->base + PIC_MASK);
  67 }
  68 
  69 static int mvebu_pic_irq_map(struct irq_domain *domain, unsigned int virq,
  70                              irq_hw_number_t hwirq)
  71 {
  72         struct mvebu_pic *pic = domain->host_data;
  73 
  74         irq_set_percpu_devid(virq);
  75         irq_set_chip_data(virq, pic);
  76         irq_set_chip_and_handler(virq, &pic->irq_chip,
  77                                  handle_percpu_devid_irq);
  78         irq_set_status_flags(virq, IRQ_LEVEL);
  79         irq_set_probe(virq);
  80 
  81         return 0;
  82 }
  83 
  84 static const struct irq_domain_ops mvebu_pic_domain_ops = {
  85         .map = mvebu_pic_irq_map,
  86         .xlate = irq_domain_xlate_onecell,
  87 };
  88 
  89 static void mvebu_pic_handle_cascade_irq(struct irq_desc *desc)
  90 {
  91         struct mvebu_pic *pic = irq_desc_get_handler_data(desc);
  92         struct irq_chip *chip = irq_desc_get_chip(desc);
  93         unsigned long irqmap, irqn;
  94         unsigned int cascade_irq;
  95 
  96         irqmap = readl_relaxed(pic->base + PIC_CAUSE);
  97         chained_irq_enter(chip, desc);
  98 
  99         for_each_set_bit(irqn, &irqmap, BITS_PER_LONG) {
 100                 cascade_irq = irq_find_mapping(pic->domain, irqn);
 101                 generic_handle_irq(cascade_irq);
 102         }
 103 
 104         chained_irq_exit(chip, desc);
 105 }
 106 
 107 static void mvebu_pic_enable_percpu_irq(void *data)
 108 {
 109         struct mvebu_pic *pic = data;
 110 
 111         mvebu_pic_reset(pic);
 112         enable_percpu_irq(pic->parent_irq, IRQ_TYPE_NONE);
 113 }
 114 
 115 static void mvebu_pic_disable_percpu_irq(void *data)
 116 {
 117         struct mvebu_pic *pic = data;
 118 
 119         disable_percpu_irq(pic->parent_irq);
 120 }
 121 
 122 static int mvebu_pic_probe(struct platform_device *pdev)
 123 {
 124         struct device_node *node = pdev->dev.of_node;
 125         struct mvebu_pic *pic;
 126         struct irq_chip *irq_chip;
 127         struct resource *res;
 128 
 129         pic = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_pic), GFP_KERNEL);
 130         if (!pic)
 131                 return -ENOMEM;
 132 
 133         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 134         pic->base = devm_ioremap_resource(&pdev->dev, res);
 135         if (IS_ERR(pic->base))
 136                 return PTR_ERR(pic->base);
 137 
 138         irq_chip = &pic->irq_chip;
 139         irq_chip->name = dev_name(&pdev->dev);
 140         irq_chip->irq_mask = mvebu_pic_mask_irq;
 141         irq_chip->irq_unmask = mvebu_pic_unmask_irq;
 142         irq_chip->irq_eoi = mvebu_pic_eoi_irq;
 143 
 144         pic->parent_irq = irq_of_parse_and_map(node, 0);
 145         if (pic->parent_irq <= 0) {
 146                 dev_err(&pdev->dev, "Failed to parse parent interrupt\n");
 147                 return -EINVAL;
 148         }
 149 
 150         pic->domain = irq_domain_add_linear(node, PIC_MAX_IRQS,
 151                                             &mvebu_pic_domain_ops, pic);
 152         if (!pic->domain) {
 153                 dev_err(&pdev->dev, "Failed to allocate irq domain\n");
 154                 return -ENOMEM;
 155         }
 156 
 157         irq_set_chained_handler(pic->parent_irq, mvebu_pic_handle_cascade_irq);
 158         irq_set_handler_data(pic->parent_irq, pic);
 159 
 160         on_each_cpu(mvebu_pic_enable_percpu_irq, pic, 1);
 161 
 162         platform_set_drvdata(pdev, pic);
 163 
 164         return 0;
 165 }
 166 
 167 static int mvebu_pic_remove(struct platform_device *pdev)
 168 {
 169         struct mvebu_pic *pic = platform_get_drvdata(pdev);
 170 
 171         on_each_cpu(mvebu_pic_disable_percpu_irq, pic, 1);
 172         irq_domain_remove(pic->domain);
 173 
 174         return 0;
 175 }
 176 
 177 static const struct of_device_id mvebu_pic_of_match[] = {
 178         { .compatible = "marvell,armada-8k-pic", },
 179         {},
 180 };
 181 MODULE_DEVICE_TABLE(of, mvebu_pic_of_match);
 182 
 183 static struct platform_driver mvebu_pic_driver = {
 184         .probe  = mvebu_pic_probe,
 185         .remove = mvebu_pic_remove,
 186         .driver = {
 187                 .name = "mvebu-pic",
 188                 .of_match_table = mvebu_pic_of_match,
 189         },
 190 };
 191 module_platform_driver(mvebu_pic_driver);
 192 
 193 MODULE_AUTHOR("Yehuda Yitschak <yehuday@marvell.com>");
 194 MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>");
 195 MODULE_LICENSE("GPL v2");
 196 MODULE_ALIAS("platform:mvebu_pic");
 197 

/* [<][>][^][v][top][bottom][index][help] */