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        } else {
 127                ret = devm_irq_alloc_descs(&pdev->dev, base_irq, base_irq, CPLDS_NB_IRQ, 0);
 128                if (ret < 0)
 129                        return ret;
 130        }
 131
 132        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 133        fpga->base = devm_ioremap_resource(&pdev->dev, res);
 134        if (IS_ERR(fpga->base))
 135                return PTR_ERR(fpga->base);
 136
 137        platform_set_drvdata(pdev, fpga);
 138
 139        writel(fpga->irq_mask, fpga->base + FPGA_IRQ_MASK_EN);
 140        writel(0, fpga->base + FPGA_IRQ_SET_CLR);
 141
 142        irqflags = irq_get_trigger_type(fpga->irq);
 143        ret = devm_request_irq(&pdev->dev, fpga->irq, cplds_irq_handler,
 144                               irqflags, dev_name(&pdev->dev), fpga);
 145        if (ret == -ENOSYS)
 146                return -EPROBE_DEFER;
 147
 148        if (ret) {
 149                dev_err(&pdev->dev, "couldn't request main irq%d: %d\n",
 150                        fpga->irq, ret);
 151                return ret;
 152        }
 153
 154        irq_set_irq_wake(fpga->irq, 1);
 155        if (base_irq)
 156                fpga->irqdomain = irq_domain_add_legacy(pdev->dev.of_node,
 157                                                        CPLDS_NB_IRQ,
 158                                                        base_irq, 0,
 159                                                        &cplds_irq_domain_ops,
 160                                                        fpga);
 161        else
 162                fpga->irqdomain = irq_domain_add_linear(pdev->dev.of_node,
 163                                                        CPLDS_NB_IRQ,
 164                                                        &cplds_irq_domain_ops,
 165                                                        fpga);
 166        if (!fpga->irqdomain)
 167                return -ENODEV;
 168
 169        return 0;
 170}
 171
 172static int cplds_remove(struct platform_device *pdev)
 173{
 174        struct cplds *fpga = platform_get_drvdata(pdev);
 175
 176        irq_set_chip_and_handler(fpga->irq, NULL, NULL);
 177
 178        return 0;
 179}
 180
 181static const struct of_device_id cplds_id_table[] = {
 182        { .compatible = "intel,lubbock-cplds-irqs", },
 183        { .compatible = "intel,mainstone-cplds-irqs", },
 184        { }
 185};
 186MODULE_DEVICE_TABLE(of, cplds_id_table);
 187
 188static struct platform_driver cplds_driver = {
 189        .driver         = {
 190                .name   = "pxa_cplds_irqs",
 191                .of_match_table = of_match_ptr(cplds_id_table),
 192        },
 193        .probe          = cplds_probe,
 194        .remove         = cplds_remove,
 195        .resume         = cplds_resume,
 196};
 197
 198module_platform_driver(cplds_driver);
 199
 200MODULE_DESCRIPTION("PXA Cplds interrupts driver");
 201MODULE_AUTHOR("Robert Jarzmik <robert.jarzmik@free.fr>");
 202MODULE_LICENSE("GPL");
 203