Skip to content

Commit 5ddabfe

Browse files
vzapolskiylinusw
authored andcommitted
gpio: lpc18xx: add GPIO pin interrupt controller support
The change adds support of LPC18xx/LPC43xx GPIO pin interrupt controller block within SoC GPIO controller. The new interrupt controller driver allows to configure and capture edge or level interrupts on 8 arbitrary selectedinput GPIO pins, and lift the signals to be reported as NVIC rising edge interrupts. Configuration of a particular GPIO pin to serve as interrupt and its mapping to an interrupt on NVIC is done by SCU pin controller, for more details see description of 'nxp,gpio-pin-interrupt' device tree property of a GPIO pin [1]. From LPC18xx and LPC43xx User Manuals the GPIO controller consists of the following blocks: * GPIO pin interrupt block at 0x40087000, this change adds its support, * GPIO GROUP0 interrupt block at 0x40088000, * GPIO GROUP1 interrupt block at 0x40089000, * GPIO port block at 0x400F4000, it is supported by the original driver. While all 4 sub-controller blocks have their own I/O addresses, moreover all 3 interrupt blocks are APB0 peripherals and high-speed GPIO block is an AHB slave, according to the hardware manual the GPIO controller is seen as a single block, and 4 sub-controllers have the shared reset signal RGU #28 and clock to register interface CLK_CPU_GPIO on CCU1. Likely support of two GPIO group interrupt blocks won't be added in short term, because the mechanism to mask several interrupt sources is not well defined. [1] Documentation/devicetree/bindings/pinctrl/nxp,lpc1850-scu.txt Signed-off-by: Vladimir Zapolskiy <[email protected]> Signed-off-by: Linus Walleij <[email protected]>
1 parent 985d8d5 commit 5ddabfe

File tree

2 files changed

+264
-4
lines changed

2 files changed

+264
-4
lines changed

drivers/gpio/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ config GPIO_LPC18XX
288288
tristate "NXP LPC18XX/43XX GPIO support"
289289
default y if ARCH_LPC18XX
290290
depends on OF_GPIO && (ARCH_LPC18XX || COMPILE_TEST)
291+
select IRQ_DOMAIN_HIERARCHY
291292
help
292293
Select this option to enable GPIO driver for
293294
NXP LPC18XX/43XX devices.

drivers/gpio/gpio-lpc18xx.c

Lines changed: 263 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,21 @@
22
/*
33
* GPIO driver for NXP LPC18xx/43xx.
44
*
5+
* Copyright (C) 2018 Vladimir Zapolskiy <[email protected]>
56
* Copyright (C) 2015 Joachim Eastwood <[email protected]>
67
*
78
*/
89

910
#include <linux/clk.h>
1011
#include <linux/gpio/driver.h>
1112
#include <linux/io.h>
13+
#include <linux/irqdomain.h>
14+
#include <linux/irqchip.h>
1215
#include <linux/module.h>
1316
#include <linux/of.h>
17+
#include <linux/of_address.h>
1418
#include <linux/of_gpio.h>
19+
#include <linux/of_irq.h>
1520
#include <linux/pinctrl/consumer.h>
1621
#include <linux/platform_device.h>
1722

@@ -21,13 +26,247 @@
2126
#define LPC18XX_MAX_PORTS 8
2227
#define LPC18XX_PINS_PER_PORT 32
2328

