Linux Watchdog driver development
 help / color / mirror / Atom feed
* [PATCH v2 00/11] Cortina Systems CS75xx: interrupt controller, GPIO, I2C and TWD watchdog
@ 2026-10-13  3:46 Fil Dunsky via B4 Relay
  2026-10-13  3:46 ` [PATCH v2 01/11] dt-bindings: interrupt-controller: Add Cortina Systems CS7542 peripheral interrupt controller Fil Dunsky via B4 Relay
                   ` (10 more replies)
  0 siblings, 11 replies; 18+ messages in thread
From: Fil Dunsky via B4 Relay @ 2026-10-13  3:46 UTC (permalink / raw)
  To: Thomas Gleixner, Radu Rendec, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski, Andi Shyti,
	Wim Van Sebroeck, Guenter Roeck, Russell King, Arnd Bergmann,
	Krzysztof Kozlowski
  Cc: linux-arm-kernel, linux-kernel, devicetree, linux-gpio, linux-i2c,
	linux-watchdog, Fil Dunsky

This is the second step of the Cortina Systems CS75xx ("Goldengate G2")
/ Securifi Almond+ support. It applies on top of the platform series
"ARM: Add support for Cortina Systems CS75xx and Securifi Almond+"
v5 [1] and adds:

  - a driver for the per-CPU private watchdog of the ARM11 MPCore,
    Cortex-A5 and Cortex-A9 ("arm,cortex-a9-twd-wdt"),
  - the peripheral interrupt controller that combines the interrupts of
    the low speed peripherals into one GIC interrupt,
  - the GPIO banks,
  - the I2C controller,
  - the device tree nodes, including the reset button of the Almond+,
    and the multi_v7_defconfig and MAINTAINERS updates.

As in the first series there is no public documentation; register
layouts come from the GPL vendor kernels (2.6.36 for the Almond+, 4.9
for the Verizon FiOS G1100 with the same SoC) and were checked on the
board with the out-of-tree OpenWrt port these drivers are based on.

The patches go to different trees and only depend on each other through
the device tree: the irqchip (1-2), GPIO (3-4), I2C (5-6) and watchdog
(7) patches can be picked up independently, the dts, defconfig and
MAINTAINERS patches (8-11) need the platform series.

TWD watchdog:

The binding "arm,cortex-a9-twd-wdt" (watchdog/arm,twd-wdt.yaml) exists
and several dtsi files use it, but there has been no driver since
mpcore_wdt was deleted in v3.11 ("watchdog: delete mpcore_wdt driver").
The reasons given then were that the watchdog is banked per CPU while
the driver only programmed the CPU that happened to run the ioctl, that
it used request_irq() for a per-CPU interrupt, and that it had no users.

This driver does not use the interrupt at all (the watchdog runs in
reset mode) and programs, pings and stops the watchdogs of all online
CPUs with on_each_cpu() under cpus_read_lock(). A CPU hotplug state
arms the watchdog of a CPU coming online while the device is active and
stops it on a CPU going offline, since an offline CPU cannot ping it.
A watchdog found running at probe time on any CPU is reported as
WDOG_HW_RUNNING and reprogrammed with the driver's timeout without
being stopped; the reset flag sets WDIOF_CARDRESET.

On the Almond+ the stock U-Boot arms the watchdog and the board resets
after ~300 s unless it is serviced. With this driver the watchdog core
pings it until userspace takes over.

The driver also registers a restart handler: the CPU that restarts the
system lets its own watchdog expire right away. The platform series has
no other way to restart the CS75xx, so without it "reboot" hangs.

Interrupt controller:

A status register shows the level of 15 sources and an enable register
masks them; the sources are acknowledged in the peripherals. The driver
uses a generic chip on a linear domain and a chained handler. There is
a second status/enable pair right behind it whose purpose I do not know;
it is not described.

GPIO and pin muxing:

Each pin is either a GPIO line or belongs to a peripheral (NAND,
Ethernet, UART, I2C, PCIe reset, ...). The choice is made per pin in one
GPIO_MUX register per bank, and these registers live in the global
control block at 0xf0000000, which also holds the boot straps, the
clock and PLL settings, peripheral resets and the other pin mux
registers.

In this version the GPIO node gets its bank's GPIO_MUX register as a
second reg entry. A line is switched to GPIO when it is requested as a
GPIO or as an interrupt and switched back to the setting found at probe
time when it is released. This keeps the driver self-contained, but it
means that requesting a line that is in use by a peripheral silently
takes the pin away from it; on this board exporting all lines through
sysfs hangs it (NAND and Ethernet pins lose their function). Boards can
list such lines in gpio-reserved-ranges.

I think the proper model is a pin controller for the global control
block: the block becomes a syscon (it is also needed by the clock driver
that will replace the fixed-clock), a pinctrl driver on top of it
describes the pins, the GPIO function and the peripheral groups, the
GPIO nodes get gpio-ranges and the GPIO driver uses
gpiochip_generic_request()/gpiochip_generic_free(). Pinmux ownership
then refuses GPIO requests for pins claimed by a peripheral's pinctrl
state. The remaining pin mux registers of the block are only partially
understood, which is why I did not start with that. Would you accept
the GPIO driver in this form first, with the pinctrl conversion (and a
binding change to gpio-ranges) later, or should the pin controller come
first?

I2C:

The controller transfers one byte per command with an OpenCores-like
command register. The driver supports plain I2C and emulated SMBus
without quick commands (every message carries at least one data byte)
and without 10-bit addresses.

Testing:

Tested on a Securifi Almond+ with next-20260929 plus series 1 v4 and
this series, both CPUs online. The watchdog found the TWD armed by the boot
loader on both CPUs and took it over ("running on CPU0-1, taking
over"); pinged for 60 s from userspace and then left alone, it reset
the board when timeleft reached zero. The I2C and GPIO interrupts are
delivered through the new peripheral interrupt controller; the reset
button (GPIO 37) is claimed by gpio-keys as an active-low input and
reports KEY_RESTART; an I2C read of the touch controller at 0x48 is
ACKed and an empty address returns -ENXIO without timeouts. reboot(2)
issued from CPU1 restarted the board through the restart handler.
The same drivers also run in an OpenWrt image on Linux 6.18 booted from
NAND through the stock U-Boot, where the SX8650 touchscreen uses a
level-triggered GPIO interrupt.

Build tested with multi_v7_defconfig (GCC 14 and Clang 21, W=1),
dt_binding_check and dtbs_check. Not tested: CPU hotplug of the
watchdog (the platform has no cpu_die yet), repeated START between
messages on I2C (only single-message transfers were run), GPIO
interrupts with real edges.

Open questions:

1. GPIO muxing, see above.
2. The TWD watchdog binding marks clocks as optional, the driver
   requires them (the rate is needed to compute the timeout). Should
   the binding be tightened, or should the driver fall back to
   something when there is no clock?
3. Is a separate MAINTAINERS entry for the TWD watchdog driver the
   right thing, or should it be listed under the watchdog subsystem?

[1] https://lore.kernel.org/all/20261013-b4-cs75xx-v5-0-4e72aa383d39@gmail.com/

---
Changes in v2:
- rebase on v5 of the platform series
- gpio, i2c, watchdog: do not include <linux/mod_devicetable.h> (Uwe)
- watchdog: the prescaler cannot be changed in watchdog mode (verified
  on the board), so a watchdog left running by the bootloader with a
  different prescaler is stopped before it is reprogrammed (sashiko-bot)
- i2c: complete a failed transfer only once its stop condition is done,
  so that the stop interrupt cannot race with the next transfer; take
  the lock over the whole interrupt handler; report a NACK of the last
  byte of a write; enable the interrupt again after a reset on timeout;
  fail with -EBUSY when the bus stays busy (sashiko-bot)
- gpio: update the both-edges mask only for a valid trigger type and
  test it under the lock in the interrupt handler; refuse more than 32
  lines (sashiko-bot)
- Link to v1: https://patch.msgid.link/20261012-b4-cs75xx-s2-v1-0-8a7641b7b79c@gmail.com

---
Fil Dunsky (11):
      dt-bindings: interrupt-controller: Add Cortina Systems CS7542 peripheral interrupt controller
      irqchip: Add Cortina Systems CS75xx peripheral interrupt controller
      dt-bindings: gpio: Add Cortina Systems CS7542 GPIO controller
      gpio: Add Cortina Systems CS75xx GPIO driver
      dt-bindings: i2c: Add Cortina Systems CS7542 I2C controller
      i2c: Add Cortina Systems CS75xx I2C controller driver
      watchdog: Add ARM TWD (MPCore private) watchdog driver
      ARM: dts: cortina: cs7542: Add interrupt controller, GPIO and I2C
      ARM: dts: cortina: almondplus: Add reset button and enable I2C
      ARM: multi_v7_defconfig: Enable Cortina Systems CS75xx GPIO, I2C and TWD watchdog
      MAINTAINERS: Add Cortina Systems CS75xx interrupt controller, GPIO and I2C

 .../bindings/gpio/cortina,cs7542-gpio.yaml         |  68 ++++
 .../bindings/i2c/cortina,cs7542-i2c.yaml           |  56 +++
 .../interrupt-controller/cortina,cs7542-intc.yaml  |  57 +++
 MAINTAINERS                                        |  12 +
 .../dts/cortina/cs7542-securifi-almondplus.dts     |  19 +
 arch/arm/boot/dts/cortina/cs7542.dtsi              |  75 ++++
 arch/arm/configs/multi_v7_defconfig                |   3 +
 drivers/gpio/Kconfig                               |  12 +
 drivers/gpio/Makefile                              |   1 +
 drivers/gpio/gpio-cs75xx.c                         | 336 ++++++++++++++++++
 drivers/i2c/busses/Kconfig                         |  11 +
 drivers/i2c/busses/Makefile                        |   1 +
 drivers/i2c/busses/i2c-cs75xx.c                    | 378 ++++++++++++++++++++
 drivers/irqchip/Kconfig                            |  12 +
 drivers/irqchip/Makefile                           |   1 +
 drivers/irqchip/irq-cs75xx.c                       | 103 ++++++
 drivers/watchdog/Kconfig                           |  15 +
 drivers/watchdog/Makefile                          |   1 +
 drivers/watchdog/arm_twd_wdt.c                     | 395 +++++++++++++++++++++
 19 files changed, 1556 insertions(+)
---
base-commit: 6474fa070f2b8013b4b87350b775b8c3be6e8aac
change-id: 20261012-b4-cs75xx-s2-5f548492f51e
prerequisite-message-id: <20261013-b4-cs75xx-v5-0-4e72aa383d39@gmail.com>
prerequisite-patch-id: 104c6fb668f30ac8c2c5b3b6c524c18caa8808c4
prerequisite-patch-id: e7d503348cbec7558cd084162133f8e34df1c1bc
prerequisite-patch-id: 1d5770db02b983d012f6c58806fbf3f5291a06c9
prerequisite-patch-id: 8515988ae3509d4289a8d845803353f5ed412982
prerequisite-patch-id: 6c0137d52f201d8ec085ee6cc7b2d49af15583d8
prerequisite-patch-id: 24e110f3c8c008deffdda079bdc27db729b5014d
prerequisite-patch-id: a0847948f39b5e1d2ae58bf8b7f51e17f3a63247
prerequisite-patch-id: 15c11a668a1a281649fa0713371e7f07ee44917e
prerequisite-patch-id: 9b4a468b7b74861017ef533ba0a5574bc14b212e
prerequisite-patch-id: 1f232c94dd3fc0d862203b7c0c1449e6ba9a6859

Best regards,
--  
Fil Dunsky <filipp.dunsky@gmail.com>



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

* [PATCH v2 01/11] dt-bindings: interrupt-controller: Add Cortina Systems CS7542 peripheral interrupt controller
  2026-10-13  3:46 [PATCH v2 00/11] Cortina Systems CS75xx: interrupt controller, GPIO, I2C and TWD watchdog Fil Dunsky via B4 Relay
@ 2026-10-13  3:46 ` Fil Dunsky via B4 Relay
  2026-10-13  3:47 ` [PATCH v2 02/11] irqchip: Add Cortina Systems CS75xx " Fil Dunsky via B4 Relay
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 18+ messages in thread
From: Fil Dunsky via B4 Relay @ 2026-10-13  3:46 UTC (permalink / raw)
  To: Thomas Gleixner, Radu Rendec, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski, Andi Shyti,
	Wim Van Sebroeck, Guenter Roeck, Russell King, Arnd Bergmann,
	Krzysztof Kozlowski
  Cc: linux-arm-kernel, linux-kernel, devicetree, linux-gpio, linux-i2c,
	linux-watchdog, Fil Dunsky

From: Fil Dunsky <filipp.dunsky@gmail.com>

The low speed peripherals of the Cortina Systems CS75xx SoCs share one
GIC interrupt. A small controller in the peripheral register block
reports which of its 15 sources are asserted and allows masking them.

Signed-off-by: Fil Dunsky <filipp.dunsky@gmail.com>
---
 .../interrupt-controller/cortina,cs7542-intc.yaml  | 57 ++++++++++++++++++++++
 1 file changed, 57 insertions(+)

diff --git a/Documentation/devicetree/bindings/interrupt-controller/cortina,cs7542-intc.yaml b/Documentation/devicetree/bindings/interrupt-controller/cortina,cs7542-intc.yaml
new file mode 100644
index 000000000..2963acaf7
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/cortina,cs7542-intc.yaml
@@ -0,0 +1,57 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/interrupt-controller/cortina,cs7542-intc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Cortina Systems CS7542 peripheral interrupt controller
+
+maintainers:
+  - Fil Dunsky <filipp.dunsky@gmail.com>
+
+description:
+  The low speed peripherals of the Cortina Systems CS75xx (Goldengate G2)
+  SoCs (two software interrupts, two timers, the AXI bridge, SPI, the five
+  GPIO banks, I2C, MDIO, SSP and the random number generator) share one GIC
+  interrupt. This controller has a status register showing the level of the
+  15 sources and an enable register to mask them. The sources are cleared in
+  the peripherals themselves.
+
+properties:
+  compatible:
+    const: cortina,cs7542-intc
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  interrupt-controller: true
+
+  "#interrupt-cells":
+    const: 1
+    description:
+      The number of the source, 0 to 14. All sources are active high level
+      interrupts.
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - interrupt-controller
+  - "#interrupt-cells"
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+    interrupt-controller@f0070004 {
+        compatible = "cortina,cs7542-intc";
+        reg = <0xf0070004 0x8>;
+        interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>;
+        interrupt-controller;
+        #interrupt-cells = <1>;
+    };

-- 
2.53.0



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

* [PATCH v2 02/11] irqchip: Add Cortina Systems CS75xx peripheral interrupt controller
  2026-10-13  3:46 [PATCH v2 00/11] Cortina Systems CS75xx: interrupt controller, GPIO, I2C and TWD watchdog Fil Dunsky via B4 Relay
  2026-10-13  3:46 ` [PATCH v2 01/11] dt-bindings: interrupt-controller: Add Cortina Systems CS7542 peripheral interrupt controller Fil Dunsky via B4 Relay
@ 2026-10-13  3:47 ` Fil Dunsky via B4 Relay
  2026-10-13  3:47 ` [PATCH v2 03/11] dt-bindings: gpio: Add Cortina Systems CS7542 GPIO controller Fil Dunsky via B4 Relay
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 18+ messages in thread
From: Fil Dunsky via B4 Relay @ 2026-10-13  3:47 UTC (permalink / raw)
  To: Thomas Gleixner, Radu Rendec, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski, Andi Shyti,
	Wim Van Sebroeck, Guenter Roeck, Russell King, Arnd Bergmann,
	Krzysztof Kozlowski
  Cc: linux-arm-kernel, linux-kernel, devicetree, linux-gpio, linux-i2c,
	linux-watchdog, Fil Dunsky

From: Fil Dunsky <filipp.dunsky@gmail.com>

The timers, GPIO banks, I2C, MDIO, SPI and a few other low speed
peripherals of the Cortina Systems CS75xx SoCs share a single GIC
interrupt. A status register shows the level of the 15 sources and an
enable register masks them; there is nothing to acknowledge, each source
is cleared in its peripheral.

Add a chained driver using a generic chip on a linear domain.

Signed-off-by: Fil Dunsky <filipp.dunsky@gmail.com>
---
 drivers/irqchip/Kconfig      |  12 +++++
 drivers/irqchip/Makefile     |   1 +
 drivers/irqchip/irq-cs75xx.c | 103 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 116 insertions(+)

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index a8f8c423b..89afb4580 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -187,6 +187,18 @@ config BRCMSTB_L2_IRQ
 	select GENERIC_IRQ_CHIP
 	select IRQ_DOMAIN
 
+config CS75XX_INTC
+	bool "Cortina Systems CS75xx peripheral interrupt controller"
+	depends on ARCH_CORTINA_ACCESS || COMPILE_TEST
+	default ARCH_CORTINA_ACCESS
+	select GENERIC_IRQ_CHIP
+	select IRQ_DOMAIN
+	help
+	  Support for the interrupt controller of the Cortina Systems
+	  CS75xx SoCs that combines the interrupts of the low speed
+	  peripherals (timers, GPIO banks, I2C, MDIO, SPI) into a single
+	  GIC interrupt. Say Y if you have a board with such an SoC.
+
 config DAVINCI_CP_INTC
 	bool
 	select GENERIC_IRQ_CHIP
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index ab33cccd8..38c94bfb3 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -135,3 +135,4 @@ obj-$(CONFIG_APPLE_AIC)			+= irq-apple-aic.o
 obj-$(CONFIG_MCHP_EIC)			+= irq-mchp-eic.o
 obj-$(CONFIG_SOPHGO_SG2042_MSI)		+= irq-sg2042-msi.o
 obj-$(CONFIG_SUNPLUS_SP7021_INTC)	+= irq-sp7021-intc.o
+obj-$(CONFIG_CS75XX_INTC)		+= irq-cs75xx.o
diff --git a/drivers/irqchip/irq-cs75xx.c b/drivers/irqchip/irq-cs75xx.c
new file mode 100644
index 000000000..4d458039a
--- /dev/null
+++ b/drivers/irqchip/irq-cs75xx.c
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Cortina Systems CS75xx peripheral interrupt controller
+ *
+ * The low speed peripherals on the register bus (timers, GPIO banks, I2C,
+ * MDIO, SPI, ...) share a single GIC interrupt. A status register shows
+ * the level of the 15 sources and an enable register masks them; there is
+ * nothing to acknowledge, the sources are cleared in the peripherals.
+ */
+
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+
+#define CS75XX_INTC_STATUS	0x00
+#define CS75XX_INTC_ENABLE	0x04
+
+#define CS75XX_INTC_NR_IRQS	15
+
+static void cs75xx_intc_handler(struct irq_desc *desc)
+{
+	struct irq_domain *domain = irq_desc_get_handler_data(desc);
+	struct irq_chip_generic *gc = irq_get_domain_generic_chip(domain, 0);
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	unsigned long pending;
+	unsigned int hwirq;
+
+	chained_irq_enter(chip, desc);
+
+	pending = readl(gc->reg_base + CS75XX_INTC_STATUS) & gc->mask_cache;
+	for_each_set_bit(hwirq, &pending, CS75XX_INTC_NR_IRQS)
+		generic_handle_domain_irq(domain, hwirq);
+
+	chained_irq_exit(chip, desc);
+}
+
+static int __init cs75xx_intc_init(struct device_node *np,
+				   struct device_node *parent)
+{
+	struct irq_chip_generic *gc;
+	struct irq_domain *domain;
+	void __iomem *base;
+	int parent_irq;
+	int ret;
+
+	base = of_io_request_and_map(np, 0, of_node_full_name(np));
+	if (IS_ERR(base)) {
+		pr_err("%pOF: unable to map registers\n", np);
+		return PTR_ERR(base);
+	}
+
+	/* Mask everything before the chained handler is installed */
+	writel(0, base + CS75XX_INTC_ENABLE);
+
+	parent_irq = irq_of_parse_and_map(np, 0);
+	if (!parent_irq) {
+		pr_err("%pOF: unable to map parent interrupt\n", np);
+		ret = -EINVAL;
+		goto err_unmap;
+	}
+
+	domain = irq_domain_create_linear(of_fwnode_handle(np),
+					  CS75XX_INTC_NR_IRQS,
+					  &irq_generic_chip_ops, NULL);
+	if (!domain) {
+		ret = -ENOMEM;
+		goto err_dispose;
+	}
+
+	ret = irq_alloc_domain_generic_chips(domain, CS75XX_INTC_NR_IRQS, 1,
+					     "cs75xx-intc", handle_level_irq,
+					     IRQ_NOREQUEST | IRQ_NOPROBE, 0,
+					     IRQ_GC_INIT_MASK_CACHE);
+	if (ret)
+		goto err_domain;
+
+	gc = irq_get_domain_generic_chip(domain, 0);
+	gc->reg_base = base;
+	gc->chip_types[0].regs.mask = CS75XX_INTC_ENABLE;
+	gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
+	gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
+
+	irq_set_chained_handler_and_data(parent_irq, cs75xx_intc_handler,
+					 domain);
+
+	return 0;
+
+err_domain:
+	irq_domain_remove(domain);
+err_dispose:
+	irq_dispose_mapping(parent_irq);
+err_unmap:
+	iounmap(base);
+	return ret;
+}
+
+IRQCHIP_DECLARE(cs7542_intc, "cortina,cs7542-intc", cs75xx_intc_init);

-- 
2.53.0



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

* [PATCH v2 03/11] dt-bindings: gpio: Add Cortina Systems CS7542 GPIO controller
  2026-10-13  3:46 [PATCH v2 00/11] Cortina Systems CS75xx: interrupt controller, GPIO, I2C and TWD watchdog Fil Dunsky via B4 Relay
  2026-10-13  3:46 ` [PATCH v2 01/11] dt-bindings: interrupt-controller: Add Cortina Systems CS7542 peripheral interrupt controller Fil Dunsky via B4 Relay
  2026-10-13  3:47 ` [PATCH v2 02/11] irqchip: Add Cortina Systems CS75xx " Fil Dunsky via B4 Relay
@ 2026-10-13  3:47 ` Fil Dunsky via B4 Relay
  2026-10-13  3:53   ` sashiko-bot
  2026-10-13  3:47 ` [PATCH v2 04/11] gpio: Add Cortina Systems CS75xx GPIO driver Fil Dunsky via B4 Relay
                   ` (7 subsequent siblings)
  10 siblings, 1 reply; 18+ messages in thread
From: Fil Dunsky via B4 Relay @ 2026-10-13  3:47 UTC (permalink / raw)
  To: Thomas Gleixner, Radu Rendec, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski, Andi Shyti,
	Wim Van Sebroeck, Guenter Roeck, Russell King, Arnd Bergmann,
	Krzysztof Kozlowski
  Cc: linux-arm-kernel, linux-kernel, devicetree, linux-gpio, linux-i2c,
	linux-watchdog, Fil Dunsky

From: Fil Dunsky <filipp.dunsky@gmail.com>

The Cortina Systems CS75xx SoCs have five banks of 32 GPIO lines. Each
pin is shared between a GPIO line and a peripheral function, selected by
the bank's GPIO_MUX register in the global control block, which is
described as the second register region of the bank.

Signed-off-by: Fil Dunsky <filipp.dunsky@gmail.com>
---
 .../bindings/gpio/cortina,cs7542-gpio.yaml         | 68 ++++++++++++++++++++++
 1 file changed, 68 insertions(+)

diff --git a/Documentation/devicetree/bindings/gpio/cortina,cs7542-gpio.yaml b/Documentation/devicetree/bindings/gpio/cortina,cs7542-gpio.yaml
new file mode 100644
index 000000000..b54421d65
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/cortina,cs7542-gpio.yaml
@@ -0,0 +1,68 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/gpio/cortina,cs7542-gpio.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Cortina Systems CS7542 GPIO controller
+
+maintainers:
+  - Fil Dunsky <filipp.dunsky@gmail.com>
+
+description:
+  The Cortina Systems CS75xx (Goldengate G2) SoCs have five banks of 32
+  GPIO lines, each with its own interrupt. Every pin is shared between a
+  GPIO line and a peripheral function; the choice is made per pin in the
+  GPIO_MUX register of the bank, which is part of the global control
+  block.
+
+properties:
+  compatible:
+    const: cortina,cs7542-gpio
+
+  reg:
+    items:
+      - description: GPIO bank registers
+      - description: GPIO_MUX register of the bank
+
+  interrupts:
+    maxItems: 1
+
+  gpio-controller: true
+
+  "#gpio-cells":
+    const: 2
+
+  gpio-line-names:
+    maxItems: 32
+
+  gpio-reserved-ranges: true
+
+  interrupt-controller: true
+
+  "#interrupt-cells":
+    const: 2
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - gpio-controller
+  - "#gpio-cells"
+  - interrupt-controller
+  - "#interrupt-cells"
+
+additionalProperties: false
+
+examples:
+  - |
+    gpio@f00701f0 {
+        compatible = "cortina,cs7542-gpio";
+        reg = <0xf00701f0 0x20>, <0xf0000020 0x4>;
+        interrupt-parent = <&intc>;
+        interrupts = <7>;
+        gpio-controller;
+        #gpio-cells = <2>;
+        interrupt-controller;
+        #interrupt-cells = <2>;
+    };

-- 
2.53.0



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

* [PATCH v2 04/11] gpio: Add Cortina Systems CS75xx GPIO driver
  2026-10-13  3:46 [PATCH v2 00/11] Cortina Systems CS75xx: interrupt controller, GPIO, I2C and TWD watchdog Fil Dunsky via B4 Relay
                   ` (2 preceding siblings ...)
  2026-10-13  3:47 ` [PATCH v2 03/11] dt-bindings: gpio: Add Cortina Systems CS7542 GPIO controller Fil Dunsky via B4 Relay
@ 2026-10-13  3:47 ` Fil Dunsky via B4 Relay
  2026-10-13  3:58   ` sashiko-bot
  2026-10-13  3:47 ` [PATCH v2 05/11] dt-bindings: i2c: Add Cortina Systems CS7542 I2C controller Fil Dunsky via B4 Relay
                   ` (6 subsequent siblings)
  10 siblings, 1 reply; 18+ messages in thread
From: Fil Dunsky via B4 Relay @ 2026-10-13  3:47 UTC (permalink / raw)
  To: Thomas Gleixner, Radu Rendec, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski, Andi Shyti,
	Wim Van Sebroeck, Guenter Roeck, Russell King, Arnd Bergmann,
	Krzysztof Kozlowski
  Cc: linux-arm-kernel, linux-kernel, devicetree, linux-gpio, linux-i2c,
	linux-watchdog, Fil Dunsky

From: Fil Dunsky <filipp.dunsky@gmail.com>

Add a driver for the GPIO banks of the Cortina Systems CS75xx SoCs. The
data and direction registers are handled by gpio-mmio; each bank has its
own interrupt with level and edge triggers, both edges are emulated by
flipping the polarity.

A pin only works as a GPIO when its bit in the bank's GPIO_MUX register
is set. The driver sets it when a line is requested as a GPIO or as an
interrupt and restores the value found at probe time when the line is
released.

Signed-off-by: Fil Dunsky <filipp.dunsky@gmail.com>
---
 drivers/gpio/Kconfig       |  12 ++
 drivers/gpio/Makefile      |   1 +
 drivers/gpio/gpio-cs75xx.c | 336 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 349 insertions(+)

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index dfdc9ee1a..6b9524f25 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -300,6 +300,18 @@ config GPIO_CLPS711X
 	help
 	  Say yes here to support GPIO on CLPS711X SoCs.
 
+config GPIO_CS75XX
+	tristate "Cortina Systems CS75xx GPIO support"
+	depends on ARCH_CORTINA_ACCESS || COMPILE_TEST
+	select GPIO_GENERIC
+	select GPIOLIB_IRQCHIP
+	help
+	  Say yes here to support the GPIO banks of the Cortina Systems
+	  CS75xx SoCs, including their interrupts.
+
+	  Requesting a line switches its pin from the peripheral function
+	  to GPIO until the line is released.
+
 config GPIO_DAVINCI
 	tristate "TI Davinci/Keystone GPIO support"
 	default y if ARCH_DAVINCI
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 88f748846..a38ad4331 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -59,6 +59,7 @@ obj-$(CONFIG_GPIO_CLPS711X)		+= gpio-clps711x.o
 obj-$(CONFIG_GPIO_SNPS_CREG)		+= gpio-creg-snps.o
 obj-$(CONFIG_GPIO_CROS_EC)		+= gpio-cros-ec.o
 obj-$(CONFIG_GPIO_CRYSTAL_COVE)		+= gpio-crystalcove.o
+obj-$(CONFIG_GPIO_CS75XX)		+= gpio-cs75xx.o
 obj-$(CONFIG_GPIO_CS5535)		+= gpio-cs5535.o
 obj-$(CONFIG_GPIO_DA9052)		+= gpio-da9052.o
 obj-$(CONFIG_GPIO_DA9055)		+= gpio-da9055.o
diff --git a/drivers/gpio/gpio-cs75xx.c b/drivers/gpio/gpio-cs75xx.c
new file mode 100644
index 000000000..d3e8d3a99
--- /dev/null
+++ b/drivers/gpio/gpio-cs75xx.c
@@ -0,0 +1,336 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Cortina Systems CS75xx GPIO controller
+ *
+ * Each bank has 32 lines with direction, data and interrupt registers.
+ * Whether a pin is driven by the GPIO bank or by its peripheral function
+ * (NAND, Ethernet, UART, ...) is selected by the bank's GPIO_MUX register
+ * in the global control block. A line is switched to GPIO when it is
+ * requested as a GPIO or as an interrupt and switched back to its initial
+ * setting when it is released.
+ */
+
+#include <linux/bitops.h>
+#include <linux/gpio/driver.h>
+#include <linux/gpio/generic.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#define CS75XX_GPIO_CFG		0x00	/* 1: input, 0: output */
+#define CS75XX_GPIO_OUT		0x04
+#define CS75XX_GPIO_IN		0x08
+#define CS75XX_GPIO_LVL		0x0c	/* 1: high level or rising edge */
+#define CS75XX_GPIO_EDGE	0x10	/* 1: edge, 0: level */
+#define CS75XX_GPIO_IE		0x14
+#define CS75XX_GPIO_INT		0x18	/* write 1 to clear */
+
+struct cs75xx_gpio {
+	struct gpio_generic_chip chip;
+	void __iomem *base;
+	void __iomem *mux;
+	u32 mux_init;
+	u32 gpio_used;
+	u32 irq_used;
+	u32 both_edges;
+};
+
+/* Called with the chip lock held */
+static void cs75xx_gpio_update_mux(struct cs75xx_gpio *g, unsigned int offset)
+{
+	u32 mask = BIT(offset);
+	u32 val = readl(g->mux);
+
+	if ((g->gpio_used | g->irq_used) & mask)
+		val |= mask;
+	else
+		val = (val & ~mask) | (g->mux_init & mask);
+
+	writel(val, g->mux);
+}
+
+static int cs75xx_gpio_request(struct gpio_chip *gc, unsigned int offset)
+{
+	struct cs75xx_gpio *g = gpiochip_get_data(gc);
+
+	guard(gpio_generic_lock_irqsave)(&g->chip);
+	g->gpio_used |= BIT(offset);
+	cs75xx_gpio_update_mux(g, offset);
+
+	return 0;
+}
+
+static void cs75xx_gpio_free(struct gpio_chip *gc, unsigned int offset)
+{
+	struct cs75xx_gpio *g = gpiochip_get_data(gc);
+
+	guard(gpio_generic_lock_irqsave)(&g->chip);
+	g->gpio_used &= ~BIT(offset);
+	cs75xx_gpio_update_mux(g, offset);
+}
+
+static void cs75xx_gpio_irq_ack(struct irq_data *d)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+	struct cs75xx_gpio *g = gpiochip_get_data(gc);
+
+	writel(BIT(irqd_to_hwirq(d)), g->base + CS75XX_GPIO_INT);
+}
+
+static void cs75xx_gpio_irq_mask(struct irq_data *d)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+	struct cs75xx_gpio *g = gpiochip_get_data(gc);
+	irq_hw_number_t hwirq = irqd_to_hwirq(d);
+
+	scoped_guard(gpio_generic_lock_irqsave, &g->chip) {
+		u32 ie = readl(g->base + CS75XX_GPIO_IE);
+
+		writel(ie & ~BIT(hwirq), g->base + CS75XX_GPIO_IE);
+	}
+
+	gpiochip_disable_irq(gc, hwirq);
+}
+
+static void cs75xx_gpio_irq_unmask(struct irq_data *d)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+	struct cs75xx_gpio *g = gpiochip_get_data(gc);
+	irq_hw_number_t hwirq = irqd_to_hwirq(d);
+	u32 ie;
+
+	gpiochip_enable_irq(gc, hwirq);
+
+	guard(gpio_generic_lock_irqsave)(&g->chip);
+	/*
+	 * The status of a level interrupt is latched while the line is
+	 * asserted, also after the ack, so it may be stale by now. Clear it,
+	 * it is set again right away if the line is still asserted.
+	 */
+	if (irqd_is_level_type(d))
+		writel(BIT(hwirq), g->base + CS75XX_GPIO_INT);
+	ie = readl(g->base + CS75XX_GPIO_IE);
+	writel(ie | BIT(hwirq), g->base + CS75XX_GPIO_IE);
+}
+
+/* Both edges are emulated by flipping the polarity on every interrupt */
+static void cs75xx_gpio_set_polarity(struct cs75xx_gpio *g, u32 mask)
+{
+	u32 lvl = readl(g->base + CS75XX_GPIO_LVL);
+
+	if (readl(g->base + CS75XX_GPIO_IN) & mask)
+		lvl &= ~mask;
+	else
+		lvl |= mask;
+
+	writel(lvl, g->base + CS75XX_GPIO_LVL);
+}
+
+static int cs75xx_gpio_irq_set_type(struct irq_data *d, unsigned int type)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+	struct cs75xx_gpio *g = gpiochip_get_data(gc);
+	u32 mask = BIT(irqd_to_hwirq(d));
+	u32 lvl, edge, both_edges;
+
+	guard(gpio_generic_lock_irqsave)(&g->chip);
+
+	lvl = readl(g->base + CS75XX_GPIO_LVL);
+	edge = readl(g->base + CS75XX_GPIO_EDGE);
+	both_edges = g->both_edges & ~mask;
+
+	switch (type & IRQ_TYPE_SENSE_MASK) {
+	case IRQ_TYPE_LEVEL_LOW:
+		lvl &= ~mask;
+		edge &= ~mask;
+		break;
+	case IRQ_TYPE_LEVEL_HIGH:
+		lvl |= mask;
+		edge &= ~mask;
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		lvl &= ~mask;
+		edge |= mask;
+		break;
+	case IRQ_TYPE_EDGE_RISING:
+		lvl |= mask;
+		edge |= mask;
+		break;
+	case IRQ_TYPE_EDGE_BOTH:
+		both_edges |= mask;
+		edge |= mask;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	g->both_edges = both_edges;
+	writel(lvl, g->base + CS75XX_GPIO_LVL);
+	writel(edge, g->base + CS75XX_GPIO_EDGE);
+	if (g->both_edges & mask)
+		cs75xx_gpio_set_polarity(g, mask);
+
+	if (type & IRQ_TYPE_LEVEL_MASK)
+		irq_set_handler_locked(d, handle_level_irq);
+	else
+		irq_set_handler_locked(d, handle_edge_irq);
+
+	return 0;
+}
+
+static int cs75xx_gpio_irq_reqres(struct irq_data *d)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+	struct cs75xx_gpio *g = gpiochip_get_data(gc);
+	irq_hw_number_t hwirq = irqd_to_hwirq(d);
+	int ret;
+
+	/* The boot loader may leave the line configured as an output */
+	ret = gc->direction_input(gc, hwirq);
+	if (ret)
+		return ret;
+
+	ret = gpiochip_reqres_irq(gc, hwirq);
+	if (ret)
+		return ret;
+
+	guard(gpio_generic_lock_irqsave)(&g->chip);
+	g->irq_used |= BIT(hwirq);
+	cs75xx_gpio_update_mux(g, hwirq);
+
+	return 0;
+}
+
+static void cs75xx_gpio_irq_relres(struct irq_data *d)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+	struct cs75xx_gpio *g = gpiochip_get_data(gc);
+	irq_hw_number_t hwirq = irqd_to_hwirq(d);
+
+	scoped_guard(gpio_generic_lock_irqsave, &g->chip) {
+		g->irq_used &= ~BIT(hwirq);
+		cs75xx_gpio_update_mux(g, hwirq);
+	}
+
+	gpiochip_relres_irq(gc, hwirq);
+}
+
+static const struct irq_chip cs75xx_gpio_irq_chip = {
+	.name			= "cs75xx-gpio",
+	.irq_ack		= cs75xx_gpio_irq_ack,
+	.irq_mask		= cs75xx_gpio_irq_mask,
+	.irq_unmask		= cs75xx_gpio_irq_unmask,
+	.irq_set_type		= cs75xx_gpio_irq_set_type,
+	.irq_request_resources	= cs75xx_gpio_irq_reqres,
+	.irq_release_resources	= cs75xx_gpio_irq_relres,
+	.flags			= IRQCHIP_IMMUTABLE | IRQCHIP_MASK_ON_SUSPEND,
+};
+
+static void cs75xx_gpio_irq_handler(struct irq_desc *desc)
+{
+	struct gpio_chip *gc = irq_desc_get_handler_data(desc);
+	struct cs75xx_gpio *g = gpiochip_get_data(gc);
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	unsigned long pending;
+	unsigned int offset;
+
+	chained_irq_enter(chip, desc);
+
+	pending = readl(g->base + CS75XX_GPIO_INT) &
+		  readl(g->base + CS75XX_GPIO_IE);
+
+	for_each_set_bit(offset, &pending, gc->ngpio) {
+		scoped_guard(gpio_generic_lock, &g->chip) {
+			if (g->both_edges & BIT(offset))
+				cs75xx_gpio_set_polarity(g, BIT(offset));
+		}
+		generic_handle_domain_irq(gc->irq.domain, offset);
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static int cs75xx_gpio_probe(struct platform_device *pdev)
+{
+	struct gpio_generic_chip_config config;
+	struct device *dev = &pdev->dev;
+	struct gpio_irq_chip *girq;
+	struct cs75xx_gpio *g;
+	int irq, ret;
+
+	g = devm_kzalloc(dev, sizeof(*g), GFP_KERNEL);
+	if (!g)
+		return -ENOMEM;
+
+	g->base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(g->base))
+		return PTR_ERR(g->base);
+
+	g->mux = devm_platform_ioremap_resource(pdev, 1);
+	if (IS_ERR(g->mux))
+		return PTR_ERR(g->mux);
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	g->mux_init = readl(g->mux);
+
+	config = (struct gpio_generic_chip_config) {
+		.dev = dev,
+		.sz = 4,
+		.dat = g->base + CS75XX_GPIO_IN,
+		.set = g->base + CS75XX_GPIO_OUT,
+		.dirin = g->base + CS75XX_GPIO_CFG,
+	};
+
+	ret = gpio_generic_chip_init(&g->chip, &config);
+	if (ret)
+		return dev_err_probe(dev, ret, "unable to init generic GPIO\n");
+
+	if (g->chip.gc.ngpio > 32)
+		return dev_err_probe(dev, -EINVAL, "too many lines: %u\n",
+				     g->chip.gc.ngpio);
+
+	g->chip.gc.request = cs75xx_gpio_request;
+	g->chip.gc.free = cs75xx_gpio_free;
+	g->chip.gc.owner = THIS_MODULE;
+
+	/* Mask and clear all interrupts before the parent is set up */
+	writel(0, g->base + CS75XX_GPIO_IE);
+	writel(~0, g->base + CS75XX_GPIO_INT);
+
+	girq = &g->chip.gc.irq;
+	gpio_irq_chip_set_chip(girq, &cs75xx_gpio_irq_chip);
+	girq->parent_handler = cs75xx_gpio_irq_handler;
+	girq->num_parents = 1;
+	girq->parents = devm_kcalloc(dev, 1, sizeof(*girq->parents),
+				     GFP_KERNEL);
+	if (!girq->parents)
+		return -ENOMEM;
+	girq->parents[0] = irq;
+	girq->default_type = IRQ_TYPE_NONE;
+	girq->handler = handle_bad_irq;
+
+	return devm_gpiochip_add_data(dev, &g->chip.gc, g);
+}
+
+static const struct of_device_id cs75xx_gpio_of_match[] = {
+	{ .compatible = "cortina,cs7542-gpio" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, cs75xx_gpio_of_match);
+
+static struct platform_driver cs75xx_gpio_driver = {
+	.probe = cs75xx_gpio_probe,
+	.driver = {
+		.name = "cs75xx-gpio",
+		.of_match_table = cs75xx_gpio_of_match,
+	},
+};
+module_platform_driver(cs75xx_gpio_driver);
+
+MODULE_DESCRIPTION("Cortina Systems CS75xx GPIO driver");
+MODULE_LICENSE("GPL");

-- 
2.53.0



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

* [PATCH v2 05/11] dt-bindings: i2c: Add Cortina Systems CS7542 I2C controller
  2026-10-13  3:46 [PATCH v2 00/11] Cortina Systems CS75xx: interrupt controller, GPIO, I2C and TWD watchdog Fil Dunsky via B4 Relay
                   ` (3 preceding siblings ...)
  2026-10-13  3:47 ` [PATCH v2 04/11] gpio: Add Cortina Systems CS75xx GPIO driver Fil Dunsky via B4 Relay
@ 2026-10-13  3:47 ` Fil Dunsky via B4 Relay
  2026-10-13  3:47 ` [PATCH v2 06/11] i2c: Add Cortina Systems CS75xx I2C controller driver Fil Dunsky via B4 Relay
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 18+ messages in thread
From: Fil Dunsky via B4 Relay @ 2026-10-13  3:47 UTC (permalink / raw)
  To: Thomas Gleixner, Radu Rendec, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski, Andi Shyti,
	Wim Van Sebroeck, Guenter Roeck, Russell King, Arnd Bergmann,
	Krzysztof Kozlowski
  Cc: linux-arm-kernel, linux-kernel, devicetree, linux-gpio, linux-i2c,
	linux-watchdog, Fil Dunsky

From: Fil Dunsky <filipp.dunsky@gmail.com>

Add a binding for the I2C ("Bi-Wire") controller of the Cortina Systems
CS75xx SoCs.

Signed-off-by: Fil Dunsky <filipp.dunsky@gmail.com>
---
 .../bindings/i2c/cortina,cs7542-i2c.yaml           | 56 ++++++++++++++++++++++
 1 file changed, 56 insertions(+)

diff --git a/Documentation/devicetree/bindings/i2c/cortina,cs7542-i2c.yaml b/Documentation/devicetree/bindings/i2c/cortina,cs7542-i2c.yaml
new file mode 100644
index 000000000..f70984bd7
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/cortina,cs7542-i2c.yaml
@@ -0,0 +1,56 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/i2c/cortina,cs7542-i2c.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Cortina Systems CS7542 I2C controller
+
+maintainers:
+  - Fil Dunsky <filipp.dunsky@gmail.com>
+
+description:
+  The I2C ("Bi-Wire") controller of the Cortina Systems CS75xx (Goldengate
+  G2) SoCs. It transfers one byte per command and runs from the peripheral
+  (APB) clock through a 16-bit prescaler.
+
+allOf:
+  - $ref: /schemas/i2c/i2c-controller.yaml#
+
+properties:
+  compatible:
+    const: cortina,cs7542-i2c
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  clock-frequency:
+    enum: [100000, 400000]
+    default: 100000
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    i2c@f00700e8 {
+        compatible = "cortina,cs7542-i2c";
+        reg = <0xf00700e8 0x28>;
+        interrupt-parent = <&intc>;
+        interrupts = <11>;
+        clocks = <&apb_clk>;
+        clock-frequency = <400000>;
+        #address-cells = <1>;
+        #size-cells = <0>;
+    };

-- 
2.53.0



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

* [PATCH v2 06/11] i2c: Add Cortina Systems CS75xx I2C controller driver
  2026-10-13  3:46 [PATCH v2 00/11] Cortina Systems CS75xx: interrupt controller, GPIO, I2C and TWD watchdog Fil Dunsky via B4 Relay
                   ` (4 preceding siblings ...)
  2026-10-13  3:47 ` [PATCH v2 05/11] dt-bindings: i2c: Add Cortina Systems CS7542 I2C controller Fil Dunsky via B4 Relay
@ 2026-10-13  3:47 ` Fil Dunsky via B4 Relay
  2026-10-13  4:04   ` sashiko-bot
  2026-10-13  3:47 ` [PATCH v2 07/11] watchdog: Add ARM TWD (MPCore private) watchdog driver Fil Dunsky via B4 Relay
                   ` (4 subsequent siblings)
  10 siblings, 1 reply; 18+ messages in thread
From: Fil Dunsky via B4 Relay @ 2026-10-13  3:47 UTC (permalink / raw)
  To: Thomas Gleixner, Radu Rendec, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski, Andi Shyti,
	Wim Van Sebroeck, Guenter Roeck, Russell King, Arnd Bergmann,
	Krzysztof Kozlowski
  Cc: linux-arm-kernel, linux-kernel, devicetree, linux-gpio, linux-i2c,
	linux-watchdog, Fil Dunsky

From: Fil Dunsky <filipp.dunsky@gmail.com>

The I2C ("Bi-Wire") controller of the Cortina Systems CS75xx SoCs
transfers one byte per command, with a command register much like the
OpenCores one (start, stop, read, write, acknowledge), and raises an
interrupt when a command has completed.

Messages are sent with a repeated start between them and a stop after
the last one. Every message needs at least one data byte, so SMBus quick
commands are not supported. Arbitration loss is reported as -EAGAIN and
retried by the core.

Signed-off-by: Fil Dunsky <filipp.dunsky@gmail.com>
---
 drivers/i2c/busses/Kconfig      |  11 ++
 drivers/i2c/busses/Makefile     |   1 +
 drivers/i2c/busses/i2c-cs75xx.c | 378 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 390 insertions(+)

diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index d7b895083..a65e951a5 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -520,6 +520,17 @@ config I2C_CADENCE
 	  Say yes here to select Cadence I2C Host Controller. This controller is
 	  e.g. used by Xilinx Zynq.
 
+config I2C_CS75XX
+	tristate "Cortina Systems CS75xx I2C controller"
+	depends on ARCH_CORTINA_ACCESS || COMPILE_TEST
+	depends on HAS_IOMEM
+	help
+	  Say yes here to support the I2C ("Bi-Wire") controller of the
+	  Cortina Systems CS75xx SoCs.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called i2c-cs75xx.
+
 config I2C_CBUS_GPIO
 	tristate "CBUS I2C driver"
 	depends on GPIOLIB || COMPILE_TEST
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 3755c54b3..aab1beeb2 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -47,6 +47,7 @@ obj-$(CONFIG_I2C_BCM2835)	+= i2c-bcm2835.o
 obj-$(CONFIG_I2C_BCM_IPROC)	+= i2c-bcm-iproc.o
 obj-$(CONFIG_I2C_CADENCE)	+= i2c-cadence.o
 obj-$(CONFIG_I2C_CBUS_GPIO)	+= i2c-cbus-gpio.o
+obj-$(CONFIG_I2C_CS75XX)	+= i2c-cs75xx.o
 obj-$(CONFIG_I2C_CGBC)		+= i2c-cgbc.o
 obj-$(CONFIG_I2C_CPM)		+= i2c-cpm.o
 obj-$(CONFIG_I2C_DAVINCI)	+= i2c-davinci.o
diff --git a/drivers/i2c/busses/i2c-cs75xx.c b/drivers/i2c/busses/i2c-cs75xx.c
new file mode 100644
index 000000000..3eca5fb14
--- /dev/null
+++ b/drivers/i2c/busses/i2c-cs75xx.c
@@ -0,0 +1,378 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Cortina Systems CS75xx I2C ("Bi-Wire") controller
+ *
+ * The controller works one byte at a time: software writes the byte to
+ * transmit and a command (start, stop, read, write, acknowledge) and gets
+ * an interrupt when the command has completed.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/spinlock.h>
+
+#define CS75XX_I2C_CFG		0x00
+#define CS75XX_I2C_CTRL		0x04
+#define CS75XX_I2C_TXR		0x08
+#define CS75XX_I2C_RXR		0x0c
+#define CS75XX_I2C_ACK		0x10
+#define CS75XX_I2C_IE		0x14
+#define CS75XX_I2C_INT		0x18
+
+#define CS75XX_I2C_CFG_EN	BIT(0)
+#define CS75XX_I2C_CFG_PRER	GENMASK(31, 16)
+
+#define CS75XX_I2C_CTRL_DONE	BIT(0)
+#define CS75XX_I2C_CTRL_NACK	BIT(3)
+#define CS75XX_I2C_CTRL_WRITE	BIT(4)
+#define CS75XX_I2C_CTRL_READ	BIT(5)
+#define CS75XX_I2C_CTRL_STOP	BIT(6)
+#define CS75XX_I2C_CTRL_START	BIT(7)
+
+#define CS75XX_I2C_ACK_AL	BIT(0)
+#define CS75XX_I2C_ACK_BUSY	BIT(1)
+#define CS75XX_I2C_ACK_NACK	BIT(2)
+
+#define CS75XX_I2C_INT_DONE	BIT(0)
+
+enum cs75xx_i2c_state {
+	CS75XX_I2C_IDLE,
+	CS75XX_I2C_ADDR,
+	CS75XX_I2C_WRITE,
+	CS75XX_I2C_READ,
+	CS75XX_I2C_STOP,
+};
+
+struct cs75xx_i2c {
+	struct i2c_adapter adap;
+	struct device *dev;
+	void __iomem *base;
+	unsigned long ref_rate;
+	u32 bus_rate;
+
+	spinlock_t lock;	/* protects the transfer state */
+	struct completion done;
+	enum cs75xx_i2c_state state;
+	struct i2c_msg *msg;
+	unsigned int pos;
+	bool last;
+	int err;
+};
+
+static void cs75xx_i2c_hw_init(struct cs75xx_i2c *i2c)
+{
+	u32 prer = DIV_ROUND_UP(i2c->ref_rate, 5 * i2c->bus_rate) - 1;
+
+	if (readl(i2c->base + CS75XX_I2C_CFG) & CS75XX_I2C_CFG_EN) {
+		writel(0, i2c->base + CS75XX_I2C_CFG);
+		usleep_range(5000, 10000);
+	}
+
+	writel(FIELD_PREP(CS75XX_I2C_CFG_PRER, prer) | CS75XX_I2C_CFG_EN,
+	       i2c->base + CS75XX_I2C_CFG);
+	usleep_range(5000, 10000);
+}
+
+/* Reset the controller and enable its interrupt again */
+static void cs75xx_i2c_reset(struct cs75xx_i2c *i2c)
+{
+	cs75xx_i2c_hw_init(i2c);
+	writel(CS75XX_I2C_INT_DONE, i2c->base + CS75XX_I2C_INT);
+	writel(CS75XX_I2C_INT_DONE, i2c->base + CS75XX_I2C_IE);
+}
+
+static void cs75xx_i2c_finish(struct cs75xx_i2c *i2c, int err)
+{
+	i2c->err = err;
+	i2c->state = CS75XX_I2C_IDLE;
+	complete(&i2c->done);
+}
+
+/*
+ * End the transfer with a stop condition and complete it only once the
+ * stop is done, so that its interrupt cannot be taken for the next one
+ */
+static void cs75xx_i2c_abort(struct cs75xx_i2c *i2c, int err)
+{
+	i2c->err = err;
+	i2c->state = CS75XX_I2C_STOP;
+	writel(CS75XX_I2C_CTRL_STOP, i2c->base + CS75XX_I2C_CTRL);
+}
+
+/* Start the transfer of the next data byte of the message */
+static void cs75xx_i2c_next_byte(struct cs75xx_i2c *i2c)
+{
+	struct i2c_msg *msg = i2c->msg;
+	bool last_byte = i2c->pos == msg->len - 1;
+	u32 cmd;
+
+	if (msg->flags & I2C_M_RD) {
+		i2c->state = CS75XX_I2C_READ;
+		cmd = CS75XX_I2C_CTRL_READ;
+		/* Do not acknowledge the last byte of a read */
+		if (last_byte)
+			cmd |= CS75XX_I2C_CTRL_NACK;
+	} else {
+		i2c->state = CS75XX_I2C_WRITE;
+		writel(msg->buf[i2c->pos++], i2c->base + CS75XX_I2C_TXR);
+		cmd = CS75XX_I2C_CTRL_WRITE;
+	}
+
+	if (last_byte && i2c->last)
+		cmd |= CS75XX_I2C_CTRL_STOP;
+
+	writel(cmd, i2c->base + CS75XX_I2C_CTRL);
+}
+
+static void cs75xx_i2c_process(struct cs75xx_i2c *i2c, u32 ack)
+{
+	struct i2c_msg *msg = i2c->msg;
+
+	if (i2c->state == CS75XX_I2C_STOP) {
+		cs75xx_i2c_finish(i2c, i2c->err);
+		return;
+	}
+
+	if (ack & CS75XX_I2C_ACK_AL) {
+		cs75xx_i2c_abort(i2c, -EAGAIN);
+		return;
+	}
+
+	switch (i2c->state) {
+	case CS75XX_I2C_ADDR:
+		if (ack & CS75XX_I2C_ACK_NACK) {
+			cs75xx_i2c_abort(i2c, -ENXIO);
+			return;
+		}
+		cs75xx_i2c_next_byte(i2c);
+		break;
+
+	case CS75XX_I2C_WRITE:
+		if (ack & CS75XX_I2C_ACK_NACK) {
+			/* The last byte of the last message carried the stop */
+			if (i2c->pos == msg->len && i2c->last)
+				cs75xx_i2c_finish(i2c, -EIO);
+			else
+				cs75xx_i2c_abort(i2c, -EIO);
+			return;
+		}
+		if (i2c->pos == msg->len) {
+			cs75xx_i2c_finish(i2c, 0);
+			return;
+		}
+		cs75xx_i2c_next_byte(i2c);
+		break;
+
+	case CS75XX_I2C_READ:
+		msg->buf[i2c->pos++] = readl(i2c->base + CS75XX_I2C_RXR);
+		if (i2c->pos == msg->len) {
+			cs75xx_i2c_finish(i2c, 0);
+			return;
+		}
+		cs75xx_i2c_next_byte(i2c);
+		break;
+
+	case CS75XX_I2C_IDLE:
+	case CS75XX_I2C_STOP:
+		/* Late interrupt of a transfer that timed out */
+		break;
+	}
+}
+
+static irqreturn_t cs75xx_i2c_isr(int irq, void *dev_id)
+{
+	struct cs75xx_i2c *i2c = dev_id;
+	u32 ctrl, ack;
+
+	guard(spinlock)(&i2c->lock);
+
+	if (!(readl(i2c->base + CS75XX_I2C_INT) & CS75XX_I2C_INT_DONE))
+		return IRQ_NONE;
+
+	writel(CS75XX_I2C_INT_DONE, i2c->base + CS75XX_I2C_INT);
+
+	/* The done flag is cleared by writing it back */
+	ctrl = readl(i2c->base + CS75XX_I2C_CTRL);
+	writel(ctrl, i2c->base + CS75XX_I2C_CTRL);
+
+	ack = readl(i2c->base + CS75XX_I2C_ACK);
+	cs75xx_i2c_process(i2c, ack);
+
+	return IRQ_HANDLED;
+}
+
+static int cs75xx_i2c_xfer_msg(struct cs75xx_i2c *i2c, struct i2c_msg *msg,
+			       bool last)
+{
+	unsigned long time_left;
+
+	spin_lock_irq(&i2c->lock);
+	reinit_completion(&i2c->done);
+	i2c->msg = msg;
+	i2c->pos = 0;
+	i2c->last = last;
+	i2c->err = 0;
+	i2c->state = CS75XX_I2C_ADDR;
+	writel(i2c_8bit_addr_from_msg(msg), i2c->base + CS75XX_I2C_TXR);
+	writel(CS75XX_I2C_CTRL_START | CS75XX_I2C_CTRL_WRITE,
+	       i2c->base + CS75XX_I2C_CTRL);
+	spin_unlock_irq(&i2c->lock);
+
+	time_left = wait_for_completion_timeout(&i2c->done, i2c->adap.timeout);
+	if (!time_left) {
+		spin_lock_irq(&i2c->lock);
+		i2c->state = CS75XX_I2C_IDLE;
+		spin_unlock_irq(&i2c->lock);
+		dev_err(i2c->dev, "transfer timed out, resetting the controller\n");
+		cs75xx_i2c_reset(i2c);
+		return -ETIMEDOUT;
+	}
+
+	return i2c->err;
+}
+
+static int cs75xx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+			   int num)
+{
+	struct cs75xx_i2c *i2c = i2c_get_adapdata(adap);
+	u32 val;
+	int i, ret;
+
+	if (readl_poll_timeout(i2c->base + CS75XX_I2C_ACK, val,
+			       !(val & CS75XX_I2C_ACK_BUSY), 10, 1000)) {
+		cs75xx_i2c_reset(i2c);
+		if (readl(i2c->base + CS75XX_I2C_ACK) & CS75XX_I2C_ACK_BUSY)
+			return -EBUSY;
+	}
+
+	for (i = 0; i < num; i++) {
+		ret = cs75xx_i2c_xfer_msg(i2c, &msgs[i], i == num - 1);
+		if (ret)
+			return ret;
+	}
+
+	return num;
+}
+
+static u32 cs75xx_i2c_func(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
+}
+
+static const struct i2c_algorithm cs75xx_i2c_algo = {
+	.xfer = cs75xx_i2c_xfer,
+	.functionality = cs75xx_i2c_func,
+};
+
+/* A message always carries at least one data byte after the address */
+static const struct i2c_adapter_quirks cs75xx_i2c_quirks = {
+	.flags = I2C_AQ_NO_ZERO_LEN,
+};
+
+static void cs75xx_i2c_disable(void *data)
+{
+	struct cs75xx_i2c *i2c = data;
+
+	writel(0, i2c->base + CS75XX_I2C_IE);
+	writel(0, i2c->base + CS75XX_I2C_CFG);
+}
+
+static int cs75xx_i2c_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct cs75xx_i2c *i2c;
+	struct clk *clk;
+	int irq, ret;
+
+	i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
+	if (!i2c)
+		return -ENOMEM;
+
+	i2c->dev = dev;
+	spin_lock_init(&i2c->lock);
+	init_completion(&i2c->done);
+
+	i2c->base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(i2c->base))
+		return PTR_ERR(i2c->base);
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	clk = devm_clk_get_enabled(dev, NULL);
+	if (IS_ERR(clk))
+		return dev_err_probe(dev, PTR_ERR(clk), "failed to get clock\n");
+
+	i2c->ref_rate = clk_get_rate(clk);
+	if (device_property_read_u32(dev, "clock-frequency", &i2c->bus_rate))
+		i2c->bus_rate = I2C_MAX_STANDARD_MODE_FREQ;
+	if (!i2c->ref_rate || !i2c->bus_rate ||
+	    i2c->bus_rate > I2C_MAX_FAST_MODE_FREQ ||
+	    i2c->ref_rate / (5 * i2c->bus_rate) > FIELD_MAX(CS75XX_I2C_CFG_PRER) + 1)
+		return dev_err_probe(dev, -EINVAL,
+				     "unsupported bus frequency %u Hz\n",
+				     i2c->bus_rate);
+
+	cs75xx_i2c_hw_init(i2c);
+
+	writel(0, i2c->base + CS75XX_I2C_IE);
+	writel(CS75XX_I2C_INT_DONE, i2c->base + CS75XX_I2C_INT);
+
+	ret = devm_add_action_or_reset(dev, cs75xx_i2c_disable, i2c);
+	if (ret)
+		return ret;
+
+	ret = devm_request_irq(dev, irq, cs75xx_i2c_isr, 0, dev_name(dev), i2c);
+	if (ret)
+		return ret;
+
+	writel(CS75XX_I2C_INT_DONE, i2c->base + CS75XX_I2C_IE);
+
+	i2c->adap.owner = THIS_MODULE;
+	i2c->adap.algo = &cs75xx_i2c_algo;
+	i2c->adap.quirks = &cs75xx_i2c_quirks;
+	i2c->adap.retries = 3;
+	i2c->adap.dev.parent = dev;
+	i2c->adap.dev.of_node = dev->of_node;
+	strscpy(i2c->adap.name, dev_name(dev), sizeof(i2c->adap.name));
+	i2c_set_adapdata(&i2c->adap, i2c);
+
+	ret = devm_i2c_add_adapter(dev, &i2c->adap);
+	if (ret)
+		return ret;
+
+	dev_dbg(dev, "bus %u Hz, reference %lu Hz\n", i2c->bus_rate,
+		i2c->ref_rate);
+
+	return 0;
+}
+
+static const struct of_device_id cs75xx_i2c_of_match[] = {
+	{ .compatible = "cortina,cs7542-i2c" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, cs75xx_i2c_of_match);
+
+static struct platform_driver cs75xx_i2c_driver = {
+	.probe = cs75xx_i2c_probe,
+	.driver = {
+		.name = "cs75xx-i2c",
+		.of_match_table = cs75xx_i2c_of_match,
+	},
+};
+module_platform_driver(cs75xx_i2c_driver);
+
+MODULE_DESCRIPTION("Cortina Systems CS75xx I2C controller driver");
+MODULE_LICENSE("GPL");

-- 
2.53.0



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

* [PATCH v2 07/11] watchdog: Add ARM TWD (MPCore private) watchdog driver
  2026-10-13  3:46 [PATCH v2 00/11] Cortina Systems CS75xx: interrupt controller, GPIO, I2C and TWD watchdog Fil Dunsky via B4 Relay
                   ` (5 preceding siblings ...)
  2026-10-13  3:47 ` [PATCH v2 06/11] i2c: Add Cortina Systems CS75xx I2C controller driver Fil Dunsky via B4 Relay
@ 2026-10-13  3:47 ` Fil Dunsky via B4 Relay
  2026-10-13  4:10   ` sashiko-bot
  2026-10-13  3:47 ` [PATCH v2 08/11] ARM: dts: cortina: cs7542: Add interrupt controller, GPIO and I2C Fil Dunsky via B4 Relay
                   ` (3 subsequent siblings)
  10 siblings, 1 reply; 18+ messages in thread
From: Fil Dunsky via B4 Relay @ 2026-10-13  3:47 UTC (permalink / raw)
  To: Thomas Gleixner, Radu Rendec, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski, Andi Shyti,
	Wim Van Sebroeck, Guenter Roeck, Russell King, Arnd Bergmann,
	Krzysztof Kozlowski
  Cc: linux-arm-kernel, linux-kernel, devicetree, linux-gpio, linux-i2c,
	linux-watchdog, Fil Dunsky

From: Fil Dunsky <filipp.dunsky@gmail.com>

ARM11 MPCore, Cortex-A5 and Cortex-A9 MPCore systems have a watchdog
next to the local timer of every CPU, banked at the same address. The
binding "arm,cortex-a9-twd-wdt" exists, but there has been no driver
since mpcore_wdt was removed in v3.11, among other reasons because it
only ever programmed the CPU that ran the ioctl.

Program, ping and stop the watchdogs of all online CPUs together with
on_each_cpu(), and use a CPU hotplug state to arm the watchdog of a CPU
coming online while the device is active and to stop it on a CPU going
offline, which could not ping it any more. The watchdog is used in reset
mode only, its interrupt is not needed.

Bootloaders such as the one of the Securifi Almond+ start the watchdog
before booting the kernel. A watchdog found running on any CPU is marked
WDOG_HW_RUNNING and taken over with the driver's timeout without being
stopped, unless it runs with a different prescaler: the prescaler cannot
be changed in watchdog mode. Once the watchdog mode is set it can only
be left through the disable register sequence, which is what stop does.

The watchdog is also registered as a restart handler: the CPU that
restarts the system, the only one left running, lets its own watchdog
expire right away.

Signed-off-by: Fil Dunsky <filipp.dunsky@gmail.com>
---
 MAINTAINERS                    |   6 +
 drivers/watchdog/Kconfig       |  15 ++
 drivers/watchdog/Makefile      |   1 +
 drivers/watchdog/arm_twd_wdt.c | 395 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 417 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 176046436..53f6fa1c5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2508,6 +2508,12 @@ T:	git git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc.git
 F:	arch/arm/mach-*/
 F:	arch/arm/plat-*/
 
+ARM TWD WATCHDOG DRIVER
+M:	Fil Dunsky <filipp.dunsky@gmail.com>
+L:	linux-watchdog@vger.kernel.org
+S:	Maintained
+F:	drivers/watchdog/arm_twd_wdt.c
+
 ARM/ACTIONS SEMI ARCHITECTURE
 M:	Andreas Färber <afaerber@suse.de>
 M:	Manivannan Sadhasivam <mani@kernel.org>
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index f7d0a0c2c..907e861ec 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -465,6 +465,21 @@ config ARM_SP805_WATCHDOG
 	  ARM Primecell SP805 Watchdog timer. This will reboot your system when
 	  the timeout is reached.
 
+config ARM_TWD_WATCHDOG
+	tristate "ARM TWD (MPCore private) Watchdog"
+	depends on HAVE_ARM_TWD || COMPILE_TEST
+	depends on HAS_IOMEM
+	select WATCHDOG_CORE
+	help
+	  Say Y here to include support for the private watchdog found next
+	  to the local (TWD) timer of every CPU in ARM11 MPCore, Cortex-A5
+	  and Cortex-A9 MPCore systems. The watchdogs of all online CPUs are
+	  driven together. A watchdog left running by the bootloader is
+	  taken over.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called arm_twd_wdt.
+
 config ARM_SBSA_WATCHDOG
 	tristate "ARM SBSA Generic Watchdog"
 	depends on ARM64
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 9ac406677..4ff0bced5 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
 # ARM Architecture
 obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o
 obj-$(CONFIG_ARM_SBSA_WATCHDOG) += sbsa_gwdt.o
+obj-$(CONFIG_ARM_TWD_WATCHDOG) += arm_twd_wdt.o
 obj-$(CONFIG_ARMADA_37XX_WATCHDOG) += armada_37xx_wdt.o
 obj-$(CONFIG_AIROHA_WATCHDOG) += airoha_wdt.o
 obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o
diff --git a/drivers/watchdog/arm_twd_wdt.c b/drivers/watchdog/arm_twd_wdt.c
new file mode 100644
index 000000000..a582234e3
--- /dev/null
+++ b/drivers/watchdog/arm_twd_wdt.c
@@ -0,0 +1,395 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Watchdog driver for the ARM Cortex-A9/A5 and ARM11 MPCore private
+ * (TWD) watchdog
+ *
+ * Every CPU has its own watchdog, banked at the same address, and any of
+ * them resets the system when it expires. The watchdog device therefore
+ * programs, pings and stops the watchdog of every online CPU, and a CPU
+ * that is brought online or taken offline has its watchdog set up or
+ * stopped on the way. Once the watchdog mode bit is set it can only be
+ * cleared by writing the disable sequence to the disable register.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/cpu.h>
+#include <linux/cpuhotplug.h>
+#include <linux/cpumask.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/math64.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/smp.h>
+#include <linux/watchdog.h>
+
+#define TWD_WDT_LOAD		0x00
+#define TWD_WDT_COUNTER		0x04
+#define TWD_WDT_CONTROL		0x08
+#define TWD_WDT_RESET_STATUS	0x10
+#define TWD_WDT_DISABLE		0x14
+
+#define TWD_WDT_CTRL_ENABLE	BIT(0)
+#define TWD_WDT_CTRL_WD_MODE	BIT(3)
+#define TWD_WDT_CTRL_PRESCALER	GENMASK(15, 8)
+
+#define TWD_WDT_RESET_FLAG	BIT(0)
+
+#define TWD_WDT_DISABLE_KEY1	0x12345678
+#define TWD_WDT_DISABLE_KEY2	0x87654321
+
+/* Largest prescaler, the counter clock is the input clock divided by 256 */
+#define TWD_WDT_PRESCALE	255
+#define TWD_WDT_DEFAULT_TIMEOUT	30
+
+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 unsigned int timeout;
+module_param(timeout, uint, 0);
+MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds");
+
+static enum cpuhp_state twd_wdt_hp_state;
+
+struct twd_wdt {
+	struct watchdog_device wdd;
+	struct hlist_node node;
+	void __iomem *base;
+	unsigned long rate;
+	/* Changed under cpus_read_lock() once the hotplug instance exists */
+	bool armed;
+	cpumask_t running;
+	cpumask_t reset;
+	u32 counter_min;
+};
+
+static u32 twd_wdt_ticks(struct twd_wdt *wdt, unsigned int secs)
+{
+	u64 ticks = div_u64((u64)wdt->rate * secs, TWD_WDT_PRESCALE + 1);
+
+	return min_t(u64, ticks, U32_MAX);
+}
+
+/* The following run on the CPU whose watchdog they access */
+static void twd_wdt_cpu_stop(void *data)
+{
+	struct twd_wdt *wdt = data;
+
+	writel(TWD_WDT_DISABLE_KEY1, wdt->base + TWD_WDT_DISABLE);
+	writel(TWD_WDT_DISABLE_KEY2, wdt->base + TWD_WDT_DISABLE);
+	writel(0, wdt->base + TWD_WDT_CONTROL);
+}
+
+static void twd_wdt_cpu_start(void *data)
+{
+	struct twd_wdt *wdt = data;
+	u32 ctrl = readl(wdt->base + TWD_WDT_CONTROL);
+
+	/*
+	 * In watchdog mode the prescaler cannot be changed, so a watchdog
+	 * started by the bootloader with a different one is stopped first.
+	 */
+	if ((ctrl & TWD_WDT_CTRL_WD_MODE) &&
+	    FIELD_GET(TWD_WDT_CTRL_PRESCALER, ctrl) != TWD_WDT_PRESCALE)
+		twd_wdt_cpu_stop(wdt);
+
+	/*
+	 * Load the counter before enabling, it may still be zero. A watchdog
+	 * that is already running is reloaded and keeps running, the mode bit
+	 * cannot be cleared by writing the control register.
+	 */
+	writel(twd_wdt_ticks(wdt, wdt->wdd.timeout), wdt->base + TWD_WDT_LOAD);
+	writel(FIELD_PREP(TWD_WDT_CTRL_PRESCALER, TWD_WDT_PRESCALE) |
+	       TWD_WDT_CTRL_WD_MODE | TWD_WDT_CTRL_ENABLE,
+	       wdt->base + TWD_WDT_CONTROL);
+}
+
+static void twd_wdt_cpu_ping(void *data)
+{
+	struct twd_wdt *wdt = data;
+
+	/* In watchdog mode a write to the load register reloads the counter */
+	writel(twd_wdt_ticks(wdt, wdt->wdd.timeout), wdt->base + TWD_WDT_LOAD);
+}
+
+static void twd_wdt_cpu_counter(void *data)
+{
+	struct twd_wdt *wdt = data;
+	u32 val = readl(wdt->base + TWD_WDT_COUNTER);
+	u32 old = READ_ONCE(wdt->counter_min);
+
+	/* Called on all CPUs at once, keep the smallest value */
+	while (val < old) {
+		u32 prev = cmpxchg(&wdt->counter_min, old, val);
+
+		if (prev == old)
+			break;
+		old = prev;
+	}
+}
+
+static void twd_wdt_cpu_probe(void *data)
+{
+	struct twd_wdt *wdt = data;
+	unsigned int cpu = smp_processor_id();
+	u32 ctrl = readl(wdt->base + TWD_WDT_CONTROL);
+
+	if ((ctrl & (TWD_WDT_CTRL_WD_MODE | TWD_WDT_CTRL_ENABLE)) ==
+	    (TWD_WDT_CTRL_WD_MODE | TWD_WDT_CTRL_ENABLE))
+		cpumask_set_cpu(cpu, &wdt->running);
+
+	if (readl(wdt->base + TWD_WDT_RESET_STATUS) & TWD_WDT_RESET_FLAG) {
+		cpumask_set_cpu(cpu, &wdt->reset);
+		/* The flag is cleared by writing one */
+		writel(TWD_WDT_RESET_FLAG, wdt->base + TWD_WDT_RESET_STATUS);
+	}
+}
+
+static void twd_wdt_on_each_cpu(struct twd_wdt *wdt, smp_call_func_t func)
+{
+	cpus_read_lock();
+	on_each_cpu(func, wdt, 1);
+	cpus_read_unlock();
+}
+
+static int twd_wdt_start(struct watchdog_device *wdd)
+{
+	struct twd_wdt *wdt = watchdog_get_drvdata(wdd);
+
+	cpus_read_lock();
+	on_each_cpu(twd_wdt_cpu_start, wdt, 1);
+	wdt->armed = true;
+	cpus_read_unlock();
+
+	return 0;
+}
+
+static int twd_wdt_stop(struct watchdog_device *wdd)
+{
+	struct twd_wdt *wdt = watchdog_get_drvdata(wdd);
+
+	cpus_read_lock();
+	wdt->armed = false;
+	on_each_cpu(twd_wdt_cpu_stop, wdt, 1);
+	cpus_read_unlock();
+
+	return 0;
+}
+
+static int twd_wdt_ping(struct watchdog_device *wdd)
+{
+	twd_wdt_on_each_cpu(watchdog_get_drvdata(wdd), twd_wdt_cpu_ping);
+
+	return 0;
+}
+
+static int twd_wdt_set_timeout(struct watchdog_device *wdd, unsigned int t)
+{
+	wdd->timeout = t;
+	if (watchdog_active(wdd) || watchdog_hw_running(wdd))
+		twd_wdt_ping(wdd);
+
+	return 0;
+}
+
+static unsigned int twd_wdt_get_timeleft(struct watchdog_device *wdd)
+{
+	struct twd_wdt *wdt = watchdog_get_drvdata(wdd);
+	u64 ticks;
+
+	cpus_read_lock();
+	wdt->counter_min = U32_MAX;
+	on_each_cpu(twd_wdt_cpu_counter, wdt, 1);
+	ticks = (u64)wdt->counter_min * (TWD_WDT_PRESCALE + 1);
+	cpus_read_unlock();
+
+	return div_u64(ticks, wdt->rate);
+}
+
+static int twd_wdt_restart(struct watchdog_device *wdd, unsigned long action,
+			   void *data)
+{
+	struct twd_wdt *wdt = watchdog_get_drvdata(wdd);
+
+	/* Only this CPU is still running, let its watchdog expire right away */
+	writel(1, wdt->base + TWD_WDT_LOAD);
+	writel(TWD_WDT_CTRL_WD_MODE | TWD_WDT_CTRL_ENABLE,
+	       wdt->base + TWD_WDT_CONTROL);
+	mdelay(500);
+
+	return 0;
+}
+
+static const struct watchdog_ops twd_wdt_ops = {
+	.owner		= THIS_MODULE,
+	.start		= twd_wdt_start,
+	.stop		= twd_wdt_stop,
+	.ping		= twd_wdt_ping,
+	.set_timeout	= twd_wdt_set_timeout,
+	.get_timeleft	= twd_wdt_get_timeleft,
+	.restart	= twd_wdt_restart,
+};
+
+static const struct watchdog_info twd_wdt_info = {
+	.options	= WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
+			  WDIOF_MAGICCLOSE | WDIOF_CARDRESET,
+	.identity	= "ARM TWD watchdog",
+};
+
+static int twd_wdt_cpu_online(unsigned int cpu, struct hlist_node *node)
+{
+	struct twd_wdt *wdt = hlist_entry(node, struct twd_wdt, node);
+
+	/*
+	 * Also called for every online CPU at probe time. A watchdog that is
+	 * running while the device is not active would not be pinged.
+	 */
+	if (wdt->armed)
+		twd_wdt_cpu_start(wdt);
+	else
+		twd_wdt_cpu_stop(wdt);
+
+	return 0;
+}
+
+static int twd_wdt_cpu_offline(unsigned int cpu, struct hlist_node *node)
+{
+	struct twd_wdt *wdt = hlist_entry(node, struct twd_wdt, node);
+
+	/* Nobody pings the watchdog of an offline CPU */
+	twd_wdt_cpu_stop(wdt);
+
+	return 0;
+}
+
+static void twd_wdt_remove_instance(void *data)
+{
+	struct twd_wdt *wdt = data;
+
+	cpuhp_state_remove_instance_nocalls(twd_wdt_hp_state, &wdt->node);
+}
+
+static int twd_wdt_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct twd_wdt *wdt;
+	struct clk *clk;
+	int ret;
+
+	wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
+	if (!wdt)
+		return -ENOMEM;
+
+	wdt->base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(wdt->base))
+		return PTR_ERR(wdt->base);
+
+	clk = devm_clk_get_enabled(dev, NULL);
+	if (IS_ERR(clk))
+		return dev_err_probe(dev, PTR_ERR(clk), "failed to get clock\n");
+
+	wdt->rate = clk_get_rate(clk);
+	if (!wdt->rate)
+		return dev_err_probe(dev, -EINVAL, "clock rate is zero\n");
+
+	wdt->wdd.info = &twd_wdt_info;
+	wdt->wdd.ops = &twd_wdt_ops;
+	wdt->wdd.parent = dev;
+	wdt->wdd.min_timeout = 1;
+	wdt->wdd.max_timeout = div_u64((u64)U32_MAX * (TWD_WDT_PRESCALE + 1),
+				       wdt->rate);
+	wdt->wdd.timeout = min_t(unsigned int, TWD_WDT_DEFAULT_TIMEOUT,
+				 wdt->wdd.max_timeout);
+	watchdog_init_timeout(&wdt->wdd, timeout, dev);
+	watchdog_set_nowayout(&wdt->wdd, nowayout);
+	watchdog_set_restart_priority(&wdt->wdd, 128);
+	watchdog_stop_on_reboot(&wdt->wdd);
+	watchdog_stop_on_unregister(&wdt->wdd);
+	watchdog_set_drvdata(&wdt->wdd, wdt);
+
+	twd_wdt_on_each_cpu(wdt, twd_wdt_cpu_probe);
+	if (!cpumask_empty(&wdt->running)) {
+		/*
+		 * Left running by the bootloader: the online callback takes it
+		 * over with our settings on every CPU without stopping it.
+		 */
+		wdt->armed = true;
+		set_bit(WDOG_HW_RUNNING, &wdt->wdd.status);
+	}
+
+	ret = cpuhp_state_add_instance(twd_wdt_hp_state, &wdt->node);
+	if (ret)
+		return ret;
+
+	ret = devm_add_action_or_reset(dev, twd_wdt_remove_instance, wdt);
+	if (ret)
+		return ret;
+
+	if (!cpumask_empty(&wdt->reset)) {
+		wdt->wdd.bootstatus = WDIOF_CARDRESET;
+		dev_info(dev, "last reset caused by the watchdog of CPU%*pbl\n",
+			 cpumask_pr_args(&wdt->reset));
+	}
+
+	if (watchdog_hw_running(&wdt->wdd))
+		dev_info(dev, "running on CPU%*pbl, taking over\n",
+			 cpumask_pr_args(&wdt->running));
+
+	ret = devm_watchdog_register_device(dev, &wdt->wdd);
+	if (ret)
+		return ret;
+
+	dev_info(dev, "timeout %u s (max %u s), clock %lu Hz\n",
+		 wdt->wdd.timeout, wdt->wdd.max_timeout, wdt->rate);
+
+	return 0;
+}
+
+static const struct of_device_id twd_wdt_of_match[] = {
+	{ .compatible = "arm,arm11mp-twd-wdt" },
+	{ .compatible = "arm,cortex-a5-twd-wdt" },
+	{ .compatible = "arm,cortex-a9-twd-wdt" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, twd_wdt_of_match);
+
+static struct platform_driver twd_wdt_driver = {
+	.probe = twd_wdt_probe,
+	.driver = {
+		.name = "arm-twd-wdt",
+		.of_match_table = twd_wdt_of_match,
+	},
+};
+
+static int __init twd_wdt_init(void)
+{
+	int ret;
+
+	ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, "watchdog/arm_twd:online",
+				      twd_wdt_cpu_online, twd_wdt_cpu_offline);
+	if (ret < 0)
+		return ret;
+	twd_wdt_hp_state = ret;
+
+	ret = platform_driver_register(&twd_wdt_driver);
+	if (ret)
+		cpuhp_remove_multi_state(twd_wdt_hp_state);
+
+	return ret;
+}
+module_init(twd_wdt_init);
+
+static void __exit twd_wdt_exit(void)
+{
+	platform_driver_unregister(&twd_wdt_driver);
+	cpuhp_remove_multi_state(twd_wdt_hp_state);
+}
+module_exit(twd_wdt_exit);
+
+MODULE_DESCRIPTION("ARM TWD (MPCore private) watchdog driver");
+MODULE_LICENSE("GPL");

-- 
2.53.0



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

* [PATCH v2 08/11] ARM: dts: cortina: cs7542: Add interrupt controller, GPIO and I2C
  2026-10-13  3:46 [PATCH v2 00/11] Cortina Systems CS75xx: interrupt controller, GPIO, I2C and TWD watchdog Fil Dunsky via B4 Relay
                   ` (6 preceding siblings ...)
  2026-10-13  3:47 ` [PATCH v2 07/11] watchdog: Add ARM TWD (MPCore private) watchdog driver Fil Dunsky via B4 Relay
@ 2026-10-13  3:47 ` Fil Dunsky via B4 Relay
  2026-10-13  3:47 ` [PATCH v2 09/11] ARM: dts: cortina: almondplus: Add reset button and enable I2C Fil Dunsky via B4 Relay
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 18+ messages in thread
From: Fil Dunsky via B4 Relay @ 2026-10-13  3:47 UTC (permalink / raw)
  To: Thomas Gleixner, Radu Rendec, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski, Andi Shyti,
	Wim Van Sebroeck, Guenter Roeck, Russell King, Arnd Bergmann,
	Krzysztof Kozlowski
  Cc: linux-arm-kernel, linux-kernel, devicetree, linux-gpio, linux-i2c,
	linux-watchdog, Fil Dunsky

From: Fil Dunsky <filipp.dunsky@gmail.com>

Describe the peripheral interrupt controller, the five GPIO banks and
the I2C controller of the CS7542.

Signed-off-by: Fil Dunsky <filipp.dunsky@gmail.com>
---
 arch/arm/boot/dts/cortina/cs7542.dtsi | 75 +++++++++++++++++++++++++++++++++++
 1 file changed, 75 insertions(+)

diff --git a/arch/arm/boot/dts/cortina/cs7542.dtsi b/arch/arm/boot/dts/cortina/cs7542.dtsi
index 29b0b7da8..c88449c54 100644
--- a/arch/arm/boot/dts/cortina/cs7542.dtsi
+++ b/arch/arm/boot/dts/cortina/cs7542.dtsi
@@ -44,6 +44,26 @@ soc {
 		#address-cells = <1>;
 		#size-cells = <1>;
 
+		intc: interrupt-controller@f0070004 {
+			compatible = "cortina,cs7542-intc";
+			reg = <0xf0070004 0x8>;
+			interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>;
+			interrupt-controller;
+			#interrupt-cells = <1>;
+		};
+
+		i2c: i2c@f00700e8 {
+			compatible = "cortina,cs7542-i2c";
+			reg = <0xf00700e8 0x28>;
+			interrupt-parent = <&intc>;
+			interrupts = <11>;
+			clocks = <&apb_clk>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			status = "disabled";
+		};
+
 		uart0: serial@f0070110 {
 			compatible = "cortina,cs7542-uart";
 			reg = <0xf0070110 0x30>;
@@ -80,6 +100,61 @@ uart3: serial@f00701a0 {
 			status = "disabled";
 		};
 
+		gpio0: gpio@f00701d0 {
+			compatible = "cortina,cs7542-gpio";
+			reg = <0xf00701d0 0x20>, <0xf000001c 0x4>;
+			interrupt-parent = <&intc>;
+			interrupts = <6>;
+			gpio-controller;
+			#gpio-cells = <2>;
+			interrupt-controller;
+			#interrupt-cells = <2>;
+		};
+
+		gpio1: gpio@f00701f0 {
+			compatible = "cortina,cs7542-gpio";
+			reg = <0xf00701f0 0x20>, <0xf0000020 0x4>;
+			interrupt-parent = <&intc>;
+			interrupts = <7>;
+			gpio-controller;
+			#gpio-cells = <2>;
+			interrupt-controller;
+			#interrupt-cells = <2>;
+		};
+
+		gpio2: gpio@f0070210 {
+			compatible = "cortina,cs7542-gpio";
+			reg = <0xf0070210 0x20>, <0xf0000024 0x4>;
+			interrupt-parent = <&intc>;
+			interrupts = <8>;
+			gpio-controller;
+			#gpio-cells = <2>;
+			interrupt-controller;
+			#interrupt-cells = <2>;
+		};
+
+		gpio3: gpio@f0070230 {
+			compatible = "cortina,cs7542-gpio";
+			reg = <0xf0070230 0x20>, <0xf0000028 0x4>;
+			interrupt-parent = <&intc>;
+			interrupts = <9>;
+			gpio-controller;
+			#gpio-cells = <2>;
+			interrupt-controller;
+			#interrupt-cells = <2>;
+		};
+
+		gpio4: gpio@f0070250 {
+			compatible = "cortina,cs7542-gpio";
+			reg = <0xf0070250 0x20>, <0xf000002c 0x4>;
+			interrupt-parent = <&intc>;
+			interrupts = <10>;
+			gpio-controller;
+			#gpio-cells = <2>;
+			interrupt-controller;
+			#interrupt-cells = <2>;
+		};
+
 		l2: cache-controller@f5010000 {
 			compatible = "arm,pl310-cache";
 			reg = <0xf5010000 0x1000>;

-- 
2.53.0



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

* [PATCH v2 09/11] ARM: dts: cortina: almondplus: Add reset button and enable I2C
  2026-10-13  3:46 [PATCH v2 00/11] Cortina Systems CS75xx: interrupt controller, GPIO, I2C and TWD watchdog Fil Dunsky via B4 Relay
                   ` (7 preceding siblings ...)
  2026-10-13  3:47 ` [PATCH v2 08/11] ARM: dts: cortina: cs7542: Add interrupt controller, GPIO and I2C Fil Dunsky via B4 Relay
@ 2026-10-13  3:47 ` Fil Dunsky via B4 Relay
  2026-10-13  3:47 ` [PATCH v2 10/11] ARM: multi_v7_defconfig: Enable Cortina Systems CS75xx GPIO, I2C and TWD watchdog Fil Dunsky via B4 Relay
  2026-10-13  3:47 ` [PATCH v2 11/11] MAINTAINERS: Add Cortina Systems CS75xx interrupt controller, GPIO and I2C Fil Dunsky via B4 Relay
  10 siblings, 0 replies; 18+ messages in thread
From: Fil Dunsky via B4 Relay @ 2026-10-13  3:47 UTC (permalink / raw)
  To: Thomas Gleixner, Radu Rendec, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski, Andi Shyti,
	Wim Van Sebroeck, Guenter Roeck, Russell King, Arnd Bergmann,
	Krzysztof Kozlowski
  Cc: linux-arm-kernel, linux-kernel, devicetree, linux-gpio, linux-i2c,
	linux-watchdog, Fil Dunsky

From: Fil Dunsky <filipp.dunsky@gmail.com>

The pinhole button on the back of the Almond+ is connected to GPIO 37
(bank 1, line 5), active low. The stock firmware uses it for restart
and factory reset.

Enable the I2C bus, which has the touchscreen controller on it; the
touchscreen itself will be added together with its driver changes.

Signed-off-by: Fil Dunsky <filipp.dunsky@gmail.com>
---
 .../boot/dts/cortina/cs7542-securifi-almondplus.dts   | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/arch/arm/boot/dts/cortina/cs7542-securifi-almondplus.dts b/arch/arm/boot/dts/cortina/cs7542-securifi-almondplus.dts
index e2988c687..0619bd754 100644
--- a/arch/arm/boot/dts/cortina/cs7542-securifi-almondplus.dts
+++ b/arch/arm/boot/dts/cortina/cs7542-securifi-almondplus.dts
@@ -5,6 +5,9 @@
 
 /dts-v1/;
 
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
+
 #include "cs7542.dtsi"
 
 / {
@@ -19,6 +22,16 @@ chosen {
 		stdout-path = "serial0:115200n8";
 	};
 
+	keys {
+		compatible = "gpio-keys";
+
+		button-reset {
+			label = "reset";
+			gpios = <&gpio1 5 GPIO_ACTIVE_LOW>;
+			linux,code = <KEY_RESTART>;
+		};
+	};
+
 	memory@0 {
 		device_type = "memory";
 		reg = <0x00000000 0x20000000>;
@@ -50,6 +63,12 @@ &apb_clk {
 	clock-frequency = <150000000>;
 };
 
+&i2c {
+	clock-frequency = <400000>;
+
+	status = "okay";
+};
+
 &uart0 {
 	status = "okay";
 };

-- 
2.53.0



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

* [PATCH v2 10/11] ARM: multi_v7_defconfig: Enable Cortina Systems CS75xx GPIO, I2C and TWD watchdog
  2026-10-13  3:46 [PATCH v2 00/11] Cortina Systems CS75xx: interrupt controller, GPIO, I2C and TWD watchdog Fil Dunsky via B4 Relay
                   ` (8 preceding siblings ...)
  2026-10-13  3:47 ` [PATCH v2 09/11] ARM: dts: cortina: almondplus: Add reset button and enable I2C Fil Dunsky via B4 Relay
@ 2026-10-13  3:47 ` Fil Dunsky via B4 Relay
  2026-10-13  6:13   ` Krzysztof Kozlowski
  2026-10-13  3:47 ` [PATCH v2 11/11] MAINTAINERS: Add Cortina Systems CS75xx interrupt controller, GPIO and I2C Fil Dunsky via B4 Relay
  10 siblings, 1 reply; 18+ messages in thread
From: Fil Dunsky via B4 Relay @ 2026-10-13  3:47 UTC (permalink / raw)
  To: Thomas Gleixner, Radu Rendec, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski, Andi Shyti,
	Wim Van Sebroeck, Guenter Roeck, Russell King, Arnd Bergmann,
	Krzysztof Kozlowski
  Cc: linux-arm-kernel, linux-kernel, devicetree, linux-gpio, linux-i2c,
	linux-watchdog, Fil Dunsky

From: Fil Dunsky <filipp.dunsky@gmail.com>

Enable the GPIO and I2C drivers of the Cortina Systems CS75xx SoCs and
the ARM TWD watchdog driver, which is needed on the Securifi Almond+
because its bootloader starts the watchdog.

Signed-off-by: Fil Dunsky <filipp.dunsky@gmail.com>
---
 arch/arm/configs/multi_v7_defconfig | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index 23f144359..9a161f8bd 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -423,6 +423,7 @@ CONFIG_I2C_ASPEED=m
 CONFIG_I2C_AT91=m
 CONFIG_I2C_BCM2835=y
 CONFIG_I2C_CADENCE=y
+CONFIG_I2C_CS75XX=y
 CONFIG_I2C_DAVINCI=y
 CONFIG_I2C_DESIGNWARE_CORE=y
 CONFIG_I2C_DIGICOLOR=m
@@ -492,6 +493,7 @@ CONFIG_PINCTRL_QCOM_SSBI_PMIC=y
 CONFIG_PINCTRL_RZA2=y
 CONFIG_PINCTRL_RZN1=y
 CONFIG_GPIO_ASPEED_SGPIO=y
+CONFIG_GPIO_CS75XX=y
 CONFIG_GPIO_DAVINCI=y
 CONFIG_GPIO_DWAPB=y
 CONFIG_GPIO_EM=y
@@ -568,6 +570,7 @@ CONFIG_UNIPHIER_THERMAL=y
 CONFIG_DA9063_WATCHDOG=m
 CONFIG_XILINX_WATCHDOG=y
 CONFIG_ARM_SP805_WATCHDOG=y
+CONFIG_ARM_TWD_WATCHDOG=y
 CONFIG_AT91SAM9X_WATCHDOG=y
 CONFIG_SAMA5D4_WATCHDOG=y
 CONFIG_S3C2410_WATCHDOG=m

-- 
2.53.0



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

* [PATCH v2 11/11] MAINTAINERS: Add Cortina Systems CS75xx interrupt controller, GPIO and I2C
  2026-10-13  3:46 [PATCH v2 00/11] Cortina Systems CS75xx: interrupt controller, GPIO, I2C and TWD watchdog Fil Dunsky via B4 Relay
                   ` (9 preceding siblings ...)
  2026-10-13  3:47 ` [PATCH v2 10/11] ARM: multi_v7_defconfig: Enable Cortina Systems CS75xx GPIO, I2C and TWD watchdog Fil Dunsky via B4 Relay
@ 2026-10-13  3:47 ` Fil Dunsky via B4 Relay
  10 siblings, 0 replies; 18+ messages in thread
From: Fil Dunsky via B4 Relay @ 2026-10-13  3:47 UTC (permalink / raw)
  To: Thomas Gleixner, Radu Rendec, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski, Andi Shyti,
	Wim Van Sebroeck, Guenter Roeck, Russell King, Arnd Bergmann,
	Krzysztof Kozlowski
  Cc: linux-arm-kernel, linux-kernel, devicetree, linux-gpio, linux-i2c,
	linux-watchdog, Fil Dunsky

From: Fil Dunsky <filipp.dunsky@gmail.com>

List the new Cortina Systems CS75xx interrupt controller, GPIO and I2C
drivers and their bindings in the CS75xx entry.

Signed-off-by: Fil Dunsky <filipp.dunsky@gmail.com>
---
 MAINTAINERS | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 53f6fa1c5..604993705 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2925,10 +2925,16 @@ M:	Fil Dunsky <filipp.dunsky@gmail.com>
 L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 S:	Maintained
 F:	Documentation/devicetree/bindings/arm/cortina.yaml
+F:	Documentation/devicetree/bindings/gpio/cortina,cs7542-gpio.yaml
+F:	Documentation/devicetree/bindings/i2c/cortina,cs7542-i2c.yaml
+F:	Documentation/devicetree/bindings/interrupt-controller/cortina,cs7542-intc.yaml
 F:	Documentation/devicetree/bindings/serial/cortina,cs7542-uart.yaml
 F:	arch/arm/boot/dts/cortina/
 F:	arch/arm/include/debug/cs75xx.S
 F:	arch/arm/mach-goldengate/
+F:	drivers/gpio/gpio-cs75xx.c
+F:	drivers/i2c/busses/i2c-cs75xx.c
+F:	drivers/irqchip/irq-cs75xx.c
 F:	drivers/tty/serial/cortina-uart.c
 
 ARM/CORTINA SYSTEMS GEMINI ARM ARCHITECTURE

-- 
2.53.0



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

* Re: [PATCH v2 03/11] dt-bindings: gpio: Add Cortina Systems CS7542 GPIO controller
  2026-10-13  3:47 ` [PATCH v2 03/11] dt-bindings: gpio: Add Cortina Systems CS7542 GPIO controller Fil Dunsky via B4 Relay
@ 2026-10-13  3:53   ` sashiko-bot
  0 siblings, 0 replies; 18+ messages in thread
From: sashiko-bot @ 2026-10-13  3:53 UTC (permalink / raw)
  To: Fil Dunsky; +Cc: devicetree, linux-gpio, linux-watchdog, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] dt-bindings: gpio: direct mapping of shared global control block
--

commit f730ed90fa497531db852f78666a6a8d9be45f40
Author: Fil Dunsky <filipp.dunsky@gmail.com>

dt-bindings: gpio: Add Cortina Systems CS7542 GPIO controller

This commit introduces a device tree binding for the Cortina Systems CS75xx
GPIO controller. It specifies five banks of 32 GPIO lines, each with its
own interrupt. The pins are shared between GPIO and peripheral functions
configured via a multiplexing register within a global control block.

> diff --git a/Documentation/devicetree/bindings/gpio/cortina,cs7542-gpio.yaml b/Documentation/devicetree/bindings/gpio/cortina,cs7542-gpio.yaml
> new file mode 100644
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/gpio/cortina,cs7542-gpio.yaml
[ ... ]
> +properties:
> +  compatible:
> +    const: cortina,cs7542-gpio
> +
> +  reg:
> +    items:
> +      - description: GPIO bank registers
> +      - description: GPIO_MUX register of the bank

[Severity: Medium]
Does directly mapping the shared global control block in the reg property
risk resource request collisions at runtime?

If multiple drivers attempt to map the shared global control block, it can
lead to request_mem_region failures. Additionally, handling pin multiplexing
directly in the GPIO controller binding appears to bypass the pinctrl
subsystem.

Should the global control block be defined as a syscon node instead, with
the GPIO controller referencing it via a phandle (for example,
cortina,syscon = <&gcb>)? This would allow safe register modifications,
leaving pin multiplexing logic to be managed by the pinctrl subsystem.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261013-b4-cs75xx-s2-v2-0-94f8c0a90cda@gmail.com?part=3

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

* Re: [PATCH v2 04/11] gpio: Add Cortina Systems CS75xx GPIO driver
  2026-10-13  3:47 ` [PATCH v2 04/11] gpio: Add Cortina Systems CS75xx GPIO driver Fil Dunsky via B4 Relay
@ 2026-10-13  3:58   ` sashiko-bot
  0 siblings, 0 replies; 18+ messages in thread
From: sashiko-bot @ 2026-10-13  3:58 UTC (permalink / raw)
  To: Fil Dunsky; +Cc: devicetree, linux-gpio, linux-watchdog, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] kconfig: unmet direct dependency on HAS_IOMEM when selecting GPIO_GENERIC
--

commit c0cf3f24c2d064770186cd2502b93c7a6f2518be
Author: Fil Dunsky <filipp.dunsky@gmail.com>

gpio: Add Cortina Systems CS75xx GPIO driver
    
This commit introduces a new GPIO driver for the Cortina Systems CS75xx SoCs.
It handles data and direction registers using gpio-mmio, manages GPIO line 
pin multiplexing, and provides interrupt support.

> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index dfdc9ee1a6b38..6b9524f25d24c 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -300,6 +300,18 @@ config GPIO_CLPS711X
>  	help
>  	  Say yes here to support GPIO on CLPS711X SoCs.
>  
> +config GPIO_CS75XX
> +	tristate "Cortina Systems CS75xx GPIO support"
> +	depends on ARCH_CORTINA_ACCESS || COMPILE_TEST
> +	select GPIO_GENERIC

[Severity: Low]
Does this select statement cause a Kconfig unmet dependency warning?

Since GPIO_GENERIC depends on HAS_IOMEM, selecting it here without inheriting
the HAS_IOMEM dependency might cause build warnings or failures when compiling
with COMPILE_TEST enabled on architectures that lack HAS_IOMEM (such as UML).

Should this config also have a depends on HAS_IOMEM to prevent this?

> +	select GPIOLIB_IRQCHIP
> +	help
> +	  Say yes here to support the GPIO banks of the Cortina Systems
> +	  CS75xx SoCs, including their interrupts.

[ ... ]

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261013-b4-cs75xx-s2-v2-0-94f8c0a90cda@gmail.com?part=4

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

* Re: [PATCH v2 06/11] i2c: Add Cortina Systems CS75xx I2C controller driver
  2026-10-13  3:47 ` [PATCH v2 06/11] i2c: Add Cortina Systems CS75xx I2C controller driver Fil Dunsky via B4 Relay
@ 2026-10-13  4:04   ` sashiko-bot
  0 siblings, 0 replies; 18+ messages in thread
From: sashiko-bot @ 2026-10-13  4:04 UTC (permalink / raw)
  To: Fil Dunsky; +Cc: devicetree, linux-gpio, linux-watchdog, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] i2c: lockless hardware reset races with ISR after transfer timeout
