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