]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - arch/arm/mach-pxa/pxa_cplds_irqs.c
UBUNTU: Ubuntu-4.15.0-96.97
[mirror_ubuntu-bionic-kernel.git] / arch / arm / mach-pxa / pxa_cplds_irqs.c
CommitLineData
aa8d6b73
RJ
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
30struct 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
38static 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
9ba63e3c
RJ
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);
aa8d6b73
RJ
51
52 return IRQ_HANDLED;
53}
54
9ba63e3c 55static void cplds_irq_mask(struct irq_data *d)
aa8d6b73
RJ
56{
57 struct cplds *fpga = irq_data_get_irq_chip_data(d);
58 unsigned int cplds_irq = irqd_to_hwirq(d);
9ba63e3c 59 unsigned int bit = BIT(cplds_irq);
aa8d6b73
RJ
60
61 fpga->irq_mask &= ~bit;
62 writel(fpga->irq_mask, fpga->base + FPGA_IRQ_MASK_EN);
aa8d6b73
RJ
63}
64
65static 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);
9ba63e3c
RJ
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);
aa8d6b73
RJ
73
74 fpga->irq_mask |= bit;
75 writel(fpga->irq_mask, fpga->base + FPGA_IRQ_MASK_EN);
76}
77
78static struct irq_chip cplds_irq_chip = {
79 .name = "pxa_cplds",
9ba63e3c
RJ
80 .irq_ack = cplds_irq_mask,
81 .irq_mask = cplds_irq_mask,
aa8d6b73
RJ
82 .irq_unmask = cplds_irq_unmask,
83 .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE,
84};
85
86static 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
97static const struct irq_domain_ops cplds_irq_domain_ops = {
98 .xlate = irq_domain_xlate_twocell,
99 .map = cplds_irq_domain_map,
100};
101
102static 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
111static int cplds_probe(struct platform_device *pdev)
112{
113 struct resource *res;
114 struct cplds *fpga;
115 int ret;
bd7413ae 116 int base_irq;
aa8d6b73
RJ
117 unsigned long irqflags = 0;
118
119 fpga = devm_kzalloc(&pdev->dev, sizeof(*fpga), GFP_KERNEL);
120 if (!fpga)
121 return -ENOMEM;
122
3738ca1b
RJ
123 fpga->irq = platform_get_irq(pdev, 0);
124 if (fpga->irq <= 0)
125 return fpga->irq;
aa8d6b73
RJ
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
3738ca1b 141 irqflags = irq_get_trigger_type(fpga->irq);
aa8d6b73
RJ
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
173static 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
182static const struct of_device_id cplds_id_table[] = {
183 { .compatible = "intel,lubbock-cplds-irqs", },
184 { .compatible = "intel,mainstone-cplds-irqs", },
185 { }
186};
187MODULE_DEVICE_TABLE(of, cplds_id_table);
188
189static 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
199module_platform_driver(cplds_driver);
200
201MODULE_DESCRIPTION("PXA Cplds interrupts driver");
202MODULE_AUTHOR("Robert Jarzmik <robert.jarzmik@free.fr>");
203MODULE_LICENSE("GPL");