- [Medium] i2c: integer overflow in CS75xx clock prescaler boundary check
- [Medium] i2c: cs75xx: premature controller reset on multi-master bus due to 1ms timeout
--

commit ab11b4fa940bf862049e3ce494994f7702146e7c
Author: Fil Dunsky <filipp.dunsky@gmail.com>

i2c: Add Cortina Systems CS75xx I2C controller driver

This commit adds a new I2C controller driver for the Cortina Systems CS75xx
SoCs. It supports single-byte transfers with a command register, handles
interrupts upon command completion, and supports arbitration loss retries.

> diff --git a/drivers/i2c/busses/i2c-cs75xx.c b/drivers/i2c/busses/i2c-cs75xx.c
> new file mode 100644
> index 0000000000000..3eca5fb14266e
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-cs75xx.c
[ ... ]
> +static irqreturn_t cs75xx_i2c_isr(int irq, void *dev_id)
> +{
> +	struct cs75xx_i2c *i2c = dev_id;
> +	u32 ctrl, ack;
> +
> +	guard(spinlock)(&i2c->lock);
> +
> +	if (!(readl(i2c->base + CS75XX_I2C_INT) & CS75XX_I2C_INT_DONE))
> +		return IRQ_NONE;
> +
> +	writel(CS75XX_I2C_INT_DONE, i2c->base + CS75XX_I2C_INT);
> +
> +	/* The done flag is cleared by writing it back */
> +	ctrl = readl(i2c->base + CS75XX_I2C_CTRL);
> +	writel(ctrl, i2c->base + CS75XX_I2C_CTRL);
> +
> +	ack = readl(i2c->base + CS75XX_I2C_ACK);
> +	cs75xx_i2c_process(i2c, ack);
> +
> +	return IRQ_HANDLED;
> +}
[ ... ]
> +static int cs75xx_i2c_xfer_msg(struct cs75xx_i2c *i2c, struct i2c_msg *msg,
> +			       bool last)
> +{
> +	unsigned long time_left;
> +
> +	spin_lock_irq(&i2c->lock);
> +	reinit_completion(&i2c->done);
> +	i2c->msg = msg;
> +	i2c->pos = 0;
> +	i2c->last = last;
> +	i2c->err = 0;
> +	i2c->state = CS75XX_I2C_ADDR;
> +	writel(i2c_8bit_addr_from_msg(msg), i2c->base + CS75XX_I2C_TXR);
> +	writel(CS75XX_I2C_CTRL_START | CS75XX_I2C_CTRL_WRITE,
> +	       i2c->base + CS75XX_I2C_CTRL);
> +	spin_unlock_irq(&i2c->lock);
> +
> +	time_left = wait_for_completion_timeout(&i2c->done, i2c->adap.timeout);
> +	if (!time_left) {
> +		spin_lock_irq(&i2c->lock);
> +		i2c->state = CS75XX_I2C_IDLE;
> +		spin_unlock_irq(&i2c->lock);
> +		dev_err(i2c->dev, "transfer timed out, resetting the controller\n");
> +		cs75xx_i2c_reset(i2c);

[Severity: High]
Is there a race condition between this hardware reset and the interrupt
handler?

When a transfer times out, the lock is dropped before resetting the controller:

CPU1 (Timeout path)
cs75xx_i2c_xfer_msg()
   spin_unlock_irq(&i2c->lock);
   cs75xx_i2c_reset(i2c);
      cs75xx_i2c_hw_init(i2c);
         writel(0, i2c->base + CS75XX_I2C_CFG);  /* Disables the IP */
         usleep_range(5000, 10000);

If a delayed interrupt fires during this sleep window, the ISR will run:

CPU2 (ISR)
cs75xx_i2c_isr()
   guard(spinlock)(&i2c->lock);
   ...
   writel(CS75XX_I2C_INT_DONE, i2c->base + CS75XX_I2C_INT);
   ctrl = readl(i2c->base + CS75XX_I2C_CTRL);
   writel(ctrl, i2c->base + CS75XX_I2C_CTRL);

The ISR grabs the lock and performs multiple MMIO reads and writes before it
checks i2c->state. Can accessing these hardware registers while the block is
disabled or in reset trigger an asynchronous external bus abort or panic?

> +		return -ETIMEDOUT;
> +	}
> +
> +	return i2c->err;
> +}
> +
> +static int cs75xx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
> +			   int num)
> +{
> +	struct cs75xx_i2c *i2c = i2c_get_adapdata(adap);
> +	u32 val;
> +	int i, ret;
> +
> +	if (readl_poll_timeout(i2c->base + CS75XX_I2C_ACK, val,
> +			       !(val & CS75XX_I2C_ACK_BUSY), 10, 1000)) {
> +		cs75xx_i2c_reset(i2c);

[Severity: Medium]
Is a 1ms (1000 us) timeout sufficient to wait for the I2C bus to become idle
when an external master is executing a legitimate transaction?

If another master initiates a transaction that takes longer than 1ms (such as
a transfer larger than 11 bytes at 100 kHz), this poll will time out.

This would cause the driver to prematurely perform a hardware reset and fail
the transfer with -EBUSY, potentially disrupting valid multi-master bus
activity.

> +		if (readl(i2c->base + CS75XX_I2C_ACK) & CS75XX_I2C_ACK_BUSY)
> +			return -EBUSY;
> +	}
[ ... ]
> +static int cs75xx_i2c_probe(struct platform_device *pdev)
> +{
[ ... ]
> +	i2c->ref_rate = clk_get_rate(clk);
> +	if (device_property_read_u32(dev, "clock-frequency", &i2c->bus_rate))
> +		i2c->bus_rate = I2C_MAX_STANDARD_MODE_FREQ;
> +	if (!i2c->ref_rate || !i2c->bus_rate ||
> +	    i2c->bus_rate > I2C_MAX_FAST_MODE_FREQ ||
> +	    i2c->ref_rate / (5 * i2c->bus_rate) > FIELD_MAX(CS75XX_I2C_CFG_PRER) + 1)

[Severity: Medium]
Could this integer division allow boundary values that overflow the 16-bit
prescaler field?

Since FIELD_MAX(CS75XX_I2C_CFG_PRER) is 65535, this check permits the integer
division to evaluate to 65536.

However, cs75xx_i2c_hw_init() calculates the actual prescaler using
DIV_ROUND_UP(i2c->ref_rate, 5 * i2c->bus_rate) - 1.

If the division yields 65536 with any non-zero remainder, DIV_ROUND_UP
evaluates to 65537, causing the calculated prescaler to equal 65536 (0x10000).

When this 17-bit value is passed to FIELD_PREP() in cs75xx_i2c_hw_init(), it
gets silently truncated to 0, which would misconfigure the hardware for the
highest possible bus frequency.

Should this boundary check use DIV_ROUND_UP to match the initialization logic?

> +		return dev_err_probe(dev, -EINVAL,
> +				     "unsupported bus frequency %u Hz\n",
> +				     i2c->bus_rate);
> +
> +	cs75xx_i2c_hw_init(i2c);
[ ... ]

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261013-b4-cs75xx-s2-v2-0-94f8c0a90cda@gmail.com?part=6

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

* Re: [PATCH v2 07/11] watchdog: Add ARM TWD (MPCore private) watchdog driver
  2026-10-13  3:47 ` [PATCH v2 07/11] watchdog: Add ARM TWD (MPCore private) watchdog driver Fil Dunsky via B4 Relay
@ 2026-10-13  4:10   ` sashiko-bot
  0 siblings, 0 replies; 18+ messages in thread
From: sashiko-bot @ 2026-10-13  4:10 UTC (permalink / raw)
  To: Fil Dunsky; +Cc: devicetree, linux-gpio, linux-watchdog, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] watchdog: arm_twd_wdt: stale clock rate causes spurious reboots
- [Medium] watchdog: arm_twd_wdt: boot CPU watchdog fails to restart after resume
--

commit 6655d83c448f4cf87494cf40f7b8891a218a895c
Author: Fil Dunsky <filipp.dunsky@gmail.com>

watchdog: Add ARM TWD (MPCore private) watchdog driver

ARM11 MPCore, Cortex-A5 and Cortex-A9 MPCore systems have a watchdog next to
the local timer of every CPU. This commit adds a driver to program, ping,
and stop the watchdogs of all online CPUs together.

> diff --git a/drivers/watchdog/arm_twd_wdt.c b/drivers/watchdog/arm_twd_wdt.c
> new file mode 100644
> index 0000000000000..a582234e3d953
> --- /dev/null
> +++ b/drivers/watchdog/arm_twd_wdt.c
> @@ -0,0 +1,395 @@

[ ... ]

> +static u32 twd_wdt_ticks(struct twd_wdt *wdt, unsigned int secs)
> +{
> +	u64 ticks = div_u64((u64)wdt->rate * secs, TWD_WDT_PRESCALE + 1);
> +
> +	return min_t(u64, ticks, U32_MAX);
> +}

[Severity: High]
Does this calculation use a stale clock rate if the CPU frequency scales up?

The ARM TWD clock is tied to the CPU frequency on many MPCore systems. If
the CPU scales up its frequency via cpufreq, the actual clock rate will be
higher than the cached wdt->rate.

Could this result in computing too few ticks for the requested timeout,
leading to a premature expiration and spurious system reboots?

[ ... ]

> +static void twd_wdt_cpu_ping(void *data)
> +{
> +	struct twd_wdt *wdt = data;
> +
> +	/* In watchdog mode a write to the load register reloads the counter */
> +	writel(twd_wdt_ticks(wdt, wdt->wdd.timeout), wdt->base + TWD_WDT_LOAD);
> +}

[Severity: Medium]
The ping function only writes to the LOAD register. If the system resumes
from suspend-to-RAM, the watchdog core's ping worker will invoke this
method. Since it does not restore the CONTROL register, will the boot
CPU's watchdog silently remain disabled after power loss?

[ ... ]

> +static int twd_wdt_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct twd_wdt *wdt;
> +	struct clk *clk;

[ ... ]

> +	clk = devm_clk_get_enabled(dev, NULL);
> +	if (IS_ERR(clk))
> +		return dev_err_probe(dev, PTR_ERR(clk), "failed to get clock\n");
> +
> +	wdt->rate = clk_get_rate(clk);

[Severity: High]
The clock rate is cached statically during probe. Since the clk pointer is
not saved, does this prevent dynamic rate updates if the CPU frequency
changes via cpufreq without modifying the driver?

> +	if (!wdt->rate)
> +		return dev_err_probe(dev, -EINVAL, "clock rate is zero\n");

[ ... ]

> +static struct platform_driver twd_wdt_driver = {
> +	.probe = twd_wdt_probe,
> +	.driver = {
> +		.name = "arm-twd-wdt",
> +		.of_match_table = twd_wdt_of_match,
> +	},
> +};

[Severity: Medium]
This platform driver appears to lack a .pm field or syscore_ops. 

During system suspend, the TWD block loses power and its CONTROL register
is cleared. While non-boot CPUs are hotplugged and restart their watchdogs
via the online hook, the boot CPU is not hotplugged. 

Does this mean the boot CPU's watchdog state is never restored after resume?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261013-b4-cs75xx-s2-v2-0-94f8c0a90cda@gmail.com?part=7

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

* Re: [PATCH v2 10/11] ARM: multi_v7_defconfig: Enable Cortina Systems CS75xx GPIO, I2C and TWD watchdog
  2026-10-13  3:47 ` [PATCH v2 10/11] ARM: multi_v7_defconfig: Enable Cortina Systems CS75xx GPIO, I2C and TWD watchdog Fil Dunsky via B4 Relay
@ 2026-10-13  6:13   ` Krzysztof Kozlowski
  2026-10-13  6:19     ` Fil Dunsky
  0 siblings, 1 reply; 18+ messages in thread
From: Krzysztof Kozlowski @ 2026-10-13  6:13 UTC (permalink / raw)
  To: filipp.dunsky, Thomas Gleixner, Radu Rendec, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Linus Walleij,
	Bartosz Golaszewski, Andi Shyti, Wim Van Sebroeck, Guenter Roeck,
	Russell King, Arnd Bergmann
  Cc: linux-arm-kernel, linux-kernel, devicetree, linux-gpio, linux-i2c,
	linux-watchdog

On 13/10/2026 05:47, Fil Dunsky via B4 Relay wrote:
> From: Fil Dunsky <filipp.dunsky@gmail.com>
> 
> Enable the GPIO and I2C drivers of the Cortina Systems CS75xx SoCs and
> the ARM TWD watchdog driver, which is needed on the Securifi Almond+
> because its bootloader starts the watchdog.
> 
> Signed-off-by: Fil Dunsky <filipp.dunsky@gmail.com>
> ---
>  arch/arm/configs/multi_v7_defconfig | 3 +++
>  1 file changed, 3 insertions(+)
> 
> diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
> index 23f144359..9a161f8bd 100644
> --- a/arch/arm/configs/multi_v7_defconfig
> +++ b/arch/arm/configs/multi_v7_defconfig
> @@ -423,6 +423,7 @@ CONFIG_I2C_ASPEED=m
>  CONFIG_I2C_AT91=m
>  CONFIG_I2C_BCM2835=y
>  CONFIG_I2C_CADENCE=y
> +CONFIG_I2C_CS75XX=y

This should be module.

>  CONFIG_I2C_DAVINCI=y
>  CONFIG_I2C_DESIGNWARE_CORE=y
>  CONFIG_I2C_DIGICOLOR=m
> @@ -492,6 +493,7 @@ CONFIG_PINCTRL_QCOM_SSBI_PMIC=y
>  CONFIG_PINCTRL_RZA2=y
>  CONFIG_PINCTRL_RZN1=y
>  CONFIG_GPIO_ASPEED_SGPIO=y
> +CONFIG_GPIO_CS75XX=y

This as well.

Best regards,
Krzysztof

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

* Re: [PATCH v2 10/11] ARM: multi_v7_defconfig: Enable Cortina Systems CS75xx GPIO, I2C and TWD watchdog
  2026-10-13  6:13   ` Krzysztof Kozlowski
@ 2026-10-13  6:19     ` Fil Dunsky
  0 siblings, 0 replies; 18+ messages in thread
From: Fil Dunsky @ 2026-10-13  6:19 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Thomas Gleixner, Radu Rendec, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Linus Walleij, Bartosz Golaszewski, Andi Shyti,
	Wim Van Sebroeck, Guenter Roeck, Russell King, Arnd Bergmann,
	linux-arm-kernel, linux-kernel, devicetree, linux-gpio, linux-i2c,
	linux-watchdog

On 13/10/2026 08:13, Krzysztof Kozlowski wrote:
> On 13/10/2026 05:47, Fil Dunsky via B4 Relay wrote:
>> +CONFIG_I2C_CS75XX=y
>
> This should be module.
>
[...]
>> +CONFIG_GPIO_CS75XX=y
>
> This as well.

Will do, both become =m in the next version.

Thanks,
Fil


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

end of thread, other threads:[~2026-10-13  6:19 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-13  3:46 [PATCH v2 00/11] Cortina Systems CS75xx: interrupt controller, GPIO, I2C and TWD watchdog Fil Dunsky via B4 Relay
2026-10-13  3:46 ` [PATCH v2 01/11] dt-bindings: interrupt-controller: Add Cortina Systems CS7542 peripheral interrupt controller Fil Dunsky via B4 Relay
2026-10-13  3:47 ` [PATCH v2 02/11] irqchip: Add Cortina Systems CS75xx " Fil Dunsky via B4 Relay
2026-10-13  3:47 ` [PATCH v2 03/11] dt-bindings: gpio: Add Cortina Systems CS7542 GPIO controller Fil Dunsky via B4 Relay
2026-10-13  3:53   ` sashiko-bot
2026-10-13  3:47 ` [PATCH v2 04/11] gpio: Add Cortina Systems CS75xx GPIO driver Fil Dunsky via B4 Relay
2026-10-13  3:58   ` sashiko-bot
2026-10-13  3:47 ` [PATCH v2 05/11] dt-bindings: i2c: Add Cortina Systems CS7542 I2C controller Fil Dunsky via B4 Relay
2026-10-13  3:47 ` [PATCH v2 06/11] i2c: Add Cortina Systems CS75xx I2C controller driver Fil Dunsky via B4 Relay
2026-10-13  4:04   ` sashiko-bot
2026-10-13  3:47 ` [PATCH v2 07/11] watchdog: Add ARM TWD (MPCore private) watchdog driver Fil Dunsky via B4 Relay
2026-10-13  4:10   ` sashiko-bot
2026-10-13  3:47 ` [PATCH v2 08/11] ARM: dts: cortina: cs7542: Add interrupt controller, GPIO and I2C Fil Dunsky via B4 Relay
2026-10-13  3:47 ` [PATCH v2 09/11] ARM: dts: cortina: almondplus: Add reset button and enable I2C Fil Dunsky via B4 Relay
2026-10-13  3:47 ` [PATCH v2 10/11] ARM: multi_v7_defconfig: Enable Cortina Systems CS75xx GPIO, I2C and TWD watchdog Fil Dunsky via B4 Relay
2026-10-13  6:13   ` Krzysztof Kozlowski
2026-10-13  6:19     ` Fil Dunsky
2026-10-13  3:47 ` [PATCH v2 11/11] MAINTAINERS: Add Cortina Systems CS75xx interrupt controller, GPIO and I2C Fil Dunsky via B4 Relay

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox