All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] gpio: lpc18xx: add GPIO pin interrupt controller support
@ 2018-11-28 22:48 ` Vladimir Zapolskiy
  0 siblings, 0 replies; 10+ messages in thread
From: Vladimir Zapolskiy @ 2018-11-28 22:48 UTC (permalink / raw)
  To: Rob Herring, Linus Walleij, Marc Zyngier
  Cc: devicetree, linux-arm-kernel, linux-gpio

>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 the blocks are relatively independant and potentially they can
be defined under their own device tree nodes, all blocks share the
same clock to enable access to register interfaces and the same reset
signal.

The change extends the existing gpio@400f4000 device tree node described
in arch/arm/boot/dts/lpc18xx.dtsi by adding 'reg-names' property to

	gpio: gpio@400f4000 {
		compatible = "nxp,lpc1850-gpio";
		reg = <0x400f4000 0x4000>, <0x40087000 0x1000>,
		      <0x40088000 0x1000>, <0x40089000 0x1000>;
		reg-names = "gpio", "gpio-pin-ic",
			    "gpio-group0-ic", "gpio-gpoup1-ic";
		clocks = <&ccu1 CLK_CPU_GPIO>;
		resets = <&rgu 28>;
		gpio-controller;
		#gpio-cells = <2>;
		interrupt-controller;
		#interrupt-cells = <2>;
	};

Note that all 4 blocks find their place, but the functional change adds
support of the GPIO pin interrupt contoller only.

Alternative and probably better representation of the GPIO pin interrupt
contoller block can be like this one added in parallel to gpio@400f4000 {}:

	gpio_pin: interrupt-controller@40087000 {
		compatible = "nxp,lpc1850-gpio-pin-ic";
		reg = <0x40087000 0x1000>;
		clocks = <&ccu1 CLK_CPU_GPIO>;
		resets = <&rgu 28>;
		interrupt-controller;
		#interrupt-cells = <2>;
	};

The unpleasant side effects associated with this representation are:

1) both gpio@400f4000 and interrupt-controller@40087000 (plus potential
   interrupt-controller@40088000 and interrupt-controller@40089000) share
   the same reset and the same clock,
2) separate device node and 'compatible' imply a new drivers/irqchip/
   driver, it is totally okay, but
3) while Linux device drivers provide interfaces and support well usage
   of the same reset or clock, interrupt controller drivers does not
   depend on clock providers, of_clk_get() will ask for defer, but
   IRQCHIP_DECLARE()d initialization won't be reprobed,
4) it might be more or less reliably assumed that GPIO pin interrupt
   controller does not get any consumer in runtime, who is not the
   consumer of the same GPIO which serves as an interrupt, so implicitly
   access to the interrupt controller functions will happen after
   the registration of the GPIO controller driver, which in turn enables
   the shared clock, and thus access to the interrupt controller registers
   will be successful then, but you see, the complexity and indirection
   leads to unavoidable fragility.

So, I go ahead with the extension of the exisitng GPIO controller device
node, comments and other opinions are welcome as usual.

The change depends on a minor clean-up series, which was sent aforehand:
* https://marc.info/?l=linux-gpio&m=154344413726424&w=1

The actual lpc18xx.dtsi change will be sent afterwards after getting
the acceptance of the proposed device tree node changes.

Marc, this is the change, which I had a luck to discuss with you on ELCE.

Vladimir Zapolskiy (2):
  dt-bindings: gpio: lpc18xx: describe interrupt controllers of GPIO controller
  gpio: lpc18xx: add GPIO pin interrupt controller support

 .../bindings/gpio/nxp,lpc1850-gpio.txt        |  38 ++-
 drivers/gpio/Kconfig                          |   1 +
 drivers/gpio/gpio-lpc18xx.c                   | 267 +++++++++++++++++-
 3 files changed, 293 insertions(+), 13 deletions(-)

-- 
2.17.1

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 0/2] gpio: lpc18xx: add GPIO pin interrupt controller support
@ 2018-11-28 22:48 ` Vladimir Zapolskiy
  0 siblings, 0 replies; 10+ messages in thread
From: Vladimir Zapolskiy @ 2018-11-28 22:48 UTC (permalink / raw)
  To: linux-arm-kernel

>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 the blocks are relatively independant and potentially they can
be defined under their own device tree nodes, all blocks share the
same clock to enable access to register interfaces and the same reset
signal.

The change extends the existing gpio at 400f4000 device tree node described
in arch/arm/boot/dts/lpc18xx.dtsi by adding 'reg-names' property to

	gpio: gpio at 400f4000 {
		compatible = "nxp,lpc1850-gpio";
		reg = <0x400f4000 0x4000>, <0x40087000 0x1000>,
		      <0x40088000 0x1000>, <0x40089000 0x1000>;
		reg-names = "gpio", "gpio-pin-ic",
			    "gpio-group0-ic", "gpio-gpoup1-ic";
		clocks = <&ccu1 CLK_CPU_GPIO>;
		resets = <&rgu 28>;
		gpio-controller;
		#gpio-cells = <2>;
		interrupt-controller;
		#interrupt-cells = <2>;
	};

Note that all 4 blocks find their place, but the functional change adds
support of the GPIO pin interrupt contoller only.

Alternative and probably better representation of the GPIO pin interrupt
contoller block can be like this one added in parallel to gpio at 400f4000 {}:

	gpio_pin: interrupt-controller at 40087000 {
		compatible = "nxp,lpc1850-gpio-pin-ic";
		reg = <0x40087000 0x1000>;
		clocks = <&ccu1 CLK_CPU_GPIO>;
		resets = <&rgu 28>;
		interrupt-controller;
		#interrupt-cells = <2>;
	};

The unpleasant side effects associated with this representation are:

1) both gpio at 400f4000 and interrupt-controller at 40087000 (plus potential
   interrupt-controller at 40088000 and interrupt-controller at 40089000) share
   the same reset and the same clock,
2) separate device node and 'compatible' imply a new drivers/irqchip/
   driver, it is totally okay, but
3) while Linux device drivers provide interfaces and support well usage
   of the same reset or clock, interrupt controller drivers does not
   depend on clock providers, of_clk_get() will ask for defer, but
   IRQCHIP_DECLARE()d initialization won't be reprobed,
