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