Linux-Hwmon Archive on lore.kernel.org
 help / color / Atom feed
* [PATCH 00/18] Add support for Kontron sl28cpld
@ 2020-03-17 20:49 Michael Walle
  2020-03-17 20:50 ` [PATCH 01/18] include/linux/ioport.h: add helper to define REG resource constructs Michael Walle
                   ` (17 more replies)
  0 siblings, 18 replies; 40+ messages in thread
From: Michael Walle @ 2020-03-17 20:49 UTC (permalink / raw)
  To: linux-gpio, devicetree, linux-kernel, linux-hwmon, linux-pwm,
	linux-watchdog, linux-arm-kernel
  Cc: Linus Walleij, Bartosz Golaszewski, Rob Herring, Jean Delvare,
	Guenter Roeck, Lee Jones, Thierry Reding, Uwe Kleine-König,
	Wim Van Sebroeck, Shawn Guo, Li Yang, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Michael Walle

The Kontron sl28cpld is a board management chip providing gpio, pwm, fan
monitoring and an interrupt controller. For now this controller is used on
the Kontron SMARC-sAL28 board. But because of its flexible nature, it
might also be used on other boards in the future. The individual blocks
(like gpio, pwm, etc) are kept intentionally small. The MFD core driver
then instantiates different (or multiple of the same) blocks. It also
provides the register layout so it might be updated in the future without a
device tree change; and support other boards with a different layout or
functionalities.

See also [1] for more information.

There is one problem though:

Documentation/devicetree/bindings/mfd/kontron,sl28cpld.example.dts:32.24-41.19: Warning (unique_unit_address): /example-0/i2c/sl28cpld@4a/gpio@0: duplicate unit-address (also used in node /example-0/i2c/sl28cpld@4a/pwm@0)
Documentation/devicetree/bindings/mfd/kontron,sl28cpld.example.dts:43.24-52.19: Warning (unique_unit_address): /example-0/i2c/sl28cpld@4a/gpio@1: duplicate unit-address (also used in node /example-0/i2c/sl28cpld@4a/pwm@1)

This is because the reg property is used to match multiple MFD cells which
has the same devicetree compatible string. See patch 3. So only the (reg,
compatible string) is unique. Maybe I should use a different property but
reg?

This is my first take of a MFD driver. I don't know wether the subsystem
maintainers should only be CCed on the patches which affect the subsystem
or on all patches for this series. I've chosen the latter so you can get a
more complete picture.

[1] https://lore.kernel.org/linux-devicetree/0e3e8204ab992d75aa07fc36af7e4ab2@walle.cc/

Michael Walle (18):
  include/linux/ioport.h: add helper to define REG resource constructs
  mfd: mfd-core: Don't overwrite the dma_mask of the child device
  mfd: mfd-core: match device tree node against reg property
  dt-bindings: mfd: Add bindings for sl28cpld
  mfd: Add support for Kontron sl28cpld management controller
  irqchip: add sl28cpld interrupt controller support
  dt-bindings: watchdog: Add bindings for sl28cpld watchdog
  watchdog: add support for sl28cpld watchdog
  dt-bindings: pwm: Add bindings for sl28cpld PWM controller
  pwm: add support for sl28cpld PWM controller
  dt-bindings: gpio: Add bindings for sl28cpld GPIO controller
  gpio: add support for the sl28cpld GPIO controller
  dt-bindings: hwmon: Add bindings for sl28cpld hardware monitoring
  hwmon: add support for the sl28cpld hardware monitoring controller
  arm64: dts: freescale: sl28: enable sl28cpld
  arm64: dts: freescale: sl28: map GPIOs to input events
  arm64: dts: freescale: sl28: enable LED support
  arm64: dts: freescale: sl28: enable fan support

 .../bindings/gpio/kontron,sl28cpld-gpio.yaml  |  52 +++
 .../hwmon/kontron,sl28cpld-hwmon.yaml         |  28 ++
 .../bindings/mfd/kontron,sl28cpld.yaml        | 146 ++++++++
 .../bindings/pwm/kontron,sl28cpld-pwm.yaml    |  33 ++
 .../watchdog/kontron,sl28cpld-wdt.yaml        |  30 ++
 .../fsl-ls1028a-kontron-kbox-a-230-ls.dts     |  14 +
 .../fsl-ls1028a-kontron-sl28-var3-ads2.dts    |   9 +
 .../freescale/fsl-ls1028a-kontron-sl28.dts    | 119 +++++++
 drivers/gpio/Kconfig                          |  11 +
 drivers/gpio/Makefile                         |   1 +
 drivers/gpio/gpio-sl28cpld.c                  | 332 ++++++++++++++++++
 drivers/hwmon/Kconfig                         |  10 +
 drivers/hwmon/Makefile                        |   1 +
 drivers/hwmon/sl28cpld-hwmon.c                | 146 ++++++++
 drivers/irqchip/Kconfig                       |   3 +
 drivers/irqchip/Makefile                      |   1 +
 drivers/irqchip/irq-sl28cpld.c                |  92 +++++
 drivers/mfd/Kconfig                           |  21 ++
 drivers/mfd/Makefile                          |   2 +
 drivers/mfd/mfd-core.c                        |  28 +-
 drivers/mfd/sl28cpld.c                        | 155 ++++++++
 drivers/pwm/Kconfig                           |  10 +
 drivers/pwm/Makefile                          |   1 +
 drivers/pwm/pwm-sl28cpld.c                    | 192 ++++++++++
 drivers/watchdog/Kconfig                      |  11 +
 drivers/watchdog/Makefile                     |   1 +
 drivers/watchdog/sl28cpld_wdt.c               | 238 +++++++++++++
 include/linux/ioport.h                        |   5 +
 28 files changed, 1682 insertions(+), 10 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/gpio/kontron,sl28cpld-gpio.yaml
 create mode 100644 Documentation/devicetree/bindings/hwmon/kontron,sl28cpld-hwmon.yaml
 create mode 100644 Documentation/devicetree/bindings/mfd/kontron,sl28cpld.yaml
 create mode 100644 Documentation/devicetree/bindings/pwm/kontron,sl28cpld-pwm.yaml
 create mode 100644 Documentation/devicetree/bindings/watchdog/kontron,sl28cpld-wdt.yaml
 create mode 100644 drivers/gpio/gpio-sl28cpld.c
 create mode 100644 drivers/hwmon/sl28cpld-hwmon.c
 create mode 100644 drivers/irqchip/irq-sl28cpld.c
 create mode 100644 drivers/mfd/sl28cpld.c
 create mode 100644 drivers/pwm/pwm-sl28cpld.c
 create mode 100644 drivers/watchdog/sl28cpld_wdt.c

-- 
2.20.1


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

* [PATCH 01/18] include/linux/ioport.h: add helper to define REG resource constructs
  2020-03-17 20:49 [PATCH 00/18] Add support for Kontron sl28cpld Michael Walle
@ 2020-03-17 20:50 ` Michael Walle
  2020-03-17 20:50 ` [PATCH 02/18] mfd: mfd-core: Don't overwrite the dma_mask of the child device Michael Walle
                   ` (16 subsequent siblings)
  17 siblings, 0 replies; 40+ messages in thread
From: Michael Walle @ 2020-03-17 20:50 UTC (permalink / raw)
  To: linux-gpio, devicetree, linux-kernel, linux-hwmon, linux-pwm,
	linux-watchdog, linux-arm-kernel
  Cc: Linus Walleij, Bartosz Golaszewski, Rob Herring, Jean Delvare,
	Guenter Roeck, Lee Jones, Thierry Reding, Uwe Kleine-König,
	Wim Van Sebroeck, Shawn Guo, Li Yang, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Michael Walle

Similar to the existing helpers, add one for IORESOURCE_REG which comes
in handy for most common resource declarations.

Signed-off-by: Michael Walle <michael@walle.cc>
---
 include/linux/ioport.h | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/include/linux/ioport.h b/include/linux/ioport.h
index a9b9170b5dd2..cdcceeec0f86 100644
--- a/include/linux/ioport.h
+++ b/include/linux/ioport.h
@@ -165,6 +165,11 @@ enum {
 #define DEFINE_RES_MEM(_start, _size)					\
 	DEFINE_RES_MEM_NAMED((_start), (_size), NULL)
 
+#define DEFINE_RES_REG_NAMED(_start, _size, _name)			\
+	DEFINE_RES_NAMED((_start), (_size), (_name), IORESOURCE_REG)
+#define DEFINE_RES_REG(_start, _size)					\
+	DEFINE_RES_REG_NAMED((_start), (_size), NULL)
+
 #define DEFINE_RES_IRQ_NAMED(_irq, _name)				\
 	DEFINE_RES_NAMED((_irq), 1, (_name), IORESOURCE_IRQ)
 #define DEFINE_RES_IRQ(_irq)						\
-- 
2.20.1


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

* [PATCH 02/18] mfd: mfd-core: Don't overwrite the dma_mask of the child device
  2020-03-17 20:49 [PATCH 00/18] Add support for Kontron sl28cpld Michael Walle
  2020-03-17 20:50 ` [PATCH 01/18] include/linux/ioport.h: add helper to define REG resource constructs Michael Walle
@ 2020-03-17 20:50 ` Michael Walle
  2020-03-17 20:50 ` [PATCH 03/18] mfd: mfd-core: match device tree node against reg property Michael Walle
                   ` (15 subsequent siblings)
  17 siblings, 0 replies; 40+ messages in thread
From: Michael Walle @ 2020-03-17 20:50 UTC (permalink / raw)
  To: linux-gpio, devicetree, linux-kernel, linux-hwmon, linux-pwm,
	linux-watchdog, linux-arm-kernel
  Cc: Linus Walleij, Bartosz Golaszewski, Rob Herring, Jean Delvare,
	Guenter Roeck, Lee Jones, Thierry Reding, Uwe Kleine-König,
	Wim Van Sebroeck, Shawn Guo, Li Yang, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Michael Walle, Robin Murphy

Commit cdfee5623290 ("driver core: initialize a default DMA mask for
platform device") initialize the DMA of a platform device. But if the
parent doesn't have a dma_mask set, for example if it's an I2C device,
the dma_mask of the child platform device will be set to zero again.
Which leads to many "DMA mask not set" warnings, if the MFD cell has the
of_compatible property set.

[    1.877937] sl28cpld-pwm sl28cpld-pwm: DMA mask not set
[    1.883282] sl28cpld-pwm sl28cpld-pwm.0: DMA mask not set
[    1.888795] sl28cpld-gpio sl28cpld-gpio: DMA mask not set

Thus don't overwrite the dma_mask of the children. Instead set the
dma_mask of the platform device.

Signed-off-by: Michael Walle <michael@walle.cc>
Suggested-by: Robin Murphy <robin.murphy@arm.com>
---
 drivers/mfd/mfd-core.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c
index f5a73af60dd4..e735565969b3 100644
--- a/drivers/mfd/mfd-core.c
+++ b/drivers/mfd/mfd-core.c
@@ -138,7 +138,7 @@ static int mfd_add_device(struct device *parent, int id,
 
 	pdev->dev.parent = parent;
 	pdev->dev.type = &mfd_dev_type;
-	pdev->dev.dma_mask = parent->dma_mask;
+	pdev->platform_dma_mask = parent->dma_mask ? *parent->dma_mask : 0;
 	pdev->dev.dma_parms = parent->dma_parms;
 	pdev->dev.coherent_dma_mask = parent->coherent_dma_mask;
 
-- 
2.20.1


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

* [PATCH 03/18] mfd: mfd-core: match device tree node against reg property
  2020-03-17 20:49 [PATCH 00/18] Add support for Kontron sl28cpld Michael Walle
  2020-03-17 20:50 ` [PATCH 01/18] include/linux/ioport.h: add helper to define REG resource constructs Michael Walle
  2020-03-17 20:50 ` [PATCH 02/18] mfd: mfd-core: Don't overwrite the dma_mask of the child device Michael Walle
@ 2020-03-17 20:50 ` Michael Walle
  2020-03-17 20:50 ` [PATCH 04/18] dt-bindings: mfd: Add bindings for sl28cpld Michael Walle
                   ` (14 subsequent siblings)
  17 siblings, 0 replies; 40+ messages in thread
From: Michael Walle @ 2020-03-17 20:50 UTC (permalink / raw)
  To: linux-gpio, devicetree, linux-kernel, linux-hwmon, linux-pwm,
	linux-watchdog, linux-arm-kernel
  Cc: Linus Walleij, Bartosz Golaszewski, Rob Herring, Jean Delvare,
	Guenter Roeck, Lee Jones, Thierry Reding, Uwe Kleine-König,
	Wim Van Sebroeck, Shawn Guo, Li Yang, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Michael Walle

There might be multiple children with the device tree compatible, for
example if a MFD has multiple instances of the same function. In this
case only the first is matched and the other children get a wrong
of_node reference.
We distinguish them by looking at the reg property and match it against
the mfd_cell id element if the latter is non-zero and the reg property
exists in the device tree node.

Signed-off-by: Michael Walle <michael@walle.cc>
---
 drivers/mfd/mfd-core.c | 26 +++++++++++++++++---------
 1 file changed, 17 insertions(+), 9 deletions(-)

diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c
index e735565969b3..0e718e6cdbde 100644
--- a/drivers/mfd/mfd-core.c
+++ b/drivers/mfd/mfd-core.c
@@ -117,6 +117,7 @@ static int mfd_add_device(struct device *parent, int id,
 	struct device_node *np = NULL;
 	int ret = -ENOMEM;
 	int platform_id;
+	u32 of_id;
 	int r;
 
 	if (id == PLATFORM_DEVID_AUTO)
@@ -151,16 +152,23 @@ static int mfd_add_device(struct device *parent, int id,
 
 	if (parent->of_node && cell->of_compatible) {
 		for_each_child_of_node(parent->of_node, np) {
-			if (of_device_is_compatible(np, cell->of_compatible)) {
-				if (!of_device_is_available(np)) {
-					/* Ignore disabled devices error free */
-					ret = 0;
-					goto fail_alias;
-				}
-				pdev->dev.of_node = np;
-				pdev->dev.fwnode = &np->fwnode;
-				break;
+			if (!of_device_is_compatible(np, cell->of_compatible))
+				continue;
+
+			/* match the reg property to the id */
+			if (!of_property_read_u32(np, "reg", &of_id))
+				if (cell->id && cell->id != of_id)
+					continue;
+
+			if (!of_device_is_available(np)) {
+				/* Ignore disabled devices error free */
+				ret = 0;
+				goto fail_alias;
 			}
+
+			pdev->dev.of_node = np;
+			pdev->dev.fwnode = &np->fwnode;
+			break;
 		}
 	}
 
-- 
2.20.1


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

* [PATCH 04/18] dt-bindings: mfd: Add bindings for sl28cpld
  2020-03-17 20:49 [PATCH 00/18] Add support for Kontron sl28cpld Michael Walle
                   ` (2 preceding siblings ...)
  2020-03-17 20:50 ` [PATCH 03/18] mfd: mfd-core: match device tree node against reg property Michael Walle
@ 2020-03-17 20:50 ` Michael Walle
  2020-03-30 22:35   ` Rob Herring
  2020-03-17 20:50 ` [PATCH 05/18] mfd: Add support for Kontron sl28cpld management controller Michael Walle
                   ` (13 subsequent siblings)
  17 siblings, 1 reply; 40+ messages in thread
From: Michael Walle @ 2020-03-17 20:50 UTC (permalink / raw)
  To: linux-gpio, devicetree, linux-kernel, linux-hwmon, linux-pwm,
	linux-watchdog, linux-arm-kernel
  Cc: Linus Walleij, Bartosz Golaszewski, Rob Herring, Jean Delvare,
	Guenter Roeck, Lee Jones, Thierry Reding, Uwe Kleine-König,
	Wim Van Sebroeck, Shawn Guo, Li Yang, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Michael Walle

This adds device tree bindings for the board management controller found
on the Kontron SMARC-sAL28 board.

Signed-off-by: Michael Walle <michael@walle.cc>
---
 .../bindings/mfd/kontron,sl28cpld.yaml        | 143 ++++++++++++++++++
 1 file changed, 143 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/kontron,sl28cpld.yaml

diff --git a/Documentation/devicetree/bindings/mfd/kontron,sl28cpld.yaml b/Documentation/devicetree/bindings/mfd/kontron,sl28cpld.yaml
new file mode 100644
index 000000000000..3b9cca49d2d6
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/kontron,sl28cpld.yaml
@@ -0,0 +1,143 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mfd/kontron,sl28cpld.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Kontron's sl28cpld board management controller
+
+maintainers:
+  - Michael Walle <michael@walle.cc>
+
+description: |
+  The board management controller may contain different IP blocks like
+  watchdog, fan monitoring, PWM controller, interrupt controller and a
+  GPIO controller.
+
+properties:
+  compatible:
+    const: kontron,sl28cpld
+
+  reg:
+    description:
+      I2C device address.
+    maxItems: 1
+
+  "#address-cells":
+    const: 1
+
+  "#size-cells":
+    const: 0
+
+  "#interrupt-cells":
+    const: 2
+
+  interrupts:
+    maxItems: 1
+
+  interrupt-controller: true
+
+patternProperties:
+  "^gp(io|i|o)(@[0-9]+)?$":
+    $ref: ../gpio/kontron,sl28cpld-gpio.yaml
+
+  "^hwmon(@[0-9]+)?$":
+    $ref: ../hwmon/kontron,sl28cpld-hwmon.yaml
+
+  "^pwm(@[0-9]+)?$":
+    $ref: ../pwm/kontron,sl28cpld-pwm.yaml
+
+  "^watchdog(@[0-9]+)?$":
+    $ref: ../watchdog/kontron,sl28cpld-wdt.yaml
+
+required:
+  - "#address-cells"
+  - "#size-cells"
+  - compatible
+  - reg
+  - "#interrupt-cells"
+  - interrupt-controller
+
+oneOf:
+  - required:
+    - interrupts
+  - required:
+    - interrupts-extended
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        sl28cpld@4a {
+            #address-cells = <1>;
+            #size-cells = <0>;
+            compatible = "kontron,sl28cpld";
+            reg = <0x4a>;
+            interrupts-extended = <&gpio2 6 IRQ_TYPE_EDGE_FALLING>;
+
+            #interrupt-cells = <2>;
+            interrupt-controller;
+
+            gpio@0 {
+                compatible = "kontron,sl28cpld-gpio";
+                reg = <0>;
+
+                gpio-controller;
+                #gpio-cells = <2>;
+
+                interrupt-controller;
+                #interrupt-cells = <2>;
+            };
+
+            gpio@1 {
+                compatible = "kontron,sl28cpld-gpio";
+                reg = <1>;
+
+                gpio-controller;
+                #gpio-cells = <2>;
+
+                interrupt-controller;
+                #interrupt-cells = <2>;
+            };
+
+            gpo {
+                compatible = "kontron,sl28cpld-gpo";
+
+                gpio-controller;
+                #gpio-cells = <2>;
+                gpio-line-names = "a", "b", "c";
+            };
+
+            gpi {
+                compatible = "kontron,sl28cpld-gpi";
+
+                gpio-controller;
+                #gpio-cells = <2>;
+            };
+
+            hwmon {
+                compatible = "kontron,sl28cpld-fan";
+            };
+
+            pwm@0 {
+                compatible = "kontron,sl28cpld-pwm";
+                reg = <0>;
+                #pwm-cells = <2>;
+            };
+
+            pwm@1 {
+                compatible = "kontron,sl28cpld-pwm";
+                reg = <1>;
+                #pwm-cells = <2>;
+            };
+
+            watchdog {
+                compatible = "kontron,sl28cpld-wdt";
+            };
+        };
+    };
-- 
2.20.1


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

* [PATCH 05/18] mfd: Add support for Kontron sl28cpld management controller
  2020-03-17 20:49 [PATCH 00/18] Add support for Kontron sl28cpld Michael Walle
                   ` (3 preceding siblings ...)
  2020-03-17 20:50 ` [PATCH 04/18] dt-bindings: mfd: Add bindings for sl28cpld Michael Walle
@ 2020-03-17 20:50 ` Michael Walle
  2020-03-18  3:28   ` Guenter Roeck
  2020-03-17 20:50 ` [PATCH 06/18] irqchip: add sl28cpld interrupt controller support Michael Walle
                   ` (12 subsequent siblings)
  17 siblings, 1 reply; 40+ messages in thread
From: Michael Walle @ 2020-03-17 20:50 UTC (permalink / raw)
  To: linux-gpio, devicetree, linux-kernel, linux-hwmon, linux-pwm,
	linux-watchdog, linux-arm-kernel
  Cc: Linus Walleij, Bartosz Golaszewski, Rob Herring, Jean Delvare,
	Guenter Roeck, Lee Jones, Thierry Reding, Uwe Kleine-König,
	Wim Van Sebroeck, Shawn Guo, Li Yang, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Michael Walle

This patch adds core support for the board management controller found
on the SMARC-sAL28 board. It consists of the following functions:
 - watchdog
 - GPIO controller
 - PWM controller
 - fan sensor
 - interrupt controller

At the moment, this controller is used on the Kontron SMARC-sAL28 board.

Signed-off-by: Michael Walle <michael@walle.cc>
---
 drivers/mfd/Kconfig    |  21 ++++++
 drivers/mfd/Makefile   |   2 +
 drivers/mfd/sl28cpld.c | 155 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 178 insertions(+)
 create mode 100644 drivers/mfd/sl28cpld.c

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 3c547ed575e6..01588c366476 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -2059,5 +2059,26 @@ config SGI_MFD_IOC3
 	  If you have an SGI Origin, Octane, or a PCI IOC3 card,
 	  then say Y. Otherwise say N.
 
+config MFD_SL28CPLD
+	tristate "Kontron sl28 core driver"
+	depends on I2C=y
+	depends on OF
+	select REGMAP_I2C
+	select REGMAP_IRQ
+	select SL28CPLD_IRQ
+	select MFD_CORE
+	help
+	  This option enables support for the board management controller
+	  found on the Kontron sl28 CPLD. You have to select individual
+	  functions, such as watchdog, GPIO, etc, under the corresponding menus
+	  in order to enable them.
+
+	  Currently supported boards are:
+
+		Kontron SMARC-sAL28
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called sl28cpld.
+
 endmenu
 endif
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index f935d10cbf0f..9bc38863b9c7 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -259,3 +259,5 @@ obj-$(CONFIG_MFD_ROHM_BD718XX)	+= rohm-bd718x7.o
 obj-$(CONFIG_MFD_STMFX) 	+= stmfx.o
 
 obj-$(CONFIG_SGI_MFD_IOC3)	+= ioc3.o
+
+obj-$(CONFIG_MFD_SL28CPLD)	+= sl28cpld.o
diff --git a/drivers/mfd/sl28cpld.c b/drivers/mfd/sl28cpld.c
new file mode 100644
index 000000000000..789f21f90752
--- /dev/null
+++ b/drivers/mfd/sl28cpld.c
@@ -0,0 +1,155 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MFD core for the CPLD on a SMARC-sAL28 board.
+ *
+ * Copyright 2019 Kontron Europe GmbH
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+
+#define SL28CPLD_VERSION 0x03
+#define SL28CPLD_WATCHDOG_BASE 0x4
+#define SL28CPLD_HWMON_FAN_BASE 0xb
+#define SL28CPLD_PWM0_BASE 0xc
+#define SL28CPLD_PWM1_BASE 0xe
+#define SL28CPLD_GPIO0_BASE 0x10
+#define SL28CPLD_GPIO1_BASE 0x15
+#define SL28CPLD_GPO_BASE 0x1a
+#define SL28CPLD_GPI_BASE 0x1b
+#define SL28CPLD_INTC_BASE 0x1c
+
+/* all subdevices share the same IRQ */
+#define SL28CPLD_IRQ 0
+
+#define SL28CPLD_MIN_REQ_VERSION 14
+
+struct sl28cpld {
+	struct device *dev;
+	struct regmap *regmap;
+};
+
+static const struct regmap_config sl28cpld_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.reg_stride = 1,
+};
+
+static struct resource sl28cpld_watchdog_resources[] = {
+	DEFINE_RES_REG(SL28CPLD_WATCHDOG_BASE, 1),
+};
+
+static struct resource sl28cpld_hwmon_fan_resources[] = {
+	DEFINE_RES_REG(SL28CPLD_HWMON_FAN_BASE, 1),
+};
+
+static struct resource sl28cpld_pwm0_resources[] = {
+	DEFINE_RES_REG(SL28CPLD_PWM0_BASE, 1),
+};
+
+static struct resource sl28cpld_pwm1_resources[] = {
+	DEFINE_RES_REG(SL28CPLD_PWM1_BASE, 1),
+};
+
+static struct resource sl28cpld_gpio0_resources[] = {
+	DEFINE_RES_REG(SL28CPLD_GPIO0_BASE, 1),
+	DEFINE_RES_IRQ(SL28CPLD_IRQ),
+};
+
+static struct resource sl28cpld_gpio1_resources[] = {
+	DEFINE_RES_REG(SL28CPLD_GPIO1_BASE, 1),
+	DEFINE_RES_IRQ(SL28CPLD_IRQ),
+};
+
+static struct resource sl28cpld_gpo_resources[] = {
+	DEFINE_RES_REG(SL28CPLD_GPO_BASE, 1),
+};
+
+static struct resource sl28cpld_gpi_resources[] = {
+	DEFINE_RES_REG(SL28CPLD_GPI_BASE, 1),
+};
+
+static struct resource sl28cpld_intc_resources[] = {
+	DEFINE_RES_REG(SL28CPLD_INTC_BASE, 1),
+	DEFINE_RES_IRQ(SL28CPLD_IRQ),
+};
+
+static const struct mfd_cell sl28cpld_devs[] = {
+	OF_MFD_CELL("sl28cpld-wdt", sl28cpld_watchdog_resources, NULL, 0, 0,
+		    "kontron,sl28cpld-wdt"),
+	OF_MFD_CELL("sl28cpld-fan", sl28cpld_hwmon_fan_resources, NULL, 0, 0,
+		    "kontron,sl28cpld-fan"),
+	OF_MFD_CELL("sl28cpld-pwm", sl28cpld_pwm0_resources, NULL, 0, 0,
+		    "kontron,sl28cpld-pwm"),
+	OF_MFD_CELL("sl28cpld-pwm", sl28cpld_pwm1_resources, NULL, 0, 1,
+		    "kontron,sl28cpld-pwm"),
+	OF_MFD_CELL("sl28cpld-gpio", sl28cpld_gpio0_resources, NULL, 0, 0,
+		    "kontron,sl28cpld-gpio"),
+	OF_MFD_CELL("sl28cpld-gpio", sl28cpld_gpio1_resources, NULL, 0, 1,
+		    "kontron,sl28cpld-gpio"),
+	OF_MFD_CELL("sl28cpld-gpo", sl28cpld_gpo_resources, NULL, 0, 0,
+		    "kontron,sl28cpld-gpo"),
+	OF_MFD_CELL("sl28cpld-gpi", sl28cpld_gpi_resources, NULL, 0, 0,
+		    "kontron,sl28cpld-gpi"),
+	OF_MFD_CELL("sl28cpld-intc", sl28cpld_intc_resources, NULL, 0, 0,
+		    "kontron,sl28cpld-intc"),
+};
+
+static int sl28cpld_probe(struct i2c_client *i2c)
+{
+	struct sl28cpld *sl28cpld;
+	struct device *dev = &i2c->dev;
+	unsigned int cpld_version;
+	int ret;
+
+	sl28cpld = devm_kzalloc(dev, sizeof(*sl28cpld), GFP_KERNEL);
+	if (!sl28cpld)
+		return -ENOMEM;
+
+	sl28cpld->regmap = devm_regmap_init_i2c(i2c, &sl28cpld_regmap_config);
+	if (IS_ERR(sl28cpld->regmap))
+		return PTR_ERR(sl28cpld->regmap);
+
+	ret = regmap_read(sl28cpld->regmap, SL28CPLD_VERSION, &cpld_version);
+	if (ret)
+		return ret;
+
+	if (cpld_version < SL28CPLD_MIN_REQ_VERSION) {
+		dev_err(dev, "unsupported CPLD version %d\n", cpld_version);
+		return -ENODEV;
+	}
+
+	sl28cpld->dev = dev;
+	i2c_set_clientdata(i2c, sl28cpld);
+
+	dev_info(dev, "successfully probed. CPLD version %d\n", cpld_version);
+
+	return devm_mfd_add_devices(dev, -1, sl28cpld_devs,
+				    ARRAY_SIZE(sl28cpld_devs), NULL,
+				    i2c->irq, NULL);
+}
+
+static const struct of_device_id sl28cpld_of_match[] = {
+	{ .compatible = "kontron,sl28cpld", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, sl28cpld_of_match);
+
+static struct i2c_driver sl28cpld_driver = {
+	.probe_new = sl28cpld_probe,
+	.driver = {
+		.name = "sl28cpld",
+		.of_match_table = of_match_ptr(sl28cpld_of_match),
+	},
+};
+module_i2c_driver(sl28cpld_driver);
+
+MODULE_DESCRIPTION("sl28cpld MFD Core Driver");
+MODULE_LICENSE("GPL");
-- 
2.20.1


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

* [PATCH 06/18] irqchip: add sl28cpld interrupt controller support
  2020-03-17 20:49 [PATCH 00/18] Add support for Kontron sl28cpld Michael Walle
                   ` (4 preceding siblings ...)
  2020-03-17 20:50 ` [PATCH 05/18] mfd: Add support for Kontron sl28cpld management controller Michael Walle
@ 2020-03-17 20:50 ` Michael Walle
  2020-03-18 16:53   ` Guenter Roeck
  2020-03-17 20:50 ` [PATCH 07/18] dt-bindings: watchdog: Add bindings for sl28cpld watchdog Michael Walle
                   ` (11 subsequent siblings)
  17 siblings, 1 reply; 40+ messages in thread
From: Michael Walle @ 2020-03-17 20:50 UTC (permalink / raw)
  To: linux-gpio, devicetree, linux-kernel, linux-hwmon, linux-pwm,
	linux-watchdog, linux-arm-kernel
  Cc: Linus Walleij, Bartosz Golaszewski, Rob Herring, Jean Delvare,
	Guenter Roeck, Lee Jones, Thierry Reding, Uwe Kleine-König,
	Wim Van Sebroeck, Shawn Guo, Li Yang, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Michael Walle

This patch adds support for the interrupt controller inside the sl28
CPLD management controller.

Signed-off-by: Michael Walle <michael@walle.cc>
---
 drivers/irqchip/Kconfig        |  3 ++
 drivers/irqchip/Makefile       |  1 +
 drivers/irqchip/irq-sl28cpld.c | 92 ++++++++++++++++++++++++++++++++++
 drivers/mfd/Kconfig            |  4 +-
 4 files changed, 98 insertions(+), 2 deletions(-)
 create mode 100644 drivers/irqchip/irq-sl28cpld.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 24fe08702ef7..3fd7415c8b55 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -246,6 +246,9 @@ config RENESAS_RZA1_IRQC
 	  Enable support for the Renesas RZ/A1 Interrupt Controller, to use up
 	  to 8 external interrupts with configurable sense select.
 
+config SL28CPLD_INTC
+	bool
+
 config ST_IRQCHIP
 	bool
 	select REGMAP
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index eae0d78cbf22..0f4a37782609 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -105,3 +105,4 @@ obj-$(CONFIG_MADERA_IRQ)		+= irq-madera.o
 obj-$(CONFIG_LS1X_IRQ)			+= irq-ls1x.o
 obj-$(CONFIG_TI_SCI_INTR_IRQCHIP)	+= irq-ti-sci-intr.o
 obj-$(CONFIG_TI_SCI_INTA_IRQCHIP)	+= irq-ti-sci-inta.o
+obj-$(CONFIG_SL28CPLD_INTC)		+= irq-sl28cpld.o
diff --git a/drivers/irqchip/irq-sl28cpld.c b/drivers/irqchip/irq-sl28cpld.c
new file mode 100644
index 000000000000..fa52ed79137b
--- /dev/null
+++ b/drivers/irqchip/irq-sl28cpld.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * SMARC-sAL28 Interrupt core driver.
+ *
+ * Copyright 2019 Kontron Europe GmbH
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+
+#define INTC_IE 0
+#define INTC_IP 1
+
+static const struct regmap_irq sl28cpld_irqs[] = {
+	REGMAP_IRQ_REG_LINE(0, 8),
+	REGMAP_IRQ_REG_LINE(1, 8),
+	REGMAP_IRQ_REG_LINE(2, 8),
+	REGMAP_IRQ_REG_LINE(3, 8),
+	REGMAP_IRQ_REG_LINE(4, 8),
+	REGMAP_IRQ_REG_LINE(5, 8),
+	REGMAP_IRQ_REG_LINE(6, 8),
+	REGMAP_IRQ_REG_LINE(7, 8),
+};
+
+struct sl28cpld_intc {
+	struct regmap *regmap;
+	struct regmap_irq_chip chip;
+	struct regmap_irq_chip_data *irq_data;
+};
+
+static int sl28cpld_intc_probe(struct platform_device *pdev)
+{
+	struct sl28cpld_intc *irqchip;
+	struct resource *res;
+	unsigned int irq;
+	int ret;
+
+	irqchip = devm_kzalloc(&pdev->dev, sizeof(*irqchip), GFP_KERNEL);
+	if (!irqchip)
+		return -ENOMEM;
+
+	if (!pdev->dev.parent)
+		return -ENODEV;
+
+	irqchip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!irqchip->regmap)
+		return -ENODEV;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	res = platform_get_resource(pdev, IORESOURCE_REG, 0);
+	if (!res)
+		return -EINVAL;
+
+	irqchip->chip.name = "sl28cpld-intc";
+	irqchip->chip.irqs = sl28cpld_irqs;
+	irqchip->chip.num_irqs = ARRAY_SIZE(sl28cpld_irqs);
+	irqchip->chip.num_regs = 1;
+	irqchip->chip.status_base = res->start + INTC_IP;
+	irqchip->chip.mask_base = res->start + INTC_IE;
+	irqchip->chip.mask_invert = true,
+	irqchip->chip.ack_base = res->start + INTC_IP;
+
+	ret = devm_regmap_add_irq_chip(&pdev->dev, irqchip->regmap, irq,
+				       IRQF_SHARED | IRQF_ONESHOT, 0,
+				       &irqchip->chip, &irqchip->irq_data);
+	if (ret)
+		return ret;
+	dev_info(&pdev->dev, "registered IRQ %d\n", irq);
+
+	return 0;
+}
+
+static struct platform_driver sl28cpld_intc_driver = {
+	.probe	= sl28cpld_intc_probe,
+	.driver = {
+		.name = "sl28cpld-intc",
+	}
+};
+module_platform_driver(sl28cpld_intc_driver);
+
+MODULE_DESCRIPTION("sl28cpld Interrupt Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 01588c366476..4f741d640705 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -2060,12 +2060,12 @@ config SGI_MFD_IOC3
 	  then say Y. Otherwise say N.
 
 config MFD_SL28CPLD
-	tristate "Kontron sl28 core driver"
+	bool "Kontron sl28 core driver"
 	depends on I2C=y
 	depends on OF
 	select REGMAP_I2C
 	select REGMAP_IRQ
-	select SL28CPLD_IRQ
+	select SL28CPLD_INTC
 	select MFD_CORE
 	help
 	  This option enables support for the board management controller
-- 
2.20.1


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

* [PATCH 07/18] dt-bindings: watchdog: Add bindings for sl28cpld watchdog
  2020-03-17 20:49 [PATCH 00/18] Add support for Kontron sl28cpld Michael Walle
                   ` (5 preceding siblings ...)
  2020-03-17 20:50 ` [PATCH 06/18] irqchip: add sl28cpld interrupt controller support Michael Walle
@ 2020-03-17 20:50 ` Michael Walle
  2020-03-17 20:50 ` [PATCH 08/18] watchdog: add support " Michael Walle
                   ` (10 subsequent siblings)
  17 siblings, 0 replies; 40+ messages in thread
From: Michael Walle @ 2020-03-17 20:50 UTC (permalink / raw)
  To: linux-gpio, devicetree, linux-kernel, linux-hwmon, linux-pwm,
	linux-watchdog, linux-arm-kernel
  Cc: Linus Walleij, Bartosz Golaszewski, Rob Herring, Jean Delvare,
	Guenter Roeck, Lee Jones, Thierry Reding, Uwe Kleine-König,
	Wim Van Sebroeck, Shawn Guo, Li Yang, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Michael Walle

This adds device tree bindings for the watchdog of the sl28cpld
management controller.

Signed-off-by: Michael Walle <michael@walle.cc>
---
 .../watchdog/kontron,sl28cpld-wdt.yaml        | 30 +++++++++++++++++++
 1 file changed, 30 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/watchdog/kontron,sl28cpld-wdt.yaml

diff --git a/Documentation/devicetree/bindings/watchdog/kontron,sl28cpld-wdt.yaml b/Documentation/devicetree/bindings/watchdog/kontron,sl28cpld-wdt.yaml
new file mode 100644
index 000000000000..f446372fce34
--- /dev/null
+++ b/Documentation/devicetree/bindings/watchdog/kontron,sl28cpld-wdt.yaml
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/watchdog/kontron,sl28cpld-wdt.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Watchdog driver for the sl28cpld board management controller
+
+maintainers:
+  - Michael Walle <michael@walle.cc>
+
+description: |
+  This module is part of the sl28cpld multi-function device. For more
+  details see Documentation/devicetree/bindings/mfd/kontron,sl28cpld.yaml.
+
+allOf:
+  - $ref: watchdog.yaml#
+
+properties:
+  compatible:
+    const: kontron,sl28cpld-wdt
+
+  reg:
+    maxItems: 1
+    description: Instance number of the watchdog
+
+required:
+  - compatible
+
+additionalProperties: false
-- 
2.20.1


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

* [PATCH 08/18] watchdog: add support for sl28cpld watchdog
  2020-03-17 20:49 [PATCH 00/18] Add support for Kontron sl28cpld Michael Walle
                   ` (6 preceding siblings ...)
  2020-03-17 20:50 ` [PATCH 07/18] dt-bindings: watchdog: Add bindings for sl28cpld watchdog Michael Walle
@ 2020-03-17 20:50 ` Michael Walle
  2020-03-18  3:17   ` Guenter Roeck
  2020-03-17 20:50 ` [PATCH 09/18] dt-bindings: pwm: Add bindings for sl28cpld PWM controller Michael Walle
                   ` (9 subsequent siblings)
  17 siblings, 1 reply; 40+ messages in thread
From: Michael Walle @ 2020-03-17 20:50 UTC (permalink / raw)
  To: linux-gpio, devicetree, linux-kernel, linux-hwmon, linux-pwm,
	linux-watchdog, linux-arm-kernel
  Cc: Linus Walleij, Bartosz Golaszewski, Rob Herring, Jean Delvare,
	Guenter Roeck, Lee Jones, Thierry Reding, Uwe Kleine-König,
	Wim Van Sebroeck, Shawn Guo, Li Yang, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Michael Walle

This adds support for the watchdog of the sl28cpld board management
controller. This is part of a multi-function device driver.

Signed-off-by: Michael Walle <michael@walle.cc>
---
 drivers/watchdog/Kconfig        |  11 ++
 drivers/watchdog/Makefile       |   1 +
 drivers/watchdog/sl28cpld_wdt.c | 238 ++++++++++++++++++++++++++++++++
 3 files changed, 250 insertions(+)
 create mode 100644 drivers/watchdog/sl28cpld_wdt.c

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 9ea2b43d4b01..c78b90ccc8cf 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -340,6 +340,17 @@ config MLX_WDT
 	  To compile this driver as a module, choose M here: the
 	  module will be called mlx-wdt.
 
+config SL28CPLD_WATCHDOG
+	tristate "Kontron sl28 watchdog"
+	depends on MFD_SL28CPLD
+	select WATCHDOG_CORE
+	help
+	  Say Y here to include support for the watchdog timer
+	  on the Kontron sl28 CPLD.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called sl28cpld_wdt.
+
 # ALPHA Architecture
 
 # ARM Architecture
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 2ee352bf3372..060e2f895fe8 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -223,3 +223,4 @@ obj-$(CONFIG_MENF21BMC_WATCHDOG) += menf21bmc_wdt.o
 obj-$(CONFIG_MENZ069_WATCHDOG) += menz69_wdt.o
 obj-$(CONFIG_RAVE_SP_WATCHDOG) += rave-sp-wdt.o
 obj-$(CONFIG_STPMIC1_WATCHDOG) += stpmic1_wdt.o
+obj-$(CONFIG_SL28CPLD_WATCHDOG) += sl28cpld_wdt.o
diff --git a/drivers/watchdog/sl28cpld_wdt.c b/drivers/watchdog/sl28cpld_wdt.c
new file mode 100644
index 000000000000..5927b7ad0be4
--- /dev/null
+++ b/drivers/watchdog/sl28cpld_wdt.c
@@ -0,0 +1,238 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * SMARC-sAL28 Watchdog driver.
+ *
+ * Copyright 2019 Kontron Europe GmbH
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+
+/*
+ * Watchdog timer block registers.
+ */
+#define SL28CPLD_WDT_CTRL	0
+#define  WDT_CTRL_EN		BIT(0)
+#define  WDT_CTRL_LOCK		BIT(2)
+#define SL28CPLD_WDT_TIMEOUT	1
+#define SL28CPLD_WDT_KICK	2
+#define  WDT_KICK_VALUE		0x6b
+#define SL28CPLD_WDT_COUNT	3
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static int timeout;
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout, "Initial watchdog timeout in seconds");
+
+struct sl28cpld_wdt {
+	struct watchdog_device wdd;
+	struct regmap *regmap;
+	u32 offset;
+};
+
+static int sl28cpld_wdt_ping(struct watchdog_device *wdd)
+{
+	struct sl28cpld_wdt *wdt = watchdog_get_drvdata(wdd);
+
+	return regmap_write(wdt->regmap, wdt->offset + SL28CPLD_WDT_KICK,
+			    WDT_KICK_VALUE);
+}
+
+static int sl28cpld_wdt_start(struct watchdog_device *wdd)
+{
+	struct sl28cpld_wdt *wdt = watchdog_get_drvdata(wdd);
+	unsigned int val;
+
+	val = WDT_CTRL_EN;
+	if (nowayout)
+		val |= WDT_CTRL_LOCK;
+
+	return regmap_update_bits(wdt->regmap, wdt->offset + SL28CPLD_WDT_CTRL,
+				  val, val);
+}
+
+static int sl28cpld_wdt_stop(struct watchdog_device *wdd)
+{
+	struct sl28cpld_wdt *wdt = watchdog_get_drvdata(wdd);
+
+	return regmap_update_bits(wdt->regmap, wdt->offset + SL28CPLD_WDT_CTRL,
+				  WDT_CTRL_EN, 0);
+}
+
+static unsigned int sl28cpld_wdt_status(struct watchdog_device *wdd)
+{
+	struct sl28cpld_wdt *wdt = watchdog_get_drvdata(wdd);
+	unsigned int status;
+	int ret;
+
+	ret = regmap_read(wdt->regmap, wdt->offset + SL28CPLD_WDT_CTRL,
+			  &status);
+	if (ret < 0)
+		return 0;
+
+	/* is the watchdog timer running? */
+	return (status & WDT_CTRL_EN) << WDOG_ACTIVE;
+}
+
+static unsigned int sl28cpld_wdt_get_timeleft(struct watchdog_device *wdd)
+{
+	struct sl28cpld_wdt *wdt = watchdog_get_drvdata(wdd);
+	int ret;
+	unsigned int val;
+
+	ret = regmap_read(wdt->regmap, wdt->offset + SL28CPLD_WDT_COUNT, &val);
+	if (ret < 0)
+		return 0;
+
+	return val;
+}
+
+static int sl28cpld_wdt_set_timeout(struct watchdog_device *wdd,
+				  unsigned int timeout)
+{
+	int ret;
+	struct sl28cpld_wdt *wdt = watchdog_get_drvdata(wdd);
+
+	ret = regmap_write(wdt->regmap, wdt->offset + SL28CPLD_WDT_TIMEOUT,
+			   timeout);
+	if (ret == 0)
+		wdd->timeout = timeout;
+
+	return ret;
+}
+
+static const struct watchdog_info sl28cpld_wdt_info = {
+	.options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+	.identity = "SMARC-sAL28 CPLD watchdog",
+};
+
+static struct watchdog_ops sl28cpld_wdt_ops = {
+	.owner = THIS_MODULE,
+	.start = sl28cpld_wdt_start,
+	.stop = sl28cpld_wdt_stop,
+	.status = sl28cpld_wdt_status,
+	.ping = sl28cpld_wdt_ping,
+	.set_timeout = sl28cpld_wdt_set_timeout,
+	.get_timeleft = sl28cpld_wdt_get_timeleft,
+};
+
+static int sl28cpld_wdt_locked(struct sl28cpld_wdt *wdt)
+{
+	unsigned int val;
+	int ret;
+
+	ret = regmap_read(wdt->regmap, wdt->offset + SL28CPLD_WDT_CTRL, &val);
+	if (ret < 0)
+		return ret;
+
+	return val & WDT_CTRL_LOCK;
+}
+
+static int sl28cpld_wdt_probe(struct platform_device *pdev)
+{
+	struct sl28cpld_wdt *wdt;
+	struct watchdog_device *wdd;
+	struct resource *res;
+	unsigned int val;
+	int ret;
+
+	wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
+	if (!wdt)
+		return -ENOMEM;
+
+	if (!pdev->dev.parent)
+		return -ENODEV;
+
+	wdt->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!wdt->regmap)
+		return -ENODEV;
+
+	res = platform_get_resource(pdev, IORESOURCE_REG, 0);
+	if (res == NULL)
+		return -EINVAL;
+	wdt->offset = res->start;
+
+	/* initialize struct watchdog_device */
+	wdd = &wdt->wdd;
+	wdd->parent = &pdev->dev;
+	wdd->info = &sl28cpld_wdt_info;
+	wdd->ops = &sl28cpld_wdt_ops;
+	wdd->min_timeout = 1;
+	wdd->max_timeout = 255;
+
+	watchdog_set_drvdata(wdd, wdt);
+
+	/* if the watchdog is locked, we set nowayout to true */
+	ret = sl28cpld_wdt_locked(wdt);
+	if (ret < 0)
+		return ret;
+	if (ret)
+		nowayout = true;
+	watchdog_set_nowayout(wdd, nowayout);
+
+	/*
+	 * Initial timeout value, can either be set by kernel parameter or by
+	 * the device tree. If both are not given the current value is used.
+	 */
+	watchdog_init_timeout(wdd, timeout, &pdev->dev);
+	if (wdd->timeout) {
+		sl28cpld_wdt_set_timeout(wdd, wdd->timeout);
+	} else {
+		ret = regmap_read(wdt->regmap,
+				  wdt->offset + SL28CPLD_WDT_TIMEOUT, &val);
+		if (ret < 0)
+			return ret;
+		wdd->timeout = val;
+	}
+
+	ret = watchdog_register_device(wdd);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to register watchdog device\n");
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, wdt);
+
+	dev_info(&pdev->dev, "CPLD watchdog: initial timeout %d sec%s\n",
+		wdd->timeout, nowayout ? ", nowayout" : "");
+
+	return 0;
+}
+
+static int sl28cpld_wdt_remove(struct platform_device *pdev)
+{
+	struct sl28cpld_wdt *wdt = platform_get_drvdata(pdev);
+
+	watchdog_unregister_device(&wdt->wdd);
+
+	return 0;
+}
+
+static void sl28cpld_wdt_shutdown(struct platform_device *pdev)
+{
+	struct sl28cpld_wdt *wdt = platform_get_drvdata(pdev);
+
+	sl28cpld_wdt_stop(&wdt->wdd);
+}
+
+static struct platform_driver sl28cpld_wdt_driver = {
+	.probe = sl28cpld_wdt_probe,
+	.remove = sl28cpld_wdt_remove,
+	.shutdown = sl28cpld_wdt_shutdown,
+	.driver = {
+		.name = "sl28cpld-wdt",
+	},
+};
+module_platform_driver(sl28cpld_wdt_driver);
+
+MODULE_DESCRIPTION("sl28cpld Watchdog Driver");
+MODULE_LICENSE("GPL");
-- 
2.20.1


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

* [PATCH 09/18] dt-bindings: pwm: Add bindings for sl28cpld PWM controller
  2020-03-17 20:49 [PATCH 00/18] Add support for Kontron sl28cpld Michael Walle
                   ` (7 preceding siblings ...)
  2020-03-17 20:50 ` [PATCH 08/18] watchdog: add support " Michael Walle
@ 2020-03-17 20:50 ` Michael Walle
  2020-03-17 20:50 ` [PATCH 10/18] pwm: add support " Michael Walle
                   ` (8 subsequent siblings)
  17 siblings, 0 replies; 40+ messages in thread
From: Michael Walle @ 2020-03-17 20:50 UTC (permalink / raw)
  To: linux-gpio, devicetree, linux-kernel, linux-hwmon, linux-pwm,
	linux-watchdog, linux-arm-kernel
  Cc: Linus Walleij, Bartosz Golaszewski, Rob Herring, Jean Delvare,
	Guenter Roeck, Lee Jones, Thierry Reding, Uwe Kleine-König,
	Wim Van Sebroeck, Shawn Guo, Li Yang, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Michael Walle

This adds device tree bindings for the PWM controller of the sl28cpld
management controller.

Signed-off-by: Michael Walle <michael@walle.cc>
---
 .../bindings/pwm/kontron,sl28cpld-pwm.yaml    | 33 +++++++++++++++++++
 1 file changed, 33 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pwm/kontron,sl28cpld-pwm.yaml

diff --git a/Documentation/devicetree/bindings/pwm/kontron,sl28cpld-pwm.yaml b/Documentation/devicetree/bindings/pwm/kontron,sl28cpld-pwm.yaml
new file mode 100644
index 000000000000..f4640dbae692
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/kontron,sl28cpld-pwm.yaml
@@ -0,0 +1,33 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pwm/kontron,sl28cpld-pwm.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: PWM driver for the sl28cpld board management controller
+
+maintainers:
+  - Michael Walle <michael@walle.cc>
+
+description: |
+  This module is part of the sl28cpld multi-function device. For more
+  details see Documentation/devicetree/bindings/mfd/kontron,sl28cpld.yaml.
+
+allOf:
+  - $ref: pwm.yaml#
+
+properties:
+  compatible:
+    const: kontron,sl28cpld-pwm
+
+  reg:
+    maxItems: 1
+    description: Instance number of the PWM controller
+
+  "#pwm-cells":
+    const: 2
+
+required:
+  - compatible
+
+additionalProperties: false
-- 
2.20.1


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

* [PATCH 10/18] pwm: add support for sl28cpld PWM controller
  2020-03-17 20:49 [PATCH 00/18] Add support for Kontron sl28cpld Michael Walle
                   ` (8 preceding siblings ...)
  2020-03-17 20:50 ` [PATCH 09/18] dt-bindings: pwm: Add bindings for sl28cpld PWM controller Michael Walle
@ 2020-03-17 20:50 ` Michael Walle
  2020-03-17 20:50 ` [PATCH 11/18] dt-bindings: gpio: Add bindings for sl28cpld GPIO controller Michael Walle
                   ` (7 subsequent siblings)
  17 siblings, 0 replies; 40+ messages in thread
From: Michael Walle @ 2020-03-17 20:50 UTC (permalink / raw)
  To: linux-gpio, devicetree, linux-kernel, linux-hwmon, linux-pwm,
	linux-watchdog, linux-arm-kernel
  Cc: Linus Walleij, Bartosz Golaszewski, Rob Herring, Jean Delvare,
	Guenter Roeck, Lee Jones, Thierry Reding, Uwe Kleine-König,
	Wim Van Sebroeck, Shawn Guo, Li Yang, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Michael Walle

This adds support for the PWM controller of the sl28cpld board
management controller. This is part of a multi-function device driver.

Signed-off-by: Michael Walle <michael@walle.cc>
---
 drivers/pwm/Kconfig        |  10 ++
 drivers/pwm/Makefile       |   1 +
 drivers/pwm/pwm-sl28cpld.c | 192 +++++++++++++++++++++++++++++++++++++
 3 files changed, 203 insertions(+)
 create mode 100644 drivers/pwm/pwm-sl28cpld.c

diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 30190beeb6e9..516d70873b4c 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -413,6 +413,16 @@ config PWM_SIFIVE
 	  To compile this driver as a module, choose M here: the module
 	  will be called pwm-sifive.
 
+config PWM_SL28CPLD
+	tristate "Kontron sl28 PWM support"
+	depends on MFD_SL28CPLD
+	help
+	  Generic PWM framework driver for board management controller
+	  found on the Kontron sl28 CPLD.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called pwm-sl28cpld.
+
 config PWM_SPEAR
 	tristate "STMicroelectronics SPEAr PWM support"
 	depends on PLAT_SPEAR
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 9a475073dafc..2c2b569dcde9 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_PWM_RENESAS_TPU)	+= pwm-renesas-tpu.o
 obj-$(CONFIG_PWM_ROCKCHIP)	+= pwm-rockchip.o
 obj-$(CONFIG_PWM_SAMSUNG)	+= pwm-samsung.o
 obj-$(CONFIG_PWM_SIFIVE)	+= pwm-sifive.o
+obj-$(CONFIG_PWM_SL28CPLD)	+= pwm-sl28cpld.o
 obj-$(CONFIG_PWM_SPEAR)		+= pwm-spear.o
 obj-$(CONFIG_PWM_SPRD)		+= pwm-sprd.o
 obj-$(CONFIG_PWM_STI)		+= pwm-sti.o
diff --git a/drivers/pwm/pwm-sl28cpld.c b/drivers/pwm/pwm-sl28cpld.c
new file mode 100644
index 000000000000..758f46898da6
--- /dev/null
+++ b/drivers/pwm/pwm-sl28cpld.c
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * SMARC-sAL28 PWM driver.
+ *
+ * Copyright 2019 Kontron Europe GmbH
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/bitfield.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+
+/*
+ * PWM timer block registers.
+ */
+#define SL28CPLD_PWM_CTRL	0
+#define   PWM_ENABLE		BIT(7)
+#define   PWM_MODE_250HZ	0
+#define   PWM_MODE_500HZ	1
+#define   PWM_MODE_1KHZ		2
+#define   PWM_MODE_2KHZ		3
+#define   PWM_MODE_MASK		GENMASK(1, 0)
+#define SL28CPLD_PWM_CYCLE	1
+#define   PWM_CYCLE_MAX		0x7f
+
+struct sl28cpld_pwm {
+	struct pwm_chip pwm_chip;
+	struct regmap *regmap;
+	u32 offset;
+};
+
+struct sl28cpld_pwm_periods {
+	u8 ctrl;
+	unsigned long duty_cycle;
+};
+
+struct sl28cpld_pwm_config {
+	unsigned long period_ns;
+	u8 max_duty_cycle;
+};
+
+static struct sl28cpld_pwm_config sl28cpld_pwm_config[] = {
+	[PWM_MODE_250HZ] = { .period_ns = 4000000, .max_duty_cycle = 0x80 },
+	[PWM_MODE_500HZ] = { .period_ns = 2000000, .max_duty_cycle = 0x40 },
+	[PWM_MODE_1KHZ] = { .period_ns = 1000000, .max_duty_cycle = 0x20 },
+	[PWM_MODE_2KHZ] = { .period_ns =  500000, .max_duty_cycle = 0x10 },
+};
+
+static inline struct sl28cpld_pwm *to_sl28cpld_pwm(struct pwm_chip *chip)
+{
+	return container_of(chip, struct sl28cpld_pwm, pwm_chip);
+}
+
+static void sl28cpld_pwm_get_state(struct pwm_chip *chip,
+				   struct pwm_device *pwm,
+				   struct pwm_state *state)
+{
+	struct sl28cpld_pwm *spc = to_sl28cpld_pwm(chip);
+	static struct sl28cpld_pwm_config *config;
+	unsigned int reg;
+	unsigned long cycle;
+	unsigned int mode;
+
+	regmap_read(spc->regmap, spc->offset + SL28CPLD_PWM_CTRL, &reg);
+
+	state->enabled = reg & PWM_ENABLE;
+
+	mode = FIELD_GET(PWM_MODE_MASK, reg);
+	config = &sl28cpld_pwm_config[mode];
+	state->period = config->period_ns;
+
+	regmap_read(spc->regmap, spc->offset + SL28CPLD_PWM_CYCLE, &reg);
+	cycle = reg * config->period_ns;
+	state->duty_cycle = DIV_ROUND_CLOSEST_ULL(cycle,
+						  config->max_duty_cycle);
+}
+
+static int sl28cpld_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+			      const struct pwm_state *state)
+{
+	struct sl28cpld_pwm *spc = to_sl28cpld_pwm(chip);
+	struct sl28cpld_pwm_config *config;
+	unsigned long long cycle;
+	int ret;
+	int mode;
+	u8 ctrl;
+
+	/* update config, first search best matching period */
+	for (mode = 0; mode < ARRAY_SIZE(sl28cpld_pwm_config); mode++) {
+		config = &sl28cpld_pwm_config[mode];
+		if (state->period == config->period_ns)
+			break;
+	}
+
+	if (mode == ARRAY_SIZE(sl28cpld_pwm_config))
+		return -EINVAL;
+
+	ctrl = FIELD_PREP(PWM_MODE_MASK, mode);
+	if (state->enabled)
+		ctrl |= PWM_ENABLE;
+
+	cycle = state->duty_cycle * config->max_duty_cycle;
+	do_div(cycle, state->period);
+
+	/*
+	 * The hardware doesn't allow to set max_duty_cycle if the
+	 * 250Hz mode is enabled. But since this is "all-high" output
+	 * just use the 500Hz mode with the duty cycle to max value.
+	 */
+	if (cycle == config->max_duty_cycle) {
+		ctrl &= ~PWM_MODE_MASK;
+		ctrl |= FIELD_PREP(PWM_MODE_MASK, PWM_MODE_500HZ);
+		cycle = PWM_CYCLE_MAX;
+	}
+
+	ret = regmap_write(spc->regmap,
+			   spc->offset + SL28CPLD_PWM_CTRL, ctrl);
+	if (ret)
+		return ret;
+
+	return regmap_write(spc->regmap,
+			    spc->offset + SL28CPLD_PWM_CYCLE, (u8)cycle);
+}
+
+static const struct pwm_ops sl28cpld_pwm_ops = {
+	.apply = sl28cpld_pwm_apply,
+	.get_state = sl28cpld_pwm_get_state,
+	.owner = THIS_MODULE,
+};
+
+static int sl28cpld_pwm_probe(struct platform_device *pdev)
+{
+	struct sl28cpld_pwm *pwm;
+	struct pwm_chip *chip;
+	struct resource *res;
+	int ret;
+
+	pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
+	if (!pwm)
+		return -ENOMEM;
+
+	if (!pdev->dev.parent)
+		return -ENODEV;
+
+	pwm->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!pwm->regmap)
+		return -ENODEV;
+
+	res = platform_get_resource(pdev, IORESOURCE_REG, 0);
+	if (!res)
+		return -EINVAL;
+	pwm->offset = res->start;
+
+	/* initialize struct gpio_chip */
+	chip = &pwm->pwm_chip;
+	chip->dev = &pdev->dev;
+	chip->ops = &sl28cpld_pwm_ops;
+	chip->base = -1;
+	chip->npwm = 1;
+
+	ret = pwmchip_add(&pwm->pwm_chip);
+	if (ret < 0)
+		return ret;
+
+	platform_set_drvdata(pdev, pwm);
+
+	return 0;
+}
+
+static int sl28cpld_pwm_remove(struct platform_device *pdev)
+{
+	struct sl28cpld_pwm *pwm = platform_get_drvdata(pdev);
+
+	return pwmchip_remove(&pwm->pwm_chip);
+}
+
+static struct platform_driver sl28cpld_pwm_driver = {
+	.probe = sl28cpld_pwm_probe,
+	.remove	= sl28cpld_pwm_remove,
+	.driver = {
+		.name = "sl28cpld-pwm",
+	},
+};
+module_platform_driver(sl28cpld_pwm_driver);
+
+MODULE_DESCRIPTION("sl28cpld PWM Driver");
+MODULE_LICENSE("GPL");
-- 
2.20.1


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

* [PATCH 11/18] dt-bindings: gpio: Add bindings for sl28cpld GPIO controller
  2020-03-17 20:49 [PATCH 00/18] Add support for Kontron sl28cpld Michael Walle
                   ` (9 preceding siblings ...)
  2020-03-17 20:50 ` [PATCH 10/18] pwm: add support " Michael Walle
@ 2020-03-17 20:50 ` Michael Walle
  2020-03-17 20:50 ` [PATCH 12/18] gpio: add support for the " Michael Walle
                   ` (6 subsequent siblings)
  17 siblings, 0 replies; 40+ messages in thread
From: Michael Walle @ 2020-03-17 20:50 UTC (permalink / raw)
  To: linux-gpio, devicetree, linux-kernel, linux-hwmon, linux-pwm,
	linux-watchdog, linux-arm-kernel
  Cc: Linus Walleij, Bartosz Golaszewski, Rob Herring, Jean Delvare,
	Guenter Roeck, Lee Jones, Thierry Reding, Uwe Kleine-König,
	Wim Van Sebroeck, Shawn Guo, Li Yang, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Michael Walle

This adds device tree bindings for the GPIO controller of the sl28 board
management controller.

Signed-off-by: Michael Walle <michael@walle.cc>
---
 .../bindings/gpio/kontron,sl28cpld-gpio.yaml  | 46 +++++++++++++++++++
 1 file changed, 46 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/kontron,sl28cpld-gpio.yaml

diff --git a/Documentation/devicetree/bindings/gpio/kontron,sl28cpld-gpio.yaml b/Documentation/devicetree/bindings/gpio/kontron,sl28cpld-gpio.yaml
new file mode 100644
index 000000000000..a6af8c4b622f
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/kontron,sl28cpld-gpio.yaml
@@ -0,0 +1,46 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/gpio/kontron,sl28cpld-gpio.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: GPIO driver for the sl28cpld board management controller
+
+maintainers:
+  - Michael Walle <michael@walle.cc>
+
+description: |
+  This module is part of the sl28cpld multi-function device. For more
+  details see Documentation/devicetree/bindings/mfd/kontron,sl28cpld.yaml.
+
+properties:
+  compatible:
+    enum:
+      - kontron,sl28cpld-gpio
+      - kontron,sl28cpld-gpi
+      - kontron,sl28cpld-gpo
+
+  reg:
+    maxItems: 1
+    description: Instance number of the GPIO controller
+
+  "#interrupt-cells":
+    const: 2
+
+  interrupt-controller: true
+
+  "#gpio-cells":
+    const: 2
+
+  gpio-controller: true
+
+  gpio-line-names:
+      minItems: 1
+      maxItems: 8
+
+required:
+  - compatible
+  - "#gpio-cells"
+  - gpio-controller
+
+additionalProperties: false
-- 
2.20.1


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

* [PATCH 12/18] gpio: add support for the sl28cpld GPIO controller
  2020-03-17 20:49 [PATCH 00/18] Add support for Kontron sl28cpld Michael Walle
                   ` (10 preceding siblings ...)
  2020-03-17 20:50 ` [PATCH 11/18] dt-bindings: gpio: Add bindings for sl28cpld GPIO controller Michael Walle
@ 2020-03-17 20:50 ` Michael Walle
  2020-03-18  9:14   ` Bartosz Golaszewski
  2020-03-17 20:50 ` [PATCH 13/18] dt-bindings: hwmon: Add bindings for sl28cpld hardware monitoring Michael Walle
                   ` (5 subsequent siblings)
  17 siblings, 1 reply; 40+ messages in thread
From: Michael Walle @ 2020-03-17 20:50 UTC (permalink / raw)
  To: linux-gpio, devicetree, linux-kernel, linux-hwmon, linux-pwm,
	linux-watchdog, linux-arm-kernel
  Cc: Linus Walleij, Bartosz Golaszewski, Rob Herring, Jean Delvare,
	Guenter Roeck, Lee Jones, Thierry Reding, Uwe Kleine-König,
	Wim Van Sebroeck, Shawn Guo, Li Yang, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Michael Walle

This adds support for the GPIO controller of the sl28 board management
controller. This driver is part of a multi-function device.

Signed-off-by: Michael Walle <michael@walle.cc>
---
 drivers/gpio/Kconfig         |  11 ++
 drivers/gpio/Makefile        |   1 +
 drivers/gpio/gpio-sl28cpld.c | 332 +++++++++++++++++++++++++++++++++++
 3 files changed, 344 insertions(+)
 create mode 100644 drivers/gpio/gpio-sl28cpld.c

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 3cbf8882a0dd..516e47017ef5 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -1211,6 +1211,17 @@ config GPIO_RC5T583
 	  This driver provides the support for driving/reading the gpio pins
 	  of RC5T583 device through standard gpio library.
 
+config GPIO_SL28CPLD
+	tristate "Kontron sl28 GPIO"
+	depends on MFD_SL28CPLD
+	depends on OF_GPIO
+	select GPIOLIB_IRQCHIP
+	help
+	  This enables support for the GPIOs found on the Kontron sl28 CPLD.
+
+	  This driver can also be built as a module. If so, the module will be
+	  called gpio-sl28cpld.
+
 config GPIO_STMPE
 	bool "STMPE GPIOs"
 	depends on MFD_STMPE
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 0b571264ddbc..0ca2d52c78e8 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -127,6 +127,7 @@ obj-$(CONFIG_GPIO_SCH311X)		+= gpio-sch311x.o
 obj-$(CONFIG_GPIO_SCH)			+= gpio-sch.o
 obj-$(CONFIG_GPIO_SIFIVE)		+= gpio-sifive.o
 obj-$(CONFIG_GPIO_SIOX)			+= gpio-siox.o
+obj-$(CONFIG_GPIO_SL28CPLD)		+= gpio-sl28cpld.o
 obj-$(CONFIG_GPIO_SODAVILLE)		+= gpio-sodaville.o
 obj-$(CONFIG_GPIO_SPEAR_SPICS)		+= gpio-spear-spics.o
 obj-$(CONFIG_GPIO_SPRD)			+= gpio-sprd.o
diff --git a/drivers/gpio/gpio-sl28cpld.c b/drivers/gpio/gpio-sl28cpld.c
new file mode 100644
index 000000000000..94f82013882f
--- /dev/null
+++ b/drivers/gpio/gpio-sl28cpld.c
@@ -0,0 +1,332 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * SMARC-sAL28 GPIO driver.
+ *
+ * Copyright 2019 Kontron Europe GmbH
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/interrupt.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+#include <linux/gpio/driver.h>
+
+#define GPIO_REG_DIR	0
+#define GPIO_REG_OUT	1
+#define GPIO_REG_IN	2
+#define GPIO_REG_IE	3
+#define GPIO_REG_IP	4
+
+#define GPI_REG_IN	0
+
+#define GPO_REG_OUT	0
+
+enum sl28cpld_gpio_type {
+	sl28cpld_gpio,
+	sl28cpld_gpi,
+	sl28cpld_gpo,
+};
+
+struct sl28cpld_gpio {
+	struct gpio_chip gpio_chip;
+	struct irq_chip irq_chip;
+	struct regmap *regmap;
+	u32 offset;
+	struct mutex lock;
+	u8 ie;
+};
+
+static void sl28cpld_gpio_set_reg(struct gpio_chip *chip, unsigned int reg,
+				  unsigned int offset, int value)
+{
+	struct sl28cpld_gpio *gpio = gpiochip_get_data(chip);
+	unsigned int mask = 1 << offset;
+	unsigned int val = value << offset;
+
+	regmap_update_bits(gpio->regmap, gpio->offset + reg, mask, val);
+}
+
+static void sl28cpld_gpio_set(struct gpio_chip *chip, unsigned int offset,
+			      int value)
+{
+	sl28cpld_gpio_set_reg(chip, GPIO_REG_OUT, offset, value);
+}
+
+static void sl28cpld_gpo_set(struct gpio_chip *chip, unsigned int offset,
+			     int value)
+{
+	sl28cpld_gpio_set_reg(chip, GPO_REG_OUT, offset, value);
+}
+
+static int sl28cpld_gpio_get_reg(struct gpio_chip *chip, unsigned int reg,
+				 unsigned int offset)
+{
+	struct sl28cpld_gpio *gpio = gpiochip_get_data(chip);
+	unsigned int mask = 1 << offset;
+	unsigned int val;
+	int ret;
+
+	ret = regmap_read(gpio->regmap, gpio->offset + reg, &val);
+	if (ret)
+		return ret;
+
+	return (val & mask) ? 1 : 0;
+}
+
+static int sl28cpld_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+	return sl28cpld_gpio_get_reg(chip, GPIO_REG_IN, offset);
+}
+
+static int sl28cpld_gpi_get(struct gpio_chip *chip, unsigned int offset)
+{
+	return sl28cpld_gpio_get_reg(chip, GPI_REG_IN, offset);
+}
+
+static int sl28cpld_gpio_get_direction(struct gpio_chip *chip,
+				       unsigned int offset)
+{
+	struct sl28cpld_gpio *gpio = gpiochip_get_data(chip);
+	unsigned int reg;
+	int ret;
+
+	ret = regmap_read(gpio->regmap, gpio->offset + GPIO_REG_DIR, &reg);
+	if (ret)
+		return ret;
+
+	if (reg & (1 << offset))
+		return GPIO_LINE_DIRECTION_OUT;
+	else
+		return GPIO_LINE_DIRECTION_IN;
+}
+
+static int sl28cpld_gpio_set_direction(struct gpio_chip *chip,
+				       unsigned int offset,
+				       bool output)
+{
+	struct sl28cpld_gpio *gpio = gpiochip_get_data(chip);
+	unsigned int mask = 1 << offset;
+	unsigned int val = (output) ? mask : 0;
+
+	return regmap_update_bits(gpio->regmap, gpio->offset + GPIO_REG_DIR,
+				  mask, val);
+
+}
+
+static int sl28cpld_gpio_direction_input(struct gpio_chip *chip,
+					 unsigned int offset)
+{
+	return sl28cpld_gpio_set_direction(chip, offset, false);
+}
+
+static int sl28cpld_gpio_direction_output(struct gpio_chip *chip,
+					  unsigned int offset, int value)
+{
+	sl28cpld_gpio_set_reg(chip, GPIO_REG_OUT, offset, value);
+	return sl28cpld_gpio_set_direction(chip, offset, true);
+}
+
+static void sl28cpld_gpio_irq_lock(struct irq_data *data)
+{
+	struct sl28cpld_gpio *gpio =
+		gpiochip_get_data(irq_data_get_irq_chip_data(data));
+
+	mutex_lock(&gpio->lock);
+}
+
+static void sl28cpld_gpio_irq_sync_unlock(struct irq_data *data)
+{
+	struct sl28cpld_gpio *gpio =
+		gpiochip_get_data(irq_data_get_irq_chip_data(data));
+
+	regmap_write(gpio->regmap, gpio->offset + GPIO_REG_IE, gpio->ie);
+	mutex_unlock(&gpio->lock);
+}
+
+static void sl28cpld_gpio_irq_disable(struct irq_data *data)
+{
+	struct sl28cpld_gpio *gpio =
+		gpiochip_get_data(irq_data_get_irq_chip_data(data));
+
+	if (data->hwirq >= 8)
+		return;
+
+	gpio->ie &= ~(1 << data->hwirq);
+}
+
+static void sl28cpld_gpio_irq_enable(struct irq_data *data)
+{
+	struct sl28cpld_gpio *gpio =
+		gpiochip_get_data(irq_data_get_irq_chip_data(data));
+
+	if (data->hwirq >= 8)
+		return;
+
+	gpio->ie |= (1 << data->hwirq);
+}
+
+static int sl28cpld_gpio_irq_set_type(struct irq_data *data, unsigned int type)
+{
+	/* only edge triggered interrupts on both edges are supported */
+	return (type == IRQ_TYPE_EDGE_BOTH) ? 0 : -EINVAL;
+}
+
+static irqreturn_t sl28cpld_gpio_irq_thread(int irq, void *data)
+{
+	struct sl28cpld_gpio *gpio = data;
+	unsigned int ip;
+	unsigned int virq;
+	int pin;
+	int ret;
+
+	ret = regmap_read(gpio->regmap, gpio->offset + GPIO_REG_IP, &ip);
+	if (ret)
+		return IRQ_NONE;
+
+	/* mask other pending interrupts which are not enabled */
+	ip &= gpio->ie;
+
+	/* ack the interrupts */
+	regmap_write(gpio->regmap, gpio->offset + GPIO_REG_IP, ip);
+
+	/* and handle them */
+	while (ip) {
+		pin = __ffs(ip);
+		ip &= ~BIT(pin);
+
+		virq = irq_find_mapping(gpio->gpio_chip.irq.domain, pin);
+		if (virq)
+			handle_nested_irq(virq);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int sl28_cpld_gpio_irq_init(struct platform_device *pdev, int irq)
+{
+	struct sl28cpld_gpio *gpio = platform_get_drvdata(pdev);
+	struct irq_chip *irq_chip = &gpio->irq_chip;
+	int ret;
+
+	irq_chip->name = "sl28cpld-gpio-irq",
+	irq_chip->irq_bus_lock = sl28cpld_gpio_irq_lock,
+	irq_chip->irq_bus_sync_unlock = sl28cpld_gpio_irq_sync_unlock,
+	irq_chip->irq_disable = sl28cpld_gpio_irq_disable,
+	irq_chip->irq_enable = sl28cpld_gpio_irq_enable,
+	irq_chip->irq_set_type = sl28cpld_gpio_irq_set_type,
+	irq_chip->flags = IRQCHIP_SKIP_SET_WAKE,
+
+	ret = gpiochip_irqchip_add_nested(&gpio->gpio_chip, irq_chip, 0,
+					  handle_simple_irq, IRQ_TYPE_NONE);
+	if (ret)
+		return ret;
+
+	ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+					sl28cpld_gpio_irq_thread,
+					IRQF_SHARED | IRQF_ONESHOT,
+					pdev->name, gpio);
+	if (ret)
+		return ret;
+
+	gpiochip_set_nested_irqchip(&gpio->gpio_chip, irq_chip, irq);
+
+	return 0;
+}
+
+static int sl28cpld_gpio_probe(struct platform_device *pdev)
+{
+	enum sl28cpld_gpio_type type =
+		platform_get_device_id(pdev)->driver_data;
+	struct device_node *np = pdev->dev.of_node;
+	struct sl28cpld_gpio *gpio;
+	struct gpio_chip *chip;
+	struct resource *res;
+	bool irq_support = false;
+	int ret;
+	int irq;
+
+	gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
+	if (!gpio)
+		return -ENOMEM;
+
+	if (!pdev->dev.parent)
+		return -ENODEV;
+
+	gpio->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!gpio->regmap)
+		return -ENODEV;
+
+	res = platform_get_resource(pdev, IORESOURCE_REG, 0);
+	if (!res)
+		return -EINVAL;
+	gpio->offset = res->start;
+
+	/* initialize struct gpio_chip */
+	mutex_init(&gpio->lock);
+	chip = &gpio->gpio_chip;
+	chip->parent = &pdev->dev;
+	chip->label = dev_name(&pdev->dev);
+	chip->owner = THIS_MODULE;
+	chip->can_sleep = true;
+	chip->base = -1;
+	chip->ngpio = 8;
+
+	switch (type) {
+	case sl28cpld_gpio:
+		chip->get_direction = sl28cpld_gpio_get_direction;
+		chip->direction_input = sl28cpld_gpio_direction_input;
+		chip->direction_output = sl28cpld_gpio_direction_output;
+		chip->get = sl28cpld_gpio_get;
+		chip->set = sl28cpld_gpio_set;
+		irq_support = true;
+		break;
+	case sl28cpld_gpo:
+		chip->set = sl28cpld_gpo_set;
+		chip->get = sl28cpld_gpi_get;
+		break;
+	case sl28cpld_gpi:
+		chip->get = sl28cpld_gpi_get;
+		break;
+	}
+
+	ret = devm_gpiochip_add_data(&pdev->dev, chip, gpio);
+	if (ret < 0)
+		return ret;
+
+	platform_set_drvdata(pdev, gpio);
+
+	if (irq_support && of_property_read_bool(np, "interrupt-controller")) {
+		irq = platform_get_irq(pdev, 0);
+		if (irq < 0)
+			return ret;
+
+		ret = sl28_cpld_gpio_irq_init(pdev, irq);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static const struct platform_device_id sl28cpld_gpio_id_table[] = {
+	{"sl28cpld-gpio", sl28cpld_gpio},
+	{"sl28cpld-gpi", sl28cpld_gpi},
+	{"sl28cpld-gpo", sl28cpld_gpo},
+};
+MODULE_DEVICE_TABLE(platform, sl28cpld_gpio_id_table);
+
+static struct platform_driver sl28cpld_gpio_driver = {
+	.probe = sl28cpld_gpio_probe,
+	.id_table = sl28cpld_gpio_id_table,
+	.driver = {
+		.name = "sl28cpld-gpio",
+	},
+};
+module_platform_driver(sl28cpld_gpio_driver);
+
+MODULE_DESCRIPTION("sl28cpld GPIO Driver");
+MODULE_LICENSE("GPL");
-- 
2.20.1


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

* [PATCH 13/18] dt-bindings: hwmon: Add bindings for sl28cpld hardware monitoring
  2020-03-17 20:49 [PATCH 00/18] Add support for Kontron sl28cpld Michael Walle
                   ` (11 preceding siblings ...)
  2020-03-17 20:50 ` [PATCH 12/18] gpio: add support for the " Michael Walle
@ 2020-03-17 20:50 ` Michael Walle
  2020-03-17 20:50 ` [PATCH 14/18] hwmon: add support for the sl28cpld hardware monitoring controller Michael Walle
                   ` (4 subsequent siblings)
  17 siblings, 0 replies; 40+ messages in thread
From: Michael Walle @ 2020-03-17 20:50 UTC (permalink / raw)
  To: linux-gpio, devicetree, linux-kernel, linux-hwmon, linux-pwm,
	linux-watchdog, linux-arm-kernel
  Cc: Linus Walleij, Bartosz Golaszewski, Rob Herring, Jean Delvare,
	Guenter Roeck, Lee Jones, Thierry Reding, Uwe Kleine-König,
	Wim Van Sebroeck, Shawn Guo, Li Yang, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Michael Walle

This adds device tree bindings for the hardware monitoring controller of
the sl28cpld board management controller. At the moment there is only
one flavor, namely the fan supervisor.

Signed-off-by: Michael Walle <michael@walle.cc>
---
 .../hwmon/kontron,sl28cpld-hwmon.yaml         | 28 +++++++++++++++++++
 1 file changed, 28 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/hwmon/kontron,sl28cpld-hwmon.yaml

diff --git a/Documentation/devicetree/bindings/hwmon/kontron,sl28cpld-hwmon.yaml b/Documentation/devicetree/bindings/hwmon/kontron,sl28cpld-hwmon.yaml
new file mode 100644
index 000000000000..b3f90d51e2e5
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/kontron,sl28cpld-hwmon.yaml
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/kontron,sl28cpld-hwmon.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Hardware monitoring driver for the sl28cpld board management controller
+
+maintainers:
+  - Michael Walle <michael@walle.cc>
+
+description: |
+  This module is part of the sl28cpld multi-function device. For more
+  details see Documentation/devicetree/bindings/mfd/kontron,sl28cpld.yaml.
+
+properties:
+  compatible:
+    enum:
+      - kontron,sl28cpld-fan
+
+  reg:
+    maxItems: 1
+    description: Instance number of the monitoring controller
+
+required:
+  - compatible
+
+additionalProperties: false
-- 
2.20.1


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

* [PATCH 14/18] hwmon: add support for the sl28cpld hardware monitoring controller
  2020-03-17 20:49 [PATCH 00/18] Add support for Kontron sl28cpld Michael Walle
                   ` (12 preceding siblings ...)
  2020-03-17 20:50 ` [PATCH 13/18] dt-bindings: hwmon: Add bindings for sl28cpld hardware monitoring Michael Walle
@ 2020-03-17 20:50 ` Michael Walle
  2020-03-18  3:27   ` Guenter Roeck
  2020-03-17 20:50 ` [PATCH 15/18] arm64: dts: freescale: sl28: enable sl28cpld Michael Walle
                   ` (3 subsequent siblings)
  17 siblings, 1 reply; 40+ messages in thread
From: Michael Walle @ 2020-03-17 20:50 UTC (permalink / raw)
  To: linux-gpio, devicetree, linux-kernel, linux-hwmon, linux-pwm,
	linux-watchdog, linux-arm-kernel
  Cc: Linus Walleij, Bartosz Golaszewski, Rob Herring, Jean Delvare,
	Guenter Roeck, Lee Jones, Thierry Reding, Uwe Kleine-König,
	Wim Van Sebroeck, Shawn Guo, Li Yang, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Michael Walle

This adds support for the hardware monitoring controller of the sl28cpld
board management controller. This driver is part of a multi-function
device.

Signed-off-by: Michael Walle <michael@walle.cc>
---
 drivers/hwmon/Kconfig          |  10 +++
 drivers/hwmon/Makefile         |   1 +
 drivers/hwmon/sl28cpld-hwmon.c | 146 +++++++++++++++++++++++++++++++++
 3 files changed, 157 insertions(+)
 create mode 100644 drivers/hwmon/sl28cpld-hwmon.c

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 05a30832c6ba..c98716f78cfa 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1412,6 +1412,16 @@ config SENSORS_RASPBERRYPI_HWMON
 	  This driver can also be built as a module. If so, the module
 	  will be called raspberrypi-hwmon.
 
+config SENSORS_SL28CPLD
+	tristate "Kontron's SMARC-sAL28 hardware monitoring driver"
+	depends on MFD_SL28CPLD
+	help
+	  If you say yes here you get support for a fan connected to the
+	  input of the SMARC connector of Kontron's SMARC-sAL28 module.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called sl28cpld-hwmon.
+
 config SENSORS_SHT15
 	tristate "Sensiron humidity and temperature sensors. SHT15 and compat."
 	depends on GPIOLIB || COMPILE_TEST
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index b0b9c8e57176..dfb0f8cda2dd 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -155,6 +155,7 @@ obj-$(CONFIG_SENSORS_S3C)	+= s3c-hwmon.o
 obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o
 obj-$(CONFIG_SENSORS_SCH5627)	+= sch5627.o
 obj-$(CONFIG_SENSORS_SCH5636)	+= sch5636.o
+obj-$(CONFIG_SENSORS_SL28CPLD)	+= sl28cpld-hwmon.o
 obj-$(CONFIG_SENSORS_SHT15)	+= sht15.o
 obj-$(CONFIG_SENSORS_SHT21)	+= sht21.o
 obj-$(CONFIG_SENSORS_SHT3x)	+= sht3x.o
diff --git a/drivers/hwmon/sl28cpld-hwmon.c b/drivers/hwmon/sl28cpld-hwmon.c
new file mode 100644
index 000000000000..7ac42bb0a48c
--- /dev/null
+++ b/drivers/hwmon/sl28cpld-hwmon.c
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * SMARC-sAL28 fan hardware monitoring driver.
+ *
+ * Copyright 2019 Kontron Europe GmbH
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/bitfield.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+#include <linux/hwmon.h>
+
+#define FAN_INPUT		0
+#define   FAN_SCALE_X8		BIT(7)
+#define   FAN_VALUE_MASK	GENMASK(6, 0)
+
+struct sl28cpld_hwmon {
+	struct regmap *regmap;
+	u32 offset;
+};
+
+static umode_t sl28cpld_hwmon_is_visible(const void *data,
+					 enum hwmon_sensor_types type,
+					 u32 attr, int channel)
+{
+	return 0444;
+}
+
+static int sl28cpld_hwmon_read(struct device *dev,
+			       enum hwmon_sensor_types type, u32 attr,
+			       int channel, long *input)
+{
+	struct sl28cpld_hwmon *hwmon = dev_get_drvdata(dev);
+	unsigned int value;
+	int ret;
+
+	switch (attr) {
+	case hwmon_fan_input:
+		ret = regmap_read(hwmon->regmap, hwmon->offset + FAN_INPUT,
+				  &value);
+		if (ret)
+			return ret;
+		/*
+		 * The register has a 7 bit value and 1 bit which indicates the
+		 * scale. If the MSB is set, then the lower 7 bit has to be
+		 * multiplied by 8, to get the correct reading.
+		 */
+		if (value & FAN_SCALE_X8)
+			value = FIELD_GET(FAN_VALUE_MASK, value) << 3;
+
+		/*
+		 * The counter period is 1000ms and the sysfs specification
+		 * says we should asssume 2 pulses per revolution.
+		 */
+		value *= 60 / 2;
+
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	*input = value;
+	return 0;
+}
+
+static const u32 sl28cpld_hwmon_fan_config[] = {
+	HWMON_F_INPUT,
+	0
+};
+
+static const struct hwmon_channel_info sl28cpld_hwmon_fan = {
+	.type = hwmon_fan,
+	.config = sl28cpld_hwmon_fan_config,
+};
+
+static const struct hwmon_channel_info *sl28cpld_hwmon_info[] = {
+	&sl28cpld_hwmon_fan,
+	NULL
+};
+
+static const struct hwmon_ops sl28cpld_hwmon_ops = {
+	.is_visible = sl28cpld_hwmon_is_visible,
+	.read = sl28cpld_hwmon_read,
+};
+
+static const struct hwmon_chip_info sl28cpld_hwmon_chip_info = {
+	.ops = &sl28cpld_hwmon_ops,
+	.info = sl28cpld_hwmon_info,
+};
+
+static int sl28cpld_hwmon_probe(struct platform_device *pdev)
+{
+	struct device *hwmon_dev;
+	struct sl28cpld_hwmon *hwmon;
+	struct resource *res;
+
+	hwmon = devm_kzalloc(&pdev->dev, sizeof(*hwmon), GFP_KERNEL);
+	if (!hwmon)
+		return -ENOMEM;
+
+	if (!pdev->dev.parent)
+		return -ENODEV;
+
+	hwmon->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!hwmon->regmap)
+		return -ENODEV;
+
+	res = platform_get_resource(pdev, IORESOURCE_REG, 0);
+	if (!res)
+		return -EINVAL;
+	hwmon->offset = res->start;
+
+	hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev,
+							 "sl28cpld_hwmon",
+							 hwmon,
+							 &sl28cpld_hwmon_chip_info,
+							 NULL);
+	if (IS_ERR(hwmon_dev)) {
+		dev_err(&pdev->dev, "failed to register as hwmon device");
+		return PTR_ERR(hwmon_dev);
+	}
+
+	return 0;
+}
+
+static const struct platform_device_id sl28cpld_hwmon_id_table[] = {
+	{"sl28cpld-fan", 0},
+};
+MODULE_DEVICE_TABLE(platform, sl28cpld_hwmon_id_table);
+
+static struct platform_driver sl28cpld_hwmon_driver = {
+	.probe = sl28cpld_hwmon_probe,
+	.id_table = sl28cpld_hwmon_id_table,
+	.driver = {
+		.name = "sl28cpld-hwmon",
+	},
+};
+module_platform_driver(sl28cpld_hwmon_driver);
+
+MODULE_DESCRIPTION("sl28cpld Hardware Monitoring Driver");
+MODULE_LICENSE("GPL");
-- 
2.20.1


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

* [PATCH 15/18] arm64: dts: freescale: sl28: enable sl28cpld
  2020-03-17 20:49 [PATCH 00/18] Add support for Kontron sl28cpld Michael Walle
                   ` (13 preceding siblings ...)
  2020-03-17 20:50 ` [PATCH 14/18] hwmon: add support for the sl28cpld hardware monitoring controller Michael Walle
@ 2020-03-17 20:50 ` Michael Walle
  2020-03-17 20:50 ` [PATCH 16/18] arm64: dts: freescale: sl28: map GPIOs to input events Michael Walle
                   ` (2 subsequent siblings)
  17 siblings, 0 replies; 40+ messages in thread
From: Michael Walle @ 2020-03-17 20:50 UTC (permalink / raw)
  To: linux-gpio, devicetree, linux-kernel, linux-hwmon, linux-pwm,
	linux-watchdog, linux-arm-kernel
  Cc: Linus Walleij, Bartosz Golaszewski, Rob Herring, Jean Delvare,
	Guenter Roeck, Lee Jones, Thierry Reding, Uwe Kleine-König,
	Wim Van Sebroeck, Shawn Guo, Li Yang, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Michael Walle

Add the board management controller node.

Signed-off-by: Michael Walle <michael@walle.cc>
---
 .../freescale/fsl-ls1028a-kontron-sl28.dts    | 87 +++++++++++++++++++
 1 file changed, 87 insertions(+)

diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1028a-kontron-sl28.dts b/arch/arm64/boot/dts/freescale/fsl-ls1028a-kontron-sl28.dts
index 1648a04ea79f..b74d9ac0c388 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1028a-kontron-sl28.dts
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1028a-kontron-sl28.dts
@@ -8,6 +8,7 @@
 
 /dts-v1/;
 #include "fsl-ls1028a.dtsi"
+#include <dt-bindings/interrupt-controller/irq.h>
 
 / {
 	model = "Kontron SMARC-sAL28";
@@ -165,6 +166,92 @@
 		reg = <0x32>;
 	};
 
+	sl28cpld: sl28cpld@4a {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		compatible = "kontron,sl28cpld";
+		reg = <0x4a>;
+		interrupts-extended = <&gpio2 6 IRQ_TYPE_EDGE_FALLING>;
+
+		interrupt-controller;
+		#interrupt-cells = <2>;
+
+		cpld_gpio0: gpio@0 {
+			compatible = "kontron,sl28cpld-gpio";
+			reg = <0>;
+
+			gpio-controller;
+			#gpio-cells = <2>;
+			gpio-line-names =
+				"GPIO0_CAM0_PWR_N", "GPIO1_CAM1_PWR_N",
+				"GPIO2_CAM0_RST_N", "GPIO3_CAM1_RST_N",
+				"GPIO4_HDA_RST_N", "GPIO5_PWM_OUT",
+				"GPIO6_TACHIN", "GPIO7";
+
+			interrupt-controller;
+			#interrupt-cells = <2>;
+		};
+
+		cpld_gpio1: gpio@1 {
+			compatible = "kontron,sl28cpld-gpio";
+			reg = <1>;
+
+			gpio-controller;
+			#gpio-cells = <2>;
+			gpio-line-names =
+				"GPIO8", "GPIO9", "GPIO10", "GPIO11",
+				"", "", "", "";
+
+			interrupt-controller;
+			#interrupt-cells = <2>;
+		};
+
+		cpld_gpo: gpo {
+			compatible = "kontron,sl28cpld-gpo";
+
+			gpio-controller;
+			#gpio-cells = <2>;
+			gpio-line-names =
+				"LCD0 voltage enable",
+				"LCD0 backlight enable",
+				"eMMC reset", "LVDS bridge reset",
+				"LVDS bridge power-down",
+				"SDIO power enable",
+				"", "";
+		};
+
+		cpld_gpi: gpi {
+			compatible = "kontron,sl28cpld-gpi";
+
+			gpio-controller;
+			#gpio-cells = <2>;
+			gpio-line-names =
+				"Power button", "Force recovery", "Sleep",
+				"Battery low", "Lid state", "Charging",
+				"Charger present", "";
+		};
+
+		hwmon {
+			compatible = "kontron,sl28cpld-fan";
+		};
+
+		pwm0: pwm@0 {
+			#pwm-cells = <2>;
+			compatible = "kontron,sl28cpld-pwm";
+			reg = <0>;
+		};
+
+		pwm1: pwm@1 {
+			#pwm-cells = <2>;
+			compatible = "kontron,sl28cpld-pwm";
+			reg = <1>;
+		};
+
+		watchdog {
+			compatible = "kontron,sl28cpld-wdt";
+		};
+	};
+
 	eeprom@50 {
 		compatible = "atmel,24c32";
 		reg = <0x50>;
-- 
2.20.1


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

* [PATCH 16/18] arm64: dts: freescale: sl28: map GPIOs to input events
  2020-03-17 20:49 [PATCH 00/18] Add support for Kontron sl28cpld Michael Walle
                   ` (14 preceding siblings ...)
  2020-03-17 20:50 ` [PATCH 15/18] arm64: dts: freescale: sl28: enable sl28cpld Michael Walle
@ 2020-03-17 20:50 ` Michael Walle
  2020-03-17 20:50 ` [PATCH 17/18] arm64: dts: freescale: sl28: enable LED support Michael Walle
  2020-03-17 20:50 ` [PATCH 18/18] arm64: dts: freescale: sl28: enable fan support Michael Walle
  17 siblings, 0 replies; 40+ messages in thread
From: Michael Walle @ 2020-03-17 20:50 UTC (permalink / raw)
  To: linux-gpio, devicetree, linux-kernel, linux-hwmon, linux-pwm,
	linux-watchdog, linux-arm-kernel
  Cc: Linus Walleij, Bartosz Golaszewski, Rob Herring, Jean Delvare,
	Guenter Roeck, Lee Jones, Thierry Reding, Uwe Kleine-König,
	Wim Van Sebroeck, Shawn Guo, Li Yang, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Michael Walle

Now that we have support for GPIO lines of the SMARC connector, map the
sleep, power and lid switch signals to the corresponding keys using the
gpio-keys and gpio-keys-polled drivers. The power and sleep signals have
dedicated interrupts, thus we use these ones. The lid switch is just
mapped to a GPIO input and needs polling.

Signed-off-by: Michael Walle <michael@walle.cc>
---
 .../freescale/fsl-ls1028a-kontron-sl28.dts    | 32 +++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1028a-kontron-sl28.dts b/arch/arm64/boot/dts/freescale/fsl-ls1028a-kontron-sl28.dts
index b74d9ac0c388..f92a2b1b6628 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1028a-kontron-sl28.dts
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1028a-kontron-sl28.dts
@@ -9,6 +9,8 @@
 /dts-v1/;
 #include "fsl-ls1028a.dtsi"
 #include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
 
 / {
 	model = "Kontron SMARC-sAL28";
@@ -22,6 +24,36 @@
 		spi1 = &dspi2;
 	};
 
+	buttons0 {
+		compatible = "gpio-keys";
+
+		power-button {
+			interrupts-extended = <&sl28cpld
+					       4 IRQ_TYPE_EDGE_BOTH>;
+			linux,code = <KEY_POWER>;
+			label = "Power";
+		};
+
+		sleep-button {
+			interrupts-extended = <&sl28cpld
+					       5 IRQ_TYPE_EDGE_BOTH>;
+			linux,code = <KEY_SLEEP>;
+			label = "Sleep";
+		};
+	};
+
+	buttons1 {
+		compatible = "gpio-keys-polled";
+		poll-interval = <200>;
+
+		lid-switch {
+			linux,input-type = <EV_SW>;
+			linux,code = <SW_LID>;
+			gpios = <&cpld_gpi 4 GPIO_ACTIVE_LOW>;
+			label = "Lid";
+		};
+	};
+
 	chosen {
 		stdout-path = "serial0:115200n8";
 	};
-- 
2.20.1


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

* [PATCH 17/18] arm64: dts: freescale: sl28: enable LED support
  2020-03-17 20:49 [PATCH 00/18] Add support for Kontron sl28cpld Michael Walle
                   ` (15 preceding siblings ...)
  2020-03-17 20:50 ` [PATCH 16/18] arm64: dts: freescale: sl28: map GPIOs to input events Michael Walle
@ 2020-03-17 20:50 ` Michael Walle
  2020-03-17 20:50 ` [PATCH 18/18] arm64: dts: freescale: sl28: enable fan support Michael Walle
  17 siblings, 0 replies; 40+ messages in thread
From: Michael Walle @ 2020-03-17 20:50 UTC (permalink / raw)
  To: linux-gpio, devicetree, linux-kernel, linux-hwmon, linux-pwm,
	linux-watchdog, linux-arm-kernel
  Cc: Linus Walleij, Bartosz Golaszewski, Rob Herring, Jean Delvare,
	Guenter Roeck, Lee Jones, Thierry Reding, Uwe Kleine-König,
	Wim Van Sebroeck, Shawn Guo, Li Yang, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Michael Walle

Now that we have support for GPIO lines of the SMARC connector, enable
LED support on the KBox A-230-LS. There are two LEDs without fixed
functions, one is yellow and one is green. Unfortunately, it is just one
multi-color LED, thus while it is possible to enable both at the same
time it is hard to tell the difference between "yellow only" and "yellow
and green".

Signed-off-by: Michael Walle <michael@walle.cc>
---
 .../fsl-ls1028a-kontron-kbox-a-230-ls.dts          | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1028a-kontron-kbox-a-230-ls.dts b/arch/arm64/boot/dts/freescale/fsl-ls1028a-kontron-kbox-a-230-ls.dts
index 4b4cc6a1573d..d4ca12b140b4 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1028a-kontron-kbox-a-230-ls.dts
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1028a-kontron-kbox-a-230-ls.dts
@@ -16,6 +16,20 @@
 	model = "Kontron KBox A-230-LS";
 	compatible = "kontron,kbox-a-230-ls", "kontron,sl28-var4",
 		     "kontron,sl28", "fsl,ls1028a";
+
+	leds {
+		compatible = "gpio-leds";
+
+		user_yellow {
+			label = "s1914:yellow:user";
+			gpios = <&cpld_gpio0 0 0>;
+		};
+
+		user_green {
+			label = "s1914:green:user";
+			gpios = <&cpld_gpio1 3 0>;
+		};
+	};
 };
 
 &enetc_mdio_pf3 {
-- 
2.20.1


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

* [PATCH 18/18] arm64: dts: freescale: sl28: enable fan support
  2020-03-17 20:49 [PATCH 00/18] Add support for Kontron sl28cpld Michael Walle
                   ` (16 preceding siblings ...)
  2020-03-17 20:50 ` [PATCH 17/18] arm64: dts: freescale: sl28: enable LED support Michael Walle
@ 2020-03-17 20:50 ` Michael Walle
  17 siblings, 0 replies; 40+ messages in thread
From: Michael Walle @ 2020-03-17 20:50 UTC (permalink / raw)
  To: linux-gpio, devicetree, linux-kernel, linux-hwmon, linux-pwm,
	linux-watchdog, linux-arm-kernel
  Cc: Linus Walleij, Bartosz Golaszewski, Rob Herring, Jean Delvare,
	Guenter Roeck, Lee Jones, Thierry Reding, Uwe Kleine-König,
	Wim Van Sebroeck, Shawn Guo, Li Yang, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Michael Walle

Add a pwm-fan mapped to the PWM channel 0 which is connected to the
fan connector of the carrier.

Signed-off-by: Michael Walle <michael@walle.cc>
---
 .../dts/freescale/fsl-ls1028a-kontron-sl28-var3-ads2.dts | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1028a-kontron-sl28-var3-ads2.dts b/arch/arm64/boot/dts/freescale/fsl-ls1028a-kontron-sl28-var3-ads2.dts
index 0973a6a45217..016b6ae5826b 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1028a-kontron-sl28-var3-ads2.dts
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1028a-kontron-sl28-var3-ads2.dts
@@ -15,6 +15,15 @@
 	compatible = "kontron,sl28-var3-ads2", "kontron,sl28-var3",
 		     "kontron,sl28", "fsl,ls1028a";
 
+	pwm-fan {
+		compatible = "pwm-fan";
+		cooling-min-state = <0>;
+		cooling-max-state = <3>;
+		#cooling-cells = <2>;
+		pwms = <&pwm0 0 4000000>;
+		cooling-levels = <1 128 192 255>;
+	};
+
 	sound {
 		#address-cells = <1>;
 		#size-cells = <0>;
-- 
2.20.1


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

* Re: [PATCH 08/18] watchdog: add support for sl28cpld watchdog
  2020-03-17 20:50 ` [PATCH 08/18] watchdog: add support " Michael Walle
@ 2020-03-18  3:17   ` Guenter Roeck
  0 siblings, 0 replies; 40+ messages in thread
From: Guenter Roeck @ 2020-03-18  3:17 UTC (permalink / raw)
  To: Michael Walle, linux-gpio, devicetree, linux-kernel, linux-hwmon,
	linux-pwm, linux-watchdog, linux-arm-kernel
  Cc: Linus Walleij, Bartosz Golaszewski, Rob Herring, Jean Delvare,
	Lee Jones, Thierry Reding, Uwe Kleine-König,
	Wim Van Sebroeck, Shawn Guo, Li Yang, Thomas Gleixner,
	Jason Cooper, Marc Zyngier

On 3/17/20 1:50 PM, Michael Walle wrote:
> This adds support for the watchdog of the sl28cpld board management
> controller. This is part of a multi-function device driver.
> 
> Signed-off-by: Michael Walle <michael@walle.cc>
> ---
>  drivers/watchdog/Kconfig        |  11 ++
>  drivers/watchdog/Makefile       |   1 +
>  drivers/watchdog/sl28cpld_wdt.c | 238 ++++++++++++++++++++++++++++++++
>  3 files changed, 250 insertions(+)
>  create mode 100644 drivers/watchdog/sl28cpld_wdt.c
> 
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index 9ea2b43d4b01..c78b90ccc8cf 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -340,6 +340,17 @@ config MLX_WDT
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called mlx-wdt.
>  
> +config SL28CPLD_WATCHDOG
> +	tristate "Kontron sl28 watchdog"
> +	depends on MFD_SL28CPLD
> +	select WATCHDOG_CORE
> +	help
> +	  Say Y here to include support for the watchdog timer
> +	  on the Kontron sl28 CPLD.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called sl28cpld_wdt.
> +
>  # ALPHA Architecture
>  
>  # ARM Architecture
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index 2ee352bf3372..060e2f895fe8 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -223,3 +223,4 @@ obj-$(CONFIG_MENF21BMC_WATCHDOG) += menf21bmc_wdt.o
>  obj-$(CONFIG_MENZ069_WATCHDOG) += menz69_wdt.o
>  obj-$(CONFIG_RAVE_SP_WATCHDOG) += rave-sp-wdt.o
>  obj-$(CONFIG_STPMIC1_WATCHDOG) += stpmic1_wdt.o
> +obj-$(CONFIG_SL28CPLD_WATCHDOG) += sl28cpld_wdt.o
> diff --git a/drivers/watchdog/sl28cpld_wdt.c b/drivers/watchdog/sl28cpld_wdt.c
> new file mode 100644
> index 000000000000..5927b7ad0be4
> --- /dev/null
> +++ b/drivers/watchdog/sl28cpld_wdt.c
> @@ -0,0 +1,238 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * SMARC-sAL28 Watchdog driver.
> + *
> + * Copyright 2019 Kontron Europe GmbH
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/regmap.h>
> +#include <linux/platform_device.h>
> +#include <linux/watchdog.h>
> +
> +/*
> + * Watchdog timer block registers.
> + */
> +#define SL28CPLD_WDT_CTRL	0
> +#define  WDT_CTRL_EN		BIT(0)
> +#define  WDT_CTRL_LOCK		BIT(2)
> +#define SL28CPLD_WDT_TIMEOUT	1
> +#define SL28CPLD_WDT_KICK	2
> +#define  WDT_KICK_VALUE		0x6b
> +#define SL28CPLD_WDT_COUNT	3
> +
> +static bool nowayout = WATCHDOG_NOWAYOUT;
> +module_param(nowayout, bool, 0);
> +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
> +				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
> +
> +static int timeout;
> +module_param(timeout, int, 0);
> +MODULE_PARM_DESC(timeout, "Initial watchdog timeout in seconds");
> +
> +struct sl28cpld_wdt {
> +	struct watchdog_device wdd;
> +	struct regmap *regmap;
> +	u32 offset;
> +};
> +
> +static int sl28cpld_wdt_ping(struct watchdog_device *wdd)
> +{
> +	struct sl28cpld_wdt *wdt = watchdog_get_drvdata(wdd);
> +
> +	return regmap_write(wdt->regmap, wdt->offset + SL28CPLD_WDT_KICK,
> +			    WDT_KICK_VALUE);
> +}
> +
> +static int sl28cpld_wdt_start(struct watchdog_device *wdd)
> +{
> +	struct sl28cpld_wdt *wdt = watchdog_get_drvdata(wdd);
> +	unsigned int val;
> +
> +	val = WDT_CTRL_EN;
> +	if (nowayout)
> +		val |= WDT_CTRL_LOCK;
> +
> +	return regmap_update_bits(wdt->regmap, wdt->offset + SL28CPLD_WDT_CTRL,
> +				  val, val);
> +}
> +
> +static int sl28cpld_wdt_stop(struct watchdog_device *wdd)
> +{
> +	struct sl28cpld_wdt *wdt = watchdog_get_drvdata(wdd);
> +
> +	return regmap_update_bits(wdt->regmap, wdt->offset + SL28CPLD_WDT_CTRL,
> +				  WDT_CTRL_EN, 0);
> +}
> +
> +static unsigned int sl28cpld_wdt_status(struct watchdog_device *wdd)
> +{
> +	struct sl28cpld_wdt *wdt = watchdog_get_drvdata(wdd);
> +	unsigned int status;
> +	int ret;
> +
> +	ret = regmap_read(wdt->regmap, wdt->offset + SL28CPLD_WDT_CTRL,
> +			  &status);
> +	if (ret < 0)
> +		return 0;
> +
> +	/* is the watchdog timer running? */
> +	return (status & WDT_CTRL_EN) << WDOG_ACTIVE;
> +}
> +
> +static unsigned int sl28cpld_wdt_get_timeleft(struct watchdog_device *wdd)
> +{
> +	struct sl28cpld_wdt *wdt = watchdog_get_drvdata(wdd);
> +	int ret;
> +	unsigned int val;
> +
> +	ret = regmap_read(wdt->regmap, wdt->offset + SL28CPLD_WDT_COUNT, &val);
> +	if (ret < 0)
> +		return 0;
> +
> +	return val;
> +}
> +
> +static int sl28cpld_wdt_set_timeout(struct watchdog_device *wdd,
> +				  unsigned int timeout)
> +{
> +	int ret;
> +	struct sl28cpld_wdt *wdt = watchdog_get_drvdata(wdd);
> +
> +	ret = regmap_write(wdt->regmap, wdt->offset + SL28CPLD_WDT_TIMEOUT,
> +			   timeout);
> +	if (ret == 0)
> +		wdd->timeout = timeout;
> +
> +	return ret;
> +}
> +
> +static const struct watchdog_info sl28cpld_wdt_info = {
> +	.options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
> +	.identity = "SMARC-sAL28 CPLD watchdog",
> +};
> +
> +static struct watchdog_ops sl28cpld_wdt_ops = {
> +	.owner = THIS_MODULE,
> +	.start = sl28cpld_wdt_start,
> +	.stop = sl28cpld_wdt_stop,
> +	.status = sl28cpld_wdt_status,
> +	.ping = sl28cpld_wdt_ping,
> +	.set_timeout = sl28cpld_wdt_set_timeout,
> +	.get_timeleft = sl28cpld_wdt_get_timeleft,
> +};
> +
> +static int sl28cpld_wdt_locked(struct sl28cpld_wdt *wdt)
> +{
> +	unsigned int val;
> +	int ret;
> +
> +	ret = regmap_read(wdt->regmap, wdt->offset + SL28CPLD_WDT_CTRL, &val);
> +	if (ret < 0)
> +		return ret;
> +
> +	return val & WDT_CTRL_LOCK;
> +}
> +
> +static int sl28cpld_wdt_probe(struct platform_device *pdev)
> +{
> +	struct sl28cpld_wdt *wdt;
> +	struct watchdog_device *wdd;
> +	struct resource *res;
> +	unsigned int val;
> +	int ret;
> +
> +	wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
> +	if (!wdt)
> +		return -ENOMEM;
> +
> +	if (!pdev->dev.parent)
> +		return -ENODEV;
> +
> +	wdt->regmap = dev_get_regmap(pdev->dev.parent, NULL);
> +	if (!wdt->regmap)
> +		return -ENODEV;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_REG, 0);
> +	if (res == NULL)
> +		return -EINVAL;
> +	wdt->offset = res->start;
> +
> +	/* initialize struct watchdog_device */
> +	wdd = &wdt->wdd;
> +	wdd->parent = &pdev->dev;
> +	wdd->info = &sl28cpld_wdt_info;
> +	wdd->ops = &sl28cpld_wdt_ops;
> +	wdd->min_timeout = 1;
> +	wdd->max_timeout = 255;
> +
> +	watchdog_set_drvdata(wdd, wdt);
> +
> +	/* if the watchdog is locked, we set nowayout to true */
> +	ret = sl28cpld_wdt_locked(wdt);
> +	if (ret < 0)
> +		return ret;
> +	if (ret)
> +		nowayout = true;
> +	watchdog_set_nowayout(wdd, nowayout);
> +
> +	/*
> +	 * Initial timeout value, can either be set by kernel parameter or by
> +	 * the device tree. If both are not given the current value is used.
> +	 */
> +	watchdog_init_timeout(wdd, timeout, &pdev->dev);
> +	if (wdd->timeout) {
> +		sl28cpld_wdt_set_timeout(wdd, wdd->timeout);
> +	} else {
> +		ret = regmap_read(wdt->regmap,
> +				  wdt->offset + SL28CPLD_WDT_TIMEOUT, &val);
> +		if (ret < 0)
> +			return ret;
> +		wdd->timeout = val;
> +	}
> +
> +	ret = watchdog_register_device(wdd);

Please use devm_watchdog_register_device().

> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "failed to register watchdog device\n");
> +		return ret;
> +	}
> +
> +	platform_set_drvdata(pdev, wdt);
> +
> +	dev_info(&pdev->dev, "CPLD watchdog: initial timeout %d sec%s\n",
> +		wdd->timeout, nowayout ? ", nowayout" : "");
> +
> +	return 0;
> +}
> +
> +static int sl28cpld_wdt_remove(struct platform_device *pdev)
> +{
> +	struct sl28cpld_wdt *wdt = platform_get_drvdata(pdev);
> +
> +	watchdog_unregister_device(&wdt->wdd);
> +
> +	return 0;
> +}
> +
> +static void sl28cpld_wdt_shutdown(struct platform_device *pdev)

Please use watchdog_stop_on_reboot().

Thanks,
Guenter

> +{
> +	struct sl28cpld_wdt *wdt = platform_get_drvdata(pdev);
> +
> +	sl28cpld_wdt_stop(&wdt->wdd);
> +}
> +
> +static struct platform_driver sl28cpld_wdt_driver = {
> +	.probe = sl28cpld_wdt_probe,
> +	.remove = sl28cpld_wdt_remove,
> +	.shutdown = sl28cpld_wdt_shutdown,
> +	.driver = {
> +		.name = "sl28cpld-wdt",
> +	},
> +};
> +module_platform_driver(sl28cpld_wdt_driver);
> +
> +MODULE_DESCRIPTION("sl28cpld Watchdog Driver");
> +MODULE_LICENSE("GPL");
> 


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

* Re: [PATCH 14/18] hwmon: add support for the sl28cpld hardware monitoring controller
  2020-03-17 20:50 ` [PATCH 14/18] hwmon: add support for the sl28cpld hardware monitoring controller Michael Walle
@ 2020-03-18  3:27   ` Guenter Roeck
  2020-03-18 16:32     ` Michael Walle
  0 siblings, 1 reply; 40+ messages in thread
From: Guenter Roeck @ 2020-03-18  3:27 UTC (permalink / raw)
  To: Michael Walle, linux-gpio, devicetree, linux-kernel, linux-hwmon,
	linux-pwm, linux-watchdog, linux-arm-kernel
  Cc: Linus Walleij, Bartosz Golaszewski, Rob Herring, Jean Delvare,
	Lee Jones, Thierry Reding, Uwe Kleine-König,
	Wim Van Sebroeck, Shawn Guo, Li Yang, Thomas Gleixner,
	Jason Cooper, Marc Zyngier

On 3/17/20 1:50 PM, Michael Walle wrote:
> This adds support for the hardware monitoring controller of the sl28cpld
> board management controller. This driver is part of a multi-function
> device.
> 
> Signed-off-by: Michael Walle <michael@walle.cc>
> ---
>  drivers/hwmon/Kconfig          |  10 +++
>  drivers/hwmon/Makefile         |   1 +
>  drivers/hwmon/sl28cpld-hwmon.c | 146 +++++++++++++++++++++++++++++++++

Please also provide Documentation/hwmon/sl28cpld.

>  3 files changed, 157 insertions(+)
>  create mode 100644 drivers/hwmon/sl28cpld-hwmon.c
> 
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 05a30832c6ba..c98716f78cfa 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -1412,6 +1412,16 @@ config SENSORS_RASPBERRYPI_HWMON
>  	  This driver can also be built as a module. If so, the module
>  	  will be called raspberrypi-hwmon.
>  
> +config SENSORS_SL28CPLD
> +	tristate "Kontron's SMARC-sAL28 hardware monitoring driver"
> +	depends on MFD_SL28CPLD
> +	help
> +	  If you say yes here you get support for a fan connected to the
> +	  input of the SMARC connector of Kontron's SMARC-sAL28 module.
> +
> +	  This driver can also be built as a module.  If so, the module
> +	  will be called sl28cpld-hwmon.
> +
>  config SENSORS_SHT15
>  	tristate "Sensiron humidity and temperature sensors. SHT15 and compat."
>  	depends on GPIOLIB || COMPILE_TEST
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index b0b9c8e57176..dfb0f8cda2dd 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -155,6 +155,7 @@ obj-$(CONFIG_SENSORS_S3C)	+= s3c-hwmon.o
>  obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o
>  obj-$(CONFIG_SENSORS_SCH5627)	+= sch5627.o
>  obj-$(CONFIG_SENSORS_SCH5636)	+= sch5636.o
> +obj-$(CONFIG_SENSORS_SL28CPLD)	+= sl28cpld-hwmon.o
>  obj-$(CONFIG_SENSORS_SHT15)	+= sht15.o
>  obj-$(CONFIG_SENSORS_SHT21)	+= sht21.o
>  obj-$(CONFIG_SENSORS_SHT3x)	+= sht3x.o
> diff --git a/drivers/hwmon/sl28cpld-hwmon.c b/drivers/hwmon/sl28cpld-hwmon.c
> new file mode 100644
> index 000000000000..7ac42bb0a48c
> --- /dev/null
> +++ b/drivers/hwmon/sl28cpld-hwmon.c
> @@ -0,0 +1,146 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * SMARC-sAL28 fan hardware monitoring driver.
> + *
> + * Copyright 2019 Kontron Europe GmbH
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/bitfield.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/of_address.h>
> +#include <linux/regmap.h>
> +#include <linux/platform_device.h>
> +#include <linux/hwmon.h>
> +
Alphabetic order of include files, please.

> +#define FAN_INPUT		0
> +#define   FAN_SCALE_X8		BIT(7)
> +#define   FAN_VALUE_MASK	GENMASK(6, 0)
> +
> +struct sl28cpld_hwmon {
> +	struct regmap *regmap;
> +	u32 offset;
> +};
> +
> +static umode_t sl28cpld_hwmon_is_visible(const void *data,
> +					 enum hwmon_sensor_types type,
> +					 u32 attr, int channel)
> +{
> +	return 0444;
> +}
> +
> +static int sl28cpld_hwmon_read(struct device *dev,
> +			       enum hwmon_sensor_types type, u32 attr,
> +			       int channel, long *input)
> +{
> +	struct sl28cpld_hwmon *hwmon = dev_get_drvdata(dev);
> +	unsigned int value;
> +	int ret;
> +
> +	switch (attr) {
> +	case hwmon_fan_input:
> +		ret = regmap_read(hwmon->regmap, hwmon->offset + FAN_INPUT,
> +				  &value);
> +		if (ret)
> +			return ret;
> +		/*
> +		 * The register has a 7 bit value and 1 bit which indicates the
> +		 * scale. If the MSB is set, then the lower 7 bit has to be
> +		 * multiplied by 8, to get the correct reading.
> +		 */
> +		if (value & FAN_SCALE_X8)
> +			value = FIELD_GET(FAN_VALUE_MASK, value) << 3;
> +
> +		/*
> +		 * The counter period is 1000ms and the sysfs specification
> +		 * says we should asssume 2 pulses per revolution.
> +		 */
> +		value *= 60 / 2;
> +
> +		break;
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +
> +	*input = value;
> +	return 0;
> +}
> +
> +static const u32 sl28cpld_hwmon_fan_config[] = {
> +	HWMON_F_INPUT,
> +	0
> +};
> +
> +static const struct hwmon_channel_info sl28cpld_hwmon_fan = {
> +	.type = hwmon_fan,
> +	.config = sl28cpld_hwmon_fan_config,
> +};
> +
> +static const struct hwmon_channel_info *sl28cpld_hwmon_info[] = {
> +	&sl28cpld_hwmon_fan,
> +	NULL
> +};
> +
> +static const struct hwmon_ops sl28cpld_hwmon_ops = {
> +	.is_visible = sl28cpld_hwmon_is_visible,
> +	.read = sl28cpld_hwmon_read,
> +};
> +
> +static const struct hwmon_chip_info sl28cpld_hwmon_chip_info = {
> +	.ops = &sl28cpld_hwmon_ops,
> +	.info = sl28cpld_hwmon_info,
> +};
> +
> +static int sl28cpld_hwmon_probe(struct platform_device *pdev)
> +{
> +	struct device *hwmon_dev;
> +	struct sl28cpld_hwmon *hwmon;
> +	struct resource *res;
> +
> +	hwmon = devm_kzalloc(&pdev->dev, sizeof(*hwmon), GFP_KERNEL);
> +	if (!hwmon)
> +		return -ENOMEM;
> +
> +	if (!pdev->dev.parent)
> +		return -ENODEV;
> +
Maybe do this first ?

> +	hwmon->regmap = dev_get_regmap(pdev->dev.parent, NULL);
> +	if (!hwmon->regmap)
> +		return -ENODEV;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_REG, 0);
> +	if (!res)
> +		return -EINVAL;
> +	hwmon->offset = res->start;
> +
> +	hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev,
> +							 "sl28cpld_hwmon",
> +							 hwmon,
> +							 &sl28cpld_hwmon_chip_info,
> +							 NULL);
> +	if (IS_ERR(hwmon_dev)) {
> +		dev_err(&pdev->dev, "failed to register as hwmon device");
> +		return PTR_ERR(hwmon_dev);
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct platform_device_id sl28cpld_hwmon_id_table[] = {
> +	{"sl28cpld-fan", 0},
> +};
> +MODULE_DEVICE_TABLE(platform, sl28cpld_hwmon_id_table);
> +
> +static struct platform_driver sl28cpld_hwmon_driver = {
> +	.probe = sl28cpld_hwmon_probe,
> +	.id_table = sl28cpld_hwmon_id_table,

I'd have expected an of_match_table.

> +	.driver = {
> +		.name = "sl28cpld-hwmon",
> +	},
> +};
> +module_platform_driver(sl28cpld_hwmon_driver);
> +
> +MODULE_DESCRIPTION("sl28cpld Hardware Monitoring Driver");
> +MODULE_LICENSE("GPL");
> 


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

* Re: [PATCH 05/18] mfd: Add support for Kontron sl28cpld management controller
  2020-03-17 20:50 ` [PATCH 05/18] mfd: Add support for Kontron sl28cpld management controller Michael Walle
@ 2020-03-18  3:28   ` Guenter Roeck
  2020-03-18 16:38     ` Michael Walle
  0 siblings, 1 reply; 40+ messages in thread
From: Guenter Roeck @ 2020-03-18  3:28 UTC (permalink / raw)
  To: Michael Walle, linux-gpio, devicetree, linux-kernel, linux-hwmon,
	linux-pwm, linux-watchdog, linux-arm-kernel
  Cc: Linus Walleij, Bartosz Golaszewski, Rob Herring, Jean Delvare,
	Lee Jones, Thierry Reding, Uwe Kleine-König,
	Wim Van Sebroeck, Shawn Guo, Li Yang, Thomas Gleixner,
	Jason Cooper, Marc Zyngier

On 3/17/20 1:50 PM, Michael Walle wrote:
> This patch adds core support for the board management controller found
> on the SMARC-sAL28 board. It consists of the following functions:
>  - watchdog
>  - GPIO controller
>  - PWM controller
>  - fan sensor
>  - interrupt controller
> 
> At the moment, this controller is used on the Kontron SMARC-sAL28 board.
> 
> Signed-off-by: Michael Walle <michael@walle.cc>
> ---
>  drivers/mfd/Kconfig    |  21 ++++++
>  drivers/mfd/Makefile   |   2 +
>  drivers/mfd/sl28cpld.c | 155 +++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 178 insertions(+)
>  create mode 100644 drivers/mfd/sl28cpld.c
> 
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index 3c547ed575e6..01588c366476 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -2059,5 +2059,26 @@ config SGI_MFD_IOC3
>  	  If you have an SGI Origin, Octane, or a PCI IOC3 card,
>  	  then say Y. Otherwise say N.
>  
> +config MFD_SL28CPLD
> +	tristate "Kontron sl28 core driver"
> +	depends on I2C=y

Why I2C=y and not just I2C ?

> +	depends on OF
> +	select REGMAP_I2C
> +	select REGMAP_IRQ
> +	select SL28CPLD_IRQ
> +	select MFD_CORE
> +	help
> +	  This option enables support for the board management controller
> +	  found on the Kontron sl28 CPLD. You have to select individual
> +	  functions, such as watchdog, GPIO, etc, under the corresponding menus
> +	  in order to enable them.
> +
> +	  Currently supported boards are:
> +
> +		Kontron SMARC-sAL28
> +
> +	  To compile this driver as a module, choose M here: the module will be
> +	  called sl28cpld.
> +
>  endmenu
>  endif
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index f935d10cbf0f..9bc38863b9c7 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -259,3 +259,5 @@ obj-$(CONFIG_MFD_ROHM_BD718XX)	+= rohm-bd718x7.o
>  obj-$(CONFIG_MFD_STMFX) 	+= stmfx.o
>  
>  obj-$(CONFIG_SGI_MFD_IOC3)	+= ioc3.o
> +
> +obj-$(CONFIG_MFD_SL28CPLD)	+= sl28cpld.o
> diff --git a/drivers/mfd/sl28cpld.c b/drivers/mfd/sl28cpld.c
> new file mode 100644
> index 000000000000..789f21f90752
> --- /dev/null
> +++ b/drivers/mfd/sl28cpld.c
> @@ -0,0 +1,155 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * MFD core for the CPLD on a SMARC-sAL28 board.
> + *
> + * Copyright 2019 Kontron Europe GmbH
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_platform.h>
> +#include <linux/i2c.h>
> +#include <linux/regmap.h>
> +#include <linux/interrupt.h>
> +#include <linux/mfd/core.h>
> +
> +#define SL28CPLD_VERSION 0x03
> +#define SL28CPLD_WATCHDOG_BASE 0x4
> +#define SL28CPLD_HWMON_FAN_BASE 0xb
> +#define SL28CPLD_PWM0_BASE 0xc
> +#define SL28CPLD_PWM1_BASE 0xe
> +#define SL28CPLD_GPIO0_BASE 0x10
> +#define SL28CPLD_GPIO1_BASE 0x15
> +#define SL28CPLD_GPO_BASE 0x1a
> +#define SL28CPLD_GPI_BASE 0x1b
> +#define SL28CPLD_INTC_BASE 0x1c
> +
> +/* all subdevices share the same IRQ */
> +#define SL28CPLD_IRQ 0
> +
> +#define SL28CPLD_MIN_REQ_VERSION 14
> +
> +struct sl28cpld {
> +	struct device *dev;
> +	struct regmap *regmap;
> +};
> +
> +static const struct regmap_config sl28cpld_regmap_config = {
> +	.reg_bits = 8,
> +	.val_bits = 8,
> +	.reg_stride = 1,
> +};
> +
> +static struct resource sl28cpld_watchdog_resources[] = {
> +	DEFINE_RES_REG(SL28CPLD_WATCHDOG_BASE, 1),
> +};
> +
> +static struct resource sl28cpld_hwmon_fan_resources[] = {
> +	DEFINE_RES_REG(SL28CPLD_HWMON_FAN_BASE, 1),
> +};
> +
> +static struct resource sl28cpld_pwm0_resources[] = {
> +	DEFINE_RES_REG(SL28CPLD_PWM0_BASE, 1),
> +};
> +
> +static struct resource sl28cpld_pwm1_resources[] = {
> +	DEFINE_RES_REG(SL28CPLD_PWM1_BASE, 1),
> +};
> +
> +static struct resource sl28cpld_gpio0_resources[] = {
> +	DEFINE_RES_REG(SL28CPLD_GPIO0_BASE, 1),
> +	DEFINE_RES_IRQ(SL28CPLD_IRQ),
> +};
> +
> +static struct resource sl28cpld_gpio1_resources[] = {
> +	DEFINE_RES_REG(SL28CPLD_GPIO1_BASE, 1),
> +	DEFINE_RES_IRQ(SL28CPLD_IRQ),
> +};
> +
> +static struct resource sl28cpld_gpo_resources[] = {
> +	DEFINE_RES_REG(SL28CPLD_GPO_BASE, 1),
> +};
> +
> +static struct resource sl28cpld_gpi_resources[] = {
> +	DEFINE_RES_REG(SL28CPLD_GPI_BASE, 1),
> +};
> +
> +static struct resource sl28cpld_intc_resources[] = {
> +	DEFINE_RES_REG(SL28CPLD_INTC_BASE, 1),
> +	DEFINE_RES_IRQ(SL28CPLD_IRQ),
> +};
> +
> +static const struct mfd_cell sl28cpld_devs[] = {
> +	OF_MFD_CELL("sl28cpld-wdt", sl28cpld_watchdog_resources, NULL, 0, 0,
> +		    "kontron,sl28cpld-wdt"),
> +	OF_MFD_CELL("sl28cpld-fan", sl28cpld_hwmon_fan_resources, NULL, 0, 0,
> +		    "kontron,sl28cpld-fan"),
> +	OF_MFD_CELL("sl28cpld-pwm", sl28cpld_pwm0_resources, NULL, 0, 0,
> +		    "kontron,sl28cpld-pwm"),
> +	OF_MFD_CELL("sl28cpld-pwm", sl28cpld_pwm1_resources, NULL, 0, 1,
> +		    "kontron,sl28cpld-pwm"),
> +	OF_MFD_CELL("sl28cpld-gpio", sl28cpld_gpio0_resources, NULL, 0, 0,
> +		    "kontron,sl28cpld-gpio"),
> +	OF_MFD_CELL("sl28cpld-gpio", sl28cpld_gpio1_resources, NULL, 0, 1,
> +		    "kontron,sl28cpld-gpio"),
> +	OF_MFD_CELL("sl28cpld-gpo", sl28cpld_gpo_resources, NULL, 0, 0,
> +		    "kontron,sl28cpld-gpo"),
> +	OF_MFD_CELL("sl28cpld-gpi", sl28cpld_gpi_resources, NULL, 0, 0,
> +		    "kontron,sl28cpld-gpi"),
> +	OF_MFD_CELL("sl28cpld-intc", sl28cpld_intc_resources, NULL, 0, 0,
> +		    "kontron,sl28cpld-intc"),
> +};
> +
> +static int sl28cpld_probe(struct i2c_client *i2c)
> +{
> +	struct sl28cpld *sl28cpld;
> +	struct device *dev = &i2c->dev;
> +	unsigned int cpld_version;
> +	int ret;
> +
> +	sl28cpld = devm_kzalloc(dev, sizeof(*sl28cpld), GFP_KERNEL);
> +	if (!sl28cpld)
> +		return -ENOMEM;
> +
> +	sl28cpld->regmap = devm_regmap_init_i2c(i2c, &sl28cpld_regmap_config);
> +	if (IS_ERR(sl28cpld->regmap))
> +		return PTR_ERR(sl28cpld->regmap);
> +
> +	ret = regmap_read(sl28cpld->regmap, SL28CPLD_VERSION, &cpld_version);
> +	if (ret)
> +		return ret;
> +
> +	if (cpld_version < SL28CPLD_MIN_REQ_VERSION) {
> +		dev_err(dev, "unsupported CPLD version %d\n", cpld_version);
> +		return -ENODEV;
> +	}
> +
> +	sl28cpld->dev = dev;
> +	i2c_set_clientdata(i2c, sl28cpld);
> +
> +	dev_info(dev, "successfully probed. CPLD version %d\n", cpld_version);
> +
> +	return devm_mfd_add_devices(dev, -1, sl28cpld_devs,
> +				    ARRAY_SIZE(sl28cpld_devs), NULL,
> +				    i2c->irq, NULL);
> +}
> +
> +static const struct of_device_id sl28cpld_of_match[] = {
> +	{ .compatible = "kontron,sl28cpld", },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, sl28cpld_of_match);
> +
> +static struct i2c_driver sl28cpld_driver = {
> +	.probe_new = sl28cpld_probe,
> +	.driver = {
> +		.name = "sl28cpld",
> +		.of_match_table = of_match_ptr(sl28cpld_of_match),
> +	},
> +};
> +module_i2c_driver(sl28cpld_driver);
> +
> +MODULE_DESCRIPTION("sl28cpld MFD Core Driver");
> +MODULE_LICENSE("GPL");
> 


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

* Re: [PATCH 12/18] gpio: add support for the sl28cpld GPIO controller
  2020-03-17 20:50 ` [PATCH 12/18] gpio: add support for the " Michael Walle
@ 2020-03-18  9:14   ` Bartosz Golaszewski
  2020-03-18 12:45     ` Michael Walle
                       ` (2 more replies)
  0 siblings, 3 replies; 40+ messages in thread
From: Bartosz Golaszewski @ 2020-03-18  9:14 UTC (permalink / raw)
  To: Michael Walle
  Cc: linux-gpio, linux-devicetree, LKML, linux-hwmon, linux-pwm,
	LINUXWATCHDOG, arm-soc, Linus Walleij, Rob Herring, Jean Delvare,
	Guenter Roeck, Lee Jones, Thierry Reding, Uwe Kleine-König,
	Wim Van Sebroeck, Shawn Guo, Li Yang, Thomas Gleixner,
	Jason Cooper, Marc Zyngier

wt., 17 mar 2020 o 21:50 Michael Walle <michael@walle.cc> napisał(a):
>
> This adds support for the GPIO controller of the sl28 board management
> controller. This driver is part of a multi-function device.
>
> Signed-off-by: Michael Walle <michael@walle.cc>

Hi Michael,

thanks for the driver. Please take a look at some comments below.

> ---
>  drivers/gpio/Kconfig         |  11 ++
>  drivers/gpio/Makefile        |   1 +
>  drivers/gpio/gpio-sl28cpld.c | 332 +++++++++++++++++++++++++++++++++++
>  3 files changed, 344 insertions(+)
>  create mode 100644 drivers/gpio/gpio-sl28cpld.c
>
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index 3cbf8882a0dd..516e47017ef5 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -1211,6 +1211,17 @@ config GPIO_RC5T583
>           This driver provides the support for driving/reading the gpio pins
>           of RC5T583 device through standard gpio library.
>
> +config GPIO_SL28CPLD
> +       tristate "Kontron sl28 GPIO"
> +       depends on MFD_SL28CPLD
> +       depends on OF_GPIO
> +       select GPIOLIB_IRQCHIP

Please see below - I think both are not needed.

> +       help
> +         This enables support for the GPIOs found on the Kontron sl28 CPLD.
> +
> +         This driver can also be built as a module. If so, the module will be
> +         called gpio-sl28cpld.
> +
>  config GPIO_STMPE
>         bool "STMPE GPIOs"
>         depends on MFD_STMPE
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index 0b571264ddbc..0ca2d52c78e8 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -127,6 +127,7 @@ obj-$(CONFIG_GPIO_SCH311X)          += gpio-sch311x.o
>  obj-$(CONFIG_GPIO_SCH)                 += gpio-sch.o
>  obj-$(CONFIG_GPIO_SIFIVE)              += gpio-sifive.o
>  obj-$(CONFIG_GPIO_SIOX)                        += gpio-siox.o
> +obj-$(CONFIG_GPIO_SL28CPLD)            += gpio-sl28cpld.o
>  obj-$(CONFIG_GPIO_SODAVILLE)           += gpio-sodaville.o
>  obj-$(CONFIG_GPIO_SPEAR_SPICS)         += gpio-spear-spics.o
>  obj-$(CONFIG_GPIO_SPRD)                        += gpio-sprd.o
> diff --git a/drivers/gpio/gpio-sl28cpld.c b/drivers/gpio/gpio-sl28cpld.c
> new file mode 100644
> index 000000000000..94f82013882f
> --- /dev/null
> +++ b/drivers/gpio/gpio-sl28cpld.c
> @@ -0,0 +1,332 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * SMARC-sAL28 GPIO driver.
> + *
> + * Copyright 2019 Kontron Europe GmbH
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/of_address.h>
> +#include <linux/interrupt.h>
> +#include <linux/regmap.h>
> +#include <linux/platform_device.h>
> +#include <linux/gpio/driver.h>
> +
> +#define GPIO_REG_DIR   0
> +#define GPIO_REG_OUT   1
> +#define GPIO_REG_IN    2
> +#define GPIO_REG_IE    3
> +#define GPIO_REG_IP    4

These values would be more clear if they were defined as hex.

> +
> +#define GPI_REG_IN     0
> +
> +#define GPO_REG_OUT    0

Please also use a common prefix even for defines.

> +
> +enum sl28cpld_gpio_type {
> +       sl28cpld_gpio,
> +       sl28cpld_gpi,
> +       sl28cpld_gpo,
> +};

Enum values should be all upper-case.

> +
> +struct sl28cpld_gpio {
> +       struct gpio_chip gpio_chip;
> +       struct irq_chip irq_chip;
> +       struct regmap *regmap;
> +       u32 offset;
> +       struct mutex lock;
> +       u8 ie;
> +};
> +
> +static void sl28cpld_gpio_set_reg(struct gpio_chip *chip, unsigned int reg,
> +                                 unsigned int offset, int value)
> +{
> +       struct sl28cpld_gpio *gpio = gpiochip_get_data(chip);
> +       unsigned int mask = 1 << offset;
> +       unsigned int val = value << offset;
> +
> +       regmap_update_bits(gpio->regmap, gpio->offset + reg, mask, val);
> +}
> +
> +static void sl28cpld_gpio_set(struct gpio_chip *chip, unsigned int offset,
> +                             int value)
> +{
> +       sl28cpld_gpio_set_reg(chip, GPIO_REG_OUT, offset, value);
> +}
> +
> +static void sl28cpld_gpo_set(struct gpio_chip *chip, unsigned int offset,
> +                            int value)
> +{
> +       sl28cpld_gpio_set_reg(chip, GPO_REG_OUT, offset, value);
> +}
> +
> +static int sl28cpld_gpio_get_reg(struct gpio_chip *chip, unsigned int reg,
> +                                unsigned int offset)
> +{
> +       struct sl28cpld_gpio *gpio = gpiochip_get_data(chip);
> +       unsigned int mask = 1 << offset;
> +       unsigned int val;
> +       int ret;
> +
> +       ret = regmap_read(gpio->regmap, gpio->offset + reg, &val);
> +       if (ret)
> +               return ret;
> +
> +       return (val & mask) ? 1 : 0;
> +}
> +
> +static int sl28cpld_gpio_get(struct gpio_chip *chip, unsigned int offset)
> +{
> +       return sl28cpld_gpio_get_reg(chip, GPIO_REG_IN, offset);
> +}
> +
> +static int sl28cpld_gpi_get(struct gpio_chip *chip, unsigned int offset)
> +{
> +       return sl28cpld_gpio_get_reg(chip, GPI_REG_IN, offset);
> +}
> +
> +static int sl28cpld_gpio_get_direction(struct gpio_chip *chip,
> +                                      unsigned int offset)
> +{
> +       struct sl28cpld_gpio *gpio = gpiochip_get_data(chip);
> +       unsigned int reg;
> +       int ret;
> +
> +       ret = regmap_read(gpio->regmap, gpio->offset + GPIO_REG_DIR, &reg);
> +       if (ret)
> +               return ret;
> +
> +       if (reg & (1 << offset))
> +               return GPIO_LINE_DIRECTION_OUT;
> +       else
> +               return GPIO_LINE_DIRECTION_IN;
> +}
> +
> +static int sl28cpld_gpio_set_direction(struct gpio_chip *chip,
> +                                      unsigned int offset,
> +                                      bool output)
> +{
> +       struct sl28cpld_gpio *gpio = gpiochip_get_data(chip);
> +       unsigned int mask = 1 << offset;
> +       unsigned int val = (output) ? mask : 0;
> +
> +       return regmap_update_bits(gpio->regmap, gpio->offset + GPIO_REG_DIR,
> +                                 mask, val);
> +

Stray newline.

> +}
> +
> +static int sl28cpld_gpio_direction_input(struct gpio_chip *chip,
> +                                        unsigned int offset)
> +{
> +       return sl28cpld_gpio_set_direction(chip, offset, false);
> +}
> +
> +static int sl28cpld_gpio_direction_output(struct gpio_chip *chip,
> +                                         unsigned int offset, int value)
> +{
> +       sl28cpld_gpio_set_reg(chip, GPIO_REG_OUT, offset, value);
> +       return sl28cpld_gpio_set_direction(chip, offset, true);
> +}
> +
> +static void sl28cpld_gpio_irq_lock(struct irq_data *data)
> +{
> +       struct sl28cpld_gpio *gpio =
> +               gpiochip_get_data(irq_data_get_irq_chip_data(data));
> +
> +       mutex_lock(&gpio->lock);

How does that actually lock anything? Regmap uses a different lock and
if you want to make sure nobody modifies the GPIO registers than you'd
need to use the same lock. Also: this looks a lot like a task for
regmap_irqchip - maybe you could use it here or in the core mfd
module?

> +}
> +
> +static void sl28cpld_gpio_irq_sync_unlock(struct irq_data *data)
> +{
> +       struct sl28cpld_gpio *gpio =
> +               gpiochip_get_data(irq_data_get_irq_chip_data(data));
> +
> +       regmap_write(gpio->regmap, gpio->offset + GPIO_REG_IE, gpio->ie);
> +       mutex_unlock(&gpio->lock);
> +}
> +
> +static void sl28cpld_gpio_irq_disable(struct irq_data *data)
> +{
> +       struct sl28cpld_gpio *gpio =
> +               gpiochip_get_data(irq_data_get_irq_chip_data(data));
> +
> +       if (data->hwirq >= 8)
> +               return;
> +
> +       gpio->ie &= ~(1 << data->hwirq);
> +}
> +
> +static void sl28cpld_gpio_irq_enable(struct irq_data *data)
> +{
> +       struct sl28cpld_gpio *gpio =
> +               gpiochip_get_data(irq_data_get_irq_chip_data(data));
> +
> +       if (data->hwirq >= 8)
> +               return;
> +
> +       gpio->ie |= (1 << data->hwirq);
> +}
> +
> +static int sl28cpld_gpio_irq_set_type(struct irq_data *data, unsigned int type)
> +{
> +       /* only edge triggered interrupts on both edges are supported */
> +       return (type == IRQ_TYPE_EDGE_BOTH) ? 0 : -EINVAL;
> +}
> +
> +static irqreturn_t sl28cpld_gpio_irq_thread(int irq, void *data)
> +{
> +       struct sl28cpld_gpio *gpio = data;
> +       unsigned int ip;
> +       unsigned int virq;
> +       int pin;
> +       int ret;
> +
> +       ret = regmap_read(gpio->regmap, gpio->offset + GPIO_REG_IP, &ip);
> +       if (ret)
> +               return IRQ_NONE;
> +
> +       /* mask other pending interrupts which are not enabled */
> +       ip &= gpio->ie;
> +
> +       /* ack the interrupts */
> +       regmap_write(gpio->regmap, gpio->offset + GPIO_REG_IP, ip);
> +
> +       /* and handle them */
> +       while (ip) {
> +               pin = __ffs(ip);
> +               ip &= ~BIT(pin);
> +
> +               virq = irq_find_mapping(gpio->gpio_chip.irq.domain, pin);
> +               if (virq)
> +                       handle_nested_irq(virq);
> +       }
> +
> +       return IRQ_HANDLED;
> +}

This definitely looks like parts of regmap_irqchip reimplemented.
Please check if you could reuse it - it would save a lot of code.

> +
> +static int sl28_cpld_gpio_irq_init(struct platform_device *pdev, int irq)
> +{
> +       struct sl28cpld_gpio *gpio = platform_get_drvdata(pdev);
> +       struct irq_chip *irq_chip = &gpio->irq_chip;
> +       int ret;
> +
> +       irq_chip->name = "sl28cpld-gpio-irq",
> +       irq_chip->irq_bus_lock = sl28cpld_gpio_irq_lock,
> +       irq_chip->irq_bus_sync_unlock = sl28cpld_gpio_irq_sync_unlock,
> +       irq_chip->irq_disable = sl28cpld_gpio_irq_disable,
> +       irq_chip->irq_enable = sl28cpld_gpio_irq_enable,
> +       irq_chip->irq_set_type = sl28cpld_gpio_irq_set_type,
> +       irq_chip->flags = IRQCHIP_SKIP_SET_WAKE,
> +
> +       ret = gpiochip_irqchip_add_nested(&gpio->gpio_chip, irq_chip, 0,
> +                                         handle_simple_irq, IRQ_TYPE_NONE);
> +       if (ret)
> +               return ret;
> +
> +       ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
> +                                       sl28cpld_gpio_irq_thread,
> +                                       IRQF_SHARED | IRQF_ONESHOT,
> +                                       pdev->name, gpio);
> +       if (ret)
> +               return ret;
> +
> +       gpiochip_set_nested_irqchip(&gpio->gpio_chip, irq_chip, irq);
> +
> +       return 0;
> +}
> +
> +static int sl28cpld_gpio_probe(struct platform_device *pdev)
> +{
> +       enum sl28cpld_gpio_type type =
> +               platform_get_device_id(pdev)->driver_data;
> +       struct device_node *np = pdev->dev.of_node;
> +       struct sl28cpld_gpio *gpio;
> +       struct gpio_chip *chip;
> +       struct resource *res;
> +       bool irq_support = false;
> +       int ret;
> +       int irq;
> +
> +       gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
> +       if (!gpio)
> +               return -ENOMEM;
> +
> +       if (!pdev->dev.parent)
> +               return -ENODEV;

Why not check this before allocating any memory?

> +
> +       gpio->regmap = dev_get_regmap(pdev->dev.parent, NULL);
> +       if (!gpio->regmap)
> +               return -ENODEV;
> +
> +       res = platform_get_resource(pdev, IORESOURCE_REG, 0);
> +       if (!res)
> +               return -EINVAL;
> +       gpio->offset = res->start;
> +

This isn't how IO resources are used. What are you trying to achieve here?

> +       /* initialize struct gpio_chip */
> +       mutex_init(&gpio->lock);
> +       chip = &gpio->gpio_chip;
> +       chip->parent = &pdev->dev;
> +       chip->label = dev_name(&pdev->dev);
> +       chip->owner = THIS_MODULE;
> +       chip->can_sleep = true;
> +       chip->base = -1;
> +       chip->ngpio = 8;
> +
> +       switch (type) {
> +       case sl28cpld_gpio:
> +               chip->get_direction = sl28cpld_gpio_get_direction;
> +               chip->direction_input = sl28cpld_gpio_direction_input;
> +               chip->direction_output = sl28cpld_gpio_direction_output;
> +               chip->get = sl28cpld_gpio_get;
> +               chip->set = sl28cpld_gpio_set;
> +               irq_support = true;
> +               break;
> +       case sl28cpld_gpo:
> +               chip->set = sl28cpld_gpo_set;
> +               chip->get = sl28cpld_gpi_get;
> +               break;
> +       case sl28cpld_gpi:
> +               chip->get = sl28cpld_gpi_get;
> +               break;
> +       }
> +
> +       ret = devm_gpiochip_add_data(&pdev->dev, chip, gpio);
> +       if (ret < 0)
> +               return ret;
> +
> +       platform_set_drvdata(pdev, gpio);
> +
> +       if (irq_support && of_property_read_bool(np, "interrupt-controller")) {

You're depending on OF_GPIO for this one function. Please switch to
device_property_read_bool() instead.

> +               irq = platform_get_irq(pdev, 0);
> +               if (irq < 0)
> +                       return ret;
> +
> +               ret = sl28_cpld_gpio_irq_init(pdev, irq);
> +               if (ret)
> +                       return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +static const struct platform_device_id sl28cpld_gpio_id_table[] = {
> +       {"sl28cpld-gpio", sl28cpld_gpio},
> +       {"sl28cpld-gpi", sl28cpld_gpi},
> +       {"sl28cpld-gpo", sl28cpld_gpo},

Could you explain this a bit more? Is this the same component with
input/output-only lines or three different components?

> +};
> +MODULE_DEVICE_TABLE(platform, sl28cpld_gpio_id_table);
> +
> +static struct platform_driver sl28cpld_gpio_driver = {
> +       .probe = sl28cpld_gpio_probe,
> +       .id_table = sl28cpld_gpio_id_table,
> +       .driver = {
> +               .name = "sl28cpld-gpio",
> +       },
> +};
> +module_platform_driver(sl28cpld_gpio_driver);
> +
> +MODULE_DESCRIPTION("sl28cpld GPIO Driver");
> +MODULE_LICENSE("GPL");

I think you could use a MODULE_ALIAS() here if you want this module to
be loaded automatically by udev.

> --
> 2.20.1
>

Best regards,
Bartosz Golaszewski

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

* Re: [PATCH 12/18] gpio: add support for the sl28cpld GPIO controller
  2020-03-18  9:14   ` Bartosz Golaszewski
@ 2020-03-18 12:45     ` Michael Walle
  2020-03-25 11:50       ` Bartosz Golaszewski
  2020-03-28 12:04     ` Michael Walle
  2020-03-28 17:20     ` Michael Walle
  2 siblings, 1 reply; 40+ messages in thread
From: Michael Walle @ 2020-03-18 12:45 UTC (permalink / raw)
  To: Bartosz Golaszewski
  Cc: linux-gpio, linux-devicetree, LKML, linux-hwmon, linux-pwm,
	LINUXWATCHDOG, arm-soc, Linus Walleij, Rob Herring, Jean Delvare,
	Guenter Roeck, Lee Jones, Thierry Reding, Uwe Kleine-König,
	Wim Van Sebroeck, Shawn Guo, Li Yang, Thomas Gleixner,
	Jason Cooper, Marc Zyngier

Hi Bartosz,

Am 2020-03-18 10:14, schrieb Bartosz Golaszewski:
> wt., 17 mar 2020 o 21:50 Michael Walle <michael@walle.cc> napisał(a):
>> 
>> This adds support for the GPIO controller of the sl28 board management
>> controller. This driver is part of a multi-function device.
>> 
>> Signed-off-by: Michael Walle <michael@walle.cc>
> 
> Hi Michael,
> 
> thanks for the driver. Please take a look at some comments below.

well, thank you for the very fast review!

>> ---
>>  drivers/gpio/Kconfig         |  11 ++
>>  drivers/gpio/Makefile        |   1 +
>>  drivers/gpio/gpio-sl28cpld.c | 332 
>> +++++++++++++++++++++++++++++++++++
>>  3 files changed, 344 insertions(+)
>>  create mode 100644 drivers/gpio/gpio-sl28cpld.c
>> 
>> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
>> index 3cbf8882a0dd..516e47017ef5 100644
>> --- a/drivers/gpio/Kconfig
>> +++ b/drivers/gpio/Kconfig
>> @@ -1211,6 +1211,17 @@ config GPIO_RC5T583
>>           This driver provides the support for driving/reading the 
>> gpio pins
>>           of RC5T583 device through standard gpio library.
>> 
>> +config GPIO_SL28CPLD
>> +       tristate "Kontron sl28 GPIO"
>> +       depends on MFD_SL28CPLD
>> +       depends on OF_GPIO
>> +       select GPIOLIB_IRQCHIP
> 
> Please see below - I think both are not needed.
> 
>> +       help
>> +         This enables support for the GPIOs found on the Kontron sl28 
>> CPLD.
>> +
>> +         This driver can also be built as a module. If so, the module 
>> will be
>> +         called gpio-sl28cpld.
>> +
>>  config GPIO_STMPE
>>         bool "STMPE GPIOs"
>>         depends on MFD_STMPE
>> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
>> index 0b571264ddbc..0ca2d52c78e8 100644
>> --- a/drivers/gpio/Makefile
>> +++ b/drivers/gpio/Makefile
>> @@ -127,6 +127,7 @@ obj-$(CONFIG_GPIO_SCH311X)          += 
>> gpio-sch311x.o
>>  obj-$(CONFIG_GPIO_SCH)                 += gpio-sch.o
>>  obj-$(CONFIG_GPIO_SIFIVE)              += gpio-sifive.o
>>  obj-$(CONFIG_GPIO_SIOX)                        += gpio-siox.o
>> +obj-$(CONFIG_GPIO_SL28CPLD)            += gpio-sl28cpld.o
>>  obj-$(CONFIG_GPIO_SODAVILLE)           += gpio-sodaville.o
>>  obj-$(CONFIG_GPIO_SPEAR_SPICS)         += gpio-spear-spics.o
>>  obj-$(CONFIG_GPIO_SPRD)                        += gpio-sprd.o
>> diff --git a/drivers/gpio/gpio-sl28cpld.c 
>> b/drivers/gpio/gpio-sl28cpld.c
>> new file mode 100644
>> index 000000000000..94f82013882f
>> --- /dev/null
>> +++ b/drivers/gpio/gpio-sl28cpld.c
>> @@ -0,0 +1,332 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * SMARC-sAL28 GPIO driver.
>> + *
>> + * Copyright 2019 Kontron Europe GmbH
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_device.h>
>> +#include <linux/of_address.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/regmap.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/gpio/driver.h>
>> +
>> +#define GPIO_REG_DIR   0
>> +#define GPIO_REG_OUT   1
>> +#define GPIO_REG_IN    2
>> +#define GPIO_REG_IE    3
>> +#define GPIO_REG_IP    4
> 
> These values would be more clear if they were defined as hex.
> 
>> +
>> +#define GPI_REG_IN     0
>> +
>> +#define GPO_REG_OUT    0
> 
> Please also use a common prefix even for defines.

ok

> 
>> +
>> +enum sl28cpld_gpio_type {
>> +       sl28cpld_gpio,
>> +       sl28cpld_gpi,
>> +       sl28cpld_gpo,
>> +};
> 
> Enum values should be all upper-case.

ok

>> +
>> +struct sl28cpld_gpio {
>> +       struct gpio_chip gpio_chip;
>> +       struct irq_chip irq_chip;
>> +       struct regmap *regmap;
>> +       u32 offset;
>> +       struct mutex lock;
>> +       u8 ie;
>> +};
>> +
>> +static void sl28cpld_gpio_set_reg(struct gpio_chip *chip, unsigned 
>> int reg,
>> +                                 unsigned int offset, int value)
>> +{
>> +       struct sl28cpld_gpio *gpio = gpiochip_get_data(chip);
>> +       unsigned int mask = 1 << offset;
>> +       unsigned int val = value << offset;
>> +
>> +       regmap_update_bits(gpio->regmap, gpio->offset + reg, mask, 
>> val);
>> +}
>> +
>> +static void sl28cpld_gpio_set(struct gpio_chip *chip, unsigned int 
>> offset,
>> +                             int value)
>> +{
>> +       sl28cpld_gpio_set_reg(chip, GPIO_REG_OUT, offset, value);
>> +}
>> +
>> +static void sl28cpld_gpo_set(struct gpio_chip *chip, unsigned int 
>> offset,
>> +                            int value)
>> +{
>> +       sl28cpld_gpio_set_reg(chip, GPO_REG_OUT, offset, value);
>> +}
>> +
>> +static int sl28cpld_gpio_get_reg(struct gpio_chip *chip, unsigned int 
>> reg,
>> +                                unsigned int offset)
>> +{
>> +       struct sl28cpld_gpio *gpio = gpiochip_get_data(chip);
>> +       unsigned int mask = 1 << offset;
>> +       unsigned int val;
>> +       int ret;
>> +
>> +       ret = regmap_read(gpio->regmap, gpio->offset + reg, &val);
>> +       if (ret)
>> +               return ret;
>> +
>> +       return (val & mask) ? 1 : 0;
>> +}
>> +
>> +static int sl28cpld_gpio_get(struct gpio_chip *chip, unsigned int 
>> offset)
>> +{
>> +       return sl28cpld_gpio_get_reg(chip, GPIO_REG_IN, offset);
>> +}
>> +
>> +static int sl28cpld_gpi_get(struct gpio_chip *chip, unsigned int 
>> offset)
>> +{
>> +       return sl28cpld_gpio_get_reg(chip, GPI_REG_IN, offset);
>> +}
>> +
>> +static int sl28cpld_gpio_get_direction(struct gpio_chip *chip,
>> +                                      unsigned int offset)
>> +{
>> +       struct sl28cpld_gpio *gpio = gpiochip_get_data(chip);
>> +       unsigned int reg;
>> +       int ret;
>> +
>> +       ret = regmap_read(gpio->regmap, gpio->offset + GPIO_REG_DIR, 
>> &reg);
>> +       if (ret)
>> +               return ret;
>> +
>> +       if (reg & (1 << offset))
>> +               return GPIO_LINE_DIRECTION_OUT;
>> +       else
>> +               return GPIO_LINE_DIRECTION_IN;
>> +}
>> +
>> +static int sl28cpld_gpio_set_direction(struct gpio_chip *chip,
>> +                                      unsigned int offset,
>> +                                      bool output)
>> +{
>> +       struct sl28cpld_gpio *gpio = gpiochip_get_data(chip);
>> +       unsigned int mask = 1 << offset;
>> +       unsigned int val = (output) ? mask : 0;
>> +
>> +       return regmap_update_bits(gpio->regmap, gpio->offset + 
>> GPIO_REG_DIR,
>> +                                 mask, val);
>> +
> 
> Stray newline.
ok

> 
>> +}
>> +
>> +static int sl28cpld_gpio_direction_input(struct gpio_chip *chip,
>> +                                        unsigned int offset)
>> +{
>> +       return sl28cpld_gpio_set_direction(chip, offset, false);
>> +}
>> +
>> +static int sl28cpld_gpio_direction_output(struct gpio_chip *chip,
>> +                                         unsigned int offset, int 
>> value)
>> +{
>> +       sl28cpld_gpio_set_reg(chip, GPIO_REG_OUT, offset, value);
>> +       return sl28cpld_gpio_set_direction(chip, offset, true);
>> +}
>> +
>> +static void sl28cpld_gpio_irq_lock(struct irq_data *data)
>> +{
>> +       struct sl28cpld_gpio *gpio =
>> +               gpiochip_get_data(irq_data_get_irq_chip_data(data));
>> +
>> +       mutex_lock(&gpio->lock);
> 
> How does that actually lock anything?

TBH, I took that from gpio-pcf857x.c. But that
  (1) don't uses regmap
  (2) also uses that lock on other places.

I'll dig deeper into that and try to understand why there is a lock at
all and why this callback is actually called _irq_lock() because that
made me wonder.

> Regmap uses a different lock and
> if you want to make sure nobody modifies the GPIO registers than you'd
> need to use the same lock. Also: this looks a lot like a task for
> regmap_irqchip - maybe you could use it here or in the core mfd
> module?

regmap_irqchip will register the interrupt controller on the device
which owns the regmap, ie. the parent. So (1) the phandle would need to
point to the parent device instead of the GPIO subnode and (2) I'm
already using the regmap_irqchip for the interrupt controller. I don't
know if you can actually have that multiple times.

there was a discussion which might apply partly to (1):
  https://lore.kernel.org/patchwork/patch/802608/

> 
>> +}
>> +
>> +static void sl28cpld_gpio_irq_sync_unlock(struct irq_data *data)
>> +{
>> +       struct sl28cpld_gpio *gpio =
>> +               gpiochip_get_data(irq_data_get_irq_chip_data(data));
>> +
>> +       regmap_write(gpio->regmap, gpio->offset + GPIO_REG_IE, 
>> gpio->ie);
>> +       mutex_unlock(&gpio->lock);
>> +}
>> +
>> +static void sl28cpld_gpio_irq_disable(struct irq_data *data)
>> +{
>> +       struct sl28cpld_gpio *gpio =
>> +               gpiochip_get_data(irq_data_get_irq_chip_data(data));
>> +
>> +       if (data->hwirq >= 8)
>> +               return;
>> +
>> +       gpio->ie &= ~(1 << data->hwirq);
>> +}
>> +
>> +static void sl28cpld_gpio_irq_enable(struct irq_data *data)
>> +{
>> +       struct sl28cpld_gpio *gpio =
>> +               gpiochip_get_data(irq_data_get_irq_chip_data(data));
>> +
>> +       if (data->hwirq >= 8)
>> +               return;
>> +
>> +       gpio->ie |= (1 << data->hwirq);
>> +}
>> +
>> +static int sl28cpld_gpio_irq_set_type(struct irq_data *data, unsigned 
>> int type)
>> +{
>> +       /* only edge triggered interrupts on both edges are supported 
>> */
>> +       return (type == IRQ_TYPE_EDGE_BOTH) ? 0 : -EINVAL;
>> +}
>> +
>> +static irqreturn_t sl28cpld_gpio_irq_thread(int irq, void *data)
>> +{
>> +       struct sl28cpld_gpio *gpio = data;
>> +       unsigned int ip;
>> +       unsigned int virq;
>> +       int pin;
>> +       int ret;
>> +
>> +       ret = regmap_read(gpio->regmap, gpio->offset + GPIO_REG_IP, 
>> &ip);
>> +       if (ret)
>> +               return IRQ_NONE;
>> +
>> +       /* mask other pending interrupts which are not enabled */
>> +       ip &= gpio->ie;
>> +
>> +       /* ack the interrupts */
>> +       regmap_write(gpio->regmap, gpio->offset + GPIO_REG_IP, ip);
>> +
>> +       /* and handle them */
>> +       while (ip) {
>> +               pin = __ffs(ip);
>> +               ip &= ~BIT(pin);
>> +
>> +               virq = irq_find_mapping(gpio->gpio_chip.irq.domain, 
>> pin);
>> +               if (virq)
>> +                       handle_nested_irq(virq);
>> +       }
>> +
>> +       return IRQ_HANDLED;
>> +}
> 
> This definitely looks like parts of regmap_irqchip reimplemented.
> Please check if you could reuse it - it would save a lot of code.

See above. I'd be happy to reuse the code though.

> 
>> +
>> +static int sl28_cpld_gpio_irq_init(struct platform_device *pdev, int 
>> irq)
>> +{
>> +       struct sl28cpld_gpio *gpio = platform_get_drvdata(pdev);
>> +       struct irq_chip *irq_chip = &gpio->irq_chip;
>> +       int ret;
>> +
>> +       irq_chip->name = "sl28cpld-gpio-irq",
>> +       irq_chip->irq_bus_lock = sl28cpld_gpio_irq_lock,
>> +       irq_chip->irq_bus_sync_unlock = sl28cpld_gpio_irq_sync_unlock,
>> +       irq_chip->irq_disable = sl28cpld_gpio_irq_disable,
>> +       irq_chip->irq_enable = sl28cpld_gpio_irq_enable,
>> +       irq_chip->irq_set_type = sl28cpld_gpio_irq_set_type,
>> +       irq_chip->flags = IRQCHIP_SKIP_SET_WAKE,
>> +
>> +       ret = gpiochip_irqchip_add_nested(&gpio->gpio_chip, irq_chip, 
>> 0,
>> +                                         handle_simple_irq, 
>> IRQ_TYPE_NONE);
>> +       if (ret)
>> +               return ret;
>> +
>> +       ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
>> +                                       sl28cpld_gpio_irq_thread,
>> +                                       IRQF_SHARED | IRQF_ONESHOT,
>> +                                       pdev->name, gpio);
>> +       if (ret)
>> +               return ret;
>> +
>> +       gpiochip_set_nested_irqchip(&gpio->gpio_chip, irq_chip, irq);
>> +
>> +       return 0;
>> +}
>> +
>> +static int sl28cpld_gpio_probe(struct platform_device *pdev)
>> +{
>> +       enum sl28cpld_gpio_type type =
>> +               platform_get_device_id(pdev)->driver_data;
>> +       struct device_node *np = pdev->dev.of_node;
>> +       struct sl28cpld_gpio *gpio;
>> +       struct gpio_chip *chip;
>> +       struct resource *res;
>> +       bool irq_support = false;
>> +       int ret;
>> +       int irq;
>> +
>> +       gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
>> +       if (!gpio)
>> +               return -ENOMEM;
>> +
>> +       if (!pdev->dev.parent)
>> +               return -ENODEV;
> 
> Why not check this before allocating any memory?

I'll change that, you're not the first one which notices that. My reason
was to have the check together with the dev_get_regmap() which uses the
parent, expecting that the error case only happen exceptionally.

> 
>> +
>> +       gpio->regmap = dev_get_regmap(pdev->dev.parent, NULL);
>> +       if (!gpio->regmap)
>> +               return -ENODEV;
>> +
>> +       res = platform_get_resource(pdev, IORESOURCE_REG, 0);
>> +       if (!res)
>> +               return -EINVAL;
>> +       gpio->offset = res->start;
>> +
> 
> This isn't how IO resources are used. What are you trying to achieve 
> here?

Mh are you sure? The blueprint for this were the regulators in
drivers/regulators/, eg the wm831x-ldo.c. IORESOURCE_REG isn't used
that often. But here is what I want to achieve (for which I haven't
found any existing drivers for now):
  (1) the individual blocks of the overall sl28cpld may be used
      multiple times, eg. this driver only has the offset to a
      base address. So if there are two blocks, this mfd core
      driver will register two devices for this driver with
      different base offsets, which are passed by IORESOURCE_REG
  (2) I wanted to avoid having a private mfd include with some
      kind of "proprietary" method how to get that offset
  (3) the mfd core driver is the one knowing the offset, thus it
      is possible to have different flavours of the sl28cpld


> 
>> +       /* initialize struct gpio_chip */
>> +       mutex_init(&gpio->lock);
>> +       chip = &gpio->gpio_chip;
>> +       chip->parent = &pdev->dev;
>> +       chip->label = dev_name(&pdev->dev);
>> +       chip->owner = THIS_MODULE;
>> +       chip->can_sleep = true;
>> +       chip->base = -1;
>> +       chip->ngpio = 8;
>> +
>> +       switch (type) {
>> +       case sl28cpld_gpio:
>> +               chip->get_direction = sl28cpld_gpio_get_direction;
>> +               chip->direction_input = sl28cpld_gpio_direction_input;
>> +               chip->direction_output = 
>> sl28cpld_gpio_direction_output;
>> +               chip->get = sl28cpld_gpio_get;
>> +               chip->set = sl28cpld_gpio_set;
>> +               irq_support = true;
>> +               break;
>> +       case sl28cpld_gpo:
>> +               chip->set = sl28cpld_gpo_set;
>> +               chip->get = sl28cpld_gpi_get;
>> +               break;
>> +       case sl28cpld_gpi:
>> +               chip->get = sl28cpld_gpi_get;
>> +               break;
>> +       }
>> +
>> +       ret = devm_gpiochip_add_data(&pdev->dev, chip, gpio);
>> +       if (ret < 0)
>> +               return ret;
>> +
>> +       platform_set_drvdata(pdev, gpio);
>> +
>> +       if (irq_support && of_property_read_bool(np, 
>> "interrupt-controller")) {
> 
> You're depending on OF_GPIO for this one function. Please switch to
> device_property_read_bool() instead.

ok


> 
>> +               irq = platform_get_irq(pdev, 0);
>> +               if (irq < 0)
>> +                       return ret;
>> +
>> +               ret = sl28_cpld_gpio_irq_init(pdev, irq);
>> +               if (ret)
>> +                       return ret;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static const struct platform_device_id sl28cpld_gpio_id_table[] = {
>> +       {"sl28cpld-gpio", sl28cpld_gpio},
>> +       {"sl28cpld-gpi", sl28cpld_gpi},
>> +       {"sl28cpld-gpo", sl28cpld_gpo},
> 
> Could you explain this a bit more? Is this the same component with
> input/output-only lines or three different components?

These are actually three different components. Ie. you could have a
flavour where you have one GPIO (sl28cpld-gpio) and two output-only
ones (sl28cpld-gpo). Is that what you wanted to know?

> 
>> +};
>> +MODULE_DEVICE_TABLE(platform, sl28cpld_gpio_id_table);
>> +
>> +static struct platform_driver sl28cpld_gpio_driver = {
>> +       .probe = sl28cpld_gpio_probe,
>> +       .id_table = sl28cpld_gpio_id_table,
>> +       .driver = {
>> +               .name = "sl28cpld-gpio",
>> +       },
>> +};
>> +module_platform_driver(sl28cpld_gpio_driver);
>> +
>> +MODULE_DESCRIPTION("sl28cpld GPIO Driver");
>> +MODULE_LICENSE("GPL");
> 
> I think you could use a MODULE_ALIAS() here if you want this module to
> be loaded automatically by udev.

ok, I'll look into that.

thanks,
-michael

> 
>> --
>> 2.20.1
>> 
> 
> Best regards,
> Bartosz Golaszewski

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

* Re: [PATCH 14/18] hwmon: add support for the sl28cpld hardware monitoring controller
  2020-03-18  3:27   ` Guenter Roeck
@ 2020-03-18 16:32     ` Michael Walle
  0 siblings, 0 replies; 40+ messages in thread
From: Michael Walle @ 2020-03-18 16:32 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: linux-gpio, devicetree, linux-kernel, linux-hwmon, linux-pwm,
	linux-watchdog, linux-arm-kernel, Linus Walleij,
	Bartosz Golaszewski, Rob Herring, Jean Delvare, Lee Jones,
	Thierry Reding, Uwe Kleine-König, Wim Van Sebroeck,
	Shawn Guo, Li Yang, Thomas Gleixner, Jason Cooper, Marc Zyngier,
	Guenter Roeck

Hi Guenter,

thanks for the review

Am 2020-03-18 04:27, schrieb Guenter Roeck:
> On 3/17/20 1:50 PM, Michael Walle wrote:
>> This adds support for the hardware monitoring controller of the 
>> sl28cpld
>> board management controller. This driver is part of a multi-function
>> device.
>> 
>> Signed-off-by: Michael Walle <michael@walle.cc>
>> ---
>>  drivers/hwmon/Kconfig          |  10 +++
>>  drivers/hwmon/Makefile         |   1 +
>>  drivers/hwmon/sl28cpld-hwmon.c | 146 
>> +++++++++++++++++++++++++++++++++
> 
> Please also provide Documentation/hwmon/sl28cpld.
> 
>>  3 files changed, 157 insertions(+)
>>  create mode 100644 drivers/hwmon/sl28cpld-hwmon.c
>> 
>> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
>> index 05a30832c6ba..c98716f78cfa 100644
>> --- a/drivers/hwmon/Kconfig
>> +++ b/drivers/hwmon/Kconfig
>> @@ -1412,6 +1412,16 @@ config SENSORS_RASPBERRYPI_HWMON
>>  	  This driver can also be built as a module. If so, the module
>>  	  will be called raspberrypi-hwmon.
>> 
>> +config SENSORS_SL28CPLD
>> +	tristate "Kontron's SMARC-sAL28 hardware monitoring driver"
>> +	depends on MFD_SL28CPLD
>> +	help
>> +	  If you say yes here you get support for a fan connected to the
>> +	  input of the SMARC connector of Kontron's SMARC-sAL28 module.
>> +
>> +	  This driver can also be built as a module.  If so, the module
>> +	  will be called sl28cpld-hwmon.
>> +
>>  config SENSORS_SHT15
>>  	tristate "Sensiron humidity and temperature sensors. SHT15 and 
>> compat."
>>  	depends on GPIOLIB || COMPILE_TEST
>> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
>> index b0b9c8e57176..dfb0f8cda2dd 100644
>> --- a/drivers/hwmon/Makefile
>> +++ b/drivers/hwmon/Makefile
>> @@ -155,6 +155,7 @@ obj-$(CONFIG_SENSORS_S3C)	+= s3c-hwmon.o
>>  obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o
>>  obj-$(CONFIG_SENSORS_SCH5627)	+= sch5627.o
>>  obj-$(CONFIG_SENSORS_SCH5636)	+= sch5636.o
>> +obj-$(CONFIG_SENSORS_SL28CPLD)	+= sl28cpld-hwmon.o
>>  obj-$(CONFIG_SENSORS_SHT15)	+= sht15.o
>>  obj-$(CONFIG_SENSORS_SHT21)	+= sht21.o
>>  obj-$(CONFIG_SENSORS_SHT3x)	+= sht3x.o
>> diff --git a/drivers/hwmon/sl28cpld-hwmon.c 
>> b/drivers/hwmon/sl28cpld-hwmon.c
>> new file mode 100644
>> index 000000000000..7ac42bb0a48c
>> --- /dev/null
>> +++ b/drivers/hwmon/sl28cpld-hwmon.c
>> @@ -0,0 +1,146 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * SMARC-sAL28 fan hardware monitoring driver.
>> + *
>> + * Copyright 2019 Kontron Europe GmbH
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/bitfield.h>
>> +#include <linux/of.h>
>> +#include <linux/of_device.h>
>> +#include <linux/of_address.h>
>> +#include <linux/regmap.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/hwmon.h>
>> +
> Alphabetic order of include files, please.

ok, I guess that applies to all of the new files.


>> +#define FAN_INPUT		0
>> +#define   FAN_SCALE_X8		BIT(7)
>> +#define   FAN_VALUE_MASK	GENMASK(6, 0)
>> +
>> +struct sl28cpld_hwmon {
>> +	struct regmap *regmap;
>> +	u32 offset;
>> +};
>> +
>> +static umode_t sl28cpld_hwmon_is_visible(const void *data,
>> +					 enum hwmon_sensor_types type,
>> +					 u32 attr, int channel)
>> +{
>> +	return 0444;
>> +}
>> +
>> +static int sl28cpld_hwmon_read(struct device *dev,
>> +			       enum hwmon_sensor_types type, u32 attr,
>> +			       int channel, long *input)
>> +{
>> +	struct sl28cpld_hwmon *hwmon = dev_get_drvdata(dev);
>> +	unsigned int value;
>> +	int ret;
>> +
>> +	switch (attr) {
>> +	case hwmon_fan_input:
>> +		ret = regmap_read(hwmon->regmap, hwmon->offset + FAN_INPUT,
>> +				  &value);
>> +		if (ret)
>> +			return ret;
>> +		/*
>> +		 * The register has a 7 bit value and 1 bit which indicates the
>> +		 * scale. If the MSB is set, then the lower 7 bit has to be
>> +		 * multiplied by 8, to get the correct reading.
>> +		 */
>> +		if (value & FAN_SCALE_X8)
>> +			value = FIELD_GET(FAN_VALUE_MASK, value) << 3;
>> +
>> +		/*
>> +		 * The counter period is 1000ms and the sysfs specification
>> +		 * says we should asssume 2 pulses per revolution.
>> +		 */
>> +		value *= 60 / 2;
>> +
>> +		break;
>> +	default:
>> +		return -EOPNOTSUPP;
>> +	}
>> +
>> +	*input = value;
>> +	return 0;
>> +}
>> +
>> +static const u32 sl28cpld_hwmon_fan_config[] = {
>> +	HWMON_F_INPUT,
>> +	0
>> +};
>> +
>> +static const struct hwmon_channel_info sl28cpld_hwmon_fan = {
>> +	.type = hwmon_fan,
>> +	.config = sl28cpld_hwmon_fan_config,
>> +};
>> +
>> +static const struct hwmon_channel_info *sl28cpld_hwmon_info[] = {
>> +	&sl28cpld_hwmon_fan,
>> +	NULL
>> +};
>> +
>> +static const struct hwmon_ops sl28cpld_hwmon_ops = {
>> +	.is_visible = sl28cpld_hwmon_is_visible,
>> +	.read = sl28cpld_hwmon_read,
>> +};
>> +
>> +static const struct hwmon_chip_info sl28cpld_hwmon_chip_info = {
>> +	.ops = &sl28cpld_hwmon_ops,
>> +	.info = sl28cpld_hwmon_info,
>> +};
>> +
>> +static int sl28cpld_hwmon_probe(struct platform_device *pdev)
>> +{
>> +	struct device *hwmon_dev;
>> +	struct sl28cpld_hwmon *hwmon;
>> +	struct resource *res;
>> +
>> +	hwmon = devm_kzalloc(&pdev->dev, sizeof(*hwmon), GFP_KERNEL);
>> +	if (!hwmon)
>> +		return -ENOMEM;
>> +
>> +	if (!pdev->dev.parent)
>> +		return -ENODEV;
>> +
> Maybe do this first ?

ok. like explained on another patch review comment, I had that
together with the following dev_get_regmap() which uses this.
But I was already not sure while writing the code whether I
should do it first and don't have to undo the devm_ in case of
an error. Well since already two reviewers stumbled on this,
I'll do it first on all the patches.


>> +	hwmon->regmap = dev_get_regmap(pdev->dev.parent, NULL);
>> +	if (!hwmon->regmap)
>> +		return -ENODEV;
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_REG, 0);
>> +	if (!res)
>> +		return -EINVAL;
>> +	hwmon->offset = res->start;
>> +
>> +	hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev,
>> +							 "sl28cpld_hwmon",
>> +							 hwmon,
>> +							 &sl28cpld_hwmon_chip_info,
>> +							 NULL);
>> +	if (IS_ERR(hwmon_dev)) {
>> +		dev_err(&pdev->dev, "failed to register as hwmon device");
>> +		return PTR_ERR(hwmon_dev);
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct platform_device_id sl28cpld_hwmon_id_table[] = {
>> +	{"sl28cpld-fan", 0},
>> +};
>> +MODULE_DEVICE_TABLE(platform, sl28cpld_hwmon_id_table);
>> +
>> +static struct platform_driver sl28cpld_hwmon_driver = {
>> +	.probe = sl28cpld_hwmon_probe,
>> +	.id_table = sl28cpld_hwmon_id_table,
> 
> I'd have expected an of_match_table.

There was one, but, since I use mfd_add_devices() which uses the
platform driver name (or the id_table) I removed it. While it
doesn't really matter here in the hwmon part for now, the GPIO
driver uses the id_table data to distinguish between different
flavors of the GPIO controller. So there I'd have to duplicate
data in the tables (eg. the id_table as well as the
of_match_table) and get that data in the _probe(), although only
the id_table data is used.

-michael

> 
>> +	.driver = {
>> +		.name = "sl28cpld-hwmon",
>> +	},
>> +};
>> +module_platform_driver(sl28cpld_hwmon_driver);
>> +
>> +MODULE_DESCRIPTION("sl28cpld Hardware Monitoring Driver");
>> +MODULE_LICENSE("GPL");
>> 

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

* Re: [PATCH 05/18] mfd: Add support for Kontron sl28cpld management controller
  2020-03-18  3:28   ` Guenter Roeck
@ 2020-03-18 16:38     ` Michael Walle
  0 siblings, 0 replies; 40+ messages in thread
From: Michael Walle @ 2020-03-18 16:38 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: linux-gpio, devicetree, linux-kernel, linux-hwmon, linux-pwm,
	linux-watchdog, linux-arm-kernel, Linus Walleij,
	Bartosz Golaszewski, Rob Herring, Jean Delvare, Lee Jones,
	Thierry Reding, Uwe Kleine-König, Wim Van Sebroeck,
	Shawn Guo, Li Yang, Thomas Gleixner, Jason Cooper, Marc Zyngier,
	Guenter Roeck

Am 2020-03-18 04:28, schrieb Guenter Roeck:
> On 3/17/20 1:50 PM, Michael Walle wrote:
>> This patch adds core support for the board management controller found
>> on the SMARC-sAL28 board. It consists of the following functions:
>>  - watchdog
>>  - GPIO controller
>>  - PWM controller
>>  - fan sensor
>>  - interrupt controller
>> 
>> At the moment, this controller is used on the Kontron SMARC-sAL28 
>> board.
>> 
>> Signed-off-by: Michael Walle <michael@walle.cc>
>> ---
>>  drivers/mfd/Kconfig    |  21 ++++++
>>  drivers/mfd/Makefile   |   2 +
>>  drivers/mfd/sl28cpld.c | 155 
>> +++++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 178 insertions(+)
>>  create mode 100644 drivers/mfd/sl28cpld.c
>> 
>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>> index 3c547ed575e6..01588c366476 100644
>> --- a/drivers/mfd/Kconfig
>> +++ b/drivers/mfd/Kconfig
>> @@ -2059,5 +2059,26 @@ config SGI_MFD_IOC3
>>  	  If you have an SGI Origin, Octane, or a PCI IOC3 card,
>>  	  then say Y. Otherwise say N.
>> 
>> +config MFD_SL28CPLD
>> +	tristate "Kontron sl28 core driver"
>> +	depends on I2C=y
> 
> Why I2C=y and not just I2C ?

Oh this should be changed in the next patch which adds
interrupt controller support, where I2C=y is needed, correct?

-michael

> 
>> +	depends on OF
>> +	select REGMAP_I2C
>> +	select REGMAP_IRQ
>> +	select SL28CPLD_IRQ
>> +	select MFD_CORE
>> +	help
>> +	  This option enables support for the board management controller
>> +	  found on the Kontron sl28 CPLD. You have to select individual
>> +	  functions, such as watchdog, GPIO, etc, under the corresponding 
>> menus
>> +	  in order to enable them.
>> +
>> +	  Currently supported boards are:
>> +
>> +		Kontron SMARC-sAL28
>> +
>> +	  To compile this driver as a module, choose M here: the module will 
>> be
>> +	  called sl28cpld.
>> +
>>  endmenu
>>  endif
>> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
>> index f935d10cbf0f..9bc38863b9c7 100644
>> --- a/drivers/mfd/Makefile
>> +++ b/drivers/mfd/Makefile
>> @@ -259,3 +259,5 @@ obj-$(CONFIG_MFD_ROHM_BD718XX)	+= rohm-bd718x7.o
>>  obj-$(CONFIG_MFD_STMFX) 	+= stmfx.o
>> 
>>  obj-$(CONFIG_SGI_MFD_IOC3)	+= ioc3.o
>> +
>> +obj-$(CONFIG_MFD_SL28CPLD)	+= sl28cpld.o
>> diff --git a/drivers/mfd/sl28cpld.c b/drivers/mfd/sl28cpld.c
>> new file mode 100644
>> index 000000000000..789f21f90752
>> --- /dev/null
>> +++ b/drivers/mfd/sl28cpld.c
>> @@ -0,0 +1,155 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * MFD core for the CPLD on a SMARC-sAL28 board.
>> + *
>> + * Copyright 2019 Kontron Europe GmbH
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_address.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/i2c.h>
>> +#include <linux/regmap.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/mfd/core.h>
>> +
>> +#define SL28CPLD_VERSION 0x03
>> +#define SL28CPLD_WATCHDOG_BASE 0x4
>> +#define SL28CPLD_HWMON_FAN_BASE 0xb
>> +#define SL28CPLD_PWM0_BASE 0xc
>> +#define SL28CPLD_PWM1_BASE 0xe
>> +#define SL28CPLD_GPIO0_BASE 0x10
>> +#define SL28CPLD_GPIO1_BASE 0x15
>> +#define SL28CPLD_GPO_BASE 0x1a
>> +#define SL28CPLD_GPI_BASE 0x1b
>> +#define SL28CPLD_INTC_BASE 0x1c
>> +
>> +/* all subdevices share the same IRQ */
>> +#define SL28CPLD_IRQ 0
>> +
>> +#define SL28CPLD_MIN_REQ_VERSION 14
>> +
>> +struct sl28cpld {
>> +	struct device *dev;
>> +	struct regmap *regmap;
>> +};
>> +
>> +static const struct regmap_config sl28cpld_regmap_config = {
>> +	.reg_bits = 8,
>> +	.val_bits = 8,
>> +	.reg_stride = 1,
>> +};
>> +
>> +static struct resource sl28cpld_watchdog_resources[] = {
>> +	DEFINE_RES_REG(SL28CPLD_WATCHDOG_BASE, 1),
>> +};
>> +
>> +static struct resource sl28cpld_hwmon_fan_resources[] = {
>> +	DEFINE_RES_REG(SL28CPLD_HWMON_FAN_BASE, 1),
>> +};
>> +
>> +static struct resource sl28cpld_pwm0_resources[] = {
>> +	DEFINE_RES_REG(SL28CPLD_PWM0_BASE, 1),
>> +};
>> +
>> +static struct resource sl28cpld_pwm1_resources[] = {
>> +	DEFINE_RES_REG(SL28CPLD_PWM1_BASE, 1),
>> +};
>> +
>> +static struct resource sl28cpld_gpio0_resources[] = {
>> +	DEFINE_RES_REG(SL28CPLD_GPIO0_BASE, 1),
>> +	DEFINE_RES_IRQ(SL28CPLD_IRQ),
>> +};
>> +
>> +static struct resource sl28cpld_gpio1_resources[] = {
>> +	DEFINE_RES_REG(SL28CPLD_GPIO1_BASE, 1),
>> +	DEFINE_RES_IRQ(SL28CPLD_IRQ),
>> +};
>> +
>> +static struct resource sl28cpld_gpo_resources[] = {
>> +	DEFINE_RES_REG(SL28CPLD_GPO_BASE, 1),
>> +};
>> +
>> +static struct resource sl28cpld_gpi_resources[] = {
>> +	DEFINE_RES_REG(SL28CPLD_GPI_BASE, 1),
>> +};
>> +
>> +static struct resource sl28cpld_intc_resources[] = {
>> +	DEFINE_RES_REG(SL28CPLD_INTC_BASE, 1),
>> +	DEFINE_RES_IRQ(SL28CPLD_IRQ),
>> +};
>> +
>> +static const struct mfd_cell sl28cpld_devs[] = {
>> +	OF_MFD_CELL("sl28cpld-wdt", sl28cpld_watchdog_resources, NULL, 0, 0,
>> +		    "kontron,sl28cpld-wdt"),
>> +	OF_MFD_CELL("sl28cpld-fan", sl28cpld_hwmon_fan_resources, NULL, 0, 
>> 0,
>> +		    "kontron,sl28cpld-fan"),
>> +	OF_MFD_CELL("sl28cpld-pwm", sl28cpld_pwm0_resources, NULL, 0, 0,
>> +		    "kontron,sl28cpld-pwm"),
>> +	OF_MFD_CELL("sl28cpld-pwm", sl28cpld_pwm1_resources, NULL, 0, 1,
>> +		    "kontron,sl28cpld-pwm"),
>> +	OF_MFD_CELL("sl28cpld-gpio", sl28cpld_gpio0_resources, NULL, 0, 0,
>> +		    "kontron,sl28cpld-gpio"),
>> +	OF_MFD_CELL("sl28cpld-gpio", sl28cpld_gpio1_resources, NULL, 0, 1,
>> +		    "kontron,sl28cpld-gpio"),
>> +	OF_MFD_CELL("sl28cpld-gpo", sl28cpld_gpo_resources, NULL, 0, 0,
>> +		    "kontron,sl28cpld-gpo"),
>> +	OF_MFD_CELL("sl28cpld-gpi", sl28cpld_gpi_resources, NULL, 0, 0,
>> +		    "kontron,sl28cpld-gpi"),
>> +	OF_MFD_CELL("sl28cpld-intc", sl28cpld_intc_resources, NULL, 0, 0,
>> +		    "kontron,sl28cpld-intc"),
>> +};
>> +
>> +static int sl28cpld_probe(struct i2c_client *i2c)
>> +{
>> +	struct sl28cpld *sl28cpld;
>> +	struct device *dev = &i2c->dev;
>> +	unsigned int cpld_version;
>> +	int ret;
>> +
>> +	sl28cpld = devm_kzalloc(dev, sizeof(*sl28cpld), GFP_KERNEL);
>> +	if (!sl28cpld)
>> +		return -ENOMEM;
>> +
>> +	sl28cpld->regmap = devm_regmap_init_i2c(i2c, 
>> &sl28cpld_regmap_config);
>> +	if (IS_ERR(sl28cpld->regmap))
>> +		return PTR_ERR(sl28cpld->regmap);
>> +
>> +	ret = regmap_read(sl28cpld->regmap, SL28CPLD_VERSION, 
>> &cpld_version);
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (cpld_version < SL28CPLD_MIN_REQ_VERSION) {
>> +		dev_err(dev, "unsupported CPLD version %d\n", cpld_version);
>> +		return -ENODEV;
>> +	}
>> +
>> +	sl28cpld->dev = dev;
>> +	i2c_set_clientdata(i2c, sl28cpld);
>> +
>> +	dev_info(dev, "successfully probed. CPLD version %d\n", 
>> cpld_version);
>> +
>> +	return devm_mfd_add_devices(dev, -1, sl28cpld_devs,
>> +				    ARRAY_SIZE(sl28cpld_devs), NULL,
>> +				    i2c->irq, NULL);
>> +}
>> +
>> +static const struct of_device_id sl28cpld_of_match[] = {
>> +	{ .compatible = "kontron,sl28cpld", },
>> +	{}
>> +};
>> +MODULE_DEVICE_TABLE(of, sl28cpld_of_match);
>> +
>> +static struct i2c_driver sl28cpld_driver = {
>> +	.probe_new = sl28cpld_probe,
>> +	.driver = {
>> +		.name = "sl28cpld",
>> +		.of_match_table = of_match_ptr(sl28cpld_of_match),
>> +	},
>> +};
>> +module_i2c_driver(sl28cpld_driver);
>> +
>> +MODULE_DESCRIPTION("sl28cpld MFD Core Driver");
>> +MODULE_LICENSE("GPL");
>> 

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

* Re: [PATCH 06/18] irqchip: add sl28cpld interrupt controller support
  2020-03-17 20:50 ` [PATCH 06/18] irqchip: add sl28cpld interrupt controller support Michael Walle
@ 2020-03-18 16:53   ` Guenter Roeck
  2020-03-18 17:06     ` Michael Walle
  0 siblings, 1 reply; 40+ messages in thread
From: Guenter Roeck @ 2020-03-18 16:53 UTC (permalink / raw)
  To: Michael Walle, linux-gpio, devicetree, linux-kernel, linux-hwmon,
	linux-pwm, linux-watchdog, linux-arm-kernel
  Cc: Linus Walleij, Bartosz Golaszewski, Rob Herring, Jean Delvare,
	Lee Jones, Thierry Reding, Uwe Kleine-König,
	Wim Van Sebroeck, Shawn Guo, Li Yang, Thomas Gleixner,
	Jason Cooper, Marc Zyngier

On 3/17/20 1:50 PM, Michael Walle wrote:
> This patch adds support for the interrupt controller inside the sl28
> CPLD management controller.
> 
> Signed-off-by: Michael Walle <michael@walle.cc>
> ---
>  drivers/irqchip/Kconfig        |  3 ++
>  drivers/irqchip/Makefile       |  1 +
>  drivers/irqchip/irq-sl28cpld.c | 92 ++++++++++++++++++++++++++++++++++
>  drivers/mfd/Kconfig            |  4 +-
>  4 files changed, 98 insertions(+), 2 deletions(-)
>  create mode 100644 drivers/irqchip/irq-sl28cpld.c
> 
> diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
> index 24fe08702ef7..3fd7415c8b55 100644
> --- a/drivers/irqchip/Kconfig
> +++ b/drivers/irqchip/Kconfig
> @@ -246,6 +246,9 @@ config RENESAS_RZA1_IRQC
>  	  Enable support for the Renesas RZ/A1 Interrupt Controller, to use up
>  	  to 8 external interrupts with configurable sense select.
>  
> +config SL28CPLD_INTC
> +	bool
> +
>  config ST_IRQCHIP
>  	bool
>  	select REGMAP
> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> index eae0d78cbf22..0f4a37782609 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -105,3 +105,4 @@ obj-$(CONFIG_MADERA_IRQ)		+= irq-madera.o
>  obj-$(CONFIG_LS1X_IRQ)			+= irq-ls1x.o
>  obj-$(CONFIG_TI_SCI_INTR_IRQCHIP)	+= irq-ti-sci-intr.o
>  obj-$(CONFIG_TI_SCI_INTA_IRQCHIP)	+= irq-ti-sci-inta.o
> +obj-$(CONFIG_SL28CPLD_INTC)		+= irq-sl28cpld.o
> diff --git a/drivers/irqchip/irq-sl28cpld.c b/drivers/irqchip/irq-sl28cpld.c
> new file mode 100644
> index 000000000000..fa52ed79137b
> --- /dev/null
> +++ b/drivers/irqchip/irq-sl28cpld.c
> @@ -0,0 +1,92 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * SMARC-sAL28 Interrupt core driver.
> + *
> + * Copyright 2019 Kontron Europe GmbH
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_platform.h>
> +#include <linux/i2c.h>
> +#include <linux/regmap.h>
> +#include <linux/interrupt.h>
> +#include <linux/mfd/core.h>
> +
> +#define INTC_IE 0
> +#define INTC_IP 1
> +
> +static const struct regmap_irq sl28cpld_irqs[] = {
> +	REGMAP_IRQ_REG_LINE(0, 8),
> +	REGMAP_IRQ_REG_LINE(1, 8),
> +	REGMAP_IRQ_REG_LINE(2, 8),
> +	REGMAP_IRQ_REG_LINE(3, 8),
> +	REGMAP_IRQ_REG_LINE(4, 8),
> +	REGMAP_IRQ_REG_LINE(5, 8),
> +	REGMAP_IRQ_REG_LINE(6, 8),
> +	REGMAP_IRQ_REG_LINE(7, 8),
> +};
> +
> +struct sl28cpld_intc {
> +	struct regmap *regmap;
> +	struct regmap_irq_chip chip;
> +	struct regmap_irq_chip_data *irq_data;
> +};
> +
> +static int sl28cpld_intc_probe(struct platform_device *pdev)
> +{
> +	struct sl28cpld_intc *irqchip;
> +	struct resource *res;
> +	unsigned int irq;
> +	int ret;
> +
> +	irqchip = devm_kzalloc(&pdev->dev, sizeof(*irqchip), GFP_KERNEL);
> +	if (!irqchip)
> +		return -ENOMEM;
> +
> +	if (!pdev->dev.parent)
> +		return -ENODEV;
> +
> +	irqchip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
> +	if (!irqchip->regmap)
> +		return -ENODEV;
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0)
> +		return irq;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_REG, 0);
> +	if (!res)
> +		return -EINVAL;
> +
> +	irqchip->chip.name = "sl28cpld-intc";
> +	irqchip->chip.irqs = sl28cpld_irqs;
> +	irqchip->chip.num_irqs = ARRAY_SIZE(sl28cpld_irqs);
> +	irqchip->chip.num_regs = 1;
> +	irqchip->chip.status_base = res->start + INTC_IP;
> +	irqchip->chip.mask_base = res->start + INTC_IE;
> +	irqchip->chip.mask_invert = true,
> +	irqchip->chip.ack_base = res->start + INTC_IP;
> +
> +	ret = devm_regmap_add_irq_chip(&pdev->dev, irqchip->regmap, irq,
> +				       IRQF_SHARED | IRQF_ONESHOT, 0,
> +				       &irqchip->chip, &irqchip->irq_data);
> +	if (ret)
> +		return ret;
> +	dev_info(&pdev->dev, "registered IRQ %d\n", irq);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver sl28cpld_intc_driver = {
> +	.probe	= sl28cpld_intc_probe,
> +	.driver = {
> +		.name = "sl28cpld-intc",
> +	}
> +};
> +module_platform_driver(sl28cpld_intc_driver);
> +
> +MODULE_DESCRIPTION("sl28cpld Interrupt Controller Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index 01588c366476..4f741d640705 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -2060,12 +2060,12 @@ config SGI_MFD_IOC3
>  	  then say Y. Otherwise say N.
>  
>  config MFD_SL28CPLD
> -	tristate "Kontron sl28 core driver"
> +	bool "Kontron sl28 core driver"

This is .... unusual. Why declare it tristate only to re-declare it bool in the next patch ?
It does explain the I2C=y, but I really think it should be bool from the start if it ends up
there.

>  	depends on I2C=y
>  	depends on OF
>  	select REGMAP_I2C
>  	select REGMAP_IRQ
> -	select SL28CPLD_IRQ
> +	select SL28CPLD_INTC

What is the point of introducing SL28CPLD_IRQ in the first place ?

>  	select MFD_CORE
>  	help
>  	  This option enables support for the board management controller
> 


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

* Re: [PATCH 06/18] irqchip: add sl28cpld interrupt controller support
  2020-03-18 16:53   ` Guenter Roeck
@ 2020-03-18 17:06     ` Michael Walle
  2020-03-18 20:35       ` Guenter Roeck
  0 siblings, 1 reply; 40+ messages in thread
From: Michael Walle @ 2020-03-18 17:06 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: linux-gpio, devicetree, linux-kernel, linux-hwmon, linux-pwm,
	linux-watchdog, linux-arm-kernel, Linus Walleij,
	Bartosz Golaszewski, Rob Herring, Jean Delvare, Lee Jones,
	Thierry Reding, Uwe Kleine-König, Wim Van Sebroeck,
	Shawn Guo, Li Yang, Thomas Gleixner, Jason Cooper, Marc Zyngier,
	Guenter Roeck

Am 2020-03-18 17:53, schrieb Guenter Roeck:
> On 3/17/20 1:50 PM, Michael Walle wrote:
>> This patch adds support for the interrupt controller inside the sl28
>> CPLD management controller.
>> 
>> Signed-off-by: Michael Walle <michael@walle.cc>
>> ---
>>  drivers/irqchip/Kconfig        |  3 ++
>>  drivers/irqchip/Makefile       |  1 +
>>  drivers/irqchip/irq-sl28cpld.c | 92 
>> ++++++++++++++++++++++++++++++++++
>>  drivers/mfd/Kconfig            |  4 +-
>>  4 files changed, 98 insertions(+), 2 deletions(-)
>>  create mode 100644 drivers/irqchip/irq-sl28cpld.c
>> 
>> diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
>> index 24fe08702ef7..3fd7415c8b55 100644
>> --- a/drivers/irqchip/Kconfig
>> +++ b/drivers/irqchip/Kconfig
>> @@ -246,6 +246,9 @@ config RENESAS_RZA1_IRQC
>>  	  Enable support for the Renesas RZ/A1 Interrupt Controller, to use 
>> up
>>  	  to 8 external interrupts with configurable sense select.
>> 
>> +config SL28CPLD_INTC
>> +	bool
>> +
>>  config ST_IRQCHIP
>>  	bool
>>  	select REGMAP
>> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
>> index eae0d78cbf22..0f4a37782609 100644
>> --- a/drivers/irqchip/Makefile
>> +++ b/drivers/irqchip/Makefile
>> @@ -105,3 +105,4 @@ obj-$(CONFIG_MADERA_IRQ)		+= irq-madera.o
>>  obj-$(CONFIG_LS1X_IRQ)			+= irq-ls1x.o
>>  obj-$(CONFIG_TI_SCI_INTR_IRQCHIP)	+= irq-ti-sci-intr.o
>>  obj-$(CONFIG_TI_SCI_INTA_IRQCHIP)	+= irq-ti-sci-inta.o
>> +obj-$(CONFIG_SL28CPLD_INTC)		+= irq-sl28cpld.o
>> diff --git a/drivers/irqchip/irq-sl28cpld.c 
>> b/drivers/irqchip/irq-sl28cpld.c
>> new file mode 100644
>> index 000000000000..fa52ed79137b
>> --- /dev/null
>> +++ b/drivers/irqchip/irq-sl28cpld.c
>> @@ -0,0 +1,92 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * SMARC-sAL28 Interrupt core driver.
>> + *
>> + * Copyright 2019 Kontron Europe GmbH
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_address.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/i2c.h>
>> +#include <linux/regmap.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/mfd/core.h>
>> +
>> +#define INTC_IE 0
>> +#define INTC_IP 1
>> +
>> +static const struct regmap_irq sl28cpld_irqs[] = {
>> +	REGMAP_IRQ_REG_LINE(0, 8),
>> +	REGMAP_IRQ_REG_LINE(1, 8),
>> +	REGMAP_IRQ_REG_LINE(2, 8),
>> +	REGMAP_IRQ_REG_LINE(3, 8),
>> +	REGMAP_IRQ_REG_LINE(4, 8),
>> +	REGMAP_IRQ_REG_LINE(5, 8),
>> +	REGMAP_IRQ_REG_LINE(6, 8),
>> +	REGMAP_IRQ_REG_LINE(7, 8),
>> +};
>> +
>> +struct sl28cpld_intc {
>> +	struct regmap *regmap;
>> +	struct regmap_irq_chip chip;
>> +	struct regmap_irq_chip_data *irq_data;
>> +};
>> +
>> +static int sl28cpld_intc_probe(struct platform_device *pdev)
>> +{
>> +	struct sl28cpld_intc *irqchip;
>> +	struct resource *res;
>> +	unsigned int irq;
>> +	int ret;
>> +
>> +	irqchip = devm_kzalloc(&pdev->dev, sizeof(*irqchip), GFP_KERNEL);
>> +	if (!irqchip)
>> +		return -ENOMEM;
>> +
>> +	if (!pdev->dev.parent)
>> +		return -ENODEV;
>> +
>> +	irqchip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
>> +	if (!irqchip->regmap)
>> +		return -ENODEV;
>> +
>> +	irq = platform_get_irq(pdev, 0);
>> +	if (irq < 0)
>> +		return irq;
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_REG, 0);
>> +	if (!res)
>> +		return -EINVAL;
>> +
>> +	irqchip->chip.name = "sl28cpld-intc";
>> +	irqchip->chip.irqs = sl28cpld_irqs;
>> +	irqchip->chip.num_irqs = ARRAY_SIZE(sl28cpld_irqs);
>> +	irqchip->chip.num_regs = 1;
>> +	irqchip->chip.status_base = res->start + INTC_IP;
>> +	irqchip->chip.mask_base = res->start + INTC_IE;
>> +	irqchip->chip.mask_invert = true,
>> +	irqchip->chip.ack_base = res->start + INTC_IP;
>> +
>> +	ret = devm_regmap_add_irq_chip(&pdev->dev, irqchip->regmap, irq,
>> +				       IRQF_SHARED | IRQF_ONESHOT, 0,
>> +				       &irqchip->chip, &irqchip->irq_data);
>> +	if (ret)
>> +		return ret;
>> +	dev_info(&pdev->dev, "registered IRQ %d\n", irq);
>> +
>> +	return 0;
>> +}
>> +
>> +static struct platform_driver sl28cpld_intc_driver = {
>> +	.probe	= sl28cpld_intc_probe,
>> +	.driver = {
>> +		.name = "sl28cpld-intc",
>> +	}
>> +};
>> +module_platform_driver(sl28cpld_intc_driver);
>> +
>> +MODULE_DESCRIPTION("sl28cpld Interrupt Controller Driver");
>> +MODULE_LICENSE("GPL");
>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>> index 01588c366476..4f741d640705 100644
>> --- a/drivers/mfd/Kconfig
>> +++ b/drivers/mfd/Kconfig
>> @@ -2060,12 +2060,12 @@ config SGI_MFD_IOC3
>>  	  then say Y. Otherwise say N.
>> 
>>  config MFD_SL28CPLD
>> -	tristate "Kontron sl28 core driver"
>> +	bool "Kontron sl28 core driver"
> 
> This is .... unusual. Why declare it tristate only to re-declare it
> bool in the next patch ?

I though it was a good idea to have that gradually build up, esp. since
these patches might go through different reviewers/trees. That being 
said,
I'll change it though.

> It does explain the I2C=y, but I really think it should be bool from
> the start if it ends up
> there.

Ok.

> 
>>  	depends on I2C=y
>>  	depends on OF
>>  	select REGMAP_I2C
>>  	select REGMAP_IRQ
>> -	select SL28CPLD_IRQ
>> +	select SL28CPLD_INTC
> 
> What is the point of introducing SL28CPLD_IRQ in the first place ?

oh damn. this is a left-over which slipped through. There should just
be a SL28CPLD_INTC.

-michael

> 
>>  	select MFD_CORE
>>  	help
>>  	  This option enables support for the board management controller
>> 

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

* Re: [PATCH 06/18] irqchip: add sl28cpld interrupt controller support
  2020-03-18 17:06     ` Michael Walle
@ 2020-03-18 20:35       ` Guenter Roeck
  0 siblings, 0 replies; 40+ messages in thread
From: Guenter Roeck @ 2020-03-18 20:35 UTC (permalink / raw)
  To: Michael Walle
  Cc: linux-gpio, devicetree, linux-kernel, linux-hwmon, linux-pwm,
	linux-watchdog, linux-arm-kernel, Linus Walleij,
	Bartosz Golaszewski, Rob Herring, Jean Delvare, Lee Jones,
	Thierry Reding, Uwe Kleine-König, Wim Van Sebroeck,
	Shawn Guo, Li Yang, Thomas Gleixner, Jason Cooper, Marc Zyngier,
	Guenter Roeck

On 3/18/20 10:06 AM, Michael Walle wrote:
> Am 2020-03-18 17:53, schrieb Guenter Roeck:
>> On 3/17/20 1:50 PM, Michael Walle wrote:
>>> This patch adds support for the interrupt controller inside the sl28
>>> CPLD management controller.
>>>
>>> Signed-off-by: Michael Walle <michael@walle.cc>
>>> ---
>>>  drivers/irqchip/Kconfig        |  3 ++
>>>  drivers/irqchip/Makefile       |  1 +
>>>  drivers/irqchip/irq-sl28cpld.c | 92 ++++++++++++++++++++++++++++++++++
>>>  drivers/mfd/Kconfig            |  4 +-
>>>  4 files changed, 98 insertions(+), 2 deletions(-)
>>>  create mode 100644 drivers/irqchip/irq-sl28cpld.c
>>>
>>> diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
>>> index 24fe08702ef7..3fd7415c8b55 100644
>>> --- a/drivers/irqchip/Kconfig
>>> +++ b/drivers/irqchip/Kconfig
>>> @@ -246,6 +246,9 @@ config RENESAS_RZA1_IRQC
>>>        Enable support for the Renesas RZ/A1 Interrupt Controller, to use up
>>>        to 8 external interrupts with configurable sense select.
>>>
>>> +config SL28CPLD_INTC
>>> +    bool
>>> +
>>>  config ST_IRQCHIP
>>>      bool
>>>      select REGMAP
>>> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
>>> index eae0d78cbf22..0f4a37782609 100644
>>> --- a/drivers/irqchip/Makefile
>>> +++ b/drivers/irqchip/Makefile
>>> @@ -105,3 +105,4 @@ obj-$(CONFIG_MADERA_IRQ)        += irq-madera.o
>>>  obj-$(CONFIG_LS1X_IRQ)            += irq-ls1x.o
>>>  obj-$(CONFIG_TI_SCI_INTR_IRQCHIP)    += irq-ti-sci-intr.o
>>>  obj-$(CONFIG_TI_SCI_INTA_IRQCHIP)    += irq-ti-sci-inta.o
>>> +obj-$(CONFIG_SL28CPLD_INTC)        += irq-sl28cpld.o
>>> diff --git a/drivers/irqchip/irq-sl28cpld.c b/drivers/irqchip/irq-sl28cpld.c
>>> new file mode 100644
>>> index 000000000000..fa52ed79137b
>>> --- /dev/null
>>> +++ b/drivers/irqchip/irq-sl28cpld.c
>>> @@ -0,0 +1,92 @@
>>> +// SPDX-License-Identifier: GPL-2.0-only
>>> +/*
>>> + * SMARC-sAL28 Interrupt core driver.
>>> + *
>>> + * Copyright 2019 Kontron Europe GmbH
>>> + */
>>> +
>>> +#include <linux/kernel.h>
>>> +#include <linux/module.h>
>>> +#include <linux/of.h>
>>> +#include <linux/of_address.h>
>>> +#include <linux/of_platform.h>
>>> +#include <linux/i2c.h>
>>> +#include <linux/regmap.h>
>>> +#include <linux/interrupt.h>
>>> +#include <linux/mfd/core.h>
>>> +
>>> +#define INTC_IE 0
>>> +#define INTC_IP 1
>>> +
>>> +static const struct regmap_irq sl28cpld_irqs[] = {
>>> +    REGMAP_IRQ_REG_LINE(0, 8),
>>> +    REGMAP_IRQ_REG_LINE(1, 8),
>>> +    REGMAP_IRQ_REG_LINE(2, 8),
>>> +    REGMAP_IRQ_REG_LINE(3, 8),
>>> +    REGMAP_IRQ_REG_LINE(4, 8),
>>> +    REGMAP_IRQ_REG_LINE(5, 8),
>>> +    REGMAP_IRQ_REG_LINE(6, 8),
>>> +    REGMAP_IRQ_REG_LINE(7, 8),
>>> +};
>>> +
>>> +struct sl28cpld_intc {
>>> +    struct regmap *regmap;
>>> +    struct regmap_irq_chip chip;
>>> +    struct regmap_irq_chip_data *irq_data;
>>> +};
>>> +
>>> +static int sl28cpld_intc_probe(struct platform_device *pdev)
>>> +{
>>> +    struct sl28cpld_intc *irqchip;
>>> +    struct resource *res;
>>> +    unsigned int irq;
>>> +    int ret;
>>> +
>>> +    irqchip = devm_kzalloc(&pdev->dev, sizeof(*irqchip), GFP_KERNEL);
>>> +    if (!irqchip)
>>> +        return -ENOMEM;
>>> +
>>> +    if (!pdev->dev.parent)
>>> +        return -ENODEV;
>>> +
>>> +    irqchip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
>>> +    if (!irqchip->regmap)
>>> +        return -ENODEV;
>>> +
>>> +    irq = platform_get_irq(pdev, 0);
>>> +    if (irq < 0)
>>> +        return irq;
>>> +
>>> +    res = platform_get_resource(pdev, IORESOURCE_REG, 0);
>>> +    if (!res)
>>> +        return -EINVAL;
>>> +
>>> +    irqchip->chip.name = "sl28cpld-intc";
>>> +    irqchip->chip.irqs = sl28cpld_irqs;
>>> +    irqchip->chip.num_irqs = ARRAY_SIZE(sl28cpld_irqs);
>>> +    irqchip->chip.num_regs = 1;
>>> +    irqchip->chip.status_base = res->start + INTC_IP;
>>> +    irqchip->chip.mask_base = res->start + INTC_IE;
>>> +    irqchip->chip.mask_invert = true,
>>> +    irqchip->chip.ack_base = res->start + INTC_IP;
>>> +
>>> +    ret = devm_regmap_add_irq_chip(&pdev->dev, irqchip->regmap, irq,
>>> +                       IRQF_SHARED | IRQF_ONESHOT, 0,
>>> +                       &irqchip->chip, &irqchip->irq_data);
>>> +    if (ret)
>>> +        return ret;
>>> +    dev_info(&pdev->dev, "registered IRQ %d\n", irq);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static struct platform_driver sl28cpld_intc_driver = {
>>> +    .probe    = sl28cpld_intc_probe,
>>> +    .driver = {
>>> +        .name = "sl28cpld-intc",
>>> +    }
>>> +};
>>> +module_platform_driver(sl28cpld_intc_driver);
>>> +
>>> +MODULE_DESCRIPTION("sl28cpld Interrupt Controller Driver");
>>> +MODULE_LICENSE("GPL");
>>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>>> index 01588c366476..4f741d640705 100644
>>> --- a/drivers/mfd/Kconfig
>>> +++ b/drivers/mfd/Kconfig
>>> @@ -2060,12 +2060,12 @@ config SGI_MFD_IOC3
>>>        then say Y. Otherwise say N.
>>>
>>>  config MFD_SL28CPLD
>>> -    tristate "Kontron sl28 core driver"
>>> +    bool "Kontron sl28 core driver"
>>
>> This is .... unusual. Why declare it tristate only to re-declare it
>> bool in the next patch ?
> 
> I though it was a good idea to have that gradually build up, esp. since
> these patches might go through different reviewers/trees. That being said,
> I'll change it though.
> 

Gradually build up is fine, but that doesn't mean to do it one way first and
change it later. This only results in feedback like mine - I2C=y just
didn't make sense in the previous patch. Please keep in mind that doing
things one way first and then changing them later only wastes people's time.
It would be much better to mention in the previous patch that the symbol is
declared bool because a subsequent patch introduces an interrupt controller
driver which needs to be built-in.

Thanks,
Guenter

>> It does explain the I2C=y, but I really think it should be bool from
>> the start if it ends up
>> there.
> 
> Ok.
> 
>>
>>>      depends on I2C=y
>>>      depends on OF
>>>      select REGMAP_I2C
>>>      select REGMAP_IRQ
>>> -    select SL28CPLD_IRQ
>>> +    select SL28CPLD_INTC
>>
>> What is the point of introducing SL28CPLD_IRQ in the first place ?
> 
> oh damn. this is a left-over which slipped through. There should just
> be a SL28CPLD_INTC.
> 
> -michael
> 
>>
>>>      select MFD_CORE
>>>      help
>>>        This option enables support for the board management controller
>>>


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

* Re: [PATCH 12/18] gpio: add support for the sl28cpld GPIO controller
  2020-03-18 12:45     ` Michael Walle
@ 2020-03-25 11:50       ` Bartosz Golaszewski
  2020-03-26 20:05         ` Michael Walle
  0 siblings, 1 reply; 40+ messages in thread
From: Bartosz Golaszewski @ 2020-03-25 11:50 UTC (permalink / raw)
  To: Michael Walle
  Cc: linux-gpio, linux-devicetree, LKML, linux-hwmon, linux-pwm,
	LINUXWATCHDOG, arm-soc, Linus Walleij, Rob Herring, Jean Delvare,
	Guenter Roeck, Lee Jones, Thierry Reding, Uwe Kleine-König,
	Wim Van Sebroeck, Shawn Guo, Li Yang, Thomas Gleixner,
	Jason Cooper, Marc Zyngier

śr., 18 mar 2020 o 13:45 Michael Walle <michael@walle.cc> napisał(a):
>
> Hi Bartosz,
>
> Am 2020-03-18 10:14, schrieb Bartosz Golaszewski:
> > wt., 17 mar 2020 o 21:50 Michael Walle <michael@walle.cc> napisał(a):
> >>
> >> This adds support for the GPIO controller of the sl28 board management
> >> controller. This driver is part of a multi-function device.
> >>
> >> Signed-off-by: Michael Walle <michael@walle.cc>
> >
> > Hi Michael,
> >
> > thanks for the driver. Please take a look at some comments below.
>
> well, thank you for the very fast review!
>
> >> ---
> >>  drivers/gpio/Kconfig         |  11 ++
> >>  drivers/gpio/Makefile        |   1 +
> >>  drivers/gpio/gpio-sl28cpld.c | 332
> >> +++++++++++++++++++++++++++++++++++
> >>  3 files changed, 344 insertions(+)
> >>  create mode 100644 drivers/gpio/gpio-sl28cpld.c
> >>
> >> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> >> index 3cbf8882a0dd..516e47017ef5 100644
> >> --- a/drivers/gpio/Kconfig
> >> +++ b/drivers/gpio/Kconfig
> >> @@ -1211,6 +1211,17 @@ config GPIO_RC5T583
> >>           This driver provides the support for driving/reading the
> >> gpio pins
> >>           of RC5T583 device through standard gpio library.
> >>
> >> +config GPIO_SL28CPLD
> >> +       tristate "Kontron sl28 GPIO"
> >> +       depends on MFD_SL28CPLD
> >> +       depends on OF_GPIO
> >> +       select GPIOLIB_IRQCHIP
> >
> > Please see below - I think both are not needed.
> >
> >> +       help
> >> +         This enables support for the GPIOs found on the Kontron sl28
> >> CPLD.
> >> +
> >> +         This driver can also be built as a module. If so, the module
> >> will be
> >> +         called gpio-sl28cpld.
> >> +
> >>  config GPIO_STMPE
> >>         bool "STMPE GPIOs"
> >>         depends on MFD_STMPE
> >> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> >> index 0b571264ddbc..0ca2d52c78e8 100644
> >> --- a/drivers/gpio/Makefile
> >> +++ b/drivers/gpio/Makefile
> >> @@ -127,6 +127,7 @@ obj-$(CONFIG_GPIO_SCH311X)          +=
> >> gpio-sch311x.o
> >>  obj-$(CONFIG_GPIO_SCH)                 += gpio-sch.o
> >>  obj-$(CONFIG_GPIO_SIFIVE)              += gpio-sifive.o
> >>  obj-$(CONFIG_GPIO_SIOX)                        += gpio-siox.o
> >> +obj-$(CONFIG_GPIO_SL28CPLD)            += gpio-sl28cpld.o
> >>  obj-$(CONFIG_GPIO_SODAVILLE)           += gpio-sodaville.o
> >>  obj-$(CONFIG_GPIO_SPEAR_SPICS)         += gpio-spear-spics.o
> >>  obj-$(CONFIG_GPIO_SPRD)                        += gpio-sprd.o
> >> diff --git a/drivers/gpio/gpio-sl28cpld.c
> >> b/drivers/gpio/gpio-sl28cpld.c
> >> new file mode 100644
> >> index 000000000000..94f82013882f
> >> --- /dev/null
> >> +++ b/drivers/gpio/gpio-sl28cpld.c
> >> @@ -0,0 +1,332 @@
> >> +// SPDX-License-Identifier: GPL-2.0-only
> >> +/*
> >> + * SMARC-sAL28 GPIO driver.
> >> + *
> >> + * Copyright 2019 Kontron Europe GmbH
> >> + */
> >> +
> >> +#include <linux/kernel.h>
> >> +#include <linux/module.h>
> >> +#include <linux/of.h>
> >> +#include <linux/of_device.h>
> >> +#include <linux/of_address.h>
> >> +#include <linux/interrupt.h>
> >> +#include <linux/regmap.h>
> >> +#include <linux/platform_device.h>
> >> +#include <linux/gpio/driver.h>
> >> +
> >> +#define GPIO_REG_DIR   0
> >> +#define GPIO_REG_OUT   1
> >> +#define GPIO_REG_IN    2
> >> +#define GPIO_REG_IE    3
> >> +#define GPIO_REG_IP    4
> >
> > These values would be more clear if they were defined as hex.
> >
> >> +
> >> +#define GPI_REG_IN     0
> >> +
> >> +#define GPO_REG_OUT    0
> >
> > Please also use a common prefix even for defines.
>
> ok
>
> >
> >> +
> >> +enum sl28cpld_gpio_type {
> >> +       sl28cpld_gpio,
> >> +       sl28cpld_gpi,
> >> +       sl28cpld_gpo,
> >> +};
> >
> > Enum values should be all upper-case.
>
> ok
>
> >> +
> >> +struct sl28cpld_gpio {
> >> +       struct gpio_chip gpio_chip;
> >> +       struct irq_chip irq_chip;
> >> +       struct regmap *regmap;
> >> +       u32 offset;
> >> +       struct mutex lock;
> >> +       u8 ie;
> >> +};
> >> +
> >> +static void sl28cpld_gpio_set_reg(struct gpio_chip *chip, unsigned
> >> int reg,
> >> +                                 unsigned int offset, int value)
> >> +{
> >> +       struct sl28cpld_gpio *gpio = gpiochip_get_data(chip);
> >> +       unsigned int mask = 1 << offset;
> >> +       unsigned int val = value << offset;
> >> +
> >> +       regmap_update_bits(gpio->regmap, gpio->offset + reg, mask,
> >> val);
> >> +}
> >> +
> >> +static void sl28cpld_gpio_set(struct gpio_chip *chip, unsigned int
> >> offset,
> >> +                             int value)
> >> +{
> >> +       sl28cpld_gpio_set_reg(chip, GPIO_REG_OUT, offset, value);
> >> +}
> >> +
> >> +static void sl28cpld_gpo_set(struct gpio_chip *chip, unsigned int
> >> offset,
> >> +                            int value)
> >> +{
> >> +       sl28cpld_gpio_set_reg(chip, GPO_REG_OUT, offset, value);
> >> +}
> >> +
> >> +static int sl28cpld_gpio_get_reg(struct gpio_chip *chip, unsigned int
> >> reg,
> >> +                                unsigned int offset)
> >> +{
> >> +       struct sl28cpld_gpio *gpio = gpiochip_get_data(chip);
> >> +       unsigned int mask = 1 << offset;
> >> +       unsigned int val;
> >> +       int ret;
> >> +
> >> +       ret = regmap_read(gpio->regmap, gpio->offset + reg, &val);
> >> +       if (ret)
> >> +               return ret;
> >> +
> >> +       return (val & mask) ? 1 : 0;
> >> +}
> >> +
> >> +static int sl28cpld_gpio_get(struct gpio_chip *chip, unsigned int
> >> offset)
> >> +{
> >> +       return sl28cpld_gpio_get_reg(chip, GPIO_REG_IN, offset);
> >> +}
> >> +
> >> +static int sl28cpld_gpi_get(struct gpio_chip *chip, unsigned int
> >> offset)
> >> +{
> >> +       return sl28cpld_gpio_get_reg(chip, GPI_REG_IN, offset);
> >> +}
> >> +
> >> +static int sl28cpld_gpio_get_direction(struct gpio_chip *chip,
> >> +                                      unsigned int offset)
> >> +{
> >> +       struct sl28cpld_gpio *gpio = gpiochip_get_data(chip);
> >> +       unsigned int reg;
> >> +       int ret;
> >> +
> >> +       ret = regmap_read(gpio->regmap, gpio->offset + GPIO_REG_DIR,
> >> &reg);
> >> +       if (ret)
> >> +               return ret;
> >> +
> >> +       if (reg & (1 << offset))
> >> +               return GPIO_LINE_DIRECTION_OUT;
> >> +       else
> >> +               return GPIO_LINE_DIRECTION_IN;
> >> +}
> >> +
> >> +static int sl28cpld_gpio_set_direction(struct gpio_chip *chip,
> >> +                                      unsigned int offset,
> >> +                                      bool output)
> >> +{
> >> +       struct sl28cpld_gpio *gpio = gpiochip_get_data(chip);
> >> +       unsigned int mask = 1 << offset;
> >> +       unsigned int val = (output) ? mask : 0;
> >> +
> >> +       return regmap_update_bits(gpio->regmap, gpio->offset +
> >> GPIO_REG_DIR,
> >> +                                 mask, val);
> >> +
> >
> > Stray newline.
> ok
>
> >
> >> +}
> >> +
> >> +static int sl28cpld_gpio_direction_input(struct gpio_chip *chip,
> >> +                                        unsigned int offset)
> >> +{
> >> +       return sl28cpld_gpio_set_direction(chip, offset, false);
> >> +}
> >> +
> >> +static int sl28cpld_gpio_direction_output(struct gpio_chip *chip,
> >> +                                         unsigned int offset, int
> >> value)
> >> +{
> >> +       sl28cpld_gpio_set_reg(chip, GPIO_REG_OUT, offset, value);
> >> +       return sl28cpld_gpio_set_direction(chip, offset, true);
> >> +}
> >> +
> >> +static void sl28cpld_gpio_irq_lock(struct irq_data *data)
> >> +{
> >> +       struct sl28cpld_gpio *gpio =
> >> +               gpiochip_get_data(irq_data_get_irq_chip_data(data));
> >> +
> >> +       mutex_lock(&gpio->lock);
> >
> > How does that actually lock anything?
>
> TBH, I took that from gpio-pcf857x.c. But that
>   (1) don't uses regmap
>   (2) also uses that lock on other places.
>
> I'll dig deeper into that and try to understand why there is a lock at
> all and why this callback is actually called _irq_lock() because that
> made me wonder.
>
> > Regmap uses a different lock and
> > if you want to make sure nobody modifies the GPIO registers than you'd
> > need to use the same lock. Also: this looks a lot like a task for
> > regmap_irqchip - maybe you could use it here or in the core mfd
> > module?
>
> regmap_irqchip will register the interrupt controller on the device
> which owns the regmap, ie. the parent. So (1) the phandle would need to
> point to the parent device instead of the GPIO subnode and (2) I'm
> already using the regmap_irqchip for the interrupt controller. I don't
> know if you can actually have that multiple times.
>
> there was a discussion which might apply partly to (1):
>   https://lore.kernel.org/patchwork/patch/802608/
>

In that case maybe you should use the disable_locking option in
regmap_config and provide your own callbacks that you can use in the
irqchip code too?

> >
> >> +}
> >> +
> >> +static void sl28cpld_gpio_irq_sync_unlock(struct irq_data *data)
> >> +{
> >> +       struct sl28cpld_gpio *gpio =
> >> +               gpiochip_get_data(irq_data_get_irq_chip_data(data));
> >> +
> >> +       regmap_write(gpio->regmap, gpio->offset + GPIO_REG_IE,
> >> gpio->ie);
> >> +       mutex_unlock(&gpio->lock);
> >> +}
> >> +
> >> +static void sl28cpld_gpio_irq_disable(struct irq_data *data)
> >> +{
> >> +       struct sl28cpld_gpio *gpio =
> >> +               gpiochip_get_data(irq_data_get_irq_chip_data(data));
> >> +
> >> +       if (data->hwirq >= 8)
> >> +               return;
> >> +
> >> +       gpio->ie &= ~(1 << data->hwirq);
> >> +}
> >> +
> >> +static void sl28cpld_gpio_irq_enable(struct irq_data *data)
> >> +{
> >> +       struct sl28cpld_gpio *gpio =
> >> +               gpiochip_get_data(irq_data_get_irq_chip_data(data));
> >> +
> >> +       if (data->hwirq >= 8)
> >> +               return;
> >> +
> >> +       gpio->ie |= (1 << data->hwirq);
> >> +}
> >> +
> >> +static int sl28cpld_gpio_irq_set_type(struct irq_data *data, unsigned
> >> int type)
> >> +{
> >> +       /* only edge triggered interrupts on both edges are supported
> >> */
> >> +       return (type == IRQ_TYPE_EDGE_BOTH) ? 0 : -EINVAL;
> >> +}
> >> +
> >> +static irqreturn_t sl28cpld_gpio_irq_thread(int irq, void *data)
> >> +{
> >> +       struct sl28cpld_gpio *gpio = data;
> >> +       unsigned int ip;
> >> +       unsigned int virq;
> >> +       int pin;
> >> +       int ret;
> >> +
> >> +       ret = regmap_read(gpio->regmap, gpio->offset + GPIO_REG_IP,
> >> &ip);
> >> +       if (ret)
> >> +               return IRQ_NONE;
> >> +
> >> +       /* mask other pending interrupts which are not enabled */
> >> +       ip &= gpio->ie;
> >> +
> >> +       /* ack the interrupts */
> >> +       regmap_write(gpio->regmap, gpio->offset + GPIO_REG_IP, ip);
> >> +
> >> +       /* and handle them */
> >> +       while (ip) {
> >> +               pin = __ffs(ip);
> >> +               ip &= ~BIT(pin);
> >> +
> >> +               virq = irq_find_mapping(gpio->gpio_chip.irq.domain,
> >> pin);
> >> +               if (virq)
> >> +                       handle_nested_irq(virq);
> >> +       }
> >> +
> >> +       return IRQ_HANDLED;
> >> +}
> >
> > This definitely looks like parts of regmap_irqchip reimplemented.
> > Please check if you could reuse it - it would save a lot of code.
>
> See above. I'd be happy to reuse the code though.
>
> >
> >> +
> >> +static int sl28_cpld_gpio_irq_init(struct platform_device *pdev, int
> >> irq)
> >> +{
> >> +       struct sl28cpld_gpio *gpio = platform_get_drvdata(pdev);
> >> +       struct irq_chip *irq_chip = &gpio->irq_chip;
> >> +       int ret;
> >> +
> >> +       irq_chip->name = "sl28cpld-gpio-irq",
> >> +       irq_chip->irq_bus_lock = sl28cpld_gpio_irq_lock,
> >> +       irq_chip->irq_bus_sync_unlock = sl28cpld_gpio_irq_sync_unlock,
> >> +       irq_chip->irq_disable = sl28cpld_gpio_irq_disable,
> >> +       irq_chip->irq_enable = sl28cpld_gpio_irq_enable,
> >> +       irq_chip->irq_set_type = sl28cpld_gpio_irq_set_type,
> >> +       irq_chip->flags = IRQCHIP_SKIP_SET_WAKE,
> >> +
> >> +       ret = gpiochip_irqchip_add_nested(&gpio->gpio_chip, irq_chip,
> >> 0,
> >> +                                         handle_simple_irq,
> >> IRQ_TYPE_NONE);
> >> +       if (ret)
> >> +               return ret;
> >> +
> >> +       ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
> >> +                                       sl28cpld_gpio_irq_thread,
> >> +                                       IRQF_SHARED | IRQF_ONESHOT,
> >> +                                       pdev->name, gpio);
> >> +       if (ret)
> >> +               return ret;
> >> +
> >> +       gpiochip_set_nested_irqchip(&gpio->gpio_chip, irq_chip, irq);
> >> +
> >> +       return 0;
> >> +}
> >> +
> >> +static int sl28cpld_gpio_probe(struct platform_device *pdev)
> >> +{
> >> +       enum sl28cpld_gpio_type type =
> >> +               platform_get_device_id(pdev)->driver_data;
> >> +       struct device_node *np = pdev->dev.of_node;
> >> +       struct sl28cpld_gpio *gpio;
> >> +       struct gpio_chip *chip;
> >> +       struct resource *res;
> >> +       bool irq_support = false;
> >> +       int ret;
> >> +       int irq;
> >> +
> >> +       gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
> >> +       if (!gpio)
> >> +               return -ENOMEM;
> >> +
> >> +       if (!pdev->dev.parent)
> >> +               return -ENODEV;
> >
> > Why not check this before allocating any memory?
>
> I'll change that, you're not the first one which notices that. My reason
> was to have the check together with the dev_get_regmap() which uses the
> parent, expecting that the error case only happen exceptionally.
>
> >
> >> +
> >> +       gpio->regmap = dev_get_regmap(pdev->dev.parent, NULL);
> >> +       if (!gpio->regmap)
> >> +               return -ENODEV;
> >> +
> >> +       res = platform_get_resource(pdev, IORESOURCE_REG, 0);
> >> +       if (!res)
> >> +               return -EINVAL;
> >> +       gpio->offset = res->start;
> >> +
> >
> > This isn't how IO resources are used. What are you trying to achieve
> > here?
>
> Mh are you sure? The blueprint for this were the regulators in
> drivers/regulators/, eg the wm831x-ldo.c. IORESOURCE_REG isn't used
> that often. But here is what I want to achieve (for which I haven't
> found any existing drivers for now):
>   (1) the individual blocks of the overall sl28cpld may be used
>       multiple times, eg. this driver only has the offset to a
>       base address. So if there are two blocks, this mfd core
>       driver will register two devices for this driver with
>       different base offsets, which are passed by IORESOURCE_REG
>   (2) I wanted to avoid having a private mfd include with some
>       kind of "proprietary" method how to get that offset
>   (3) the mfd core driver is the one knowing the offset, thus it
>       is possible to have different flavours of the sl28cpld
>

Ok, now I see it's documented in the bindings. Thanks for the explanation.

>
> >
> >> +       /* initialize struct gpio_chip */
> >> +       mutex_init(&gpio->lock);
> >> +       chip = &gpio->gpio_chip;
> >> +       chip->parent = &pdev->dev;
> >> +       chip->label = dev_name(&pdev->dev);
> >> +       chip->owner = THIS_MODULE;
> >> +       chip->can_sleep = true;
> >> +       chip->base = -1;
> >> +       chip->ngpio = 8;
> >> +
> >> +       switch (type) {
> >> +       case sl28cpld_gpio:
> >> +               chip->get_direction = sl28cpld_gpio_get_direction;
> >> +               chip->direction_input = sl28cpld_gpio_direction_input;
> >> +               chip->direction_output =
> >> sl28cpld_gpio_direction_output;
> >> +               chip->get = sl28cpld_gpio_get;
> >> +               chip->set = sl28cpld_gpio_set;
> >> +               irq_support = true;
> >> +               break;
> >> +       case sl28cpld_gpo:
> >> +               chip->set = sl28cpld_gpo_set;
> >> +               chip->get = sl28cpld_gpi_get;
> >> +               break;
> >> +       case sl28cpld_gpi:
> >> +               chip->get = sl28cpld_gpi_get;
> >> +               break;
> >> +       }
> >> +
> >> +       ret = devm_gpiochip_add_data(&pdev->dev, chip, gpio);
> >> +       if (ret < 0)
> >> +               return ret;
> >> +
> >> +       platform_set_drvdata(pdev, gpio);
> >> +
> >> +       if (irq_support && of_property_read_bool(np,
> >> "interrupt-controller")) {
> >
> > You're depending on OF_GPIO for this one function. Please switch to
> > device_property_read_bool() instead.
>
> ok
>
>
> >
> >> +               irq = platform_get_irq(pdev, 0);
> >> +               if (irq < 0)
> >> +                       return ret;
> >> +
> >> +               ret = sl28_cpld_gpio_irq_init(pdev, irq);
> >> +               if (ret)
> >> +                       return ret;
> >> +       }
> >> +
> >> +       return 0;
> >> +}
> >> +
> >> +static const struct platform_device_id sl28cpld_gpio_id_table[] = {
> >> +       {"sl28cpld-gpio", sl28cpld_gpio},
> >> +       {"sl28cpld-gpi", sl28cpld_gpi},
> >> +       {"sl28cpld-gpo", sl28cpld_gpo},
> >
> > Could you explain this a bit more? Is this the same component with
> > input/output-only lines or three different components?
>
> These are actually three different components. Ie. you could have a
> flavour where you have one GPIO (sl28cpld-gpio) and two output-only
> ones (sl28cpld-gpo). Is that what you wanted to know?
>

Yes, thanks. This could use some documentation in the bindings though.

Bartosz

> >
> >> +};
> >> +MODULE_DEVICE_TABLE(platform, sl28cpld_gpio_id_table);
> >> +
> >> +static struct platform_driver sl28cpld_gpio_driver = {
> >> +       .probe = sl28cpld_gpio_probe,
> >> +       .id_table = sl28cpld_gpio_id_table,
> >> +       .driver = {
> >> +               .name = "sl28cpld-gpio",
> >> +       },
> >> +};
> >> +module_platform_driver(sl28cpld_gpio_driver);
> >> +
> >> +MODULE_DESCRIPTION("sl28cpld GPIO Driver");
> >> +MODULE_LICENSE("GPL");
> >
> > I think you could use a MODULE_ALIAS() here if you want this module to
> > be loaded automatically by udev.
>
> ok, I'll look into that.
>
> thanks,
> -michael
>
> >
> >> --
> >> 2.20.1
> >>
> >
> > Best regards,
> > Bartosz Golaszewski

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

* Re: [PATCH 12/18] gpio: add support for the sl28cpld GPIO controller
  2020-03-25 11:50       ` Bartosz Golaszewski
@ 2020-03-26 20:05         ` Michael Walle
  2020-03-27 10:20           ` Linus Walleij
  0 siblings, 1 reply; 40+ messages in thread
From: Michael Walle @ 2020-03-26 20:05 UTC (permalink / raw)
  To: Bartosz Golaszewski
  Cc: linux-gpio, linux-devicetree, LKML, linux-hwmon, linux-pwm,
	LINUXWATCHDOG, arm-soc, Linus Walleij, Rob Herring, Jean Delvare,
	Guenter Roeck, Lee Jones, Thierry Reding, Uwe Kleine-König,
	Wim Van Sebroeck, Shawn Guo, Li Yang, Thomas Gleixner,
	Jason Cooper, Marc Zyngier

Hi Bartosz,

Am 2020-03-25 12:50, schrieb Bartosz Golaszewski:
> śr., 18 mar 2020 o 13:45 Michael Walle <michael@walle.cc> napisał(a):
>> 
>> Hi Bartosz,
>> 
>> Am 2020-03-18 10:14, schrieb Bartosz Golaszewski:
>> > wt., 17 mar 2020 o 21:50 Michael Walle <michael@walle.cc> napisał(a):
>> >>
>> >> This adds support for the GPIO controller of the sl28 board management
>> >> controller. This driver is part of a multi-function device.
>> >>
>> >> Signed-off-by: Michael Walle <michael@walle.cc>
>> >
>> > Hi Michael,
>> >
>> > thanks for the driver. Please take a look at some comments below.
>> 
>> well, thank you for the very fast review!
>> 
>> >> ---
>> >>  drivers/gpio/Kconfig         |  11 ++
>> >>  drivers/gpio/Makefile        |   1 +
>> >>  drivers/gpio/gpio-sl28cpld.c | 332
>> >> +++++++++++++++++++++++++++++++++++
>> >>  3 files changed, 344 insertions(+)
>> >>  create mode 100644 drivers/gpio/gpio-sl28cpld.c
>> >>
>> >> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
>> >> index 3cbf8882a0dd..516e47017ef5 100644
>> >> --- a/drivers/gpio/Kconfig
>> >> +++ b/drivers/gpio/Kconfig
>> >> @@ -1211,6 +1211,17 @@ config GPIO_RC5T583
>> >>           This driver provides the support for driving/reading the
>> >> gpio pins
>> >>           of RC5T583 device through standard gpio library.
>> >>
>> >> +config GPIO_SL28CPLD
>> >> +       tristate "Kontron sl28 GPIO"
>> >> +       depends on MFD_SL28CPLD
>> >> +       depends on OF_GPIO
>> >> +       select GPIOLIB_IRQCHIP
>> >
>> > Please see below - I think both are not needed.
>> >
>> >> +       help
>> >> +         This enables support for the GPIOs found on the Kontron sl28
>> >> CPLD.
>> >> +
>> >> +         This driver can also be built as a module. If so, the module
>> >> will be
>> >> +         called gpio-sl28cpld.
>> >> +
>> >>  config GPIO_STMPE
>> >>         bool "STMPE GPIOs"
>> >>         depends on MFD_STMPE
>> >> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
>> >> index 0b571264ddbc..0ca2d52c78e8 100644
>> >> --- a/drivers/gpio/Makefile
>> >> +++ b/drivers/gpio/Makefile
>> >> @@ -127,6 +127,7 @@ obj-$(CONFIG_GPIO_SCH311X)          +=
>> >> gpio-sch311x.o
>> >>  obj-$(CONFIG_GPIO_SCH)                 += gpio-sch.o
>> >>  obj-$(CONFIG_GPIO_SIFIVE)              += gpio-sifive.o
>> >>  obj-$(CONFIG_GPIO_SIOX)                        += gpio-siox.o
>> >> +obj-$(CONFIG_GPIO_SL28CPLD)            += gpio-sl28cpld.o
>> >>  obj-$(CONFIG_GPIO_SODAVILLE)           += gpio-sodaville.o
>> >>  obj-$(CONFIG_GPIO_SPEAR_SPICS)         += gpio-spear-spics.o
>> >>  obj-$(CONFIG_GPIO_SPRD)                        += gpio-sprd.o
>> >> diff --git a/drivers/gpio/gpio-sl28cpld.c
>> >> b/drivers/gpio/gpio-sl28cpld.c
>> >> new file mode 100644
>> >> index 000000000000..94f82013882f
>> >> --- /dev/null
>> >> +++ b/drivers/gpio/gpio-sl28cpld.c
>> >> @@ -0,0 +1,332 @@
>> >> +// SPDX-License-Identifier: GPL-2.0-only
>> >> +/*
>> >> + * SMARC-sAL28 GPIO driver.
>> >> + *
>> >> + * Copyright 2019 Kontron Europe GmbH
>> >> + */
>> >> +
>> >> +#include <linux/kernel.h>
>> >> +#include <linux/module.h>
>> >> +#include <linux/of.h>
>> >> +#include <linux/of_device.h>
>> >> +#include <linux/of_address.h>
>> >> +#include <linux/interrupt.h>
>> >> +#include <linux/regmap.h>
>> >> +#include <linux/platform_device.h>
>> >> +#include <linux/gpio/driver.h>
>> >> +
>> >> +#define GPIO_REG_DIR   0
>> >> +#define GPIO_REG_OUT   1
>> >> +#define GPIO_REG_IN    2
>> >> +#define GPIO_REG_IE    3
>> >> +#define GPIO_REG_IP    4
>> >
>> > These values would be more clear if they were defined as hex.
>> >
>> >> +
>> >> +#define GPI_REG_IN     0
>> >> +
>> >> +#define GPO_REG_OUT    0
>> >
>> > Please also use a common prefix even for defines.
>> 
>> ok
>> 
>> >
>> >> +
>> >> +enum sl28cpld_gpio_type {
>> >> +       sl28cpld_gpio,
>> >> +       sl28cpld_gpi,
>> >> +       sl28cpld_gpo,
>> >> +};
>> >
>> > Enum values should be all upper-case.
>> 
>> ok
>> 
>> >> +
>> >> +struct sl28cpld_gpio {
>> >> +       struct gpio_chip gpio_chip;
>> >> +       struct irq_chip irq_chip;
>> >> +       struct regmap *regmap;
>> >> +       u32 offset;
>> >> +       struct mutex lock;
>> >> +       u8 ie;
>> >> +};
>> >> +
>> >> +static void sl28cpld_gpio_set_reg(struct gpio_chip *chip, unsigned
>> >> int reg,
>> >> +                                 unsigned int offset, int value)
>> >> +{
>> >> +       struct sl28cpld_gpio *gpio = gpiochip_get_data(chip);
>> >> +       unsigned int mask = 1 << offset;
>> >> +       unsigned int val = value << offset;
>> >> +
>> >> +       regmap_update_bits(gpio->regmap, gpio->offset + reg, mask,
>> >> val);
>> >> +}
>> >> +
>> >> +static void sl28cpld_gpio_set(struct gpio_chip *chip, unsigned int
>> >> offset,
>> >> +                             int value)
>> >> +{
>> >> +       sl28cpld_gpio_set_reg(chip, GPIO_REG_OUT, offset, value);
>> >> +}
>> >> +
>> >> +static void sl28cpld_gpo_set(struct gpio_chip *chip, unsigned int
>> >> offset,
>> >> +                            int value)
>> >> +{
>> >> +       sl28cpld_gpio_set_reg(chip, GPO_REG_OUT, offset, value);
>> >> +}
>> >> +
>> >> +static int sl28cpld_gpio_get_reg(struct gpio_chip *chip, unsigned int
>> >> reg,
>> >> +                                unsigned int offset)
>> >> +{
>> >> +       struct sl28cpld_gpio *gpio = gpiochip_get_data(chip);
>> >> +       unsigned int mask = 1 << offset;
>> >> +       unsigned int val;
>> >> +       int ret;
>> >> +
>> >> +       ret = regmap_read(gpio->regmap, gpio->offset + reg, &val);
>> >> +       if (ret)
>> >> +               return ret;
>> >> +
>> >> +       return (val & mask) ? 1 : 0;
>> >> +}
>> >> +
>> >> +static int sl28cpld_gpio_get(struct gpio_chip *chip, unsigned int
>> >> offset)
>> >> +{
>> >> +       return sl28cpld_gpio_get_reg(chip, GPIO_REG_IN, offset);
>> >> +}
>> >> +
>> >> +static int sl28cpld_gpi_get(struct gpio_chip *chip, unsigned int
>> >> offset)
>> >> +{
>> >> +       return sl28cpld_gpio_get_reg(chip, GPI_REG_IN, offset);
>> >> +}
>> >> +
>> >> +static int sl28cpld_gpio_get_direction(struct gpio_chip *chip,
>> >> +                                      unsigned int offset)
>> >> +{
>> >> +       struct sl28cpld_gpio *gpio = gpiochip_get_data(chip);
>> >> +       unsigned int reg;
>> >> +       int ret;
>> >> +
>> >> +       ret = regmap_read(gpio->regmap, gpio->offset + GPIO_REG_DIR,
>> >> &reg);
>> >> +       if (ret)
>> >> +               return ret;
>> >> +
>> >> +       if (reg & (1 << offset))
>> >> +               return GPIO_LINE_DIRECTION_OUT;
>> >> +       else
>> >> +               return GPIO_LINE_DIRECTION_IN;
>> >> +}
>> >> +
>> >> +static int sl28cpld_gpio_set_direction(struct gpio_chip *chip,
>> >> +                                      unsigned int offset,
>> >> +                                      bool output)
>> >> +{
>> >> +       struct sl28cpld_gpio *gpio = gpiochip_get_data(chip);
>> >> +       unsigned int mask = 1 << offset;
>> >> +       unsigned int val = (output) ? mask : 0;
>> >> +
>> >> +       return regmap_update_bits(gpio->regmap, gpio->offset +
>> >> GPIO_REG_DIR,
>> >> +                                 mask, val);
>> >> +
>> >
>> > Stray newline.
>> ok
>> 
>> >
>> >> +}
>> >> +
>> >> +static int sl28cpld_gpio_direction_input(struct gpio_chip *chip,
>> >> +                                        unsigned int offset)
>> >> +{
>> >> +       return sl28cpld_gpio_set_direction(chip, offset, false);
>> >> +}
>> >> +
>> >> +static int sl28cpld_gpio_direction_output(struct gpio_chip *chip,
>> >> +                                         unsigned int offset, int
>> >> value)
>> >> +{
>> >> +       sl28cpld_gpio_set_reg(chip, GPIO_REG_OUT, offset, value);
>> >> +       return sl28cpld_gpio_set_direction(chip, offset, true);
>> >> +}
>> >> +
>> >> +static void sl28cpld_gpio_irq_lock(struct irq_data *data)
>> >> +{
>> >> +       struct sl28cpld_gpio *gpio =
>> >> +               gpiochip_get_data(irq_data_get_irq_chip_data(data));
>> >> +
>> >> +       mutex_lock(&gpio->lock);
>> >
>> > How does that actually lock anything?
>> 
>> TBH, I took that from gpio-pcf857x.c. But that
>>   (1) don't uses regmap
>>   (2) also uses that lock on other places.
>> 
>> I'll dig deeper into that and try to understand why there is a lock at
>> all and why this callback is actually called _irq_lock() because that
>> made me wonder.
>> 
>> > Regmap uses a different lock and
>> > if you want to make sure nobody modifies the GPIO registers than you'd
>> > need to use the same lock. Also: this looks a lot like a task for
>> > regmap_irqchip - maybe you could use it here or in the core mfd
>> > module?
>> 
>> regmap_irqchip will register the interrupt controller on the device
>> which owns the regmap, ie. the parent. So (1) the phandle would need 
>> to
>> point to the parent device instead of the GPIO subnode and (2) I'm
>> already using the regmap_irqchip for the interrupt controller. I don't
>> know if you can actually have that multiple times.
>> 
>> there was a discussion which might apply partly to (1):
>>   https://lore.kernel.org/patchwork/patch/802608/
>> 
> 
> In that case maybe you should use the disable_locking option in
> regmap_config and provide your own callbacks that you can use in the
> irqchip code too?

But how would that solve problem (1). And keep in mind, that the
reqmap_irqchip is actually used for the interrupt controller, which
is not this gpio controller.

Ie. the interrupt controller of the sl28cpld uses the regmap_irqchip
and all interrupt phandles pointing to the interrupt controller will
reference the toplevel node. Any phandles pointing to the gpio
controller will reference the GPIO subnode.

> 
>> >
>> >> +}
>> >> +
>> >> +static void sl28cpld_gpio_irq_sync_unlock(struct irq_data *data)
>> >> +{
>> >> +       struct sl28cpld_gpio *gpio =
>> >> +               gpiochip_get_data(irq_data_get_irq_chip_data(data));
>> >> +
>> >> +       regmap_write(gpio->regmap, gpio->offset + GPIO_REG_IE,
>> >> gpio->ie);
>> >> +       mutex_unlock(&gpio->lock);
>> >> +}
>> >> +
>> >> +static void sl28cpld_gpio_irq_disable(struct irq_data *data)
>> >> +{
>> >> +       struct sl28cpld_gpio *gpio =
>> >> +               gpiochip_get_data(irq_data_get_irq_chip_data(data));
>> >> +
>> >> +       if (data->hwirq >= 8)
>> >> +               return;
>> >> +
>> >> +       gpio->ie &= ~(1 << data->hwirq);
>> >> +}
>> >> +
>> >> +static void sl28cpld_gpio_irq_enable(struct irq_data *data)
>> >> +{
>> >> +       struct sl28cpld_gpio *gpio =
>> >> +               gpiochip_get_data(irq_data_get_irq_chip_data(data));
>> >> +
>> >> +       if (data->hwirq >= 8)
>> >> +               return;
>> >> +
>> >> +       gpio->ie |= (1 << data->hwirq);
>> >> +}
>> >> +
>> >> +static int sl28cpld_gpio_irq_set_type(struct irq_data *data, unsigned
>> >> int type)
>> >> +{
>> >> +       /* only edge triggered interrupts on both edges are supported
>> >> */
>> >> +       return (type == IRQ_TYPE_EDGE_BOTH) ? 0 : -EINVAL;
>> >> +}
>> >> +
>> >> +static irqreturn_t sl28cpld_gpio_irq_thread(int irq, void *data)
>> >> +{
>> >> +       struct sl28cpld_gpio *gpio = data;
>> >> +       unsigned int ip;
>> >> +       unsigned int virq;
>> >> +       int pin;
>> >> +       int ret;
>> >> +
>> >> +       ret = regmap_read(gpio->regmap, gpio->offset + GPIO_REG_IP,
>> >> &ip);
>> >> +       if (ret)
>> >> +               return IRQ_NONE;
>> >> +
>> >> +       /* mask other pending interrupts which are not enabled */
>> >> +       ip &= gpio->ie;
>> >> +
>> >> +       /* ack the interrupts */
>> >> +       regmap_write(gpio->regmap, gpio->offset + GPIO_REG_IP, ip);
>> >> +
>> >> +       /* and handle them */
>> >> +       while (ip) {
>> >> +               pin = __ffs(ip);
>> >> +               ip &= ~BIT(pin);
>> >> +
>> >> +               virq = irq_find_mapping(gpio->gpio_chip.irq.domain,
>> >> pin);
>> >> +               if (virq)
>> >> +                       handle_nested_irq(virq);
>> >> +       }
>> >> +
>> >> +       return IRQ_HANDLED;
>> >> +}
>> >
>> > This definitely looks like parts of regmap_irqchip reimplemented.
>> > Please check if you could reuse it - it would save a lot of code.
>> 
>> See above. I'd be happy to reuse the code though.
>> 
>> >
>> >> +
>> >> +static int sl28_cpld_gpio_irq_init(struct platform_device *pdev, int
>> >> irq)
>> >> +{
>> >> +       struct sl28cpld_gpio *gpio = platform_get_drvdata(pdev);
>> >> +       struct irq_chip *irq_chip = &gpio->irq_chip;
>> >> +       int ret;
>> >> +
>> >> +       irq_chip->name = "sl28cpld-gpio-irq",
>> >> +       irq_chip->irq_bus_lock = sl28cpld_gpio_irq_lock,
>> >> +       irq_chip->irq_bus_sync_unlock = sl28cpld_gpio_irq_sync_unlock,
>> >> +       irq_chip->irq_disable = sl28cpld_gpio_irq_disable,
>> >> +       irq_chip->irq_enable = sl28cpld_gpio_irq_enable,
>> >> +       irq_chip->irq_set_type = sl28cpld_gpio_irq_set_type,
>> >> +       irq_chip->flags = IRQCHIP_SKIP_SET_WAKE,
>> >> +
>> >> +       ret = gpiochip_irqchip_add_nested(&gpio->gpio_chip, irq_chip,
>> >> 0,
>> >> +                                         handle_simple_irq,
>> >> IRQ_TYPE_NONE);
>> >> +       if (ret)
>> >> +               return ret;
>> >> +
>> >> +       ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
>> >> +                                       sl28cpld_gpio_irq_thread,
>> >> +                                       IRQF_SHARED | IRQF_ONESHOT,
>> >> +                                       pdev->name, gpio);
>> >> +       if (ret)
>> >> +               return ret;
>> >> +
>> >> +       gpiochip_set_nested_irqchip(&gpio->gpio_chip, irq_chip, irq);
>> >> +
>> >> +       return 0;
>> >> +}
>> >> +
>> >> +static int sl28cpld_gpio_probe(struct platform_device *pdev)
>> >> +{
>> >> +       enum sl28cpld_gpio_type type =
>> >> +               platform_get_device_id(pdev)->driver_data;
>> >> +       struct device_node *np = pdev->dev.of_node;
>> >> +       struct sl28cpld_gpio *gpio;
>> >> +       struct gpio_chip *chip;
>> >> +       struct resource *res;
>> >> +       bool irq_support = false;
>> >> +       int ret;
>> >> +       int irq;
>> >> +
>> >> +       gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
>> >> +       if (!gpio)
>> >> +               return -ENOMEM;
>> >> +
>> >> +       if (!pdev->dev.parent)
>> >> +               return -ENODEV;
>> >
>> > Why not check this before allocating any memory?
>> 
>> I'll change that, you're not the first one which notices that. My 
>> reason
>> was to have the check together with the dev_get_regmap() which uses 
>> the
>> parent, expecting that the error case only happen exceptionally.
>> 
>> >
>> >> +
>> >> +       gpio->regmap = dev_get_regmap(pdev->dev.parent, NULL);
>> >> +       if (!gpio->regmap)
>> >> +               return -ENODEV;
>> >> +
>> >> +       res = platform_get_resource(pdev, IORESOURCE_REG, 0);
>> >> +       if (!res)
>> >> +               return -EINVAL;
>> >> +       gpio->offset = res->start;
>> >> +
>> >
>> > This isn't how IO resources are used. What are you trying to achieve
>> > here?
>> 
>> Mh are you sure? The blueprint for this were the regulators in
>> drivers/regulators/, eg the wm831x-ldo.c. IORESOURCE_REG isn't used
>> that often. But here is what I want to achieve (for which I haven't
>> found any existing drivers for now):
>>   (1) the individual blocks of the overall sl28cpld may be used
>>       multiple times, eg. this driver only has the offset to a
>>       base address. So if there are two blocks, this mfd core
>>       driver will register two devices for this driver with
>>       different base offsets, which are passed by IORESOURCE_REG
>>   (2) I wanted to avoid having a private mfd include with some
>>       kind of "proprietary" method how to get that offset
>>   (3) the mfd core driver is the one knowing the offset, thus it
>>       is possible to have different flavours of the sl28cpld
>> 
> 
> Ok, now I see it's documented in the bindings. Thanks for the 
> explanation.
> 
>> 
>> >
>> >> +       /* initialize struct gpio_chip */
>> >> +       mutex_init(&gpio->lock);
>> >> +       chip = &gpio->gpio_chip;
>> >> +       chip->parent = &pdev->dev;
>> >> +       chip->label = dev_name(&pdev->dev);
>> >> +       chip->owner = THIS_MODULE;
>> >> +       chip->can_sleep = true;
>> >> +       chip->base = -1;
>> >> +       chip->ngpio = 8;
>> >> +
>> >> +       switch (type) {
>> >> +       case sl28cpld_gpio:
>> >> +               chip->get_direction = sl28cpld_gpio_get_direction;
>> >> +               chip->direction_input = sl28cpld_gpio_direction_input;
>> >> +               chip->direction_output =
>> >> sl28cpld_gpio_direction_output;
>> >> +               chip->get = sl28cpld_gpio_get;
>> >> +               chip->set = sl28cpld_gpio_set;
>> >> +               irq_support = true;
>> >> +               break;
>> >> +       case sl28cpld_gpo:
>> >> +               chip->set = sl28cpld_gpo_set;
>> >> +               chip->get = sl28cpld_gpi_get;
>> >> +               break;
>> >> +       case sl28cpld_gpi:
>> >> +               chip->get = sl28cpld_gpi_get;
>> >> +               break;
>> >> +       }
>> >> +
>> >> +       ret = devm_gpiochip_add_data(&pdev->dev, chip, gpio);
>> >> +       if (ret < 0)
>> >> +               return ret;
>> >> +
>> >> +       platform_set_drvdata(pdev, gpio);
>> >> +
>> >> +       if (irq_support && of_property_read_bool(np,
>> >> "interrupt-controller")) {
>> >
>> > You're depending on OF_GPIO for this one function. Please switch to
>> > device_property_read_bool() instead.
>> 
>> ok
>> 
>> 
>> >
>> >> +               irq = platform_get_irq(pdev, 0);
>> >> +               if (irq < 0)
>> >> +                       return ret;
>> >> +
>> >> +               ret = sl28_cpld_gpio_irq_init(pdev, irq);
>> >> +               if (ret)
>> >> +                       return ret;
>> >> +       }
>> >> +
>> >> +       return 0;
>> >> +}
>> >> +
>> >> +static const struct platform_device_id sl28cpld_gpio_id_table[] = {
>> >> +       {"sl28cpld-gpio", sl28cpld_gpio},
>> >> +       {"sl28cpld-gpi", sl28cpld_gpi},
>> >> +       {"sl28cpld-gpo", sl28cpld_gpo},
>> >
>> > Could you explain this a bit more? Is this the same component with
>> > input/output-only lines or three different components?
>> 
>> These are actually three different components. Ie. you could have a
>> flavour where you have one GPIO (sl28cpld-gpio) and two output-only
>> ones (sl28cpld-gpo). Is that what you wanted to know?
>> 
> 
> Yes, thanks. This could use some documentation in the bindings though.

ok. thats easy ;)

-michael

> Bartosz
> 
>> >
>> >> +};
>> >> +MODULE_DEVICE_TABLE(platform, sl28cpld_gpio_id_table);
>> >> +
>> >> +static struct platform_driver sl28cpld_gpio_driver = {
>> >> +       .probe = sl28cpld_gpio_probe,
>> >> +       .id_table = sl28cpld_gpio_id_table,
>> >> +       .driver = {
>> >> +               .name = "sl28cpld-gpio",
>> >> +       },
>> >> +};
>> >> +module_platform_driver(sl28cpld_gpio_driver);
>> >> +
>> >> +MODULE_DESCRIPTION("sl28cpld GPIO Driver");
>> >> +MODULE_LICENSE("GPL");
>> >
>> > I think you could use a MODULE_ALIAS() here if you want this module to
>> > be loaded automatically by udev.
>> 
>> ok, I'll look into that.
>> 
>> thanks,
>> -michael
>> 
>> >
>> >> --
>> >> 2.20.1
>> >>
>> >
>> > Best regards,
>> > Bartosz Golaszewski

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

* Re: [PATCH 12/18] gpio: add support for the sl28cpld GPIO controller
  2020-03-26 20:05         ` Michael Walle
@ 2020-03-27 10:20           ` Linus Walleij
  2020-03-27 15:28             ` Michael Walle
  0 siblings, 1 reply; 40+ messages in thread
From: Linus Walleij @ 2020-03-27 10:20 UTC (permalink / raw)
  To: Michael Walle
  Cc: Bartosz Golaszewski, linux-gpio, linux-devicetree, LKML,
	linux-hwmon, linux-pwm, LINUXWATCHDOG, arm-soc, Rob Herring,
	Jean Delvare, Guenter Roeck, Lee Jones, Thierry Reding,
	Uwe Kleine-König, Wim Van Sebroeck, Shawn Guo, Li Yang,
	Thomas Gleixner, Jason Cooper, Marc Zyngier

On Thu, Mar 26, 2020 at 9:06 PM Michael Walle <michael@walle.cc> wrote:
> Am 2020-03-25 12:50, schrieb Bartosz Golaszewski:

> > In that case maybe you should use the disable_locking option in
> > regmap_config and provide your own callbacks that you can use in the
> > irqchip code too?
>
> But how would that solve problem (1). And keep in mind, that the
> reqmap_irqchip is actually used for the interrupt controller, which
> is not this gpio controller.
>
> Ie. the interrupt controller of the sl28cpld uses the regmap_irqchip
> and all interrupt phandles pointing to the interrupt controller will
> reference the toplevel node. Any phandles pointing to the gpio
> controller will reference the GPIO subnode.

Ideally we would create something generic that has been on my
mind for some time, like a generic GPIO regmap irqchip now that
there are a few controllers like that.

I don't know how feasible it is or how much work it would be. But
as with GPIO_GENERIC (for MMIO) it would be helpful since we
can then implement things like .set_multiple() and .get_multiple()
for everyone.

Yours,
Linus Walleij

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

* Re: [PATCH 12/18] gpio: add support for the sl28cpld GPIO controller
  2020-03-27 10:20           ` Linus Walleij
@ 2020-03-27 15:28             ` Michael Walle
  2020-03-27 19:01               ` Linus Walleij
  2020-03-30 11:21               ` Bartosz Golaszewski
  0 siblings, 2 replies; 40+ messages in thread
From: Michael Walle @ 2020-03-27 15:28 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Bartosz Golaszewski, linux-gpio, linux-devicetree, LKML,
	linux-hwmon, linux-pwm, LINUXWATCHDOG, arm-soc, Rob Herring,
	Jean Delvare, Guenter Roeck, Lee Jones, Thierry Reding,
	Uwe Kleine-König, Wim Van Sebroeck, Shawn Guo, Li Yang,
	Thomas Gleixner, Jason Cooper, Marc Zyngier

Am 2020-03-27 11:20, schrieb Linus Walleij:
> On Thu, Mar 26, 2020 at 9:06 PM Michael Walle <michael@walle.cc> wrote:
>> Am 2020-03-25 12:50, schrieb Bartosz Golaszewski:
> 
>> > In that case maybe you should use the disable_locking option in
>> > regmap_config and provide your own callbacks that you can use in the
>> > irqchip code too?
>> 
>> But how would that solve problem (1). And keep in mind, that the
>> reqmap_irqchip is actually used for the interrupt controller, which
>> is not this gpio controller.
>> 
>> Ie. the interrupt controller of the sl28cpld uses the regmap_irqchip
>> and all interrupt phandles pointing to the interrupt controller will
>> reference the toplevel node. Any phandles pointing to the gpio
>> controller will reference the GPIO subnode.
> 
> Ideally we would create something generic that has been on my
> mind for some time, like a generic GPIO regmap irqchip now that
> there are a few controllers like that.
> 
> I don't know how feasible it is or how much work it would be. But
> as with GPIO_GENERIC (for MMIO) it would be helpful since we
> can then implement things like .set_multiple() and .get_multiple()
> for everyone.

For starters, would that be a drivers/gpio/gpio-regmap.c or a
drivers/base/regmap/regmap-gpio.c? I would assume the first,
because the stuff in drivers/base/regmap operates on a given
regmap and we'd just be using one, correct? On the other hand
there is also the reqmap-irq.c. But as pointed out before, it
will add an interrupt controller to the regmap, not a device
so to speak.

-michael

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

* Re: [PATCH 12/18] gpio: add support for the sl28cpld GPIO controller
  2020-03-27 15:28             ` Michael Walle
@ 2020-03-27 19:01               ` Linus Walleij
  2020-03-30 11:21               ` Bartosz Golaszewski
  1 sibling, 0 replies; 40+ messages in thread
From: Linus Walleij @ 2020-03-27 19:01 UTC (permalink / raw)
  To: Michael Walle
  Cc: Bartosz Golaszewski, linux-gpio, linux-devicetree, LKML,
	linux-hwmon, linux-pwm, LINUXWATCHDOG, arm-soc, Rob Herring,
	Jean Delvare, Guenter Roeck, Lee Jones, Thierry Reding,
	Uwe Kleine-König, Wim Van Sebroeck, Shawn Guo, Li Yang,
	Thomas Gleixner, Jason Cooper, Marc Zyngier

On Fri, Mar 27, 2020 at 4:28 PM Michael Walle <michael@walle.cc> wrote:

> For starters, would that be a drivers/gpio/gpio-regmap.c or a
> drivers/base/regmap/regmap-gpio.c? I would assume the first,

Yeah I would name it like that. gpio-regmap.c.

Yours,
Linus Walleij

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

* Re: [PATCH 12/18] gpio: add support for the sl28cpld GPIO controller
  2020-03-18  9:14   ` Bartosz Golaszewski
  2020-03-18 12:45     ` Michael Walle
@ 2020-03-28 12:04     ` Michael Walle
  2020-03-28 17:20     ` Michael Walle
  2 siblings, 0 replies; 40+ messages in thread
From: Michael Walle @ 2020-03-28 12:04 UTC (permalink / raw)
  To: Bartosz Golaszewski
  Cc: linux-gpio, linux-devicetree, LKML, linux-hwmon, linux-pwm,
	LINUXWATCHDOG, arm-soc, Linus Walleij, Rob Herring, Jean Delvare,
	Guenter Roeck, Lee Jones, Thierry Reding, Uwe Kleine-König,
	Wim Van Sebroeck, Shawn Guo, Li Yang, Thomas Gleixner,
	Jason Cooper, Marc Zyngier

Hi Bartosz,

Am 2020-03-18 10:14, schrieb Bartosz Golaszewski:
> wt., 17 mar 2020 o 21:50 Michael Walle <michael@walle.cc> napisał(a):
>> 
>> This adds support for the GPIO controller of the sl28 board management
>> controller. This driver is part of a multi-function device.
>> 
>> Signed-off-by: Michael Walle <michael@walle.cc>
> 
> Hi Michael,
> 
> thanks for the driver. Please take a look at some comments below.
> 
>> ---

[..]

>> +#define GPIO_REG_DIR   0
>> +#define GPIO_REG_OUT   1
>> +#define GPIO_REG_IN    2
>> +#define GPIO_REG_IE    3
>> +#define GPIO_REG_IP    4
> 
> These values would be more clear if they were defined as hex.
> 
>> +
>> +#define GPI_REG_IN     0
>> +
>> +#define GPO_REG_OUT    0
> 
> Please also use a common prefix even for defines.

The GPIO_, GPI_ and GPO_ prefixes corresponds to the different
flavours. Do they still need a common prefix? Ie. the GPI_REG_IN
has nothing to do with GPO_REG_OUT, nor has both something
to do with the GPIO_REG_IN and GPIO_REG_OUT. I could prefix them
with SL28CPLD_ though. But I don't know if that is what you had
in mind because then they would be SL28CPLD_GPIO_REG_IN and
SL28CPLD_GPI_REG_IN for example.

-michael

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

* Re: [PATCH 12/18] gpio: add support for the sl28cpld GPIO controller
  2020-03-18  9:14   ` Bartosz Golaszewski
  2020-03-18 12:45     ` Michael Walle
  2020-03-28 12:04     ` Michael Walle
@ 2020-03-28 17:20     ` Michael Walle
  2 siblings, 0 replies; 40+ messages in thread
From: Michael Walle @ 2020-03-28 17:20 UTC (permalink / raw)
  To: Bartosz Golaszewski
  Cc: linux-gpio, linux-devicetree, LKML, linux-hwmon, linux-pwm,
	LINUXWATCHDOG, arm-soc, Linus Walleij, Rob Herring, Jean Delvare,
	Guenter Roeck, Lee Jones, Thierry Reding, Uwe Kleine-König,
	Wim Van Sebroeck, Shawn Guo, Li Yang, Thomas Gleixner,
	Jason Cooper, Marc Zyngier

Am 2020-03-18 10:14, schrieb Bartosz Golaszewski:
> wt., 17 mar 2020 o 21:50 Michael Walle <michael@walle.cc> napisał(a):
>> 
>> This adds support for the GPIO controller of the sl28 board management
>> controller. This driver is part of a multi-function device.
>> 
>> Signed-off-by: Michael Walle <michael@walle.cc>
> 
> Hi Michael,
> 
> thanks for the driver. Please take a look at some comments below.
> 
>> ---
>>  drivers/gpio/Kconfig         |  11 ++
>>  drivers/gpio/Makefile        |   1 +
>>  drivers/gpio/gpio-sl28cpld.c | 332 
>> +++++++++++++++++++++++++++++++++++
>>  3 files changed, 344 insertions(+)
>>  create mode 100644 drivers/gpio/gpio-sl28cpld.c
>> 
>> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
>> index 3cbf8882a0dd..516e47017ef5 100644
>> --- a/drivers/gpio/Kconfig
>> +++ b/drivers/gpio/Kconfig
>> @@ -1211,6 +1211,17 @@ config GPIO_RC5T583
>>           This driver provides the support for driving/reading the 
>> gpio pins
>>           of RC5T583 device through standard gpio library.
>> 
>> +config GPIO_SL28CPLD
>> +       tristate "Kontron sl28 GPIO"
>> +       depends on MFD_SL28CPLD
>> +       depends on OF_GPIO
>> +       select GPIOLIB_IRQCHIP
> 
> Please see below - I think both are not needed.
> 
>> +       help
>> +         This enables support for the GPIOs found on the Kontron sl28 
>> CPLD.
>> +
>> +         This driver can also be built as a module. If so, the module 
>> will be
>> +         called gpio-sl28cpld.
>> +
>>  config GPIO_STMPE
>>         bool "STMPE GPIOs"
>>         depends on MFD_STMPE
>> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
>> index 0b571264ddbc..0ca2d52c78e8 100644
>> --- a/drivers/gpio/Makefile
>> +++ b/drivers/gpio/Makefile
>> @@ -127,6 +127,7 @@ obj-$(CONFIG_GPIO_SCH311X)          += 
>> gpio-sch311x.o
>>  obj-$(CONFIG_GPIO_SCH)                 += gpio-sch.o
>>  obj-$(CONFIG_GPIO_SIFIVE)              += gpio-sifive.o
>>  obj-$(CONFIG_GPIO_SIOX)                        += gpio-siox.o
>> +obj-$(CONFIG_GPIO_SL28CPLD)            += gpio-sl28cpld.o
>>  obj-$(CONFIG_GPIO_SODAVILLE)           += gpio-sodaville.o
>>  obj-$(CONFIG_GPIO_SPEAR_SPICS)         += gpio-spear-spics.o
>>  obj-$(CONFIG_GPIO_SPRD)                        += gpio-sprd.o
>> diff --git a/drivers/gpio/gpio-sl28cpld.c 
>> b/drivers/gpio/gpio-sl28cpld.c
>> new file mode 100644
>> index 000000000000..94f82013882f
>> --- /dev/null
>> +++ b/drivers/gpio/gpio-sl28cpld.c
>> @@ -0,0 +1,332 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * SMARC-sAL28 GPIO driver.
>> + *
>> + * Copyright 2019 Kontron Europe GmbH
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_device.h>
>> +#include <linux/of_address.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/regmap.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/gpio/driver.h>
>> +
>> +#define GPIO_REG_DIR   0
>> +#define GPIO_REG_OUT   1
>> +#define GPIO_REG_IN    2
>> +#define GPIO_REG_IE    3
>> +#define GPIO_REG_IP    4
> 
> These values would be more clear if they were defined as hex.
> 
>> +
>> +#define GPI_REG_IN     0
>> +
>> +#define GPO_REG_OUT    0
> 
> Please also use a common prefix even for defines.
> 
>> +
>> +enum sl28cpld_gpio_type {
>> +       sl28cpld_gpio,
>> +       sl28cpld_gpi,
>> +       sl28cpld_gpo,
>> +};
> 
> Enum values should be all upper-case.
> 
>> +
>> +struct sl28cpld_gpio {
>> +       struct gpio_chip gpio_chip;
>> +       struct irq_chip irq_chip;
>> +       struct regmap *regmap;
>> +       u32 offset;
>> +       struct mutex lock;
>> +       u8 ie;
>> +};
>> +
>> +static void sl28cpld_gpio_set_reg(struct gpio_chip *chip, unsigned 
>> int reg,
>> +                                 unsigned int offset, int value)
>> +{
>> +       struct sl28cpld_gpio *gpio = gpiochip_get_data(chip);
>> +       unsigned int mask = 1 << offset;
>> +       unsigned int val = value << offset;
>> +
>> +       regmap_update_bits(gpio->regmap, gpio->offset + reg, mask, 
>> val);
>> +}
>> +
>> +static void sl28cpld_gpio_set(struct gpio_chip *chip, unsigned int 
>> offset,
>> +                             int value)
>> +{
>> +       sl28cpld_gpio_set_reg(chip, GPIO_REG_OUT, offset, value);
>> +}
>> +
>> +static void sl28cpld_gpo_set(struct gpio_chip *chip, unsigned int 
>> offset,
>> +                            int value)
>> +{
>> +       sl28cpld_gpio_set_reg(chip, GPO_REG_OUT, offset, value);
>> +}
>> +
>> +static int sl28cpld_gpio_get_reg(struct gpio_chip *chip, unsigned int 
>> reg,
>> +                                unsigned int offset)
>> +{
>> +       struct sl28cpld_gpio *gpio = gpiochip_get_data(chip);
>> +       unsigned int mask = 1 << offset;
>> +       unsigned int val;
>> +       int ret;
>> +
>> +       ret = regmap_read(gpio->regmap, gpio->offset + reg, &val);
>> +       if (ret)
>> +               return ret;
>> +
>> +       return (val & mask) ? 1 : 0;
>> +}
>> +
>> +static int sl28cpld_gpio_get(struct gpio_chip *chip, unsigned int 
>> offset)
>> +{
>> +       return sl28cpld_gpio_get_reg(chip, GPIO_REG_IN, offset);
>> +}
>> +
>> +static int sl28cpld_gpi_get(struct gpio_chip *chip, unsigned int 
>> offset)
>> +{
>> +       return sl28cpld_gpio_get_reg(chip, GPI_REG_IN, offset);
>> +}
>> +
>> +static int sl28cpld_gpio_get_direction(struct gpio_chip *chip,
>> +                                      unsigned int offset)
>> +{
>> +       struct sl28cpld_gpio *gpio = gpiochip_get_data(chip);
>> +       unsigned int reg;
>> +       int ret;
>> +
>> +       ret = regmap_read(gpio->regmap, gpio->offset + GPIO_REG_DIR, 
>> &reg);
>> +       if (ret)
>> +               return ret;
>> +
>> +       if (reg & (1 << offset))
>> +               return GPIO_LINE_DIRECTION_OUT;
>> +       else
>> +               return GPIO_LINE_DIRECTION_IN;
>> +}
>> +
>> +static int sl28cpld_gpio_set_direction(struct gpio_chip *chip,
>> +                                      unsigned int offset,
>> +                                      bool output)
>> +{
>> +       struct sl28cpld_gpio *gpio = gpiochip_get_data(chip);
>> +       unsigned int mask = 1 << offset;
>> +       unsigned int val = (output) ? mask : 0;
>> +
>> +       return regmap_update_bits(gpio->regmap, gpio->offset + 
>> GPIO_REG_DIR,
>> +                                 mask, val);
>> +
> 
> Stray newline.
> 
>> +}
>> +
>> +static int sl28cpld_gpio_direction_input(struct gpio_chip *chip,
>> +                                        unsigned int offset)
>> +{
>> +       return sl28cpld_gpio_set_direction(chip, offset, false);
>> +}
>> +
>> +static int sl28cpld_gpio_direction_output(struct gpio_chip *chip,
>> +                                         unsigned int offset, int 
>> value)
>> +{
>> +       sl28cpld_gpio_set_reg(chip, GPIO_REG_OUT, offset, value);
>> +       return sl28cpld_gpio_set_direction(chip, offset, true);
>> +}
>> +
>> +static void sl28cpld_gpio_irq_lock(struct irq_data *data)
>> +{
>> +       struct sl28cpld_gpio *gpio =
>> +               gpiochip_get_data(irq_data_get_irq_chip_data(data));
>> +
>> +       mutex_lock(&gpio->lock);
> 
> How does that actually lock anything? Regmap uses a different lock and
> if you want to make sure nobody modifies the GPIO registers than you'd
> need to use the same lock. Also: this looks a lot like a task for
> regmap_irqchip - maybe you could use it here or in the core mfd
> module?
> 
>> +}
>> +
>> +static void sl28cpld_gpio_irq_sync_unlock(struct irq_data *data)
>> +{
>> +       struct sl28cpld_gpio *gpio =
>> +               gpiochip_get_data(irq_data_get_irq_chip_data(data));
>> +
>> +       regmap_write(gpio->regmap, gpio->offset + GPIO_REG_IE, 
>> gpio->ie);
>> +       mutex_unlock(&gpio->lock);
>> +}
>> +
>> +static void sl28cpld_gpio_irq_disable(struct irq_data *data)
>> +{
>> +       struct sl28cpld_gpio *gpio =
>> +               gpiochip_get_data(irq_data_get_irq_chip_data(data));
>> +
>> +       if (data->hwirq >= 8)
>> +               return;
>> +
>> +       gpio->ie &= ~(1 << data->hwirq);
>> +}
>> +
>> +static void sl28cpld_gpio_irq_enable(struct irq_data *data)
>> +{
>> +       struct sl28cpld_gpio *gpio =
>> +               gpiochip_get_data(irq_data_get_irq_chip_data(data));
>> +
>> +       if (data->hwirq >= 8)
>> +               return;
>> +
>> +       gpio->ie |= (1 << data->hwirq);
>> +}
>> +
>> +static int sl28cpld_gpio_irq_set_type(struct irq_data *data, unsigned 
>> int type)
>> +{
>> +       /* only edge triggered interrupts on both edges are supported 
>> */
>> +       return (type == IRQ_TYPE_EDGE_BOTH) ? 0 : -EINVAL;
>> +}
>> +
>> +static irqreturn_t sl28cpld_gpio_irq_thread(int irq, void *data)
>> +{
>> +       struct sl28cpld_gpio *gpio = data;
>> +       unsigned int ip;
>> +       unsigned int virq;
>> +       int pin;
>> +       int ret;
>> +
>> +       ret = regmap_read(gpio->regmap, gpio->offset + GPIO_REG_IP, 
>> &ip);
>> +       if (ret)
>> +               return IRQ_NONE;
>> +
>> +       /* mask other pending interrupts which are not enabled */
>> +       ip &= gpio->ie;
>> +
>> +       /* ack the interrupts */
>> +       regmap_write(gpio->regmap, gpio->offset + GPIO_REG_IP, ip);
>> +
>> +       /* and handle them */
>> +       while (ip) {
>> +               pin = __ffs(ip);
>> +               ip &= ~BIT(pin);
>> +
>> +               virq = irq_find_mapping(gpio->gpio_chip.irq.domain, 
>> pin);
>> +               if (virq)
>> +                       handle_nested_irq(virq);
>> +       }
>> +
>> +       return IRQ_HANDLED;
>> +}
> 
> This definitely looks like parts of regmap_irqchip reimplemented.
> Please check if you could reuse it - it would save a lot of code.
> 
>> +
>> +static int sl28_cpld_gpio_irq_init(struct platform_device *pdev, int 
>> irq)
>> +{
>> +       struct sl28cpld_gpio *gpio = platform_get_drvdata(pdev);
>> +       struct irq_chip *irq_chip = &gpio->irq_chip;
>> +       int ret;
>> +
>> +       irq_chip->name = "sl28cpld-gpio-irq",
>> +       irq_chip->irq_bus_lock = sl28cpld_gpio_irq_lock,
>> +       irq_chip->irq_bus_sync_unlock = sl28cpld_gpio_irq_sync_unlock,
>> +       irq_chip->irq_disable = sl28cpld_gpio_irq_disable,
>> +       irq_chip->irq_enable = sl28cpld_gpio_irq_enable,
>> +       irq_chip->irq_set_type = sl28cpld_gpio_irq_set_type,
>> +       irq_chip->flags = IRQCHIP_SKIP_SET_WAKE,
>> +
>> +       ret = gpiochip_irqchip_add_nested(&gpio->gpio_chip, irq_chip, 
>> 0,
>> +                                         handle_simple_irq, 
>> IRQ_TYPE_NONE);
>> +       if (ret)
>> +               return ret;
>> +
>> +       ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
>> +                                       sl28cpld_gpio_irq_thread,
>> +                                       IRQF_SHARED | IRQF_ONESHOT,
>> +                                       pdev->name, gpio);
>> +       if (ret)
>> +               return ret;
>> +
>> +       gpiochip_set_nested_irqchip(&gpio->gpio_chip, irq_chip, irq);
>> +
>> +       return 0;
>> +}
>> +
>> +static int sl28cpld_gpio_probe(struct platform_device *pdev)
>> +{
>> +       enum sl28cpld_gpio_type type =
>> +               platform_get_device_id(pdev)->driver_data;
>> +       struct device_node *np = pdev->dev.of_node;
>> +       struct sl28cpld_gpio *gpio;
>> +       struct gpio_chip *chip;
>> +       struct resource *res;
>> +       bool irq_support = false;
>> +       int ret;
>> +       int irq;
>> +
>> +       gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
>> +       if (!gpio)
>> +               return -ENOMEM;
>> +
>> +       if (!pdev->dev.parent)
>> +               return -ENODEV;
> 
> Why not check this before allocating any memory?
> 
>> +
>> +       gpio->regmap = dev_get_regmap(pdev->dev.parent, NULL);
>> +       if (!gpio->regmap)
>> +               return -ENODEV;
>> +
>> +       res = platform_get_resource(pdev, IORESOURCE_REG, 0);
>> +       if (!res)
>> +               return -EINVAL;
>> +       gpio->offset = res->start;
>> +
> 
> This isn't how IO resources are used. What are you trying to achieve 
> here?
> 
>> +       /* initialize struct gpio_chip */
>> +       mutex_init(&gpio->lock);
>> +       chip = &gpio->gpio_chip;
>> +       chip->parent = &pdev->dev;
>> +       chip->label = dev_name(&pdev->dev);
>> +       chip->owner = THIS_MODULE;
>> +       chip->can_sleep = true;
>> +       chip->base = -1;
>> +       chip->ngpio = 8;
>> +
>> +       switch (type) {
>> +       case sl28cpld_gpio:
>> +               chip->get_direction = sl28cpld_gpio_get_direction;
>> +               chip->direction_input = sl28cpld_gpio_direction_input;
>> +               chip->direction_output = 
>> sl28cpld_gpio_direction_output;
>> +               chip->get = sl28cpld_gpio_get;
>> +               chip->set = sl28cpld_gpio_set;
>> +               irq_support = true;
>> +               break;
>> +       case sl28cpld_gpo:
>> +               chip->set = sl28cpld_gpo_set;
>> +               chip->get = sl28cpld_gpi_get;
>> +               break;
>> +       case sl28cpld_gpi:
>> +               chip->get = sl28cpld_gpi_get;
>> +               break;
>> +       }
>> +
>> +       ret = devm_gpiochip_add_data(&pdev->dev, chip, gpio);
>> +       if (ret < 0)
>> +               return ret;
>> +
>> +       platform_set_drvdata(pdev, gpio);
>> +
>> +       if (irq_support && of_property_read_bool(np, 
>> "interrupt-controller")) {
> 
> You're depending on OF_GPIO for this one function. Please switch to
> device_property_read_bool() instead.
> 
>> +               irq = platform_get_irq(pdev, 0);
>> +               if (irq < 0)
>> +                       return ret;
>> +
>> +               ret = sl28_cpld_gpio_irq_init(pdev, irq);
>> +               if (ret)
>> +                       return ret;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static const struct platform_device_id sl28cpld_gpio_id_table[] = {
>> +       {"sl28cpld-gpio", sl28cpld_gpio},
>> +       {"sl28cpld-gpi", sl28cpld_gpi},
>> +       {"sl28cpld-gpo", sl28cpld_gpo},
> 
> Could you explain this a bit more? Is this the same component with
> input/output-only lines or three different components?
> 
>> +};
>> +MODULE_DEVICE_TABLE(platform, sl28cpld_gpio_id_table);
>> +
>> +static struct platform_driver sl28cpld_gpio_driver = {
>> +       .probe = sl28cpld_gpio_probe,
>> +       .id_table = sl28cpld_gpio_id_table,
>> +       .driver = {
>> +               .name = "sl28cpld-gpio",
>> +       },
>> +};
>> +module_platform_driver(sl28cpld_gpio_driver);
>> +
>> +MODULE_DESCRIPTION("sl28cpld GPIO Driver");
>> +MODULE_LICENSE("GPL");
> 
> I think you could use a MODULE_ALIAS() here if you want this module to
> be loaded automatically by udev.

Turns out MODULE_ALIAS("platform:..") isn't working with mfd and
OF_MFD_CELL(), because the match is done on "of:.." module aliases.
So I guess I'll need a of_device_id array after all, although the
matching for the mfd is via the platform driver name.

-michael

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

* Re: [PATCH 12/18] gpio: add support for the sl28cpld GPIO controller
  2020-03-27 15:28             ` Michael Walle
  2020-03-27 19:01               ` Linus Walleij
@ 2020-03-30 11:21               ` Bartosz Golaszewski
  2020-03-30 11:48                 ` Michael Walle
  1 sibling, 1 reply; 40+ messages in thread
From: Bartosz Golaszewski @ 2020-03-30 11:21 UTC (permalink / raw)
  To: Michael Walle
  Cc: Linus Walleij, linux-gpio, linux-devicetree, LKML, linux-hwmon,
	linux-pwm, LINUXWATCHDOG, arm-soc, Rob Herring, Jean Delvare,
	Guenter Roeck, Lee Jones, Thierry Reding, Uwe Kleine-König,
	Wim Van Sebroeck, Shawn Guo, Li Yang, Thomas Gleixner,
	Jason Cooper, Marc Zyngier

pt., 27 mar 2020 o 16:28 Michael Walle <michael@walle.cc> napisał(a):
>
> Am 2020-03-27 11:20, schrieb Linus Walleij:
> > On Thu, Mar 26, 2020 at 9:06 PM Michael Walle <michael@walle.cc> wrote:
> >> Am 2020-03-25 12:50, schrieb Bartosz Golaszewski:
> >
> >> > In that case maybe you should use the disable_locking option in
> >> > regmap_config and provide your own callbacks that you can use in the
> >> > irqchip code too?
> >>
> >> But how would that solve problem (1). And keep in mind, that the
> >> reqmap_irqchip is actually used for the interrupt controller, which
> >> is not this gpio controller.
> >>
> >> Ie. the interrupt controller of the sl28cpld uses the regmap_irqchip
> >> and all interrupt phandles pointing to the interrupt controller will
> >> reference the toplevel node. Any phandles pointing to the gpio
> >> controller will reference the GPIO subnode.
> >
> > Ideally we would create something generic that has been on my
> > mind for some time, like a generic GPIO regmap irqchip now that
> > there are a few controllers like that.
> >
> > I don't know how feasible it is or how much work it would be. But
> > as with GPIO_GENERIC (for MMIO) it would be helpful since we
> > can then implement things like .set_multiple() and .get_multiple()
> > for everyone.
>
> For starters, would that be a drivers/gpio/gpio-regmap.c or a
> drivers/base/regmap/regmap-gpio.c? I would assume the first,
> because the stuff in drivers/base/regmap operates on a given
> regmap and we'd just be using one, correct? On the other hand
> there is also the reqmap-irq.c. But as pointed out before, it
> will add an interrupt controller to the regmap, not a device
> so to speak.
>
> -michael

This has been on my TODO list for so long, but I've never been able to
find the time... I'd really appreciate any effort in that direction as
I believe it would allow us to slowly port a big part of the GPIO
expander drivers over to it and make large portions of our codebase
generic.

Best regards,
Bartosz Golaszewski

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

* Re: [PATCH 12/18] gpio: add support for the sl28cpld GPIO controller
  2020-03-30 11:21               ` Bartosz Golaszewski
@ 2020-03-30 11:48                 ` Michael Walle
  0 siblings, 0 replies; 40+ messages in thread
From: Michael Walle @ 2020-03-30 11:48 UTC (permalink / raw)
  To: Bartosz Golaszewski
  Cc: Linus Walleij, linux-gpio, linux-devicetree, LKML, linux-hwmon,
	linux-pwm, LINUXWATCHDOG, arm-soc, Rob Herring, Jean Delvare,
	Guenter Roeck, Lee Jones, Thierry Reding, Uwe Kleine-König,
	Wim Van Sebroeck, Shawn Guo, Li Yang, Thomas Gleixner,
	Jason Cooper, Marc Zyngier

Hi,


Am 2020-03-30 13:21, schrieb Bartosz Golaszewski:
> pt., 27 mar 2020 o 16:28 Michael Walle <michael@walle.cc> napisał(a):
>> 
>> Am 2020-03-27 11:20, schrieb Linus Walleij:
>> > On Thu, Mar 26, 2020 at 9:06 PM Michael Walle <michael@walle.cc> wrote:
>> >> Am 2020-03-25 12:50, schrieb Bartosz Golaszewski:
>> >
>> >> > In that case maybe you should use the disable_locking option in
>> >> > regmap_config and provide your own callbacks that you can use in the
>> >> > irqchip code too?
>> >>
>> >> But how would that solve problem (1). And keep in mind, that the
>> >> reqmap_irqchip is actually used for the interrupt controller, which
>> >> is not this gpio controller.
>> >>
>> >> Ie. the interrupt controller of the sl28cpld uses the regmap_irqchip
>> >> and all interrupt phandles pointing to the interrupt controller will
>> >> reference the toplevel node. Any phandles pointing to the gpio
>> >> controller will reference the GPIO subnode.
>> >
>> > Ideally we would create something generic that has been on my
>> > mind for some time, like a generic GPIO regmap irqchip now that
>> > there are a few controllers like that.
>> >
>> > I don't know how feasible it is or how much work it would be. But
>> > as with GPIO_GENERIC (for MMIO) it would be helpful since we
>> > can then implement things like .set_multiple() and .get_multiple()
>> > for everyone.
>> 
>> For starters, would that be a drivers/gpio/gpio-regmap.c or a
>> drivers/base/regmap/regmap-gpio.c? I would assume the first,
>> because the stuff in drivers/base/regmap operates on a given
>> regmap and we'd just be using one, correct? On the other hand
>> there is also the reqmap-irq.c. But as pointed out before, it
>> will add an interrupt controller to the regmap, not a device
>> so to speak.
>> 
>> -michael
> 
> This has been on my TODO list for so long, but I've never been able to
> find the time... I'd really appreciate any effort in that direction as
> I believe it would allow us to slowly port a big part of the GPIO
> expander drivers over to it and make large portions of our codebase
> generic.

I might have at least a first patch this week. So if you and Linus
Walleij might have some time to help review and comment on that, it
would be greatly appreciated.

-michael

> 
> Best regards,
> Bartosz Golaszewski

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

* Re: [PATCH 04/18] dt-bindings: mfd: Add bindings for sl28cpld
  2020-03-17 20:50 ` [PATCH 04/18] dt-bindings: mfd: Add bindings for sl28cpld Michael Walle
@ 2020-03-30 22:35   ` Rob Herring
  2020-03-31  7:40     ` Michael Walle
  0 siblings, 1 reply; 40+ messages in thread
From: Rob Herring @ 2020-03-30 22:35 UTC (permalink / raw)
  To: Michael Walle
  Cc: linux-gpio, devicetree, linux-kernel, linux-hwmon, linux-pwm,
	linux-watchdog, linux-arm-kernel, Linus Walleij,
	Bartosz Golaszewski, Jean Delvare, Guenter Roeck, Lee Jones,
	Thierry Reding, Uwe Kleine-König, Wim Van Sebroeck,
	Shawn Guo, Li Yang, Thomas Gleixner, Jason Cooper, Marc Zyngier

On Tue, Mar 17, 2020 at 09:50:03PM +0100, Michael Walle wrote:
> This adds device tree bindings for the board management controller found
> on the Kontron SMARC-sAL28 board.
> 
> Signed-off-by: Michael Walle <michael@walle.cc>
> ---
>  .../bindings/mfd/kontron,sl28cpld.yaml        | 143 ++++++++++++++++++
>  1 file changed, 143 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mfd/kontron,sl28cpld.yaml
> 
> diff --git a/Documentation/devicetree/bindings/mfd/kontron,sl28cpld.yaml b/Documentation/devicetree/bindings/mfd/kontron,sl28cpld.yaml
> new file mode 100644
> index 000000000000..3b9cca49d2d6
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/kontron,sl28cpld.yaml
> @@ -0,0 +1,143 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/mfd/kontron,sl28cpld.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Kontron's sl28cpld board management controller
> +
> +maintainers:
> +  - Michael Walle <michael@walle.cc>
> +
> +description: |
> +  The board management controller may contain different IP blocks like
> +  watchdog, fan monitoring, PWM controller, interrupt controller and a
> +  GPIO controller.
> +
> +properties:
> +  compatible:
> +    const: kontron,sl28cpld
> +
> +  reg:
> +    description:
> +      I2C device address.
> +    maxItems: 1
> +
> +  "#address-cells":
> +    const: 1
> +
> +  "#size-cells":
> +    const: 0
> +
> +  "#interrupt-cells":
> +    const: 2
> +
> +  interrupts:
> +    maxItems: 1
> +
> +  interrupt-controller: true
> +
> +patternProperties:
> +  "^gp(io|i|o)(@[0-9]+)?$":

Just 'gpio'. We don't need that level of distinguishing in node names.

> +    $ref: ../gpio/kontron,sl28cpld-gpio.yaml
> +
> +  "^hwmon(@[0-9]+)?$":
> +    $ref: ../hwmon/kontron,sl28cpld-hwmon.yaml
> +
> +  "^pwm(@[0-9]+)?$":
> +    $ref: ../pwm/kontron,sl28cpld-pwm.yaml
> +
> +  "^watchdog(@[0-9]+)?$":
> +    $ref: ../watchdog/kontron,sl28cpld-wdt.yaml

The patches for these files need to come first or validating this file 
fails. Really, you can just make all five files 1 patch.

> +
> +required:
> +  - "#address-cells"
> +  - "#size-cells"
> +  - compatible
> +  - reg
> +  - "#interrupt-cells"
> +  - interrupt-controller
> +
> +oneOf:
> +  - required:
> +    - interrupts
> +  - required:
> +    - interrupts-extended

Don't need to do this. Just make 'interrupts' required and you'll get 
interrupts-extended for free.

> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/interrupt-controller/irq.h>
> +    i2c {
> +        #address-cells = <1>;
> +        #size-cells = <0>;
> +
> +        sl28cpld@4a {
> +            #address-cells = <1>;
> +            #size-cells = <0>;
> +            compatible = "kontron,sl28cpld";
> +            reg = <0x4a>;
> +            interrupts-extended = <&gpio2 6 IRQ_TYPE_EDGE_FALLING>;
> +
> +            #interrupt-cells = <2>;
> +            interrupt-controller;
> +
> +            gpio@0 {
> +                compatible = "kontron,sl28cpld-gpio";
> +                reg = <0>;
> +
> +                gpio-controller;
> +                #gpio-cells = <2>;
> +
> +                interrupt-controller;
> +                #interrupt-cells = <2>;
> +            };
> +
> +            gpio@1 {
> +                compatible = "kontron,sl28cpld-gpio";
> +                reg = <1>;
> +
> +                gpio-controller;
> +                #gpio-cells = <2>;
> +
> +                interrupt-controller;
> +                #interrupt-cells = <2>;
> +            };
> +
> +            gpo {
> +                compatible = "kontron,sl28cpld-gpo";
> +
> +                gpio-controller;
> +                #gpio-cells = <2>;
> +                gpio-line-names = "a", "b", "c";
> +            };
> +
> +            gpi {
> +                compatible = "kontron,sl28cpld-gpi";
> +
> +                gpio-controller;
> +                #gpio-cells = <2>;
> +            };
> +
> +            hwmon {
> +                compatible = "kontron,sl28cpld-fan";
> +            };
> +
> +            pwm@0 {

You already used the '0' address. You can't have 2 things at the 
same address. There's only one number space at a given level. 

All these child devices don't have any DT resources, so you don't really 
need them. The parent node could be a gpio and pwm provider and that's 
all you need in DT. Aside from DT resources, the only other reason 
to have all these child nodes are if the child blocks are going to get 
assembled in different combinations across a variety of h/w.

Rob

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

* Re: [PATCH 04/18] dt-bindings: mfd: Add bindings for sl28cpld
  2020-03-30 22:35   ` Rob Herring
@ 2020-03-31  7:40     ` Michael Walle
  0 siblings, 0 replies; 40+ messages in thread
From: Michael Walle @ 2020-03-31  7:40 UTC (permalink / raw)
  To: Rob Herring
  Cc: linux-gpio, devicetree, linux-kernel, linux-hwmon, linux-pwm,
	linux-watchdog, linux-arm-kernel, Linus Walleij,
	Bartosz Golaszewski, Jean Delvare, Guenter Roeck, Lee Jones,
	Thierry Reding, Uwe Kleine-König, Wim Van Sebroeck,
	Shawn Guo, Li Yang, Thomas Gleixner, Jason Cooper, Marc Zyngier

Am 2020-03-31 00:35, schrieb Rob Herring:
> On Tue, Mar 17, 2020 at 09:50:03PM +0100, Michael Walle wrote:
>> This adds device tree bindings for the board management controller 
>> found
>> on the Kontron SMARC-sAL28 board.
>> 
>> Signed-off-by: Michael Walle <michael@walle.cc>
>> ---
>>  .../bindings/mfd/kontron,sl28cpld.yaml        | 143 
>> ++++++++++++++++++
>>  1 file changed, 143 insertions(+)
>>  create mode 100644 
>> Documentation/devicetree/bindings/mfd/kontron,sl28cpld.yaml
>> 
>> diff --git 
>> a/Documentation/devicetree/bindings/mfd/kontron,sl28cpld.yaml 
>> b/Documentation/devicetree/bindings/mfd/kontron,sl28cpld.yaml
>> new file mode 100644
>> index 000000000000..3b9cca49d2d6
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/mfd/kontron,sl28cpld.yaml
>> @@ -0,0 +1,143 @@
>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/mfd/kontron,sl28cpld.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Kontron's sl28cpld board management controller
>> +
>> +maintainers:
>> +  - Michael Walle <michael@walle.cc>
>> +
>> +description: |
>> +  The board management controller may contain different IP blocks 
>> like
>> +  watchdog, fan monitoring, PWM controller, interrupt controller and 
>> a
>> +  GPIO controller.
>> +
>> +properties:
>> +  compatible:
>> +    const: kontron,sl28cpld
>> +
>> +  reg:
>> +    description:
>> +      I2C device address.
>> +    maxItems: 1
>> +
>> +  "#address-cells":
>> +    const: 1
>> +
>> +  "#size-cells":
>> +    const: 0
>> +
>> +  "#interrupt-cells":
>> +    const: 2
>> +
>> +  interrupts:
>> +    maxItems: 1
>> +
>> +  interrupt-controller: true
>> +
>> +patternProperties:
>> +  "^gp(io|i|o)(@[0-9]+)?$":
> 
> Just 'gpio'. We don't need that level of distinguishing in node names.
> 
>> +    $ref: ../gpio/kontron,sl28cpld-gpio.yaml
>> +
>> +  "^hwmon(@[0-9]+)?$":
>> +    $ref: ../hwmon/kontron,sl28cpld-hwmon.yaml
>> +
>> +  "^pwm(@[0-9]+)?$":
>> +    $ref: ../pwm/kontron,sl28cpld-pwm.yaml
>> +
>> +  "^watchdog(@[0-9]+)?$":
>> +    $ref: ../watchdog/kontron,sl28cpld-wdt.yaml
> 
> The patches for these files need to come first or validating this file
> fails. Really, you can just make all five files 1 patch.
> 
>> +
>> +required:
>> +  - "#address-cells"
>> +  - "#size-cells"
>> +  - compatible
>> +  - reg
>> +  - "#interrupt-cells"
>> +  - interrupt-controller
>> +
>> +oneOf:
>> +  - required:
>> +    - interrupts
>> +  - required:
>> +    - interrupts-extended
> 
> Don't need to do this. Just make 'interrupts' required and you'll get
> interrupts-extended for free.
> 
>> +
>> +additionalProperties: false
>> +
>> +examples:
>> +  - |
>> +    #include <dt-bindings/interrupt-controller/irq.h>
>> +    i2c {
>> +        #address-cells = <1>;
>> +        #size-cells = <0>;
>> +
>> +        sl28cpld@4a {
>> +            #address-cells = <1>;
>> +            #size-cells = <0>;
>> +            compatible = "kontron,sl28cpld";
>> +            reg = <0x4a>;
>> +            interrupts-extended = <&gpio2 6 IRQ_TYPE_EDGE_FALLING>;
>> +
>> +            #interrupt-cells = <2>;
>> +            interrupt-controller;
>> +
>> +            gpio@0 {
>> +                compatible = "kontron,sl28cpld-gpio";
>> +                reg = <0>;
>> +
>> +                gpio-controller;
>> +                #gpio-cells = <2>;
>> +
>> +                interrupt-controller;
>> +                #interrupt-cells = <2>;
>> +            };
>> +
>> +            gpio@1 {
>> +                compatible = "kontron,sl28cpld-gpio";
>> +                reg = <1>;
>> +
>> +                gpio-controller;
>> +                #gpio-cells = <2>;
>> +
>> +                interrupt-controller;
>> +                #interrupt-cells = <2>;
>> +            };
>> +
>> +            gpo {
>> +                compatible = "kontron,sl28cpld-gpo";
>> +
>> +                gpio-controller;
>> +                #gpio-cells = <2>;
>> +                gpio-line-names = "a", "b", "c";
>> +            };
>> +
>> +            gpi {
>> +                compatible = "kontron,sl28cpld-gpi";
>> +
>> +                gpio-controller;
>> +                #gpio-cells = <2>;
>> +            };
>> +
>> +            hwmon {
>> +                compatible = "kontron,sl28cpld-fan";
>> +            };
>> +
>> +            pwm@0 {
> 
> You already used the '0' address. You can't have 2 things at the
> same address. There's only one number space at a given level.

There was a reason for having duplicate unit-addresses. See here
for my reasoning:
https://lore.kernel.org/linux-devicetree/e55d59a68f497c8f2eb406d40ae878b9@walle.cc/

But I've already noticed that it shouldn't be done it this way. The
DT check is already complaining.


> All these child devices don't have any DT resources, so you don't 
> really
> need them. The parent node could be a gpio and pwm provider and that's
> all you need in DT. Aside from DT resources, the only other reason
> to have all these child nodes are if the child blocks are going to get
> assembled in different combinations across a variety of h/w.

What do you mean by DT resources? There is a new patch series in 
preparation
where for example the watchdog has a new property
"kontron,assert-wdt-timeout-pin". Which IMHO should be go under the 
watchdog
node.
Besides from that, there are actually three interrupt controllers, ie. 
the
two full featured gpio controllers and one interrupt controller. Do you 
think
it makes sense to combine that into the parent node eg. merging 
different
thinks like an interrupt controller and the gpio controllers into a 
single
interrupt mapping in the parent node.
See also:
https://lore.kernel.org/linux-devicetree/0e3e8204ab992d75aa07fc36af7e4ab2@walle.cc/

Also please keep in mind, that these are only the current available 
building
blocks of a sl28cpld. They are likely to be extended for other 
functionalities.
So while it might be possible to merge the current nodes into the parent 
I don't
know it that is possible for future blocks.

-michael

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

end of thread, back to index

Thread overview: 40+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-03-17 20:49 [PATCH 00/18] Add support for Kontron sl28cpld Michael Walle
2020-03-17 20:50 ` [PATCH 01/18] include/linux/ioport.h: add helper to define REG resource constructs Michael Walle
2020-03-17 20:50 ` [PATCH 02/18] mfd: mfd-core: Don't overwrite the dma_mask of the child device Michael Walle
2020-03-17 20:50 ` [PATCH 03/18] mfd: mfd-core: match device tree node against reg property Michael Walle
2020-03-17 20:50 ` [PATCH 04/18] dt-bindings: mfd: Add bindings for sl28cpld Michael Walle
2020-03-30 22:35   ` Rob Herring
2020-03-31  7:40     ` Michael Walle
2020-03-17 20:50 ` [PATCH 05/18] mfd: Add support for Kontron sl28cpld management controller Michael Walle
2020-03-18  3:28   ` Guenter Roeck
2020-03-18 16:38     ` Michael Walle
2020-03-17 20:50 ` [PATCH 06/18] irqchip: add sl28cpld interrupt controller support Michael Walle
2020-03-18 16:53   ` Guenter Roeck
2020-03-18 17:06     ` Michael Walle
2020-03-18 20:35       ` Guenter Roeck
2020-03-17 20:50 ` [PATCH 07/18] dt-bindings: watchdog: Add bindings for sl28cpld watchdog Michael Walle
2020-03-17 20:50 ` [PATCH 08/18] watchdog: add support " Michael Walle
2020-03-18  3:17   ` Guenter Roeck
2020-03-17 20:50 ` [PATCH 09/18] dt-bindings: pwm: Add bindings for sl28cpld PWM controller Michael Walle
2020-03-17 20:50 ` [PATCH 10/18] pwm: add support " Michael Walle
2020-03-17 20:50 ` [PATCH 11/18] dt-bindings: gpio: Add bindings for sl28cpld GPIO controller Michael Walle
2020-03-17 20:50 ` [PATCH 12/18] gpio: add support for the " Michael Walle
2020-03-18  9:14   ` Bartosz Golaszewski
2020-03-18 12:45     ` Michael Walle
2020-03-25 11:50       ` Bartosz Golaszewski
2020-03-26 20:05         ` Michael Walle
2020-03-27 10:20           ` Linus Walleij
2020-03-27 15:28             ` Michael Walle
2020-03-27 19:01               ` Linus Walleij
2020-03-30 11:21               ` Bartosz Golaszewski
2020-03-30 11:48                 ` Michael Walle
2020-03-28 12:04     ` Michael Walle
2020-03-28 17:20     ` Michael Walle
2020-03-17 20:50 ` [PATCH 13/18] dt-bindings: hwmon: Add bindings for sl28cpld hardware monitoring Michael Walle
2020-03-17 20:50 ` [PATCH 14/18] hwmon: add support for the sl28cpld hardware monitoring controller Michael Walle
2020-03-18  3:27   ` Guenter Roeck
2020-03-18 16:32     ` Michael Walle
2020-03-17 20:50 ` [PATCH 15/18] arm64: dts: freescale: sl28: enable sl28cpld Michael Walle
2020-03-17 20:50 ` [PATCH 16/18] arm64: dts: freescale: sl28: map GPIOs to input events Michael Walle
2020-03-17 20:50 ` [PATCH 17/18] arm64: dts: freescale: sl28: enable LED support Michael Walle
2020-03-17 20:50 ` [PATCH 18/18] arm64: dts: freescale: sl28: enable fan support Michael Walle

Linux-Hwmon Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-hwmon/0 linux-hwmon/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-hwmon linux-hwmon/ https://lore.kernel.org/linux-hwmon \
		linux-hwmon@vger.kernel.org
	public-inbox-index linux-hwmon

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-hwmon


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git