4) it might be more or less reliably assumed that GPIO pin interrupt
   controller does not get any consumer in runtime, who is not the
   consumer of the same GPIO which serves as an interrupt, so implicitly
   access to the interrupt controller functions will happen after
   the registration of the GPIO controller driver, which in turn enables
   the shared clock, and thus access to the interrupt controller registers
   will be successful then, but you see, the complexity and indirection
   leads to unavoidable fragility.

So, I go ahead with the extension of the exisitng GPIO controller device
node, comments and other opinions are welcome as usual.

The change depends on a minor clean-up series, which was sent aforehand:
* https://marc.info/?l=linux-gpio&m=154344413726424&w=1

The actual lpc18xx.dtsi change will be sent afterwards after getting
the acceptance of the proposed device tree node changes.

Marc, this is the change, which I had a luck to discuss with you on ELCE.

Vladimir Zapolskiy (2):
  dt-bindings: gpio: lpc18xx: describe interrupt controllers of GPIO controller
  gpio: lpc18xx: add GPIO pin interrupt controller support

 .../bindings/gpio/nxp,lpc1850-gpio.txt        |  38 ++-
 drivers/gpio/Kconfig                          |   1 +
 drivers/gpio/gpio-lpc18xx.c                   | 267 +++++++++++++++++-
 3 files changed, 293 insertions(+), 13 deletions(-)

-- 
2.17.1

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 1/2] dt-bindings: gpio: lpc18xx: describe interrupt controllers of GPIO controller
  2018-11-28 22:48 ` Vladimir Zapolskiy
@ 2018-11-28 22:48   ` Vladimir Zapolskiy
  -1 siblings, 0 replies; 10+ messages in thread
From: Vladimir Zapolskiy @ 2018-11-28 22:48 UTC (permalink / raw)
  To: Rob Herring, Linus Walleij, Marc Zyngier
  Cc: devicetree, linux-arm-kernel, linux-gpio

>From LPC18xx and LPC43xx User Manuals the GPIO controller consists of
the following weakly connected blocks:
* GPIO pin interrupt block at 0x40087000,
* GPIO GROUP0 interrupt block at 0x40088000,
* GPIO GROUP1 interrupt block at 0x40089000,
* GPIO port block at 0x400F4000.

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 interrupt controllers and
GPIO controller block are seen as a single device, all 4 sub-controllers
have the shared reset signal RGU #28 and the same shared clock to access
registers CLK_Mx_GPIO on CCU1.

The change adds descriptions of the currently missing interrupt controller
blocks found on GPIO controller, new added properties are 'reg-names',
'resets', 'interrupt-controller' and '#interrupt-cells', also the example
is updated to reflect the changes in device tree binding description.

Signed-off-by: Vladimir Zapolskiy <vz@mleia.com>
---
 .../bindings/gpio/nxp,lpc1850-gpio.txt        | 38 ++++++++++++++-----
 1 file changed, 29 insertions(+), 9 deletions(-)

diff --git a/Documentation/devicetree/bindings/gpio/nxp,lpc1850-gpio.txt b/Documentation/devicetree/bindings/gpio/nxp,lpc1850-gpio.txt
index eb7cdd69e10b..627efc78ecf2 100644
--- a/Documentation/devicetree/bindings/gpio/nxp,lpc1850-gpio.txt
+++ b/Documentation/devicetree/bindings/gpio/nxp,lpc1850-gpio.txt
@@ -3,12 +3,24 @@ NXP LPC18xx/43xx GPIO controller Device Tree Bindings
 
 Required properties:
 - compatible		: Should be "nxp,lpc1850-gpio"
-- reg			: Address and length of the register set for the device
-- clocks		: Clock specifier (see clock bindings for details)
-- gpio-controller	: Marks the device node as a GPIO controller.
-- #gpio-cells 		: Should be two
-			  - First cell is the GPIO line number
-			  - Second cell is used to specify polarity
+- reg			: List of addresses and lengths of the GPIO controller
+			  register sets
+- reg-names		: Should be "gpio", "gpio-pin-ic", "gpio-group0-ic" and
+			  "gpio-gpoup1-ic"
+- clocks		: Phandle and clock specifier pair for GPIO controller
+- resets		: Phandle and reset specifier pair for GPIO controller
+- gpio-controller	: Marks the device node as a GPIO controller
+- #gpio-cells 		: Should be two:
+			  - The first cell is the GPIO line number
+			  - The second cell is used to specify polarity
+- interrupt-controller	: Marks the device node as an interrupt controller
+- #interrupt-cells	: Should be two:
+			  - The first cell is an interrupt number within
+			    0..9 range, for GPIO pin interrupts it is equal
+			    to 'nxp,gpio-pin-interrupt' property value of
+			    GPIO pin configuration, 8 is for GPIO GROUP0
+			    interrupt, 9 is for GPIO GROUP1 interrupt
+			  - The second cell is used to specify interrupt type
 
 Optional properties:
 - gpio-ranges		: Mapping between GPIO and pinctrl
