* [PATCH v7 00/15] Ambarella CV75 SoC minimal bring-up
@ 2026-09-15 11:15 Long Zhao via B4 Relay
2026-09-15 11:15 ` [PATCH v7 01/15] dt-bindings: arm: add Ambarella CV75 platforms Long Zhao via B4 Relay
` (15 more replies)
0 siblings, 16 replies; 34+ messages in thread
From: Long Zhao via B4 Relay @ 2026-09-15 11:15 UTC (permalink / raw)
To: Arnd Bergmann, Krzysztof Kozlowski, Alexandre Belloni, soc,
linux-arm-kernel
Cc: Long Zhao, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Michael Turquette, Stephen Boyd, Jerome Brunet, Linus Walleij,
Bartosz Golaszewski, Greg Kroah-Hartman, Jiri Slaby,
Andy Shevchenko, Ilpo Järvinen, Catalin Marinas,
Will Deacon, Long Zhao, Lee Jones, mfd, devicetree, linux-clk,
linux-gpio, linux-serial, linux-kernel, Krzysztof Kozlowski
This series adds minimal Ambarella CV75 support for early bring-up with
a serial console: DT bindings, RCT clocks, pinctrl, PL061 GPIO via
gpio-regmap, 8250_dw UART quirks, ARCH_AMBARELLA, CV75 EVK DT, and
MAINTAINERS.
This is a single unified series. Please apply via the SoC
tree; subsystem maintainers are Cc'd for their pieces.
GPIO data path uses gpio-regmap with two layouts (ARM PrimeCell and
Ambarella). IRQ stays a custom PL061 irqchip: a linear domain is
created after gpio_regmap_register(), attached with
gpiochip_irqchip_add_domain() via gpio_regmap_get_chip(), and the
chained handler uses gpio_chip as chip data. gpio-regmap girq and
regmap-irq are not used.
Verified with:
- scripts/checkpatch.pl --strict (0 errors)
- make dt_binding_check DT_SCHEMA_FILES=ambarella:pl061-gpio.yaml:syscon.yaml:snps-dw-apb-uart.yaml
- make CHECK_DTBS=y ambarella/cv75-evk.dtb
- arm64 build of new drivers/DTS
- CV75 EVK: console via 8250_dw (dw-apb-uart); GPIO edge IRQ
(gpio-hammer / gpio-event-mon)
Signed-off-by: Long Zhao <longzhao@ambarella.com>
---
Changes in v7:
- gpio: 10/15 convert ARM PL061 to gpio-regmap; 11/15 add Ambarella
layout with a second gpio_regmap config (Andy)
- gpio: 10/15 drop gpio-regmap girq; custom irqchip with a linear
domain, gpio_chip chip data, and a chained handler (Andy)
- gpio-regmap: 08/15 write_data_after_dir; 09/15 gpio_regmap_get_chip()
and quirk kerneldoc (Andy)
- dt-bindings: gpio: 05/15 $ref arm/primecell.yaml and
unevaluatedProperties (Rob)
- clk: fold the RCT driver into a single clk-cv75.c (Jerome)
- serial: 8250_dw startup only updates the up->ier shadow (Ilpo)
- collect lore tags on unchanged patches (Linus Reviewed-by; Krzysztof
Acked-by on the UART binding)
Link to v6:
https://lore.kernel.org/r/20260904-cv75-v5-v6-0-e918514cb3b1@ambarella.com
Changes in v6:
- gpio: convert gpio-pl061 data path to gpio-regmap; keep PL061 irqchip
(Andy, Linus). Add write_data_after_dir + girq helpers to gpio-regmap.
Ambarella layout uses JEP106 periphid 0x000e8061; no AFSEL clearing in
the GPIO driver; Ambarella skips unverified GPIO PM save/restore
- serial: restore 8250_dw Ambarella quirks from v3; drop standalone
8250_ambarella (Andy). Commit message clarifies Ambarella IP with a
DW-like register map needing only a thin quirk. Binding under
snps-dw-apb-uart; DT uses ambarella,cv75-uart + snps,dw-apb-uart with
reg-shift/reg-io-width
- clock/RCT: document #reset-cells (Krzysztof); soft-reset ID header and
reset provider deferred (no resets= consumers in this series)
- arm64/Kconfig: do not select ARM_PSCI / HAVE_ARM_ARCH_TIMER (Sashiko)
- pinctrl: no Ambarella suspend/resume claim without validated PM
Link to v5:
https://lore.kernel.org/r/20260818-cv75-v5-v5-0-7bbb12f0360f@ambarella.com
Link to v2:
https://lore.kernel.org/r/20260806-longzhao-upstream-cv75-v2-v2-0-6b09707c5fe9@ambarella.com
Link to v3-A:
https://lore.kernel.org/r/20260813-cv75-v3-a-v3-0-cfdd20b1b4dd@ambarella.com
Link to v3-B:
https://lore.kernel.org/r/20260813-cv75-v3-b-v3-0-1d82e18da09b@ambarella.com
Link to v3-C:
https://lore.kernel.org/r/20260813-cv75-v3-c-v3-0-9352b3fd6026@ambarella.com
Link to v4-B:
https://lore.kernel.org/r/20260814-cv75-v4-b-v4-0-622e63153d97@ambarella.com
To: Arnd Bergmann <arnd@arndb.de>
To: Krzysztof Kozlowski <krzk@kernel.org>
To: Alexandre Belloni <alexandre.belloni@bootlin.com>
To: soc@lists.linux.dev
To: linux-arm-kernel@lists.infradead.org
Cc: Rob Herring <robh@kernel.org>
Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
Cc: Conor Dooley <conor+dt@kernel.org>
Cc: Michael Turquette <mturquette@baylibre.com>
Cc: Stephen Boyd <sboyd@kernel.org>
Cc: Jerome Brunet <jbrunet@baylibre.com>
Cc: Linus Walleij <linusw@kernel.org>
Cc: Bartosz Golaszewski <brgl@kernel.org>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Jiri Slaby <jirislaby@kernel.org>
Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Cc: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Long Zhao <zl020895@163.com>
Cc: Lee Jones <lee@kernel.org>
Cc: mfd@lists.linux.dev
Cc: devicetree@vger.kernel.org
Cc: linux-clk@vger.kernel.org
Cc: linux-gpio@vger.kernel.org
Cc: linux-serial@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
---
Long Zhao (15):
dt-bindings: arm: add Ambarella CV75 platforms
dt-bindings: mfd: syscon: add Ambarella CV75 secure scratchpad
dt-bindings: clock: add Ambarella CV75 RCT
dt-bindings: pinctrl: add Ambarella CV75 pinctrl
dt-bindings: gpio: pl061: add Ambarella CV75 variant
dt-bindings: serial: snps-dw-apb-uart: add ambarella,cv75-uart
clk: ambarella: add CV75 RCT clock controller
gpiolib: regmap: add write_data_after_dir quirk
gpiolib: regmap: add gpio_regmap_get_chip()
gpio: pl061: convert to gpio-regmap and a custom irqchip
gpio: pl061: add Ambarella register-layout variant
pinctrl: ambarella: add CV75 pin controller
serial: 8250_dw: add Ambarella CV75 quirks
arm64: ambarella: add ARCH_AMBARELLA and CV75 EVK DT
MAINTAINERS: add ARM/AMBARELLA SoC support
.../devicetree/bindings/arm/ambarella.yaml | 24 +
.../bindings/clock/ambarella,cv75-rct.yaml | 64 +++
.../devicetree/bindings/gpio/pl061-gpio.yaml | 25 +-
Documentation/devicetree/bindings/mfd/syscon.yaml | 1 +
.../bindings/pinctrl/ambarella,cv75-pinctrl.yaml | 237 +++++++++
.../bindings/serial/snps-dw-apb-uart.yaml | 1 +
.../devicetree/bindings/vendor-prefixes.yaml | 2 +
MAINTAINERS | 14 +
arch/arm64/Kconfig.platforms | 6 +
arch/arm64/boot/dts/Makefile | 1 +
arch/arm64/boot/dts/ambarella/Makefile | 3 +
arch/arm64/boot/dts/ambarella/cv75-evk.dts | 30 ++
arch/arm64/boot/dts/ambarella/cv75.dtsi | 177 +++++++
arch/arm64/configs/defconfig | 1 +
drivers/clk/Kconfig | 1 +
drivers/clk/Makefile | 1 +
drivers/clk/ambarella/Kconfig | 9 +
drivers/clk/ambarella/Makefile | 3 +
drivers/clk/ambarella/clk-cv75.c | 366 +++++++++++++
drivers/gpio/Kconfig | 2 +
drivers/gpio/gpio-pl061.c | 589 ++++++++++++++-------
drivers/gpio/gpio-regmap.c | 40 +-
drivers/pinctrl/Kconfig | 17 +
drivers/pinctrl/Makefile | 2 +
drivers/pinctrl/pinctrl-ambarella-cv75.c | 554 +++++++++++++++++++
drivers/pinctrl/pinctrl-ambarella.c | 554 +++++++++++++++++++
drivers/pinctrl/pinctrl-ambarella.h | 44 ++
drivers/tty/serial/8250/8250_dw.c | 32 ++
include/dt-bindings/clock/ambarella,cv75-rct.h | 14 +
include/linux/amba/bus.h | 1 +
include/linux/gpio/regmap.h | 9 +
31 files changed, 2625 insertions(+), 199 deletions(-)
---
base-commit: 34cf6dafc47441dfb6b356a095b89c3585a93714
change-id: 20260818-cv75-v5-9e2039b349bc
Best regards,
--
Long Zhao <longzhao@ambarella.com>
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v7 01/15] dt-bindings: arm: add Ambarella CV75 platforms
2026-09-15 11:15 [PATCH v7 00/15] Ambarella CV75 SoC minimal bring-up Long Zhao via B4 Relay
@ 2026-09-15 11:15 ` Long Zhao via B4 Relay
2026-09-15 11:15 ` [PATCH v7 02/15] dt-bindings: mfd: syscon: add Ambarella CV75 secure scratchpad Long Zhao via B4 Relay
` (14 subsequent siblings)
15 siblings, 0 replies; 34+ messages in thread
From: Long Zhao via B4 Relay @ 2026-09-15 11:15 UTC (permalink / raw)
To: Arnd Bergmann, Krzysztof Kozlowski, Alexandre Belloni, soc,
linux-arm-kernel
Cc: Long Zhao, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Michael Turquette, Stephen Boyd, Jerome Brunet, Linus Walleij,
Bartosz Golaszewski, Greg Kroah-Hartman, Jiri Slaby,
Andy Shevchenko, Ilpo Järvinen, Catalin Marinas,
Will Deacon, Long Zhao, Lee Jones, mfd, devicetree, linux-clk,
linux-gpio, linux-serial, linux-kernel, Krzysztof Kozlowski
From: Long Zhao <longzhao@ambarella.com>
Add the Ambarella vendor prefix and CV75 EVK board compatible for the
initial SoC bring-up.
Reviewed-by: Linus Walleij <linusw@kernel.org>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Signed-off-by: Long Zhao <longzhao@ambarella.com>
---
.../devicetree/bindings/arm/ambarella.yaml | 24 ++++++++++++++++++++++
.../devicetree/bindings/vendor-prefixes.yaml | 2 ++
2 files changed, 26 insertions(+)
diff --git a/Documentation/devicetree/bindings/arm/ambarella.yaml b/Documentation/devicetree/bindings/arm/ambarella.yaml
new file mode 100644
index 000000000000..72946e94fa76
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/ambarella.yaml
@@ -0,0 +1,24 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/arm/ambarella.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Ambarella SoC platforms
+
+maintainers:
+ - Long Zhao <longzhao@ambarella.com>
+
+properties:
+ $nodename:
+ const: "/"
+
+ compatible:
+ oneOf:
+ - description: Ambarella CV75 boards
+ items:
+ - enum:
+ - ambarella,cv75-evk
+ - const: ambarella,cv75
+
+additionalProperties: true
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index 85a27937832e..f48aa6dabbe9 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -112,6 +112,8 @@ patternProperties:
description: Amarula Solutions
"^amazon,.*":
description: Amazon.com, Inc.
+ "^ambarella,.*":
+ description: Ambarella, Inc.
"^amcc,.*":
description: Applied Micro Circuits Corporation (APM, formally AMCC)
"^amd,.*":
--
2.34.1
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v7 02/15] dt-bindings: mfd: syscon: add Ambarella CV75 secure scratchpad
2026-09-15 11:15 [PATCH v7 00/15] Ambarella CV75 SoC minimal bring-up Long Zhao via B4 Relay
2026-09-15 11:15 ` [PATCH v7 01/15] dt-bindings: arm: add Ambarella CV75 platforms Long Zhao via B4 Relay
@ 2026-09-15 11:15 ` Long Zhao via B4 Relay
2026-09-15 11:15 ` [PATCH v7 03/15] dt-bindings: clock: add Ambarella CV75 RCT Long Zhao via B4 Relay
` (13 subsequent siblings)
15 siblings, 0 replies; 34+ messages in thread
From: Long Zhao via B4 Relay @ 2026-09-15 11:15 UTC (permalink / raw)
To: Arnd Bergmann, Krzysztof Kozlowski, Alexandre Belloni, soc,
linux-arm-kernel
Cc: Long Zhao, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Michael Turquette, Stephen Boyd, Jerome Brunet, Linus Walleij,
Bartosz Golaszewski, Greg Kroah-Hartman, Jiri Slaby,
Andy Shevchenko, Ilpo Järvinen, Catalin Marinas,
Will Deacon, Long Zhao, Lee Jones, mfd, devicetree, linux-clk,
linux-gpio, linux-serial, linux-kernel, Krzysztof Kozlowski
From: Long Zhao <longzhao@ambarella.com>
Allow ambarella,cv75-sec-scratchpad as a syscon for the shared secure
scratchpad registers on CV75.
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Signed-off-by: Long Zhao <longzhao@ambarella.com>
---
Documentation/devicetree/bindings/mfd/syscon.yaml | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/mfd/syscon.yaml b/Documentation/devicetree/bindings/mfd/syscon.yaml
index fe882d3f828b..9ebf702c06f1 100644
--- a/Documentation/devicetree/bindings/mfd/syscon.yaml
+++ b/Documentation/devicetree/bindings/mfd/syscon.yaml
@@ -32,6 +32,7 @@ properties:
- allwinner,sun50i-a64-system-controller
- altr,l3regs
- altr,sdr-ctl
+ - ambarella,cv75-sec-scratchpad
- amd,pensando-elba-syscon
- amlogic,meson-mx-assist
- amlogic,meson-mx-bootrom
--
2.34.1
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v7 03/15] dt-bindings: clock: add Ambarella CV75 RCT
2026-09-15 11:15 [PATCH v7 00/15] Ambarella CV75 SoC minimal bring-up Long Zhao via B4 Relay
2026-09-15 11:15 ` [PATCH v7 01/15] dt-bindings: arm: add Ambarella CV75 platforms Long Zhao via B4 Relay
2026-09-15 11:15 ` [PATCH v7 02/15] dt-bindings: mfd: syscon: add Ambarella CV75 secure scratchpad Long Zhao via B4 Relay
@ 2026-09-15 11:15 ` Long Zhao via B4 Relay
2026-09-15 11:15 ` [PATCH v7 04/15] dt-bindings: pinctrl: add Ambarella CV75 pinctrl Long Zhao via B4 Relay
` (12 subsequent siblings)
15 siblings, 0 replies; 34+ messages in thread
From: Long Zhao via B4 Relay @ 2026-09-15 11:15 UTC (permalink / raw)
To: Arnd Bergmann, Krzysztof Kozlowski, Alexandre Belloni, soc,
linux-arm-kernel
Cc: Long Zhao, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Michael Turquette, Stephen Boyd, Jerome Brunet, Linus Walleij,
Bartosz Golaszewski, Greg Kroah-Hartman, Jiri Slaby,
Andy Shevchenko, Ilpo Järvinen, Catalin Marinas,
Will Deacon, Long Zhao, Lee Jones, mfd, devicetree, linux-clk,
linux-gpio, linux-serial, linux-kernel, Krzysztof Kozlowski
From: Long Zhao <longzhao@ambarella.com>
Document the CV75 RCT clock controller and the clock ID header used by
DT consumers.
Document #reset-cells for the RCT reset-provider role. Soft-reset line
IDs and the reset provider driver are deferred; this series has no
resets= consumers.
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Signed-off-by: Long Zhao <longzhao@ambarella.com>
---
.../bindings/clock/ambarella,cv75-rct.yaml | 64 ++++++++++++++++++++++
include/dt-bindings/clock/ambarella,cv75-rct.h | 14 +++++
2 files changed, 78 insertions(+)
diff --git a/Documentation/devicetree/bindings/clock/ambarella,cv75-rct.yaml b/Documentation/devicetree/bindings/clock/ambarella,cv75-rct.yaml
new file mode 100644
index 000000000000..be89bd75f9e2
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/ambarella,cv75-rct.yaml
@@ -0,0 +1,64 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/ambarella,cv75-rct.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Ambarella CV75 RCT Clock Controller
+
+maintainers:
+ - Long Zhao <longzhao@ambarella.com>
+
+description: |
+ The RCT (Reset and Clock Tree) block on the Ambarella CV75 SoC provides
+ PLLs, clock muxes, dividers and soft-reset controls for on-chip
+ peripherals.
+
+ The same register region is also exposed as a syscon for other SoC
+ blocks (e.g. pin drive-strength), hence the "syscon" compatible.
+
+ Clock indices are defined in include/dt-bindings/clock/ambarella,cv75-rct.h.
+
+properties:
+
+ compatible:
+ items:
+ - const: ambarella,cv75-rct
+ - const: syscon
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ description: Reference crystal oscillator (typically 24 MHz)
+ maxItems: 1
+
+ clock-names:
+ const: osc
+
+ "#clock-cells":
+ const: 1
+
+ "#reset-cells":
+ const: 1
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - "#clock-cells"
+ - "#reset-cells"
+
+additionalProperties: false
+
+examples:
+ - |
+ clock-controller@ed080000 {
+ compatible = "ambarella,cv75-rct", "syscon";
+ reg = <0xed080000 0x1000>;
+ clocks = <&osc>;
+ clock-names = "osc";
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ };
diff --git a/include/dt-bindings/clock/ambarella,cv75-rct.h b/include/dt-bindings/clock/ambarella,cv75-rct.h
new file mode 100644
index 000000000000..b3e965b4ff2e
--- /dev/null
+++ b/include/dt-bindings/clock/ambarella,cv75-rct.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */
+/*
+ * Copyright (C) 2026 Ambarella, Inc.
+ */
+
+#ifndef _DT_BINDINGS_CLOCK_AMBARELLA_CV75_RCT_H
+#define _DT_BINDINGS_CLOCK_AMBARELLA_CV75_RCT_H
+
+#define CV75_GCLK_UART0 0
+#define CV75_GCLK_CORE 1
+#define CV75_GCLK_AHB 2
+#define CV75_GCLK_APB 3
+
+#endif
--
2.34.1
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v7 04/15] dt-bindings: pinctrl: add Ambarella CV75 pinctrl
2026-09-15 11:15 [PATCH v7 00/15] Ambarella CV75 SoC minimal bring-up Long Zhao via B4 Relay
` (2 preceding siblings ...)
2026-09-15 11:15 ` [PATCH v7 03/15] dt-bindings: clock: add Ambarella CV75 RCT Long Zhao via B4 Relay
@ 2026-09-15 11:15 ` Long Zhao via B4 Relay
2026-09-15 11:15 ` [PATCH v7 05/15] dt-bindings: gpio: pl061: add Ambarella CV75 variant Long Zhao via B4 Relay
` (11 subsequent siblings)
15 siblings, 0 replies; 34+ messages in thread
From: Long Zhao via B4 Relay @ 2026-09-15 11:15 UTC (permalink / raw)
To: Arnd Bergmann, Krzysztof Kozlowski, Alexandre Belloni, soc,
linux-arm-kernel
Cc: Long Zhao, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Michael Turquette, Stephen Boyd, Jerome Brunet, Linus Walleij,
Bartosz Golaszewski, Greg Kroah-Hartman, Jiri Slaby,
Andy Shevchenko, Ilpo Järvinen, Catalin Marinas,
Will Deacon, Long Zhao, Lee Jones, mfd, devicetree, linux-clk,
linux-gpio, linux-serial, linux-kernel, Krzysztof Kozlowski
From: Long Zhao <longzhao@ambarella.com>
Document the CV75 pin controller binding using generic function and
groups properties for pinmux configuration.
Reviewed-by: Linus Walleij <linusw@kernel.org>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Signed-off-by: Long Zhao <longzhao@ambarella.com>
---
.../bindings/pinctrl/ambarella,cv75-pinctrl.yaml | 237 +++++++++++++++++++++
1 file changed, 237 insertions(+)
diff --git a/Documentation/devicetree/bindings/pinctrl/ambarella,cv75-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/ambarella,cv75-pinctrl.yaml
new file mode 100644
index 000000000000..ee464a0f80d9
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/ambarella,cv75-pinctrl.yaml
@@ -0,0 +1,237 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pinctrl/ambarella,cv75-pinctrl.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Ambarella CV75 Pin Controller
+
+maintainers:
+ - Long Zhao <longzhao@ambarella.com>
+
+description:
+ The Ambarella CV75 pin controller configures pin multiplexing, bias and
+ drive strength for the main GPIO banks. Pin groups and their mux settings
+ are defined by the pin controller driver and selected by function and group
+ names.
+
+allOf:
+ - $ref: pinctrl.yaml#
+
+properties:
+ compatible:
+ const: ambarella,cv75-pinctrl
+
+ reg:
+ maxItems: 1
+ description: IOMUX registers
+
+ ambarella,drive-strength-syscon:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description: RCT syscon containing the drive-strength registers.
+
+ ambarella,pull-syscon:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description: Secure scratchpad syscon containing the pull registers.
+
+patternProperties:
+ "^[a-z0-9-]+-pins$":
+ type: object
+ allOf:
+ - $ref: pinmux-node.yaml#
+ - $ref: pincfg-node.yaml#
+ additionalProperties: false
+
+ properties:
+ function:
+ enum:
+ - can0
+ - can1
+ - dmic0
+ - enet0
+ - hsync0
+ - hsync1
+ - i2c0
+ - i2c1
+ - i2c2
+ - i2c3
+ - i2cs
+ - i2s0
+ - i2s1
+ - ir
+ - pwm0
+ - pwm1
+ - pwm2
+ - pwm3
+ - pwm4
+ - pwm5
+ - pwm6
+ - pwm7
+ - pwm8
+ - pwm9
+ - pwm10
+ - pwm11
+ - sdmmc0
+ - sdmmc1
+ - snand
+ - spi0
+ - spi1
+ - spi2
+ - spi3
+ - spi_slave
+ - spinor
+ - uart0
+ - uart1
+ - uart2
+ - uart3
+ - uart4
+ - vin_master_sync
+ - vsync0
+ - vsync1
+ - vsync2
+ - vsync3
+ - wdt
+
+ groups:
+ items:
+ enum:
+ - can0
+ - can1
+ - dmic0
+ - enet_2nd_ref_clk_a
+ - enet_2nd_ref_clk_b
+ - enet_ext_osc_clk
+ - enet0_ptp_pps_o
+ - hsync0
+ - hsync1
+ - i2c0_a
+ - i2c0_b
+ - i2c1_a
+ - i2c1_b
+ - i2c2
+ - i2c3_a
+ - i2c3_b
+ - i2c3_c
+ - i2cs_a
+ - i2cs_b
+ - i2cs_c
+ - i2cs_d
+ - i2s0
+ - i2s1
+ - ir
+ - pwm0
+ - pwm1
+ - pwm2
+ - pwm3
+ - pwm4_a
+ - pwm4_b
+ - pwm5_a
+ - pwm5_b
+ - pwm6_a
+ - pwm6_b
+ - pwm7_a
+ - pwm7_b
+ - pwm8_a
+ - pwm8_b
+ - pwm9_a
+ - pwm9_b
+ - pwm10_a
+ - pwm10_b
+ - pwm11_a
+ - pwm11_b
+ - rgmii0
+ - rmii0
+ - sdmmc0_1bit
+ - sdmmc0_4bit
+ - sdmmc0_cd
+ - sdmmc0_hs_sel
+ - sdmmc0_reset
+ - sdmmc0_wp
+ - sdmmc1_1bit
+ - sdmmc1_4bit
+ - sdmmc1_cd
+ - sdmmc1_hs_sel
+ - sdmmc1_reset
+ - sdmmc1_wp
+ - snand
+ - spi0
+ - spi1
+ - spi2
+ - spi3_a
+ - spi3_b
+ - spi3_c
+ - spi_slave_a
+ - spi_slave_b
+ - spi_slave_c
+ - spi_slave_d
+ - spi_slave_e
+ - spinor
+ - uart0
+ - uart1
+ - uart1_flow
+ - uart2_a
+ - uart2_b
+ - uart2_c
+ - uart2_flow_a
+ - uart2_flow_b
+ - uart3_a
+ - uart3_b
+ - uart3_flow_a
+ - uart3_flow_b
+ - uart4_a
+ - uart4_b
+ - uart4_flow_a
+ - uart4_flow_b
+ - vin_master_sync_a
+ - vin_master_sync_b
+ - vin_master_sync_c
+ - vin_master_sync_d
+ - vin_master_sync_e
+ - vsync0
+ - vsync1
+ - vsync2
+ - vsync3
+ - wdt_a
+ - wdt_b
+ - wdt_c
+ - wdt_d
+ - wdt_e
+ - wdt_f
+ minItems: 1
+ maxItems: 6
+ uniqueItems: true
+
+ bias-disable: true
+ bias-pull-down: true
+ bias-pull-up: true
+
+ drive-strength:
+ enum: [3, 4, 5, 6, 7, 8, 9, 12]
+
+ required:
+ - function
+ - groups
+
+required:
+ - compatible
+ - reg
+ - ambarella,drive-strength-syscon
+ - ambarella,pull-syscon
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ pinctrl: pinctrl@e4010000 {
+ compatible = "ambarella,cv75-pinctrl";
+ reg = <0xe4010000 0x1000>;
+ ambarella,drive-strength-syscon = <&rct>;
+ ambarella,pull-syscon = <&scratchpad>;
+
+ uart0-pins {
+ function = "uart0";
+ groups = "uart0";
+ bias-disable;
+ drive-strength = <8>;
+ };
+ };
--
2.34.1
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v7 05/15] dt-bindings: gpio: pl061: add Ambarella CV75 variant
2026-09-15 11:15 [PATCH v7 00/15] Ambarella CV75 SoC minimal bring-up Long Zhao via B4 Relay
` (3 preceding siblings ...)
2026-09-15 11:15 ` [PATCH v7 04/15] dt-bindings: pinctrl: add Ambarella CV75 pinctrl Long Zhao via B4 Relay
@ 2026-09-15 11:15 ` Long Zhao via B4 Relay
2026-09-15 11:15 ` [PATCH v7 06/15] dt-bindings: serial: snps-dw-apb-uart: add ambarella,cv75-uart Long Zhao via B4 Relay
` (10 subsequent siblings)
15 siblings, 0 replies; 34+ messages in thread
From: Long Zhao via B4 Relay @ 2026-09-15 11:15 UTC (permalink / raw)
To: Arnd Bergmann, Krzysztof Kozlowski, Alexandre Belloni, soc,
linux-arm-kernel
Cc: Long Zhao, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Michael Turquette, Stephen Boyd, Jerome Brunet, Linus Walleij,
Bartosz Golaszewski, Greg Kroah-Hartman, Jiri Slaby,
Andy Shevchenko, Ilpo Järvinen, Catalin Marinas,
Will Deacon, Long Zhao, Lee Jones, mfd, devicetree, linux-clk,
linux-gpio, linux-serial, linux-kernel
From: Long Zhao <longzhao@ambarella.com>
Add ambarella,cv75-gpio as a PrimeCell-compatible PL061 variant, with
arm,primecell-periphid and gpio-ranges required. Reference
primecell.yaml and use unevaluatedProperties.
Signed-off-by: Long Zhao <longzhao@ambarella.com>
---
.../devicetree/bindings/gpio/pl061-gpio.yaml | 25 +++++++++++++++++++---
1 file changed, 22 insertions(+), 3 deletions(-)
diff --git a/Documentation/devicetree/bindings/gpio/pl061-gpio.yaml b/Documentation/devicetree/bindings/gpio/pl061-gpio.yaml
index 4d970e55104b..00d0d8ddd2c9 100644
--- a/Documentation/devicetree/bindings/gpio/pl061-gpio.yaml
+++ b/Documentation/devicetree/bindings/gpio/pl061-gpio.yaml
@@ -15,17 +15,36 @@ select:
properties:
compatible:
contains:
- const: arm,pl061
+ enum:
+ - arm,pl061
+ - ambarella,cv75-gpio
required:
- compatible
+allOf:
+ - $ref: /schemas/arm/primecell.yaml#
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: ambarella,cv75-gpio
+ then:
+ properties:
+ arm,primecell-periphid:
+ const: 0x000e8061
+ required:
+ - arm,primecell-periphid
+ - gpio-ranges
+
properties:
$nodename:
pattern: "^gpio@[0-9a-f]+$"
compatible:
items:
- - const: arm,pl061
+ - enum:
+ - ambarella,cv75-gpio
+ - arm,pl061
- const: arm,primecell
reg:
@@ -64,6 +83,6 @@ required:
- "#gpio-cells"
- gpio-controller
-additionalProperties: false
+unevaluatedProperties: false
...
--
2.34.1
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v7 06/15] dt-bindings: serial: snps-dw-apb-uart: add ambarella,cv75-uart
2026-09-15 11:15 [PATCH v7 00/15] Ambarella CV75 SoC minimal bring-up Long Zhao via B4 Relay
` (4 preceding siblings ...)
2026-09-15 11:15 ` [PATCH v7 05/15] dt-bindings: gpio: pl061: add Ambarella CV75 variant Long Zhao via B4 Relay
@ 2026-09-15 11:15 ` Long Zhao via B4 Relay
2026-09-15 11:15 ` [PATCH v7 07/15] clk: ambarella: add CV75 RCT clock controller Long Zhao via B4 Relay
` (9 subsequent siblings)
15 siblings, 0 replies; 34+ messages in thread
From: Long Zhao via B4 Relay @ 2026-09-15 11:15 UTC (permalink / raw)
To: Arnd Bergmann, Krzysztof Kozlowski, Alexandre Belloni, soc,
linux-arm-kernel
Cc: Long Zhao, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Michael Turquette, Stephen Boyd, Jerome Brunet, Linus Walleij,
Bartosz Golaszewski, Greg Kroah-Hartman, Jiri Slaby,
Andy Shevchenko, Ilpo Järvinen, Catalin Marinas,
Will Deacon, Long Zhao, Lee Jones, mfd, devicetree, linux-clk,
linux-gpio, linux-serial, linux-kernel, Krzysztof Kozlowski
From: Long Zhao <longzhao@ambarella.com>
Add the Ambarella CV75 compatible string to the Synopsys DW APB UART
binding. The block is Ambarella IP with a DW-like register map, so it
is described as a DW APB UART variant.
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Reviewed-by: Linus Walleij <linusw@kernel.org>
Signed-off-by: Long Zhao <longzhao@ambarella.com>
---
Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml b/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml
index 49f51b002879..cec5c679f1a9 100644
--- a/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml
+++ b/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.yaml
@@ -51,6 +51,7 @@ properties:
- const: renesas,rzn1-uart
- items:
- enum:
+ - ambarella,cv75-uart
- anlogic,dr1v90-uart
- brcm,bcm11351-dw-apb-uart
- brcm,bcm21664-dw-apb-uart
--
2.34.1
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v7 07/15] clk: ambarella: add CV75 RCT clock controller
2026-09-15 11:15 [PATCH v7 00/15] Ambarella CV75 SoC minimal bring-up Long Zhao via B4 Relay
` (5 preceding siblings ...)
2026-09-15 11:15 ` [PATCH v7 06/15] dt-bindings: serial: snps-dw-apb-uart: add ambarella,cv75-uart Long Zhao via B4 Relay
@ 2026-09-15 11:15 ` Long Zhao via B4 Relay
2026-09-15 13:24 ` Uwe Kleine-König
2026-09-15 13:27 ` Andy Shevchenko
2026-09-15 11:15 ` [PATCH v7 08/15] gpiolib: regmap: add write_data_after_dir quirk Long Zhao via B4 Relay
` (8 subsequent siblings)
15 siblings, 2 replies; 34+ messages in thread
From: Long Zhao via B4 Relay @ 2026-09-15 11:15 UTC (permalink / raw)
To: Arnd Bergmann, Krzysztof Kozlowski, Alexandre Belloni, soc,
linux-arm-kernel
Cc: Long Zhao, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Michael Turquette, Stephen Boyd, Jerome Brunet, Linus Walleij,
Bartosz Golaszewski, Greg Kroah-Hartman, Jiri Slaby,
Andy Shevchenko, Ilpo Järvinen, Catalin Marinas,
Will Deacon, Long Zhao, Lee Jones, mfd, devicetree, linux-clk,
linux-gpio, linux-serial, linux-kernel
From: Long Zhao <longzhao@ambarella.com>
Add a table-driven CCF driver for the CV75 root clock tree. Register
the core PLL, AHB/APB fixed factors and UART0 composite clock, with
osc supplied via clk_parent_data.
Signed-off-by: Long Zhao <longzhao@ambarella.com>
---
drivers/clk/Kconfig | 1 +
drivers/clk/Makefile | 1 +
drivers/clk/ambarella/Kconfig | 9 +
drivers/clk/ambarella/Makefile | 3 +
drivers/clk/ambarella/clk-cv75.c | 366 +++++++++++++++++++++++++++++++++++++++
5 files changed, 380 insertions(+)
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 1717ce75a907..fbbf4963716c 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -506,6 +506,7 @@ config COMMON_CLK_RPMI
the RISC-V platform management interface (RPMI) specification.
source "drivers/clk/actions/Kconfig"
+source "drivers/clk/ambarella/Kconfig"
source "drivers/clk/analogbits/Kconfig"
source "drivers/clk/aspeed/Kconfig"
source "drivers/clk/bcm/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index cc108a75a900..30d823ee606f 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -112,6 +112,7 @@ obj-$(CONFIG_COMMON_CLK_XGENE) += clk-xgene.o
# please keep this section sorted lexicographically by directory path name
obj-y += actions/
+obj-y += ambarella/
obj-y += analogbits/
obj-y += aspeed/
obj-$(CONFIG_COMMON_CLK_AT91) += at91/
diff --git a/drivers/clk/ambarella/Kconfig b/drivers/clk/ambarella/Kconfig
new file mode 100644
index 000000000000..720eebdc5c9c
--- /dev/null
+++ b/drivers/clk/ambarella/Kconfig
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config CLK_AMBARELLA_CV75
+ bool "Ambarella CV75 RCT clock controller"
+ depends on ARCH_AMBARELLA || COMPILE_TEST
+ default ARCH_AMBARELLA
+ help
+ Say Y to enable the Ambarella CV75 RCT clock controller for
+ early bring-up clocks (core PLL, AHB, APB, UART0).
diff --git a/drivers/clk/ambarella/Makefile b/drivers/clk/ambarella/Makefile
new file mode 100644
index 000000000000..29555e452441
--- /dev/null
+++ b/drivers/clk/ambarella/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-$(CONFIG_CLK_AMBARELLA_CV75) += clk-cv75.o
diff --git a/drivers/clk/ambarella/clk-cv75.c b/drivers/clk/ambarella/clk-cv75.c
new file mode 100644
index 000000000000..80afe037c16a
--- /dev/null
+++ b/drivers/clk/ambarella/clk-cv75.c
@@ -0,0 +1,366 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Ambarella CV75 RCT clock controller
+ *
+ * Copyright (C) 2026 Ambarella, Inc.
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk-provider.h>
+#include <linux/container_of.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/math64.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/overflow.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include <dt-bindings/clock/ambarella,cv75-rct.h>
+
+#define CV75_NUM_CLKS (CV75_GCLK_APB + 1)
+
+#define PLL_CTRL_BYPASS BIT(2)
+#define PLL_CTRL_FRAC_MODE BIT(3)
+#define PLL_CTRL_FORCE_RESET BIT(4)
+#define PLL_CTRL_POWER_DOWN BIT(5)
+#define PLL_CTRL_HALT_VCO BIT(6)
+#define PLL_CTRL_SDIV GENMASK(15, 12)
+#define PLL_CTRL_SOUT GENMASK(19, 16)
+#define PLL_CTRL_INTP GENMASK(30, 24)
+
+#define PLL_CTRL2_VCODIV_DIV2 BIT(8)
+#define PLL_CTRL2_FSDIV_DIV2 BIT(9)
+#define PLL_CTRL2_FSOUT_DIV2 BIT(11)
+#define PLL_CTRL2_BYPASS_HSDIV BIT(12)
+
+struct cv75_pll_hw {
+ struct clk_hw hw;
+ u32 ctrl;
+ u32 frac;
+ u32 ctrl2;
+};
+
+struct cv75_clk {
+ void __iomem *base;
+ spinlock_t lock;
+ struct cv75_pll_hw core;
+ struct cv75_pll_hw enet;
+ struct cv75_pll_hw sd;
+};
+
+struct cv75_pll {
+ const char *name;
+ struct cv75_pll_hw *hw;
+ unsigned long flags;
+ u32 ctrl;
+ u32 frac;
+ u32 ctrl2;
+ int id;
+};
+
+struct cv75_fixed_factor {
+ const char *name;
+ u32 id;
+ u16 mult;
+ u16 div;
+};
+
+struct cv75_composite {
+ const char *name;
+ const struct clk_parent_data *parents;
+ unsigned long flags;
+ u32 id;
+ u16 mux_reg;
+ u16 div_reg;
+ u8 num_parents;
+ u8 mux_shift;
+ u8 mux_width;
+ u8 div_shift;
+ u8 div_width;
+};
+
+struct cv75_composite_hw {
+ struct clk_mux mux;
+ struct clk_divider div;
+ struct clk_gate gate;
+};
+
+static struct cv75_clk cv75_ccu;
+
+#define CV75_PLL(_id, _name, _ctrl, _frac, _ctrl2, _flags, _hw) { \
+ .name = _name, \
+ .hw = _hw, \
+ .flags = _flags, \
+ .ctrl = _ctrl, \
+ .frac = _frac, \
+ .ctrl2 = _ctrl2, \
+ .id = _id, \
+}
+
+static const struct cv75_pll cv75_plls[] = {
+ CV75_PLL(CV75_GCLK_CORE, "core", 0x000, 0x004, 0x100,
+ CLK_GET_RATE_NOCACHE | CLK_IS_CRITICAL, &cv75_ccu.core),
+ CV75_PLL(-1, "pll_enet", 0x520, 0x524, 0x528,
+ CLK_GET_RATE_NOCACHE, &cv75_ccu.enet),
+ CV75_PLL(-1, "pll_sd", 0x4ac, 0x4b0, 0x4b4,
+ CLK_GET_RATE_NOCACHE, &cv75_ccu.sd),
+};
+
+static const struct cv75_fixed_factor cv75_fixed_factors[] = {
+ { .id = CV75_GCLK_AHB, .name = "ahb", .mult = 1, .div = 2 },
+ { .id = CV75_GCLK_APB, .name = "apb", .mult = 1, .div = 4 },
+};
+
+static const struct clk_parent_data cv75_uart_parents[] = {
+ { .fw_name = "osc" },
+ { .hw = &cv75_ccu.core.hw },
+ { .hw = &cv75_ccu.enet.hw },
+ { .hw = &cv75_ccu.sd.hw },
+};
+
+#define CV75_COMPOSITE(_id, _name, _parents, _mreg, _mshift, _mwidth, \
+ _dreg, _dshift, _dwidth, _flags) \
+{ \
+ .id = _id, \
+ .name = _name, \
+ .parents = _parents, \
+ .num_parents = ARRAY_SIZE(_parents), \
+ .mux_reg = _mreg, \
+ .mux_shift = _mshift, \
+ .mux_width = _mwidth, \
+ .div_reg = _dreg, \
+ .div_shift = _dshift, \
+ .div_width = _dwidth, \
+ .flags = _flags, \
+}
+
+static const struct cv75_composite cv75_composites[] = {
+ CV75_COMPOSITE(CV75_GCLK_UART0, "uart0", cv75_uart_parents,
+ 0x1c8, 0, 2, 0x038, 0, 24,
+ CLK_SET_RATE_NO_REPARENT),
+};
+
+#define to_cv75_pll(_hw) container_of(_hw, struct cv75_pll_hw, hw)
+
+static unsigned long cv75_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct cv75_pll_hw *pll = to_cv75_pll(hw);
+ u32 ctrl, ctrl2, frac_val;
+ u32 intp, sdiv, sout;
+ u32 vcodiv, fsdiv, fsout;
+ u64 vco, frac = 0;
+
+ ctrl = readl_relaxed(cv75_ccu.base + pll->ctrl);
+ if (ctrl & (PLL_CTRL_POWER_DOWN | PLL_CTRL_HALT_VCO | PLL_CTRL_FORCE_RESET))
+ return 0;
+
+ if (ctrl & PLL_CTRL_BYPASS)
+ return parent_rate;
+
+ ctrl2 = readl_relaxed(cv75_ccu.base + pll->ctrl2);
+ intp = FIELD_GET(PLL_CTRL_INTP, ctrl) + 1;
+ sdiv = FIELD_GET(PLL_CTRL_SDIV, ctrl) + 1;
+ sout = FIELD_GET(PLL_CTRL_SOUT, ctrl) + 1;
+ vcodiv = (ctrl2 & PLL_CTRL2_VCODIV_DIV2) ? 2 : 1;
+ fsdiv = (ctrl2 & PLL_CTRL2_FSDIV_DIV2) ? 2 : 1;
+ fsout = (ctrl2 & PLL_CTRL2_FSOUT_DIV2) ? 2 : 1;
+
+ vco = (u64)parent_rate * vcodiv * fsdiv * intp * sdiv;
+ if (ctrl & PLL_CTRL_FRAC_MODE) {
+ frac_val = readl_relaxed(cv75_ccu.base + pll->frac);
+ frac = mul_u64_u32_shr((u64)parent_rate * vcodiv * fsdiv * sdiv,
+ frac_val, 32);
+ vco += frac;
+ }
+
+ if (!(ctrl2 & PLL_CTRL2_BYPASS_HSDIV))
+ vco = div_u64(vco, (u64)vcodiv * fsout * sout);
+
+ return vco;
+}
+
+static const struct clk_ops cv75_pll_ops = {
+ .recalc_rate = cv75_pll_recalc_rate,
+};
+
+static int cv75_register_plls(struct device *dev,
+ struct clk_hw_onecell_data *data)
+{
+ struct clk_parent_data parent_data = { .fw_name = "osc" };
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(cv75_plls); i++) {
+ const struct cv75_pll *desc = &cv75_plls[i];
+ struct cv75_pll_hw *pll = desc->hw;
+ struct clk_init_data init = {};
+ int ret;
+
+ pll->ctrl = desc->ctrl;
+ pll->frac = desc->frac;
+ pll->ctrl2 = desc->ctrl2;
+
+ init.name = desc->name;
+ init.ops = &cv75_pll_ops;
+ init.parent_data = &parent_data;
+ init.num_parents = 1;
+ init.flags = desc->flags;
+ pll->hw.init = &init;
+
+ ret = devm_clk_hw_register(dev, &pll->hw);
+ if (ret)
+ return ret;
+
+ if (desc->id >= 0)
+ data->hws[desc->id] = &pll->hw;
+ }
+
+ return 0;
+}
+
+static int cv75_register_fixed_factors(struct device *dev,
+ struct clk_hw_onecell_data *data)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(cv75_fixed_factors); i++) {
+ const struct cv75_fixed_factor *f = &cv75_fixed_factors[i];
+ struct clk_hw *hw;
+
+ hw = devm_clk_hw_register_fixed_factor_parent_hw(dev, f->name,
+ &cv75_ccu.core.hw, 0,
+ f->mult, f->div);
+ if (IS_ERR(hw))
+ return PTR_ERR(hw);
+
+ data->hws[f->id] = hw;
+ }
+
+ return 0;
+}
+
+static struct clk_hw *cv75_register_composite(struct device *dev,
+ const struct cv75_composite *c)
+{
+ const struct clk_ops *mux_ops = NULL, *div_ops = NULL, *gate_ops = NULL;
+ struct clk_hw *mux_hw = NULL, *div_hw = NULL, *gate_hw = NULL;
+ struct cv75_composite_hw *comp;
+
+ comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL);
+ if (!comp)
+ return ERR_PTR(-ENOMEM);
+
+ if (c->mux_width) {
+ comp->mux.reg = cv75_ccu.base + c->mux_reg;
+ comp->mux.shift = c->mux_shift;
+ comp->mux.mask = GENMASK(c->mux_width - 1, 0);
+ comp->mux.lock = &cv75_ccu.lock;
+ mux_hw = &comp->mux.hw;
+ mux_ops = &clk_mux_ops;
+ }
+
+ if (c->div_width) {
+ comp->div.reg = cv75_ccu.base + c->div_reg;
+ comp->div.shift = c->div_shift;
+ comp->div.width = c->div_width;
+ comp->div.flags = CLK_DIVIDER_ONE_BASED;
+ comp->div.lock = &cv75_ccu.lock;
+ div_hw = &comp->div.hw;
+ div_ops = &clk_divider_ops;
+
+ comp->gate.reg = cv75_ccu.base + c->div_reg;
+ comp->gate.bit_idx = c->div_shift + c->div_width;
+ comp->gate.flags = CLK_GATE_SET_TO_DISABLE;
+ comp->gate.lock = &cv75_ccu.lock;
+ gate_hw = &comp->gate.hw;
+ gate_ops = &clk_gate_ops;
+ }
+
+ return devm_clk_hw_register_composite_pdata(dev, c->name, c->parents,
+ c->num_parents,
+ mux_hw, mux_ops,
+ div_hw, div_ops,
+ gate_hw, gate_ops, c->flags);
+}
+
+static int cv75_register_composites(struct device *dev,
+ struct clk_hw_onecell_data *data)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(cv75_composites); i++) {
+ const struct cv75_composite *c = &cv75_composites[i];
+ struct clk_hw *hw;
+
+ hw = cv75_register_composite(dev, c);
+ if (IS_ERR(hw))
+ return PTR_ERR(hw);
+
+ data->hws[c->id] = hw;
+ }
+
+ return 0;
+}
+
+static int cv75_rct_probe(struct platform_device *pdev)
+{
+ struct clk_hw_onecell_data *data;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ spin_lock_init(&cv75_ccu.lock);
+
+ cv75_ccu.base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(cv75_ccu.base))
+ return PTR_ERR(cv75_ccu.base);
+
+ data = devm_kzalloc(dev, struct_size(data, hws, CV75_NUM_CLKS),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ data->num = CV75_NUM_CLKS;
+
+ ret = cv75_register_plls(dev, data);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register plls\n");
+
+ ret = cv75_register_fixed_factors(dev, data);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register fixed factors\n");
+
+ ret = cv75_register_composites(dev, data);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register composites\n");
+
+ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, data);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to add clock provider\n");
+
+ return 0;
+}
+
+static const struct of_device_id cv75_rct_match[] = {
+ { .compatible = "ambarella,cv75-rct" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, cv75_rct_match);
+
+static struct platform_driver cv75_rct_driver = {
+ .probe = cv75_rct_probe,
+ .driver = {
+ .name = "ambarella-cv75-rct",
+ .of_match_table = cv75_rct_match,
+ },
+};
+module_platform_driver(cv75_rct_driver);
+
+MODULE_AUTHOR("Long Zhao <longzhao@ambarella.com>");
+MODULE_DESCRIPTION("Ambarella CV75 RCT clock controller");
+MODULE_LICENSE("GPL");
--
2.34.1
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v7 08/15] gpiolib: regmap: add write_data_after_dir quirk
2026-09-15 11:15 [PATCH v7 00/15] Ambarella CV75 SoC minimal bring-up Long Zhao via B4 Relay
` (6 preceding siblings ...)
2026-09-15 11:15 ` [PATCH v7 07/15] clk: ambarella: add CV75 RCT clock controller Long Zhao via B4 Relay
@ 2026-09-15 11:15 ` Long Zhao via B4 Relay
2026-09-15 15:07 ` Andy Shevchenko
2026-09-15 11:15 ` [PATCH v7 09/15] gpiolib: regmap: add gpio_regmap_get_chip() Long Zhao via B4 Relay
` (7 subsequent siblings)
15 siblings, 1 reply; 34+ messages in thread
From: Long Zhao via B4 Relay @ 2026-09-15 11:15 UTC (permalink / raw)
To: Arnd Bergmann, Krzysztof Kozlowski, Alexandre Belloni, soc,
linux-arm-kernel
Cc: Long Zhao, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Michael Turquette, Stephen Boyd, Jerome Brunet, Linus Walleij,
Bartosz Golaszewski, Greg Kroah-Hartman, Jiri Slaby,
Andy Shevchenko, Ilpo Järvinen, Catalin Marinas,
Will Deacon, Long Zhao, Lee Jones, mfd, devicetree, linux-clk,
linux-gpio, linux-serial, linux-kernel
From: Long Zhao <longzhao@ambarella.com>
Some controllers ignore data-register writes while a line is still an
input. Optionally write the output value again after switching the
direction, matching the existing PL061 behaviour.
Signed-off-by: Long Zhao <longzhao@ambarella.com>
---
drivers/gpio/gpio-regmap.c | 20 ++++++++++++++++++--
include/linux/gpio/regmap.h | 8 ++++++++
2 files changed, 26 insertions(+), 2 deletions(-)
diff --git a/drivers/gpio/gpio-regmap.c b/drivers/gpio/gpio-regmap.c
index 51b4d69b8740..dfcc1f1122ec 100644
--- a/drivers/gpio/gpio-regmap.c
+++ b/drivers/gpio/gpio-regmap.c
@@ -31,6 +31,7 @@ struct gpio_regmap {
unsigned int reg_clr_base;
unsigned int reg_dir_in_base;
unsigned int reg_dir_out_base;
+ bool write_data_after_dir;
unsigned long *fixed_direction_mask;
unsigned long *fixed_direction_output;
@@ -271,9 +272,22 @@ static int gpio_regmap_direction_output(struct gpio_chip *chip,
return ret;
}
- gpio_regmap_set(chip, offset, value);
+ ret = gpio_regmap_set(chip, offset, value);
+ if (ret)
+ return ret;
+
+ ret = gpio_regmap_set_direction(chip, offset, true);
+ if (ret)
+ return ret;
- return gpio_regmap_set_direction(chip, offset, true);
+ /*
+ * gpio value is set again, because pl061 doesn't allow to set value of
+ * a gpio pin before configuring it in OUT mode.
+ */
+ if (gpio->write_data_after_dir)
+ return gpio_regmap_set(chip, offset, value);
+
+ return 0;
}
void *gpio_regmap_get_drvdata(struct gpio_regmap *gpio)
@@ -376,6 +390,8 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config
config->fixed_direction_output, chip->ngpio);
}
+ gpio->write_data_after_dir = config->write_data_after_dir;
+
/* if not set, assume there is only one register */
gpio->ngpio_per_reg = config->ngpio_per_reg;
if (!gpio->ngpio_per_reg)
diff --git a/include/linux/gpio/regmap.h b/include/linux/gpio/regmap.h
index 06255756710d..d7ffc12b00a2 100644
--- a/include/linux/gpio/regmap.h
+++ b/include/linux/gpio/regmap.h
@@ -48,6 +48,13 @@ struct regmap;
* (Optional) Bitmap representing the fixed direction of
* the GPIO lines. Useful when there are GPIO lines with a
* fixed direction mixed together in the same register.
+ * @write_data_after_dir:
+ * (Optional) Write the output value again after
+ * switching a line to output in ->direction_output().
+ * Needed for hardware which ignores data register
+ * writes while the line is configured as an input.
+ * This is a legacy quirk (e.g. ARM PL061); new hardware
+ * must not use it. Direction changes will glitch.
* @drvdata: (Optional) Pointer to driver specific data which is
* not used by gpio-remap but is provided "as is" to the
* driver callback(s).
@@ -94,6 +101,7 @@ struct gpio_regmap_config {
unsigned int reg_dir_out_base;
int reg_stride;
int ngpio_per_reg;
+ bool write_data_after_dir;
struct irq_domain *irq_domain;
unsigned long *fixed_direction_mask;
unsigned long *fixed_direction_output;
--
2.34.1
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v7 09/15] gpiolib: regmap: add gpio_regmap_get_chip()
2026-09-15 11:15 [PATCH v7 00/15] Ambarella CV75 SoC minimal bring-up Long Zhao via B4 Relay
` (7 preceding siblings ...)
2026-09-15 11:15 ` [PATCH v7 08/15] gpiolib: regmap: add write_data_after_dir quirk Long Zhao via B4 Relay
@ 2026-09-15 11:15 ` Long Zhao via B4 Relay
2026-09-15 15:30 ` Andy Shevchenko
2026-09-15 11:15 ` [PATCH v7 10/15] gpio: pl061: convert to gpio-regmap and a custom irqchip Long Zhao via B4 Relay
` (6 subsequent siblings)
15 siblings, 1 reply; 34+ messages in thread
From: Long Zhao via B4 Relay @ 2026-09-15 11:15 UTC (permalink / raw)
To: Arnd Bergmann, Krzysztof Kozlowski, Alexandre Belloni, soc,
linux-arm-kernel
Cc: Long Zhao, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Michael Turquette, Stephen Boyd, Jerome Brunet, Linus Walleij,
Bartosz Golaszewski, Greg Kroah-Hartman, Jiri Slaby,
Andy Shevchenko, Ilpo Järvinen, Catalin Marinas,
Will Deacon, Long Zhao, Lee Jones, mfd, devicetree, linux-clk,
linux-gpio, linux-serial, linux-kernel
From: Long Zhao <longzhao@ambarella.com>
Let drivers that implement a custom irqchip retrieve the gpio_chip
after registration. Attach a caller-provided irq_domain only when it
is present.
Signed-off-by: Long Zhao <longzhao@ambarella.com>
---
drivers/gpio/gpio-regmap.c | 20 +++++++++++++++-----
include/linux/gpio/regmap.h | 1 +
2 files changed, 16 insertions(+), 5 deletions(-)
diff --git a/drivers/gpio/gpio-regmap.c b/drivers/gpio/gpio-regmap.c
index dfcc1f1122ec..56c2c873cbfa 100644
--- a/drivers/gpio/gpio-regmap.c
+++ b/drivers/gpio/gpio-regmap.c
@@ -296,6 +296,12 @@ void *gpio_regmap_get_drvdata(struct gpio_regmap *gpio)
}
EXPORT_SYMBOL_GPL(gpio_regmap_get_drvdata);
+struct gpio_chip *gpio_regmap_get_chip(struct gpio_regmap *gpio)
+{
+ return &gpio->gpio_chip;
+}
+EXPORT_SYMBOL_GPL(gpio_regmap_get_chip);
+
/**
* gpio_regmap_register() - Register a generic regmap GPIO controller
* @config: configuration for gpio_regmap
@@ -304,7 +310,6 @@ EXPORT_SYMBOL_GPL(gpio_regmap_get_drvdata);
*/
struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config)
{
- struct irq_domain *irq_domain;
struct gpio_regmap *gpio;
struct gpio_chip *chip;
int ret;
@@ -412,6 +417,8 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config
#ifdef CONFIG_REGMAP_IRQ
if (config->regmap_irq_chip) {
+ struct irq_domain *irq_domain;
+
gpio->regmap_irq_line = config->regmap_irq_line;
ret = regmap_add_irq_chip_fwnode(dev_fwnode(config->parent), config->regmap,
config->regmap_irq_line, config->regmap_irq_flags,
@@ -420,12 +427,15 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config
goto err_remove_gpiochip;
irq_domain = regmap_irq_get_domain(gpio->irq_chip_data);
+ if (irq_domain) {
+ ret = gpiochip_irqchip_add_domain(chip, irq_domain);
+ if (ret)
+ goto err_remove_gpiochip;
+ }
} else
#endif
- irq_domain = config->irq_domain;
-
- if (irq_domain) {
- ret = gpiochip_irqchip_add_domain(chip, irq_domain);
+ if (config->irq_domain) {
+ ret = gpiochip_irqchip_add_domain(chip, config->irq_domain);
if (ret)
goto err_remove_gpiochip;
}
diff --git a/include/linux/gpio/regmap.h b/include/linux/gpio/regmap.h
index d7ffc12b00a2..6bc00b9775a5 100644
--- a/include/linux/gpio/regmap.h
+++ b/include/linux/gpio/regmap.h
@@ -128,5 +128,6 @@ void gpio_regmap_unregister(struct gpio_regmap *gpio);
struct gpio_regmap *devm_gpio_regmap_register(struct device *dev,
const struct gpio_regmap_config *config);
void *gpio_regmap_get_drvdata(struct gpio_regmap *gpio);
+struct gpio_chip *gpio_regmap_get_chip(struct gpio_regmap *gpio);
#endif /* _LINUX_GPIO_REGMAP_H */
--
2.34.1
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v7 10/15] gpio: pl061: convert to gpio-regmap and a custom irqchip
2026-09-15 11:15 [PATCH v7 00/15] Ambarella CV75 SoC minimal bring-up Long Zhao via B4 Relay
` (8 preceding siblings ...)
2026-09-15 11:15 ` [PATCH v7 09/15] gpiolib: regmap: add gpio_regmap_get_chip() Long Zhao via B4 Relay
@ 2026-09-15 11:15 ` Long Zhao via B4 Relay
2026-09-16 10:21 ` Andy Shevchenko
2026-09-15 11:15 ` [PATCH v7 11/15] gpio: pl061: add Ambarella register-layout variant Long Zhao via B4 Relay
` (5 subsequent siblings)
15 siblings, 1 reply; 34+ messages in thread
From: Long Zhao via B4 Relay @ 2026-09-15 11:15 UTC (permalink / raw)
To: Arnd Bergmann, Krzysztof Kozlowski, Alexandre Belloni, soc,
linux-arm-kernel
Cc: Long Zhao, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Michael Turquette, Stephen Boyd, Jerome Brunet, Linus Walleij,
Bartosz Golaszewski, Greg Kroah-Hartman, Jiri Slaby,
Andy Shevchenko, Ilpo Järvinen, Catalin Marinas,
Will Deacon, Long Zhao, Lee Jones, mfd, devicetree, linux-clk,
linux-gpio, linux-serial, linux-kernel
From: Long Zhao <longzhao@ambarella.com>
Move line get/set/direction onto gpio-regmap. Keep the existing ARM
masked data addresses and write-after-direction behaviour.
Do not use gpiochip irqchip setup (girq) or regmap-irq. gpio-regmap
owns gpiochip registration, so girq would have to be plumbed through
that helper. PL061 IRQ type programming needs IS/IBE/IEV, including
both-edge, plus a hardirq chained demux from the parent AMBA IRQ;
regmap-irq is a poor fit for that.
Create a linear irq_domain with gpio_chip as host data, attach it with
gpiochip_irqchip_add_domain(), and chain the parent IRQ in this driver.
Signed-off-by: Long Zhao <longzhao@ambarella.com>
---
drivers/gpio/Kconfig | 2 +
drivers/gpio/gpio-pl061.c | 531 +++++++++++++++++++++++++++++-----------------
2 files changed, 344 insertions(+), 189 deletions(-)
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index f03c05288376..55a129836158 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -583,6 +583,8 @@ config GPIO_PL061
depends on ARM_AMBA || COMPILE_TEST
select IRQ_DOMAIN
select GPIOLIB_IRQCHIP
+ select GPIO_REGMAP
+ select REGMAP_MMIO
help
Say yes here to support the PrimeCell PL061 GPIO device.
diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c
index 919cf86fd590..1d448aafca50 100644
--- a/drivers/gpio/gpio-pl061.c
+++ b/drivers/gpio/gpio-pl061.c
@@ -11,130 +11,109 @@
#include <linux/amba/bus.h>
#include <linux/bitops.h>
#include <linux/device.h>
+#include <linux/err.h>
#include <linux/errno.h>
-#include <linux/gpio/driver.h>
-#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/irq.h>
-#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/log2.h>
#include <linux/module.h>
-#include <linux/pinctrl/consumer.h>
#include <linux/pm.h>
+#include <linux/regmap.h>
#include <linux/seq_file.h>
-#include <linux/slab.h>
#include <linux/spinlock.h>
-#define GPIODIR 0x400
-#define GPIOIS 0x404
-#define GPIOIBE 0x408
-#define GPIOIEV 0x40C
-#define GPIOIE 0x410
-#define GPIORIS 0x414
-#define GPIOMIS 0x418
-#define GPIOIC 0x41C
-
-#define PL061_GPIO_NR 8
-
-struct pl061_context_save_regs {
- u8 gpio_data;
- u8 gpio_dir;
- u8 gpio_is;
- u8 gpio_ibe;
- u8 gpio_iev;
- u8 gpio_ie;
+#include <linux/gpio/driver.h>
+#include <linux/gpio/regmap.h>
+#include <linux/irqchip/chained_irq.h>
+
+#define PL061_GPIO_NR 8
+#define PL061_DATA_OFFSET 2
+
+#define PL061_DIR 0x400
+#define PL061_IS 0x404
+#define PL061_IBE 0x408
+#define PL061_IEV 0x40c
+#define PL061_IE 0x410
+#define PL061_MIS 0x418
+#define PL061_IC 0x41c
+
+struct pl061_regs {
+ unsigned int dat;
+ unsigned int dir;
+ unsigned int is;
+ unsigned int ibe;
+ unsigned int iev;
+ unsigned int ie;
+ unsigned int mis;
+ unsigned int ic;
+ unsigned int mask;
+ unsigned int enable;
+};
+
+struct pl061_drvdata {
+ const struct regmap_config *regmap_config;
+ const struct pl061_regs *regs;
+ unsigned int ngpio;
+ bool write_data_after_dir;
+ bool clear_irq_on_type;
+ bool pm;
+ int (*reg_mask_xlate)(struct gpio_regmap *gpio, unsigned int base,
+ unsigned int offset, unsigned int *reg,
+ unsigned int *mask);
};
struct pl061 {
raw_spinlock_t lock;
-
- void __iomem *base;
- struct gpio_chip gc;
+ struct regmap *regmap;
+ const struct pl061_drvdata *data;
+ struct irq_domain *irq_domain;
int parent_irq;
-
- struct pl061_context_save_regs csave_regs;
+ u32 saved_dat;
};
-static int pl061_get_direction(struct gpio_chip *gc, unsigned offset)
-{
- struct pl061 *pl061 = gpiochip_get_data(gc);
-
- if (readb(pl061->base + GPIODIR) & BIT(offset))
- return GPIO_LINE_DIRECTION_OUT;
-
- return GPIO_LINE_DIRECTION_IN;
-}
-
-static int pl061_direction_input(struct gpio_chip *gc, unsigned offset)
-{
- struct pl061 *pl061 = gpiochip_get_data(gc);
- unsigned long flags;
- unsigned char gpiodir;
-
- raw_spin_lock_irqsave(&pl061->lock, flags);
- gpiodir = readb(pl061->base + GPIODIR);
- gpiodir &= ~(BIT(offset));
- writeb(gpiodir, pl061->base + GPIODIR);
- raw_spin_unlock_irqrestore(&pl061->lock, flags);
-
- return 0;
-}
-
-static int pl061_direction_output(struct gpio_chip *gc, unsigned offset,
- int value)
+static struct pl061 *pl061_from_gpio_chip(struct gpio_chip *gc)
{
- struct pl061 *pl061 = gpiochip_get_data(gc);
- unsigned long flags;
- unsigned char gpiodir;
-
- raw_spin_lock_irqsave(&pl061->lock, flags);
- writeb(!!value << offset, pl061->base + (BIT(offset + 2)));
- gpiodir = readb(pl061->base + GPIODIR);
- gpiodir |= BIT(offset);
- writeb(gpiodir, pl061->base + GPIODIR);
-
- /*
- * gpio value is set again, because pl061 doesn't allow to set value of
- * a gpio pin before configuring it in OUT mode.
- */
- writeb(!!value << offset, pl061->base + (BIT(offset + 2)));
- raw_spin_unlock_irqrestore(&pl061->lock, flags);
-
- return 0;
+ return gpio_regmap_get_drvdata(gpiochip_get_data(gc));
}
-static int pl061_get_value(struct gpio_chip *gc, unsigned offset)
+static unsigned int pl061_line_mask(struct pl061 *pl061, irq_hw_number_t hwirq)
{
- struct pl061 *pl061 = gpiochip_get_data(gc);
-
- return !!readb(pl061->base + (BIT(offset + 2)));
+ return BIT(hwirq % pl061->data->ngpio);
}
-static int pl061_set_value(struct gpio_chip *gc, unsigned int offset, int value)
+static int pl061_arm_reg_mask_xlate(struct gpio_regmap *gpio, unsigned int base,
+ unsigned int offset, unsigned int *reg,
+ unsigned int *mask)
{
- struct pl061 *pl061 = gpiochip_get_data(gc);
-
- writeb(!!value << offset, pl061->base + (BIT(offset + 2)));
+ if (base == 0)
+ *reg = BIT(offset + PL061_DATA_OFFSET);
+ else
+ *reg = base;
+ *mask = BIT(offset);
return 0;
}
-static int pl061_irq_type(struct irq_data *d, unsigned trigger)
+static int pl061_irq_type(struct irq_data *d, unsigned int trigger)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
- struct pl061 *pl061 = gpiochip_get_data(gc);
+ struct pl061 *pl061 = pl061_from_gpio_chip(gc);
+ const struct pl061_regs *regs = pl061->data->regs;
+ unsigned int gpiois, gpioibe, gpioiev;
int offset = irqd_to_hwirq(d);
unsigned long flags;
- u8 gpiois, gpioibe, gpioiev;
- u8 bit = BIT(offset);
+ unsigned int bit;
+ int ret;
- if (offset < 0 || offset >= PL061_GPIO_NR)
+ if (offset < 0 || offset >= pl061->data->ngpio)
return -EINVAL;
- if ((trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) &&
- (trigger & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)))
- {
+ bit = BIT(offset);
+
+ if ((trigger & IRQ_TYPE_LEVEL_MASK) && (trigger & IRQ_TYPE_EDGE_BOTH)) {
dev_err(gc->parent,
"trying to configure line %d for both level and edge "
"detection, choose one!\n",
@@ -142,14 +121,19 @@ static int pl061_irq_type(struct irq_data *d, unsigned trigger)
return -EINVAL;
}
-
raw_spin_lock_irqsave(&pl061->lock, flags);
- gpioiev = readb(pl061->base + GPIOIEV);
- gpiois = readb(pl061->base + GPIOIS);
- gpioibe = readb(pl061->base + GPIOIBE);
+ ret = regmap_read(pl061->regmap, regs->iev, &gpioiev);
+ if (ret)
+ goto out;
+ ret = regmap_read(pl061->regmap, regs->is, &gpiois);
+ if (ret)
+ goto out;
+ ret = regmap_read(pl061->regmap, regs->ibe, &gpioibe);
+ if (ret)
+ goto out;
- if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) {
+ if (trigger & IRQ_TYPE_LEVEL_MASK) {
bool polarity = trigger & IRQ_TYPE_LEVEL_HIGH;
/* Disable edge detection */
@@ -199,30 +183,38 @@ static int pl061_irq_type(struct irq_data *d, unsigned trigger)
offset);
}
- writeb(gpiois, pl061->base + GPIOIS);
- writeb(gpioibe, pl061->base + GPIOIBE);
- writeb(gpioiev, pl061->base + GPIOIEV);
+ ret = regmap_write(pl061->regmap, regs->is, gpiois);
+ if (ret)
+ goto out;
+ ret = regmap_write(pl061->regmap, regs->ibe, gpioibe);
+ if (ret)
+ goto out;
+ ret = regmap_write(pl061->regmap, regs->iev, gpioiev);
+ if (ret)
+ goto out;
+ if (pl061->data->clear_irq_on_type)
+ ret = regmap_write(pl061->regmap, regs->ic, bit);
+out:
raw_spin_unlock_irqrestore(&pl061->lock, flags);
-
- return 0;
+ return ret;
}
static void pl061_irq_handler(struct irq_desc *desc)
{
unsigned long pending;
+ unsigned int mis;
int offset;
struct gpio_chip *gc = irq_desc_get_handler_data(desc);
- struct pl061 *pl061 = gpiochip_get_data(gc);
+ struct pl061 *pl061 = pl061_from_gpio_chip(gc);
struct irq_chip *irqchip = irq_desc_get_chip(desc);
chained_irq_enter(irqchip, desc);
- pending = readb(pl061->base + GPIOMIS);
- if (pending) {
- for_each_set_bit(offset, &pending, PL061_GPIO_NR)
- generic_handle_domain_irq(gc->irq.domain,
- offset);
+ if (!regmap_read(pl061->regmap, pl061->data->regs->mis, &mis) && mis) {
+ pending = mis;
+ for_each_set_bit(offset, &pending, pl061->data->ngpio)
+ generic_handle_domain_irq(pl061->irq_domain, offset);
}
chained_irq_exit(irqchip, desc);
@@ -231,31 +223,21 @@ static void pl061_irq_handler(struct irq_desc *desc)
static void pl061_irq_mask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
- struct pl061 *pl061 = gpiochip_get_data(gc);
- u8 mask = BIT(irqd_to_hwirq(d) % PL061_GPIO_NR);
- u8 gpioie;
-
- raw_spin_lock(&pl061->lock);
- gpioie = readb(pl061->base + GPIOIE) & ~mask;
- writeb(gpioie, pl061->base + GPIOIE);
- raw_spin_unlock(&pl061->lock);
+ struct pl061 *pl061 = pl061_from_gpio_chip(gc);
+ unsigned int mask = pl061_line_mask(pl061, irqd_to_hwirq(d));
+ regmap_update_bits(pl061->regmap, pl061->data->regs->ie, mask, 0);
gpiochip_disable_irq(gc, d->hwirq);
}
static void pl061_irq_unmask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
- struct pl061 *pl061 = gpiochip_get_data(gc);
- u8 mask = BIT(irqd_to_hwirq(d) % PL061_GPIO_NR);
- u8 gpioie;
+ struct pl061 *pl061 = pl061_from_gpio_chip(gc);
+ unsigned int mask = pl061_line_mask(pl061, irqd_to_hwirq(d));
gpiochip_enable_irq(gc, d->hwirq);
-
- raw_spin_lock(&pl061->lock);
- gpioie = readb(pl061->base + GPIOIE) | mask;
- writeb(gpioie, pl061->base + GPIOIE);
- raw_spin_unlock(&pl061->lock);
+ regmap_update_bits(pl061->regmap, pl061->data->regs->ie, mask, mask);
}
/**
@@ -269,18 +251,16 @@ static void pl061_irq_unmask(struct irq_data *d)
static void pl061_irq_ack(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
- struct pl061 *pl061 = gpiochip_get_data(gc);
- u8 mask = BIT(irqd_to_hwirq(d) % PL061_GPIO_NR);
+ struct pl061 *pl061 = pl061_from_gpio_chip(gc);
+ unsigned int mask = pl061_line_mask(pl061, irqd_to_hwirq(d));
- raw_spin_lock(&pl061->lock);
- writeb(mask, pl061->base + GPIOIC);
- raw_spin_unlock(&pl061->lock);
+ regmap_write(pl061->regmap, pl061->data->regs->ic, mask);
}
static int pl061_irq_set_wake(struct irq_data *d, unsigned int state)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
- struct pl061 *pl061 = gpiochip_get_data(gc);
+ struct pl061 *pl061 = pl061_from_gpio_chip(gc);
return irq_set_irq_wake(pl061->parent_irq, state);
}
@@ -292,7 +272,8 @@ static void pl061_irq_print_chip(struct irq_data *data, struct seq_file *p)
seq_puts(p, dev_name(gc->parent));
}
-static const struct irq_chip pl061_irq_chip = {
+static const struct irq_chip pl061_irqchip = {
+ .name = "pl061",
.irq_ack = pl061_irq_ack,
.irq_mask = pl061_irq_mask,
.irq_unmask = pl061_irq_unmask,
@@ -303,59 +284,186 @@ static const struct irq_chip pl061_irq_chip = {
GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
+static int pl061_irq_domain_map(struct irq_domain *d, unsigned int virq,
+ irq_hw_number_t hwirq)
+{
+ struct gpio_chip *gc = d->host_data;
+ struct pl061 *pl061 = pl061_from_gpio_chip(gc);
+
+ irq_set_chip_data(virq, gc);
+ irq_set_chip_and_handler(virq, &pl061_irqchip, handle_bad_irq);
+ irq_set_noprobe(virq);
+ irq_set_parent(virq, pl061->parent_irq);
+
+ return 0;
+}
+
+static void pl061_irq_domain_unmap(struct irq_domain *d, unsigned int virq)
+{
+ irq_set_chip_and_handler(virq, NULL, NULL);
+ irq_set_chip_data(virq, NULL);
+}
+
+static const struct irq_domain_ops pl061_irq_domain_ops = {
+ .map = pl061_irq_domain_map,
+ .unmap = pl061_irq_domain_unmap,
+ .xlate = irq_domain_xlate_twothreecell,
+};
+
+static void pl061_remove_irq(void *data)
+{
+ struct pl061 *pl061 = data;
+
+ irq_set_chained_handler_and_data(pl061->parent_irq, NULL, NULL);
+
+ for (unsigned int i = 0; i < pl061->data->ngpio; i++) {
+ unsigned int virq = irq_find_mapping(pl061->irq_domain, i);
+
+ if (virq)
+ irq_dispose_mapping(virq);
+ }
+
+ irq_domain_remove(pl061->irq_domain);
+}
+
+static bool pl061_arm_is_data_reg(unsigned int reg)
+{
+ return reg >= BIT(PL061_DATA_OFFSET) &&
+ reg <= BIT(PL061_DATA_OFFSET + PL061_GPIO_NR - 1) &&
+ is_power_of_2(reg);
+}
+
+static bool pl061_arm_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case PL061_MIS:
+ case PL061_IC:
+ return true;
+ default:
+ return pl061_arm_is_data_reg(reg);
+ }
+}
+
+static const struct regmap_config pl061_arm_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .reg_stride = 4,
+ .max_register = PL061_IC,
+ .volatile_reg = pl061_arm_volatile_reg,
+ .cache_type = REGCACHE_FLAT_S,
+ .fast_io = true,
+};
+
+static const struct pl061_regs pl061_arm_regs = {
+ .dir = PL061_DIR,
+ .is = PL061_IS,
+ .ibe = PL061_IBE,
+ .iev = PL061_IEV,
+ .ie = PL061_IE,
+ .mis = PL061_MIS,
+ .ic = PL061_IC,
+};
+
+static const struct pl061_drvdata pl061_arm = {
+ .regmap_config = &pl061_arm_regmap_config,
+ .regs = &pl061_arm_regs,
+ .ngpio = PL061_GPIO_NR,
+ .write_data_after_dir = true,
+ .pm = true,
+ .reg_mask_xlate = pl061_arm_reg_mask_xlate,
+};
+
static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
{
struct device *dev = &adev->dev;
+ const struct pl061_drvdata *data = id->data;
+ const struct pl061_regs *regs;
+ struct gpio_regmap_config config = {};
+ struct gpio_regmap *gpio_regmap;
+ struct gpio_chip *gc;
struct pl061 *pl061;
- struct gpio_irq_chip *girq;
+ void __iomem *base;
int ret, irq;
+ if (!data)
+ return -EINVAL;
+
+ regs = data->regs;
+
pl061 = devm_kzalloc(dev, sizeof(*pl061), GFP_KERNEL);
- if (pl061 == NULL)
+ if (!pl061)
return -ENOMEM;
- pl061->base = devm_ioremap_resource(dev, &adev->res);
- if (IS_ERR(pl061->base))
- return PTR_ERR(pl061->base);
-
+ pl061->data = data;
raw_spin_lock_init(&pl061->lock);
- pl061->gc.request = gpiochip_generic_request;
- pl061->gc.free = gpiochip_generic_free;
- pl061->gc.base = -1;
- pl061->gc.get_direction = pl061_get_direction;
- pl061->gc.direction_input = pl061_direction_input;
- pl061->gc.direction_output = pl061_direction_output;
- pl061->gc.get = pl061_get_value;
- pl061->gc.set = pl061_set_value;
- pl061->gc.ngpio = PL061_GPIO_NR;
- pl061->gc.label = dev_name(dev);
- pl061->gc.parent = dev;
- pl061->gc.owner = THIS_MODULE;
+
+ base = devm_ioremap_resource(dev, &adev->res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ pl061->regmap = devm_regmap_init_mmio(dev, base, data->regmap_config);
+ if (IS_ERR(pl061->regmap))
+ return PTR_ERR(pl061->regmap);
+
+ if (regs->enable) {
+ ret = regmap_write(pl061->regmap, regs->enable, ~0U);
+ if (ret)
+ return ret;
+ }
+ if (regs->mask) {
+ ret = regmap_write(pl061->regmap, regs->mask, ~0U);
+ if (ret)
+ return ret;
+ }
/*
* irq_chip support
*/
- writeb(0, pl061->base + GPIOIE); /* disable irqs */
+ ret = regmap_write(pl061->regmap, regs->ie, 0); /* disable irqs */
+ if (ret)
+ return ret;
+
irq = adev->irq[0];
if (!irq)
- dev_warn(&adev->dev, "IRQ support disabled\n");
+ dev_warn(dev, "IRQ support disabled\n");
pl061->parent_irq = irq;
- girq = &pl061->gc.irq;
- gpio_irq_chip_set_chip(girq, &pl061_irq_chip);
- girq->parent_handler = pl061_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;
-
- ret = devm_gpiochip_add_data(dev, &pl061->gc, pl061);
- if (ret)
- return ret;
+ config.parent = dev;
+ config.regmap = pl061->regmap;
+ config.ngpio = data->ngpio;
+ config.reg_dat_base = GPIO_REGMAP_ADDR(regs->dat);
+ config.reg_set_base = GPIO_REGMAP_ADDR(regs->dat);
+ config.reg_dir_out_base = regs->dir;
+ config.reg_mask_xlate = data->reg_mask_xlate;
+ config.write_data_after_dir = data->write_data_after_dir;
+ config.drvdata = pl061;
+
+ gpio_regmap = devm_gpio_regmap_register(dev, &config);
+ if (IS_ERR(gpio_regmap))
+ return PTR_ERR(gpio_regmap);
+
+ gc = gpio_regmap_get_chip(gpio_regmap);
+
+ if (irq) {
+ pl061->irq_domain = irq_domain_create_linear(dev_fwnode(dev),
+ data->ngpio,
+ &pl061_irq_domain_ops,
+ gc);
+ if (!pl061->irq_domain)
+ return -ENOMEM;
+
+ ret = gpiochip_irqchip_add_domain(gc, pl061->irq_domain);
+ if (ret) {
+ irq_domain_remove(pl061->irq_domain);
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(dev, pl061_remove_irq, pl061);
+ if (ret)
+ return ret;
+
+ irq_set_chained_handler_and_data(irq, pl061_irq_handler, gc);
+ }
amba_set_drvdata(adev, pl061);
dev_info(dev, "PL061 GPIO chip registered\n");
@@ -363,22 +471,52 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
return 0;
}
+static int pl061_cache_ctrl_regs(struct pl061 *pl061, unsigned int *dir)
+{
+ const struct pl061_regs *regs = pl061->data->regs;
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(pl061->regmap, regs->dir, dir);
+ if (ret)
+ return ret;
+ ret = regmap_read(pl061->regmap, regs->is, &val);
+ if (ret)
+ return ret;
+ ret = regmap_read(pl061->regmap, regs->ibe, &val);
+ if (ret)
+ return ret;
+ ret = regmap_read(pl061->regmap, regs->iev, &val);
+ if (ret)
+ return ret;
+
+ return regmap_read(pl061->regmap, regs->ie, &val);
+}
+
static int pl061_suspend(struct device *dev)
{
struct pl061 *pl061 = dev_get_drvdata(dev);
- int offset;
+ unsigned int dir, val;
+ int offset, ret;
+
+ if (!pl061->data->pm)
+ return 0;
+
+ ret = pl061_cache_ctrl_regs(pl061, &dir);
+ if (ret)
+ return ret;
- pl061->csave_regs.gpio_data = 0;
- pl061->csave_regs.gpio_dir = readb(pl061->base + GPIODIR);
- pl061->csave_regs.gpio_is = readb(pl061->base + GPIOIS);
- pl061->csave_regs.gpio_ibe = readb(pl061->base + GPIOIBE);
- pl061->csave_regs.gpio_iev = readb(pl061->base + GPIOIEV);
- pl061->csave_regs.gpio_ie = readb(pl061->base + GPIOIE);
-
- for (offset = 0; offset < PL061_GPIO_NR; offset++) {
- if (pl061->csave_regs.gpio_dir & (BIT(offset)))
- pl061->csave_regs.gpio_data |=
- pl061_get_value(&pl061->gc, offset) << offset;
+ pl061->saved_dat = 0;
+ for (offset = 0; offset < pl061->data->ngpio; offset++) {
+ if (!(dir & BIT(offset)))
+ continue;
+
+ ret = regmap_read_bypassed(pl061->regmap,
+ BIT(offset + PL061_DATA_OFFSET),
+ &val);
+ if (ret)
+ return ret;
+ pl061->saved_dat |= val;
}
return 0;
@@ -387,23 +525,37 @@ static int pl061_suspend(struct device *dev)
static int pl061_resume(struct device *dev)
{
struct pl061 *pl061 = dev_get_drvdata(dev);
- int offset;
+ const struct pl061_regs *regs;
+ unsigned int dir;
+ int offset, ret;
- for (offset = 0; offset < PL061_GPIO_NR; offset++) {
- if (pl061->csave_regs.gpio_dir & (BIT(offset)))
- pl061_direction_output(&pl061->gc, offset,
- pl061->csave_regs.gpio_data &
- (BIT(offset)));
- else
- pl061_direction_input(&pl061->gc, offset);
- }
+ if (!pl061->data->pm)
+ return 0;
- writeb(pl061->csave_regs.gpio_is, pl061->base + GPIOIS);
- writeb(pl061->csave_regs.gpio_ibe, pl061->base + GPIOIBE);
- writeb(pl061->csave_regs.gpio_iev, pl061->base + GPIOIEV);
- writeb(pl061->csave_regs.gpio_ie, pl061->base + GPIOIE);
+ regs = pl061->data->regs;
- return 0;
+ ret = regmap_read(pl061->regmap, regs->dir, &dir);
+ if (ret)
+ return ret;
+
+ regcache_mark_dirty(pl061->regmap);
+
+ ret = regcache_sync_region(pl061->regmap, regs->dir, regs->dir);
+ if (ret)
+ return ret;
+
+ for (offset = 0; offset < pl061->data->ngpio; offset++) {
+ if (!(dir & BIT(offset)))
+ continue;
+
+ ret = regmap_write(pl061->regmap,
+ BIT(offset + PL061_DATA_OFFSET),
+ !!(pl061->saved_dat & BIT(offset)) << offset);
+ if (ret)
+ return ret;
+ }
+
+ return regcache_sync_region(pl061->regmap, regs->is, regs->ie);
}
static DEFINE_SIMPLE_DEV_PM_OPS(pl061_dev_pm_ops, pl061_suspend, pl061_resume);
@@ -412,6 +564,7 @@ static const struct amba_id pl061_ids[] = {
{
.id = 0x00041061,
.mask = 0x000fffff,
+ .data = (void *)&pl061_arm,
},
{ 0, 0 },
};
--
2.34.1
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v7 11/15] gpio: pl061: add Ambarella register-layout variant
2026-09-15 11:15 [PATCH v7 00/15] Ambarella CV75 SoC minimal bring-up Long Zhao via B4 Relay
` (9 preceding siblings ...)
2026-09-15 11:15 ` [PATCH v7 10/15] gpio: pl061: convert to gpio-regmap and a custom irqchip Long Zhao via B4 Relay
@ 2026-09-15 11:15 ` Long Zhao via B4 Relay
2026-09-16 10:51 ` Andy Shevchenko
2026-09-15 11:15 ` [PATCH v7 12/15] pinctrl: ambarella: add CV75 pin controller Long Zhao via B4 Relay
` (4 subsequent siblings)
15 siblings, 1 reply; 34+ messages in thread
From: Long Zhao via B4 Relay @ 2026-09-15 11:15 UTC (permalink / raw)
To: Arnd Bergmann, Krzysztof Kozlowski, Alexandre Belloni, soc,
linux-arm-kernel
Cc: Long Zhao, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Michael Turquette, Stephen Boyd, Jerome Brunet, Linus Walleij,
Bartosz Golaszewski, Greg Kroah-Hartman, Jiri Slaby,
Andy Shevchenko, Ilpo Järvinen, Catalin Marinas,
Will Deacon, Long Zhao, Lee Jones, mfd, devicetree, linux-clk,
linux-gpio, linux-serial, linux-kernel
From: Long Zhao <longzhao@ambarella.com>
Add a 32-bit, 32-line layout with DAT/DIR/IS/IBE/IEV/IE/MIS/IC plus
MASK and ENABLE. Probe unmasks the bank and enables the controller.
IRQ type changes also clear the pending bit. This derivative does not
need write-after-direction or system suspend save/restore.
Match the part with designer AMBA_VENDOR_AMBARELLA (JEP106 identity
0x68, bit 7 set) and part number 0x061.
Signed-off-by: Long Zhao <longzhao@ambarella.com>
---
drivers/gpio/gpio-pl061.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++
include/linux/amba/bus.h | 1 +
2 files changed, 59 insertions(+)
diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c
index 1d448aafca50..1abd08a85422 100644
--- a/drivers/gpio/gpio-pl061.c
+++ b/drivers/gpio/gpio-pl061.c
@@ -40,6 +40,18 @@
#define PL061_MIS 0x418
#define PL061_IC 0x41c
+#define AMBARELLA_GPIO_NR 32
+#define AMBARELLA_DAT 0x00
+#define AMBARELLA_DIR 0x04
+#define AMBARELLA_IS 0x08
+#define AMBARELLA_IBE 0x0c
+#define AMBARELLA_IEV 0x10
+#define AMBARELLA_IE 0x14
+#define AMBARELLA_MIS 0x20
+#define AMBARELLA_IC 0x24
+#define AMBARELLA_MASK 0x28
+#define AMBARELLA_ENABLE 0x2c
+
struct pl061_regs {
unsigned int dat;
unsigned int dir;
@@ -344,6 +356,18 @@ static bool pl061_arm_volatile_reg(struct device *dev, unsigned int reg)
}
}
+static bool pl061_ambarella_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case AMBARELLA_DAT:
+ case AMBARELLA_MIS:
+ case AMBARELLA_IC:
+ return true;
+ default:
+ return false;
+ }
+}
+
static const struct regmap_config pl061_arm_regmap_config = {
.reg_bits = 16,
.val_bits = 8,
@@ -354,6 +378,15 @@ static const struct regmap_config pl061_arm_regmap_config = {
.fast_io = true,
};
+static const struct regmap_config pl061_ambarella_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = AMBARELLA_ENABLE,
+ .volatile_reg = pl061_ambarella_volatile_reg,
+ .fast_io = true,
+};
+
static const struct pl061_regs pl061_arm_regs = {
.dir = PL061_DIR,
.is = PL061_IS,
@@ -364,6 +397,19 @@ static const struct pl061_regs pl061_arm_regs = {
.ic = PL061_IC,
};
+static const struct pl061_regs pl061_ambarella_regs = {
+ .dat = AMBARELLA_DAT,
+ .dir = AMBARELLA_DIR,
+ .is = AMBARELLA_IS,
+ .ibe = AMBARELLA_IBE,
+ .iev = AMBARELLA_IEV,
+ .ie = AMBARELLA_IE,
+ .mis = AMBARELLA_MIS,
+ .ic = AMBARELLA_IC,
+ .mask = AMBARELLA_MASK,
+ .enable = AMBARELLA_ENABLE,
+};
+
static const struct pl061_drvdata pl061_arm = {
.regmap_config = &pl061_arm_regmap_config,
.regs = &pl061_arm_regs,
@@ -373,6 +419,13 @@ static const struct pl061_drvdata pl061_arm = {
.reg_mask_xlate = pl061_arm_reg_mask_xlate,
};
+static const struct pl061_drvdata pl061_ambarella = {
+ .regmap_config = &pl061_ambarella_regmap_config,
+ .regs = &pl061_ambarella_regs,
+ .ngpio = AMBARELLA_GPIO_NR,
+ .clear_irq_on_type = true,
+};
+
static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
{
struct device *dev = &adev->dev;
@@ -566,6 +619,11 @@ static const struct amba_id pl061_ids[] = {
.mask = 0x000fffff,
.data = (void *)&pl061_arm,
},
+ {
+ .id = (AMBA_VENDOR_AMBARELLA << 12) | 0x061,
+ .mask = 0x000fffff,
+ .data = (void *)&pl061_ambarella,
+ },
{ 0, 0 },
};
MODULE_DEVICE_TABLE(amba, pl061_ids);
diff --git a/include/linux/amba/bus.h b/include/linux/amba/bus.h
index 80a74cd2da7e..411533bfa61d 100644
--- a/include/linux/amba/bus.h
+++ b/include/linux/amba/bus.h
@@ -98,6 +98,7 @@ enum amba_vendor {
AMBA_VENDOR_ST = 0x80,
AMBA_VENDOR_QCOM = 0x51,
AMBA_VENDOR_LSI = 0xb6,
+ AMBA_VENDOR_AMBARELLA = 0xe8,
};
extern const struct bus_type amba_bustype;
--
2.34.1
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v7 12/15] pinctrl: ambarella: add CV75 pin controller
2026-09-15 11:15 [PATCH v7 00/15] Ambarella CV75 SoC minimal bring-up Long Zhao via B4 Relay
` (10 preceding siblings ...)
2026-09-15 11:15 ` [PATCH v7 11/15] gpio: pl061: add Ambarella register-layout variant Long Zhao via B4 Relay
@ 2026-09-15 11:15 ` Long Zhao via B4 Relay
2026-09-16 10:49 ` Andy Shevchenko
2026-09-15 11:15 ` [PATCH v7 13/15] serial: 8250_dw: add Ambarella CV75 quirks Long Zhao via B4 Relay
` (3 subsequent siblings)
15 siblings, 1 reply; 34+ messages in thread
From: Long Zhao via B4 Relay @ 2026-09-15 11:15 UTC (permalink / raw)
To: Arnd Bergmann, Krzysztof Kozlowski, Alexandre Belloni, soc,
linux-arm-kernel
Cc: Long Zhao, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Michael Turquette, Stephen Boyd, Jerome Brunet, Linus Walleij,
Bartosz Golaszewski, Greg Kroah-Hartman, Jiri Slaby,
Andy Shevchenko, Ilpo Järvinen, Catalin Marinas,
Will Deacon, Long Zhao, Lee Jones, mfd, devicetree, linux-clk,
linux-gpio, linux-serial, linux-kernel
From: Long Zhao <longzhao@ambarella.com>
Add an Ambarella pinmux-only pinctrl driver with CV75 function/group
tables. GPIO is handled by the PL061 driver.
Signed-off-by: Long Zhao <longzhao@ambarella.com>
---
drivers/pinctrl/Kconfig | 17 +
drivers/pinctrl/Makefile | 2 +
drivers/pinctrl/pinctrl-ambarella-cv75.c | 554 +++++++++++++++++++++++++++++++
drivers/pinctrl/pinctrl-ambarella.c | 554 +++++++++++++++++++++++++++++++
drivers/pinctrl/pinctrl-ambarella.h | 44 +++
5 files changed, 1171 insertions(+)
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index c2cdd7b2c49b..08fbe3dc041b 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -37,6 +37,23 @@ config DEBUG_PINCTRL
help
Say Y here to add some extra checks and diagnostics to PINCTRL calls.
+config PINCTRL_AMB
+ bool "Ambarella pin controller support"
+ depends on ARCH_AMBARELLA || COMPILE_TEST
+ depends on HAS_IOMEM
+ default y if ARCH_AMBARELLA
+ select GENERIC_PINCONF
+ select GENERIC_PINCTRL_GROUPS
+ select GENERIC_PINMUX_FUNCTIONS
+ select GPIOLIB
+ select GPIO_PL061
+ select MFD_SYSCON
+ select PINCONF
+ select PINMUX
+ help
+ Say Y here to enable the Ambarella pinmux/pinconf controller.
+ GPIO is provided by the PL061 driver.
+
config PINCTRL_AMD
bool "AMD GPIO pin control"
depends on HAS_IOMEM
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index a35d71135abf..ce0825f4fc1b 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -10,6 +10,8 @@ obj-$(CONFIG_GENERIC_PINCONF) += pinconf-generic.o
obj-$(CONFIG_GENERIC_PINCTRL) += pinctrl-generic.o
obj-$(CONFIG_OF) += devicetree.o
+obj-$(CONFIG_PINCTRL_AMB) += pinctrl-ambarella.o
+obj-$(CONFIG_PINCTRL_AMB) += pinctrl-ambarella-cv75.o
obj-$(CONFIG_PINCTRL_AMD) += pinctrl-amd.o
obj-$(CONFIG_PINCTRL_AMDISP) += pinctrl-amdisp.o
obj-$(CONFIG_PINCTRL_APPLE_GPIO) += pinctrl-apple-gpio.o
diff --git a/drivers/pinctrl/pinctrl-ambarella-cv75.c b/drivers/pinctrl/pinctrl-ambarella-cv75.c
new file mode 100644
index 000000000000..7db03bb60c15
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-ambarella-cv75.c
@@ -0,0 +1,554 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Ambarella CV75 pinctrl data
+ *
+ * Copyright (C) 2026, Ambarella, Inc.
+ */
+
+#include <linux/array_size.h>
+#include <linux/types.h>
+
+#include <linux/pinctrl/pinctrl.h>
+
+#include "pinctrl-ambarella.h"
+
+#define CV75_PINMUX_GROUP(_name, ...) \
+ static const u32 cv75_##_name##_pinmux[] = { __VA_ARGS__ }
+
+#define CV75_GROUP(_name) \
+ { \
+ .name = #_name, \
+ .mux = cv75_##_name##_pinmux, \
+ .nmux = ARRAY_SIZE(cv75_##_name##_pinmux), \
+ }
+
+#define CV75_FUNCTION(_name) \
+ PINCTRL_PINFUNCTION(#_name, cv75_##_name##_groups, \
+ ARRAY_SIZE(cv75_##_name##_groups))
+
+/* UART */
+CV75_PINMUX_GROUP(uart0, AMBA_PINMUX(44, 1), AMBA_PINMUX(45, 1));
+CV75_PINMUX_GROUP(uart1, AMBA_PINMUX(46, 1), AMBA_PINMUX(47, 1));
+CV75_PINMUX_GROUP(uart1_flow, AMBA_PINMUX(48, 1), AMBA_PINMUX(49, 1));
+CV75_PINMUX_GROUP(uart2_a, AMBA_PINMUX(46, 2), AMBA_PINMUX(47, 2));
+CV75_PINMUX_GROUP(uart2_b, AMBA_PINMUX(50, 2), AMBA_PINMUX(51, 2));
+CV75_PINMUX_GROUP(uart2_c, AMBA_PINMUX(66, 2), AMBA_PINMUX(68, 2));
+CV75_PINMUX_GROUP(uart2_flow_a, AMBA_PINMUX(48, 2), AMBA_PINMUX(49, 2));
+CV75_PINMUX_GROUP(uart2_flow_b, AMBA_PINMUX(65, 3), AMBA_PINMUX(67, 3));
+CV75_PINMUX_GROUP(uart3_a, AMBA_PINMUX(70, 3), AMBA_PINMUX(72, 3));
+CV75_PINMUX_GROUP(uart3_b, AMBA_PINMUX(80, 3), AMBA_PINMUX(79, 3));
+CV75_PINMUX_GROUP(uart3_flow_a, AMBA_PINMUX(71, 3), AMBA_PINMUX(69, 3));
+CV75_PINMUX_GROUP(uart3_flow_b, AMBA_PINMUX(81, 3), AMBA_PINMUX(82, 3));
+CV75_PINMUX_GROUP(uart4_a, AMBA_PINMUX(29, 3), AMBA_PINMUX(30, 3));
+CV75_PINMUX_GROUP(uart4_b, AMBA_PINMUX(76, 3), AMBA_PINMUX(77, 3));
+CV75_PINMUX_GROUP(uart4_flow_a, AMBA_PINMUX(28, 3), AMBA_PINMUX(31, 3));
+CV75_PINMUX_GROUP(uart4_flow_b, AMBA_PINMUX(74, 3), AMBA_PINMUX(75, 3));
+
+/* Flash */
+CV75_PINMUX_GROUP(snand, AMBA_PINMUX(79, 1), AMBA_PINMUX(80, 1),
+ AMBA_PINMUX(81, 1), AMBA_PINMUX(82, 1),
+ AMBA_PINMUX(83, 1), AMBA_PINMUX(84, 1));
+CV75_PINMUX_GROUP(spinor, AMBA_PINMUX(79, 2), AMBA_PINMUX(80, 2),
+ AMBA_PINMUX(81, 2), AMBA_PINMUX(82, 2),
+ AMBA_PINMUX(83, 2), AMBA_PINMUX(84, 2),
+ AMBA_PINMUX(85, 2));
+
+/* SD/MMC */
+CV75_PINMUX_GROUP(sdmmc0_cd, AMBA_PINMUX(6, 1));
+CV75_PINMUX_GROUP(sdmmc0_wp, AMBA_PINMUX(7, 1));
+CV75_PINMUX_GROUP(sdmmc0_reset, AMBA_PINMUX(8, 1));
+CV75_PINMUX_GROUP(sdmmc0_hs_sel, AMBA_PINMUX(93, 1));
+CV75_PINMUX_GROUP(sdmmc0_1bit, AMBA_PINMUX(0, 1), AMBA_PINMUX(4, 1),
+ AMBA_PINMUX(5, 1));
+CV75_PINMUX_GROUP(sdmmc0_4bit, AMBA_PINMUX(0, 1), AMBA_PINMUX(1, 1),
+ AMBA_PINMUX(2, 1), AMBA_PINMUX(3, 1),
+ AMBA_PINMUX(4, 1), AMBA_PINMUX(5, 1));
+CV75_PINMUX_GROUP(sdmmc1_cd, AMBA_PINMUX(15, 1));
+CV75_PINMUX_GROUP(sdmmc1_wp, AMBA_PINMUX(16, 1));
+CV75_PINMUX_GROUP(sdmmc1_reset, AMBA_PINMUX(17, 1));
+CV75_PINMUX_GROUP(sdmmc1_hs_sel, AMBA_PINMUX(94, 1));
+CV75_PINMUX_GROUP(sdmmc1_1bit, AMBA_PINMUX(9, 1), AMBA_PINMUX(13, 1),
+ AMBA_PINMUX(14, 1));
+CV75_PINMUX_GROUP(sdmmc1_4bit, AMBA_PINMUX(9, 1), AMBA_PINMUX(10, 1),
+ AMBA_PINMUX(11, 1), AMBA_PINMUX(12, 1),
+ AMBA_PINMUX(13, 1), AMBA_PINMUX(14, 1));
+
+/* Ethernet */
+CV75_PINMUX_GROUP(enet_ext_osc_clk, AMBA_PINMUX(77, 1));
+CV75_PINMUX_GROUP(enet_2nd_ref_clk_a, AMBA_PINMUX(78, 1));
+CV75_PINMUX_GROUP(enet_2nd_ref_clk_b, AMBA_PINMUX(76, 2));
+CV75_PINMUX_GROUP(enet0_ptp_pps_o, AMBA_PINMUX(74, 1));
+CV75_PINMUX_GROUP(rgmii0, AMBA_PINMUX(62, 1), AMBA_PINMUX(63, 1),
+ AMBA_PINMUX(64, 1), AMBA_PINMUX(65, 1),
+ AMBA_PINMUX(66, 1), AMBA_PINMUX(67, 1),
+ AMBA_PINMUX(68, 1), AMBA_PINMUX(69, 1),
+ AMBA_PINMUX(70, 1), AMBA_PINMUX(71, 1),
+ AMBA_PINMUX(72, 1), AMBA_PINMUX(73, 1),
+ AMBA_PINMUX(75, 1), AMBA_PINMUX(76, 1));
+CV75_PINMUX_GROUP(rmii0, AMBA_PINMUX(62, 1), AMBA_PINMUX(63, 1),
+ AMBA_PINMUX(64, 1), AMBA_PINMUX(67, 1),
+ AMBA_PINMUX(68, 1), AMBA_PINMUX(71, 1),
+ AMBA_PINMUX(72, 1), AMBA_PINMUX(73, 1),
+ AMBA_PINMUX(75, 2));
+
+/* I2C */
+CV75_PINMUX_GROUP(i2c0_a, AMBA_PINMUX(67, 2), AMBA_PINMUX(68, 2));
+CV75_PINMUX_GROUP(i2c0_b, AMBA_PINMUX(86, 1), AMBA_PINMUX(87, 1));
+CV75_PINMUX_GROUP(i2c1_a, AMBA_PINMUX(22, 2), AMBA_PINMUX(23, 2));
+CV75_PINMUX_GROUP(i2c1_b, AMBA_PINMUX(69, 2), AMBA_PINMUX(70, 2));
+CV75_PINMUX_GROUP(i2c2, AMBA_PINMUX(88, 1), AMBA_PINMUX(89, 1));
+CV75_PINMUX_GROUP(i2c3_a, AMBA_PINMUX(24, 2), AMBA_PINMUX(25, 2));
+CV75_PINMUX_GROUP(i2c3_b, AMBA_PINMUX(48, 3), AMBA_PINMUX(49, 3));
+CV75_PINMUX_GROUP(i2c3_c, AMBA_PINMUX(58, 3), AMBA_PINMUX(59, 3));
+CV75_PINMUX_GROUP(i2cs_a, AMBA_PINMUX(19, 2), AMBA_PINMUX(21, 2));
+CV75_PINMUX_GROUP(i2cs_b, AMBA_PINMUX(60, 3), AMBA_PINMUX(61, 3));
+CV75_PINMUX_GROUP(i2cs_c, AMBA_PINMUX(71, 2), AMBA_PINMUX(72, 2));
+CV75_PINMUX_GROUP(i2cs_d, AMBA_PINMUX(86, 2), AMBA_PINMUX(87, 2));
+
+/* CAN, IR, WDT */
+CV75_PINMUX_GROUP(can0, AMBA_PINMUX(50, 1), AMBA_PINMUX(51, 1));
+CV75_PINMUX_GROUP(can1, AMBA_PINMUX(52, 1), AMBA_PINMUX(53, 1));
+CV75_PINMUX_GROUP(ir, AMBA_PINMUX(18, 1));
+CV75_PINMUX_GROUP(wdt_a, AMBA_PINMUX(20, 2));
+CV75_PINMUX_GROUP(wdt_b, AMBA_PINMUX(27, 3));
+CV75_PINMUX_GROUP(wdt_c, AMBA_PINMUX(39, 2));
+CV75_PINMUX_GROUP(wdt_d, AMBA_PINMUX(83, 3));
+CV75_PINMUX_GROUP(wdt_e, AMBA_PINMUX(85, 4));
+CV75_PINMUX_GROUP(wdt_f, AMBA_PINMUX(90, 1));
+
+/* I2S */
+CV75_PINMUX_GROUP(i2s0, AMBA_PINMUX(54, 1), AMBA_PINMUX(55, 1),
+ AMBA_PINMUX(56, 1), AMBA_PINMUX(57, 1));
+CV75_PINMUX_GROUP(i2s1, AMBA_PINMUX(58, 1), AMBA_PINMUX(59, 1),
+ AMBA_PINMUX(60, 1), AMBA_PINMUX(61, 1));
+CV75_PINMUX_GROUP(dmic0, AMBA_PINMUX(54, 2), AMBA_PINMUX(55, 2));
+
+/* PWM */
+CV75_PINMUX_GROUP(pwm0, AMBA_PINMUX(40, 1));
+CV75_PINMUX_GROUP(pwm1, AMBA_PINMUX(41, 1));
+CV75_PINMUX_GROUP(pwm2, AMBA_PINMUX(42, 1));
+CV75_PINMUX_GROUP(pwm3, AMBA_PINMUX(43, 1));
+CV75_PINMUX_GROUP(pwm4_a, AMBA_PINMUX(19, 3));
+CV75_PINMUX_GROUP(pwm4_b, AMBA_PINMUX(32, 4));
+CV75_PINMUX_GROUP(pwm5_a, AMBA_PINMUX(20, 3));
+CV75_PINMUX_GROUP(pwm5_b, AMBA_PINMUX(33, 4));
+CV75_PINMUX_GROUP(pwm6_a, AMBA_PINMUX(21, 3));
+CV75_PINMUX_GROUP(pwm6_b, AMBA_PINMUX(34, 4));
+CV75_PINMUX_GROUP(pwm7_a, AMBA_PINMUX(22, 3));
+CV75_PINMUX_GROUP(pwm7_b, AMBA_PINMUX(35, 4));
+CV75_PINMUX_GROUP(pwm8_a, AMBA_PINMUX(23, 3));
+CV75_PINMUX_GROUP(pwm8_b, AMBA_PINMUX(36, 4));
+CV75_PINMUX_GROUP(pwm9_a, AMBA_PINMUX(24, 3));
+CV75_PINMUX_GROUP(pwm9_b, AMBA_PINMUX(37, 4));
+CV75_PINMUX_GROUP(pwm10_a, AMBA_PINMUX(25, 3));
+CV75_PINMUX_GROUP(pwm10_b, AMBA_PINMUX(38, 4));
+CV75_PINMUX_GROUP(pwm11_a, AMBA_PINMUX(26, 3));
+CV75_PINMUX_GROUP(pwm11_b, AMBA_PINMUX(39, 4));
+
+/* SPI */
+CV75_PINMUX_GROUP(spi0, AMBA_PINMUX(19, 1), AMBA_PINMUX(20, 1),
+ AMBA_PINMUX(21, 1));
+CV75_PINMUX_GROUP(spi1, AMBA_PINMUX(24, 1), AMBA_PINMUX(25, 1),
+ AMBA_PINMUX(26, 1));
+CV75_PINMUX_GROUP(spi2, AMBA_PINMUX(28, 1), AMBA_PINMUX(29, 1),
+ AMBA_PINMUX(30, 1));
+CV75_PINMUX_GROUP(spi3_a, AMBA_PINMUX(32, 5), AMBA_PINMUX(33, 5),
+ AMBA_PINMUX(34, 5));
+CV75_PINMUX_GROUP(spi3_b, AMBA_PINMUX(40, 2), AMBA_PINMUX(41, 2),
+ AMBA_PINMUX(43, 2));
+CV75_PINMUX_GROUP(spi3_c, AMBA_PINMUX(58, 2), AMBA_PINMUX(59, 2),
+ AMBA_PINMUX(60, 2));
+CV75_PINMUX_GROUP(spi_slave_a, AMBA_PINMUX(24, 4), AMBA_PINMUX(25, 4),
+ AMBA_PINMUX(26, 4), AMBA_PINMUX(27, 4));
+CV75_PINMUX_GROUP(spi_slave_b, AMBA_PINMUX(28, 2), AMBA_PINMUX(29, 2),
+ AMBA_PINMUX(30, 2), AMBA_PINMUX(31, 2));
+CV75_PINMUX_GROUP(spi_slave_c, AMBA_PINMUX(36, 5), AMBA_PINMUX(37, 5),
+ AMBA_PINMUX(38, 5), AMBA_PINMUX(39, 5));
+CV75_PINMUX_GROUP(spi_slave_d, AMBA_PINMUX(50, 3), AMBA_PINMUX(51, 3),
+ AMBA_PINMUX(52, 3), AMBA_PINMUX(53, 3));
+CV75_PINMUX_GROUP(spi_slave_e, AMBA_PINMUX(81, 4), AMBA_PINMUX(82, 4),
+ AMBA_PINMUX(83, 4), AMBA_PINMUX(84, 4));
+
+/* VIN master sync */
+CV75_PINMUX_GROUP(vin_master_sync_a, AMBA_PINMUX(91, 1),
+ AMBA_PINMUX(92, 1));
+CV75_PINMUX_GROUP(vin_master_sync_b, AMBA_PINMUX(91, 2),
+ AMBA_PINMUX(92, 2));
+CV75_PINMUX_GROUP(vin_master_sync_c, AMBA_PINMUX(40, 3),
+ AMBA_PINMUX(41, 3));
+CV75_PINMUX_GROUP(vin_master_sync_d, AMBA_PINMUX(46, 3),
+ AMBA_PINMUX(47, 3));
+CV75_PINMUX_GROUP(vin_master_sync_e, AMBA_PINMUX(79, 4),
+ AMBA_PINMUX(80, 4));
+CV75_PINMUX_GROUP(vsync0, AMBA_PINMUX(32, 1));
+CV75_PINMUX_GROUP(vsync1, AMBA_PINMUX(33, 1));
+CV75_PINMUX_GROUP(vsync2, AMBA_PINMUX(34, 1));
+CV75_PINMUX_GROUP(vsync3, AMBA_PINMUX(35, 1));
+CV75_PINMUX_GROUP(hsync0, AMBA_PINMUX(36, 1));
+CV75_PINMUX_GROUP(hsync1, AMBA_PINMUX(37, 1));
+
+static const struct amb_pinmux_group cv75_pin_groups[] = {
+ CV75_GROUP(uart0),
+ CV75_GROUP(uart1), CV75_GROUP(uart1_flow),
+ CV75_GROUP(uart2_a), CV75_GROUP(uart2_b), CV75_GROUP(uart2_c),
+ CV75_GROUP(uart2_flow_a), CV75_GROUP(uart2_flow_b),
+ CV75_GROUP(uart3_a), CV75_GROUP(uart3_b),
+ CV75_GROUP(uart3_flow_a), CV75_GROUP(uart3_flow_b),
+ CV75_GROUP(uart4_a), CV75_GROUP(uart4_b),
+ CV75_GROUP(uart4_flow_a), CV75_GROUP(uart4_flow_b),
+ CV75_GROUP(snand), CV75_GROUP(spinor),
+ CV75_GROUP(sdmmc0_cd), CV75_GROUP(sdmmc0_wp),
+ CV75_GROUP(sdmmc0_reset), CV75_GROUP(sdmmc0_hs_sel),
+ CV75_GROUP(sdmmc0_1bit), CV75_GROUP(sdmmc0_4bit),
+ CV75_GROUP(sdmmc1_cd), CV75_GROUP(sdmmc1_wp),
+ CV75_GROUP(sdmmc1_reset), CV75_GROUP(sdmmc1_hs_sel),
+ CV75_GROUP(sdmmc1_1bit), CV75_GROUP(sdmmc1_4bit),
+ CV75_GROUP(enet_ext_osc_clk), CV75_GROUP(enet_2nd_ref_clk_a),
+ CV75_GROUP(enet_2nd_ref_clk_b), CV75_GROUP(enet0_ptp_pps_o),
+ CV75_GROUP(rgmii0), CV75_GROUP(rmii0),
+ CV75_GROUP(i2c0_a), CV75_GROUP(i2c0_b),
+ CV75_GROUP(i2c1_a), CV75_GROUP(i2c1_b), CV75_GROUP(i2c2),
+ CV75_GROUP(i2c3_a), CV75_GROUP(i2c3_b), CV75_GROUP(i2c3_c),
+ CV75_GROUP(i2cs_a), CV75_GROUP(i2cs_b),
+ CV75_GROUP(i2cs_c), CV75_GROUP(i2cs_d),
+ CV75_GROUP(can0), CV75_GROUP(can1), CV75_GROUP(ir),
+ CV75_GROUP(wdt_a), CV75_GROUP(wdt_b), CV75_GROUP(wdt_c),
+ CV75_GROUP(wdt_d), CV75_GROUP(wdt_e), CV75_GROUP(wdt_f),
+ CV75_GROUP(i2s0), CV75_GROUP(i2s1),
+ CV75_GROUP(dmic0),
+ CV75_GROUP(pwm0), CV75_GROUP(pwm1),
+ CV75_GROUP(pwm2), CV75_GROUP(pwm3),
+ CV75_GROUP(pwm4_a), CV75_GROUP(pwm4_b),
+ CV75_GROUP(pwm5_a), CV75_GROUP(pwm5_b),
+ CV75_GROUP(pwm6_a), CV75_GROUP(pwm6_b),
+ CV75_GROUP(pwm7_a), CV75_GROUP(pwm7_b),
+ CV75_GROUP(pwm8_a), CV75_GROUP(pwm8_b),
+ CV75_GROUP(pwm9_a), CV75_GROUP(pwm9_b),
+ CV75_GROUP(pwm10_a), CV75_GROUP(pwm10_b),
+ CV75_GROUP(pwm11_a), CV75_GROUP(pwm11_b),
+ CV75_GROUP(spi0), CV75_GROUP(spi1), CV75_GROUP(spi2),
+ CV75_GROUP(spi3_a), CV75_GROUP(spi3_b), CV75_GROUP(spi3_c),
+ CV75_GROUP(spi_slave_a), CV75_GROUP(spi_slave_b),
+ CV75_GROUP(spi_slave_c), CV75_GROUP(spi_slave_d),
+ CV75_GROUP(spi_slave_e),
+ CV75_GROUP(vin_master_sync_a), CV75_GROUP(vin_master_sync_b),
+ CV75_GROUP(vin_master_sync_c), CV75_GROUP(vin_master_sync_d),
+ CV75_GROUP(vin_master_sync_e),
+ CV75_GROUP(vsync0), CV75_GROUP(vsync1),
+ CV75_GROUP(vsync2), CV75_GROUP(vsync3),
+ CV75_GROUP(hsync0), CV75_GROUP(hsync1),
+};
+
+static const char * const cv75_uart0_groups[] = {
+ "uart0",
+};
+
+static const char * const cv75_uart1_groups[] = {
+ "uart1",
+ "uart1_flow",
+};
+
+static const char * const cv75_uart2_groups[] = {
+ "uart2_a",
+ "uart2_b",
+ "uart2_c",
+ "uart2_flow_a",
+ "uart2_flow_b",
+};
+
+static const char * const cv75_uart3_groups[] = {
+ "uart3_a",
+ "uart3_b",
+ "uart3_flow_a",
+ "uart3_flow_b",
+};
+
+static const char * const cv75_uart4_groups[] = {
+ "uart4_a",
+ "uart4_b",
+ "uart4_flow_a",
+ "uart4_flow_b",
+};
+
+static const char * const cv75_snand_groups[] = {
+ "snand",
+};
+
+static const char * const cv75_spinor_groups[] = {
+ "spinor",
+};
+
+static const char * const cv75_sdmmc0_groups[] = {
+ "sdmmc0_cd",
+ "sdmmc0_wp",
+ "sdmmc0_reset",
+ "sdmmc0_hs_sel",
+ "sdmmc0_1bit",
+ "sdmmc0_4bit",
+};
+
+static const char * const cv75_sdmmc1_groups[] = {
+ "sdmmc1_cd",
+ "sdmmc1_wp",
+ "sdmmc1_reset",
+ "sdmmc1_hs_sel",
+ "sdmmc1_1bit",
+ "sdmmc1_4bit",
+};
+
+static const char * const cv75_enet0_groups[] = {
+ "enet_ext_osc_clk",
+ "enet_2nd_ref_clk_a",
+ "enet_2nd_ref_clk_b",
+ "enet0_ptp_pps_o",
+ "rgmii0",
+ "rmii0",
+};
+
+static const char * const cv75_i2c0_groups[] = {
+ "i2c0_a",
+ "i2c0_b",
+};
+
+static const char * const cv75_i2c1_groups[] = {
+ "i2c1_a",
+ "i2c1_b",
+};
+
+static const char * const cv75_i2c2_groups[] = {
+ "i2c2",
+};
+
+static const char * const cv75_i2c3_groups[] = {
+ "i2c3_a",
+ "i2c3_b",
+ "i2c3_c",
+};
+
+static const char * const cv75_i2cs_groups[] = {
+ "i2cs_a",
+ "i2cs_b",
+ "i2cs_c",
+ "i2cs_d",
+};
+
+static const char * const cv75_can0_groups[] = {
+ "can0",
+};
+
+static const char * const cv75_can1_groups[] = {
+ "can1",
+};
+
+static const char * const cv75_ir_groups[] = {
+ "ir",
+};
+
+static const char * const cv75_wdt_groups[] = {
+ "wdt_a",
+ "wdt_b",
+ "wdt_c",
+ "wdt_d",
+ "wdt_e",
+ "wdt_f",
+};
+
+static const char * const cv75_i2s0_groups[] = {
+ "i2s0",
+};
+
+static const char * const cv75_i2s1_groups[] = {
+ "i2s1",
+};
+
+static const char * const cv75_dmic0_groups[] = {
+ "dmic0",
+};
+
+static const char * const cv75_pwm0_groups[] = {
+ "pwm0",
+};
+
+static const char * const cv75_pwm1_groups[] = {
+ "pwm1",
+};
+
+static const char * const cv75_pwm2_groups[] = {
+ "pwm2",
+};
+
+static const char * const cv75_pwm3_groups[] = {
+ "pwm3",
+};
+
+static const char * const cv75_pwm4_groups[] = {
+ "pwm4_a",
+ "pwm4_b",
+};
+
+static const char * const cv75_pwm5_groups[] = {
+ "pwm5_a",
+ "pwm5_b",
+};
+
+static const char * const cv75_pwm6_groups[] = {
+ "pwm6_a",
+ "pwm6_b",
+};
+
+static const char * const cv75_pwm7_groups[] = {
+ "pwm7_a",
+ "pwm7_b",
+};
+
+static const char * const cv75_pwm8_groups[] = {
+ "pwm8_a",
+ "pwm8_b",
+};
+
+static const char * const cv75_pwm9_groups[] = {
+ "pwm9_a",
+ "pwm9_b",
+};
+
+static const char * const cv75_pwm10_groups[] = {
+ "pwm10_a",
+ "pwm10_b",
+};
+
+static const char * const cv75_pwm11_groups[] = {
+ "pwm11_a",
+ "pwm11_b",
+};
+
+static const char * const cv75_spi0_groups[] = {
+ "spi0",
+};
+
+static const char * const cv75_spi1_groups[] = {
+ "spi1",
+};
+
+static const char * const cv75_spi2_groups[] = {
+ "spi2",
+};
+
+static const char * const cv75_spi3_groups[] = {
+ "spi3_a",
+ "spi3_b",
+ "spi3_c",
+};
+
+static const char * const cv75_spi_slave_groups[] = {
+ "spi_slave_a",
+ "spi_slave_b",
+ "spi_slave_c",
+ "spi_slave_d",
+ "spi_slave_e",
+};
+
+static const char * const cv75_vin_master_sync_groups[] = {
+ "vin_master_sync_a",
+ "vin_master_sync_b",
+ "vin_master_sync_c",
+ "vin_master_sync_d",
+ "vin_master_sync_e",
+};
+
+static const char * const cv75_vsync0_groups[] = {
+ "vsync0",
+};
+
+static const char * const cv75_vsync1_groups[] = {
+ "vsync1",
+};
+
+static const char * const cv75_vsync2_groups[] = {
+ "vsync2",
+};
+
+static const char * const cv75_vsync3_groups[] = {
+ "vsync3",
+};
+
+static const char * const cv75_hsync0_groups[] = {
+ "hsync0",
+};
+
+static const char * const cv75_hsync1_groups[] = {
+ "hsync1",
+};
+
+static const struct pinfunction cv75_pin_functions[] = {
+ CV75_FUNCTION(uart0),
+ CV75_FUNCTION(uart1),
+ CV75_FUNCTION(uart2),
+ CV75_FUNCTION(uart3),
+ CV75_FUNCTION(uart4),
+ CV75_FUNCTION(snand),
+ CV75_FUNCTION(spinor),
+ CV75_FUNCTION(sdmmc0),
+ CV75_FUNCTION(sdmmc1),
+ CV75_FUNCTION(enet0),
+ CV75_FUNCTION(i2c0),
+ CV75_FUNCTION(i2c1),
+ CV75_FUNCTION(i2c2),
+ CV75_FUNCTION(i2c3),
+ CV75_FUNCTION(i2cs),
+ CV75_FUNCTION(can0),
+ CV75_FUNCTION(can1),
+ CV75_FUNCTION(ir),
+ CV75_FUNCTION(wdt),
+ CV75_FUNCTION(i2s0),
+ CV75_FUNCTION(i2s1),
+ CV75_FUNCTION(dmic0),
+ CV75_FUNCTION(pwm0),
+ CV75_FUNCTION(pwm1),
+ CV75_FUNCTION(pwm2),
+ CV75_FUNCTION(pwm3),
+ CV75_FUNCTION(pwm4),
+ CV75_FUNCTION(pwm5),
+ CV75_FUNCTION(pwm6),
+ CV75_FUNCTION(pwm7),
+ CV75_FUNCTION(pwm8),
+ CV75_FUNCTION(pwm9),
+ CV75_FUNCTION(pwm10),
+ CV75_FUNCTION(pwm11),
+ CV75_FUNCTION(spi0),
+ CV75_FUNCTION(spi1),
+ CV75_FUNCTION(spi2),
+ CV75_FUNCTION(spi3),
+ CV75_FUNCTION(spi_slave),
+ CV75_FUNCTION(vin_master_sync),
+ CV75_FUNCTION(vsync0),
+ CV75_FUNCTION(vsync1),
+ CV75_FUNCTION(vsync2),
+ CV75_FUNCTION(vsync3),
+ CV75_FUNCTION(hsync0),
+ CV75_FUNCTION(hsync1),
+};
+
+const struct amb_pinctrl_data ambarella_cv75_pinctrl_data = {
+ .groups = cv75_pin_groups,
+ .functions = cv75_pin_functions,
+ .ngroups = ARRAY_SIZE(cv75_pin_groups),
+ .nfunctions = ARRAY_SIZE(cv75_pin_functions),
+ .nr_banks = 3,
+ .npins = 96,
+ .ds0 = {
+ 0x314, 0x320, 0x32c,
+ },
+ .ds1 = {
+ 0x318, 0x324, 0x330,
+ },
+ .ds2 = {
+ 0x31c, 0x328, 0x334,
+ },
+ .pull_en = {
+ 0x60, 0x64, 0x68,
+ },
+ .pull_dir = {
+ 0x7c, 0x80, 0x84,
+ },
+ .have_ds2 = true,
+};
diff --git a/drivers/pinctrl/pinctrl-ambarella.c b/drivers/pinctrl/pinctrl-ambarella.c
new file mode 100644
index 000000000000..0186267a1eec
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-ambarella.c
@@ -0,0 +1,554 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Pinctrl driver for Ambarella SoCs
+ *
+ * Copyright (C) 2012-2026, Ambarella, Inc.
+ */
+
+#include <linux/array_size.h>
+#include <linux/bits.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+
+#include "core.h"
+#include "pinmux.h"
+#include "pinctrl-ambarella.h"
+
+#define IOMUX_REG(bank, n) (((bank) * 0xc) + ((n) * 4))
+#define IOMUX_CTRL_SET 0xf0
+
+#define PINID_TO_BANK(p) ((p) >> 5)
+#define PINID_TO_OFFSET(p) ((p) & 0x1f)
+
+struct amb_pinctrl {
+ struct pinctrl_desc desc;
+ void __iomem *iomux_base;
+ struct regmap *ds_regmap;
+ struct regmap *pull_regmap;
+ const struct amb_pinctrl_data *data;
+ struct device *dev;
+ struct pinctrl_dev *pctl;
+ spinlock_t lock;
+};
+
+static const int amb_ds1_ma[] = { 2, 4, 8, 12 };
+static const int amb_ds2_ma[] = { 3, 4, 6, 8, 9, 12 };
+
+static const struct pinctrl_ops amb_pctrl_ops = {
+ .get_groups_count = pinctrl_generic_get_group_count,
+ .get_group_name = pinctrl_generic_get_group_name,
+ .get_group_pins = pinctrl_generic_get_group_pins,
+ .dt_node_to_map = pinconf_generic_dt_node_to_map_all,
+ .dt_free_map = pinconf_generic_dt_free_map,
+};
+
+static void amb_iomux_commit(struct amb_pinctrl *ipc)
+{
+ writel(0x1, ipc->iomux_base + IOMUX_CTRL_SET);
+ writel(0x0, ipc->iomux_base + IOMUX_CTRL_SET);
+}
+
+static void amb_pinmux_set_altfunc(struct amb_pinctrl *ipc, u32 bank,
+ u32 offset, u32 altfunc)
+{
+ if (bank >= ipc->data->nr_banks)
+ return;
+
+ for (unsigned int i = 0; i < 3; i++) {
+ u32 data;
+
+ data = readl_relaxed(ipc->iomux_base + IOMUX_REG(bank, i));
+ data &= ~BIT(offset);
+ data |= ((altfunc >> i) & 1U) << offset;
+ writel_relaxed(data, ipc->iomux_base + IOMUX_REG(bank, i));
+ }
+}
+
+static int amb_pinmux_set_mux(struct pinctrl_dev *pctldev,
+ unsigned int selector, unsigned int group)
+{
+ struct amb_pinctrl *ipc = pinctrl_dev_get_drvdata(pctldev);
+ struct group_desc *grp;
+ const u32 *mux;
+ unsigned long flags;
+
+ (void)selector;
+
+ grp = pinctrl_generic_get_group(pctldev, group);
+ if (!grp || !grp->data)
+ return -EINVAL;
+
+ mux = grp->data;
+
+ spin_lock_irqsave(&ipc->lock, flags);
+ for (unsigned int i = 0; i < grp->grp.npins; i++) {
+ u32 pin = AMBA_PINMUX_TO_PIN(mux[i]);
+ u32 alt = AMBA_PINMUX_TO_ALT(mux[i]);
+
+ amb_pinmux_set_altfunc(ipc, PINID_TO_BANK(pin),
+ PINID_TO_OFFSET(pin), alt);
+ }
+ amb_iomux_commit(ipc);
+ spin_unlock_irqrestore(&ipc->lock, flags);
+
+ return 0;
+}
+
+static int amb_pinmux_gpio_request_enable(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ unsigned int pin)
+{
+ struct amb_pinctrl *ipc = pinctrl_dev_get_drvdata(pctldev);
+ unsigned long flags;
+
+ if (!range || !range->gc)
+ return -EINVAL;
+
+ if (pin >= ipc->data->npins)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ipc->lock, flags);
+ amb_pinmux_set_altfunc(ipc, PINID_TO_BANK(pin), PINID_TO_OFFSET(pin), 0);
+ amb_iomux_commit(ipc);
+ spin_unlock_irqrestore(&ipc->lock, flags);
+
+ return 0;
+}
+
+static const struct pinmux_ops amb_pinmux_ops = {
+ .get_functions_count = pinmux_generic_get_function_count,
+ .get_function_name = pinmux_generic_get_function_name,
+ .get_function_groups = pinmux_generic_get_function_groups,
+ .set_mux = amb_pinmux_set_mux,
+ .gpio_request_enable = amb_pinmux_gpio_request_enable,
+ .strict = true,
+};
+
+static int amb_drive_strength_to_reg_ds1(u32 strength)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(amb_ds1_ma); i++) {
+ if (amb_ds1_ma[i] == strength)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int amb_drive_strength_to_reg_ds2(u32 strength)
+{
+ /* Hardware steps are 3, 4, 6, 8, 9, 12 mA; 5 and 7 are accepted aliases */
+ if (strength == 5)
+ strength = 4;
+ else if (strength == 7)
+ strength = 8;
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(amb_ds2_ma); i++) {
+ if (amb_ds2_ma[i] == strength)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int amb_drive_strength_to_reg(const struct amb_pinctrl *ipc, u32 strength)
+{
+ if (ipc->data->have_ds2)
+ return amb_drive_strength_to_reg_ds2(strength);
+
+ return amb_drive_strength_to_reg_ds1(strength);
+}
+
+static int amb_reg_to_drive_strength(const struct amb_pinctrl *ipc, u32 ds)
+{
+ if (ipc->data->have_ds2) {
+ if (ds >= ARRAY_SIZE(amb_ds2_ma))
+ return -EINVAL;
+ return amb_ds2_ma[ds];
+ }
+
+ if (ds >= ARRAY_SIZE(amb_ds1_ma))
+ return -EINVAL;
+
+ return amb_ds1_ma[ds];
+}
+
+static int amb_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
+ unsigned long *configs, unsigned int num_configs)
+{
+ struct amb_pinctrl *ipc = pinctrl_dev_get_drvdata(pctldev);
+ u32 bank = PINID_TO_BANK(pin);
+ u32 offset = PINID_TO_OFFSET(pin);
+ int ret;
+
+ if (bank >= ipc->data->nr_banks)
+ return -EINVAL;
+
+ for (unsigned int i = 0; i < num_configs; i++) {
+ enum pin_config_param param = pinconf_to_config_param(configs[i]);
+ u32 arg = pinconf_to_config_argument(configs[i]);
+ int ds;
+
+ switch (param) {
+ case PIN_CONFIG_BIAS_DISABLE:
+ ret = regmap_update_bits(ipc->pull_regmap,
+ ipc->data->pull_en[bank], BIT(offset), 0);
+ if (ret)
+ return ret;
+ break;
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ case PIN_CONFIG_BIAS_PULL_UP:
+ ret = regmap_update_bits(ipc->pull_regmap,
+ ipc->data->pull_dir[bank], BIT(offset),
+ (param == PIN_CONFIG_BIAS_PULL_UP) ?
+ BIT(offset) : 0);
+ if (ret)
+ return ret;
+ ret = regmap_update_bits(ipc->pull_regmap,
+ ipc->data->pull_en[bank], BIT(offset),
+ BIT(offset));
+ if (ret)
+ return ret;
+ break;
+ case PIN_CONFIG_DRIVE_STRENGTH:
+ ds = amb_drive_strength_to_reg(ipc, arg);
+ if (ds < 0)
+ return ds;
+ if (ipc->data->have_ds2) {
+ ret = regmap_update_bits(ipc->ds_regmap,
+ ipc->data->ds0[bank], BIT(offset),
+ (ds & BIT(0)) ? BIT(offset) : 0);
+ if (ret)
+ return ret;
+ ret = regmap_update_bits(ipc->ds_regmap,
+ ipc->data->ds1[bank], BIT(offset),
+ (ds & BIT(1)) ? BIT(offset) : 0);
+ if (ret)
+ return ret;
+ ret = regmap_update_bits(ipc->ds_regmap,
+ ipc->data->ds2[bank], BIT(offset),
+ (ds & BIT(2)) ? BIT(offset) : 0);
+ if (ret)
+ return ret;
+ } else {
+ ret = regmap_update_bits(ipc->ds_regmap,
+ ipc->data->ds0[bank], BIT(offset),
+ (ds & BIT(1)) ? BIT(offset) : 0);
+ if (ret)
+ return ret;
+ ret = regmap_update_bits(ipc->ds_regmap,
+ ipc->data->ds1[bank], BIT(offset),
+ (ds & BIT(0)) ? BIT(offset) : 0);
+ if (ret)
+ return ret;
+ }
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ }
+
+ return 0;
+}
+
+static int amb_pinconf_group_set(struct pinctrl_dev *pctldev,
+ unsigned int selector,
+ unsigned long *configs,
+ unsigned int num_configs)
+{
+ const unsigned int *pins;
+ unsigned int npins;
+ int ret;
+
+ ret = pinctrl_generic_get_group_pins(pctldev, selector, &pins, &npins);
+ if (ret)
+ return ret;
+
+ for (unsigned int i = 0; i < npins; i++) {
+ ret = amb_pinconf_set(pctldev, pins[i], configs, num_configs);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int amb_pinconf_get(struct pinctrl_dev *pctldev,
+ unsigned int pin, unsigned long *config)
+{
+ struct amb_pinctrl *ipc = pinctrl_dev_get_drvdata(pctldev);
+ enum pin_config_param param = pinconf_to_config_param(*config);
+ u32 bank = PINID_TO_BANK(pin);
+ u32 offset = PINID_TO_OFFSET(pin);
+ u32 pull_en, pull_dir, ds0, ds1, ds2, ds;
+ int ret, strength;
+
+ if (bank >= ipc->data->nr_banks)
+ return -EINVAL;
+
+ switch (param) {
+ case PIN_CONFIG_BIAS_DISABLE:
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ case PIN_CONFIG_BIAS_PULL_UP:
+ ret = regmap_read(ipc->pull_regmap, ipc->data->pull_en[bank],
+ &pull_en);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(ipc->pull_regmap, ipc->data->pull_dir[bank],
+ &pull_dir);
+ if (ret)
+ return ret;
+
+ pull_en = (pull_en >> offset) & 1;
+ pull_dir = (pull_dir >> offset) & 1;
+
+ if (param == PIN_CONFIG_BIAS_DISABLE) {
+ if (pull_en)
+ return -EINVAL;
+ *config = pinconf_to_config_packed(param, 0);
+ return 0;
+ }
+
+ if (!pull_en)
+ return -EINVAL;
+ if (param == PIN_CONFIG_BIAS_PULL_UP && !pull_dir)
+ return -EINVAL;
+ if (param == PIN_CONFIG_BIAS_PULL_DOWN && pull_dir)
+ return -EINVAL;
+
+ *config = pinconf_to_config_packed(param, 1);
+ return 0;
+
+ case PIN_CONFIG_DRIVE_STRENGTH:
+ ret = regmap_read(ipc->ds_regmap, ipc->data->ds0[bank], &ds0);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(ipc->ds_regmap, ipc->data->ds1[bank], &ds1);
+ if (ret)
+ return ret;
+
+ ds0 = (ds0 >> offset) & 1;
+ ds1 = (ds1 >> offset) & 1;
+ if (ipc->data->have_ds2) {
+ ret = regmap_read(ipc->ds_regmap, ipc->data->ds2[bank],
+ &ds2);
+ if (ret)
+ return ret;
+
+ ds2 = (ds2 >> offset) & 1;
+ ds = (ds2 << 2) | (ds1 << 1) | ds0;
+ } else {
+ ds = (ds0 << 1) | ds1;
+ }
+
+ strength = amb_reg_to_drive_strength(ipc, ds);
+ if (strength < 0)
+ return strength;
+
+ *config = pinconf_to_config_packed(param, strength);
+ return 0;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static const struct pinconf_ops amb_pinconf_ops = {
+ .is_generic = true,
+ .pin_config_get = amb_pinconf_get,
+ .pin_config_set = amb_pinconf_set,
+ .pin_config_group_set = amb_pinconf_group_set,
+};
+
+static int amb_pinctrl_add_groups(struct amb_pinctrl *ipc)
+{
+ const struct amb_pinctrl_data *data = ipc->data;
+ unsigned int total_pins = 0;
+ unsigned int *pins;
+
+ for (unsigned int i = 0; i < data->ngroups; i++) {
+ const struct amb_pinmux_group *g = &data->groups[i];
+
+ if (!g->name || !g->mux || !g->nmux)
+ return -EINVAL;
+
+ total_pins += g->nmux;
+ }
+
+ pins = devm_kcalloc(ipc->dev, total_pins, sizeof(*pins), GFP_KERNEL);
+ if (!pins)
+ return -ENOMEM;
+
+ for (unsigned int i = 0; i < data->ngroups; i++) {
+ const struct amb_pinmux_group *g = &data->groups[i];
+ unsigned int *grp_pins = pins;
+ int ret;
+
+ for (unsigned int j = 0; j < g->nmux; j++) {
+ unsigned int pin = AMBA_PINMUX_TO_PIN(g->mux[j]);
+
+ if (pin >= data->npins)
+ return -EINVAL;
+
+ pins[j] = pin;
+ }
+
+ pins += g->nmux;
+
+ ret = pinctrl_generic_add_group(ipc->pctl, g->name, grp_pins,
+ g->nmux, (void *)(uintptr_t)g->mux);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int amb_pinctrl_add_functions(struct amb_pinctrl *ipc)
+{
+ const struct amb_pinctrl_data *data = ipc->data;
+
+ for (unsigned int i = 0; i < data->nfunctions; i++) {
+ int ret;
+
+ ret = pinmux_generic_add_pinfunction(ipc->pctl,
+ &data->functions[i], NULL);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int amb_pinctrl_register(struct amb_pinctrl *ipc)
+{
+ struct pinctrl_pin_desc *pindesc;
+ int ret;
+
+ pindesc = devm_kcalloc(ipc->dev, ipc->data->npins, sizeof(*pindesc),
+ GFP_KERNEL);
+ if (!pindesc)
+ return -ENOMEM;
+
+ for (unsigned int pin = 0; pin < ipc->data->npins; pin++) {
+ pindesc[pin].number = pin;
+ pindesc[pin].name = devm_kasprintf(ipc->dev, GFP_KERNEL,
+ "io%u", pin);
+ if (!pindesc[pin].name)
+ return -ENOMEM;
+ }
+
+ ipc->desc.name = dev_name(ipc->dev);
+ ipc->desc.pins = pindesc;
+ ipc->desc.npins = ipc->data->npins;
+ ipc->desc.pctlops = &amb_pctrl_ops;
+ ipc->desc.pmxops = &amb_pinmux_ops;
+ ipc->desc.confops = &amb_pinconf_ops;
+ ipc->desc.owner = THIS_MODULE;
+
+ ret = devm_pinctrl_register_and_init(ipc->dev, &ipc->desc, ipc, &ipc->pctl);
+ if (ret)
+ return ret;
+
+ ret = amb_pinctrl_add_groups(ipc);
+ if (ret)
+ return ret;
+
+ ret = amb_pinctrl_add_functions(ipc);
+ if (ret)
+ return ret;
+
+ return pinctrl_enable(ipc->pctl);
+}
+
+static int amb_pinctrl_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct amb_pinctrl *ipc;
+ int ret;
+
+ ipc = devm_kzalloc(dev, sizeof(*ipc), GFP_KERNEL);
+ if (!ipc)
+ return -ENOMEM;
+
+ ipc->dev = dev;
+ ipc->data = device_get_match_data(dev);
+ if (!ipc->data)
+ return dev_err_probe(dev, -EINVAL, "missing SoC data\n");
+
+ if (!ipc->data->nr_banks || ipc->data->nr_banks > AMBA_MAX_BANKS ||
+ !ipc->data->npins ||
+ !ipc->data->groups || !ipc->data->ngroups ||
+ !ipc->data->functions || !ipc->data->nfunctions)
+ return dev_err_probe(dev, -EINVAL, "invalid SoC data\n");
+
+ ipc->iomux_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(ipc->iomux_base))
+ return PTR_ERR(ipc->iomux_base);
+
+ ipc->ds_regmap = syscon_regmap_lookup_by_phandle(dev_of_node(dev),
+ "ambarella,drive-strength-syscon");
+ if (IS_ERR(ipc->ds_regmap))
+ return dev_err_probe(dev, PTR_ERR(ipc->ds_regmap),
+ "missing drive-strength syscon\n");
+
+ ipc->pull_regmap = syscon_regmap_lookup_by_phandle(dev_of_node(dev),
+ "ambarella,pull-syscon");
+ if (IS_ERR(ipc->pull_regmap))
+ return dev_err_probe(dev, PTR_ERR(ipc->pull_regmap),
+ "missing pull syscon\n");
+
+ spin_lock_init(&ipc->lock);
+
+ ret = amb_pinctrl_register(ipc);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register pinctrl\n");
+
+ platform_set_drvdata(pdev, ipc);
+
+ return 0;
+}
+
+static const struct of_device_id amb_pinctrl_dt_match[] = {
+ {
+ .compatible = "ambarella,cv75-pinctrl",
+ .data = &ambarella_cv75_pinctrl_data,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, amb_pinctrl_dt_match);
+
+static struct platform_driver amb_pinctrl_driver = {
+ .probe = amb_pinctrl_probe,
+ .driver = {
+ .name = "ambarella-pinctrl",
+ .of_match_table = amb_pinctrl_dt_match,
+ },
+};
+
+static int __init amb_pinctrl_drv_register(void)
+{
+ return platform_driver_register(&amb_pinctrl_driver);
+}
+arch_initcall(amb_pinctrl_drv_register);
+
+MODULE_AUTHOR("Cao Rongrong <rrcao@ambarella.com>");
+MODULE_DESCRIPTION("Ambarella SoC pinctrl driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pinctrl/pinctrl-ambarella.h b/drivers/pinctrl/pinctrl-ambarella.h
new file mode 100644
index 000000000000..fc56c19a3087
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-ambarella.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Ambarella pinctrl SoC data
+ *
+ * Copyright (C) 2012-2026, Ambarella, Inc.
+ */
+
+#ifndef _PINCTRL_AMBARELLA_H
+#define _PINCTRL_AMBARELLA_H
+
+#include <linux/types.h>
+
+#include <linux/pinctrl/pinctrl.h>
+
+#define AMBA_MAX_BANKS 8
+
+#define AMBA_PINMUX(pin, alt) (((alt) << 12) | (pin))
+#define AMBA_PINMUX_TO_PIN(mux) ((mux) & 0xfff)
+#define AMBA_PINMUX_TO_ALT(mux) (((mux) >> 12) & 0x7)
+
+struct amb_pinmux_group {
+ const char *name;
+ const u32 *mux;
+ unsigned int nmux;
+};
+
+struct amb_pinctrl_data {
+ const struct amb_pinmux_group *groups;
+ const struct pinfunction *functions;
+ unsigned int ngroups;
+ unsigned int nfunctions;
+ unsigned int nr_banks;
+ unsigned int npins;
+ unsigned int ds0[AMBA_MAX_BANKS];
+ unsigned int ds1[AMBA_MAX_BANKS];
+ unsigned int ds2[AMBA_MAX_BANKS];
+ unsigned int pull_en[AMBA_MAX_BANKS];
+ unsigned int pull_dir[AMBA_MAX_BANKS];
+ bool have_ds2;
+};
+
+extern const struct amb_pinctrl_data ambarella_cv75_pinctrl_data;
+
+#endif /* _PINCTRL_AMBARELLA_H */
--
2.34.1
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v7 13/15] serial: 8250_dw: add Ambarella CV75 quirks
2026-09-15 11:15 [PATCH v7 00/15] Ambarella CV75 SoC minimal bring-up Long Zhao via B4 Relay
` (11 preceding siblings ...)
2026-09-15 11:15 ` [PATCH v7 12/15] pinctrl: ambarella: add CV75 pin controller Long Zhao via B4 Relay
@ 2026-09-15 11:15 ` Long Zhao via B4 Relay
2026-09-15 11:47 ` Ilpo Järvinen
2026-09-15 13:21 ` Andy Shevchenko
2026-09-15 11:15 ` [PATCH v7 14/15] arm64: ambarella: add ARCH_AMBARELLA and CV75 EVK DT Long Zhao via B4 Relay
` (2 subsequent siblings)
15 siblings, 2 replies; 34+ messages in thread
From: Long Zhao via B4 Relay @ 2026-09-15 11:15 UTC (permalink / raw)
To: Arnd Bergmann, Krzysztof Kozlowski, Alexandre Belloni, soc,
linux-arm-kernel
Cc: Long Zhao, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Michael Turquette, Stephen Boyd, Jerome Brunet, Linus Walleij,
Bartosz Golaszewski, Greg Kroah-Hartman, Jiri Slaby,
Andy Shevchenko, Ilpo Järvinen, Catalin Marinas,
Will Deacon, Long Zhao, Lee Jones, mfd, devicetree, linux-clk,
linux-gpio, linux-serial, linux-kernel
From: Long Zhao <longzhao@ambarella.com>
The Ambarella CV75 UART is Ambarella IP, not a Synopsys DesignWare
licensed block. At the register level it is close enough to DW APB UART
that a full custom UART driver is unnecessary: reuse 8250_dw with a
small platform quirk.
Quirks:
- keep IER ETOI (bit 5) set for RX timeout behaviour
- skip set_rate; baud clock is managed outside the UART block
Reviewed-by: Linus Walleij <linusw@kernel.org>
Signed-off-by: Long Zhao <longzhao@ambarella.com>
---
drivers/tty/serial/8250/8250_dw.c | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index 5fba913f3301..19431c476c55 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -43,6 +43,9 @@
#define RZN1_UART_xDMACR_8_WORD_BURST (2 << 1)
#define RZN1_UART_xDMACR_BLK_SZ(x) ((x) << 3)
+/* Ambarella IER: receive timeout interrupt enable */
+#define AMBARELLA_UART_IER_ETOI BIT(5)
+
/* Quirks */
#define DW_UART_QUIRK_OCTEON BIT(0)
#define DW_UART_QUIRK_ARMADA_38X BIT(1)
@@ -60,6 +63,7 @@
struct dw8250_platform_data {
u8 usr_reg;
+ u8 ier_mask;
u32 cpr_value;
unsigned int quirks;
};
@@ -572,6 +576,25 @@ static void dw8250_prepare_rx_dma(struct uart_8250_port *p)
dw8250_writel_ext(up, RZN1_UART_RDMACR, val);
}
+static int dw8250_ier_mask_startup(struct uart_port *p)
+{
+ struct dw8250_data *d = to_dw8250_data(p->private_data);
+ struct uart_8250_port *up = up_to_u8250p(p);
+ int ret;
+
+ ret = serial8250_do_startup(p);
+ if (ret)
+ return ret;
+
+ /*
+ * Ambarella keeps variant IER bits (e.g. ETOI) set for correct RX
+ * timeout behaviour.
+ */
+ up->ier |= d->pdata->ier_mask;
+
+ return 0;
+}
+
static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data)
{
unsigned int quirks = data->pdata->quirks;
@@ -606,6 +629,8 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data)
p->serial_in = dw8250_serial_in32;
data->uart_16550_compatible = true;
}
+ if (data->pdata->ier_mask)
+ p->startup = dw8250_ier_mask_startup;
}
static void dw8250_reset_control_assert(void *data)
@@ -879,6 +904,12 @@ static const struct dw8250_platform_data dw8250_intc10ee = {
.quirks = DW_UART_QUIRK_IER_KICK,
};
+static const struct dw8250_platform_data dw8250_ambarella_cv75_data = {
+ .usr_reg = DW_UART_USR,
+ .ier_mask = AMBARELLA_UART_IER_ETOI,
+ .quirks = DW_UART_QUIRK_SKIP_SET_RATE,
+};
+
static const struct dw8250_platform_data dw8250_ultrarisc_dp1000_data = {
.usr_reg = DW_UART_USR,
.cpr_value = FIELD_PREP_CONST(DW_UART_CPR_ABP_DATA_WIDTH, 2) |
@@ -889,6 +920,7 @@ static const struct dw8250_platform_data dw8250_ultrarisc_dp1000_data = {
};
static const struct of_device_id dw8250_of_match[] = {
+ { .compatible = "ambarella,cv75-uart", .data = &dw8250_ambarella_cv75_data },
{ .compatible = "snps,dw-apb-uart", .data = &dw8250_dw_apb },
{ .compatible = "cavium,octeon-3860-uart", .data = &dw8250_octeon_3860_data },
{ .compatible = "marvell,armada-38x-uart", .data = &dw8250_armada_38x_data },
--
2.34.1
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v7 14/15] arm64: ambarella: add ARCH_AMBARELLA and CV75 EVK DT
2026-09-15 11:15 [PATCH v7 00/15] Ambarella CV75 SoC minimal bring-up Long Zhao via B4 Relay
` (12 preceding siblings ...)
2026-09-15 11:15 ` [PATCH v7 13/15] serial: 8250_dw: add Ambarella CV75 quirks Long Zhao via B4 Relay
@ 2026-09-15 11:15 ` Long Zhao via B4 Relay
2026-09-15 11:15 ` [PATCH v7 15/15] MAINTAINERS: add ARM/AMBARELLA SoC support Long Zhao via B4 Relay
2026-09-16 9:14 ` [PATCH v7 00/15] Ambarella CV75 SoC minimal bring-up Bartosz Golaszewski
15 siblings, 0 replies; 34+ messages in thread
From: Long Zhao via B4 Relay @ 2026-09-15 11:15 UTC (permalink / raw)
To: Arnd Bergmann, Krzysztof Kozlowski, Alexandre Belloni, soc,
linux-arm-kernel
Cc: Long Zhao, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Michael Turquette, Stephen Boyd, Jerome Brunet, Linus Walleij,
Bartosz Golaszewski, Greg Kroah-Hartman, Jiri Slaby,
Andy Shevchenko, Ilpo Järvinen, Catalin Marinas,
Will Deacon, Long Zhao, Lee Jones, mfd, devicetree, linux-clk,
linux-gpio, linux-serial, linux-kernel
From: Long Zhao <longzhao@ambarella.com>
Enable ARCH_AMBARELLA and add a minimal CV75 SoC / EVK device tree for
console bring-up with the RCT clocks, UART, pinctrl and GPIO banks.
Reviewed-by: Linus Walleij <linusw@kernel.org>
Signed-off-by: Long Zhao <longzhao@ambarella.com>
---
arch/arm64/Kconfig.platforms | 6 +
arch/arm64/boot/dts/Makefile | 1 +
arch/arm64/boot/dts/ambarella/Makefile | 3 +
arch/arm64/boot/dts/ambarella/cv75-evk.dts | 30 +++++
arch/arm64/boot/dts/ambarella/cv75.dtsi | 177 +++++++++++++++++++++++++++++
arch/arm64/configs/defconfig | 1 +
6 files changed, 218 insertions(+)
diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms
index d2acfac73003..16d7e3a4c2d6 100644
--- a/arch/arm64/Kconfig.platforms
+++ b/arch/arm64/Kconfig.platforms
@@ -26,6 +26,12 @@ config ARCH_SUNXI
help
This enables support for Allwinner sunxi based SoCs like the A64.
+config ARCH_AMBARELLA
+ bool "Ambarella SoC family"
+ select PINCTRL
+ help
+ This enables support for the ARM64 based Ambarella SoCs like CV75.
+
config ARCH_ALPINE
bool "Annapurna Labs Alpine platform"
select ALPINE_MSI if PCI
diff --git a/arch/arm64/boot/dts/Makefile b/arch/arm64/boot/dts/Makefile
index fc726b215f12..f1709eb5d915 100644
--- a/arch/arm64/boot/dts/Makefile
+++ b/arch/arm64/boot/dts/Makefile
@@ -4,6 +4,7 @@ subdir-y += airoha
subdir-y += allwinner
subdir-y += altera
subdir-y += amazon
+subdir-y += ambarella
subdir-y += amd
subdir-y += amlogic
subdir-y += apm
diff --git a/arch/arm64/boot/dts/ambarella/Makefile b/arch/arm64/boot/dts/ambarella/Makefile
new file mode 100644
index 000000000000..ddca3d7b18cc
--- /dev/null
+++ b/arch/arm64/boot/dts/ambarella/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+dtb-$(CONFIG_ARCH_AMBARELLA) += cv75-evk.dtb
diff --git a/arch/arm64/boot/dts/ambarella/cv75-evk.dts b/arch/arm64/boot/dts/ambarella/cv75-evk.dts
new file mode 100644
index 000000000000..42a0fe24b83b
--- /dev/null
+++ b/arch/arm64/boot/dts/ambarella/cv75-evk.dts
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Ambarella CV75 EVK board
+ */
+
+/dts-v1/;
+
+#include "cv75.dtsi"
+
+/ {
+ model = "Ambarella CV75 EVK Board";
+ compatible = "ambarella,cv75-evk", "ambarella,cv75";
+
+ aliases {
+ serial0 = &uart0;
+ };
+
+ chosen {
+ stdout-path = &uart0;
+ };
+
+ memory@200000 {
+ device_type = "memory";
+ reg = <0x0 0x00200000 0x0 0x07e00000>;
+ };
+};
+
+&uart0 {
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/ambarella/cv75.dtsi b/arch/arm64/boot/dts/ambarella/cv75.dtsi
new file mode 100644
index 000000000000..c36b6af48237
--- /dev/null
+++ b/arch/arm64/boot/dts/ambarella/cv75.dtsi
@@ -0,0 +1,177 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Ambarella CV75 SoC
+ *
+ * Boot peripheral: UART0 console.
+ * Clocks: a single ambarella,cv75-rct provider.
+ */
+
+#include <dt-bindings/clock/ambarella,cv75-rct.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+/ {
+ compatible = "ambarella,cv75";
+ interrupt-parent = <&gic>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu@0 {
+ compatible = "arm,cortex-a76";
+ device_type = "cpu";
+ reg = <0x000>;
+ enable-method = "psci";
+ };
+
+ cpu@1 {
+ compatible = "arm,cortex-a76";
+ device_type = "cpu";
+ reg = <0x100>;
+ enable-method = "psci";
+ };
+ };
+
+ osc: oscillator {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <24000000>;
+ clock-output-names = "osc";
+ };
+
+ psci {
+ compatible = "arm,psci-0.2";
+ method = "smc";
+ };
+
+ soc {
+ compatible = "simple-bus";
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+
+ ahb@ffe0000000 {
+ compatible = "simple-bus";
+ reg = <0xff 0xe0000000 0x0 0x01000000>;
+ ranges = <0xe0000000 0xff 0xe0000000 0x01000000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ s_scratchpad_syscon: scratchpad@e002e000 {
+ compatible = "ambarella,cv75-sec-scratchpad", "syscon";
+ reg = <0xe002e000 0x1000>;
+ };
+ };
+
+ apb@ffe4000000 {
+ compatible = "simple-bus";
+ reg = <0xff 0xe4000000 0x0 0x01000000>;
+ ranges = <0xe4000000 0xff 0xe4000000 0x01000000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ uart0: serial@e4000000 {
+ compatible = "ambarella,cv75-uart", "snps,dw-apb-uart";
+ reg = <0xe4000000 0x1000>;
+ clocks = <&rct CV75_GCLK_UART0>;
+ interrupts = <GIC_SPI 170 IRQ_TYPE_LEVEL_HIGH>;
+ reg-shift = <2>;
+ reg-io-width = <4>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart0_pins>;
+
+ status = "disabled";
+ };
+
+ pinctrl: pinctrl@e4010000 {
+ compatible = "ambarella,cv75-pinctrl";
+ reg = <0xe4010000 0x1000>;
+ ambarella,drive-strength-syscon = <&rct>;
+ ambarella,pull-syscon = <&s_scratchpad_syscon>;
+
+ uart0_pins: uart0-pins {
+ function = "uart0";
+ groups = "uart0";
+ };
+ };
+
+ gpio0: gpio@e4013000 {
+ compatible = "ambarella,cv75-gpio", "arm,primecell";
+ reg = <0xe4013000 0x1000>;
+ clocks = <&rct CV75_GCLK_APB>;
+ clock-names = "apb_pclk";
+ interrupts = <GIC_SPI 163 IRQ_TYPE_LEVEL_HIGH>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ gpio-ranges = <&pinctrl 0 0 32>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ arm,primecell-periphid = <0x000e8061>;
+ };
+
+ gpio1: gpio@e4014000 {
+ compatible = "ambarella,cv75-gpio", "arm,primecell";
+ reg = <0xe4014000 0x1000>;
+ clocks = <&rct CV75_GCLK_APB>;
+ clock-names = "apb_pclk";
+ interrupts = <GIC_SPI 164 IRQ_TYPE_LEVEL_HIGH>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ gpio-ranges = <&pinctrl 0 32 32>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ arm,primecell-periphid = <0x000e8061>;
+ };
+
+ gpio2: gpio@e4015000 {
+ compatible = "ambarella,cv75-gpio", "arm,primecell";
+ reg = <0xe4015000 0x1000>;
+ clocks = <&rct CV75_GCLK_APB>;
+ clock-names = "apb_pclk";
+ interrupts = <GIC_SPI 165 IRQ_TYPE_LEVEL_HIGH>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ gpio-ranges = <&pinctrl 0 64 32>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ arm,primecell-periphid = <0x000e8061>;
+ };
+ };
+
+ rct: clock-controller@ffed080000 {
+ compatible = "ambarella,cv75-rct", "syscon";
+ reg = <0xff 0xed080000 0x0 0x1000>;
+ clocks = <&osc>;
+ clock-names = "osc";
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ assigned-clocks = <&rct CV75_GCLK_UART0>;
+ assigned-clock-parents = <&osc>;
+ assigned-clock-rates = <24000000>;
+ };
+
+ gic: interrupt-controller@fff0101000 {
+ compatible = "arm,gic-400";
+ reg = <0xff 0xf0101000 0x0 0x1000>,
+ <0xff 0xf0102000 0x0 0x2000>,
+ <0xff 0xf0104000 0x0 0x2000>,
+ <0xff 0xf0106000 0x0 0x2000>;
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ #address-cells = <0>;
+ interrupts = <GIC_PPI 9
+ (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>;
+ };
+ };
+
+ timer {
+ compatible = "arm,armv8-timer";
+ interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>,
+ <GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>,
+ <GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>,
+ <GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>,
+ <GIC_PPI 12 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>;
+ };
+};
diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index ad3bd3d9507b..454f07a6a6a6 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -36,6 +36,7 @@ CONFIG_CRASH_DUMP=y
CONFIG_ARCH_ACTIONS=y
CONFIG_ARCH_AIROHA=y
CONFIG_ARCH_SUNXI=y
+CONFIG_ARCH_AMBARELLA=y
CONFIG_ARCH_ALPINE=y
CONFIG_ARCH_APPLE=y
CONFIG_ARCH_ARTPEC=y
--
2.34.1
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v7 15/15] MAINTAINERS: add ARM/AMBARELLA SoC support
2026-09-15 11:15 [PATCH v7 00/15] Ambarella CV75 SoC minimal bring-up Long Zhao via B4 Relay
` (13 preceding siblings ...)
2026-09-15 11:15 ` [PATCH v7 14/15] arm64: ambarella: add ARCH_AMBARELLA and CV75 EVK DT Long Zhao via B4 Relay
@ 2026-09-15 11:15 ` Long Zhao via B4 Relay
2026-09-16 9:14 ` [PATCH v7 00/15] Ambarella CV75 SoC minimal bring-up Bartosz Golaszewski
15 siblings, 0 replies; 34+ messages in thread
From: Long Zhao via B4 Relay @ 2026-09-15 11:15 UTC (permalink / raw)
To: Arnd Bergmann, Krzysztof Kozlowski, Alexandre Belloni, soc,
linux-arm-kernel
Cc: Long Zhao, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Michael Turquette, Stephen Boyd, Jerome Brunet, Linus Walleij,
Bartosz Golaszewski, Greg Kroah-Hartman, Jiri Slaby,
Andy Shevchenko, Ilpo Järvinen, Catalin Marinas,
Will Deacon, Long Zhao, Lee Jones, mfd, devicetree, linux-clk,
linux-gpio, linux-serial, linux-kernel
From: Long Zhao <longzhao@ambarella.com>
Add a maintainer entry covering Ambarella CV75 bindings, DTS and
related drivers.
Reviewed-by: Linus Walleij <linusw@kernel.org>
Signed-off-by: Long Zhao <longzhao@ambarella.com>
---
MAINTAINERS | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 803b60ad628f..59e5b32d7a02 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2502,6 +2502,20 @@ F: drivers/clocksource/asm9260_timer.c
F: drivers/rtc/rtc-asm9260.c
F: drivers/watchdog/asm9260_wdt.c
+ARM/AMBARELLA SOC SUPPORT
+M: Long Zhao <longzhao@ambarella.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+L: devicetree@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/arm/ambarella.yaml
+F: Documentation/devicetree/bindings/clock/ambarella,cv75-rct.yaml
+F: Documentation/devicetree/bindings/pinctrl/ambarella,cv75-pinctrl.yaml
+F: arch/arm64/boot/dts/ambarella/
+F: drivers/clk/ambarella/
+F: drivers/pinctrl/pinctrl-ambarella*
+F: include/dt-bindings/clock/ambarella,cv75-rct.h
+N: ambarella
+
ARM/AMD PENSANDO ARM64 ARCHITECTURE
M: Brad Larson <blarson@amd.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
--
2.34.1
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH v7 13/15] serial: 8250_dw: add Ambarella CV75 quirks
2026-09-15 11:15 ` [PATCH v7 13/15] serial: 8250_dw: add Ambarella CV75 quirks Long Zhao via B4 Relay
@ 2026-09-15 11:47 ` Ilpo Järvinen
2026-09-15 13:21 ` Andy Shevchenko
1 sibling, 0 replies; 34+ messages in thread
From: Ilpo Järvinen @ 2026-09-15 11:47 UTC (permalink / raw)
To: Long Zhao
Cc: Arnd Bergmann, Krzysztof Kozlowski, Alexandre Belloni, soc,
linux-arm-kernel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Michael Turquette, Stephen Boyd, Jerome Brunet, Linus Walleij,
Bartosz Golaszewski, Greg Kroah-Hartman, Jiri Slaby,
Andy Shevchenko, Catalin Marinas, Will Deacon, Long Zhao,
Lee Jones, mfd, devicetree, linux-clk, linux-gpio, linux-serial,
LKML
[-- Attachment #1: Type: text/plain, Size: 3543 bytes --]
On Tue, 15 Sep 2026, Long Zhao via B4 Relay wrote:
> From: Long Zhao <longzhao@ambarella.com>
>
> The Ambarella CV75 UART is Ambarella IP, not a Synopsys DesignWare
> licensed block. At the register level it is close enough to DW APB UART
> that a full custom UART driver is unnecessary: reuse 8250_dw with a
> small platform quirk.
>
> Quirks:
> - keep IER ETOI (bit 5) set for RX timeout behaviour
> - skip set_rate; baud clock is managed outside the UART block
>
> Reviewed-by: Linus Walleij <linusw@kernel.org>
> Signed-off-by: Long Zhao <longzhao@ambarella.com>
> ---
> drivers/tty/serial/8250/8250_dw.c | 32 ++++++++++++++++++++++++++++++++
> 1 file changed, 32 insertions(+)
>
> diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
> index 5fba913f3301..19431c476c55 100644
> --- a/drivers/tty/serial/8250/8250_dw.c
> +++ b/drivers/tty/serial/8250/8250_dw.c
> @@ -43,6 +43,9 @@
> #define RZN1_UART_xDMACR_8_WORD_BURST (2 << 1)
> #define RZN1_UART_xDMACR_BLK_SZ(x) ((x) << 3)
>
> +/* Ambarella IER: receive timeout interrupt enable */
> +#define AMBARELLA_UART_IER_ETOI BIT(5)
> +
> /* Quirks */
> #define DW_UART_QUIRK_OCTEON BIT(0)
> #define DW_UART_QUIRK_ARMADA_38X BIT(1)
> @@ -60,6 +63,7 @@
>
> struct dw8250_platform_data {
> u8 usr_reg;
> + u8 ier_mask;
> u32 cpr_value;
> unsigned int quirks;
> };
> @@ -572,6 +576,25 @@ static void dw8250_prepare_rx_dma(struct uart_8250_port *p)
> dw8250_writel_ext(up, RZN1_UART_RDMACR, val);
> }
>
> +static int dw8250_ier_mask_startup(struct uart_port *p)
> +{
> + struct dw8250_data *d = to_dw8250_data(p->private_data);
> + struct uart_8250_port *up = up_to_u8250p(p);
> + int ret;
> +
> + ret = serial8250_do_startup(p);
> + if (ret)
> + return ret;
> +
> + /*
> + * Ambarella keeps variant IER bits (e.g. ETOI) set for correct RX
> + * timeout behaviour.
> + */
> + up->ier |= d->pdata->ier_mask;
> +
> + return 0;
> +}
> +
> static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data)
> {
> unsigned int quirks = data->pdata->quirks;
> @@ -606,6 +629,8 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data)
> p->serial_in = dw8250_serial_in32;
> data->uart_16550_compatible = true;
> }
> + if (data->pdata->ier_mask)
> + p->startup = dw8250_ier_mask_startup;
> }
>
> static void dw8250_reset_control_assert(void *data)
> @@ -879,6 +904,12 @@ static const struct dw8250_platform_data dw8250_intc10ee = {
> .quirks = DW_UART_QUIRK_IER_KICK,
> };
>
> +static const struct dw8250_platform_data dw8250_ambarella_cv75_data = {
> + .usr_reg = DW_UART_USR,
> + .ier_mask = AMBARELLA_UART_IER_ETOI,
> + .quirks = DW_UART_QUIRK_SKIP_SET_RATE,
> +};
> +
> static const struct dw8250_platform_data dw8250_ultrarisc_dp1000_data = {
> .usr_reg = DW_UART_USR,
> .cpr_value = FIELD_PREP_CONST(DW_UART_CPR_ABP_DATA_WIDTH, 2) |
> @@ -889,6 +920,7 @@ static const struct dw8250_platform_data dw8250_ultrarisc_dp1000_data = {
> };
>
> static const struct of_device_id dw8250_of_match[] = {
> + { .compatible = "ambarella,cv75-uart", .data = &dw8250_ambarella_cv75_data },
> { .compatible = "snps,dw-apb-uart", .data = &dw8250_dw_apb },
> { .compatible = "cavium,octeon-3860-uart", .data = &dw8250_octeon_3860_data },
> { .compatible = "marvell,armada-38x-uart", .data = &dw8250_armada_38x_data },
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
--
i.
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH v7 13/15] serial: 8250_dw: add Ambarella CV75 quirks
2026-09-15 11:15 ` [PATCH v7 13/15] serial: 8250_dw: add Ambarella CV75 quirks Long Zhao via B4 Relay
2026-09-15 11:47 ` Ilpo Järvinen
@ 2026-09-15 13:21 ` Andy Shevchenko
1 sibling, 0 replies; 34+ messages in thread
From: Andy Shevchenko @ 2026-09-15 13:21 UTC (permalink / raw)
To: longzhao
Cc: Arnd Bergmann, Krzysztof Kozlowski, Alexandre Belloni, soc,
linux-arm-kernel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Michael Turquette, Stephen Boyd, Jerome Brunet, Linus Walleij,
Bartosz Golaszewski, Greg Kroah-Hartman, Jiri Slaby,
Ilpo Järvinen, Catalin Marinas, Will Deacon, Long Zhao,
Lee Jones, mfd, devicetree, linux-clk, linux-gpio, linux-serial,
linux-kernel
On Tue, Sep 15, 2026 at 07:15:43PM +0800, Long Zhao via B4 Relay wrote:
> The Ambarella CV75 UART is Ambarella IP, not a Synopsys DesignWare
> licensed block. At the register level it is close enough to DW APB UART
> that a full custom UART driver is unnecessary: reuse 8250_dw with a
> small platform quirk.
>
> Quirks:
> - keep IER ETOI (bit 5) set for RX timeout behaviour
> - skip set_rate; baud clock is managed outside the UART block
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
...
> static const struct of_device_id dw8250_of_match[] = {
> + { .compatible = "ambarella,cv75-uart", .data = &dw8250_ambarella_cv75_data },
> { .compatible = "snps,dw-apb-uart", .data = &dw8250_dw_apb },
> { .compatible = "cavium,octeon-3860-uart", .data = &dw8250_octeon_3860_data },
> { .compatible = "marvell,armada-38x-uart", .data = &dw8250_armada_38x_data },
I assume we deliberately keep snps compatible to be the first, so your will be
the second. Update in case it will be v8 of the series.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH v7 07/15] clk: ambarella: add CV75 RCT clock controller
2026-09-15 11:15 ` [PATCH v7 07/15] clk: ambarella: add CV75 RCT clock controller Long Zhao via B4 Relay
@ 2026-09-15 13:24 ` Uwe Kleine-König
2026-09-15 13:27 ` Andy Shevchenko
1 sibling, 0 replies; 34+ messages in thread
From: Uwe Kleine-König @ 2026-09-15 13:24 UTC (permalink / raw)
To: Long Zhao
Cc: Arnd Bergmann, Krzysztof Kozlowski, Alexandre Belloni, soc,
linux-arm-kernel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Michael Turquette, Stephen Boyd, Jerome Brunet, Linus Walleij,
Bartosz Golaszewski, Greg Kroah-Hartman, Jiri Slaby,
Andy Shevchenko, Ilpo Järvinen, Catalin Marinas,
Will Deacon, Long Zhao, Lee Jones, mfd, devicetree, linux-clk,
linux-gpio, linux-serial, linux-kernel
[-- Attachment #1: Type: text/plain, Size: 445 bytes --]
On Tue, Sep 15, 2026 at 07:15:37PM +0800, Long Zhao wrote:
> +#include <linux/mod_devicetable.h>
Please don't include <linux/mod_devicetable.h>. That's a hog for
unneeded dependencies and I'm working on getting rid of it.
> [...]
> +#include <linux/platform_device.h>
You can rely on <linux/platform_device.h> to pull in the definition of
of_device_id. (If you want to go full iwyu, include
<linux/device-id/of.h> instead.)
Best regards
Uwe
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH v7 07/15] clk: ambarella: add CV75 RCT clock controller
2026-09-15 11:15 ` [PATCH v7 07/15] clk: ambarella: add CV75 RCT clock controller Long Zhao via B4 Relay
2026-09-15 13:24 ` Uwe Kleine-König
@ 2026-09-15 13:27 ` Andy Shevchenko
1 sibling, 0 replies; 34+ messages in thread
From: Andy Shevchenko @ 2026-09-15 13:27 UTC (permalink / raw)
To: longzhao
Cc: Arnd Bergmann, Krzysztof Kozlowski, Alexandre Belloni, soc,
linux-arm-kernel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Michael Turquette, Stephen Boyd, Jerome Brunet, Linus Walleij,
Bartosz Golaszewski, Greg Kroah-Hartman, Jiri Slaby,
Ilpo Järvinen, Catalin Marinas, Will Deacon, Long Zhao,
Lee Jones, mfd, devicetree, linux-clk, linux-gpio, linux-serial,
linux-kernel
On Tue, Sep 15, 2026 at 07:15:37PM +0800, Long Zhao via B4 Relay wrote:
> Add a table-driven CCF driver for the CV75 root clock tree. Register
> the core PLL, AHB/APB fixed factors and UART0 composite clock, with
> osc supplied via clk_parent_data.
...
> +#include <linux/array_size.h>
> +#include <linux/bitfield.h>
> +#include <linux/bits.h>
> +#include <linux/clk-provider.h>
> +#include <linux/container_of.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/math64.h>
> +#include <linux/mod_devicetable.h>
No new driver should use this, ID tables are provided by...
> +#include <linux/module.h>
> +#include <linux/overflow.h>
> +#include <linux/platform_device.h>
^^^...this one.
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/types.h>
...
> +struct cv75_fixed_factor {
> + const char *name;
> + u32 id;
> + u16 mult;
> + u16 div;
There is struct u16_fract in math.h. Can you use it instead of these two?
> +};
...
> +static int cv75_rct_probe(struct platform_device *pdev)
> +{
> + struct clk_hw_onecell_data *data;
> + struct device *dev = &pdev->dev;
> + int ret;
> +
> + spin_lock_init(&cv75_ccu.lock);
> +
> + cv75_ccu.base = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(cv75_ccu.base))
> + return PTR_ERR(cv75_ccu.base);
> + data = devm_kzalloc(dev, struct_size(data, hws, CV75_NUM_CLKS),
> + GFP_KERNEL);
I think it's okay to have this on a single line.
> + if (!data)
> + return -ENOMEM;
> + data->num = CV75_NUM_CLKS;
> +
> + ret = cv75_register_plls(dev, data);
> + if (ret)
> + return dev_err_probe(dev, ret, "failed to register plls\n");
> +
> + ret = cv75_register_fixed_factors(dev, data);
> + if (ret)
> + return dev_err_probe(dev, ret, "failed to register fixed factors\n");
> +
> + ret = cv75_register_composites(dev, data);
> + if (ret)
> + return dev_err_probe(dev, ret, "failed to register composites\n");
> +
> + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, data);
> + if (ret)
> + return dev_err_probe(dev, ret, "failed to add clock provider\n");
> +
> + return 0;
> +}
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH v7 08/15] gpiolib: regmap: add write_data_after_dir quirk
2026-09-15 11:15 ` [PATCH v7 08/15] gpiolib: regmap: add write_data_after_dir quirk Long Zhao via B4 Relay
@ 2026-09-15 15:07 ` Andy Shevchenko
2026-09-16 4:47 ` zl020895
0 siblings, 1 reply; 34+ messages in thread
From: Andy Shevchenko @ 2026-09-15 15:07 UTC (permalink / raw)
To: longzhao
Cc: Arnd Bergmann, Krzysztof Kozlowski, Alexandre Belloni, soc,
linux-arm-kernel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Michael Turquette, Stephen Boyd, Jerome Brunet, Linus Walleij,
Bartosz Golaszewski, Greg Kroah-Hartman, Jiri Slaby,
Ilpo Järvinen, Catalin Marinas, Will Deacon, Long Zhao,
Lee Jones, mfd, devicetree, linux-clk, linux-gpio, linux-serial,
linux-kernel
On Tue, Sep 15, 2026 at 07:15:38PM +0800, Long Zhao via B4 Relay wrote:
> Some controllers ignore data-register writes while a line is still an
> input. Optionally write the output value again after switching the
> direction, matching the existing PL061 behaviour.
...
> - gpio_regmap_set(chip, offset, value);
> + ret = gpio_regmap_set(chip, offset, value);
> + if (ret)
> + return ret;
I think that this might lead to regressions when the end driver just leaves
the output in the previous state in case of errors. In any case, even if this
is a correct update, it needs to be done separately with the justification
that shows that all current users won't have regressions or the regression is
actually a fix of the behaviour.
> + ret = gpio_regmap_set_direction(chip, offset, true);
> + if (ret)
> + return ret;
>
> - return gpio_regmap_set_direction(chip, offset, true);
> + /*
> + * gpio value is set again, because pl061 doesn't allow to set value of
> + * a gpio pin before configuring it in OUT mode.
> + */
> + if (gpio->write_data_after_dir)
> + return gpio_regmap_set(chip, offset, value);
Taking the above into account I'm not sure if we want the inconsistency with
the above. Perhaps also ignore the returned value for now?
> + return 0;
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH v7 09/15] gpiolib: regmap: add gpio_regmap_get_chip()
2026-09-15 11:15 ` [PATCH v7 09/15] gpiolib: regmap: add gpio_regmap_get_chip() Long Zhao via B4 Relay
@ 2026-09-15 15:30 ` Andy Shevchenko
2026-09-16 4:49 ` zl020895
0 siblings, 1 reply; 34+ messages in thread
From: Andy Shevchenko @ 2026-09-15 15:30 UTC (permalink / raw)
To: longzhao
Cc: Arnd Bergmann, Krzysztof Kozlowski, Alexandre Belloni, soc,
linux-arm-kernel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Michael Turquette, Stephen Boyd, Jerome Brunet, Linus Walleij,
Bartosz Golaszewski, Greg Kroah-Hartman, Jiri Slaby,
Ilpo Järvinen, Catalin Marinas, Will Deacon, Long Zhao,
Lee Jones, mfd, devicetree, linux-clk, linux-gpio, linux-serial,
linux-kernel
On Tue, Sep 15, 2026 at 07:15:39PM +0800, Long Zhao via B4 Relay wrote:
> Let drivers that implement a custom irqchip retrieve the gpio_chip
> after registration. Attach a caller-provided irq_domain only when it
> is present.
...
> struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config)
> {
> - struct irq_domain *irq_domain;
> struct gpio_regmap *gpio;
> struct gpio_chip *chip;
> int ret;
> #ifdef CONFIG_REGMAP_IRQ
> if (config->regmap_irq_chip) {
> + struct irq_domain *irq_domain;
> +
> gpio->regmap_irq_line = config->regmap_irq_line;
> ret = regmap_add_irq_chip_fwnode(dev_fwnode(config->parent), config->regmap,
> config->regmap_irq_line, config->regmap_irq_flags,
> @@ -420,12 +427,15 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config
> goto err_remove_gpiochip;
>
> irq_domain = regmap_irq_get_domain(gpio->irq_chip_data);
> + if (irq_domain) {
> + ret = gpiochip_irqchip_add_domain(chip, irq_domain);
> + if (ret)
> + goto err_remove_gpiochip;
> + }
> } else
> #endif
> - irq_domain = config->irq_domain;
> -
> - if (irq_domain) {
> - ret = gpiochip_irqchip_add_domain(chip, irq_domain);
> + if (config->irq_domain) {
> + ret = gpiochip_irqchip_add_domain(chip, config->irq_domain);
> if (ret)
> goto err_remove_gpiochip;
> }
I do not understand how it's different from the original one. Can you elaborate?
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re:Re: [PATCH v7 08/15] gpiolib: regmap: add write_data_after_dir quirk
2026-09-15 15:07 ` Andy Shevchenko
@ 2026-09-16 4:47 ` zl020895
0 siblings, 0 replies; 34+ messages in thread
From: zl020895 @ 2026-09-16 4:47 UTC (permalink / raw)
To: Andy Shevchenko
Cc: longzhao, Arnd Bergmann, Krzysztof Kozlowski, Alexandre Belloni,
soc, linux-arm-kernel, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Michael Turquette, Stephen Boyd, Jerome Brunet,
Linus Walleij, Bartosz Golaszewski, Greg Kroah-Hartman,
Jiri Slaby, Ilpo Järvinen, Catalin Marinas, Will Deacon,
Lee Jones, mfd, devicetree, linux-clk, linux-gpio, linux-serial,
linux-kernel
Hi Andy,
OK. v8 will ignore both gpio_regmap_set() returns, same as before.
Only the extra write for the PL061 quirk stays.
Best regards,
Long Zhao
At 2026-09-15 23:07:19, "Andy Shevchenko" <andriy.shevchenko@linux.intel.com> wrote:
>On Tue, Sep 15, 2026 at 07:15:38PM +0800, Long Zhao via B4 Relay wrote:
>
>> Some controllers ignore data-register writes while a line is still an
>> input. Optionally write the output value again after switching the
>> direction, matching the existing PL061 behaviour.
>
>...
>
>> - gpio_regmap_set(chip, offset, value);
>> + ret = gpio_regmap_set(chip, offset, value);
>> + if (ret)
>> + return ret;
>
>I think that this might lead to regressions when the end driver just leaves
>the output in the previous state in case of errors. In any case, even if this
>is a correct update, it needs to be done separately with the justification
>that shows that all current users won't have regressions or the regression is
>actually a fix of the behaviour.
>
>> + ret = gpio_regmap_set_direction(chip, offset, true);
>> + if (ret)
>> + return ret;
>>
>> - return gpio_regmap_set_direction(chip, offset, true);
>> + /*
>> + * gpio value is set again, because pl061 doesn't allow to set value of
>> + * a gpio pin before configuring it in OUT mode.
>> + */
>> + if (gpio->write_data_after_dir)
>> + return gpio_regmap_set(chip, offset, value);
>
>Taking the above into account I'm not sure if we want the inconsistency with
>the above. Perhaps also ignore the returned value for now?
>
>> + return 0;
>
>--
>With Best Regards,
>Andy Shevchenko
>
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re:Re: [PATCH v7 09/15] gpiolib: regmap: add gpio_regmap_get_chip()
2026-09-15 15:30 ` Andy Shevchenko
@ 2026-09-16 4:49 ` zl020895
0 siblings, 0 replies; 34+ messages in thread
From: zl020895 @ 2026-09-16 4:49 UTC (permalink / raw)
To: Andy Shevchenko
Cc: longzhao, Arnd Bergmann, Krzysztof Kozlowski, Alexandre Belloni,
soc, linux-arm-kernel, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Michael Turquette, Stephen Boyd, Jerome Brunet,
Linus Walleij, Bartosz Golaszewski, Greg Kroah-Hartman,
Jiri Slaby, Ilpo Järvinen, Catalin Marinas, Will Deacon,
Lee Jones, mfd, devicetree, linux-clk, linux-gpio, linux-serial,
linux-kernel
Hi Andy,
Thanks for the review.
It isn't; that was leftover from dropping girq. v8 will restore the
original attach code. The real addition is gpio_regmap_get_chip(), so
PL061 can create its irq domain after register.
Best regards,
Long Zhao
At 2026-09-15 23:30:38, "Andy Shevchenko" <andriy.shevchenko@linux.intel.com> wrote:
>On Tue, Sep 15, 2026 at 07:15:39PM +0800, Long Zhao via B4 Relay wrote:
>
>> Let drivers that implement a custom irqchip retrieve the gpio_chip
>> after registration. Attach a caller-provided irq_domain only when it
>> is present.
>
>...
>
>> struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config)
>> {
>> - struct irq_domain *irq_domain;
>> struct gpio_regmap *gpio;
>> struct gpio_chip *chip;
>> int ret;
>
>> #ifdef CONFIG_REGMAP_IRQ
>> if (config->regmap_irq_chip) {
>> + struct irq_domain *irq_domain;
>> +
>> gpio->regmap_irq_line = config->regmap_irq_line;
>> ret = regmap_add_irq_chip_fwnode(dev_fwnode(config->parent), config->regmap,
>> config->regmap_irq_line, config->regmap_irq_flags,
>> @@ -420,12 +427,15 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config
>> goto err_remove_gpiochip;
>>
>> irq_domain = regmap_irq_get_domain(gpio->irq_chip_data);
>> + if (irq_domain) {
>> + ret = gpiochip_irqchip_add_domain(chip, irq_domain);
>> + if (ret)
>> + goto err_remove_gpiochip;
>> + }
>> } else
>> #endif
>> - irq_domain = config->irq_domain;
>> -
>> - if (irq_domain) {
>> - ret = gpiochip_irqchip_add_domain(chip, irq_domain);
>> + if (config->irq_domain) {
>> + ret = gpiochip_irqchip_add_domain(chip, config->irq_domain);
>> if (ret)
>> goto err_remove_gpiochip;
>> }
>
>I do not understand how it's different from the original one. Can you elaborate?
>
>--
>With Best Regards,
>Andy Shevchenko
>
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH v7 00/15] Ambarella CV75 SoC minimal bring-up
2026-09-15 11:15 [PATCH v7 00/15] Ambarella CV75 SoC minimal bring-up Long Zhao via B4 Relay
` (14 preceding siblings ...)
2026-09-15 11:15 ` [PATCH v7 15/15] MAINTAINERS: add ARM/AMBARELLA SoC support Long Zhao via B4 Relay
@ 2026-09-16 9:14 ` Bartosz Golaszewski
2026-09-16 9:42 ` Linus Walleij
15 siblings, 1 reply; 34+ messages in thread
From: Bartosz Golaszewski @ 2026-09-16 9:14 UTC (permalink / raw)
To: longzhao
Cc: Long Zhao via B4 Relay, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Michael Turquette, Stephen Boyd, Jerome Brunet,
Linus Walleij, Bartosz Golaszewski, Greg Kroah-Hartman,
Jiri Slaby, Andy Shevchenko, Ilpo Järvinen, Catalin Marinas,
Will Deacon, Long Zhao, Lee Jones, mfd, devicetree, linux-clk,
linux-gpio, linux-serial, linux-kernel, Krzysztof Kozlowski,
Arnd Bergmann, Krzysztof Kozlowski, Alexandre Belloni, soc,
linux-arm-kernel
On Tue, 15 Sep 2026 13:15:30 +0200, Long Zhao via B4 Relay
<devnull+longzhao.ambarella.com@kernel.org> said:
> This series adds minimal Ambarella CV75 support for early bring-up with
> a serial console: DT bindings, RCT clocks, pinctrl, PL061 GPIO via
> gpio-regmap, 8250_dw UART quirks, ARCH_AMBARELLA, CV75 EVK DT, and
> MAINTAINERS.
>
> This is a single unified series. Please apply via the SoC
> tree; subsystem maintainers are Cc'd for their pieces.
>
I would prefer to take the GPIO regmap bits through the GPIO tree and provide
an immutable branch to the SoC tree as it has potential for conflicts that
early into the cycle.
Bart
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH v7 00/15] Ambarella CV75 SoC minimal bring-up
2026-09-16 9:14 ` [PATCH v7 00/15] Ambarella CV75 SoC minimal bring-up Bartosz Golaszewski
@ 2026-09-16 9:42 ` Linus Walleij
2026-09-16 10:23 ` Andy Shevchenko
0 siblings, 1 reply; 34+ messages in thread
From: Linus Walleij @ 2026-09-16 9:42 UTC (permalink / raw)
To: Bartosz Golaszewski, soc
Cc: longzhao, Long Zhao via B4 Relay, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Michael Turquette,
Stephen Boyd, Jerome Brunet, Greg Kroah-Hartman, Jiri Slaby,
Andy Shevchenko, Ilpo Järvinen, Catalin Marinas,
Will Deacon, Long Zhao, Lee Jones, mfd, devicetree, linux-clk,
linux-gpio, linux-serial, linux-kernel, Krzysztof Kozlowski,
Arnd Bergmann, Krzysztof Kozlowski, Alexandre Belloni,
linux-arm-kernel
On Wed, Sep 16, 2026 at 11:14 AM Bartosz Golaszewski <brgl@kernel.org> wrote:
> On Tue, 15 Sep 2026 13:15:30 +0200, Long Zhao via B4 Relay
> <devnull+longzhao.ambarella.com@kernel.org> said:
> > This series adds minimal Ambarella CV75 support for early bring-up with
> > a serial console: DT bindings, RCT clocks, pinctrl, PL061 GPIO via
> > gpio-regmap, 8250_dw UART quirks, ARCH_AMBARELLA, CV75 EVK DT, and
> > MAINTAINERS.
> >
> > This is a single unified series. Please apply via the SoC
> > tree; subsystem maintainers are Cc'd for their pieces.
> >
>
> I would prefer to take the GPIO regmap bits through the GPIO tree and provide
> an immutable branch to the SoC tree as it has potential for conflicts that
> early into the cycle.
Queue them up and send us the IB if you think they are ready!
Long can probably send a PR based on that IB for the rest to the
SoC tree, a bit tricksy but it works.
Yours,
Linus Walleij
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH v7 10/15] gpio: pl061: convert to gpio-regmap and a custom irqchip
2026-09-15 11:15 ` [PATCH v7 10/15] gpio: pl061: convert to gpio-regmap and a custom irqchip Long Zhao via B4 Relay
@ 2026-09-16 10:21 ` Andy Shevchenko
0 siblings, 0 replies; 34+ messages in thread
From: Andy Shevchenko @ 2026-09-16 10:21 UTC (permalink / raw)
To: longzhao
Cc: Arnd Bergmann, Krzysztof Kozlowski, Alexandre Belloni, soc,
linux-arm-kernel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Michael Turquette, Stephen Boyd, Jerome Brunet, Linus Walleij,
Bartosz Golaszewski, Greg Kroah-Hartman, Jiri Slaby,
Ilpo Järvinen, Catalin Marinas, Will Deacon, Long Zhao,
Lee Jones, mfd, devicetree, linux-clk, linux-gpio, linux-serial,
linux-kernel
On Tue, Sep 15, 2026 at 07:15:40PM +0800, Long Zhao via B4 Relay wrote:
>
> Move line get/set/direction onto gpio-regmap. Keep the existing ARM
> masked data addresses and write-after-direction behaviour.
>
> Do not use gpiochip irqchip setup (girq) or regmap-irq. gpio-regmap
> owns gpiochip registration, so girq would have to be plumbed through
> that helper. PL061 IRQ type programming needs IS/IBE/IEV, including
> both-edge, plus a hardirq chained demux from the parent AMBA IRQ;
> regmap-irq is a poor fit for that.
>
> Create a linear irq_domain with gpio_chip as host data, attach it with
> gpiochip_irqchip_add_domain(), and chain the parent IRQ in this driver.
Besides some comments below about splitting more of this patch to better
isolated logical changes, this one can also be split to a few stages. For
example, one stage is to move to regmap (from direct MMIO access) without
really switching over to gpio-regmap.
...
> +#include <linux/err.h>
> #include <linux/errno.h>
Now errno.h can be removed as basic errno is provided by err.h.
...
> -#include <linux/irqchip/chained_irq.h>
> +#include <linux/irqchip/chained_irq.h>
Unneeded churn.
...
> - if ((trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) &&
> - (trigger & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)))
> - {
> + if ((trigger & IRQ_TYPE_LEVEL_MASK) && (trigger & IRQ_TYPE_EDGE_BOTH)) {
See below, this belongs to a separate change.
...
> @@ -142,14 +121,19 @@ static int pl061_irq_type(struct irq_data *d, unsigned trigger)
> return -EINVAL;
> }
>
> -
> raw_spin_lock_irqsave(&pl061->lock, flags);
Stray change. If required, should be in a separate patch.
...
> - if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) {
> + if (trigger & IRQ_TYPE_LEVEL_MASK) {
This change should be in a separate patch.
...
> - writeb(gpiois, pl061->base + GPIOIS);
> - writeb(gpioibe, pl061->base + GPIOIBE);
> - writeb(gpioiev, pl061->base + GPIOIEV);
> + ret = regmap_write(pl061->regmap, regs->is, gpiois);
> + if (ret)
> + goto out;
> + ret = regmap_write(pl061->regmap, regs->ibe, gpioibe);
> + if (ret)
> + goto out;
> + ret = regmap_write(pl061->regmap, regs->iev, gpioiev);
> + if (ret)
> + goto out;
> + if (pl061->data->clear_irq_on_type)
> + ret = regmap_write(pl061->regmap, regs->ic, bit);
>
> +out:
> raw_spin_unlock_irqrestore(&pl061->lock, flags);
> -
> - return 0;
> + return ret;
Make an additional patch to move current driver to use cleanup.h, id est
guard()() and possibly scoped_guard() from there.
Then in this patch those ton's of goto:s should be replaced with simple
'return ret;'.`
> }
...
> + if (!regmap_read(pl061->regmap, pl061->data->regs->mis, &mis) && mis) {
Why not handling error properly here?
ret = regmap_read(...);
if (ret)
goto out_irq_exit;
> + pending = mis;
> + for_each_set_bit(offset, &pending, pl061->data->ngpio)
> + generic_handle_domain_irq(pl061->irq_domain, offset);
> }
...
> +static int pl061_irq_domain_map(struct irq_domain *d, unsigned int virq,
> + irq_hw_number_t hwirq)
> +{
> + struct gpio_chip *gc = d->host_data;
> + struct pl061 *pl061 = pl061_from_gpio_chip(gc);
> +
> + irq_set_chip_data(virq, gc);
> + irq_set_chip_and_handler(virq, &pl061_irqchip, handle_bad_irq);
> + irq_set_noprobe(virq);
> + irq_set_parent(virq, pl061->parent_irq);
Is lockdep happy with this?
> + return 0;
> +}
...
> +static void pl061_remove_irq(void *data)
> +{
> + struct pl061 *pl061 = data;
> +
> + irq_set_chained_handler_and_data(pl061->parent_irq, NULL, NULL);
> +
> + for (unsigned int i = 0; i < pl061->data->ngpio; i++) {
> + unsigned int virq = irq_find_mapping(pl061->irq_domain, i);
Split assignment
unsigned int virq;
virq = irq_find_mapping(pl061->irq_domain, i);
if (virq)
> + if (virq)
> + irq_dispose_mapping(virq);
> + }
> +
> + irq_domain_remove(pl061->irq_domain);
> +}
...
> struct device *dev = &adev->dev;
> + const struct pl061_drvdata *data = id->data;
Split assignment as it is validated later on.
> + const struct pl061_regs *regs;
> + struct gpio_regmap_config config = {};
> + struct gpio_regmap *gpio_regmap;
> + struct gpio_chip *gc;
> struct pl061 *pl061;
> - struct gpio_irq_chip *girq;
> + void __iomem *base;
> int ret, irq;
>
> + if (!data)
> + return -EINVAL;
-ENODATA?
> + regs = data->regs;
...
> + for (offset = 0; offset < pl061->data->ngpio; offset++) {
> + if (!(dir & BIT(offset)))
> + continue;
for_each_set_bit(), but make sure the type of the variable is unsigned long
(might require a temporary one).
> + ret = regmap_read_bypassed(pl061->regmap,
> + BIT(offset + PL061_DATA_OFFSET),
> + &val);
> + if (ret)
> + return ret;
> + pl061->saved_dat |= val;
> }
...
> + for (offset = 0; offset < pl061->data->ngpio; offset++) {
> + if (!(dir & BIT(offset)))
> + continue;
Ditto.
> + ret = regmap_write(pl061->regmap,
> + BIT(offset + PL061_DATA_OFFSET),
> + !!(pl061->saved_dat & BIT(offset)) << offset);
> + if (ret)
> + return ret;
> + }
> +
> + return regcache_sync_region(pl061->regmap, regs->is, regs->ie);
> }
...
> static const struct amba_id pl061_ids[] = {
> {
> .id = 0x00041061,
> .mask = 0x000fffff,
> + .data = (void *)&pl061_arm,
Oh, this is something that needs to be fixed (but it's not your issue).
Why on earth this is not const?
> },
> { 0, 0 },
> };
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH v7 00/15] Ambarella CV75 SoC minimal bring-up
2026-09-16 9:42 ` Linus Walleij
@ 2026-09-16 10:23 ` Andy Shevchenko
2026-09-16 11:01 ` zl020895
0 siblings, 1 reply; 34+ messages in thread
From: Andy Shevchenko @ 2026-09-16 10:23 UTC (permalink / raw)
To: Linus Walleij
Cc: Bartosz Golaszewski, soc, longzhao, Long Zhao via B4 Relay,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Michael Turquette, Stephen Boyd, Jerome Brunet,
Greg Kroah-Hartman, Jiri Slaby, Ilpo Järvinen,
Catalin Marinas, Will Deacon, Long Zhao, Lee Jones, mfd,
devicetree, linux-clk, linux-gpio, linux-serial, linux-kernel,
Krzysztof Kozlowski, Arnd Bergmann, Krzysztof Kozlowski,
Alexandre Belloni, linux-arm-kernel
On Wed, Sep 16, 2026 at 11:42:56AM +0200, Linus Walleij wrote:
> On Wed, Sep 16, 2026 at 11:14 AM Bartosz Golaszewski <brgl@kernel.org> wrote:
> > On Tue, 15 Sep 2026 13:15:30 +0200, Long Zhao via B4 Relay
> > <devnull+longzhao.ambarella.com@kernel.org> said:
> > > This series adds minimal Ambarella CV75 support for early bring-up with
> > > a serial console: DT bindings, RCT clocks, pinctrl, PL061 GPIO via
> > > gpio-regmap, 8250_dw UART quirks, ARCH_AMBARELLA, CV75 EVK DT, and
> > > MAINTAINERS.
> > >
> > > This is a single unified series. Please apply via the SoC
> > > tree; subsystem maintainers are Cc'd for their pieces.
> >
> > I would prefer to take the GPIO regmap bits through the GPIO tree and provide
> > an immutable branch to the SoC tree as it has potential for conflicts that
> > early into the cycle.
>
> Queue them up and send us the IB if you think they are ready!
>
> Long can probably send a PR based on that IB for the rest to the
> SoC tree, a bit tricksy but it works.
I'm still unsure why we can't use IRQ facility from gpio-regmap.
Can you have a look there?
With that being said, I'm not sure that the patch that exposing gpio_chip
from gpio-regmap is justified.
The whole GPIO rework needs a bit more routine work (resplitting, refactoring,
et cetera), so later we may see clearer what's going on.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH v7 12/15] pinctrl: ambarella: add CV75 pin controller
2026-09-15 11:15 ` [PATCH v7 12/15] pinctrl: ambarella: add CV75 pin controller Long Zhao via B4 Relay
@ 2026-09-16 10:49 ` Andy Shevchenko
2026-09-17 9:06 ` zl020895
0 siblings, 1 reply; 34+ messages in thread
From: Andy Shevchenko @ 2026-09-16 10:49 UTC (permalink / raw)
To: longzhao
Cc: Arnd Bergmann, Krzysztof Kozlowski, Alexandre Belloni, soc,
linux-arm-kernel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Michael Turquette, Stephen Boyd, Jerome Brunet, Linus Walleij,
Bartosz Golaszewski, Greg Kroah-Hartman, Jiri Slaby,
Ilpo Järvinen, Catalin Marinas, Will Deacon, Long Zhao,
Lee Jones, mfd, devicetree, linux-clk, linux-gpio, linux-serial,
linux-kernel
On Tue, Sep 15, 2026 at 07:15:42PM +0800, Long Zhao via B4 Relay wrote:
> Add an Ambarella pinmux-only pinctrl driver with CV75 function/group
> tables. GPIO is handled by the PL061 driver.
...
> +#define CV75_GROUP(_name) \
> + { \
> + .name = #_name, \
> + .mux = cv75_##_name##_pinmux, \
> + .nmux = ARRAY_SIZE(cv75_##_name##_pinmux), \
> + }
Can't we use PICTRL_PINGROUP()? Why not?
...
> +#define CV75_FUNCTION(_name) \
> + PINCTRL_PINFUNCTION(#_name, cv75_##_name##_groups, \
> + ARRAY_SIZE(cv75_##_name##_groups))
I would dare to make it a single line.
...
> +#include <linux/array_size.h>
> +#include <linux/bits.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/errno.h>
I don't see the need to use errno.h, err.h provides the basic ones.
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/types.h>
...
> +static void amb_pinmux_set_altfunc(struct amb_pinctrl *ipc, u32 bank,
> + u32 offset, u32 altfunc)
> +{
> + if (bank >= ipc->data->nr_banks)
> + return;
> +
> + for (unsigned int i = 0; i < 3; i++) {
> + u32 data;
> +
> + data = readl_relaxed(ipc->iomux_base + IOMUX_REG(bank, i));
> + data &= ~BIT(offset);
> + data |= ((altfunc >> i) & 1U) << offset;
data |= ((altfunc & BIT(i)) >> i) << offset;
Or even
unsigned long data;
...
__assign_bit(offset, &data, altfunc & BIT(i));
> + writel_relaxed(data, ipc->iomux_base + IOMUX_REG(bank, i));
> + }
> +}
...
> +static int amb_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
> + unsigned long *configs, unsigned int num_configs)
> +{
> + struct amb_pinctrl *ipc = pinctrl_dev_get_drvdata(pctldev);
> + u32 bank = PINID_TO_BANK(pin);
> + u32 offset = PINID_TO_OFFSET(pin);
> + int ret;
> +
> + if (bank >= ipc->data->nr_banks)
> + return -EINVAL;
> +
> + for (unsigned int i = 0; i < num_configs; i++) {
> + enum pin_config_param param = pinconf_to_config_param(configs[i]);
> + u32 arg = pinconf_to_config_argument(configs[i]);
> + int ds;
> +
> + switch (param) {
> + case PIN_CONFIG_BIAS_DISABLE:
> + ret = regmap_update_bits(ipc->pull_regmap,
> + ipc->data->pull_en[bank], BIT(offset), 0);
> + if (ret)
> + return ret;
> + break;
> + case PIN_CONFIG_BIAS_PULL_DOWN:
> + case PIN_CONFIG_BIAS_PULL_UP:
> + ret = regmap_update_bits(ipc->pull_regmap,
> + ipc->data->pull_dir[bank], BIT(offset),
> + (param == PIN_CONFIG_BIAS_PULL_UP) ?
> + BIT(offset) : 0);
_assign_bits()?
Ditto for the rest of the similar cases.
> + if (ret)
> + return ret;
> + ret = regmap_update_bits(ipc->pull_regmap,
> + ipc->data->pull_en[bank], BIT(offset),
> + BIT(offset));
> + if (ret)
> + return ret;
> + break;
> + case PIN_CONFIG_DRIVE_STRENGTH:
> + ds = amb_drive_strength_to_reg(ipc, arg);
> + if (ds < 0)
> + return ds;
> + if (ipc->data->have_ds2) {
> + ret = regmap_update_bits(ipc->ds_regmap,
> + ipc->data->ds0[bank], BIT(offset),
> + (ds & BIT(0)) ? BIT(offset) : 0);
> + if (ret)
> + return ret;
> + ret = regmap_update_bits(ipc->ds_regmap,
> + ipc->data->ds1[bank], BIT(offset),
> + (ds & BIT(1)) ? BIT(offset) : 0);
> + if (ret)
> + return ret;
> + ret = regmap_update_bits(ipc->ds_regmap,
> + ipc->data->ds2[bank], BIT(offset),
> + (ds & BIT(2)) ? BIT(offset) : 0);
> + if (ret)
> + return ret;
> + } else {
> + ret = regmap_update_bits(ipc->ds_regmap,
> + ipc->data->ds0[bank], BIT(offset),
> + (ds & BIT(1)) ? BIT(offset) : 0);
> + if (ret)
> + return ret;
> + ret = regmap_update_bits(ipc->ds_regmap,
> + ipc->data->ds1[bank], BIT(offset),
> + (ds & BIT(0)) ? BIT(offset) : 0);
> + if (ret)
> + return ret;
> + }
> + break;
> + default:
> + return -EOPNOTSUPP;
Is it indeed what we use in pin control? I think the correct one here is
ENOTSUPP (and in that case errno.h is required, yes). Yeah, some drivers
has a mixture and they probably didn't get how this error code is used.
> + }
> + }
> +
> + return 0;
> +}
...
> +static int amb_pinconf_get(struct pinctrl_dev *pctldev,
> + unsigned int pin, unsigned long *config)
> +{
> + struct amb_pinctrl *ipc = pinctrl_dev_get_drvdata(pctldev);
> + enum pin_config_param param = pinconf_to_config_param(*config);
> + u32 bank = PINID_TO_BANK(pin);
> + u32 offset = PINID_TO_OFFSET(pin);
> + u32 pull_en, pull_dir, ds0, ds1, ds2, ds;
> + int ret, strength;
> +
> + if (bank >= ipc->data->nr_banks)
> + return -EINVAL;
> +
> + switch (param) {
> + case PIN_CONFIG_BIAS_DISABLE:
> + case PIN_CONFIG_BIAS_PULL_DOWN:
> + case PIN_CONFIG_BIAS_PULL_UP:
> + ret = regmap_read(ipc->pull_regmap, ipc->data->pull_en[bank],
> + &pull_en);
> + if (ret)
> + return ret;
> +
> + ret = regmap_read(ipc->pull_regmap, ipc->data->pull_dir[bank],
> + &pull_dir);
> + if (ret)
> + return ret;
> + pull_en = (pull_en >> offset) & 1;
> + pull_dir = (pull_dir >> offset) & 1;
Seems to me they can be boolean?
In any case, use ' & BIT(offset)' instead of the above.
> + if (param == PIN_CONFIG_BIAS_DISABLE) {
> + if (pull_en)
> + return -EINVAL;
> + *config = pinconf_to_config_packed(param, 0);
> + return 0;
> + }
> +
> + if (!pull_en)
> + return -EINVAL;
> + if (param == PIN_CONFIG_BIAS_PULL_UP && !pull_dir)
> + return -EINVAL;
> + if (param == PIN_CONFIG_BIAS_PULL_DOWN && pull_dir)
> + return -EINVAL;
> +
> + *config = pinconf_to_config_packed(param, 1);
> + return 0;
> +
> + case PIN_CONFIG_DRIVE_STRENGTH:
> + ret = regmap_read(ipc->ds_regmap, ipc->data->ds0[bank], &ds0);
> + if (ret)
> + return ret;
> +
> + ret = regmap_read(ipc->ds_regmap, ipc->data->ds1[bank], &ds1);
> + if (ret)
> + return ret;
> +
> + ds0 = (ds0 >> offset) & 1;
> + ds1 = (ds1 >> offset) & 1;
> + if (ipc->data->have_ds2) {
> + ret = regmap_read(ipc->ds_regmap, ipc->data->ds2[bank],
> + &ds2);
> + if (ret)
> + return ret;
> +
> + ds2 = (ds2 >> offset) & 1;
> + ds = (ds2 << 2) | (ds1 << 1) | ds0;
> + } else {
> + ds = (ds0 << 1) | ds1;
> + }
Same here, use BIT(offset). For example,
ds2 = !!(ds2 & BIT(offset));
> + strength = amb_reg_to_drive_strength(ipc, ds);
> + if (strength < 0)
> + return strength;
> +
> + *config = pinconf_to_config_packed(param, strength);
> + return 0;
> +
> + default:
> + return -EOPNOTSUPP;
Same Q about the error code.
> + }
> +}
...
> + for (unsigned int pin = 0; pin < ipc->data->npins; pin++) {
> + pindesc[pin].number = pin;
> + pindesc[pin].name = devm_kasprintf(ipc->dev, GFP_KERNEL,
> + "io%u", pin);
> + if (!pindesc[pin].name)
> + return -ENOMEM;
> + }
Use devm_kasprintf_strarray().
...
> +static int amb_pinctrl_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct amb_pinctrl *ipc;
> + int ret;
> +
> + ipc = devm_kzalloc(dev, sizeof(*ipc), GFP_KERNEL);
> + if (!ipc)
> + return -ENOMEM;
> +
> + ipc->dev = dev;
> + ipc->data = device_get_match_data(dev);
> + if (!ipc->data)
> + return dev_err_probe(dev, -EINVAL, "missing SoC data\n");
-ENODATA
> + if (!ipc->data->nr_banks || ipc->data->nr_banks > AMBA_MAX_BANKS ||
> + !ipc->data->npins ||
> + !ipc->data->groups || !ipc->data->ngroups ||
> + !ipc->data->functions || !ipc->data->nfunctions)
> + return dev_err_probe(dev, -EINVAL, "invalid SoC data\n");
> +
> + ipc->iomux_base = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(ipc->iomux_base))
> + return PTR_ERR(ipc->iomux_base);
> +
> + ipc->ds_regmap = syscon_regmap_lookup_by_phandle(dev_of_node(dev),
> + "ambarella,drive-strength-syscon");
> + if (IS_ERR(ipc->ds_regmap))
> + return dev_err_probe(dev, PTR_ERR(ipc->ds_regmap),
> + "missing drive-strength syscon\n");
> +
> + ipc->pull_regmap = syscon_regmap_lookup_by_phandle(dev_of_node(dev),
> + "ambarella,pull-syscon");
> + if (IS_ERR(ipc->pull_regmap))
> + return dev_err_probe(dev, PTR_ERR(ipc->pull_regmap),
> + "missing pull syscon\n");
> +
> + spin_lock_init(&ipc->lock);
> +
> + ret = amb_pinctrl_register(ipc);
> + if (ret)
> + return dev_err_probe(dev, ret, "failed to register pinctrl\n");
> +
> + platform_set_drvdata(pdev, ipc);
> +
> + return 0;
> +}
...
> +#include <linux/types.h>
> +#include <linux/pinctrl/pinctrl.h>
Not really used. Can be replaced with forward declarations.
> +#define AMBA_MAX_BANKS 8
> +
> +#define AMBA_PINMUX(pin, alt) (((alt) << 12) | (pin))
> +#define AMBA_PINMUX_TO_PIN(mux) ((mux) & 0xfff)
> +#define AMBA_PINMUX_TO_ALT(mux) (((mux) >> 12) & 0x7)
> +
> +struct amb_pinmux_group {
> + const char *name;
> + const u32 *mux;
> + unsigned int nmux;
> +};
> +
> +struct amb_pinctrl_data {
> + const struct amb_pinmux_group *groups;
> + const struct pinfunction *functions;
> + unsigned int ngroups;
> + unsigned int nfunctions;
> + unsigned int nr_banks;
> + unsigned int npins;
> + unsigned int ds0[AMBA_MAX_BANKS];
> + unsigned int ds1[AMBA_MAX_BANKS];
> + unsigned int ds2[AMBA_MAX_BANKS];
> + unsigned int pull_en[AMBA_MAX_BANKS];
> + unsigned int pull_dir[AMBA_MAX_BANKS];
> + bool have_ds2;
> +};
> +
> +extern const struct amb_pinctrl_data ambarella_cv75_pinctrl_data;
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH v7 11/15] gpio: pl061: add Ambarella register-layout variant
2026-09-15 11:15 ` [PATCH v7 11/15] gpio: pl061: add Ambarella register-layout variant Long Zhao via B4 Relay
@ 2026-09-16 10:51 ` Andy Shevchenko
0 siblings, 0 replies; 34+ messages in thread
From: Andy Shevchenko @ 2026-09-16 10:51 UTC (permalink / raw)
To: longzhao
Cc: Arnd Bergmann, Krzysztof Kozlowski, Alexandre Belloni, soc,
linux-arm-kernel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Michael Turquette, Stephen Boyd, Jerome Brunet, Linus Walleij,
Bartosz Golaszewski, Greg Kroah-Hartman, Jiri Slaby,
Ilpo Järvinen, Catalin Marinas, Will Deacon, Long Zhao,
Lee Jones, mfd, devicetree, linux-clk, linux-gpio, linux-serial,
linux-kernel
On Tue, Sep 15, 2026 at 07:15:41PM +0800, Long Zhao via B4 Relay wrote:
> Add a 32-bit, 32-line layout with DAT/DIR/IS/IBE/IEV/IE/MIS/IC plus
> MASK and ENABLE. Probe unmasks the bank and enables the controller.
> IRQ type changes also clear the pending bit. This derivative does not
> need write-after-direction or system suspend save/restore.
>
> Match the part with designer AMBA_VENDOR_AMBARELLA (JEP106 identity
> 0x68, bit 7 set) and part number 0x061.
Yep, this one looks nice now!
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re:Re: [PATCH v7 00/15] Ambarella CV75 SoC minimal bring-up
2026-09-16 10:23 ` Andy Shevchenko
@ 2026-09-16 11:01 ` zl020895
2026-09-16 15:20 ` Andy Shevchenko
0 siblings, 1 reply; 34+ messages in thread
From: zl020895 @ 2026-09-16 11:01 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Linus Walleij, Bartosz Golaszewski, soc, longzhao,
Long Zhao via B4 Relay, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Michael Turquette, Stephen Boyd, Jerome Brunet,
Greg Kroah-Hartman, Jiri Slaby, Ilpo Järvinen,
Catalin Marinas, Will Deacon, Lee Jones, mfd, devicetree,
linux-clk, linux-gpio, linux-serial, linux-kernel,
Krzysztof Kozlowski, Arnd Bergmann, Krzysztof Kozlowski,
Alexandre Belloni, linux-arm-kernel
Thanks Bart, Linus, Andy.
ACK: GPIO bits through the GPIO tree, then an IB for the SoC tree.
I will keep a unified series for now.
regmap-irq cannot do PL061 type (IS/IBE/IEV, both-edge) plus the
AMBA chained demux.
Two options for v8 — Andy, which do you prefer?
1) Convert PL061 MMIO to regmap only. Keep the existing gpio_chip
and irqchip/girq (double-write stays in the driver). Ambarella
layout can be a follow-up on that. No gpio-regmap yet.
2) After (1), also move get/set/direction to gpio-regmap. IRQ
would stay a custom PL061 irqchip (not regmap-irq). I am not
sure we can avoid a way to get the gpio_chip for
gpiochip_*_irq() / the chained handler.
I am leaning to (1) for v8.
Best regards,
Long Zhao
在 2026-09-16 18:23:45,"Andy Shevchenko" <andriy.shevchenko@linux.intel.com> 写道:
>On Wed, Sep 16, 2026 at 11:42:56AM +0200, Linus Walleij wrote:
>> On Wed, Sep 16, 2026 at 11:14 AM Bartosz Golaszewski <brgl@kernel.org> wrote:
>> > On Tue, 15 Sep 2026 13:15:30 +0200, Long Zhao via B4 Relay
>> > <devnull+longzhao.ambarella.com@kernel.org> said:
>> > > This series adds minimal Ambarella CV75 support for early bring-up with
>> > > a serial console: DT bindings, RCT clocks, pinctrl, PL061 GPIO via
>> > > gpio-regmap, 8250_dw UART quirks, ARCH_AMBARELLA, CV75 EVK DT, and
>> > > MAINTAINERS.
>> > >
>> > > This is a single unified series. Please apply via the SoC
>> > > tree; subsystem maintainers are Cc'd for their pieces.
>> >
>> > I would prefer to take the GPIO regmap bits through the GPIO tree and provide
>> > an immutable branch to the SoC tree as it has potential for conflicts that
>> > early into the cycle.
>>
>> Queue them up and send us the IB if you think they are ready!
>>
>> Long can probably send a PR based on that IB for the rest to the
>> SoC tree, a bit tricksy but it works.
>
>I'm still unsure why we can't use IRQ facility from gpio-regmap.
>Can you have a look there?
>
>With that being said, I'm not sure that the patch that exposing gpio_chip
>from gpio-regmap is justified.
>
>The whole GPIO rework needs a bit more routine work (resplitting, refactoring,
>et cetera), so later we may see clearer what's going on.
>
>--
>With Best Regards,
>Andy Shevchenko
>
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: Re: [PATCH v7 00/15] Ambarella CV75 SoC minimal bring-up
2026-09-16 11:01 ` zl020895
@ 2026-09-16 15:20 ` Andy Shevchenko
2026-09-17 9:05 ` zl020895
0 siblings, 1 reply; 34+ messages in thread
From: Andy Shevchenko @ 2026-09-16 15:20 UTC (permalink / raw)
To: zl020895
Cc: Linus Walleij, Bartosz Golaszewski, soc, longzhao,
Long Zhao via B4 Relay, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Michael Turquette, Stephen Boyd, Jerome Brunet,
Greg Kroah-Hartman, Jiri Slaby, Ilpo Järvinen,
Catalin Marinas, Will Deacon, Lee Jones, mfd, devicetree,
linux-clk, linux-gpio, linux-serial, linux-kernel,
Krzysztof Kozlowski, Arnd Bergmann, Krzysztof Kozlowski,
Alexandre Belloni, linux-arm-kernel
On Wed, Sep 16, 2026 at 07:01:10PM +0800, zl020895 wrote:
> ACK: GPIO bits through the GPIO tree, then an IB for the SoC tree.
> I will keep a unified series for now.
>
> regmap-irq cannot do PL061 type (IS/IBE/IEV, both-edge) plus the
> AMBA chained demux.
>
> Two options for v8 — Andy, which do you prefer?
> 1) Convert PL061 MMIO to regmap only. Keep the existing gpio_chip
> and irqchip/girq (double-write stays in the driver). Ambarella
> layout can be a follow-up on that. No gpio-regmap yet.
>
> 2) After (1), also move get/set/direction to gpio-regmap. IRQ
> would stay a custom PL061 irqchip (not regmap-irq). I am not
> sure we can avoid a way to get the gpio_chip for
> gpiochip_*_irq() / the chained handler.
>
> I am leaning to (1) for v8.
I would prefer to see (2) and then we can discuss to roll-back if it looks not
good enough.
Either way your PM runtime support for gpio-regmap is a good change.
> 在 2026-09-16 18:23:45,"Andy Shevchenko" <andriy.shevchenko@linux.intel.com> 写道:
> >On Wed, Sep 16, 2026 at 11:42:56AM +0200, Linus Walleij wrote:
> >> On Wed, Sep 16, 2026 at 11:14 AM Bartosz Golaszewski <brgl@kernel.org> wrote:
> >> > On Tue, 15 Sep 2026 13:15:30 +0200, Long Zhao via B4 Relay
> >> > <devnull+longzhao.ambarella.com@kernel.org> said:
> >> > > This series adds minimal Ambarella CV75 support for early bring-up with
> >> > > a serial console: DT bindings, RCT clocks, pinctrl, PL061 GPIO via
> >> > > gpio-regmap, 8250_dw UART quirks, ARCH_AMBARELLA, CV75 EVK DT, and
> >> > > MAINTAINERS.
> >> > >
> >> > > This is a single unified series. Please apply via the SoC
> >> > > tree; subsystem maintainers are Cc'd for their pieces.
> >> >
> >> > I would prefer to take the GPIO regmap bits through the GPIO tree and provide
> >> > an immutable branch to the SoC tree as it has potential for conflicts that
> >> > early into the cycle.
> >>
> >> Queue them up and send us the IB if you think they are ready!
> >>
> >> Long can probably send a PR based on that IB for the rest to the
> >> SoC tree, a bit tricksy but it works.
> >
> >I'm still unsure why we can't use IRQ facility from gpio-regmap.
> >Can you have a look there?
> >
> >With that being said, I'm not sure that the patch that exposing gpio_chip
> >from gpio-regmap is justified.
> >
> >The whole GPIO rework needs a bit more routine work (resplitting, refactoring,
> >et cetera), so later we may see clearer what's going on.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re:Re: Re: [PATCH v7 00/15] Ambarella CV75 SoC minimal bring-up
2026-09-16 15:20 ` Andy Shevchenko
@ 2026-09-17 9:05 ` zl020895
0 siblings, 0 replies; 34+ messages in thread
From: zl020895 @ 2026-09-17 9:05 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Linus Walleij, Bartosz Golaszewski, soc, longzhao,
Long Zhao via B4 Relay, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Michael Turquette, Stephen Boyd, Jerome Brunet,
Greg Kroah-Hartman, Jiri Slaby, Ilpo Järvinen,
Catalin Marinas, Will Deacon, Lee Jones, mfd, devicetree,
linux-clk, linux-gpio, linux-serial, linux-kernel,
Krzysztof Kozlowski, Arnd Bergmann, Krzysztof Kozlowski,
Alexandre Belloni, linux-arm-kernel
Hi Andy,
Thanks. I'll go with option 2.
v8 will split: MMIO to regmap with the existing irqchip first,
then IRQ_TYPE_* masks, then gpio-regmap for get/set/dir and the
custom irqchip.
regmap-irq still cannot do IS/IBE/IEV plus the AMBA chained
demux.
gpio_regmap_get_chip() stays for now: the linear domain and
gpiochip_enable_irq() need gpio_chip, which only exists after
gpio_regmap_register(). I can drop it if we get a better hook.
Best regards,
Long Zhao
At 2026-09-16 23:20:40, "Andy Shevchenko" <andriy.shevchenko@linux.intel.com> wrote:
>On Wed, Sep 16, 2026 at 07:01:10PM +0800, zl020895 wrote:
>
>> ACK: GPIO bits through the GPIO tree, then an IB for the SoC tree.
>> I will keep a unified series for now.
>>
>> regmap-irq cannot do PL061 type (IS/IBE/IEV, both-edge) plus the
>> AMBA chained demux.
>>
>> Two options for v8 — Andy, which do you prefer?
>> 1) Convert PL061 MMIO to regmap only. Keep the existing gpio_chip
>> and irqchip/girq (double-write stays in the driver). Ambarella
>> layout can be a follow-up on that. No gpio-regmap yet.
>>
>> 2) After (1), also move get/set/direction to gpio-regmap. IRQ
>> would stay a custom PL061 irqchip (not regmap-irq). I am not
>> sure we can avoid a way to get the gpio_chip for
>> gpiochip_*_irq() / the chained handler.
>>
>> I am leaning to (1) for v8.
>
>I would prefer to see (2) and then we can discuss to roll-back if it looks not
>good enough.
>
>Either way your PM runtime support for gpio-regmap is a good change.
>
>> 在 2026-09-16 18:23:45,"Andy Shevchenko" <andriy.shevchenko@linux.intel.com> 写道:
>> >On Wed, Sep 16, 2026 at 11:42:56AM +0200, Linus Walleij wrote:
>> >> On Wed, Sep 16, 2026 at 11:14 AM Bartosz Golaszewski <brgl@kernel.org> wrote:
>> >> > On Tue, 15 Sep 2026 13:15:30 +0200, Long Zhao via B4 Relay
>> >> > <devnull+longzhao.ambarella.com@kernel.org> said:
>> >> > > This series adds minimal Ambarella CV75 support for early bring-up with
>> >> > > a serial console: DT bindings, RCT clocks, pinctrl, PL061 GPIO via
>> >> > > gpio-regmap, 8250_dw UART quirks, ARCH_AMBARELLA, CV75 EVK DT, and
>> >> > > MAINTAINERS.
>> >> > >
>> >> > > This is a single unified series. Please apply via the SoC
>> >> > > tree; subsystem maintainers are Cc'd for their pieces.
>> >> >
>> >> > I would prefer to take the GPIO regmap bits through the GPIO tree and provide
>> >> > an immutable branch to the SoC tree as it has potential for conflicts that
>> >> > early into the cycle.
>> >>
>> >> Queue them up and send us the IB if you think they are ready!
>> >>
>> >> Long can probably send a PR based on that IB for the rest to the
>> >> SoC tree, a bit tricksy but it works.
>> >
>> >I'm still unsure why we can't use IRQ facility from gpio-regmap.
>> >Can you have a look there?
>> >
>> >With that being said, I'm not sure that the patch that exposing gpio_chip
>> >from gpio-regmap is justified.
>> >
>> >The whole GPIO rework needs a bit more routine work (resplitting, refactoring,
>> >et cetera), so later we may see clearer what's going on.
>
>--
>With Best Regards,
>Andy Shevchenko
>
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re:Re: [PATCH v7 12/15] pinctrl: ambarella: add CV75 pin controller
2026-09-16 10:49 ` Andy Shevchenko
@ 2026-09-17 9:06 ` zl020895
0 siblings, 0 replies; 34+ messages in thread
From: zl020895 @ 2026-09-17 9:06 UTC (permalink / raw)
To: Andy Shevchenko
Cc: longzhao, Arnd Bergmann, Krzysztof Kozlowski, Alexandre Belloni,
soc, linux-arm-kernel, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Michael Turquette, Stephen Boyd, Jerome Brunet,
Linus Walleij, Bartosz Golaszewski, Greg Kroah-Hartman,
Jiri Slaby, Ilpo Järvinen, Catalin Marinas, Will Deacon,
Lee Jones, mfd, devicetree, linux-clk, linux-gpio, linux-serial,
linux-kernel
Hi Andy,
Thanks for the review. The small comments will be in v8,
including pinconf set via regmap_assign_bits.
PINCTRL_PINGROUP does not fit here: groups carry
AMBA_PINMUX(pin, alt), not a pin-number list, so I will keep
CV75_GROUP.
Best regards,
Long Zhao
At 2026-09-16 18:49:58, "Andy Shevchenko" <andriy.shevchenko@linux.intel.com> wrote:
>On Tue, Sep 15, 2026 at 07:15:42PM +0800, Long Zhao via B4 Relay wrote:
>
>> Add an Ambarella pinmux-only pinctrl driver with CV75 function/group
>> tables. GPIO is handled by the PL061 driver.
>
>...
>
>> +#define CV75_GROUP(_name) \
>> + { \
>> + .name = #_name, \
>> + .mux = cv75_##_name##_pinmux, \
>> + .nmux = ARRAY_SIZE(cv75_##_name##_pinmux), \
>> + }
>
>Can't we use PICTRL_PINGROUP()? Why not?
>
>...
>
>> +#define CV75_FUNCTION(_name) \
>
>> + PINCTRL_PINFUNCTION(#_name, cv75_##_name##_groups, \
>> + ARRAY_SIZE(cv75_##_name##_groups))
>
>I would dare to make it a single line.
>
>...
>
>> +#include <linux/array_size.h>
>> +#include <linux/bits.h>
>> +#include <linux/device.h>
>> +#include <linux/err.h>
>> +#include <linux/errno.h>
>
>I don't see the need to use errno.h, err.h provides the basic ones.
>
>> +#include <linux/init.h>
>> +#include <linux/io.h>
>> +#include <linux/mfd/syscon.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/regmap.h>
>> +#include <linux/slab.h>
>> +#include <linux/spinlock.h>
>> +#include <linux/types.h>
>
>...
>
>> +static void amb_pinmux_set_altfunc(struct amb_pinctrl *ipc, u32 bank,
>> + u32 offset, u32 altfunc)
>> +{
>> + if (bank >= ipc->data->nr_banks)
>> + return;
>> +
>> + for (unsigned int i = 0; i < 3; i++) {
>> + u32 data;
>> +
>> + data = readl_relaxed(ipc->iomux_base + IOMUX_REG(bank, i));
>> + data &= ~BIT(offset);
>> + data |= ((altfunc >> i) & 1U) << offset;
>
> data |= ((altfunc & BIT(i)) >> i) << offset;
>
>Or even
>
> unsigned long data;
> ...
> __assign_bit(offset, &data, altfunc & BIT(i));
>
>> + writel_relaxed(data, ipc->iomux_base + IOMUX_REG(bank, i));
>> + }
>> +}
>
>...
>
>> +static int amb_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
>> + unsigned long *configs, unsigned int num_configs)
>> +{
>> + struct amb_pinctrl *ipc = pinctrl_dev_get_drvdata(pctldev);
>> + u32 bank = PINID_TO_BANK(pin);
>> + u32 offset = PINID_TO_OFFSET(pin);
>> + int ret;
>> +
>> + if (bank >= ipc->data->nr_banks)
>> + return -EINVAL;
>> +
>> + for (unsigned int i = 0; i < num_configs; i++) {
>> + enum pin_config_param param = pinconf_to_config_param(configs[i]);
>> + u32 arg = pinconf_to_config_argument(configs[i]);
>> + int ds;
>> +
>> + switch (param) {
>> + case PIN_CONFIG_BIAS_DISABLE:
>> + ret = regmap_update_bits(ipc->pull_regmap,
>> + ipc->data->pull_en[bank], BIT(offset), 0);
>> + if (ret)
>> + return ret;
>> + break;
>> + case PIN_CONFIG_BIAS_PULL_DOWN:
>> + case PIN_CONFIG_BIAS_PULL_UP:
>> + ret = regmap_update_bits(ipc->pull_regmap,
>> + ipc->data->pull_dir[bank], BIT(offset),
>> + (param == PIN_CONFIG_BIAS_PULL_UP) ?
>> + BIT(offset) : 0);
>
>_assign_bits()?
>Ditto for the rest of the similar cases.
>
>> + if (ret)
>> + return ret;
>> + ret = regmap_update_bits(ipc->pull_regmap,
>> + ipc->data->pull_en[bank], BIT(offset),
>> + BIT(offset));
>> + if (ret)
>> + return ret;
>> + break;
>> + case PIN_CONFIG_DRIVE_STRENGTH:
>> + ds = amb_drive_strength_to_reg(ipc, arg);
>> + if (ds < 0)
>> + return ds;
>> + if (ipc->data->have_ds2) {
>> + ret = regmap_update_bits(ipc->ds_regmap,
>> + ipc->data->ds0[bank], BIT(offset),
>> + (ds & BIT(0)) ? BIT(offset) : 0);
>> + if (ret)
>> + return ret;
>> + ret = regmap_update_bits(ipc->ds_regmap,
>> + ipc->data->ds1[bank], BIT(offset),
>> + (ds & BIT(1)) ? BIT(offset) : 0);
>> + if (ret)
>> + return ret;
>> + ret = regmap_update_bits(ipc->ds_regmap,
>> + ipc->data->ds2[bank], BIT(offset),
>> + (ds & BIT(2)) ? BIT(offset) : 0);
>> + if (ret)
>> + return ret;
>> + } else {
>> + ret = regmap_update_bits(ipc->ds_regmap,
>> + ipc->data->ds0[bank], BIT(offset),
>> + (ds & BIT(1)) ? BIT(offset) : 0);
>> + if (ret)
>> + return ret;
>> + ret = regmap_update_bits(ipc->ds_regmap,
>> + ipc->data->ds1[bank], BIT(offset),
>> + (ds & BIT(0)) ? BIT(offset) : 0);
>> + if (ret)
>> + return ret;
>> + }
>> + break;
>> + default:
>> + return -EOPNOTSUPP;
>
>Is it indeed what we use in pin control? I think the correct one here is
>ENOTSUPP (and in that case errno.h is required, yes). Yeah, some drivers
>has a mixture and they probably didn't get how this error code is used.
>
>> + }
>> + }
>> +
>> + return 0;
>> +}
>
>...
>
>> +static int amb_pinconf_get(struct pinctrl_dev *pctldev,
>> + unsigned int pin, unsigned long *config)
>> +{
>> + struct amb_pinctrl *ipc = pinctrl_dev_get_drvdata(pctldev);
>> + enum pin_config_param param = pinconf_to_config_param(*config);
>> + u32 bank = PINID_TO_BANK(pin);
>> + u32 offset = PINID_TO_OFFSET(pin);
>> + u32 pull_en, pull_dir, ds0, ds1, ds2, ds;
>> + int ret, strength;
>> +
>> + if (bank >= ipc->data->nr_banks)
>> + return -EINVAL;
>> +
>> + switch (param) {
>> + case PIN_CONFIG_BIAS_DISABLE:
>> + case PIN_CONFIG_BIAS_PULL_DOWN:
>> + case PIN_CONFIG_BIAS_PULL_UP:
>> + ret = regmap_read(ipc->pull_regmap, ipc->data->pull_en[bank],
>> + &pull_en);
>> + if (ret)
>> + return ret;
>> +
>> + ret = regmap_read(ipc->pull_regmap, ipc->data->pull_dir[bank],
>> + &pull_dir);
>> + if (ret)
>> + return ret;
>
>> + pull_en = (pull_en >> offset) & 1;
>> + pull_dir = (pull_dir >> offset) & 1;
>
>Seems to me they can be boolean?
>In any case, use ' & BIT(offset)' instead of the above.
>
>> + if (param == PIN_CONFIG_BIAS_DISABLE) {
>> + if (pull_en)
>> + return -EINVAL;
>> + *config = pinconf_to_config_packed(param, 0);
>> + return 0;
>> + }
>> +
>> + if (!pull_en)
>> + return -EINVAL;
>> + if (param == PIN_CONFIG_BIAS_PULL_UP && !pull_dir)
>> + return -EINVAL;
>> + if (param == PIN_CONFIG_BIAS_PULL_DOWN && pull_dir)
>> + return -EINVAL;
>> +
>> + *config = pinconf_to_config_packed(param, 1);
>> + return 0;
>> +
>> + case PIN_CONFIG_DRIVE_STRENGTH:
>> + ret = regmap_read(ipc->ds_regmap, ipc->data->ds0[bank], &ds0);
>> + if (ret)
>> + return ret;
>> +
>> + ret = regmap_read(ipc->ds_regmap, ipc->data->ds1[bank], &ds1);
>> + if (ret)
>> + return ret;
>> +
>> + ds0 = (ds0 >> offset) & 1;
>> + ds1 = (ds1 >> offset) & 1;
>> + if (ipc->data->have_ds2) {
>> + ret = regmap_read(ipc->ds_regmap, ipc->data->ds2[bank],
>> + &ds2);
>> + if (ret)
>> + return ret;
>> +
>> + ds2 = (ds2 >> offset) & 1;
>> + ds = (ds2 << 2) | (ds1 << 1) | ds0;
>> + } else {
>> + ds = (ds0 << 1) | ds1;
>> + }
>
>Same here, use BIT(offset). For example,
>
> ds2 = !!(ds2 & BIT(offset));
>
>> + strength = amb_reg_to_drive_strength(ipc, ds);
>> + if (strength < 0)
>> + return strength;
>> +
>> + *config = pinconf_to_config_packed(param, strength);
>> + return 0;
>> +
>> + default:
>> + return -EOPNOTSUPP;
>
>Same Q about the error code.
>
>> + }
>> +}
>
>...
>
>> + for (unsigned int pin = 0; pin < ipc->data->npins; pin++) {
>> + pindesc[pin].number = pin;
>> + pindesc[pin].name = devm_kasprintf(ipc->dev, GFP_KERNEL,
>> + "io%u", pin);
>> + if (!pindesc[pin].name)
>> + return -ENOMEM;
>> + }
>
>Use devm_kasprintf_strarray().
>
>...
>
>> +static int amb_pinctrl_probe(struct platform_device *pdev)
>> +{
>> + struct device *dev = &pdev->dev;
>> + struct amb_pinctrl *ipc;
>> + int ret;
>> +
>> + ipc = devm_kzalloc(dev, sizeof(*ipc), GFP_KERNEL);
>> + if (!ipc)
>> + return -ENOMEM;
>> +
>> + ipc->dev = dev;
>> + ipc->data = device_get_match_data(dev);
>> + if (!ipc->data)
>> + return dev_err_probe(dev, -EINVAL, "missing SoC data\n");
>
>-ENODATA
>
>> + if (!ipc->data->nr_banks || ipc->data->nr_banks > AMBA_MAX_BANKS ||
>> + !ipc->data->npins ||
>> + !ipc->data->groups || !ipc->data->ngroups ||
>> + !ipc->data->functions || !ipc->data->nfunctions)
>> + return dev_err_probe(dev, -EINVAL, "invalid SoC data\n");
>> +
>> + ipc->iomux_base = devm_platform_ioremap_resource(pdev, 0);
>> + if (IS_ERR(ipc->iomux_base))
>> + return PTR_ERR(ipc->iomux_base);
>> +
>> + ipc->ds_regmap = syscon_regmap_lookup_by_phandle(dev_of_node(dev),
>> + "ambarella,drive-strength-syscon");
>> + if (IS_ERR(ipc->ds_regmap))
>> + return dev_err_probe(dev, PTR_ERR(ipc->ds_regmap),
>> + "missing drive-strength syscon\n");
>> +
>> + ipc->pull_regmap = syscon_regmap_lookup_by_phandle(dev_of_node(dev),
>> + "ambarella,pull-syscon");
>> + if (IS_ERR(ipc->pull_regmap))
>> + return dev_err_probe(dev, PTR_ERR(ipc->pull_regmap),
>> + "missing pull syscon\n");
>> +
>> + spin_lock_init(&ipc->lock);
>> +
>> + ret = amb_pinctrl_register(ipc);
>> + if (ret)
>> + return dev_err_probe(dev, ret, "failed to register pinctrl\n");
>> +
>> + platform_set_drvdata(pdev, ipc);
>> +
>> + return 0;
>> +}
>
>...
>
>> +#include <linux/types.h>
>
>> +#include <linux/pinctrl/pinctrl.h>
>
>Not really used. Can be replaced with forward declarations.
>
>> +#define AMBA_MAX_BANKS 8
>> +
>> +#define AMBA_PINMUX(pin, alt) (((alt) << 12) | (pin))
>> +#define AMBA_PINMUX_TO_PIN(mux) ((mux) & 0xfff)
>> +#define AMBA_PINMUX_TO_ALT(mux) (((mux) >> 12) & 0x7)
>> +
>> +struct amb_pinmux_group {
>> + const char *name;
>> + const u32 *mux;
>> + unsigned int nmux;
>> +};
>> +
>> +struct amb_pinctrl_data {
>> + const struct amb_pinmux_group *groups;
>> + const struct pinfunction *functions;
>> + unsigned int ngroups;
>> + unsigned int nfunctions;
>> + unsigned int nr_banks;
>> + unsigned int npins;
>> + unsigned int ds0[AMBA_MAX_BANKS];
>> + unsigned int ds1[AMBA_MAX_BANKS];
>> + unsigned int ds2[AMBA_MAX_BANKS];
>> + unsigned int pull_en[AMBA_MAX_BANKS];
>> + unsigned int pull_dir[AMBA_MAX_BANKS];
>> + bool have_ds2;
>> +};
>> +
>> +extern const struct amb_pinctrl_data ambarella_cv75_pinctrl_data;
>
>--
>With Best Regards,
>Andy Shevchenko
>
^ permalink raw reply [flat|nested] 34+ messages in thread
end of thread, other threads:[~2026-09-17 9:08 UTC | newest]
Thread overview: 34+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-15 11:15 [PATCH v7 00/15] Ambarella CV75 SoC minimal bring-up Long Zhao via B4 Relay
2026-09-15 11:15 ` [PATCH v7 01/15] dt-bindings: arm: add Ambarella CV75 platforms Long Zhao via B4 Relay
2026-09-15 11:15 ` [PATCH v7 02/15] dt-bindings: mfd: syscon: add Ambarella CV75 secure scratchpad Long Zhao via B4 Relay
2026-09-15 11:15 ` [PATCH v7 03/15] dt-bindings: clock: add Ambarella CV75 RCT Long Zhao via B4 Relay
2026-09-15 11:15 ` [PATCH v7 04/15] dt-bindings: pinctrl: add Ambarella CV75 pinctrl Long Zhao via B4 Relay
2026-09-15 11:15 ` [PATCH v7 05/15] dt-bindings: gpio: pl061: add Ambarella CV75 variant Long Zhao via B4 Relay
2026-09-15 11:15 ` [PATCH v7 06/15] dt-bindings: serial: snps-dw-apb-uart: add ambarella,cv75-uart Long Zhao via B4 Relay
2026-09-15 11:15 ` [PATCH v7 07/15] clk: ambarella: add CV75 RCT clock controller Long Zhao via B4 Relay
2026-09-15 13:24 ` Uwe Kleine-König
2026-09-15 13:27 ` Andy Shevchenko
2026-09-15 11:15 ` [PATCH v7 08/15] gpiolib: regmap: add write_data_after_dir quirk Long Zhao via B4 Relay
2026-09-15 15:07 ` Andy Shevchenko
2026-09-16 4:47 ` zl020895
2026-09-15 11:15 ` [PATCH v7 09/15] gpiolib: regmap: add gpio_regmap_get_chip() Long Zhao via B4 Relay
2026-09-15 15:30 ` Andy Shevchenko
2026-09-16 4:49 ` zl020895
2026-09-15 11:15 ` [PATCH v7 10/15] gpio: pl061: convert to gpio-regmap and a custom irqchip Long Zhao via B4 Relay
2026-09-16 10:21 ` Andy Shevchenko
2026-09-15 11:15 ` [PATCH v7 11/15] gpio: pl061: add Ambarella register-layout variant Long Zhao via B4 Relay
2026-09-16 10:51 ` Andy Shevchenko
2026-09-15 11:15 ` [PATCH v7 12/15] pinctrl: ambarella: add CV75 pin controller Long Zhao via B4 Relay
2026-09-16 10:49 ` Andy Shevchenko
2026-09-17 9:06 ` zl020895
2026-09-15 11:15 ` [PATCH v7 13/15] serial: 8250_dw: add Ambarella CV75 quirks Long Zhao via B4 Relay
2026-09-15 11:47 ` Ilpo Järvinen
2026-09-15 13:21 ` Andy Shevchenko
2026-09-15 11:15 ` [PATCH v7 14/15] arm64: ambarella: add ARCH_AMBARELLA and CV75 EVK DT Long Zhao via B4 Relay
2026-09-15 11:15 ` [PATCH v7 15/15] MAINTAINERS: add ARM/AMBARELLA SoC support Long Zhao via B4 Relay
2026-09-16 9:14 ` [PATCH v7 00/15] Ambarella CV75 SoC minimal bring-up Bartosz Golaszewski
2026-09-16 9:42 ` Linus Walleij
2026-09-16 10:23 ` Andy Shevchenko
2026-09-16 11:01 ` zl020895
2026-09-16 15:20 ` Andy Shevchenko
2026-09-17 9:05 ` zl020895
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®