diff options
author | Robert Jarzmik <robert.jarzmik@free.fr> | 2015-04-24 16:22:19 -0400 |
---|---|---|
committer | Robert Jarzmik <robert.jarzmik@free.fr> | 2015-04-30 16:52:12 -0400 |
commit | aa8d6b73ea33c2167c543663ab66039ec94d58f1 (patch) | |
tree | 0ccedd60d6a7e71aa4e1c38742f65f6e269545ff /arch | |
parent | b787f68c36d49bb1d9236f403813641efa74a031 (diff) |
ARM: pxa: pxa_cplds: add lubbock and mainstone IO
Historically, this support was in arch/arm/mach-pxa/lubbock.c and
arch/arm/mach-pxa/mainstone.c. When gpio-pxa was moved to drivers/pxa,
it became a driver, and its initialization and probing happened at
postcore initcall. The lubbock code used to install the chained lubbock
interrupt handler at init_irq() time.
The consequence of the gpio-pxa change is that the installed chained irq
handler lubbock_irq_handler() was overwritten in pxa_gpio_probe(_dt)(),
removing :
- the handler
- the falling edge detection setting of GPIO0, which revealed the
interrupt request from the lubbock IO board.
As a fix, move the gpio0 chained handler setup to a place where we have
the guarantee that pxa_gpio_probe() was called before, so that lubbock
handler becomes the true IRQ chained handler of GPIO0, demuxing the
lubbock IO board interrupts.
This patch moves all that handling to a mfd driver. It's only purpose
for the time being is the interrupt handling, but in the future it
should encompass all the motherboard CPLDs handling :
- leds
- switches
- hexleds
The same logic applies to mainstone board.
Fixes: 157d2644cb0c ("ARM: pxa: change gpio to platform device")
Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mach-pxa/Kconfig | 9 | ||||
-rw-r--r-- | arch/arm/mach-pxa/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/mach-pxa/pxa_cplds_irqs.c | 200 |
3 files changed, 210 insertions, 0 deletions
diff --git a/arch/arm/mach-pxa/Kconfig b/arch/arm/mach-pxa/Kconfig index 8896e71586f5..f09683687963 100644 --- a/arch/arm/mach-pxa/Kconfig +++ b/arch/arm/mach-pxa/Kconfig | |||
@@ -691,4 +691,13 @@ config SHARPSL_PM_MAX1111 | |||
691 | config PXA310_ULPI | 691 | config PXA310_ULPI |
692 | bool | 692 | bool |
693 | 693 | ||
694 | config PXA_SYSTEMS_CPLDS | ||
695 | tristate "Motherboard cplds" | ||
696 | default ARCH_LUBBOCK || MACH_MAINSTONE | ||
697 | help | ||
698 | This driver supports the Lubbock and Mainstone multifunction chip | ||
699 | found on the pxa25x development platform system (Lubbock) and pxa27x | ||
700 | development platform system (Mainstone). This IO board supports the | ||
701 | interrupts handling, ethernet controller, flash chips, etc ... | ||
702 | |||
694 | endif | 703 | endif |
diff --git a/arch/arm/mach-pxa/Makefile b/arch/arm/mach-pxa/Makefile index eb0bf7678a99..4087d334ecdf 100644 --- a/arch/arm/mach-pxa/Makefile +++ b/arch/arm/mach-pxa/Makefile | |||
@@ -90,4 +90,5 @@ obj-$(CONFIG_MACH_RAUMFELD_CONNECTOR) += raumfeld.o | |||
90 | obj-$(CONFIG_MACH_RAUMFELD_SPEAKER) += raumfeld.o | 90 | obj-$(CONFIG_MACH_RAUMFELD_SPEAKER) += raumfeld.o |
91 | obj-$(CONFIG_MACH_ZIPIT2) += z2.o | 91 | obj-$(CONFIG_MACH_ZIPIT2) += z2.o |
92 | 92 | ||
93 | obj-$(CONFIG_PXA_SYSTEMS_CPLDS) += pxa_cplds_irqs.o | ||
93 | obj-$(CONFIG_TOSA_BT) += tosa-bt.o | 94 | obj-$(CONFIG_TOSA_BT) += tosa-bt.o |
diff --git a/arch/arm/mach-pxa/pxa_cplds_irqs.c b/arch/arm/mach-pxa/pxa_cplds_irqs.c new file mode 100644 index 000000000000..f1aeb54fabe3 --- /dev/null +++ b/arch/arm/mach-pxa/pxa_cplds_irqs.c | |||
@@ -0,0 +1,200 @@ | |||
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 | pending = readl(fpga->base + FPGA_IRQ_SET_CLR) & fpga->irq_mask; | ||
45 | for_each_set_bit(bit, &pending, CPLDS_NB_IRQ) | ||
46 | generic_handle_irq(irq_find_mapping(fpga->irqdomain, bit)); | ||
47 | |||
48 | return IRQ_HANDLED; | ||
49 | } | ||
50 | |||
51 | static void cplds_irq_mask_ack(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 set, bit = BIT(cplds_irq); | ||
56 | |||
57 | fpga->irq_mask &= ~bit; | ||
58 | writel(fpga->irq_mask, fpga->base + FPGA_IRQ_MASK_EN); | ||
59 | set = readl(fpga->base + FPGA_IRQ_SET_CLR); | ||
60 | writel(set & ~bit, fpga->base + FPGA_IRQ_SET_CLR); | ||
61 | } | ||
62 | |||
63 | static void cplds_irq_unmask(struct irq_data *d) | ||
64 | { | ||
65 | struct cplds *fpga = irq_data_get_irq_chip_data(d); | ||
66 | unsigned int cplds_irq = irqd_to_hwirq(d); | ||
67 | unsigned int bit = BIT(cplds_irq); | ||
68 | |||
69 | fpga->irq_mask |= bit; | ||
70 | writel(fpga->irq_mask, fpga->base + FPGA_IRQ_MASK_EN); | ||
71 | } | ||
72 | |||
73 | static struct irq_chip cplds_irq_chip = { | ||
74 | .name = "pxa_cplds", | ||
75 | .irq_mask_ack = cplds_irq_mask_ack, | ||
76 | .irq_unmask = cplds_irq_unmask, | ||
77 | .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE, | ||
78 | }; | ||
79 | |||
80 | static int cplds_irq_domain_map(struct irq_domain *d, unsigned int irq, | ||
81 | irq_hw_number_t hwirq) | ||
82 | { | ||
83 | struct cplds *fpga = d->host_data; | ||
84 | |||
85 | irq_set_chip_and_handler(irq, &cplds_irq_chip, handle_level_irq); | ||
86 | irq_set_chip_data(irq, fpga); | ||
87 | |||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | static const struct irq_domain_ops cplds_irq_domain_ops = { | ||
92 | .xlate = irq_domain_xlate_twocell, | ||
93 | .map = cplds_irq_domain_map, | ||
94 | }; | ||
95 | |||
96 | static int cplds_resume(struct platform_device *pdev) | ||
97 | { | ||
98 | struct cplds *fpga = platform_get_drvdata(pdev); | ||
99 | |||
100 | writel(fpga->irq_mask, fpga->base + FPGA_IRQ_MASK_EN); | ||
101 | |||
102 | return 0; | ||
103 | } | ||
104 | |||
105 | static int cplds_probe(struct platform_device *pdev) | ||
106 | { | ||
107 | struct resource *res; | ||
108 | struct cplds *fpga; | ||
109 | int ret; | ||
110 | unsigned int base_irq = 0; | ||
111 | unsigned long irqflags = 0; | ||
112 | |||
113 | fpga = devm_kzalloc(&pdev->dev, sizeof(*fpga), GFP_KERNEL); | ||
114 | if (!fpga) | ||
115 | return -ENOMEM; | ||
116 | |||
117 | res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | ||
118 | if (res) { | ||
119 | fpga->irq = (unsigned int)res->start; | ||
120 | irqflags = res->flags; | ||
121 | } | ||
122 | if (!fpga->irq) | ||
123 | return -ENODEV; | ||
124 | |||
125 | base_irq = platform_get_irq(pdev, 1); | ||
126 | if (base_irq < 0) | ||
127 | base_irq = 0; | ||
128 | |||
129 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
130 | fpga->base = devm_ioremap_resource(&pdev->dev, res); | ||
131 | if (IS_ERR(fpga->base)) | ||
132 | return PTR_ERR(fpga->base); | ||
133 | |||
134 | platform_set_drvdata(pdev, fpga); | ||
135 | |||
136 | writel(fpga->irq_mask, fpga->base + FPGA_IRQ_MASK_EN); | ||
137 | writel(0, fpga->base + FPGA_IRQ_SET_CLR); | ||
138 | |||
139 | ret = devm_request_irq(&pdev->dev, fpga->irq, cplds_irq_handler, | ||
140 | irqflags, dev_name(&pdev->dev), fpga); | ||
141 | if (ret == -ENOSYS) | ||
142 | return -EPROBE_DEFER; | ||
143 | |||
144 | if (ret) { | ||
145 | dev_err(&pdev->dev, "couldn't request main irq%d: %d\n", | ||
146 | fpga->irq, ret); | ||
147 | return ret; | ||
148 | } | ||
149 | |||
150 | irq_set_irq_wake(fpga->irq, 1); | ||
151 | fpga->irqdomain = irq_domain_add_linear(pdev->dev.of_node, | ||
152 | CPLDS_NB_IRQ, | ||
153 | &cplds_irq_domain_ops, fpga); | ||
154 | if (!fpga->irqdomain) | ||
155 | return -ENODEV; | ||
156 | |||
157 | if (base_irq) { | ||
158 | ret = irq_create_strict_mappings(fpga->irqdomain, base_irq, 0, | ||
159 | CPLDS_NB_IRQ); | ||
160 | if (ret) { | ||
161 | dev_err(&pdev->dev, "couldn't create the irq mapping %d..%d\n", | ||
162 | base_irq, base_irq + CPLDS_NB_IRQ); | ||
163 | return ret; | ||
164 | } | ||
165 | } | ||
166 | |||
167 | return 0; | ||
168 | } | ||
169 | |||
170 | static int cplds_remove(struct platform_device *pdev) | ||
171 | { | ||
172 | struct cplds *fpga = platform_get_drvdata(pdev); | ||
173 | |||
174 | irq_set_chip_and_handler(fpga->irq, NULL, NULL); | ||
175 | |||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | static const struct of_device_id cplds_id_table[] = { | ||
180 | { .compatible = "intel,lubbock-cplds-irqs", }, | ||
181 | { .compatible = "intel,mainstone-cplds-irqs", }, | ||
182 | { } | ||
183 | }; | ||
184 | MODULE_DEVICE_TABLE(of, cplds_id_table); | ||
185 | |||
186 | static struct platform_driver cplds_driver = { | ||
187 | .driver = { | ||
188 | .name = "pxa_cplds_irqs", | ||
189 | .of_match_table = of_match_ptr(cplds_id_table), | ||
190 | }, | ||
191 | .probe = cplds_probe, | ||
192 | .remove = cplds_remove, | ||
193 | .resume = cplds_resume, | ||
194 | }; | ||
195 | |||
196 | module_platform_driver(cplds_driver); | ||
197 | |||
198 | MODULE_DESCRIPTION("PXA Cplds interrupts driver"); | ||
199 | MODULE_AUTHOR("Robert Jarzmik <robert.jarzmik@free.fr>"); | ||
200 | MODULE_LICENSE("GPL"); | ||