@@ -19,21 +31,29 @@ Example:
 
 gpio: gpio@400f4000 {
 	compatible = "nxp,lpc1850-gpio";
-	reg = <0x400f4000 0x4000>;
+	reg = <0x400f4000 0x4000>, <0x40087000 0x1000>,
+	      <0x40088000 0x1000>, <0x40089000 0x1000>;
+	reg-names = "gpio", "gpio-pin-ic",
+		    "gpio-group0-ic", "gpio-gpoup1-ic";
 	clocks = <&ccu1 CLK_CPU_GPIO>;
+	resets = <&rgu 28>;
 	gpio-controller;
 	#gpio-cells = <2>;
+	interrupt-controller;
+	#interrupt-cells = <2>;
 	gpio-ranges =	<&pinctrl LPC_GPIO(0,0)  LPC_PIN(0,0)  2>,
 			...
 			<&pinctrl LPC_GPIO(7,19) LPC_PIN(f,5)  7>;
 };
 
 gpio_joystick {
-	compatible = "gpio-keys-polled";
+	compatible = "gpio-keys";
 	...
 
-	button@0 {
+	button0 {
 		...
+		interrupt-parent = <&gpio>;
+		interrupts = <1 IRQ_TYPE_EDGE_BOTH>;
 		gpios = <&gpio LPC_GPIO(4,8) GPIO_ACTIVE_LOW>;
 	};
 };
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 1/2] dt-bindings: gpio: lpc18xx: describe interrupt controllers of GPIO controller
@ 2018-11-28 22:48   ` Vladimir Zapolskiy
  0 siblings, 0 replies; 10+ messages in thread
From: Vladimir Zapolskiy @ 2018-11-28 22:48 UTC (permalink / raw)
  To: linux-arm-kernel

>From LPC18xx and LPC43xx User Manuals the GPIO controller consists of
the following weakly connected blocks:
* GPIO pin interrupt block at 0x40087000,
* GPIO GROUP0 interrupt block at 0x40088000,
* GPIO GROUP1 interrupt block at 0x40089000,
* GPIO port block at 0x400F4000.

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 interrupt controllers and
GPIO controller block are seen as a single device, all 4 sub-controllers
have the shared reset signal RGU #28 and the same shared clock to access
registers CLK_Mx_GPIO on CCU1.

The change adds descriptions of the currently missing interrupt controller
blocks found on GPIO controller, new added properties are 'reg-names',
'resets', 'interrupt-controller' and '#interrupt-cells', also the example
is updated to reflect the changes in device tree binding description.

Signed-off-by: Vladimir Zapolskiy <vz@mleia.com>
---
 .../bindings/gpio/nxp,lpc1850-gpio.txt        | 38 ++++++++++++++-----
 1 file changed, 29 insertions(+), 9 deletions(-)

diff --git a/Documentation/devicetree/bindings/gpio/nxp,lpc1850-gpio.txt b/Documentation/devicetree/bindings/gpio/nxp,lpc1850-gpio.txt
index eb7cdd69e10b..627efc78ecf2 100644
--- a/Documentation/devicetree/bindings/gpio/nxp,lpc1850-gpio.txt
+++ b/Documentation/devicetree/bindings/gpio/nxp,lpc1850-gpio.txt
@@ -3,12 +3,24 @@ NXP LPC18xx/43xx GPIO controller Device Tree Bindings
 
 Required properties:
 - compatible		: Should be "nxp,lpc1850-gpio"
-- reg			: Address and length of the register set for the device
-- clocks		: Clock specifier (see clock bindings for details)
-- gpio-controller	: Marks the device node as a GPIO controller.
-- #gpio-cells 		: Should be two
-			  - First cell is the GPIO line number
-			  - Second cell is used to specify polarity
+- reg			: List of addresses and lengths of the GPIO controller
+			  register sets
+- reg-names		: Should be "gpio", "gpio-pin-ic", "gpio-group0-ic" and
+			  "gpio-gpoup1-ic"
+- clocks		: Phandle and clock specifier pair for GPIO controller
+- resets		: Phandle and reset specifier pair for GPIO controller
+- gpio-controller	: Marks the device node as a GPIO controller
+- #gpio-cells 		: Should be two:
+			  - The first cell is the GPIO line number
+			  - The second cell is used to specify polarity
+- interrupt-controller	: Marks the device node as an interrupt controller
+- #interrupt-cells	: Should be two:
+			  - The first cell is an interrupt number within
+			    0..9 range, for GPIO pin interrupts it is equal
+			    to 'nxp,gpio-pin-interrupt' property value of
+			    GPIO pin configuration, 8 is for GPIO GROUP0
+			    interrupt, 9 is for GPIO GROUP1 interrupt
+			  - The second cell is used to specify interrupt type
 
 Optional properties:
 - gpio-ranges		: Mapping between GPIO and pinctrl
@@ -19,21 +31,29 @@ Example:
 
 gpio: gpio at 400f4000 {
 	compatible = "nxp,lpc1850-gpio";
-	reg = <0x400f4000 0x4000>;
+	reg = <0x400f4000 0x4000>, <0x40087000 0x1000>,
+	      <0x40088000 0x1000>, <0x40089000 0x1000>;
+	reg-names = "gpio", "gpio-pin-ic",
+		    "gpio-group0-ic", "gpio-gpoup1-ic";
 	clocks = <&ccu1 CLK_CPU_GPIO>;
+	resets = <&rgu 28>;
 	gpio-controller;
 	#gpio-cells = <2>;
+	interrupt-controller;
+	#interrupt-cells = <2>;
 	gpio-ranges =	<&pinctrl LPC_GPIO(0,0)  LPC_PIN(0,0)  2>,
 			...
 			<&pinctrl LPC_GPIO(7,19) LPC_PIN(f,5)  7>;
 };
 
 gpio_joystick {
-	compatible = "gpio-keys-polled";
+	compatible = "gpio-keys";
 	...
 
-	button at 0 {
+	button0 {
 		...
+		interrupt-parent = <&gpio>;
+		interrupts = <1 IRQ_TYPE_EDGE_BOTH>;
 		gpios = <&gpio LPC_GPIO(4,8) GPIO_ACTIVE_LOW>;
 	};
 };
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 2/2] gpio: lpc18xx: add GPIO pin interrupt controller support
  2018-11-28 22:48 ` Vladimir Zapolskiy
@ 2018-11-28 22:48   ` Vladimir Zapolskiy
  -1 siblings, 0 replies; 10+ messages in thread
From: Vladimir Zapolskiy @ 2018-11-28 22:48 UTC (permalink / raw)
  To: Rob Herring, Linus Walleij, Marc Zyngier
  Cc: devicetree, linux-arm-kernel, linux-gpio

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 <vz@mleia.com>
---
 drivers/gpio/Kconfig        |   1 +
 drivers/gpio/gpio-lpc18xx.c | 267 +++++++++++++++++++++++++++++++++++-
 2 files changed, 264 insertions(+), 4 deletions(-)

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 833a1b51c948..a1876c39a7b7 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -288,6 +288,7 @@ config GPIO_LPC18XX
 	tristate "NXP LPC18XX/43XX GPIO support"
 	default y if ARCH_LPC18XX
 	depends on OF_GPIO && (ARCH_LPC18XX || COMPILE_TEST)
+	select IRQ_DOMAIN_HIERARCHY
 	help
 	  Select this option to enable GPIO driver for
 	  NXP LPC18XX/43XX devices.
diff --git a/drivers/gpio/gpio-lpc18xx.c b/drivers/gpio/gpio-lpc18xx.c
index dc33185a89b1..040fb59d06f7 100644
--- a/drivers/gpio/gpio-lpc18xx.c
+++ b/drivers/gpio/gpio-lpc18xx.c
@@ -2,6 +2,7 @@
 /*
  * GPIO driver for NXP LPC18xx/43xx.
  *
+ * Copyright (C) 2018 Vladimir Zapolskiy <vz@mleia.com>
  * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
  *
  */
@@ -9,9 +10,13 @@
 #include <linux/clk.h>
 #include <linux/gpio/driver.h>
 #include <linux/io.h>
+#include <linux/irqdomain.h>
+#include <linux/irqchip.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/of_address.h>
 #include <linux/of_gpio.h>
+#include <linux/of_irq.h>
 #include <linux/pinctrl/consumer.h>
 #include <linux/platform_device.h>
 
@@ -21,13 +26,247 @@
 #define LPC18XX_MAX_PORTS	8
 #define LPC18XX_PINS_PER_PORT	32
 
+/* LPC18xx GPIO pin interrupt controller register offsets */
+#define LPC18XX_GPIO_PIN_IC_ISEL	0x00
+#define LPC18XX_GPIO_PIN_IC_IENR	0x04
+#define LPC18XX_GPIO_PIN_IC_SIENR	0x08
+#define LPC18XX_GPIO_PIN_IC_CIENR	0x0c
+#define LPC18XX_GPIO_PIN_IC_IENF	0x10
+#define LPC18XX_GPIO_PIN_IC_SIENF	0x14
+#define LPC18XX_GPIO_PIN_IC_CIENF	0x18
+#define LPC18XX_GPIO_PIN_IC_RISE	0x1c
+#define LPC18XX_GPIO_PIN_IC_FALL	0x20
+#define LPC18XX_GPIO_PIN_IC_IST		0x24
+
+#define NR_LPC18XX_GPIO_PIN_IC_IRQS	8
+
+struct lpc18xx_gpio_pin_ic {
+	void __iomem *base;
+	struct irq_domain *domain;
+	struct raw_spinlock lock;
+};
+
 struct lpc18xx_gpio_chip {
 	struct gpio_chip gpio;
 	void __iomem *base;
 	struct clk *clk;
+	struct lpc18xx_gpio_pin_ic *pin_ic;
 	spinlock_t lock;
 };
 
+static inline void lpc18xx_gpio_pin_ic_isel(struct lpc18xx_gpio_pin_ic *ic,
+					    u32 pin, bool set)
+{
+	u32 val = readl_relaxed(ic->base + LPC18XX_GPIO_PIN_IC_ISEL);
+
+	if (set)
+		val &= ~BIT(pin);
+	else
+		val |= BIT(pin);
+
+	writel_relaxed(val, ic->base + LPC18XX_GPIO_PIN_IC_ISEL);
+}
+
+static inline void lpc18xx_gpio_pin_ic_set(struct lpc18xx_gpio_pin_ic *ic,
+					   u32 pin, u32 reg)
+{
+	writel_relaxed(BIT(pin), ic->base + reg);
+}
+
+static void lpc18xx_gpio_pin_ic_mask(struct irq_data *d)
+{
+	struct lpc18xx_gpio_pin_ic *ic = d->chip_data;
+	u32 type = irqd_get_trigger_type(d);
+
+	raw_spin_lock(&ic->lock);
+
+	if (type & IRQ_TYPE_LEVEL_MASK || type & IRQ_TYPE_EDGE_RISING)
+		lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
+					LPC18XX_GPIO_PIN_IC_CIENR);
+
+	if (type & IRQ_TYPE_EDGE_FALLING)
+		lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
+					LPC18XX_GPIO_PIN_IC_CIENF);
+
+	raw_spin_unlock(&ic->lock);
+
+	irq_chip_mask_parent(d);
+}
+
+static void lpc18xx_gpio_pin_ic_unmask(struct irq_data *d)
+{
+	struct lpc18xx_gpio_pin_ic *ic = d->chip_data;
+	u32 type = irqd_get_trigger_type(d);
+
+	raw_spin_lock(&ic->lock);
+
+	if (type & IRQ_TYPE_LEVEL_MASK || type & IRQ_TYPE_EDGE_RISING)
+		lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
+					LPC18XX_GPIO_PIN_IC_SIENR);
+
+	if (type & IRQ_TYPE_EDGE_FALLING)
+		lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
+					LPC18XX_GPIO_PIN_IC_SIENF);
+
+	raw_spin_unlock(&ic->lock);
+
+	irq_chip_unmask_parent(d);
+}
+
+static void lpc18xx_gpio_pin_ic_eoi(struct irq_data *d)
+{
+	struct lpc18xx_gpio_pin_ic *ic = d->chip_data;
+	u32 type = irqd_get_trigger_type(d);
+
+	raw_spin_lock(&ic->lock);
+
+	if (type & IRQ_TYPE_EDGE_BOTH)
+		lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
+					LPC18XX_GPIO_PIN_IC_IST);
+
+	raw_spin_unlock(&ic->lock);
+
+	irq_chip_eoi_parent(d);
+}
+
+static int lpc18xx_gpio_pin_ic_set_type(struct irq_data *d, unsigned int type)
+{
+	struct lpc18xx_gpio_pin_ic *ic = d->chip_data;
+
+	raw_spin_lock(&ic->lock);
+
+	if (type & IRQ_TYPE_LEVEL_HIGH) {
+		lpc18xx_gpio_pin_ic_isel(ic, d->hwirq, true);
+		lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
+					LPC18XX_GPIO_PIN_IC_SIENF);
+	} else if (type & IRQ_TYPE_LEVEL_LOW) {
+		lpc18xx_gpio_pin_ic_isel(ic, d->hwirq, true);
+		lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
+					LPC18XX_GPIO_PIN_IC_CIENF);
+	} else {
+		lpc18xx_gpio_pin_ic_isel(ic, d->hwirq, false);
+	}
+
+	raw_spin_unlock(&ic->lock);
+
+	return 0;
+}
+
+static struct irq_chip lpc18xx_gpio_pin_ic = {
+	.name		= "LPC18xx GPIO pin",
+	.irq_mask	= lpc18xx_gpio_pin_ic_mask,
+	.irq_unmask	= lpc18xx_gpio_pin_ic_unmask,
+	.irq_eoi	= lpc18xx_gpio_pin_ic_eoi,
+	.irq_set_type	= lpc18xx_gpio_pin_ic_set_type,
+	.irq_retrigger	= irq_chip_retrigger_hierarchy,
+	.flags		= IRQCHIP_SET_TYPE_MASKED,
+};
+
+static int lpc18xx_gpio_pin_ic_domain_alloc(struct irq_domain *domain,
+					    unsigned int virq,
+					    unsigned int nr_irqs, void *data)
+{
+	struct irq_fwspec parent_fwspec, *fwspec = data;
+	struct lpc18xx_gpio_pin_ic *ic = domain->host_data;
+	irq_hw_number_t hwirq;
+	int ret;
+
+	if (nr_irqs != 1)
+		return -EINVAL;
+
+	hwirq = fwspec->param[0];
+	if (hwirq >= NR_LPC18XX_GPIO_PIN_IC_IRQS)
+		return -EINVAL;
+
+	/*
+	 * All LPC18xx/LPC43xx GPIO pin hardware interrupts are translated
+	 * into edge interrupts 32...39 on parent Cortex-M3/M4 NVIC
+	 */
+	parent_fwspec.fwnode = domain->parent->fwnode;
+	parent_fwspec.param_count = 1;
+	parent_fwspec.param[0] = hwirq + 32;
+
+	ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &parent_fwspec);
+	if (ret < 0) {
+		pr_err("failed to allocate parent irq %u: %d\n",
+		       parent_fwspec.param[0], ret);
+		return ret;
+	}
+
+	return irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
+					     &lpc18xx_gpio_pin_ic, ic);
+}
+
+static const struct irq_domain_ops lpc18xx_gpio_pin_ic_domain_ops = {
+	.alloc	= lpc18xx_gpio_pin_ic_domain_alloc,
+	.xlate	= irq_domain_xlate_twocell,
+	.free	= irq_domain_free_irqs_common,
+};
+
+static int lpc18xx_gpio_pin_ic_probe(struct lpc18xx_gpio_chip *gc)
+{
+	struct device *dev = gc->gpio.parent;
+	struct irq_domain *parent_domain;
+	struct device_node *parent_node;
+	struct lpc18xx_gpio_pin_ic *ic;
+	struct resource res;
+	int ret, index;
+
+	parent_node = of_irq_find_parent(dev->of_node);
+	if (!parent_node)
+		return -ENXIO;
+
+	parent_domain = irq_find_host(parent_node);
+	of_node_put(parent_node);
+	if (!parent_domain)
+		return -ENXIO;
+
+	ic = devm_kzalloc(dev, sizeof(*ic), GFP_KERNEL);
+	if (!ic)
+		return -ENOMEM;
+
+	index = of_property_match_string(dev->of_node, "reg-names",
+					 "gpio-pin-ic");
+	if (index < 0) {
+		ret = -ENODEV;
+		goto free_ic;
+	}
+
+	ret = of_address_to_resource(dev->of_node, index, &res);
+	if (ret < 0)
+		goto free_ic;
+
+	ic->base = devm_ioremap_resource(dev, &res);
+	if (IS_ERR(ic->base)) {
+		ret = PTR_ERR(ic->base);
+		goto free_ic;
+	}
+
+	raw_spin_lock_init(&ic->lock);
+
+	ic->domain = irq_domain_add_hierarchy(parent_domain, 0,
+					      NR_LPC18XX_GPIO_PIN_IC_IRQS,
+					      dev->of_node,
+					      &lpc18xx_gpio_pin_ic_domain_ops,
+					      ic);
+	if (!ic->domain) {
+		pr_err("unable to add irq domain\n");
+		ret = -ENODEV;
+		goto free_iomap;
+	}
+
+	gc->pin_ic = ic;
+
+	return 0;
+
+free_iomap:
+	devm_iounmap(dev, ic->base);
+free_ic:
+	devm_kfree(dev, ic);
+
+	return ret;
+}
+
 static void lpc18xx_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
 {
 	struct lpc18xx_gpio_chip *gc = gpiochip_get_data(chip);
@@ -91,8 +330,7 @@ static int lpc18xx_gpio_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
 	struct lpc18xx_gpio_chip *gc;
-	struct resource *res;
-	int ret;
+	int index, ret;
 
 	gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL);
 	if (!gc)
@@ -101,8 +339,22 @@ static int lpc18xx_gpio_probe(struct platform_device *pdev)
 	gc->gpio = lpc18xx_chip;
 	platform_set_drvdata(pdev, gc);
 
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	gc->base = devm_ioremap_resource(dev, res);
+	index = of_property_match_string(dev->of_node, "reg-names", "gpio");
+	if (index < 0) {
+		/* To support backward compatibility take the first resource */
+		struct resource *res;
+
+		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+		gc->base = devm_ioremap_resource(dev, res);
+	} else {
+		struct resource res;
+
+		ret = of_address_to_resource(dev->of_node, index, &res);
+		if (ret < 0)
+			return ret;
+
+		gc->base = devm_ioremap_resource(dev, &res);
+	}
 	if (IS_ERR(gc->base))
 		return PTR_ERR(gc->base);
 
@@ -129,6 +381,9 @@ static int lpc18xx_gpio_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	/* On error GPIO pin interrupt controller just won't be registered */
+	lpc18xx_gpio_pin_ic_probe(gc);
+
 	return 0;
 }
 
@@ -136,6 +391,9 @@ static int lpc18xx_gpio_remove(struct platform_device *pdev)
 {
 	struct lpc18xx_gpio_chip *gc = platform_get_drvdata(pdev);
 
+	if (gc->pin_ic)
+		irq_domain_remove(gc->pin_ic->domain);
+
 	clk_disable_unprepare(gc->clk);
 
 	return 0;
@@ -158,5 +416,6 @@ static struct platform_driver lpc18xx_gpio_driver = {
 module_platform_driver(lpc18xx_gpio_driver);
 
 MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
+MODULE_AUTHOR("Vladimir Zapolskiy <vz@mleia.com>");
 MODULE_DESCRIPTION("GPIO driver for LPC18xx/43xx");
 MODULE_LICENSE("GPL v2");
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 2/2] gpio: lpc18xx: add GPIO pin interrupt controller support
@ 2018-11-28 22:48   ` Vladimir Zapolskiy
  0 siblings, 0 replies; 10+ messages in thread