29+
/* LPC18xx GPIO pin interrupt controller register offsets */
30+
#define LPC18XX_GPIO_PIN_IC_ISEL 0x00
31+
#define LPC18XX_GPIO_PIN_IC_IENR 0x04
32+
#define LPC18XX_GPIO_PIN_IC_SIENR 0x08
33+
#define LPC18XX_GPIO_PIN_IC_CIENR 0x0c
34+
#define LPC18XX_GPIO_PIN_IC_IENF 0x10
35+
#define LPC18XX_GPIO_PIN_IC_SIENF 0x14
36+
#define LPC18XX_GPIO_PIN_IC_CIENF 0x18
37+
#define LPC18XX_GPIO_PIN_IC_RISE 0x1c
38+
#define LPC18XX_GPIO_PIN_IC_FALL 0x20
39+
#define LPC18XX_GPIO_PIN_IC_IST 0x24
40+
41+
#define NR_LPC18XX_GPIO_PIN_IC_IRQS 8
42+
43+
struct lpc18xx_gpio_pin_ic {
44+
void __iomem *base;
45+
struct irq_domain *domain;
46+
struct raw_spinlock lock;
47+
};
48+
2449
struct lpc18xx_gpio_chip {
2550
struct gpio_chip gpio;
2651
void __iomem *base;
2752
struct clk *clk;
53+
struct lpc18xx_gpio_pin_ic *pin_ic;
2854
spinlock_t lock;
2955
};
3056

57+
static inline void lpc18xx_gpio_pin_ic_isel(struct lpc18xx_gpio_pin_ic *ic,
58+
u32 pin, bool set)
59+
{
60+
u32 val = readl_relaxed(ic->base + LPC18XX_GPIO_PIN_IC_ISEL);
61+
62+
if (set)
63+
val &= ~BIT(pin);
64+
else
65+
val |= BIT(pin);
66+
67+
writel_relaxed(val, ic->base + LPC18XX_GPIO_PIN_IC_ISEL);
68+
}
69+
70+
static inline void lpc18xx_gpio_pin_ic_set(struct lpc18xx_gpio_pin_ic *ic,
71+
u32 pin, u32 reg)
72+
{
73+
writel_relaxed(BIT(pin), ic->base + reg);
74+
}
75+
76+
static void lpc18xx_gpio_pin_ic_mask(struct irq_data *d)
77+
{
78+
struct lpc18xx_gpio_pin_ic *ic = d->chip_data;
79+
u32 type = irqd_get_trigger_type(d);
80+
81+
raw_spin_lock(&ic->lock);
82+
83+
if (type & IRQ_TYPE_LEVEL_MASK || type & IRQ_TYPE_EDGE_RISING)
84+
lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
85+
LPC18XX_GPIO_PIN_IC_CIENR);
86+
87+
if (type & IRQ_TYPE_EDGE_FALLING)
88+
lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
89+
LPC18XX_GPIO_PIN_IC_CIENF);
90+
91+
raw_spin_unlock(&ic->lock);
92+
93+
irq_chip_mask_parent(d);
94+
}
95+
96+
static void lpc18xx_gpio_pin_ic_unmask(struct irq_data *d)
97+
{
98+
struct lpc18xx_gpio_pin_ic *ic = d->chip_data;
99+
u32 type = irqd_get_trigger_type(d);
100+
101+
raw_spin_lock(&ic->lock);
102+
103+
if (type & IRQ_TYPE_LEVEL_MASK || type & IRQ_TYPE_EDGE_RISING)
104+
lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
105+
LPC18XX_GPIO_PIN_IC_SIENR);
106+
107+
if (type & IRQ_TYPE_EDGE_FALLING)
108+
lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
109+
LPC18XX_GPIO_PIN_IC_SIENF);
110+
111+
raw_spin_unlock(&ic->lock);
112+
113+
irq_chip_unmask_parent(d);
114+
}
115+
116+
static void lpc18xx_gpio_pin_ic_eoi(struct irq_data *d)
117+
{
118+
struct lpc18xx_gpio_pin_ic *ic = d->chip_data;
119+
u32 type = irqd_get_trigger_type(d);
120+
121+
raw_spin_lock(&ic->lock);
122+
123+
if (type & IRQ_TYPE_EDGE_BOTH)
124+
lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
125+
LPC18XX_GPIO_PIN_IC_IST);
126+
127+
raw_spin_unlock(&ic->lock);
128+
129+
irq_chip_eoi_parent(d);
130+
}
131+
132+
static int lpc18xx_gpio_pin_ic_set_type(struct irq_data *d, unsigned int type)
133+
{
134+
struct lpc18xx_gpio_pin_ic *ic = d->chip_data;
135+
136+
raw_spin_lock(&ic->lock);
137+
138+
if (type & IRQ_TYPE_LEVEL_HIGH) {
139+
lpc18xx_gpio_pin_ic_isel(ic, d->hwirq, true);
140+
lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
141+
LPC18XX_GPIO_PIN_IC_SIENF);
142+
} else if (type & IRQ_TYPE_LEVEL_LOW) {
143+
lpc18xx_gpio_pin_ic_isel(ic, d->hwirq, true);
144+
lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
145+
LPC18XX_GPIO_PIN_IC_CIENF);
146+
} else {
147+
lpc18xx_gpio_pin_ic_isel(ic, d->hwirq, false);
148+
}
149+
150+
raw_spin_unlock(&ic->lock);
151+
152+
return 0;
153+
}
154+
155+
static struct irq_chip lpc18xx_gpio_pin_ic = {
156+
.name = "LPC18xx GPIO pin",
157+
.irq_mask = lpc18xx_gpio_pin_ic_mask,
158+
.irq_unmask = lpc18xx_gpio_pin_ic_unmask,
159+
.irq_eoi = lpc18xx_gpio_pin_ic_eoi,
160+
.irq_set_type = lpc18xx_gpio_pin_ic_set_type,
161+
.irq_retrigger = irq_chip_retrigger_hierarchy,
162+
.flags = IRQCHIP_SET_TYPE_MASKED,
163+
};
164+
165+
static int lpc18xx_gpio_pin_ic_domain_alloc(struct irq_domain *domain,
166+
unsigned int virq,
167+
unsigned int nr_irqs, void *data)
168+
{
169+
struct irq_fwspec parent_fwspec, *fwspec = data;
170+
struct lpc18xx_gpio_pin_ic *ic = domain->host_data;
171+
irq_hw_number_t hwirq;
172+
int ret;
173+
174+
if (nr_irqs != 1)
175+
return -EINVAL;
176+
177+
hwirq = fwspec->param[0];
178+
if (hwirq >= NR_LPC18XX_GPIO_PIN_IC_IRQS)
179+
return -EINVAL;
180+
181+
/*
182+
* All LPC18xx/LPC43xx GPIO pin hardware interrupts are translated
183+
* into edge interrupts 32...39 on parent Cortex-M3/M4 NVIC
184+
*/
185+
parent_fwspec.fwnode = domain->parent->fwnode;
186+
parent_fwspec.param_count = 1;
187+
parent_fwspec.param[0] = hwirq + 32;
188+
189+
ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &parent_fwspec);
190+
if (ret < 0) {
191+
pr_err("failed to allocate parent irq %u: %d\n",
192+
parent_fwspec.param[0], ret);
193+
return ret;
194+
}
195+
196+
return irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
197+
&lpc18xx_gpio_pin_ic, ic);
198+
}
199+
200+
static const struct irq_domain_ops lpc18xx_gpio_pin_ic_domain_ops = {
201+
.alloc = lpc18xx_gpio_pin_ic_domain_alloc,
202+
.xlate = irq_domain_xlate_twocell,
203+
.free = irq_domain_free_irqs_common,
204+
};
205+
206+
static int lpc18xx_gpio_pin_ic_probe(struct lpc18xx_gpio_chip *gc)
207+
{
208+
struct device *dev = gc->gpio.parent;
209+
struct irq_domain *parent_domain;
210+
struct device_node *parent_node;
211+
struct lpc18xx_gpio_pin_ic *ic;
212+
struct resource res;
213+
int ret, index;
214+
215+
parent_node = of_irq_find_parent(dev->of_node);
216+
if (!parent_node)
217+
return -ENXIO;
218+
219+
parent_domain = irq_find_host(parent_node);
220+
of_node_put(parent_node);
221+
if (!parent_domain)
222+
return -ENXIO;
223+
224+
ic = devm_kzalloc(dev, sizeof(*ic), GFP_KERNEL);
225+
if (!ic)
226+
return -ENOMEM;
227+
228+
index = of_property_match_string(dev->of_node, "reg-names",
229+
"gpio-pin-ic");
230+
if (index < 0) {
231+
ret = -ENODEV;
232+
goto free_ic;
233+
}
234+
235+
ret = of_address_to_resource(dev->of_node, index, &res);
236+
if (ret < 0)
237+
goto free_ic;
238+
239+
ic->base = devm_ioremap_resource(dev, &res);
240+
if (IS_ERR(ic->base)) {
241+
ret = PTR_ERR(ic->base);
242+
goto free_ic;
243+
}
244+
245+
raw_spin_lock_init(&ic->lock);
246+
247+
ic->domain = irq_domain_add_hierarchy(parent_domain, 0,
248+
NR_LPC18XX_GPIO_PIN_IC_IRQS,
249+
dev->of_node,
250+
&lpc18xx_gpio_pin_ic_domain_ops,
251+
ic);
252+
if (!ic->domain) {
253+
pr_err("unable to add irq domain\n");
254+
ret = -ENODEV;
255+
goto free_iomap;
256+
}
257+
258+
gc->pin_ic = ic;
259+
260+
return 0;
261+
262+
free_iomap:
263+
devm_iounmap(dev, ic->base);
264+
free_ic:
265+
devm_kfree(dev, ic);
266+
267+
return ret;
268+
}
269+
31270
static void lpc18xx_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
32271
{
33272
struct lpc18xx_gpio_chip *gc = gpiochip_get_data(chip);
@@ -91,8 +330,7 @@ static int lpc18xx_gpio_probe(struct platform_device *pdev)
91330
{
92331
struct device *dev = &pdev->dev;
93332
struct lpc18xx_gpio_chip *gc;
94-
struct resource *res;
95-
int ret;
333+
int index, ret;
96334

97335
gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL);
98336
if (!gc)
@@ -101,8 +339,22 @@ static int lpc18xx_gpio_probe(struct platform_device *pdev)
101339
gc->gpio = lpc18xx_chip;
102340
platform_set_drvdata(pdev, gc);
103341

104-
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
105-
gc->base = devm_ioremap_resource(dev, res);
342+
index = of_property_match_string(dev->of_node, "reg-names", "gpio");
343+
if (index < 0) {
344+
/* To support backward compatibility take the first resource */
345+
struct resource *res;
346+
347+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
348+
gc->base = devm_ioremap_resource(dev, res);
349+
} else {
350+
struct resource res;
351+
352+
ret = of_address_to_resource(dev->of_node, index, &res);
353+
if (ret < 0)
354+
return ret;
355+
356+
gc->base = devm_ioremap_resource(dev, &res);
357+
}
106358
if (IS_ERR(gc->base))
107359
return PTR_ERR(gc->base);
108360

@@ -129,13 +381,19 @@ static int lpc18xx_gpio_probe(struct platform_device *pdev)
129381
return ret;
130382
}
131383

384+
/* On error GPIO pin interrupt controller just won't be registered */
385+
lpc18xx_gpio_pin_ic_probe(gc);
386+
132387
return 0;
133388
}
134389

135390
static int lpc18xx_gpio_remove(struct platform_device *pdev)
136391
{
137392
struct lpc18xx_gpio_chip *gc = platform_get_drvdata(pdev);
138393

394+
if (gc->pin_ic)
395+
irq_domain_remove(gc->pin_ic->domain);
396+
139397
clk_disable_unprepare(gc->clk);
140398

141399
return 0;
@@ -158,5 +416,6 @@ static struct platform_driver lpc18xx_gpio_driver = {
158416
module_platform_driver(lpc18xx_gpio_driver);
159417

160418
MODULE_AUTHOR("Joachim Eastwood <[email protected]>");
419+
MODULE_AUTHOR("Vladimir Zapolskiy <[email protected]>");
161420
MODULE_DESCRIPTION("GPIO driver for LPC18xx/43xx");
162421
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)