linux/arch/arm/mach-pxa/pxa_cplds_irqs.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Intel Reference Systems cplds
   4 *
   5 * Copyright (C) 2014 Robert Jarzmik
   6 *
   7 * Cplds motherboard driver, supporting lubbock and mainstone SoC board.
   8 */
   9
  10#include <linux/bitops.h>
  11#include <linux/gpio.h>
  12#include <linux/gpio/consumer.h>
  13#include <linux/interrupt.h>
  14#include <linux/io.h>
  15#include <linux/irq.h>
  16#include <linux/irqdomain.h>
  17#include <linux/mfd/core.h>
  18#include <linux/module.h>
  19#include <linux/of_platform.h>
  20
  21#define FPGA_IRQ_MASK_EN 0x0
  22#define FPGA_IRQ_SET_CLR 0x10
  23
  24#define CPLDS_NB_IRQ    32
  25
  26struct cplds {
  27        void __iomem *base;
  28        int irq;
  29        unsigned int irq_mask;
  30        struct gpio_desc *gpio0;
  31        struct irq_domain *irqdomain;
  32};
  33
  34static irqreturn_t cplds_irq_handler(int in_irq, void *d)
  35{
  36        struct cplds *fpga = d;
  37        unsigned long pending;
  38        unsigned int bit;
  39
  40        do {
  41                pending = readl(fpga->base + FPGA_IRQ_SET_CLR) & fpga->irq_mask;
  42                for_each_set_bit(bit, &pending, CPLDS_NB_IRQ) {
  43                        generic_handle_irq(irq_find_mapping(fpga->irqdomain,
  44                                                            bit));
  45                }
  46        } while (pending);
  47
  48        return IRQ_HANDLED;
  49}
  50
  51static void cplds_irq_mask(struct irq_data *d)
  52{
  53        struct cplds *fpga = irq_data_get_irq_chip_data(d);
  54        unsigned int cplds_irq = irqd_to_hwirq(d);
  55        unsigned int bit = BIT(cplds_irq);
  56
  57        fpga->irq_mask &= ~bit;
  58        writel(fpga->irq_mask, fpga->base + FPGA_IRQ_MASK_EN);
  59}
  60
  61static void cplds_irq_unmask(struct irq_data *d)
  62{
  63        struct cplds *fpga = irq_data_get_irq_chip_data(d);
  64        unsigned int cplds_irq = irqd_to_hwirq(d);
  65        unsigned int set, bit = BIT(cplds_irq);
  66
  67        set = readl(fpga->base + FPGA_IRQ_SET_CLR);
  68        writel(set & ~bit, fpga->base + FPGA_IRQ_SET_CLR);
  69
  70        fpga->irq_mask |= bit;
  71        writel(fpga->irq_mask, fpga->base + FPGA_IRQ_MASK_EN);
  72}
  73
  74static struct irq_chip cplds_irq_chip = {
  75        .name           = "pxa_cplds",
  76        .irq_ack        = cplds_irq_mask,
  77        .irq_mask       = cplds_irq_mask,
  78        .irq_unmask     = cplds_irq_unmask,
  79        .flags          = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE,
  80};
  81
  82static int cplds_irq_domain_map(struct irq_domain *d, unsigned int irq,
  83                                   irq_hw_number_t hwirq)
  84{
  85        struct cplds *fpga = d->host_data;
  86
  87        irq_set_chip_and_handler(irq, &cplds_irq_chip, handle_level_irq);
  88        irq_set_chip_data(irq, fpga);
  89
  90        return 0;
  91}
  92
  93static const struct irq_domain_ops cplds_irq_domain_ops = {
  94        .xlate = irq_domain_xlate_twocell,
  95        .map = cplds_irq_domain_map,
  96};
  97
  98static int cplds_resume(struct platform_device *pdev)
  99{
 100        struct cplds *fpga = platform_get_drvdata(pdev);
 101
 102        writel(fpga->irq_mask, fpga->base + FPGA_IRQ_MASK_EN);
 103
 104        return 0;
 105}
 106
 107static int cplds_probe(struct platform_device *pdev)
 108{
 109        struct resource *res;
 110        struct cplds *fpga;
 111        int ret;
 112        int base_irq;
 113        unsigned long irqflags = 0;
 114
 115        fpga = devm_kzalloc(&pdev->dev, sizeof(*fpga), GFP_KERNEL);
 116        if (!fpga)
 117                return -ENOMEM;
 118
 119        fpga->irq = platform_get_irq(pdev, 0);
 120        if (fpga->irq <= 0)
 121                return fpga->irq;
 122
 123        base_irq = platform_get_irq(pdev, 1);
 124        if (base_irq < 0)
 125                base_irq = 0;
 126
 127        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 128        fpga->base = devm_ioremap_resource(&pdev->dev, res);
 129        if (IS_ERR(fpga->base))
 130                return PTR_ERR(fpga->base);
 131
 132        platform_set_drvdata(pdev, fpga);
 133
 134        writel(fpga->irq_mask, fpga->base + FPGA_IRQ_MASK_EN);
 135        writel(0, fpga->base + FPGA_IRQ_SET_CLR);
 136
 137        irqflags = irq_get_trigger_type(fpga->irq);
 138        ret = devm_request_irq(&pdev->dev, fpga->irq, cplds_irq_handler,
 139                               irqflags, dev_name(&pdev->dev), fpga);
 140        if (ret == -ENOSYS)
 141                return -EPROBE_DEFER;
 142
 143        if (ret) {
 144                dev_err(&pdev->dev, "couldn't request main irq%d: %d\n",
 145                        fpga->irq, ret);
 146                return ret;
 147        }
 148
 149        irq_set_irq_wake(fpga->irq, 1);
 150        fpga->irqdomain = irq_domain_add_linear(pdev->dev.of_node,
 151                                               CPLDS_NB_IRQ,
 152                                               &cplds_irq_domain_ops, fpga);
 153        if (!fpga->irqdomain)
 154                return -ENODEV;
 155
 156        if (base_irq) {
 157                ret = irq_create_strict_mappings(fpga->irqdomain, base_irq, 0,
 158                                                 CPLDS_NB_IRQ);
 159                if (ret) {
 160                        dev_err(&pdev->dev, "couldn't create the irq mapping %d..%d\n",
 161                                base_irq, base_irq + CPLDS_NB_IRQ);
 162                        return ret;
 163                }
 164        }
 165
 166        return 0;
 167}
 168
 169static int cplds_remove(struct platform_device *pdev)
 170{
 171        struct cplds *fpga = platform_get_drvdata(pdev);
 172
 173        irq_set_chip_and_handler(fpga->irq, NULL, NULL);
 174
 175        return 0;
 176}
 177
 178static const struct of_device_id cplds_id_table[] = {
 179        { .compatible = "intel,lubbock-cplds-irqs", },
 180        { .compatible = "intel,mainstone-cplds-irqs", },
 181        { }
 182};
 183MODULE_DEVICE_TABLE(of, cplds_id_table);
 184
 185static struct platform_driver cplds_driver = {
 186        .driver         = {
 187                .name   = "pxa_cplds_irqs",
 188                .of_match_table = of_match_ptr(cplds_id_table),
 189        },
 190        .probe          = cplds_probe,
 191        .remove         = cplds_remove,
 192        .resume         = cplds_resume,
 193};
 194
 195module_platform_driver(cplds_driver);
 196
 197MODULE_DESCRIPTION("PXA Cplds interrupts driver");
 198MODULE_AUTHOR("Robert Jarzmik <robert.jarzmik@free.fr>");
 199MODULE_LICENSE("GPL");
 200