From: Vladimir Zapolskiy @ 2018-11-28 22:48 UTC (permalink / raw)
  To: linux-arm-kernel

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 <vz@mleia.com>
---
 drivers/gpio/Kconfig        |   1 +
 drivers/gpio/gpio-lpc18xx.c | 267 +++++++++++++++++++++++++++++++++++-
 2 files changed, 264 insertions(+), 4 deletions(-)

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 833a1b51c948..a1876c39a7b7 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -288,6 +288,7 @@ config GPIO_LPC18XX
 	tristate "NXP LPC18XX/43XX GPIO support"
 	default y if ARCH_LPC18XX
 	depends on OF_GPIO && (ARCH_LPC18XX || COMPILE_TEST)
+	select IRQ_DOMAIN_HIERARCHY
 	help
 	  Select this option to enable GPIO driver for
 	  NXP LPC18XX/43XX devices.
diff --git a/drivers/gpio/gpio-lpc18xx.c b/drivers/gpio/gpio-lpc18xx.c
index dc33185a89b1..040fb59d06f7 100644
--- a/drivers/gpio/gpio-lpc18xx.c
+++ b/drivers/gpio/gpio-lpc18xx.c
@@ -2,6 +2,7 @@
 /*
  * GPIO driver for NXP LPC18xx/43xx.
  *
+ * Copyright (C) 2018 Vladimir Zapolskiy <vz@mleia.com>
  * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
  *
  */
