Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Intel Reference Systems cplds
0004  *
0005  * Copyright (C) 2014 Robert Jarzmik
0006  *
0007  * Cplds motherboard driver, supporting lubbock and mainstone SoC board.
0008  */
0009 
0010 #include <linux/bitops.h>
0011 #include <linux/gpio.h>
0012 #include <linux/gpio/consumer.h>
0013 #include <linux/interrupt.h>
0014 #include <linux/io.h>
0015 #include <linux/irq.h>
0016 #include <linux/irqdomain.h>
0017 #include <linux/mfd/core.h>
0018 #include <linux/module.h>
0019 #include <linux/of_platform.h>
0020 
0021 #define FPGA_IRQ_MASK_EN 0x0
0022 #define FPGA_IRQ_SET_CLR 0x10
0023 
0024 #define CPLDS_NB_IRQ    32
0025 
0026 struct cplds {
0027     void __iomem *base;
0028     int irq;
0029     unsigned int irq_mask;
0030     struct gpio_desc *gpio0;
0031     struct irq_domain *irqdomain;
0032 };
0033 
0034 static irqreturn_t cplds_irq_handler(int in_irq, void *d)
0035 {
0036     struct cplds *fpga = d;
0037     unsigned long pending;
0038     unsigned int bit;
0039 
0040     do {
0041         pending = readl(fpga->base + FPGA_IRQ_SET_CLR) & fpga->irq_mask;
0042         for_each_set_bit(bit, &pending, CPLDS_NB_IRQ)
0043             generic_handle_domain_irq(fpga->irqdomain, bit);
0044     } while (pending);
0045 
0046     return IRQ_HANDLED;
0047 }
0048 
0049 static void cplds_irq_mask(struct irq_data *d)
0050 {
0051     struct cplds *fpga = irq_data_get_irq_chip_data(d);
0052     unsigned int cplds_irq = irqd_to_hwirq(d);
0053     unsigned int bit = BIT(cplds_irq);
0054 
0055     fpga->irq_mask &= ~bit;
0056     writel(fpga->irq_mask, fpga->base + FPGA_IRQ_MASK_EN);
0057 }
0058 
0059 static void cplds_irq_unmask(struct irq_data *d)
0060 {
0061     struct cplds *fpga = irq_data_get_irq_chip_data(d);
0062     unsigned int cplds_irq = irqd_to_hwirq(d);
0063     unsigned int set, bit = BIT(cplds_irq);
0064 
0065     set = readl(fpga->base + FPGA_IRQ_SET_CLR);
0066     writel(set & ~bit, fpga->base + FPGA_IRQ_SET_CLR);
0067 
0068     fpga->irq_mask |= bit;
0069     writel(fpga->irq_mask, fpga->base + FPGA_IRQ_MASK_EN);
0070 }
0071 
0072 static struct irq_chip cplds_irq_chip = {
0073     .name       = "pxa_cplds",
0074     .irq_ack    = cplds_irq_mask,
0075     .irq_mask   = cplds_irq_mask,
0076     .irq_unmask = cplds_irq_unmask,
0077     .flags      = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE,
0078 };
0079 
0080 static int cplds_irq_domain_map(struct irq_domain *d, unsigned int irq,
0081                    irq_hw_number_t hwirq)
0082 {
0083     struct cplds *fpga = d->host_data;
0084 
0085     irq_set_chip_and_handler(irq, &cplds_irq_chip, handle_level_irq);
0086     irq_set_chip_data(irq, fpga);
0087 
0088     return 0;
0089 }
0090 
0091 static const struct irq_domain_ops cplds_irq_domain_ops = {
0092     .xlate = irq_domain_xlate_twocell,
0093     .map = cplds_irq_domain_map,
0094 };
0095 
0096 static int cplds_resume(struct platform_device *pdev)
0097 {
0098     struct cplds *fpga = platform_get_drvdata(pdev);
0099 
0100     writel(fpga->irq_mask, fpga->base + FPGA_IRQ_MASK_EN);
0101 
0102     return 0;
0103 }
0104 
0105 static int cplds_probe(struct platform_device *pdev)
0106 {
0107     struct resource *res;
0108     struct cplds *fpga;
0109     int ret;
0110     int base_irq;
0111     unsigned long irqflags = 0;
0112 
0113     fpga = devm_kzalloc(&pdev->dev, sizeof(*fpga), GFP_KERNEL);
0114     if (!fpga)
0115         return -ENOMEM;
0116 
0117     fpga->irq = platform_get_irq(pdev, 0);
0118     if (fpga->irq <= 0)
0119         return fpga->irq;
0120 
0121     base_irq = platform_get_irq(pdev, 1);
0122     if (base_irq < 0) {
0123         base_irq = 0;
0124     } else {
0125         ret = devm_irq_alloc_descs(&pdev->dev, base_irq, base_irq, CPLDS_NB_IRQ, 0);
0126         if (ret < 0)
0127             return ret;
0128     }
0129 
0130     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0131     fpga->base = devm_ioremap_resource(&pdev->dev, res);
0132     if (IS_ERR(fpga->base))
0133         return PTR_ERR(fpga->base);
0134 
0135     platform_set_drvdata(pdev, fpga);
0136 
0137     writel(fpga->irq_mask, fpga->base + FPGA_IRQ_MASK_EN);
0138     writel(0, fpga->base + FPGA_IRQ_SET_CLR);
0139 
0140     irqflags = irq_get_trigger_type(fpga->irq);
0141     ret = devm_request_irq(&pdev->dev, fpga->irq, cplds_irq_handler,
0142                    irqflags, dev_name(&pdev->dev), fpga);
0143     if (ret == -ENOSYS)
0144         return -EPROBE_DEFER;
0145 
0146     if (ret) {
0147         dev_err(&pdev->dev, "couldn't request main irq%d: %d\n",
0148             fpga->irq, ret);
0149         return ret;
0150     }
0151 
0152     irq_set_irq_wake(fpga->irq, 1);
0153     if (base_irq)
0154         fpga->irqdomain = irq_domain_add_legacy(pdev->dev.of_node,
0155                             CPLDS_NB_IRQ,
0156                             base_irq, 0,
0157                             &cplds_irq_domain_ops,
0158                             fpga);
0159     else
0160         fpga->irqdomain = irq_domain_add_linear(pdev->dev.of_node,
0161                             CPLDS_NB_IRQ,
0162                             &cplds_irq_domain_ops,
0163                             fpga);
0164     if (!fpga->irqdomain)
0165         return -ENODEV;
0166 
0167     return 0;
0168 }
0169 
0170 static int cplds_remove(struct platform_device *pdev)
0171 {
0172     struct cplds *fpga = platform_get_drvdata(pdev);
0173 
0174     irq_set_chip_and_handler(fpga->irq, NULL, NULL);
0175 
0176     return 0;
0177 }
0178 
0179 static const struct of_device_id cplds_id_table[] = {
0180     { .compatible = "intel,lubbock-cplds-irqs", },
0181     { .compatible = "intel,mainstone-cplds-irqs", },
0182     { }
0183 };
0184 MODULE_DEVICE_TABLE(of, cplds_id_table);
0185 
0186 static struct platform_driver cplds_driver = {
0187     .driver     = {
0188         .name   = "pxa_cplds_irqs",
0189         .of_match_table = of_match_ptr(cplds_id_table),
0190     },
0191     .probe      = cplds_probe,
0192     .remove     = cplds_remove,
0193     .resume     = cplds_resume,
0194 };
0195 
0196 module_platform_driver(cplds_driver);
0197 
0198 MODULE_DESCRIPTION("PXA Cplds interrupts driver");
0199 MODULE_AUTHOR("Robert Jarzmik <robert.jarzmik@free.fr>");
0200 MODULE_LICENSE("GPL");