linux/drivers/irqchip/irq-mvebu-pic.c
<<
>>
Prefs
   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
  28struct mvebu_pic {
  29        void __iomem *base;
  30        u32 parent_irq;
  31        struct irq_domain *domain;
  32        struct irq_chip irq_chip;
  33};
  34
  35static 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
  42static 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
  49static 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
  59static 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
  69static 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
  84static const struct irq_domain_ops mvebu_pic_domain_ops = {
  85        .map = mvebu_pic_irq_map,
  86        .xlate = irq_domain_xlate_onecell,
  87};
  88
  89static 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
 107static 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
 115static 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
 122static 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
 167static 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
 177static const struct of_device_id mvebu_pic_of_match[] = {
 178        { .compatible = "marvell,armada-8k-pic", },
 179        {},
 180};
 181MODULE_DEVICE_TABLE(of, mvebu_pic_of_match);
 182
 183static 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};
 191module_platform_driver(mvebu_pic_driver);
 192
 193MODULE_AUTHOR("Yehuda Yitschak <yehuday@marvell.com>");
 194MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>");
 195MODULE_LICENSE("GPL v2");
 196MODULE_ALIAS("platform:mvebu_pic");
 197
 198