@@ -9,9 +10,13 @@
 #include <linux/clk.h>
 #include <linux/gpio/driver.h>
 #include <linux/io.h>
+#include <linux/irqdomain.h>
+#include <linux/irqchip.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/of_address.h>
 #include <linux/of_gpio.h>
+#include <linux/of_irq.h>
 #include <linux/pinctrl/consumer.h>
 #include <linux/platform_device.h>
 
@@ -21,13 +26,247 @@
 #define LPC18XX_MAX_PORTS	8
 #define LPC18XX_PINS_PER_PORT	32
 
+/* LPC18xx GPIO pin interrupt controller register offsets */
+#define LPC18XX_GPIO_PIN_IC_ISEL	0x00
+#define LPC18XX_GPIO_PIN_IC_IENR	0x04
+#define LPC18XX_GPIO_PIN_IC_SIENR	0x08
+#define LPC18XX_GPIO_PIN_IC_CIENR	0x0c
+#define LPC18XX_GPIO_PIN_IC_IENF	0x10
+#define LPC18XX_GPIO_PIN_IC_SIENF	0x14
+#define LPC18XX_GPIO_PIN_IC_CIENF	0x18
+#define LPC18XX_GPIO_PIN_IC_RISE	0x1c
+#define LPC18XX_GPIO_PIN_IC_FALL	0x20
+#define LPC18XX_GPIO_PIN_IC_IST		0x24
+
+#define NR_LPC18XX_GPIO_PIN_IC_IRQS	8
+
+struct lpc18xx_gpio_pin_ic {
+	void __iomem *base;
+	struct irq_domain *domain;
+	struct raw_spinlock lock;
+};
+
 struct lpc18xx_gpio_chip {
 	struct gpio_chip gpio;
 	void __iomem *base;
 	struct clk *clk;
+	struct lpc18xx_gpio_pin_ic *pin_ic;
 	spinlock_t lock;
 };
 
+static inline void lpc18xx_gpio_pin_ic_isel(struct lpc18xx_gpio_pin_ic *ic,
+					    u32 pin, bool set)
+{
+	u32 val = readl_relaxed(ic->base + LPC18XX_GPIO_PIN_IC_ISEL);
+
+	if (set)
+		val &= ~BIT(pin);
+	else
+		val |= BIT(pin);
+
+	writel_relaxed(val, ic->base + LPC18XX_GPIO_PIN_IC_ISEL);
+}
+
+static inline void lpc18xx_gpio_pin_ic_set(struct lpc18xx_gpio_pin_ic *ic,
+					   u32 pin, u32 reg)
+{
+	writel_relaxed(BIT(pin), ic->base + reg);
+}
+
+static void lpc18xx_gpio_pin_ic_mask(struct irq_data *d)
+{
+	struct lpc18xx_gpio_pin_ic *ic = d->chip_data;
+	u32 type = irqd_get_trigger_type(d);
+
+	raw_spin_lock(&ic->lock);
+
+	if (type & IRQ_TYPE_LEVEL_MASK || type & IRQ_TYPE_EDGE_RISING)
+		lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
+					LPC18XX_GPIO_PIN_IC_CIENR);
+
+	if (type & IRQ_TYPE_EDGE_FALLING)
+		lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
+					LPC18XX_GPIO_PIN_IC_CIENF);
+
+	raw_spin_unlock(&ic->lock);
+
+	irq_chip_mask_parent(d);
+}
+
+static void lpc18xx_gpio_pin_ic_unmask(struct irq_data *d)
+{
+	struct lpc18xx_gpio_pin_ic *ic = d->chip_data;
+	u32 type = irqd_get_trigger_type(d);
+
+	raw_spin_lock(&ic->lock);
+
+	if (type & IRQ_TYPE_LEVEL_MASK || type & IRQ_TYPE_EDGE_RISING)
+		lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
+					LPC18XX_GPIO_PIN_IC_SIENR);
+
+	if (type & IRQ_TYPE_EDGE_FALLING)
+		lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
+					LPC18XX_GPIO_PIN_IC_SIENF);
+
+	raw_spin_unlock(&ic->lock);
+
+	irq_chip_unmask_parent(d);
+}
+
+static void lpc18xx_gpio_pin_ic_eoi(struct irq_data *d)
+{
+	struct lpc18xx_gpio_pin_ic *ic = d->chip_data;
+	u32 type = irqd_get_trigger_type(d);
+
+	raw_spin_lock(&ic->lock);
+
+	if (type & IRQ_TYPE_EDGE_BOTH)
+		lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
+					LPC18XX_GPIO_PIN_IC_IST);
+
+	raw_spin_unlock(&ic->lock);
+
+	irq_chip_eoi_parent(d);
+}
+
+static int lpc18xx_gpio_pin_ic_set_type(struct irq_data *d, unsigned int type)
+{
+	struct lpc18xx_gpio_pin_ic *ic = d->chip_data;
+
+	raw_spin_lock(&ic->lock);
+
+	if (type & IRQ_TYPE_LEVEL_HIGH) {
+		lpc18xx_gpio_pin_ic_isel(ic, d->hwirq, true);
+		lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
+					LPC18XX_GPIO_PIN_IC_SIENF);
+	} else if (type & IRQ_TYPE_LEVEL_LOW) {
+		lpc18xx_gpio_pin_ic_isel(ic, d->hwirq, true);
+		lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
+					LPC18XX_GPIO_PIN_IC_CIENF);
+	} else {
+		lpc18xx_gpio_pin_ic_isel(ic, d->hwirq, false);
+	}
+
+	raw_spin_unlock(&ic->lock);
+
+	return 0;
+}
+
+static struct irq_chip lpc18xx_gpio_pin_ic = {
+	.name		= "LPC18xx GPIO pin",
+	.irq_mask	= lpc18xx_gpio_pin_ic_mask,
+	.irq_unmask	= lpc18xx_gpio_pin_ic_unmask,
+	.irq_eoi	= lpc18xx_gpio_pin_ic_eoi,
+	.irq_set_type	= lpc18xx_gpio_pin_ic_set_type,
+	.irq_retrigger	= irq_chip_retrigger_hierarchy,
+	.flags		= IRQCHIP_SET_TYPE_MASKED,
+};
+
+static int lpc18xx_gpio_pin_ic_domain_alloc(struct irq_domain *domain,
+					    unsigned int virq,
+					    unsigned int nr_irqs, void *data)
+{
+	struct irq_fwspec parent_fwspec, *fwspec = data;
+	struct lpc18xx_gpio_pin_ic *ic = domain->host_data;
+	irq_hw_number_t hwirq;
+	int ret;
+
+	if (nr_irqs != 1)
+		return -EINVAL;
+
+	hwirq = fwspec->param[0];
+	if (hwirq >= NR_LPC18XX_GPIO_PIN_IC_IRQS)
+		return -EINVAL;
+
+	/*
+	 * All LPC18xx/LPC43xx GPIO pin hardware interrupts are translated
+	 * into edge interrupts 32...39 on parent Cortex-M3/M4 NVIC
+	 */
+	parent_fwspec.fwnode = domain->parent->fwnode;
+	parent_fwspec.param_count = 1;
+	parent_fwspec.param[0] = hwirq + 32;
+
+	ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &parent_fwspec);
+	if (ret < 0) {
+		pr_err("failed to allocate parent irq %u: %d\n",
+		       parent_fwspec.param[0], ret);
+		return ret;
+	}
+
+	return irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
+					     &lpc18xx_gpio_pin_ic, ic);
+}
+
+static const struct irq_domain_ops lpc18xx_gpio_pin_ic_domain_ops = {
+	.alloc	= lpc18xx_gpio_pin_ic_domain_alloc,
+	.xlate	= irq_domain_xlate_twocell,
+	.free	= irq_domain_free_irqs_common,
+};
+
+static int lpc18xx_gpio_pin_ic_probe(struct lpc18xx_gpio_chip *gc)
+{
+	struct device *dev = gc->gpio.parent;
+	struct irq_domain *parent_domain;
+	struct device_node *parent_node;
+	struct lpc18xx_gpio_pin_ic *ic;
+	struct resource res;
+	int ret, index;
+
+	parent_node = of_irq_find_parent(dev->of_node);
+	if (!parent_node)
+		return -ENXIO;
+
+	parent_domain = irq_find_host(parent_node);
+	of_node_put(parent_node);
+	if (!parent_domain)
+		return -ENXIO;
+
+	ic = devm_kzalloc(dev, sizeof(*ic), GFP_KERNEL);
+	if (!ic)
+		return -ENOMEM;
+
+	index = of_property_match_string(dev->of_node, "reg-names",
+					 "gpio-pin-ic");
+	if (index < 0) {
+		ret = -ENODEV;
+		goto free_ic;
+	}
+
+	ret = of_address_to_resource(dev->of_node, index, &res);
+	if (ret < 0)
+		goto free_ic;
+
+	ic->base = devm_ioremap_resource(dev, &res);
+	if (IS_ERR(ic->base)) {
+		ret = PTR_ERR(ic->base);
+		goto free_ic;
+	}
+
+	raw_spin_lock_init(&ic->lock);
+
+	ic->domain = irq_domain_add_hierarchy(parent_domain, 0,
+					      NR_LPC18XX_GPIO_PIN_IC_IRQS,
+					      dev->of_node,
+					      &lpc18xx_gpio_pin_ic_domain_ops,
+					      ic);
+	if (!ic->domain) {
+		pr_err("unable to add irq domain\n");
+		ret = -ENODEV;
+		goto free_iomap;
+	}
+
+	gc->pin_ic = ic;
+
+	return 0;
+
+free_iomap:
+	devm_iounmap(dev, ic->base);
+free_ic:
+	devm_kfree(dev, ic);
+
+	return ret;
+}
+
 static void lpc18xx_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
 {
 	struct lpc18xx_gpio_chip *gc = gpiochip_get_data(chip);
@@ -91,8 +330,7 @@ static int lpc18xx_gpio_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
 	struct lpc18xx_gpio_chip *gc;
-	struct resource *res;
-	int ret;
+	int index, ret;
 
 	gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL);
 	if (!gc)
@@ -101,8 +339,22 @@ static int lpc18xx_gpio_probe(struct platform_device *pdev)
 	gc->gpio = lpc18xx_chip;
 	platform_set_drvdata(pdev, gc);
 
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	gc->base = devm_ioremap_resource(dev, res);
+	index = of_property_match_string(dev->of_node, "reg-names", "gpio");
+	if (index < 0) {
+		/* To support backward compatibility take the first resource */
+		struct resource *res;
+
+		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+		gc->base = devm_ioremap_resource(dev, res);
+	} else {
+		struct resource res;
+
+		ret = of_address_to_resource(dev->of_node, index, &res);
+		if (ret < 0)
+			return ret;
+
+		gc->base = devm_ioremap_resource(dev, &res);
+	}
 	if (IS_ERR(gc->base))
 		return PTR_ERR(gc->base);
 
@@ -129,6 +381,9 @@ static int lpc18xx_gpio_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	/* On error GPIO pin interrupt controller just won't be registered */
+	lpc18xx_gpio_pin_ic_probe(gc);
+
 	return 0;
 }
 
