* [PATCH v3 01/11] dt-bindings: interrupt-controller: Add Cortina Systems CS7542 peripheral interrupt controller
2026-10-13 6:07 [PATCH v3 00/11] Cortina Systems CS75xx: interrupt controller, GPIO, I2C and TWD watchdog Fil Dunsky via B4 Relay
@ 2026-10-13 6:07 ` Fil Dunsky via B4 Relay
2026-10-13 6:07 ` [PATCH v3 02/11] irqchip: Add Cortina Systems CS75xx " Fil Dunsky via B4 Relay
` (10 subsequent siblings)
11 siblings, 0 replies; 14+ messages in thread
From: Fil Dunsky via B4 Relay @ 2026-10-13 6:07 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] 14+ messages in thread* [PATCH v3 02/11] irqchip: Add Cortina Systems CS75xx peripheral interrupt controller
2026-10-13 6:07 [PATCH v3 00/11] Cortina Systems CS75xx: interrupt controller, GPIO, I2C and TWD watchdog Fil Dunsky via B4 Relay
2026-10-13 6:07 ` [PATCH v3 01/11] dt-bindings: interrupt-controller: Add Cortina Systems CS7542 peripheral interrupt controller Fil Dunsky via B4 Relay
@ 2026-10-13 6:07 ` Fil Dunsky via B4 Relay
2026-10-13 6:07 ` [PATCH v3 03/11] dt-bindings: gpio: Add Cortina Systems CS7542 GPIO controller Fil Dunsky via B4 Relay
` (9 subsequent siblings)
11 siblings, 0 replies; 14+ messages in thread
From: Fil Dunsky via B4 Relay @ 2026-10-13 6:07 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] 14+ messages in thread* [PATCH v3 03/11] dt-bindings: gpio: Add Cortina Systems CS7542 GPIO controller
2026-10-13 6:07 [PATCH v3 00/11] Cortina Systems CS75xx: interrupt controller, GPIO, I2C and TWD watchdog Fil Dunsky via B4 Relay
2026-10-13 6:07 ` [PATCH v3 01/11] dt-bindings: interrupt-controller: Add Cortina Systems CS7542 peripheral interrupt controller Fil Dunsky via B4 Relay
2026-10-13 6:07 ` [PATCH v3 02/11] irqchip: Add Cortina Systems CS75xx " Fil Dunsky via B4 Relay
@ 2026-10-13 6:07 ` Fil Dunsky via B4 Relay
2026-10-13 6:07 ` [PATCH v3 04/11] gpio: Add Cortina Systems CS75xx GPIO driver Fil Dunsky via B4 Relay
` (8 subsequent siblings)
11 siblings, 0 replies; 14+ messages in thread
From: Fil Dunsky via B4 Relay @ 2026-10-13 6:07 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] 14+ messages in thread* [PATCH v3 04/11] gpio: Add Cortina Systems CS75xx GPIO driver
2026-10-13 6:07 [PATCH v3 00/11] Cortina Systems CS75xx: interrupt controller, GPIO, I2C and TWD watchdog Fil Dunsky via B4 Relay
` (2 preceding siblings ...)
2026-10-13 6:07 ` [PATCH v3 03/11] dt-bindings: gpio: Add Cortina Systems CS7542 GPIO controller Fil Dunsky via B4 Relay
@ 2026-10-13 6:07 ` Fil Dunsky via B4 Relay
2026-10-13 6:07 ` [PATCH v3 05/11] dt-bindings: i2c: Add Cortina Systems CS7542 I2C controller Fil Dunsky via B4 Relay
` (7 subsequent siblings)
11 siblings, 0 replies; 14+ messages in thread
From: Fil Dunsky via B4 Relay @ 2026-10-13 6:07 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] 14+ messages in thread* [PATCH v3 05/11] dt-bindings: i2c: Add Cortina Systems CS7542 I2C controller
2026-10-13 6:07 [PATCH v3 00/11] Cortina Systems CS75xx: interrupt controller, GPIO, I2C and TWD watchdog Fil Dunsky via B4 Relay
` (3 preceding siblings ...)
2026-10-13 6:07 ` [PATCH v3 04/11] gpio: Add Cortina Systems CS75xx GPIO driver Fil Dunsky via B4 Relay
@ 2026-10-13 6:07 ` Fil Dunsky via B4 Relay
2026-10-13 6:07 ` [PATCH v3 06/11] i2c: Add Cortina Systems CS75xx I2C controller driver Fil Dunsky via B4 Relay
` (6 subsequent siblings)
11 siblings, 0 replies; 14+ messages in thread
From: Fil Dunsky via B4 Relay @ 2026-10-13 6:07 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] 14+ messages in thread* [PATCH v3 06/11] i2c: Add Cortina Systems CS75xx I2C controller driver
2026-10-13 6:07 [PATCH v3 00/11] Cortina Systems CS75xx: interrupt controller, GPIO, I2C and TWD watchdog Fil Dunsky via B4 Relay
` (4 preceding siblings ...)
2026-10-13 6:07 ` [PATCH v3 05/11] dt-bindings: i2c: Add Cortina Systems CS7542 I2C controller Fil Dunsky via B4 Relay
@ 2026-10-13 6:07 ` Fil Dunsky via B4 Relay
2026-10-13 6:07 ` [PATCH v3 07/11] watchdog: Add ARM TWD (MPCore private) watchdog driver Fil Dunsky via B4 Relay
` (5 subsequent siblings)
11 siblings, 0 replies; 14+ messages in thread
From: Fil Dunsky via B4 Relay @ 2026-10-13 6:07 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 | 392 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 404 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..d39c8e75a
--- /dev/null
+++ b/drivers/i2c/busses/i2c-cs75xx.c
@@ -0,0 +1,392 @@
+// 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;
+ int irq;
+ u32 prer;
+
+ 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)
+{
+ 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, i2c->prer) | CS75XX_I2C_CFG_EN,
+ i2c->base + CS75XX_I2C_CFG);
+ usleep_range(5000, 10000);
+}
+
+/*
+ * Reset the controller and enable its interrupt again, with the handler
+ * kept away from the registers meanwhile
+ */
+static void cs75xx_i2c_reset(struct cs75xx_i2c *i2c)
+{
+ disable_irq(i2c->irq);
+ 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);
+ enable_irq(i2c->irq);
+}
+
+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;
+
+ /* Another master may hold the bus for a whole transfer */
+ if (readl_poll_timeout(i2c->base + CS75XX_I2C_ACK, val,
+ !(val & CS75XX_I2C_ACK_BUSY), 100,
+ jiffies_to_usecs(adap->timeout))) {
+ 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;
+ unsigned long ref_rate;
+ struct clk *clk;
+ u32 bus_rate;
+ u64 div;
+ int 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);
+
+ i2c->irq = platform_get_irq(pdev, 0);
+ if (i2c->irq < 0)
+ return i2c->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");
+
+ ref_rate = clk_get_rate(clk);
+ if (device_property_read_u32(dev, "clock-frequency", &bus_rate))
+ bus_rate = I2C_MAX_STANDARD_MODE_FREQ;
+ if (!bus_rate || bus_rate > I2C_MAX_FAST_MODE_FREQ)
+ return dev_err_probe(dev, -EINVAL,
+ "unsupported bus frequency %u Hz\n",
+ bus_rate);
+
+ div = DIV_ROUND_UP_ULL(ref_rate, 5ULL * bus_rate);
+ if (!div || div - 1 > FIELD_MAX(CS75XX_I2C_CFG_PRER))
+ return dev_err_probe(dev, -EINVAL,
+ "cannot derive %u Hz from %lu Hz\n",
+ bus_rate, ref_rate);
+ i2c->prer = div - 1;
+
+ 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, i2c->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", bus_rate,
+ 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] 14+ messages in thread* [PATCH v3 07/11] watchdog: Add ARM TWD (MPCore private) watchdog driver
2026-10-13 6:07 [PATCH v3 00/11] Cortina Systems CS75xx: interrupt controller, GPIO, I2C and TWD watchdog Fil Dunsky via B4 Relay
` (5 preceding siblings ...)
2026-10-13 6:07 ` [PATCH v3 06/11] i2c: Add Cortina Systems CS75xx I2C controller driver Fil Dunsky via B4 Relay
@ 2026-10-13 6:07 ` Fil Dunsky via B4 Relay
2026-10-13 6:07 ` [PATCH v3 08/11] ARM: dts: cortina: cs7542: Add interrupt controller, GPIO and I2C Fil Dunsky via B4 Relay
` (4 subsequent siblings)
11 siblings, 0 replies; 14+ messages in thread
From: Fil Dunsky via B4 Relay @ 2026-10-13 6:07 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 | 472 +++++++++++++++++++++++++++++++++++++++++
4 files changed, 494 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..18875ae24
--- /dev/null
+++ b/drivers/watchdog/arm_twd_wdt.c
@@ -0,0 +1,472 @@
+// 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/pm.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;
+ struct notifier_block clk_nb;
+ /* Changed under cpus_read_lock() once the hotplug instance exists */
+ unsigned long rate;
+ bool armed;
+ bool resume_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);
+}
+
+/*
+ * The TWD runs from the peripheral clock, which follows the CPU clock. While
+ * the rate goes up, count with the new rate already: the watchdog then just
+ * fires later than asked until the change is done.
+ */
+static int twd_wdt_clk_notify(struct notifier_block *nb, unsigned long event,
+ void *data)
+{
+ struct twd_wdt *wdt = container_of(nb, struct twd_wdt, clk_nb);
+ struct clk_notifier_data *cnd = data;
+ unsigned long rate;
+
+ switch (event) {
+ case PRE_RATE_CHANGE:
+ if (cnd->new_rate <= cnd->old_rate)
+ return NOTIFY_OK;
+ rate = cnd->new_rate;
+ break;
+ case POST_RATE_CHANGE:
+ rate = cnd->new_rate;
+ break;
+ case ABORT_RATE_CHANGE:
+ rate = cnd->old_rate;
+ break;
+ default:
+ return NOTIFY_DONE;
+ }
+
+ if (!rate)
+ return NOTIFY_OK;
+
+ cpus_read_lock();
+ wdt->rate = rate;
+ if (wdt->armed)
+ on_each_cpu(twd_wdt_cpu_ping, wdt, 1);
+ cpus_read_unlock();
+
+ return NOTIFY_OK;
+}
+
+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->clk_nb.notifier_call = twd_wdt_clk_notify;
+ ret = devm_clk_notifier_register(dev, clk, &wdt->clk_nb);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register clock notifier\n");
+ platform_set_drvdata(pdev, wdt);
+
+ 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);
+
+/*
+ * The watchdogs lose their state in suspend. The secondary CPUs get theirs
+ * back through the hotplug state, the boot CPU through resume.
+ */
+static int twd_wdt_suspend(struct device *dev)
+{
+ struct twd_wdt *wdt = dev_get_drvdata(dev);
+
+ wdt->resume_armed = wdt->armed;
+ if (wdt->resume_armed)
+ twd_wdt_stop(&wdt->wdd);
+
+ return 0;
+}
+
+static int twd_wdt_resume(struct device *dev)
+{
+ struct twd_wdt *wdt = dev_get_drvdata(dev);
+
+ if (wdt->resume_armed)
+ twd_wdt_start(&wdt->wdd);
+
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(twd_wdt_pm_ops, twd_wdt_suspend, twd_wdt_resume);
+
+static struct platform_driver twd_wdt_driver = {
+ .probe = twd_wdt_probe,
+ .driver = {
+ .name = "arm-twd-wdt",
+ .of_match_table = twd_wdt_of_match,
+ .pm = pm_sleep_ptr(&twd_wdt_pm_ops),
+ },
+};
+
+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] 14+ messages in thread* [PATCH v3 08/11] ARM: dts: cortina: cs7542: Add interrupt controller, GPIO and I2C
2026-10-13 6:07 [PATCH v3 00/11] Cortina Systems CS75xx: interrupt controller, GPIO, I2C and TWD watchdog Fil Dunsky via B4 Relay
` (6 preceding siblings ...)
2026-10-13 6:07 ` [PATCH v3 07/11] watchdog: Add ARM TWD (MPCore private) watchdog driver Fil Dunsky via B4 Relay
@ 2026-10-13 6:07 ` Fil Dunsky via B4 Relay
2026-10-13 6:07 ` [PATCH v3 09/11] ARM: dts: cortina: almondplus: Add reset button and enable I2C Fil Dunsky via B4 Relay
` (3 subsequent siblings)
11 siblings, 0 replies; 14+ messages in thread
From: Fil Dunsky via B4 Relay @ 2026-10-13 6:07 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] 14+ messages in thread* [PATCH v3 09/11] ARM: dts: cortina: almondplus: Add reset button and enable I2C
2026-10-13 6:07 [PATCH v3 00/11] Cortina Systems CS75xx: interrupt controller, GPIO, I2C and TWD watchdog Fil Dunsky via B4 Relay
` (7 preceding siblings ...)
2026-10-13 6:07 ` [PATCH v3 08/11] ARM: dts: cortina: cs7542: Add interrupt controller, GPIO and I2C Fil Dunsky via B4 Relay
@ 2026-10-13 6:07 ` Fil Dunsky via B4 Relay
2026-10-13 6:07 ` [PATCH v3 10/11] ARM: multi_v7_defconfig: Enable Cortina Systems CS75xx GPIO, I2C and TWD watchdog Fil Dunsky via B4 Relay
` (2 subsequent siblings)
11 siblings, 0 replies; 14+ messages in thread
From: Fil Dunsky via B4 Relay @ 2026-10-13 6:07 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] 14+ messages in thread* [PATCH v3 10/11] ARM: multi_v7_defconfig: Enable Cortina Systems CS75xx GPIO, I2C and TWD watchdog
2026-10-13 6:07 [PATCH v3 00/11] Cortina Systems CS75xx: interrupt controller, GPIO, I2C and TWD watchdog Fil Dunsky via B4 Relay
` (8 preceding siblings ...)
2026-10-13 6:07 ` [PATCH v3 09/11] ARM: dts: cortina: almondplus: Add reset button and enable I2C Fil Dunsky via B4 Relay
@ 2026-10-13 6:07 ` Fil Dunsky via B4 Relay
2026-10-13 6:07 ` [PATCH v3 11/11] MAINTAINERS: Add Cortina Systems CS75xx interrupt controller, GPIO and I2C Fil Dunsky via B4 Relay
2026-10-13 6:18 ` [PATCH v3 00/11] Cortina Systems CS75xx: interrupt controller, GPIO, I2C and TWD watchdog Krzysztof Kozlowski
11 siblings, 0 replies; 14+ messages in thread
From: Fil Dunsky via B4 Relay @ 2026-10-13 6:07 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] 14+ messages in thread* [PATCH v3 11/11] MAINTAINERS: Add Cortina Systems CS75xx interrupt controller, GPIO and I2C
2026-10-13 6:07 [PATCH v3 00/11] Cortina Systems CS75xx: interrupt controller, GPIO, I2C and TWD watchdog Fil Dunsky via B4 Relay
` (9 preceding siblings ...)
2026-10-13 6:07 ` [PATCH v3 10/11] ARM: multi_v7_defconfig: Enable Cortina Systems CS75xx GPIO, I2C and TWD watchdog Fil Dunsky via B4 Relay
@ 2026-10-13 6:07 ` Fil Dunsky via B4 Relay
2026-10-13 6:18 ` [PATCH v3 00/11] Cortina Systems CS75xx: interrupt controller, GPIO, I2C and TWD watchdog Krzysztof Kozlowski
11 siblings, 0 replies; 14+ messages in thread
From: Fil Dunsky via B4 Relay @ 2026-10-13 6:07 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] 14+ messages in thread* Re: [PATCH v3 00/11] Cortina Systems CS75xx: interrupt controller, GPIO, I2C and TWD watchdog
2026-10-13 6:07 [PATCH v3 00/11] Cortina Systems CS75xx: interrupt controller, GPIO, I2C and TWD watchdog Fil Dunsky via B4 Relay
` (10 preceding siblings ...)
2026-10-13 6:07 ` [PATCH v3 11/11] MAINTAINERS: Add Cortina Systems CS75xx interrupt controller, GPIO and I2C Fil Dunsky via B4 Relay
@ 2026-10-13 6:18 ` Krzysztof Kozlowski
2026-10-13 6:19 ` Fil Dunsky
11 siblings, 1 reply; 14+ messages in thread
From: Krzysztof Kozlowski @ 2026-10-13 6:18 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 08:07, Fil Dunsky via B4 Relay wrote:
> 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+"
> v6 [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.
>
Slow down. One small patchset per 24h. If you send some big work, then
once per 48h.
Implement previous review.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [PATCH v3 00/11] Cortina Systems CS75xx: interrupt controller, GPIO, I2C and TWD watchdog
2026-10-13 6:18 ` [PATCH v3 00/11] Cortina Systems CS75xx: interrupt controller, GPIO, I2C and TWD watchdog Krzysztof Kozlowski
@ 2026-10-13 6:19 ` Fil Dunsky
0 siblings, 0 replies; 14+ 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:18, Krzysztof Kozlowski wrote:
> Slow down. One small patchset per 24h. If you send some big work, then
> once per 48h.
>
> Implement previous review.
Sorry, understood. Your comments on v2 arrived after v3 had gone out;
they are done in my tree (I2C and GPIO as modules in multi_v7_defconfig)
and will be in v4, which I will not send before 48 hours have passed,
and the platform series likewise.
Thanks,
Fil
^ permalink raw reply [flat|nested] 14+ messages in thread