@@ -136,6 +391,9 @@ static int lpc18xx_gpio_remove(struct platform_device *pdev)
 {
 	struct lpc18xx_gpio_chip *gc = platform_get_drvdata(pdev);
 
+	if (gc->pin_ic)
+		irq_domain_remove(gc->pin_ic->domain);
+
 	clk_disable_unprepare(gc->clk);
 
 	return 0;
@@ -158,5 +416,6 @@ static struct platform_driver lpc18xx_gpio_driver = {
 module_platform_driver(lpc18xx_gpio_driver);
 
 MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
+MODULE_AUTHOR("Vladimir Zapolskiy <vz@mleia.com>");
 MODULE_DESCRIPTION("GPIO driver for LPC18xx/43xx");
 MODULE_LICENSE("GPL v2");
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/2] dt-bindings: gpio: lpc18xx: describe interrupt controllers of GPIO controller
  2018-11-28 22:48   ` Vladimir Zapolskiy
@ 2018-12-07  9:56     ` Linus Walleij
  -1 siblings, 0 replies; 10+ messages in thread
From: Linus Walleij @ 2018-12-07  9:56 UTC (permalink / raw)
  To: Vladimir Zapolskiy
  Cc: Marc Zyngier,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	Rob Herring, Linux ARM, open list:GPIO SUBSYSTEM

On Wed, Nov 28, 2018 at 11:48 PM Vladimir Zapolskiy <vz@mleia.com> wrote:

> From LPC18xx and LPC43xx User Manuals the GPIO controller consists of
> the following weakly connected blocks:
> * GPIO pin interrupt block at 0x40087000,
> * GPIO GROUP0 interrupt block at 0x40088000,
> * GPIO GROUP1 interrupt block at 0x40089000,
> * GPIO port block at 0x400F4000.
>
> 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 interrupt controllers and
> GPIO controller block are seen as a single device, all 4 sub-controllers
> have the shared reset signal RGU #28 and the same shared clock to access
> registers CLK_Mx_GPIO on CCU1.
>
> The change adds descriptions of the currently missing interrupt controller
> blocks found on GPIO controller, new added properties are 'reg-names',
> 'resets', 'interrupt-controller' and '#interrupt-cells', also the example
> is updated to reflect the changes in device tree binding description.
>
> Signed-off-by: Vladimir Zapolskiy <vz@mleia.com>

That is some seriously Frankenstein hardware.

But I see no problem with the way you are dealing with it,
it seems pretty much to the point.

So: patch applied.

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/2] dt-bindings: gpio: lpc18xx: describe interrupt controllers of GPIO controller
@ 2018-12-07  9:56     ` Linus Walleij
  0 siblings, 0 replies; 10+ messages in thread
From: Linus Walleij @ 2018-12-07  9:56 UTC (permalink / raw)
  To: Vladimir Zapolskiy
  Cc: Marc Zyngier,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	Rob Herring, Linux ARM, open list:GPIO SUBSYSTEM

On Wed, Nov 28, 2018 at 11:48 PM Vladimir Zapolskiy <vz@mleia.com> wrote:

> From LPC18xx and LPC43xx User Manuals the GPIO controller consists of
> the following weakly connected blocks:
> * GPIO pin interrupt block at 0x40087000,
> * GPIO GROUP0 interrupt block at 0x40088000,
> * GPIO GROUP1 interrupt block at 0x40089000,
> * GPIO port block at 0x400F4000.
>
> 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 interrupt controllers and
> GPIO controller block are seen as a single device, all 4 sub-controllers
> have the shared reset signal RGU #28 and the same shared clock to access
> registers CLK_Mx_GPIO on CCU1.
>
> The change adds descriptions of the currently missing interrupt controller
> blocks found on GPIO controller, new added properties are 'reg-names',
> 'resets', 'interrupt-controller' and '#interrupt-cells', also the example
> is updated to reflect the changes in device tree binding description.
>
> Signed-off-by: Vladimir Zapolskiy <vz@mleia.com>

That is some seriously Frankenstein hardware.

But I see no problem with the way you are dealing with it,
it seems pretty much to the point.

So: patch applied.

Yours,
Linus Walleij

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 2/2] gpio: lpc18xx: add GPIO pin interrupt controller support
  2018-11-28 22:48   ` Vladimir Zapolskiy
@ 2018-12-07 10:00     ` Linus Walleij
  -1 siblings, 0 replies; 10+ messages in thread
From: Linus Walleij @ 2018-12-07 10:00 UTC (permalink / raw)
  To: Vladimir Zapolskiy
  Cc: Marc Zyngier,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	Rob Herring, Linux ARM, open list:GPIO SUBSYSTEM

On Wed, Nov 28, 2018 at 11:48 PM Vladimir Zapolskiy <vz@mleia.com> wrote:

> 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 <vz@mleia.com>

It's quite complex, but your implementation is so crisp and clean
that there is nothing to complain about really.

We are discussing putting some hierarchy handling into the
gpiolib core, so you might want to simplify the code later to
make use of this, but for now it's perfectly fine.

Patch applied.

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 2/2] gpio: lpc18xx: add GPIO pin interrupt controller support
@ 2018-12-07 10:00     ` Linus Walleij
  0 siblings, 0 replies; 10+ messages in thread
From: Linus Walleij @ 2018-12-07 10:00 UTC (permalink / raw)
  To: Vladimir Zapolskiy
  Cc: Marc Zyngier,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	Rob Herring, Linux ARM, open list:GPIO SUBSYSTEM

On Wed, Nov 28, 2018 at 11:48 PM Vladimir Zapolskiy <vz@mleia.com> wrote:

> 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 <vz@mleia.com>

It's quite complex, but your implementation is so crisp and clean
that there is nothing to complain about really.

We are discussing putting some hierarchy handling into the
gpiolib core, so you might want to simplify the code later to
make use of this, but for now it's perfectly fine.

Patch applied.

Yours,
Linus Walleij

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2018-12-07 10:00 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-11-28 22:48 [PATCH 0/2] gpio: lpc18xx: add GPIO pin interrupt controller support Vladimir Zapolskiy
2018-11-28 22:48 ` Vladimir Zapolskiy
2018-11-28 22:48 ` [PATCH 1/2] dt-bindings: gpio: lpc18xx: describe interrupt controllers of GPIO controller Vladimir Zapolskiy
2018-11-28 22:48   ` Vladimir Zapolskiy
2018-12-07  9:56   ` Linus Walleij
2018-12-07  9:56     ` Linus Walleij
2018-11-28 22:48 ` [PATCH 2/2] gpio: lpc18xx: add GPIO pin interrupt controller support Vladimir Zapolskiy
2018-11-28 22:48   ` Vladimir Zapolskiy
2018-12-07 10:00   ` Linus Walleij
2018-12-07 10:00     ` Linus Walleij

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.