Devicetree
 help / color / mirror / Atom feed
* Re: [RFC PATCH 1/3] dt-bindings: iio: temperature: Add STS30 devicetree bindings
From: sashiko-bot @ 2026-06-20  4:52 UTC (permalink / raw)
  To: Maxwell Doose; +Cc: devicetree, robh, conor+dt
In-Reply-To: <20260620044010.1082621-2-m32285159@gmail.com>

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The STS30 device tree binding omits the `interrupts` property, preventing users from modeling the hardware's ALERT pin.
--

commit 91523cd6a186b322c33e0fb78f1316cdc24a39af
Author: Maxwell Doose <m32285159@gmail.com>

dt-bindings: iio: temperature: Add STS30 devicetree bindings

This commit adds device tree bindings for the Sensirion STS30 family of 
digital temperature sensors, which includes the STS30, STS31, and STS35.

> diff --git a/Documentation/devicetree/bindings/iio/temperature/sensirion,sts30.yaml b/Documentation/devicetree/bindings/iio/temperature/sensirion,sts30.yaml
> new file mode 100644
> index 0000000000000..dc0c6b7832fb8
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/temperature/sensirion,sts30.yaml

[ ... ]

> +properties:
> +  compatible:
> +    enum:
> +      - sensirion,sts30
> +      - sensirion,sts31
> +      - sensirion,sts35
> +
> +  reg:
> +    maxItems: 1
> +
> +  vdd-supply: true
> +
> +required:
> +  - compatible
> +  - reg
> +
> +additionalProperties: false

[Severity: Medium]
Does this schema prevent modeling the hardware's ALERT pin?

The Sensirion STS3x family hardware features an ALERT pin that can be wired
to an interrupt controller to signal high and low temperature thresholds.
Because this schema enforces additionalProperties: false but omits the
interrupts property from the properties block, any device tree that
accurately models the physical hardware connection will fail
dt_binding_check.

Should the interrupts property be explicitly listed to allow accurately
describing the hardware capabilities, even if the current IIO driver does
not yet utilize the ALERT pin?

> +
> +examples:

[ ... ]

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260620044010.1082621-1-m32285159@gmail.com?part=1

^ permalink raw reply

* Re: [PATCH v4 3/3] arm64: dts: qcom: Add Vicharak Axon Mini
From: Ajit Singh @ 2026-06-20  4:57 UTC (permalink / raw)
  To: Konrad Dybcio
  Cc: Bjorn Andersson, Bartosz Golaszewski, Dmitry Baryshkov,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, linux-arm-msm,
	devicetree, linux-kernel
In-Reply-To: <c16a2837-4b04-466a-bf4a-3717436c25be@oss.qualcomm.com>

On Fri, Jun 19, 2026 at 05:30:40PM +0530, Konrad Dybcio wrote:
> On 6/18/26 8:29 AM, Ajit Singh wrote:
> > On Tue, Jun 16, 2026 at 02:10:44PM +0530, Konrad Dybcio wrote:
> >> On 6/12/26 6:16 AM, Ajit Singh wrote:
> >>> On Wed, Jun 10, 2026 at 02:58:19PM +0530, Konrad Dybcio wrote:
> >>>> On 6/7/26 1:36 PM, Ajit Singh wrote:
> >>>>> Add DTS for the Vicharak Axon Mini board based on the Qualcomm
> >>>>> QCS6490 SoC.
> >>>>>
> >>>>> This adds debug UART, eMMC, UFS, SDIO WLAN, USB 2.0 host, PCIe
> >>>>> support along with regulators.
> >>>>>
> >>>>> The UFS ICE block is kept disabled because enabling it currently causes
> >>>>> an SError during qcom_ice_create() on this board. UFS works without ICE.
> >>>>>
> >>>>> Signed-off-by: Ajit Singh <blfizzyy@gmail.com>
> >>>>> ---
> >>>>
> >>>> [...]
> >>>>
> >>>>> +		vreg_l12c_1p8: ldo12 {
> >>>>> +			regulator-name = "vreg_l12c_1p8";
> >>>>> +			regulator-min-microvolt = <1800000>;
> >>>>> +			regulator-max-microvolt = <2000000>;
> >>>>> +			regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
> >>>>> +
> >>>>> +			/*
> >>>>> +			 * VREG_L12C_1P8 supplies the Ampak WLAN/BT module
> >>>>> +			 * VDDIO and the external 32.768 kHz oscillator.
> >>>>> +			 */
> >>>>
> >>>> Sorry for the long review timelines on the previous patch, many of us
> >>>> were out for conferences..
> >>>>
> >>>> Is the oscillator used for that WLAN module? Would you ideally like to
> >>>> be able to turn it on/off?
> >>>
> >>> yes, oscillator is used for WLAN modules. Oscillator is powered from the same
> >>> VREG_L12C rail as WLAN VDDIO, so there is no separate regulator control to put
> >>> in pwrseq. So I think this will work fine?
> >>
> >> Probably? My point is that you marked it as always-on, so it will *never*
> >> turn off right now. For e.g. Qualcomm wifi, there's some timing spec that
> >> needs to be met wrt delays between toggling various regulators and GPIOs
> >> going to the module, hence I suggested you may need some pwrseq inbetween
> >> to achieve reliable powering on/off
> >>
> > Right, I checked the module timing requirements.
> > 
> > The module requires VBAT to be present before or at the same time as VDDIO, and
> > WL_REG_ON to be asserted only after VBAT/VDDIO are valid, around 2 sleep-clock
> > cycles later.
> > 
> > On this board, VBAT is the shared VCC_3V3 rail and is enabled by hardware, so
> > it is already present before VDDIO. VREG_L12C supplies WLAN/BT VDDIO and is
> > kept on. The WL_REG_ON timing is handled by the existing mmc-pwrseq-simple
> > reset GPIO/delay before SDIO enumeration.
> > 
> > So I think the current sequencing matches the module timing requirement.
> 
> Since the sequencing is handled, can we drop the always-on property,
> perhaps by describing the SDIO WLAN?
> 
> e.g. in arch/arm/boot/dts/st/ste-ux500-samsung-codina-tmo.dts there is:
> 
> /* WLAN SDIO channel */
> mmc@80118000 {
> 	arm,primecell-periphid = <0x10480180>;
> 	max-frequency = <50000000>;
> 	bus-width = <4>;
> 	non-removable;
> 	cap-sd-highspeed;
> 	pinctrl-names = "default", "sleep";
> 	pinctrl-0 = <&mc1_a_2_default>;
> 	pinctrl-1 = <&mc1_a_2_sleep>;
> 	/*
> 		* GPIO-controlled voltage enablement: this drives
> 		* the WL_REG_ON line high when we use this device.
> 		* Represented as regulator to fill OCR mask.
> 		*/
> 	vmmc-supply = <&wl_reg>;
> 
> 	#address-cells = <1>;
> 	#size-cells = <0>;
> 	status = "okay";
> 
> 	wifi@1 {
> 		compatible = "brcm,bcm4334-fmac", "brcm,bcm4329-fmac";
> 		reg = <1>;
> 		/* GPIO216 WL_HOST_WAKE */
> 		interrupt-parent = <&gpio6>;
> 		interrupts = <24 IRQ_TYPE_EDGE_FALLING>;
> 		interrupt-names = "host-wake";
> 		pinctrl-names = "default";
> 		pinctrl-0 = <&wlan_default_mode>;
> 	};
> };
> 
> most notably though, it seems that the brcmfmac driver doesn't even use
> the regulator framework, probably because all of the SDIO WLANs that
> Linux supports were wired in a more "embedded" way, where the V(Q)MMC
> supplies were enough.. unless it's the case here too?

It might not be possible to drop regulator-always-on for VREG_L12C here. The
SDHC2 host already has its own vmmc/vqmmc rails:

vqmmc-supply = <&vreg_l2c_1p62>;
vmmc-supply = <&vreg_l6c_2p96>;

VREG_L12C is a separate module-side VDDIO rail for the AP6272S WLAN/BT module.
There is no separate GPIO-controlled enable for this rail;

as you said, since brcmfmac does not appear to consume an extra VDDIO regulator
from the SDIO child node, so unless there is a preferred way to model this
module-side VDDIO rail, I think VREG_L12C still needs to stay always-on.

> 
> Konrad

Ajit

^ permalink raw reply

* [PATCH v4] riscv: dts: spacemit: Use symbolic PDMA request numbers on K1
From: Guodong Xu @ 2026-06-20  5:07 UTC (permalink / raw)
  To: Vinod Koul, Frank Li, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Yixun Lan, Paul Walmsley, Palmer Dabbelt, Albert Ou,
	Alexandre Ghiti
  Cc: linux-kernel, dmaengine, devicetree, linux-riscv, spacemit,
	Guodong Xu

The PDMA request numbers (DRQ) are fixed values specific to the SoC from
a hardware perspective. The detailed definition can be found in K1 User
Manual [1], Chapter 9.4.3 DMA Connectivity & Assignments. Add a DTS
header file to define the symbolic names for the DRQs of non-secure DMA
peripherals.

Convert the K1 SPI3 node to these macros.

Link: https://www.spacemit.com/community/document/info?lang=en&nodepath=hardware/key_stone/k1/k1_docs/k1_usermanual/9.Top_System.md [1]
Signed-off-by: Guodong Xu <docular.xu@gmail.com>
---
Changes in v4:
- Rework the commit message (Yixun Lan).
- Trim the k1-pdma.h file comment (Yixun Lan).
- Link to v3: https://patch.msgid.link/20260611-b4-k1-pdma-req-macros-v3-1-ae8416052571@gmail.com

Changes in v3:
- Move the request-number macros from include/dt-bindings/dma/ to a local
  DTS header arch/riscv/boot/dts/spacemit/k1-pdma.h (Conor).
- Squash the header and its user into a single patch.
- Link to v2: https://patch.msgid.link/20260609-b4-k1-pdma-req-macros-v2-0-5d5d7b997b54@gmail.com

Changes in v2:
- Drop the #dma-cells description change in spacemit,k1-pdma.yaml; the request
  numbers are hardware-fixed and unused by the driver (Conor)
- Link to v1: https://patch.msgid.link/20260607-b4-k1-pdma-req-macros-v1-0-5b2a3955007c@gmail.com
---
 arch/riscv/boot/dts/spacemit/k1-pdma.h | 56 ++++++++++++++++++++++++++++++++++
 arch/riscv/boot/dts/spacemit/k1.dtsi   |  4 ++-
 2 files changed, 59 insertions(+), 1 deletion(-)

diff --git a/arch/riscv/boot/dts/spacemit/k1-pdma.h b/arch/riscv/boot/dts/spacemit/k1-pdma.h
new file mode 100644
index 0000000000000..7e5ad3d7111d4
--- /dev/null
+++ b/arch/riscv/boot/dts/spacemit/k1-pdma.h
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/*
+ * DMA request number (DRQ) definitions for non-secure peripherals of
+ * the SpacemiT K1 PDMA.
+ *
+ * Copyright (c) 2026 Guodong Xu <docular.xu@gmail.com>
+ */
+
+#ifndef _DTS_SPACEMIT_K1_PDMA_H
+#define _DTS_SPACEMIT_K1_PDMA_H
+
+#define K1_PDMA_UART0_TX	3
+#define K1_PDMA_UART0_RX	4
+#define K1_PDMA_UART2_TX	5
+#define K1_PDMA_UART2_RX	6
+#define K1_PDMA_UART3_TX	7
+#define K1_PDMA_UART3_RX	8
+#define K1_PDMA_UART4_TX	9
+#define K1_PDMA_UART4_RX	10
+#define K1_PDMA_I2C0_TX		11
+#define K1_PDMA_I2C0_RX		12
+#define K1_PDMA_I2C1_TX		13
+#define K1_PDMA_I2C1_RX		14
+#define K1_PDMA_I2C2_TX		15
+#define K1_PDMA_I2C2_RX		16
+#define K1_PDMA_I2C4_TX		17
+#define K1_PDMA_I2C4_RX		18
+#define K1_PDMA_SPI3_TX		19
+#define K1_PDMA_SPI3_RX		20
+#define K1_PDMA_I2S0_TX		21
+#define K1_PDMA_I2S0_RX		22
+#define K1_PDMA_I2S1_TX		23
+#define K1_PDMA_I2S1_RX		24
+#define K1_PDMA_UART5_TX	25
+#define K1_PDMA_UART5_RX	26
+#define K1_PDMA_UART6_TX	27
+#define K1_PDMA_UART6_RX	28
+#define K1_PDMA_UART7_TX	29
+#define K1_PDMA_UART7_RX	30
+#define K1_PDMA_UART8_TX	31
+#define K1_PDMA_UART8_RX	32
+#define K1_PDMA_UART9_TX	33
+#define K1_PDMA_UART9_RX	34
+#define K1_PDMA_I2C5_TX		35
+#define K1_PDMA_I2C5_RX		36
+#define K1_PDMA_I2C6_TX		37
+#define K1_PDMA_I2C6_RX		38
+#define K1_PDMA_I2C7_TX		39
+#define K1_PDMA_I2C7_RX		40
+#define K1_PDMA_I2C8_TX		41
+#define K1_PDMA_I2C8_RX		42
+#define K1_PDMA_CAN0_RX		43
+#define K1_PDMA_QSPI_RX		44
+#define K1_PDMA_QSPI_TX		45
+
+#endif /* _DTS_SPACEMIT_K1_PDMA_H */
diff --git a/arch/riscv/boot/dts/spacemit/k1.dtsi b/arch/riscv/boot/dts/spacemit/k1.dtsi
index 08a0f28d011fe..7d414e15d2cc2 100644
--- a/arch/riscv/boot/dts/spacemit/k1.dtsi
+++ b/arch/riscv/boot/dts/spacemit/k1.dtsi
@@ -6,6 +6,8 @@
 #include <dt-bindings/clock/spacemit,k1-syscon.h>
 #include <dt-bindings/phy/phy.h>
 
+#include "k1-pdma.h"
+
 /dts-v1/;
 / {
 	#address-cells = <2>;
@@ -1094,7 +1096,7 @@ spi3: spi@d401c000 {
 				clock-names = "core", "bus";
 				resets = <&syscon_apbc RESET_SSP3>;
 				interrupts = <55>;
-				dmas = <&pdma 20>, <&pdma 19>;
+				dmas = <&pdma K1_PDMA_SPI3_RX>, <&pdma K1_PDMA_SPI3_TX>;
 				dma-names = "rx", "tx";
 				status = "disabled";
 			};

---
base-commit: 793cc54475b49b5b558902b5c13e4bfe66530a50
change-id: 20260607-b4-k1-pdma-req-macros-8d276d0126df

Best regards,
--  
Guodong Xu <docular.xu@gmail.com>


^ permalink raw reply related

* Re: [PATCH v7 0/2] arm64: dts: rockchip: add Vicharak Axon board support
From: Hrushiraj Gandhi @ 2026-06-20  5:37 UTC (permalink / raw)
  To: heiko
  Cc: krzk+dt, robh, conor+dt, devicetree, linux-arm-kernel,
	linux-rockchip, linux-kernel
In-Reply-To: <20260608060940.52549-1-hrushirajg23@gmail.com>

Hi Heiko,

Just a friendly ping on this series.

The binding patch has received an Acked-by from Krzysztof and the
requested review comments from previous revisions have been addressed.

Could you please take a look when you have a chance? Any additional
feedback on either the binding or DTS patch would be greatly appreciated.

Thanks for your time.

Best regards,
Hrushiraj

^ permalink raw reply

* Re: [PATCH v2 2/2] riscv: dts: spacemit: add QSPI support for K3 Pico-ITX
From: Aurelien Jarno @ 2026-06-20  6:23 UTC (permalink / raw)
  To: Zhengyu He
  Cc: Han Xu, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Yixun Lan, Paul Walmsley, Palmer Dabbelt, Albert Ou,
	Alexandre Ghiti, linux-spi, imx, devicetree, linux-kernel,
	linux-riscv, spacemit, Wei Fu, Cody Kang
In-Reply-To: <20260521-k3-pico-itx-qspi-v2-for-next-20260521-v2-2-52bce26e5fd8@gmail.com>

On 2026-05-21 22:44, Zhengyu He wrote:
> Add K3 QSPI controller node into k3.dtsi, and add related pinmux
> configuration.
> 
> Enable QSPI on Pico-ITX board, and describe the NOR flash which wires
> to it.
> 
> Signed-off-by: Cody Kang <cody.kang.hk@outlook.com>
> Signed-off-by: Zhengyu He <hezhy472013@gmail.com>
> ---
> Changes in v2:
> - Add "spacemit,k1-qspi" fallback to the K3 QSPI compatible.
> - Reordered Signed-off-by trailers.
> ---
>  arch/riscv/boot/dts/spacemit/k3-pico-itx.dts | 58 ++++++++++++++++++++++++++++
>  arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi | 21 ++++++++++
>  arch/riscv/boot/dts/spacemit/k3.dtsi         | 17 ++++++++
>  3 files changed, 96 insertions(+)

Thanks for the patchset.

Tested-by: Aurelien Jarno <aurelien@aurel32.net>
Reviewed-by: Aurelien Jarno <aurelien@aurel32.net>

-- 
Aurelien Jarno                          GPG: 4096R/1DDD8C9B
aurelien@aurel32.net                     http://aurel32.net

^ permalink raw reply

* Re: [PATCH 2/5] riscv: dts: spacemit: enable the i2c2 adapter on K3 Pico-ITX board
From: sashiko-bot @ 2026-06-20  6:29 UTC (permalink / raw)
  To: Aurelien Jarno; +Cc: robh, devicetree, conor+dt
In-Reply-To: <20260620062238.3199108-3-aurelien@aurel32.net>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Low] The pinctrl subnode is named `i2c2-0-pins` but belongs to the `i2c2-1-cfg` parent node.
- [Low] Missing space before the assignment operator in `bias-pull-up= <0>;`.
--

commit 4dc6c92d42831e85b38f3350c15707d0e2b3af18
Author: Aurelien Jarno <aurelien@aurel32.net>

riscv: dts: spacemit: enable the i2c2 adapter on K3 Pico-ITX board

This commit defines the I2C2 pinctrl properties and enables the adapter
on the K3 Pico-ITX board. It will be used by the 24c02 eeprom.

> diff --git a/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi b/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi
> index 23899d3f308a0..c9fe9c2e5aeee 100644
> --- a/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi
> +++ b/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi

[ ... ]

> @@ -45,6 +45,17 @@ gmac0-phy-0-pins {
>  		};
>  	};
>  
> +	/omit-if-no-ref/
> +	i2c2_1_cfg: i2c2-1-cfg {
> +		i2c2-0-pins {

[Severity: Low]
This isn't a bug, but should this subnode be named i2c2-1-pins to match
the i2c2-1-cfg parent node? It seems to diverge from the standard naming
pattern where the suffix of the parent and child matches.

> +			pinmux = <K3_PADCONF(46, 5)>,	/* i2c2 scl */
> +				 <K3_PADCONF(47, 5)>;	/* i2c2 sda */
> +
> +			bias-pull-up= <0>;

[Severity: Low]
This isn't a bug, but there is a missing space before the equals sign
here, which diverges from the spacing used in the surrounding code.

> +			drive-strength = <25>;
> +		};
> +	};

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260620062238.3199108-1-aurelien@aurel32.net?part=2

^ permalink raw reply

* [PATCH 3/5] riscv: dts: spacemit: add 24c02 eeprom on K3 Pico-ITX board
From: Aurelien Jarno @ 2026-06-20  6:22 UTC (permalink / raw)
  To: linux-kernel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Yixun Lan, Paul Walmsley, Palmer Dabbelt, Albert Ou,
	Alexandre Ghiti
  Cc: Aurelien Jarno,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list:RISC-V ARCHITECTURE,
	open list:RISC-V SPACEMIT SoC Support
In-Reply-To: <20260620062238.3199108-1-aurelien@aurel32.net>

The K3 Pico-ITX board includes a 24c02 eeprom, that stores the product
name, the part name, the MAC address of the network interfaces and the
board's serial number. These values are also exposed via an
onie,tlv-layout nvmem layout.

The eeprom is marked as read-only since its contents are not supposed to
be modified.

Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
---
 arch/riscv/boot/dts/spacemit/k3-pico-itx.dts | 30 ++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts
index 5fe9041ec5702..0a0c221c99e74 100644
--- a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts
+++ b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts
@@ -47,6 +47,36 @@ &i2c2 {
 	pinctrl-names = "default";
 	pinctrl-0 = <&i2c2_1_cfg>;
 	status = "okay";
+
+	eeprom@50 {
+		compatible = "atmel,24c02";
+		reg = <0x50>;
+		pagesize = <8>;
+		read-only;
+		size = <256>;
+		status = "okay";
+
+		nvmem-layout {
+			compatible = "onie,tlv-layout";
+
+			mac-address {
+				#nvmem-cell-cells = <1>;
+			};
+
+			num-macs {
+			};
+
+			part-number {
+			};
+
+			product-name {
+			};
+
+			serial-number {
+			};
+
+		};
+	};
 };
 
 &i2c8 {
-- 
2.53.0


^ permalink raw reply related

* [PATCH 1/5] riscv: dts: spacemit: set console baud rate on K3 Pico-ITX board
From: Aurelien Jarno @ 2026-06-20  6:22 UTC (permalink / raw)
  To: linux-kernel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Yixun Lan, Paul Walmsley, Palmer Dabbelt, Albert Ou,
	Alexandre Ghiti, Guodong Xu
  Cc: Aurelien Jarno,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list:RISC-V SPACEMIT SoC Support,
	open list:RISC-V SPACEMIT SoC Support
In-Reply-To: <20260620062238.3199108-1-aurelien@aurel32.net>

Because the default console's baud rate is not set, defconfig kernels do
not have any serial output on this platform. Set the baud rate to
115200, matching what is used by U-Boot etc on this platform.

Fixes: 7a6131804986 ("riscv: dts: spacemit: add K3 Pico-ITX board support")
Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
---
 arch/riscv/boot/dts/spacemit/k3-pico-itx.dts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts
index b89c1521e6649..509cebc0c9568 100644
--- a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts
+++ b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts
@@ -18,7 +18,7 @@ aliases {
 	};
 
 	chosen {
-		stdout-path = "serial0";
+		stdout-path = "serial0:115200n8";
 	};
 
 	memory@100000000 {
-- 
2.53.0


^ permalink raw reply related

* [PATCH 5/5] riscv: dts: spacemit: improve RTL8211F PHY configuration on K3 Pico-ITX board
From: Aurelien Jarno @ 2026-06-20  6:22 UTC (permalink / raw)
  To: linux-kernel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Yixun Lan, Paul Walmsley, Palmer Dabbelt, Albert Ou,
	Alexandre Ghiti
  Cc: Aurelien Jarno,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list:RISC-V SPACEMIT SoC Support,
	open list:RISC-V SPACEMIT SoC Support
In-Reply-To: <20260620062238.3199108-1-aurelien@aurel32.net>

Vendor kernel enabled ALDPS (Advanced Link Down Power Saving) on the
RTL8211F PHY to save power when link down.

Vendor kernel also disabled the 125MHz clkout clock signal, and indeed
the schematics confirms that it only goes to a test point (TP14), so
let's do the same.

Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
---
 arch/riscv/boot/dts/spacemit/k3-pico-itx.dts | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts
index bf64b6c6f867f..e12d798b48a49 100644
--- a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts
+++ b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts
@@ -234,6 +234,8 @@ phy0: phy@1 {
 			reset-gpios = <&gpio 0 15 GPIO_ACTIVE_LOW>;
 			reset-assert-us = <10000>;
 			reset-deassert-us = <10000>;
+			realtek,aldps-enable;
+			realtek,clkout-disable;
 		};
 	};
 };
-- 
2.53.0


^ permalink raw reply related

* [PATCH 4/5] riscv: dts: spacemit: add i2c aliases on K3 Pico-ITX board
From: Aurelien Jarno @ 2026-06-20  6:22 UTC (permalink / raw)
  To: linux-kernel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Yixun Lan, Paul Walmsley, Palmer Dabbelt, Albert Ou,
	Alexandre Ghiti
  Cc: Aurelien Jarno,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list:RISC-V SPACEMIT SoC Support,
	open list:RISC-V SPACEMIT SoC Support
In-Reply-To: <20260620062238.3199108-1-aurelien@aurel32.net>

Add i2c aliases for i2c2 and i2c8 on K3 Pico-ITX board. This is useful
to keep a stable number for the /dev entries after loading the i2c-dev
module.

Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
---
 arch/riscv/boot/dts/spacemit/k3-pico-itx.dts | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts
index 0a0c221c99e74..bf64b6c6f867f 100644
--- a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts
+++ b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts
@@ -14,6 +14,8 @@ / {
 
 	aliases {
 		ethernet0 = &eth0;
+		i2c2 = &i2c2;
+		i2c8 = &i2c8;
 		serial0 = &uart0;
 	};
 
-- 
2.53.0


^ permalink raw reply related

* [PATCH 2/5] riscv: dts: spacemit: enable the i2c2 adapter on K3 Pico-ITX board
From: Aurelien Jarno @ 2026-06-20  6:22 UTC (permalink / raw)
  To: linux-kernel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
	Yixun Lan
  Cc: Aurelien Jarno,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list:RISC-V ARCHITECTURE,
	open list:RISC-V SPACEMIT SoC Support
In-Reply-To: <20260620062238.3199108-1-aurelien@aurel32.net>

Define I2C2 pinctrl properties, and enable it on the K3 Pico-ITX board.
It will be used by the 24c02 eeprom.

Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
---
 arch/riscv/boot/dts/spacemit/k3-pico-itx.dts |  6 ++++++
 arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi | 11 +++++++++++
 2 files changed, 17 insertions(+)

diff --git a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts
index 509cebc0c9568..5fe9041ec5702 100644
--- a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts
+++ b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts
@@ -43,6 +43,12 @@ reg_aux_vcc5v: regulator-aux-vcc5v {
 	};
 };
 
+&i2c2 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2c2_1_cfg>;
+	status = "okay";
+};
+
 &i2c8 {
 	pinctrl-names = "default";
 	pinctrl-0 = <&i2c8_cfg>;
diff --git a/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi b/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi
index 3ee1471f37985..2017aa14fb424 100644
--- a/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi
+++ b/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi
@@ -78,6 +78,17 @@ gmac1-phy-0-pins {
 		};
 	};
 
+	/omit-if-no-ref/
+	i2c2_1_cfg: i2c2-1-cfg {
+		i2c2-0-pins {
+			pinmux = <K3_PADCONF(46, 5)>,	/* i2c2 scl */
+				 <K3_PADCONF(47, 5)>;	/* i2c2 sda */
+
+			bias-pull-up= <0>;
+			drive-strength = <25>;
+		};
+	};
+
 	/omit-if-no-ref/
 	i2c8_cfg: i2c8-cfg {
 		i2c8-pins {
-- 
2.53.0


^ permalink raw reply related

* Re: [RFC PATCH 3/3] MAINTAINERS: Add entry for Sensirion STS30 driver
From: Joshua Crofts @ 2026-06-20  7:00 UTC (permalink / raw)
  To: Maxwell Doose
  Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	open list:IIO SUBSYSTEM AND DRIVERS,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list
In-Reply-To: <20260620044010.1082621-4-m32285159@gmail.com>

On Fri, 19 Jun 2026 23:40:07 -0500
Maxwell Doose <m32285159@gmail.com> wrote:  
> +SENSIRION STS30 TEMPERATURE SENSOR DRIVER
> +M:	Maxwell Doose <m32285159@gmail.com>
> +S:	Maintained
> +F:	Documentation/devicetree/bindings/iio/temperature/sensirion,sts30.yaml

Fold the above changes into your dt-bindings patch.

> +F:	drivers/iio/temperature/sts30.c

This change then goes into the driver code patch. If you're
adding new files, ensure that your patch also adds a mention
to said file to MAINTAINERS (IIRC b4 actually checks for this
when you run prep --check).

-- 
Kind regards

CJD

^ permalink raw reply

* Re: [RFC PATCH 2/3] iio: temperature: Add STS30 temperature sensor driver
From: Joshua Crofts @ 2026-06-20  7:43 UTC (permalink / raw)
  To: Maxwell Doose
  Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	open list:IIO SUBSYSTEM AND DRIVERS,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list
In-Reply-To: <20260620044010.1082621-3-m32285159@gmail.com>

Hi Max,

comments inline, some nits, some more serious, some Sashiko reviews.

Josh

On Fri, 19 Jun 2026 23:40:06 -0500
Maxwell Doose <m32285159@gmail.com> wrote:
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2026 Maxwell Doose
> + *
> + * Sensirion STS30 temperature sensor driver
> + *
> + * Datasheet: https://sensirion.com/media/documents/1DA31AFD/65D613A8/Datasheet_STS3x_DIS.pdf
> + *
> + * Author: Maxwell Doose <m32285159@gmail.com>

Nit, but this is probably unnecessary since you already have
the copyright statement above.

> + */
> +

You're missing array_size.h, as used in probe.

> +#include <linux/bits.h>
> +#include <linux/cleanup.h>
> +#include <linux/crc8.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/errno.h>
> +#include <linux/export.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/types.h>

Move the IIO specific headers below the generic linux headers
and group them separately.

> +#include <linux/i2c.h>
> +#include <linux/kernel.h>

Don't use kernel.h for new entries, you should include what
you actually use instead of relying on this. (there is a tool
that can help with this, it's called `iwyu-tool`).

> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/types.h>
> +#include <linux/unaligned.h>
> +
> +/* Amount of bytes received from the STS30 after a read command */
> +#define STS30_MEAS_SIZE 3
> +
> +#define STS30_COMMAND_READ_HIGH_REPEAT 0x2C06
> +#define STS30_COMMAND_READ_MED_REPEAT 0x2C0D
> +#define STS30_COMMAND_READ_LOW_REPEAT 0x2C10
> +
> +/* Soft reset command */

No point in having a comment if your macro is sensibly named.

> +#define STS30_COMMAND_RESET 0x30A2
> +
> +/*
> + * sts30 includes a CRC8 checksum at the end of its i2c responses. The polynomial
> + * is used to generate the CRC8 table and the seed is the starting value.
> + */
> +#define STS30_CRC8_POLYNOMIAL 0x31
> +#define STS30_CRC8_SEED 0xFF
> +
> +DECLARE_CRC8_TABLE(sts30_crc_table);
> +
> +enum sts30_read_delays {
> +	STS30_REPEAT_LOW = 4500,
> +	STS30_REPEAT_MED = 6000,
> +	STS30_REPEAT_HIGH = 15000
> +};
> +
> +/**
> + * struct sts30_data - data structure for STS30 driver
> + *
> + * @client: underlying i2c client data structure
> + * @lock: mutex for serialized communication on the i2c bus
> + * @delay: measurement duration for the current repeatability mode
> + */

I'd remove this comment altogether and just add a comment on why
you need a mutex.

> +struct sts30_data {
> +	struct i2c_client *client;
> +	struct mutex lock;
> +	/*
> +	 * sts30 has three potential repeatability/measurement durations. We need to
> +	 * account for them while reading the i2c bus.
> +	 *
> +	 * See section 2.2 in the datasheet for more info on processing times.
> +	 */
> +	enum sts30_read_delays delay;
> +};
> +
> +static int sts30_verify_crc8(struct sts30_data *data, u8 buf[STS30_MEAS_SIZE])
> +{
> +	int crc;
> +
> +	crc = crc8(sts30_crc_table, buf, 2, STS30_CRC8_SEED);

Please use sizeof() instead of hard coding the buffer length.

> +	if (crc != buf[2]) {
> +		dev_err(&data->client->dev, "Expected CRC8 value of 0x%02x, got 0x%02x\n",
> +			buf[2], crc);
> +		return -EIO;
> +	}
> +
> +	return 0;
> +}
> +
> +static int sts30_read(struct sts30_data *data, u16 command, u16 *val)
> +{
> +	u8 tmp[2];
> +	u8 buf[STS30_MEAS_SIZE];
	
Reverse christmas tree order.

> +	int ret;
> +
> +	put_unaligned_be16(command, tmp);
> +
> +	ret = i2c_master_send(data->client, tmp, sizeof(tmp));
> +	if (ret < 0)
> +		return ret;
> +	if (ret != sizeof(tmp))
> +		return -EIO;
> +
> +	fsleep(data->delay);

Adding Sashiko's comment:

Will sending a STOP condition here abort the measurement? Since
i2c_master_send() issues a STOP condition rather than a Repeated START,
this sequence violates the protocol for the Clock Stretching Enabled
commands (0x2Cxx) defined above.
Should this use the "Clock Stretching Disabled" commands (e.g., 0x2400)
instead, or alternatively use a single i2c_transfer() with a Repeated START?

> +
> +	ret = i2c_master_recv(data->client, buf, sizeof(buf));
> +	if (ret < 0)
> +		return ret;
> +	if (ret != sizeof(buf))
> +		return -EIO;
> +
> +	*val = get_unaligned_be16(buf);
> +
> +	ret = sts30_verify_crc8(data, buf);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}

...

> +static int sts30_reset(struct sts30_data *data)
> +{
> +	int ret;
> +
> +	guard(mutex)(&data->lock);
> +
> +	ret = sts30_write(data, STS30_COMMAND_RESET);
> +	if (ret)
> +		return ret;
> +
> +	fsleep(1500);

Add a comment or change this to a macro to explain why 1500
specifically.

> +
> +	return 0;
> +}
> +
> +static int sts30_read_raw(struct iio_dev *indio_dev,
> +			  struct iio_chan_spec const *chan, int *val, int *val2,
> +			  long mask)
> +{
> +	struct sts30_data *data = iio_priv(indio_dev);
> +	int ret;
> +	u16 tmp;
> +
> +	guard(mutex)(&data->lock);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		switch (data->delay) {
> +		case STS30_REPEAT_LOW:
> +			ret = sts30_read(data, STS30_COMMAND_READ_LOW_REPEAT, &tmp);
> +			break;
> +		case STS30_REPEAT_MED:
> +			ret = sts30_read(data, STS30_COMMAND_READ_MED_REPEAT, &tmp);
> +			break;
> +		case STS30_REPEAT_HIGH:
> +			ret = sts30_read(data, STS30_COMMAND_READ_HIGH_REPEAT, &tmp);
> +			break;
> +		default:
> +			dev_warn(&data->client->dev, "Repeatability state corrupted, got: %d\n",
> +				 data->delay);

Do we need this warning? Returning -EINVAL is sufficient
enough.

> +			return -EINVAL;
> +		}
> +
> +		if (ret)
> +			return ret;
> +
> +		*val = tmp;
> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_OFFSET:
> +		/*
> +		 * We use this constant -16852 as calculated using the formula
> +		 * in the datasheet. See section 4.12 in the data sheet for more
> +		 * info.
> +		 */
> +		*val = -16852;

Okay, this would definitely be better if it were a macro. I'd even
be tempted to add the formula in a comment for safe keeping.
 
...

> +static const struct i2c_device_id sts30_id[] = {
> +	{ "sts30" },
> +	{ "sts31" },
> +	{ "sts35" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, sts30_id);

Move the struct i2c_device_id struct after probe(). Additionally,
use the `.name` named initializer when defining the IDs (this reflects
on Uwe Kleine-Konig's effort in IIO).

> +
> +static int sts30_probe(struct i2c_client *client)
> +{
> +	struct iio_dev *indio_dev;
> +	struct sts30_data *data;
> +	int ret;
> +
> +	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
> +	if (!indio_dev)
> +		return -ENOMEM;
> +
> +	indio_dev->name = client->name;
> +	indio_dev->info = &sts30_info;
> +	indio_dev->channels = sts30_channels;
> +	indio_dev->num_channels = ARRAY_SIZE(sts30_channels);
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +	data = iio_priv(indio_dev);
> +	data->client = client;
> +	data->delay = STS30_REPEAT_HIGH;
> +
> +	ret = devm_mutex_init(&client->dev, &data->lock);
> +	if (ret)
> +		return ret;
> +
> +	i2c_set_clientdata(client, indio_dev);
> +
> +	ret = sts30_reset(data);

You're not checking ret here.

> +
> +	return devm_iio_device_register(&client->dev, indio_dev);
> +}
> +

-- 
Kind regards

CJD

^ permalink raw reply

* Re: [PATCH] arm64: dts: broadcom: bcm2712: Remove non-functional EL2 virtual timer
From: Marc Zyngier @ 2026-06-20  8:49 UTC (permalink / raw)
  To: Daniel Drake
  Cc: robh, krzk+dt, conor+dt, florian.fainelli,
	bcm-kernel-feedback-list, devicetree, linux-rpi-kernel,
	linux-arm-kernel, m.szyprowski, andrea.porta
In-Reply-To: <20260619204832.586079-1-dan@reactivated.net>

Hi Daniel,

Thanks for posting this.

On Fri, 19 Jun 2026 21:48:32 +0100,
Daniel Drake <dan@reactivated.net> wrote:
> 
> Commit d87773de9efe1 ("clocksource/drivers/arm_arch_timer: Default to
> EL2 virtual timer when running VHE") causes boot to hang on
> Raspberry Pi 5. The newly-selected EL2 virtual timer does not generate
> any interrupts, even though the GIC_DIST_ENABLE_SET flag has been
> confirmed set via readback.
> 
> The reasons for this failure are unknown, however it is likely that
> this timer was never tested. Raspberry Pi's original devicetree did

The timer is part of the CPU, and there are enough A76 implementations
around to prove that it actually works. The same can be said for the
GIC400 this is (supposedly) attached to.

> not include this timer interrupt; it was only introduced via a
> suggestion[1] made in code review as part of the upstreaming process.
> (Current RPi firmware versions do include this timer, but only because
> they rebased on top of the upstreamed devicetree starting with
> Linux 6.12)
> 
> Until more is known about this non-firing timer interrupt, remove
> the devicetree entry to enable RPi5 devices to boot.

I'd like to understand the reason why the timer interrupt isn't being
delivered *before* we paper over it, and not the other way
around. Each of the CPUs definitely have an EL2 virtual timer, the GIC
has a per-CPU interrupt, but somehow the two don't seem to be linked.

Since DT is supposed to describe the HW, I'd expect someone from
Broadcom or RPi to shine a light on this issue. Integration mistakes
happen, and we work around them (see the handful of Samsung SoCs where
the timer interrupt was simply not wired). But we absolutely need to
know what we are dealing with beforehand.

Finally, just hacking the DT is not enough. Assuming that the timer is
indeed unusable, we need to cope with the fact that there are DTs
describing it in the wild, as nobody should be forced to upgrade their
DT in lockstep with the kernel. For that, you'd also need something
like the patch below (untested, and in need of a proper commit
message, which I expect the SoC vendor to provide).

Thanks,

	M.

From 9de354b472e28112d73fdb63be986f68fb3c91a9 Mon Sep 17 00:00:00 2001
From: Marc Zyngier <maz@kernel.org>
Date: Sat, 20 Jun 2026 09:32:09 +0100
Subject: [PATCH] clocksource/drivers/arm_arch_timer: Workaround RPi5 broken
 EL2 virtual timer

Insert $REASON here.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 drivers/clocksource/arm_arch_timer.c | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 4adf756423de9..de9007a30a923 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -1090,6 +1090,16 @@ static int __init arch_timer_common_init(void)
 	return arch_timer_arch_init();
 }
 
+static bool __init has_broken_el2_vtimer(void)
+{
+	static const char * const broken_el2_vtimer[] __initconst = {
+		"brcm,bcm2712",
+		NULL
+	};
+
+	return of_machine_compatible_match(broken_el2_vtimer);
+}
+
 /**
  * arch_timer_select_ppi() - Select suitable PPI for the current system.
  *
@@ -1115,7 +1125,8 @@ static int __init arch_timer_common_init(void)
 static enum arch_timer_ppi_nr __init arch_timer_select_ppi(void)
 {
 	if (is_kernel_in_hyp_mode()) {
-		if (arch_timer_ppi[ARCH_TIMER_HYP_VIRT_PPI])
+		if (arch_timer_ppi[ARCH_TIMER_HYP_VIRT_PPI] &&
+		    !has_broken_el2_vtimer())
 			return ARCH_TIMER_HYP_VIRT_PPI;
 
 		pr_warn_once(FW_BUG "VHE-capable CPU without EL2 virtual timer interrupt\n");
-- 
2.47.3


-- 
Jazz isn't dead. It just smells funny.

^ permalink raw reply related

* [PATCH v6 0/3] regulator: Add support for Unisoc SC2730 PMIC regulators
From: Otto Pflüger @ 2026-06-20  8:53 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Orson Zhai, Baolin Wang, Chunyan Zhang, Lee Jones
  Cc: linux-kernel, devicetree, Otto Pflüger, Krzysztof Kozlowski,
	Zhongfa Wang

Add device tree bindings and a driver for the regulators found in the
Spreadtrum/Unisoc SC2730 PMIC.

Signed-off-by: Otto Pflüger <otto.pflueger@abscue.de>
---
Changes in v6:
- Use dev_err_probe() to simplify error handling
- Specify .name field explicitly in ID table
- Simplify regulator list by removing the min_sel parameter, which is
  always set to 0 for sc2730
- Link to v5: https://lore.kernel.org/r/20260619-sc2730-regulators-v5-0-0a24c7ed7a97@abscue.de

Changes in v5:
- Drop conditional binding patch, will be sent separately
- Rebase on next-20260618
- Link to v4: https://lore.kernel.org/r/20260521-sc2730-regulators-v4-0-1ac8a3b5ed82@abscue.de

Changes in v4:
- Add missing patch for MFD cell registration
- Use conditional binding for referencing the regulators now that the
  child node cannot be distinguished by its compatible for validation
  purposes.
- Drop requirement for removed compatible property (sorry, should have
  double-checked this before sending v3)
- Link to v3: https://lore.kernel.org/r/20260519-sc2730-regulators-v3-0-5bf0e02507e3@abscue.de

Changes in v3:
- Drop compatible property
- Drop unused VDDSIM0 and VDDSIM1 IDs
- Link to v2: https://lore.kernel.org/r/20260518-sc2730-regulators-v2-0-9a5b3a7b1e49@abscue.de

Changes in v2:
- Use lowercase names without underscores for device tree nodes
- Use oneOf for binding reference instead of making it conditional
- Remove some excess line breaks
- Fix author name in driver and add original Signed-off-by
- Link to v1: https://lore.kernel.org/r/20260220-sc2730-regulators-v1-0-3f2bbc9ecf14@abscue.de

---
Otto Pflüger (3):
      regulator: dt-bindings: Add Unisoc SC2730 PMIC
      mfd: sprd-sc27xx: Add SC2730 regulator cell
      regulator: Add regulator driver for Unisoc SC2730 PMIC

 .../bindings/regulator/sprd,sc2730-regulator.yaml  |  44 +++
 drivers/mfd/sprd-sc27xx-spi.c                      |   1 +
 drivers/regulator/Kconfig                          |   7 +
 drivers/regulator/Makefile                         |   1 +
 drivers/regulator/sc2730-regulator.c               | 375 +++++++++++++++++++++
 5 files changed, 428 insertions(+)
---
base-commit: 598c7067dd8b65b93f3ccada47e9014a13137f1b
change-id: 20260216-sc2730-regulators-13ba789641a4

Best regards,
-- 
Otto Pflüger <otto.pflueger@abscue.de>


^ permalink raw reply

* [PATCH v6 2/3] mfd: sprd-sc27xx: Add SC2730 regulator cell
From: Otto Pflüger @ 2026-06-20  8:54 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Orson Zhai, Baolin Wang, Chunyan Zhang, Lee Jones
  Cc: linux-kernel, devicetree, Otto Pflüger
In-Reply-To: <20260620-sc2730-regulators-v6-0-bbd2db395231@abscue.de>

Add an MFD cell to register the SC2730 PMIC's regulators.

Signed-off-by: Otto Pflüger <otto.pflueger@abscue.de>
---
 drivers/mfd/sprd-sc27xx-spi.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/mfd/sprd-sc27xx-spi.c b/drivers/mfd/sprd-sc27xx-spi.c
index aa052f646623..214bcbef0c27 100644
--- a/drivers/mfd/sprd-sc27xx-spi.c
+++ b/drivers/mfd/sprd-sc27xx-spi.c
@@ -61,6 +61,7 @@ static const struct mfd_cell sc2730_devices[] = {
 	MFD_CELL_OF("sc2730-efuse", NULL, NULL, 0, 0, "sprd,sc2730-efuse"),
 	MFD_CELL_OF("sc2730-eic", NULL, NULL, 0, 0, "sprd,sc2730-eic"),
 	MFD_CELL_OF("sc2730-fgu", NULL, NULL, 0, 0, "sprd,sc2730-fgu"),
+	MFD_CELL_NAME("sc2730-regulator"),
 	MFD_CELL_OF("sc2730-rtc", NULL, NULL, 0, 0, "sprd,sc2730-rtc"),
 	MFD_CELL_OF("sc2730-vibrator", NULL, NULL, 0, 0, "sprd,sc2730-vibrator"),
 };

-- 
2.51.0


^ permalink raw reply related

* [PATCH v6 1/3] regulator: dt-bindings: Add Unisoc SC2730 PMIC
From: Otto Pflüger @ 2026-06-20  8:54 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Orson Zhai, Baolin Wang, Chunyan Zhang, Lee Jones
  Cc: linux-kernel, devicetree, Otto Pflüger, Krzysztof Kozlowski
In-Reply-To: <20260620-sc2730-regulators-v6-0-bbd2db395231@abscue.de>

Add bindings for the regulators found in the Spreadtrum/Unisoc SC2730
PMIC, used e.g. with the UMS512 and UMS9230 SoCs.

Signed-off-by: Otto Pflüger <otto.pflueger@abscue.de>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
---
 .../bindings/regulator/sprd,sc2730-regulator.yaml  | 44 ++++++++++++++++++++++
 1 file changed, 44 insertions(+)

diff --git a/Documentation/devicetree/bindings/regulator/sprd,sc2730-regulator.yaml b/Documentation/devicetree/bindings/regulator/sprd,sc2730-regulator.yaml
new file mode 100644
index 000000000000..ab945c46b08e
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/sprd,sc2730-regulator.yaml
@@ -0,0 +1,44 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/regulator/sprd,sc2730-regulator.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Unisoc SC2730 Power Management IC regulators
+
+maintainers:
+  - Otto Pflüger <otto.pflueger@abscue.de>
+
+patternProperties:
+  "^dcdc-(core|cpu|gen[0-1]|gpu|mem|memq|modem|sram)$":
+    type: object
+    $ref: regulator.yaml#
+    unevaluatedProperties: false
+
+  "^ldo-avdd(12|18)$":
+    type: object
+    $ref: regulator.yaml#
+    unevaluatedProperties: false
+
+  "^ldo-vdd(18-dcxo|28)$":
+    type: object
+    $ref: regulator.yaml#
+    unevaluatedProperties: false
+
+  "^ldo-vdd(emmccore|kpled|ldo[0-2]|sd(core|io)|sim[0-2]|usb33|wcn|wifipa)$":
+    type: object
+    $ref: regulator.yaml#
+    unevaluatedProperties: false
+
+  "^ldo-vddcam(a0|a1|d0|d1|io|mot)$":
+    type: object
+    $ref: regulator.yaml#
+    unevaluatedProperties: false
+
+  "^ldo-vddrf(1v25|18)$":
+    type: object
+    $ref: regulator.yaml#
+    unevaluatedProperties: false
+
+additionalProperties: false
+...

-- 
2.51.0


^ permalink raw reply related

* [PATCH v6 3/3] regulator: Add regulator driver for Unisoc SC2730 PMIC
From: Otto Pflüger @ 2026-06-20  8:54 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Orson Zhai, Baolin Wang, Chunyan Zhang, Lee Jones
  Cc: linux-kernel, devicetree, Otto Pflüger, Zhongfa Wang
In-Reply-To: <20260620-sc2730-regulators-v6-0-bbd2db395231@abscue.de>

Add a regulator driver for the Spreadtrum/Unisoc SC2730 PMIC, used
e.g. with the UMS512 and UMS9230 SoCs. This version of the driver is
based on a downstream driver provided by Unisoc [1][2] and the existing
SC2731 driver.

[1]: https://github.com/MotorolaMobilityLLC/kernel-sprd/commit/30be0ddfe6b9a877fc9c328fbd2bae84e645eb31
[2]: https://github.com/MotorolaMobilityLLC/kernel-sprd/blob/android-13-release-tla33/drivers/regulator/sc2730-regulator.c

Signed-off-by: Zhongfa Wang <zhongfa.wang@unisoc.com>
[cleanup, adapt to new device tree requirements]
Signed-off-by: Otto Pflüger <otto.pflueger@abscue.de>
---
 drivers/regulator/Kconfig            |   7 +
 drivers/regulator/Makefile           |   1 +
 drivers/regulator/sc2730-regulator.c | 375 +++++++++++++++++++++++++++++++++++
 3 files changed, 383 insertions(+)

diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index a54a549196fe..89789ac7a786 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -1477,6 +1477,13 @@ config REGULATOR_S5M8767
 	 via I2C bus. S5M8767A have 9 Bucks and 28 LDOs output and
 	 supports DVS mode with 8bits of output voltage control.
 
+config REGULATOR_SC2730
+	tristate "Spreadtrum SC2730 power regulator driver"
+	depends on MFD_SC27XX_PMIC || COMPILE_TEST
+	help
+	  This driver provides support for the voltage regulators on the
+	  SC2730 PMIC.
+
 config REGULATOR_SC2731
 	tristate "Spreadtrum SC2731 power regulator driver"
 	depends on MFD_SC27XX_PMIC || COMPILE_TEST
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 134eee274dbf..5a764cec8df8 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -170,6 +170,7 @@ obj-$(CONFIG_REGULATOR_S2DOS05) += s2dos05-regulator.o
 obj-$(CONFIG_REGULATOR_S2MPA01) += s2mpa01.o
 obj-$(CONFIG_REGULATOR_S2MPS11) += s2mps11.o
 obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o
+obj-$(CONFIG_REGULATOR_SC2730) += sc2730-regulator.o
 obj-$(CONFIG_REGULATOR_SC2731) += sc2731-regulator.o
 obj-$(CONFIG_REGULATOR_SGM3804) += sgm3804-regulator.o
 obj-$(CONFIG_REGULATOR_SKY81452) += sky81452-regulator.o
diff --git a/drivers/regulator/sc2730-regulator.c b/drivers/regulator/sc2730-regulator.c
new file mode 100644
index 000000000000..166b19ed539b
--- /dev/null
+++ b/drivers/regulator/sc2730-regulator.c
@@ -0,0 +1,375 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018-2021 Unisoc Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+
+/*
+ * SC2730 regulator base address
+ */
+#define SC2730_REGULATOR_BASE		0x1800
+
+/*
+ * SC2730 regulator lock register
+ */
+#define SC2730_WR_UNLOCK_VALUE		0x6e7f
+#define SC2730_PWR_WR_PROT		(SC2730_REGULATOR_BASE + 0x3d0)
+
+/*
+ * SC2730 enable register
+ */
+#define SC2730_POWER_PD_SW		(SC2730_REGULATOR_BASE + 0x01c)
+#define SC2730_LDO_VDDRF18_PD		(SC2730_REGULATOR_BASE + 0x10c)
+#define SC2730_LDO_VDDCAMIO_PD		(SC2730_REGULATOR_BASE + 0x118)
+#define SC2730_LDO_VDDWCN_PD		(SC2730_REGULATOR_BASE + 0x11c)
+#define SC2730_LDO_VDDCAMD1_PD		(SC2730_REGULATOR_BASE + 0x128)
+#define SC2730_LDO_VDDCAMD0_PD		(SC2730_REGULATOR_BASE + 0x134)
+#define SC2730_LDO_VDDRF1V25_PD		(SC2730_REGULATOR_BASE + 0x140)
+#define SC2730_LDO_AVDD12_PD		(SC2730_REGULATOR_BASE + 0x14c)
+#define SC2730_LDO_VDDCAMA0_PD		(SC2730_REGULATOR_BASE + 0x158)
+#define SC2730_LDO_VDDCAMA1_PD		(SC2730_REGULATOR_BASE + 0x164)
+#define SC2730_LDO_VDDCAMMOT_PD		(SC2730_REGULATOR_BASE + 0x170)
+#define SC2730_LDO_VDDSIM2_PD		(SC2730_REGULATOR_BASE + 0x194)
+#define SC2730_LDO_VDDEMMCCORE_PD	(SC2730_REGULATOR_BASE + 0x1a0)
+#define SC2730_LDO_VDDSDCORE_PD		(SC2730_REGULATOR_BASE + 0x1ac)
+#define SC2730_LDO_VDDSDIO_PD		(SC2730_REGULATOR_BASE + 0x1b8)
+#define SC2730_LDO_VDDWIFIPA_PD		(SC2730_REGULATOR_BASE + 0x1d0)
+#define SC2730_LDO_VDDUSB33_PD		(SC2730_REGULATOR_BASE + 0x1e8)
+#define SC2730_LDO_VDDLDO0_PD		(SC2730_REGULATOR_BASE + 0x1f4)
+#define SC2730_LDO_VDDLDO1_PD		(SC2730_REGULATOR_BASE + 0x200)
+#define SC2730_LDO_VDDLDO2_PD		(SC2730_REGULATOR_BASE + 0x20c)
+#define SC2730_LDO_VDDKPLED_PD		(SC2730_REGULATOR_BASE + 0x38c)
+
+/*
+ * SC2730 enable mask
+ */
+#define SC2730_DCDC_CPU_PD_MASK		BIT(4)
+#define SC2730_DCDC_GPU_PD_MASK		BIT(3)
+#define SC2730_DCDC_CORE_PD_MASK	BIT(5)
+#define SC2730_DCDC_MODEM_PD_MASK	BIT(11)
+#define SC2730_DCDC_MEM_PD_MASK		BIT(6)
+#define SC2730_DCDC_MEMQ_PD_MASK	BIT(12)
+#define SC2730_DCDC_GEN0_PD_MASK	BIT(8)
+#define SC2730_DCDC_GEN1_PD_MASK	BIT(7)
+#define SC2730_DCDC_SRAM_PD_MASK	BIT(13)
+#define SC2730_LDO_AVDD18_PD_MASK	BIT(2)
+#define SC2730_LDO_VDDRF18_PD_MASK	BIT(0)
+#define SC2730_LDO_VDDCAMIO_PD_MASK	BIT(0)
+#define SC2730_LDO_VDDWCN_PD_MASK	BIT(0)
+#define SC2730_LDO_VDDCAMD1_PD_MASK	BIT(0)
+#define SC2730_LDO_VDDCAMD0_PD_MASK	BIT(0)
+#define SC2730_LDO_VDDRF1V25_PD_MASK	BIT(0)
+#define SC2730_LDO_AVDD12_PD_MASK	BIT(0)
+#define SC2730_LDO_VDDCAMA0_PD_MASK	BIT(0)
+#define SC2730_LDO_VDDCAMA1_PD_MASK	BIT(0)
+#define SC2730_LDO_VDDCAMMOT_PD_MASK	BIT(0)
+#define SC2730_LDO_VDDSIM2_PD_MASK	BIT(0)
+#define SC2730_LDO_VDDEMMCCORE_PD_MASK	BIT(0)
+#define SC2730_LDO_VDDSDCORE_PD_MASK	BIT(0)
+#define SC2730_LDO_VDDSDIO_PD_MASK	BIT(0)
+#define SC2730_LDO_VDD28_PD_MASK	BIT(1)
+#define SC2730_LDO_VDDWIFIPA_PD_MASK	BIT(0)
+#define SC2730_LDO_VDD18_DCXO_PD_MASK	BIT(10)
+#define SC2730_LDO_VDDUSB33_PD_MASK	BIT(0)
+#define SC2730_LDO_VDDLDO0_PD_MASK	BIT(0)
+#define SC2730_LDO_VDDLDO1_PD_MASK	BIT(0)
+#define SC2730_LDO_VDDLDO2_PD_MASK	BIT(0)
+#define SC2730_LDO_VDDKPLED_PD_MASK	BIT(15)
+
+/*
+ * SC2730 vsel register
+ */
+#define SC2730_DCDC_CPU_VOL		(SC2730_REGULATOR_BASE + 0x44)
+#define SC2730_DCDC_GPU_VOL		(SC2730_REGULATOR_BASE + 0x54)
+#define SC2730_DCDC_CORE_VOL		(SC2730_REGULATOR_BASE + 0x64)
+#define SC2730_DCDC_MODEM_VOL		(SC2730_REGULATOR_BASE + 0x74)
+#define SC2730_DCDC_MEM_VOL		(SC2730_REGULATOR_BASE + 0x84)
+#define SC2730_DCDC_MEMQ_VOL		(SC2730_REGULATOR_BASE + 0x94)
+#define SC2730_DCDC_GEN0_VOL		(SC2730_REGULATOR_BASE + 0xa4)
+#define SC2730_DCDC_GEN1_VOL		(SC2730_REGULATOR_BASE + 0xb4)
+#define SC2730_DCDC_SRAM_VOL		(SC2730_REGULATOR_BASE + 0xdc)
+#define SC2730_LDO_AVDD18_VOL		(SC2730_REGULATOR_BASE + 0x104)
+#define SC2730_LDO_VDDRF18_VOL		(SC2730_REGULATOR_BASE + 0x110)
+#define SC2730_LDO_VDDCAMIO_VOL		(SC2730_REGULATOR_BASE + 0x28)
+#define SC2730_LDO_VDDWCN_VOL		(SC2730_REGULATOR_BASE + 0x120)
+#define SC2730_LDO_VDDCAMD1_VOL		(SC2730_REGULATOR_BASE + 0x12c)
+#define SC2730_LDO_VDDCAMD0_VOL		(SC2730_REGULATOR_BASE + 0x138)
+#define SC2730_LDO_VDDRF1V25_VOL	(SC2730_REGULATOR_BASE + 0x144)
+#define SC2730_LDO_AVDD12_VOL		(SC2730_REGULATOR_BASE + 0x150)
+#define SC2730_LDO_VDDCAMA0_VOL		(SC2730_REGULATOR_BASE + 0x15c)
+#define SC2730_LDO_VDDCAMA1_VOL		(SC2730_REGULATOR_BASE + 0x168)
+#define SC2730_LDO_VDDCAMMOT_VOL	(SC2730_REGULATOR_BASE + 0x174)
+#define SC2730_LDO_VDDSIM2_VOL		(SC2730_REGULATOR_BASE + 0x198)
+#define SC2730_LDO_VDDEMMCCORE_VOL	(SC2730_REGULATOR_BASE + 0x1a4)
+#define SC2730_LDO_VDDSDCORE_VOL	(SC2730_REGULATOR_BASE + 0x1b0)
+#define SC2730_LDO_VDDSDIO_VOL		(SC2730_REGULATOR_BASE + 0x1bc)
+#define SC2730_LDO_VDD28_VOL		(SC2730_REGULATOR_BASE + 0x1c8)
+#define SC2730_LDO_VDDWIFIPA_VOL	(SC2730_REGULATOR_BASE + 0x1d4)
+#define SC2730_LDO_VDD18_DCXO_VOL	(SC2730_REGULATOR_BASE + 0x1e0)
+#define SC2730_LDO_VDDUSB33_VOL		(SC2730_REGULATOR_BASE + 0x1ec)
+#define SC2730_LDO_VDDLDO0_VOL		(SC2730_REGULATOR_BASE + 0x1f8)
+#define SC2730_LDO_VDDLDO1_VOL		(SC2730_REGULATOR_BASE + 0x204)
+#define SC2730_LDO_VDDLDO2_VOL		(SC2730_REGULATOR_BASE + 0x210)
+#define SC2730_LDO_VDDKPLED_VOL		(SC2730_REGULATOR_BASE + 0x38c)
+
+/*
+ * SC2730 vsel register mask
+ */
+#define SC2730_DCDC_CPU_VOL_MASK	GENMASK(8, 0)
+#define SC2730_DCDC_GPU_VOL_MASK	GENMASK(8, 0)
+#define SC2730_DCDC_CORE_VOL_MASK	GENMASK(8, 0)
+#define SC2730_DCDC_MODEM_VOL_MASK	GENMASK(8, 0)
+#define SC2730_DCDC_MEM_VOL_MASK	GENMASK(7, 0)
+#define SC2730_DCDC_MEMQ_VOL_MASK	GENMASK(8, 0)
+#define SC2730_DCDC_GEN0_VOL_MASK	GENMASK(7, 0)
+#define SC2730_DCDC_GEN1_VOL_MASK	GENMASK(7, 0)
+#define SC2730_DCDC_SRAM_VOL_MASK	GENMASK(8, 0)
+#define SC2730_LDO_AVDD18_VOL_MASK	GENMASK(5, 0)
+#define SC2730_LDO_VDDRF18_VOL_MASK	GENMASK(5, 0)
+#define SC2730_LDO_VDDCAMIO_VOL_MASK	GENMASK(5, 0)
+#define SC2730_LDO_VDDWCN_VOL_MASK	GENMASK(5, 0)
+#define SC2730_LDO_VDDCAMD1_VOL_MASK	GENMASK(4, 0)
+#define SC2730_LDO_VDDCAMD0_VOL_MASK	GENMASK(4, 0)
+#define SC2730_LDO_VDDRF1V25_VOL_MASK	GENMASK(4, 0)
+#define SC2730_LDO_AVDD12_VOL_MASK	GENMASK(4, 0)
+#define SC2730_LDO_VDDCAMA0_VOL_MASK	GENMASK(7, 0)
+#define SC2730_LDO_VDDCAMA1_VOL_MASK	GENMASK(7, 0)
+#define SC2730_LDO_VDDCAMMOT_VOL_MASK	GENMASK(7, 0)
+#define SC2730_LDO_VDDSIM2_VOL_MASK	GENMASK(7, 0)
+#define SC2730_LDO_VDDEMMCCORE_VOL_MASK	GENMASK(7, 0)
+#define SC2730_LDO_VDDSDCORE_VOL_MASK	GENMASK(7, 0)
+#define SC2730_LDO_VDDSDIO_VOL_MASK	GENMASK(7, 0)
+#define SC2730_LDO_VDD28_VOL_MASK	GENMASK(7, 0)
+#define SC2730_LDO_VDDWIFIPA_VOL_MASK	GENMASK(7, 0)
+#define SC2730_LDO_VDD18_DCXO_VOL_MASK	GENMASK(7, 0)
+#define SC2730_LDO_VDDUSB33_VOL_MASK	GENMASK(7, 0)
+#define SC2730_LDO_VDDLDO0_VOL_MASK	GENMASK(7, 0)
+#define SC2730_LDO_VDDLDO1_VOL_MASK	GENMASK(7, 0)
+#define SC2730_LDO_VDDLDO2_VOL_MASK	GENMASK(7, 0)
+#define SC2730_LDO_VDDKPLED_VOL_MASK	GENMASK(14, 7)
+
+enum sc2730_regulator_id {
+	SC2730_DCDC_CPU,
+	SC2730_DCDC_GPU,
+	SC2730_DCDC_CORE,
+	SC2730_DCDC_MODEM,
+	SC2730_DCDC_MEM,
+	SC2730_DCDC_MEMQ,
+	SC2730_DCDC_GEN0,
+	SC2730_DCDC_GEN1,
+	SC2730_DCDC_SRAM,
+	SC2730_LDO_AVDD18,
+	SC2730_LDO_VDDRF18,
+	SC2730_LDO_VDDCAMIO,
+	SC2730_LDO_VDDWCN,
+	SC2730_LDO_VDDCAMD1,
+	SC2730_LDO_VDDCAMD0,
+	SC2730_LDO_VDDRF1V25,
+	SC2730_LDO_AVDD12,
+	SC2730_LDO_VDDCAMA0,
+	SC2730_LDO_VDDCAMA1,
+	SC2730_LDO_VDDCAMMOT,
+	SC2730_LDO_VDDSIM2,
+	SC2730_LDO_VDDEMMCCORE,
+	SC2730_LDO_VDDSDCORE,
+	SC2730_LDO_VDDSDIO,
+	SC2730_LDO_VDD28,
+	SC2730_LDO_VDDWIFIPA,
+	SC2730_LDO_VDD18_DCXO,
+	SC2730_LDO_VDDUSB33,
+	SC2730_LDO_VDDLDO0,
+	SC2730_LDO_VDDLDO1,
+	SC2730_LDO_VDDLDO2,
+	SC2730_LDO_VDDKPLED,
+};
+
+static const struct regulator_ops sc2730_regu_linear_ops = {
+	.enable = regulator_enable_regmap,
+	.disable = regulator_disable_regmap,
+	.is_enabled = regulator_is_enabled_regmap,
+	.list_voltage = regulator_list_voltage_linear,
+	.get_voltage_sel = regulator_get_voltage_sel_regmap,
+	.set_voltage_sel = regulator_set_voltage_sel_regmap,
+};
+
+#define SC2730_REGU_LINEAR(_id, of_name, en_reg, en_mask, vreg, vmask,	\
+			   vstep, vmin, vmax) {				\
+	.name			= #_id,					\
+	.of_match		= of_name,				\
+	.regulators_node	= "regulators",				\
+	.ops			= &sc2730_regu_linear_ops,		\
+	.type			= REGULATOR_VOLTAGE,			\
+	.id			= SC2730_##_id,				\
+	.owner			= THIS_MODULE,				\
+	.min_uV			= vmin,					\
+	.n_voltages		= ((vmax) - (vmin)) / (vstep) + 1,	\
+	.uV_step		= vstep,				\
+	.enable_is_inverted	= true,					\
+	.enable_val		= 0,					\
+	.enable_reg		= en_reg,				\
+	.enable_mask		= en_mask,				\
+	.vsel_reg		= vreg,					\
+	.vsel_mask		= vmask,				\
+	.linear_min_sel		= 0,					\
+}
+
+static const struct regulator_desc regulators[] = {
+	SC2730_REGU_LINEAR(DCDC_CPU, "dcdc-cpu", SC2730_POWER_PD_SW,
+			   SC2730_DCDC_CPU_PD_MASK, SC2730_DCDC_CPU_VOL,
+			   SC2730_DCDC_CPU_VOL_MASK, 3125, 0, 1596875),
+	SC2730_REGU_LINEAR(DCDC_GPU, "dcdc-gpu", SC2730_POWER_PD_SW,
+			   SC2730_DCDC_GPU_PD_MASK, SC2730_DCDC_GPU_VOL,
+			   SC2730_DCDC_GPU_VOL_MASK, 3125, 0, 1596875),
+	SC2730_REGU_LINEAR(DCDC_CORE, "dcdc-core", SC2730_POWER_PD_SW,
+			   SC2730_DCDC_CORE_PD_MASK, SC2730_DCDC_CORE_VOL,
+			   SC2730_DCDC_CORE_VOL_MASK, 3125, 0, 1596875),
+	SC2730_REGU_LINEAR(DCDC_MODEM, "dcdc-modem", SC2730_POWER_PD_SW,
+			   SC2730_DCDC_MODEM_PD_MASK, SC2730_DCDC_MODEM_VOL,
+			   SC2730_DCDC_MODEM_VOL_MASK, 3125, 0, 1596875),
+	SC2730_REGU_LINEAR(DCDC_MEM, "dcdc-mem", SC2730_POWER_PD_SW,
+			   SC2730_DCDC_MEM_PD_MASK, SC2730_DCDC_MEM_VOL,
+			   SC2730_DCDC_MEM_VOL_MASK, 6250, 0, 1593750),
+	SC2730_REGU_LINEAR(DCDC_MEMQ, "dcdc-memq", SC2730_POWER_PD_SW,
+			   SC2730_DCDC_MEMQ_PD_MASK, SC2730_DCDC_MEMQ_VOL,
+			   SC2730_DCDC_MEMQ_VOL_MASK, 3125, 0, 1596875),
+	SC2730_REGU_LINEAR(DCDC_GEN0, "dcdc-gen0", SC2730_POWER_PD_SW,
+			   SC2730_DCDC_GEN0_PD_MASK, SC2730_DCDC_GEN0_VOL,
+			   SC2730_DCDC_GEN0_VOL_MASK, 9375, 20000, 2410625),
+	SC2730_REGU_LINEAR(DCDC_GEN1, "dcdc-gen1", SC2730_POWER_PD_SW,
+			   SC2730_DCDC_GEN1_PD_MASK, SC2730_DCDC_GEN1_VOL,
+			   SC2730_DCDC_GEN1_VOL_MASK, 6250, 50000, 1643750),
+	SC2730_REGU_LINEAR(DCDC_SRAM, "dcdc-sram", SC2730_POWER_PD_SW,
+			   SC2730_DCDC_SRAM_PD_MASK, SC2730_DCDC_SRAM_VOL,
+			   SC2730_DCDC_SRAM_VOL_MASK, 3125, 0, 1596875),
+	SC2730_REGU_LINEAR(LDO_AVDD18, "ldo-avdd18", SC2730_POWER_PD_SW,
+			   SC2730_LDO_AVDD18_PD_MASK, SC2730_LDO_AVDD18_VOL,
+			   SC2730_LDO_AVDD18_VOL_MASK, 10000, 1175000, 1805000),
+	SC2730_REGU_LINEAR(LDO_VDDRF18, "ldo-vddrf18", SC2730_LDO_VDDRF18_PD,
+			   SC2730_LDO_VDDRF18_PD_MASK, SC2730_LDO_VDDRF18_VOL,
+			   SC2730_LDO_VDDRF18_VOL_MASK, 10000, 1175000, 1805000),
+	SC2730_REGU_LINEAR(LDO_VDDCAMIO, "ldo-vddcamio", SC2730_LDO_VDDCAMIO_PD,
+			   SC2730_LDO_VDDCAMIO_PD_MASK, SC2730_LDO_VDDCAMIO_VOL,
+			   SC2730_LDO_VDDCAMIO_VOL_MASK, 10000, 1200000, 1830000),
+	SC2730_REGU_LINEAR(LDO_VDDWCN, "ldo-vddwcn", SC2730_LDO_VDDWCN_PD,
+			   SC2730_LDO_VDDWCN_PD_MASK, SC2730_LDO_VDDWCN_VOL,
+			   SC2730_LDO_VDDWCN_VOL_MASK, 15000, 900000, 1845000),
+	SC2730_REGU_LINEAR(LDO_VDDCAMD1, "ldo-vddcamd1", SC2730_LDO_VDDCAMD1_PD,
+			   SC2730_LDO_VDDCAMD1_PD_MASK, SC2730_LDO_VDDCAMD1_VOL,
+			   SC2730_LDO_VDDCAMD1_VOL_MASK, 15000, 900000, 1365000),
+	SC2730_REGU_LINEAR(LDO_VDDCAMD0, "ldo-vddcamd0", SC2730_LDO_VDDCAMD0_PD,
+			   SC2730_LDO_VDDCAMD0_PD_MASK, SC2730_LDO_VDDCAMD0_VOL,
+			   SC2730_LDO_VDDCAMD0_VOL_MASK, 15000, 900000, 1365000),
+	SC2730_REGU_LINEAR(LDO_VDDRF1V25, "ldo-vddrf1v25", SC2730_LDO_VDDRF1V25_PD,
+			   SC2730_LDO_VDDRF1V25_PD_MASK, SC2730_LDO_VDDRF1V25_VOL,
+			   SC2730_LDO_VDDRF1V25_VOL_MASK, 15000, 900000, 1365000),
+	SC2730_REGU_LINEAR(LDO_AVDD12, "ldo-avdd12", SC2730_LDO_AVDD12_PD,
+			   SC2730_LDO_AVDD12_PD_MASK, SC2730_LDO_AVDD12_VOL,
+			   SC2730_LDO_AVDD12_VOL_MASK, 15000, 900000, 1365000),
+	SC2730_REGU_LINEAR(LDO_VDDCAMA0, "ldo-vddcama0", SC2730_LDO_VDDCAMA0_PD,
+			   SC2730_LDO_VDDCAMA0_PD_MASK, SC2730_LDO_VDDCAMA0_VOL,
+			   SC2730_LDO_VDDCAMA0_VOL_MASK, 10000, 1200000, 3750000),
+	SC2730_REGU_LINEAR(LDO_VDDCAMA1, "ldo-vddcama1", SC2730_LDO_VDDCAMA1_PD,
+			   SC2730_LDO_VDDCAMA1_PD_MASK, SC2730_LDO_VDDCAMA1_VOL,
+			   SC2730_LDO_VDDCAMA1_VOL_MASK, 10000, 1200000, 3750000),
+	SC2730_REGU_LINEAR(LDO_VDDCAMMOT, "ldo-vddcammot", SC2730_LDO_VDDCAMMOT_PD,
+			   SC2730_LDO_VDDCAMMOT_PD_MASK, SC2730_LDO_VDDCAMMOT_VOL,
+			   SC2730_LDO_VDDCAMMOT_VOL_MASK, 10000, 1200000, 3750000),
+	SC2730_REGU_LINEAR(LDO_VDDSIM2, "ldo-vddsim2", SC2730_LDO_VDDSIM2_PD,
+			   SC2730_LDO_VDDSIM2_PD_MASK, SC2730_LDO_VDDSIM2_VOL,
+			   SC2730_LDO_VDDSIM2_VOL_MASK, 10000, 1200000, 3750000),
+	SC2730_REGU_LINEAR(LDO_VDDEMMCCORE, "ldo-vddemmccore", SC2730_LDO_VDDEMMCCORE_PD,
+			   SC2730_LDO_VDDEMMCCORE_PD_MASK, SC2730_LDO_VDDEMMCCORE_VOL,
+			   SC2730_LDO_VDDEMMCCORE_VOL_MASK, 10000, 1200000, 3750000),
+	SC2730_REGU_LINEAR(LDO_VDDSDCORE, "ldo-vddsdcore", SC2730_LDO_VDDSDCORE_PD,
+			   SC2730_LDO_VDDSDCORE_PD_MASK, SC2730_LDO_VDDSDCORE_VOL,
+			   SC2730_LDO_VDDSDCORE_VOL_MASK, 10000, 1200000, 3750000),
+	SC2730_REGU_LINEAR(LDO_VDDSDIO, "ldo-vddsdio", SC2730_LDO_VDDSDIO_PD,
+			   SC2730_LDO_VDDSDIO_PD_MASK, SC2730_LDO_VDDSDIO_VOL,
+			   SC2730_LDO_VDDSDIO_VOL_MASK, 10000, 1200000, 3750000),
+	SC2730_REGU_LINEAR(LDO_VDD28, "ldo-vdd28", SC2730_POWER_PD_SW,
+			   SC2730_LDO_VDD28_PD_MASK, SC2730_LDO_VDD28_VOL,
+			   SC2730_LDO_VDD28_VOL_MASK, 10000, 1200000, 3750000),
+	SC2730_REGU_LINEAR(LDO_VDDWIFIPA, "ldo-vddwifipa", SC2730_LDO_VDDWIFIPA_PD,
+			   SC2730_LDO_VDDWIFIPA_PD_MASK, SC2730_LDO_VDDWIFIPA_VOL,
+			   SC2730_LDO_VDDWIFIPA_VOL_MASK, 10000, 1200000, 3750000),
+	SC2730_REGU_LINEAR(LDO_VDD18_DCXO, "ldo-vdd18-dcxo", SC2730_POWER_PD_SW,
+			   SC2730_LDO_VDD18_DCXO_PD_MASK, SC2730_LDO_VDD18_DCXO_VOL,
+			   SC2730_LDO_VDD18_DCXO_VOL_MASK, 10000, 1200000, 3750000),
+	SC2730_REGU_LINEAR(LDO_VDDUSB33, "ldo-vddusb33", SC2730_LDO_VDDUSB33_PD,
+			   SC2730_LDO_VDDUSB33_PD_MASK, SC2730_LDO_VDDUSB33_VOL,
+			   SC2730_LDO_VDDUSB33_VOL_MASK, 10000, 1200000, 3750000),
+	SC2730_REGU_LINEAR(LDO_VDDLDO0, "ldo-vddldo0", SC2730_LDO_VDDLDO0_PD,
+			   SC2730_LDO_VDDLDO0_PD_MASK, SC2730_LDO_VDDLDO0_VOL,
+			   SC2730_LDO_VDDLDO0_VOL_MASK, 10000, 1200000, 3750000),
+	SC2730_REGU_LINEAR(LDO_VDDLDO1, "ldo-vddldo1", SC2730_LDO_VDDLDO1_PD,
+			   SC2730_LDO_VDDLDO1_PD_MASK, SC2730_LDO_VDDLDO1_VOL,
+			   SC2730_LDO_VDDLDO1_VOL_MASK, 10000, 1200000, 3750000),
+	SC2730_REGU_LINEAR(LDO_VDDLDO2, "ldo-vddldo2", SC2730_LDO_VDDLDO2_PD,
+			   SC2730_LDO_VDDLDO2_PD_MASK, SC2730_LDO_VDDLDO2_VOL,
+			   SC2730_LDO_VDDLDO2_VOL_MASK, 10000, 1200000, 3750000),
+	SC2730_REGU_LINEAR(LDO_VDDKPLED, "ldo-vddkpled", SC2730_LDO_VDDKPLED_PD,
+			   SC2730_LDO_VDDKPLED_PD_MASK, SC2730_LDO_VDDKPLED_VOL,
+			   SC2730_LDO_VDDKPLED_VOL_MASK, 10000, 1200000, 3750000),
+};
+
+static int sc2730_regulator_unlock(struct regmap *regmap)
+{
+	return regmap_write(regmap, SC2730_PWR_WR_PROT, SC2730_WR_UNLOCK_VALUE);
+}
+
+static int sc2730_regulator_probe(struct platform_device *pdev)
+{
+	int i, ret;
+	struct regmap *regmap;
+	struct regulator_config config = { };
+	struct regulator_dev *rdev;
+
+	regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!regmap)
+		return dev_err_probe(&pdev->dev, -ENODEV, "failed to get regmap\n");
+
+	ret = sc2730_regulator_unlock(regmap);
+	if (ret)
+		return dev_err_probe(&pdev->dev, ret, "failed to release regulator lock\n");
+
+	config.dev = pdev->dev.parent;
+	config.regmap = regmap;
+
+	for (i = 0; i < ARRAY_SIZE(regulators); i++) {
+		rdev = devm_regulator_register(&pdev->dev, &regulators[i], &config);
+		if (IS_ERR(rdev)) {
+			return dev_err_probe(&pdev->dev, PTR_ERR(rdev),
+					     "failed to register regulator %s\n",
+					     regulators[i].name);
+		}
+	}
+
+	return 0;
+}
+
+static const struct platform_device_id sc2730_regulator_id_table[] = {
+	{ .name = "sc2730-regulator" },
+	{ }
+};
+MODULE_DEVICE_TABLE(platform, sc2730_regulator_id_table);
+
+static struct platform_driver sc2730_regulator_driver = {
+	.driver = {
+		.name = "sc2730-regulator",
+		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
+	},
+	.probe = sc2730_regulator_probe,
+	.id_table = sc2730_regulator_id_table,
+};
+
+module_platform_driver(sc2730_regulator_driver);
+
+MODULE_AUTHOR("Zhongfa Wang <zhongfa.wang@unisoc.com>");
+MODULE_DESCRIPTION("Spreadtrum SC2730 regulator driver");
+MODULE_LICENSE("GPL");

-- 
2.51.0


^ permalink raw reply related

* Re: [PATCH 1/5] riscv: dts: spacemit: set console baud rate on K3 Pico-ITX board
From: Guodong Xu @ 2026-06-20  9:03 UTC (permalink / raw)
  To: Aurelien Jarno
  Cc: linux-kernel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Yixun Lan, Paul Walmsley, Palmer Dabbelt, Albert Ou,
	Alexandre Ghiti, Guodong Xu,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list:RISC-V SPACEMIT SoC Support,
	open list:RISC-V SPACEMIT SoC Support
In-Reply-To: <20260620062238.3199108-2-aurelien@aurel32.net>

Hi Aurelien,

On 2026-06-20 08:22, Aurelien Jarno wrote:
>Because the default console's baud rate is not set, defconfig kernels do
>not have any serial output on this platform. Set the baud rate to
>115200, matching what is used by U-Boot etc on this platform.
>
>Fixes: 7a6131804986 ("riscv: dts: spacemit: add K3 Pico-ITX board support")
>Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>

Thanks for the patch.

Reviewed-by: Guodong Xu <docular.xu@gmail.com>

>---
> arch/riscv/boot/dts/spacemit/k3-pico-itx.dts | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
>diff --git a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts
>index b89c1521e6649..509cebc0c9568 100644
>--- a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts
>+++ b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts
>@@ -18,7 +18,7 @@ aliases {
> 	};
>
> 	chosen {
>-		stdout-path = "serial0";
>+		stdout-path = "serial0:115200n8";
> 	};
>
> 	memory@100000000 {
>-- 
>2.53.0
>
>
>_______________________________________________
>linux-riscv mailing list
>linux-riscv@lists.infradead.org
>http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply

* Re: [PATCH v6 2/3] mfd: sprd-sc27xx: Add SC2730 regulator cell
From: sashiko-bot @ 2026-06-20  9:04 UTC (permalink / raw)
  To: Otto Pflüger; +Cc: robh, devicetree, conor+dt
In-Reply-To: <20260620-sc2730-regulators-v6-2-bbd2db395231@abscue.de>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Pre-existing issues:
- [High] The preexisting 'sc2731-regulator' driver fails to bind and cannot parse Device Tree properties.
- [High] The custom SPI read function `sprd_pmic_spi_read()` is fundamentally broken, reading corrupted data and failing to transmit register addresses.
--

commit 64895ca35a5a54c5838030a81a11a1b90576b82b
Author: Otto Pflüger <otto.pflueger@abscue.de>
Subject: mfd: sprd-sc27xx: Add SC2730 regulator cell

Add an MFD cell to register the SC2730 PMIC's regulators.

> diff --git a/drivers/mfd/sprd-sc27xx-spi.c b/drivers/mfd/sprd-sc27xx-spi.c
> index aa052f6466231..214bcbef0c271 100644
> --- a/drivers/mfd/sprd-sc27xx-spi.c
> +++ b/drivers/mfd/sprd-sc27xx-spi.c
> @@ -61,6 +61,7 @@ static const struct mfd_cell sc2730_devices[] = {
>  	MFD_CELL_OF("sc2730-efuse", NULL, NULL, 0, 0, "sprd,sc2730-efuse"),
>  	MFD_CELL_OF("sc2730-eic", NULL, NULL, 0, 0, "sprd,sc2730-eic"),
>  	MFD_CELL_OF("sc2730-fgu", NULL, NULL, 0, 0, "sprd,sc2730-fgu"),
> +	MFD_CELL_NAME("sc2730-regulator"),
>  	MFD_CELL_OF("sc2730-rtc", NULL, NULL, 0, 0, "sprd,sc2730-rtc"),
>  	MFD_CELL_OF("sc2730-vibrator", NULL, NULL, 0, 0, "sprd,sc2730-vibrator"),
>  };

While reviewing this patch, I noticed a couple of pre-existing regressions in
this file:

drivers/mfd/sprd-sc27xx-spi.c:sc2731_devices() {
    ...
	MFD_CELL_OF("sc2731-fgu", NULL, NULL, 0, 0, "sprd,sc2731-fgu"),
	MFD_CELL_NAME("sc2731-poweroff"),
	MFD_CELL_NAME("sc2731-regulator"),

[Severity: High]
This is a pre-existing issue, but could the sc2731-regulator MFD cell fail to
bind to its platform driver due to a name mismatch?

The platform driver in drivers/regulator/sc2731-regulator.c is named
"sc27xx-regulator" and lacks an id_table, which means the platform bus will
not match them and the driver will remain unbound.

Furthermore, even if it did bind, sc2731_regulator_probe() sets
config.dev = &pdev->dev. Because MFD_CELL_NAME() creates the device without
an of_node, pdev->dev.of_node is NULL. Does this prevent the regulator
core from parsing any DT constraints or bindings, breaking regulator
initialization?
    ...
}

drivers/mfd/sprd-sc27xx-spi.c:sprd_pmic_spi_read() {
    ...
	/* Copy address to read from into first element of SPI buffer. */
	memcpy(rx_buf, reg, sizeof(u32));
	ret = spi_read(spi, rx_buf, 1);

[Severity: High]
This isn't a bug introduced by this patch, but is the custom SPI read
function sprd_pmic_spi_read() fundamentally broken?

It attempts to read a 4-byte PMIC register by copying the 4-byte register
address into rx_buf and then calling spi_read(spi, rx_buf, 1). The core
spi_read() API performs a receive-only transfer (tx_buf is NULL), meaning
the register address is never actually sent to the SPI slave.

Additionally, the length is set to 1 byte instead of 4, so only 1 byte is
clocked in from the slave.

	if (ret < 0)
		return ret;

	memcpy(val, rx_buf, val_size);

Finally, copying 4 bytes back to the caller returns the 1 byte read from the
bus concatenated with the remaining 3 bytes of the register address that were
initially placed into rx_buf. Does this silently return garbage data for
every register read?
    ...
}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260620-sc2730-regulators-v6-0-bbd2db395231@abscue.de?part=2

^ permalink raw reply

* Re: [PATCH v6 1/3] regulator: dt-bindings: Add Unisoc SC2730 PMIC
From: sashiko-bot @ 2026-06-20  9:05 UTC (permalink / raw)
  To: Otto Pflüger; +Cc: robh, devicetree, conor+dt
In-Reply-To: <20260620-sc2730-regulators-v6-1-bbd2db395231@abscue.de>

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The YAML binding schema omits the `properties` block defining the `compatible` property, causing schema validation to fail if device trees include it and allowing incomplete nodes to incorrectly pass validation.
--

commit 8c5bcc12a2e0793aa168b5a819709950e4b101f4
Author: Otto Pflüger <otto.pflueger@abscue.de>

regulator: dt-bindings: Add Unisoc SC2730 PMIC

This commit adds the device tree binding schema for the regulators found
in the Spreadtrum/Unisoc SC2730 PMIC.

> diff --git a/Documentation/devicetree/bindings/regulator/sprd,sc2730-regulator.yaml b/Documentation/devicetree/bindings/regulator/sprd,sc2730-regulator.yaml
> new file mode 100644
> index 0000000000000..ab945c46b08e0
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/regulator/sprd,sc2730-regulator.yaml

[ ... ]

>  "^ldo-vddrf(1v25|18)$":
>    type: object
>    $ref: regulator.yaml#
>    unevaluatedProperties: false
>
> additionalProperties: false

[Severity: Medium]
Should there be a properties block defining the compatible string here?

Because this schema specifies additionalProperties: false but omits a
properties block and a required list for the compatible string, it acts
as a catch-all that can successfully validate an empty regulators node.

Furthermore, if a device tree author specifies a compatible string (such
as compatible = "sprd,sc2730-regulator"), validation will falsely fail
due to the additionalProperties constraint. Could a properties block and
a required list be added to explicitly allow and require the compatible
property?

> ...

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260620-sc2730-regulators-v6-0-bbd2db395231@abscue.de?part=1

^ permalink raw reply

* Re: [PATCH v10 2/2] media: i2c: add Himax HM1246 image sensor driver
From: Sakari Ailus @ 2026-06-20  9:24 UTC (permalink / raw)
  To: Matthias Fend
  Cc: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Hans Verkuil, Hans de Goede, Ricardo Ribalda,
	André Apitzsch, Tarang Raval, Andy Shevchenko,
	Benjamin Mugnier, Sylvain Petinot, Dongcheng Yan,
	Bryan O'Donoghue, Alan Stern, Jingjing Xiong,
	Heimir Thor Sverrisson, Mehdi Djait, Vladimir Zapolskiy,
	Laurent Pinchart, Hardevsinh Palaniya, Svyatoslav Ryhel,
	Philipp Zabel, Hans Verkuil, Hans de Goede, Xiaolei Wang,
	Walter Werner Schneider, Kate Hsuan, Bartosz Golaszewski,
	Miguel Vadillo, linux-media, devicetree, linux-kernel, Hao Yao,
	Himanshu Bhavani
In-Reply-To: <20260619-hm1246-v10-2-d88e431a6c11@emfend.at>

Hi Matthias,

On Fri, Jun 19, 2026 at 02:28:55PM +0200, Matthias Fend wrote:
> Add a V4L2 sub-device driver for Himax HM1246 image sensor.
> 
> The Himax HM1246-AWD is a 1/3.7-Inch CMOS image sensor SoC with an active
> array size of 1296 x 976. It is programmable through an I2C interface and
> connected via parallel bus.
> 
> The sensor has an internal ISP with a complete image processing pipeline
> including control loops. However, this driver uses the sensor in raw mode
> and the entire ISP is bypassed.

Is there possibly a need to support the ISP later on? Some drivers such as
the mt9m114 expose a separate sub-device for it and adding one would be a
UAPI change. Is there more information on the ISP in the hm1246?

A few more minor comments below.

> 
> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> Signed-off-by: Matthias Fend <matthias.fend@emfend.at>
> ---
>  MAINTAINERS                |    1 +
>  drivers/media/i2c/Kconfig  |   10 +
>  drivers/media/i2c/Makefile |    1 +
>  drivers/media/i2c/hm1246.c | 1291 ++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 1303 insertions(+)
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index c320e7e9a2bd88319945bfe1202bc891d35f0f7c..ca13a5b21306e921e077e2548f6242041c6ee24c 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -11558,6 +11558,7 @@ M:	Matthias Fend <matthias.fend@emfend.at>
>  L:	linux-media@vger.kernel.org
>  S:	Maintained
>  F:	Documentation/devicetree/bindings/media/i2c/himax,hm1246.yaml
> +F:	drivers/media/i2c/hm1246.c
>  
>  HIMAX HX83112B TOUCHSCREEN SUPPORT
>  M:	Job Noorman <job@noorman.info>
> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> index 5d173e0ecf424f2f204f8d426be818e44357f8e4..52398bad32a0989c40097524ee9dde04e76813b8 100644
> --- a/drivers/media/i2c/Kconfig
> +++ b/drivers/media/i2c/Kconfig
> @@ -137,6 +137,16 @@ config VIDEO_HI847
>            To compile this driver as a module, choose M here: the
>            module will be called hi847.
>  
> +config VIDEO_HM1246
> +	tristate "Himax HM1246 sensor support"
> +	select V4L2_CCI_I2C
> +	help
> +	  This is a Video4Linux2 sensor driver for the Himax
> +	  HM1246 camera.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called hm1246.
> +
>  config VIDEO_IMX111
>  	tristate "Sony IMX111 sensor support"
>  	select V4L2_CCI_I2C
> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> index e45359efe0e41e13e3c0869e5ead7d6cf4aca3a7..df420ff4e1d6304ef62f9cd84c8ddb9e2db30a11 100644
> --- a/drivers/media/i2c/Makefile
> +++ b/drivers/media/i2c/Makefile
> @@ -45,6 +45,7 @@ obj-$(CONFIG_VIDEO_GC2145) += gc2145.o
>  obj-$(CONFIG_VIDEO_HI556) += hi556.o
>  obj-$(CONFIG_VIDEO_HI846) += hi846.o
>  obj-$(CONFIG_VIDEO_HI847) += hi847.o
> +obj-$(CONFIG_VIDEO_HM1246) += hm1246.o
>  obj-$(CONFIG_VIDEO_I2C) += video-i2c.o
>  obj-$(CONFIG_VIDEO_IMX111) += imx111.o
>  obj-$(CONFIG_VIDEO_IMX208) += imx208.o
> diff --git a/drivers/media/i2c/hm1246.c b/drivers/media/i2c/hm1246.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..ba120967814f3b2b4a5d2d6b1b6e4f5e5139944f
> --- /dev/null
> +++ b/drivers/media/i2c/hm1246.c
> @@ -0,0 +1,1291 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Driver for Himax HM1246 image sensor
> + *
> + * Copyright 2026 Matthias Fend <matthias.fend@emfend.at>
> + */
> +
> +#include <linux/array_size.h>
> +#include <linux/bitops.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/i2c.h>
> +#include <linux/limits.h>
> +#include <linux/math.h>
> +#include <linux/math64.h>
> +#include <linux/module.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/property.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/reset.h>
> +#include <linux/types.h>
> +#include <linux/units.h>
> +#include <media/media-entity.h>
> +#include <media/v4l2-async.h>
> +#include <media/v4l2-cci.h>
> +#include <media/v4l2-common.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-fwnode.h>
> +#include <media/v4l2-subdev.h>
> +
> +/* Status registers */
> +#define HM1246_MODEL_ID_REG		 CCI_REG16(0x0000)
> +
> +/* General setup registers */
> +#define HM1246_MODE_SELECT_REG		 CCI_REG8(0x0100)
> +#define HM1246_MODE_SELECT_STANDBY	 0x00
> +#define HM1246_MODE_SELECT_STREAM	 0x01
> +#define HM1246_MODE_SELECT_STOP		 0x02
> +#define HM1246_IMAGE_ORIENTATION_REG	 CCI_REG8(0x0101)
> +#define HM1246_IMAGE_ORIENTATION_VFLIP	 BIT(1)
> +#define HM1246_IMAGE_ORIENTATION_HFLIP	 BIT(0)
> +#define HM1246_CMU_UPDATE_REG		 CCI_REG8(0x0104)
> +
> +/* Output setup registers */
> +#define HM1246_COARSE_INTG_REG		 CCI_REG16(0x0202)
> +#define HM1246_ANALOG_GLOBAL_GAIN_REG	 CCI_REG8(0x0205)
> +
> +/* Clock setup registers */
> +#define HM1246_PLL1CFG_REG		 CCI_REG8(0x0303)
> +#define HM1246_PLL1CFG_MULTIPLIER(x)	 (((x) & 0xff) << 0)
> +#define HM1246_PLL2CFG_REG		 CCI_REG8(0x0305)
> +#define HM1246_PLL2CFG_PRE_DIV(x)	 (((x) & 0x1f) << 1)
> +#define HM1246_PLL2CFG_MULTIPLIER(x)	 (((x) & 0x01) << 0)
> +#define HM1246_PLL3CFG_REG		 CCI_REG8(0x0307)
> +#define HM1246_PLL3CFG_POST_DIV(x)	 (((x) & 0x3) << 6)
> +#define HM1246_PLL3CFG_SYSCLK_DIV(x)	 (((x) & 0x3) << 4)
> +#define HM1246_PLL3CFG_PCLK_DIV(x)	 (((x) & 0x7) << 0)
> +
> +/* Frame timing registers */
> +#define HM1246_FRAME_LENGTH_LINES_REG	 CCI_REG16(0x0340)
> +#define HM1246_LINE_LENGTH_PCK_REG	 CCI_REG16(0x0342)
> +
> +/* Image size registers */
> +#define HM1246_X_ADDR_START_REG		 CCI_REG16(0x0344)
> +#define HM1246_Y_ADDR_START_REG		 CCI_REG16(0x0346)
> +#define HM1246_X_ADDR_END_REG		 CCI_REG16(0x0348)
> +#define HM1246_Y_ADDR_END_REG		 CCI_REG16(0x034a)
> +#define HM1246_X_LA_START_REG		 CCI_REG16(0x0351)
> +#define HM1246_X_LA_END_REG		 CCI_REG16(0x0353)
> +#define HM1246_Y_LA_START_REG		 CCI_REG16(0x0355)
> +#define HM1246_Y_LA_END_REG		 CCI_REG16(0x0357)
> +
> +/* Test pattern registers */
> +#define HM1246_TEST_PATTERN_MODE_REG	 CCI_REG8(0x0601)
> +#define HM1246_TEST_PATTERN_MODE_MODE(x) (((x) & 0xf) << 4)
> +#define HM1246_TEST_PATTERN_MODE_ENABLE	 BIT(0)
> +#define HM1246_TEST_DATA_BLUE_REG	 CCI_REG16(0x0602)
> +#define HM1246_TEST_DATA_GB_REG		 CCI_REG16(0x0604)
> +#define HM1246_TEST_DATA_RED_REG	 CCI_REG16(0x0606)
> +#define HM1246_TEST_DATA_GR_REG		 CCI_REG16(0x0608)
> +
> +/* SBC registers */
> +#define HM1246_SBC_BOOT_REF2_REG	 CCI_REG8(0x2001)
> +#define HM1246_SBC_BOOT_REF2_PLL_LOCK	 BIT(4)
> +#define HM1246_SBC_CTRL_REG		 CCI_REG8(0x2003)
> +#define HM1246_SBC_CTRL_PLL_EN		 BIT(0)
> +
> +/* System registers */
> +#define HM1246_OUTPUT_PRT_CTRL_REG	 CCI_REG8(0x2f02)
> +#define HM1246_POLARITY_CTRL_REG	 CCI_REG8(0x2f20)
> +#define HM1246_POLARITY_CTRL_HSYNC	 BIT(7)
> +#define HM1246_POLARITY_CTRL_VSYNC	 BIT(6)
> +#define HM1246_PCLK_CTRL_REG		 CCI_REG8(0x2f24)
> +#define HM1246_PCLK_CTRL_POL		 BIT(3)
> +
> +/* Digital window control & parameter registers */
> +#define HM1246_DWIN_XOFFSET_REG		 CCI_REG16(0xd5e4)
> +#define HM1246_DWIN_XSIZE_REG		 CCI_REG16(0xd5e6)
> +#define HM1246_DWIN_YOFFSET_REG		 CCI_REG16(0xd5e8)
> +#define HM1246_DWIN_YSIZE_REG		 CCI_REG16(0xd5ea)
> +
> +#define HM1246_MODEL_ID			 0x1245
> +
> +#define HM1246_NATIVE_WIDTH		 1296
> +#define HM1246_NATIVE_HEIGHT		 976
> +
> +#define HM1246_VTS_MAX			 65535
> +
> +#define HM1246_COARSE_INTG_MARGIN	 2
> +#define HM1246_COARSE_INTG_MIN		 4
> +#define HM1246_COARSE_INTG_STEP		 1
> +
> +#define HM1246_ANALOG_GLOBAL_GAIN_MIN	 0x00
> +#define HM1246_ANALOG_GLOBAL_GAIN_MAX	 0xe8
> +#define HM1246_ANALOG_GLOBAL_GAIN_STEP	 0x01
> +
> +#define HM1246_XCLK_MIN			 (6 * HZ_PER_MHZ)
> +#define HM1246_XCLK_MAX			 (27 * HZ_PER_MHZ)
> +
> +#define HM1246_PCLK_MIN			 (8 * HZ_PER_MHZ)
> +#define HM1246_PCLK_MAX			 (96 * HZ_PER_MHZ)
> +
> +#define HM1246_PLL_VCO_MIN		 (360 * HZ_PER_MHZ)
> +#define HM1246_PLL_VCO_MAX		 (680 * HZ_PER_MHZ)
> +
> +#define HM1246_PLL_INCLK_MIN		 (1000 * HZ_PER_KHZ)
> +#define HM1246_PLL_INCLK_MAX		 (2500 * HZ_PER_KHZ)
> +
> +#define HM1246_PLL_MULTI_L_MIN		 1
> +#define HM1246_PLL_MULTI_L_MAX		 256
> +
> +#define HM1246_PLL_MULTI_H_MIN		 2
> +#define HM1246_PLL_MULTI_H_MAX		 3
> +
> +#define HM1246_PLL_MULTI_MIN \
> +	(HM1246_PLL_MULTI_H_MIN * HM1246_PLL_MULTI_L_MIN)
> +#define HM1246_PLL_MULTI_MAX \
> +	(HM1246_PLL_MULTI_H_MAX * HM1246_PLL_MULTI_L_MAX)
> +
> +static const char *const hm1246_test_pattern_menu[] = {
> +	"Disabled",
> +	"Checkboard",

Checkerboard?

> +	"Ramp",
> +	"Moving ones",
> +	"Blending color bars",
> +	"Color bars",
> +	"Solid white",
> +	"Solid black",
> +	"Solid red",
> +	"Solid green",
> +	"Solid blue",
> +};
> +
> +static const char *const hm1246_supply_names[] = {
> +	"avdd",
> +	"iovdd",
> +	"dvdd",
> +};
> +
> +struct hm1246 {
> +	struct v4l2_subdev sd;
> +	struct media_pad pad;
> +	struct device *dev;
> +
> +	struct regulator_bulk_data supplies[ARRAY_SIZE(hm1246_supply_names)];
> +	struct clk *xclk;
> +	unsigned long xclk_freq;
> +	struct reset_control *reset;
> +	unsigned int mbus_flags;
> +	s64 link_frequency;
> +
> +	struct v4l2_ctrl_handler ctrls;
> +	struct v4l2_ctrl *exposure_ctrl;
> +	struct v4l2_ctrl *hflip_ctrl;
> +	struct v4l2_ctrl *vflip_ctrl;
> +
> +	struct regmap *regmap;
> +
> +	bool identified;
> +};
> +
> +static const struct cci_reg_sequence mode_1296x976_raw[] = {
> +	{ HM1246_X_LA_START_REG, 60 },
> +	{ HM1246_X_LA_END_REG, 1355 },
> +	{ HM1246_Y_LA_START_REG, 0 },
> +	{ HM1246_Y_LA_END_REG, 975 },
> +	{ HM1246_OUTPUT_PRT_CTRL_REG, 0x20 },
> +	{ CCI_REG8(0x300a), 0x01 },
> +	{ CCI_REG8(0x300b), 0x00 },
> +	{ CCI_REG8(0x50f5), 0x01 },
> +	{ CCI_REG8(0x50dd), 0x00 },
> +	{ CCI_REG8(0x50a1), 0x02 },
> +	{ CCI_REG8(0x50aa), 0x1c },
> +	{ CCI_REG8(0x50ac), 0xdd },
> +	{ CCI_REG8(0x50ad), 0x08 },
> +	{ CCI_REG8(0x50ab), 0x04 },
> +	{ CCI_REG8(0x50a0), 0x40 },
> +	{ CCI_REG8(0x50a2), 0x12 },
> +	{ CCI_REG8(0x50ae), 0x30 },
> +	{ CCI_REG8(0x50b3), 0x04 },
> +	{ CCI_REG8(0x5204), 0x40 },
> +	{ CCI_REG8(0x5208), 0x55 },
> +	{ CCI_REG8(0x520b), 0x05 },
> +	{ CCI_REG8(0x520d), 0x40 },
> +	{ CCI_REG8(0x5214), 0x18 },
> +	{ CCI_REG8(0x5215), 0x0f },
> +	{ CCI_REG8(0x5217), 0x01 },
> +	{ CCI_REG8(0x5218), 0x07 },
> +	{ CCI_REG8(0x5219), 0x01 },
> +	{ CCI_REG8(0x521a), 0x50 },
> +	{ CCI_REG8(0x521b), 0x24 },
> +	{ CCI_REG8(0x5232), 0x01 },
> +	{ CCI_REG8(0x5220), 0x11 },
> +	{ CCI_REG8(0x5227), 0x01 },
> +	{ CCI_REG8(0x5106), 0xc1 },
> +	{ CCI_REG8(0x5115), 0xc0 },
> +	{ CCI_REG8(0x5116), 0xc1 },
> +	{ CCI_REG8(0x5138), 0x40 },
> +	{ CCI_REG8(0x5139), 0x60 },
> +	{ CCI_REG8(0x513a), 0x80 },
> +	{ CCI_REG8(0x513b), 0xa0 },
> +	{ CCI_REG8(0x513c), 0xa1 },
> +	{ CCI_REG8(0x513d), 0xa2 },
> +	{ CCI_REG8(0x513e), 0xa3 },
> +	{ CCI_REG8(0x5140), 0x40 },
> +	{ CCI_REG8(0x5141), 0x60 },
> +	{ CCI_REG8(0x5142), 0x80 },
> +	{ CCI_REG8(0x5143), 0x81 },
> +	{ CCI_REG8(0x5144), 0x82 },
> +	{ CCI_REG8(0x5145), 0x83 },
> +	{ CCI_REG8(0x5146), 0x93 },
> +	{ CCI_REG8(0x51c1), 0xc3 },
> +	{ CCI_REG8(0x51c5), 0xc3 },
> +	{ CCI_REG8(0x51c9), 0xc3 },
> +	{ CCI_REG8(0x51cd), 0xc2 },
> +	{ CCI_REG8(0x51d1), 0xc1 },
> +	{ CCI_REG8(0x51d5), 0xc1 },
> +	{ CCI_REG8(0x51d9), 0x81 },
> +	{ CCI_REG8(0x51dd), 0x81 },
> +	{ CCI_REG8(0x51c2), 0x49 },
> +	{ CCI_REG8(0x51c6), 0x49 },
> +	{ CCI_REG8(0x51ca), 0x49 },
> +	{ CCI_REG8(0x51ce), 0x49 },
> +	{ CCI_REG8(0x51d2), 0x49 },
> +	{ CCI_REG8(0x51d6), 0x59 },
> +	{ CCI_REG8(0x51da), 0x59 },
> +	{ CCI_REG8(0x51de), 0x59 },
> +	{ CCI_REG8(0x51c3), 0x20 },
> +	{ CCI_REG8(0x51c7), 0x38 },
> +	{ CCI_REG8(0x51cb), 0x21 },
> +	{ CCI_REG8(0x51cf), 0x11 },
> +	{ CCI_REG8(0x51d3), 0x11 },
> +	{ CCI_REG8(0x51d7), 0x13 },
> +	{ CCI_REG8(0x51db), 0x13 },
> +	{ CCI_REG8(0x51df), 0x13 },
> +	{ CCI_REG8(0x51e0), 0x03 },
> +	{ CCI_REG8(0x51e2), 0x03 },
> +	{ CCI_REG8(0x51f0), 0x42 },
> +	{ CCI_REG8(0x51f1), 0x40 },
> +	{ CCI_REG8(0x51f2), 0x4a },
> +	{ CCI_REG8(0x51f3), 0x48 },
> +	{ CCI_REG8(0x5015), 0x73 },
> +	{ CCI_REG8(0x504a), 0x04 },
> +	{ CCI_REG8(0x5044), 0x07 },
> +	{ CCI_REG8(0x5040), 0x03 },
> +	{ CCI_REG8(0x5135), 0xc4 },
> +	{ CCI_REG8(0x5136), 0xc5 },
> +	{ CCI_REG8(0x5166), 0xc4 },
> +	{ CCI_REG8(0x5196), 0xc4 },
> +	{ CCI_REG8(0x51c0), 0x10 },
> +	{ CCI_REG8(0x51c4), 0x10 },
> +	{ CCI_REG8(0x51c8), 0xa0 },
> +	{ CCI_REG8(0x51cc), 0xa0 },
> +	{ CCI_REG8(0x51d0), 0xa1 },
> +	{ CCI_REG8(0x51d4), 0xa5 },
> +	{ CCI_REG8(0x51d8), 0xa5 },
> +	{ CCI_REG8(0x51dc), 0xa5 },
> +	{ CCI_REG8(0x5200), 0xe4 },
> +	{ CCI_REG8(0x5209), 0x04 },
> +	{ CCI_REG8(0x301b), 0x01 },
> +	{ CCI_REG8(0x3130), 0x01 },
> +	{ CCI_REG8(0x5013), 0x07 },
> +	{ CCI_REG8(0x5016), 0x01 },
> +	{ CCI_REG8(0x501d), 0x50 },
> +	{ CCI_REG8(0x0350), 0xfe },
> +	{ CCI_REG8(0x2f03), 0x15 },
> +	{ CCI_REG8(0xd380), 0x00 },
> +	{ CCI_REG8(0x3047), 0x7f },
> +	{ CCI_REG8(0x304d), 0x34 },
> +	{ CCI_REG8(0x3041), 0x4b },
> +	{ CCI_REG8(0x3042), 0x2d },
> +	{ CCI_REG8(0x3056), 0x64 },
> +	{ CCI_REG8(0x3059), 0x1e },
> +	{ CCI_REG8(0x305e), 0x10 },
> +	{ CCI_REG8(0x305f), 0x10 },
> +	{ CCI_REG8(0x306d), 0x10 },
> +	{ CCI_REG8(0x306e), 0x0c },
> +	{ CCI_REG8(0x3064), 0x50 },
> +	{ CCI_REG8(0x3067), 0x78 },
> +	{ CCI_REG8(0x3068), 0x4b },
> +	{ CCI_REG8(0x306a), 0x78 },
> +	{ CCI_REG8(0x306b), 0x4b },
> +	{ CCI_REG8(0xd442), 0x3d },
> +	{ CCI_REG8(0xd443), 0x06 },
> +	{ CCI_REG8(0xd440), 0x63 },
> +	{ CCI_REG8(0xd446), 0xb0 },
> +	{ CCI_REG8(0xd447), 0x60 },
> +	{ CCI_REG8(0xd448), 0x48 },
> +	{ CCI_REG8(0xd449), 0x30 },
> +	{ CCI_REG8(0xd44a), 0x18 },
> +	{ CCI_REG8(0xd360), 0x03 },
> +	{ CCI_REG8(0x30ac), 0x10 },
> +	{ CCI_REG8(0x30ad), 0x10 },
> +	{ CCI_REG8(0x30ae), 0x10 },
> +	{ CCI_REG8(0x3040), 0x0b },
> +	{ CCI_REG8(0x2002), 0x00 },
> +	{ CCI_REG8(0x2000), 0x08 },
> +};
> +
> +struct hm1246_reg_list {
> +	u32 num_of_regs;
> +	const struct cci_reg_sequence *regs;
> +};
> +
> +struct hm1246_mode {
> +	u32 codes[4];
> +	u32 clocks_per_pixel;
> +	struct v4l2_rect rect;
> +	u32 hts;
> +	u32 vts_min;
> +	const struct hm1246_reg_list reg_list;
> +};
> +
> +#define FLIP_FORMAT_INDEX(v, h) ((v ? 2 : 0) | (h ? 1 : 0))
> +
> +/* Get the format code of the mode considering current flip setting. */
> +static u32 hm1246_get_format_code(struct hm1246 *hm1246,
> +				  const struct hm1246_mode *hm1246_mode)
> +{
> +	return hm1246_mode->codes[FLIP_FORMAT_INDEX(hm1246->vflip_ctrl->val,
> +						    hm1246->hflip_ctrl->val)];
> +}
> +
> +static const struct hm1246_mode hm1246_modes[] = {
> +	{
> +		.codes = {
> +			[FLIP_FORMAT_INDEX(0, 0)] = MEDIA_BUS_FMT_SBGGR10_1X10,
> +			[FLIP_FORMAT_INDEX(0, 1)] = MEDIA_BUS_FMT_SGBRG10_1X10,
> +			[FLIP_FORMAT_INDEX(1, 0)] = MEDIA_BUS_FMT_SGRBG10_1X10,
> +			[FLIP_FORMAT_INDEX(1, 1)] = MEDIA_BUS_FMT_SRGGB10_1X10,
> +		},
> +		.clocks_per_pixel = 1,
> +		.rect.top = 0,
> +		.rect.left = 0,
> +		.rect.width = 1296,
> +		.rect.height = 976,
> +		.hts = 1420,
> +		.vts_min = 990,
> +		.reg_list = {
> +			.num_of_regs = ARRAY_SIZE(mode_1296x976_raw),
> +			.regs = mode_1296x976_raw,
> +		},
> +	},
> +};
> +
> +static inline struct hm1246 *to_hm1246(struct v4l2_subdev *sd)
> +{
> +	return container_of_const(sd, struct hm1246, sd);
> +}
> +
> +static const struct hm1246_mode *
> +hm1246_find_mode_by_mbus_code(struct hm1246 *hm1246, u32 code)
> +{
> +	for (unsigned int i = 0; i < ARRAY_SIZE(hm1246_modes); i++) {
> +		if (code == hm1246_get_format_code(hm1246, &hm1246_modes[i]))
> +			return &hm1246_modes[i];
> +	}
> +
> +	return NULL;
> +}
> +
> +static int hm1246_power_on(struct device *dev)
> +{
> +	struct v4l2_subdev *sd = dev_get_drvdata(dev);
> +	struct hm1246 *hm1246 = to_hm1246(sd);
> +	int ret;
> +
> +	ret = regulator_bulk_enable(ARRAY_SIZE(hm1246_supply_names),
> +				    hm1246->supplies);
> +	if (ret) {
> +		dev_err(hm1246->dev, "failed to enable regulators\n");
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(hm1246->xclk);
> +	if (ret) {
> +		regulator_bulk_disable(ARRAY_SIZE(hm1246_supply_names),
> +				       hm1246->supplies);
> +		dev_err(hm1246->dev, "failed to enable clock\n");
> +		return ret;
> +	}
> +
> +	reset_control_deassert(hm1246->reset);
> +
> +	/*
> +	 * XSHUTDOWN to crystal clock oscillation (tcrystal):  650us (typical)
> +	 * Sample bootstrap pin (tsample):                    2000us (maximum)
> +	 * Built in self test (tbist):                        3000us (maximum)
> +	 */
> +	fsleep(6 * USEC_PER_MSEC);
> +
> +	return 0;
> +}
> +
> +static int hm1246_power_off(struct device *dev)
> +{
> +	struct v4l2_subdev *sd = dev_get_drvdata(dev);
> +	struct hm1246 *hm1246 = to_hm1246(sd);
> +
> +	reset_control_assert(hm1246->reset);
> +
> +	clk_disable_unprepare(hm1246->xclk);
> +
> +	regulator_bulk_disable(ARRAY_SIZE(hm1246_supply_names),
> +			       hm1246->supplies);
> +
> +	return 0;
> +}
> +
> +static int hm1246_enum_mbus_code(struct v4l2_subdev *sd,
> +				 struct v4l2_subdev_state *sd_state,
> +				 struct v4l2_subdev_mbus_code_enum *code)
> +{
> +	struct hm1246 *hm1246 = to_hm1246(sd);
> +
> +	if (code->index >= ARRAY_SIZE(hm1246_modes))
> +		return -EINVAL;
> +
> +	code->code = hm1246_get_format_code(hm1246, &hm1246_modes[code->index]);
> +
> +	return 0;
> +}
> +
> +static int hm1246_enum_frame_size(struct v4l2_subdev *subdev,
> +				  struct v4l2_subdev_state *sd_state,
> +				  struct v4l2_subdev_frame_size_enum *fse)
> +{
> +	struct hm1246 *hm1246 = to_hm1246(subdev);
> +	const struct hm1246_mode *mode;
> +
> +	if (fse->index > 0)
> +		return -EINVAL;
> +
> +	mode = hm1246_find_mode_by_mbus_code(hm1246, fse->code);
> +	if (!mode)
> +		return -EINVAL;
> +
> +	fse->min_width = mode->rect.width;
> +	fse->max_width = mode->rect.width;
> +	fse->min_height = mode->rect.height;
> +	fse->max_height = mode->rect.height;
> +
> +	return 0;
> +}
> +
> +static void hm1246_update_pad_format(struct hm1246 *hm1246,
> +				     const struct hm1246_mode *hm1246_mode,
> +				     struct v4l2_mbus_framefmt *fmt)
> +{
> +	fmt->width = hm1246_mode->rect.width;
> +	fmt->height = hm1246_mode->rect.height;
> +	fmt->code = hm1246_get_format_code(hm1246, hm1246_mode);
> +	fmt->field = V4L2_FIELD_NONE;
> +	fmt->colorspace = V4L2_COLORSPACE_RAW;
> +	fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
> +	fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
> +	fmt->xfer_func = V4L2_XFER_FUNC_NONE;
> +}
> +
> +static int hm1246_set_format(struct v4l2_subdev *sd,
> +			     struct v4l2_subdev_state *state,
> +			     struct v4l2_subdev_format *fmt)
> +{
> +	struct hm1246 *hm1246 = to_hm1246(sd);
> +	struct v4l2_mbus_framefmt *mbus_fmt;
> +	struct v4l2_rect *crop;
> +	const struct hm1246_mode *mode;
> +
> +	mode = hm1246_find_mode_by_mbus_code(hm1246, fmt->format.code);
> +	if (!mode)
> +		mode = &hm1246_modes[0];
> +
> +	crop = v4l2_subdev_state_get_crop(state, 0);
> +	*crop = mode->rect;
> +
> +	hm1246_update_pad_format(hm1246, mode, &fmt->format);
> +	mbus_fmt = v4l2_subdev_state_get_format(state, 0);
> +	*mbus_fmt = fmt->format;
> +
> +	return 0;
> +}
> +
> +static int hm1246_get_selection(struct v4l2_subdev *sd,
> +				struct v4l2_subdev_state *state,
> +				struct v4l2_subdev_selection *sel)
> +{
> +	const struct v4l2_mbus_framefmt *format;
> +	const struct hm1246_mode *mode;
> +
> +	format = v4l2_subdev_state_get_format(state, 0);
> +	mode = v4l2_find_nearest_size(hm1246_modes, ARRAY_SIZE(hm1246_modes),
> +				      rect.width, rect.height, format->width,
> +				      format->height);
> +
> +	switch (sel->target) {
> +	case V4L2_SEL_TGT_CROP:
> +		sel->r = *v4l2_subdev_state_get_crop(state, 0);
> +		return 0;
> +
> +	case V4L2_SEL_TGT_NATIVE_SIZE:
> +		sel->r.top = 0;
> +		sel->r.left = 0;
> +		sel->r.width = HM1246_NATIVE_WIDTH;
> +		sel->r.height = HM1246_NATIVE_HEIGHT;
> +		return 0;
> +
> +	case V4L2_SEL_TGT_CROP_DEFAULT:
> +	case V4L2_SEL_TGT_CROP_BOUNDS:
> +		sel->r = mode->rect;
> +		return 0;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int hm1246_init_state(struct v4l2_subdev *sd,
> +			     struct v4l2_subdev_state *state)
> +{
> +	struct hm1246 *hm1246 = to_hm1246(sd);
> +	struct v4l2_subdev_format fmt = {
> +		.which = V4L2_SUBDEV_FORMAT_TRY,
> +		.pad = 0,
> +		.format = {
> +			.code = hm1246_get_format_code(hm1246,
> +						       &hm1246_modes[0]),
> +			.width = hm1246_modes[0].rect.width,
> +			.height = hm1246_modes[0].rect.height,
> +		},
> +	};
> +
> +	hm1246_set_format(sd, state, &fmt);
> +
> +	return 0;
> +}
> +
> +static int hm1246_calc_pll(u32 xclk, u32 link_freq, u32 clocks_per_pixel,
> +			   u8 *pll1, u8 *pll2, u8 *pll3)
> +{
> +	static const u8 pclk_div_table[] = { 4, 5, 6, 7, 8, 12, 14, 16 };
> +	static const u8 sysclk_div_table[] = { 1, 2, 3, 4 };
> +	static const u8 post_div_table[] = { 1, 2, 4, 8 };
> +	static const int sysclk_pclk_ratio = 3; /* Recommended value */
> +	u32 pclk, vco_out;
> +	int pclk_div_index, sysclk_div_index, post_div_index;
> +	bool sysclk_pclk_ratio_found = false;
> +
> +	if (link_freq < HM1246_PCLK_MIN || link_freq > HM1246_PCLK_MAX)
> +		return -EINVAL;
> +
> +	/*
> +	 * In raw mode (1 pixel per clock) the pixel clock is internally
> +	 * divided by two.
> +	 */
> +	pclk = 2 * link_freq / clocks_per_pixel;
> +
> +	/* Find suitable PCLK and SYSCLK dividers. */
> +	for (pclk_div_index = 0; pclk_div_index < ARRAY_SIZE(pclk_div_table);
> +	     pclk_div_index++) {
> +		for (sysclk_div_index = 0;
> +		     sysclk_div_index < ARRAY_SIZE(sysclk_div_table);
> +		     sysclk_div_index++) {
> +			if (sysclk_div_table[sysclk_div_index] *
> +				    sysclk_pclk_ratio ==
> +			    pclk_div_table[pclk_div_index]) {
> +				sysclk_pclk_ratio_found = true;
> +				break;
> +			}
> +		}
> +		if (sysclk_pclk_ratio_found)
> +			break;
> +	}
> +
> +	if (!sysclk_pclk_ratio_found)
> +		return -EINVAL;
> +
> +	/* Determine an appropriate post divider. */
> +	for (post_div_index = 0; post_div_index < ARRAY_SIZE(post_div_table);
> +	     post_div_index++) {
> +		vco_out = pclk * pclk_div_table[pclk_div_index] *
> +			  post_div_table[post_div_index];
> +
> +		if (vco_out >= HM1246_PLL_VCO_MIN &&
> +		    vco_out <= HM1246_PLL_VCO_MAX)
> +			break;
> +	}
> +	if (post_div_index >= ARRAY_SIZE(post_div_table))
> +		return -EINVAL;
> +
> +	/* Find pre-divider and multiplier values. */
> +	for (u32 div = DIV_ROUND_UP(xclk, HM1246_PLL_INCLK_MAX);
> +	     div <= xclk / HM1246_PLL_INCLK_MIN; div++) {
> +		u32 multi, multi_h, multi_l, vco;
> +
> +		multi = DIV_ROUND_CLOSEST_ULL((u64)vco_out * div, xclk);
> +		if (multi < HM1246_PLL_MULTI_MIN ||
> +		    multi > HM1246_PLL_MULTI_MAX)
> +			continue;
> +
> +		multi_h = multi / (HM1246_PLL_MULTI_H_MIN *
> +				   HM1246_PLL_MULTI_L_MAX) +
> +			  2;
> +		multi_l = multi / multi_h;
> +		vco = div_u64((u64)xclk * multi_h * multi_l, div);
> +
> +		if (vco != vco_out)
> +			continue;
> +
> +		if (pll1 && pll2 && pll3) {
> +			*pll1 = HM1246_PLL1CFG_MULTIPLIER(multi_l - 1);
> +			*pll2 = HM1246_PLL2CFG_PRE_DIV(div - 1) |
> +				HM1246_PLL2CFG_MULTIPLIER(multi_h - 2);
> +			*pll3 = HM1246_PLL3CFG_POST_DIV(post_div_index) |
> +				HM1246_PLL3CFG_SYSCLK_DIV(sysclk_div_index) |
> +				HM1246_PLL3CFG_PCLK_DIV(pclk_div_index);
> +		}
> +
> +		return 0;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static int hm1246_cci_write_pll(struct hm1246 *hm1246, u8 pll1, u8 pll2,
> +				u8 pll3)
> +{
> +	const struct cci_reg_sequence pll_regs[] = {
> +		{ HM1246_PLL1CFG_REG, pll1 },
> +		{ HM1246_PLL2CFG_REG, pll2 },
> +		{ HM1246_PLL3CFG_REG, pll3 },
> +		{ HM1246_SBC_CTRL_REG, HM1246_SBC_CTRL_PLL_EN },
> +	};
> +
> +	return cci_multi_reg_write(hm1246->regmap, pll_regs,
> +				   ARRAY_SIZE(pll_regs), NULL);
> +}
> +
> +static int hm1246_pll_check_locked(struct hm1246 *hm1246)
> +{
> +	u64 boot_ref2;
> +	int ret;
> +
> +	ret = cci_read(hm1246->regmap, HM1246_SBC_BOOT_REF2_REG, &boot_ref2,
> +		       NULL);
> +	if (ret)
> +		return ret;
> +
> +	return (boot_ref2 & HM1246_SBC_BOOT_REF2_PLL_LOCK) ? 0 : -EIO;
> +}
> +
> +static int hm1246_setup_pll(struct hm1246 *hm1246,
> +			    const struct hm1246_mode *mode)
> +{
> +	u8 pll1, pll2, pll3;
> +	int ret;
> +
> +	ret = hm1246_calc_pll(hm1246->xclk_freq, hm1246->link_frequency,
> +			      mode->clocks_per_pixel, &pll1, &pll2, &pll3);
> +	if (ret)
> +		return ret;
> +
> +	ret = hm1246_cci_write_pll(hm1246, pll1, pll2, pll3);
> +	if (ret)
> +		return ret;
> +
> +	/* PLL lock time (tpll): 100us (typical) */
> +	fsleep(200);
> +
> +	return hm1246_pll_check_locked(hm1246);
> +}
> +
> +static int hm1246_cci_write_test_pattern(struct hm1246 *hm1246, u8 mode,
> +					 u16 r, u16 g, u16 b)
> +{
> +	const struct cci_reg_sequence tpg_enable_regs[] = {
> +		{ HM1246_TEST_DATA_RED_REG, r },
> +		{ HM1246_TEST_DATA_GR_REG, g },
> +		{ HM1246_TEST_DATA_GB_REG, g },
> +		{ HM1246_TEST_DATA_BLUE_REG, b },
> +		{ HM1246_TEST_PATTERN_MODE_REG, mode },
> +	};
> +
> +	return cci_multi_reg_write(hm1246->regmap, tpg_enable_regs,
> +				   ARRAY_SIZE(tpg_enable_regs), NULL);
> +}
> +
> +static int hm1246_test_pattern(struct hm1246 *hm1246, u32 index)
> +{
> +	static const u16 RGBMIN = 0, RGBMAX = 0x3ff;
> +	static const struct tp {
> +		int pattern;
> +		u16 r, g, b;
> +	} tps[] = {
> +		/* Disabled */
> +		[0] = { .pattern = 0, .r = RGBMIN, .g = RGBMIN, .b = RGBMIN },
> +		/* Checkboard pattern */
> +		[1] = { .pattern = 0, .r = RGBMIN, .g = RGBMIN, .b = RGBMIN },
> +		/* Ramp */
> +		[2] = { .pattern = 1, .r = RGBMIN, .g = RGBMIN, .b = RGBMIN },
> +		/* Moving ones */
> +		[3] = { .pattern = 2, .r = RGBMIN, .g = RGBMIN, .b = RGBMIN },
> +		/* Blending color bars */
> +		[4] = { .pattern = 3, .r = RGBMIN, .g = RGBMIN, .b = RGBMIN },
> +		/* Color bars */
> +		[5] = { .pattern = 4, .r = RGBMIN, .g = RGBMIN, .b = RGBMIN },
> +		/* Solid white */
> +		[6] = { .pattern = 15, .r = RGBMAX, .g = RGBMAX, .b = RGBMAX },
> +		/* Solid black */
> +		[7] = { .pattern = 15, .r = RGBMIN, .g = RGBMIN, .b = RGBMIN },
> +		/* Solid red */
> +		[8] = { .pattern = 15, .r = RGBMAX, .g = RGBMIN, .b = RGBMIN },
> +		/* Solid green */
> +		[9] = { .pattern = 15, .r = RGBMIN, .g = RGBMAX, .b = RGBMIN },
> +		/* Solid blue */
> +		[10] = { .pattern = 15, .r = RGBMIN, .g = RGBMIN, .b = RGBMAX },
> +	};
> +	u8 mode;
> +
> +	if (index >= ARRAY_SIZE(tps))
> +		return -EINVAL;
> +
> +	mode = HM1246_TEST_PATTERN_MODE_MODE(tps[index].pattern);
> +	if (index)
> +		mode |= HM1246_TEST_PATTERN_MODE_ENABLE;
> +
> +	return hm1246_cci_write_test_pattern(hm1246, mode, tps[index].r,
> +					     tps[index].g, tps[index].b);
> +}
> +
> +static int hm1246_set_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct hm1246 *hm1246 =
> +		container_of_const(ctrl->handler, struct hm1246, ctrls);
> +	struct v4l2_subdev_state *state;
> +	const struct v4l2_mbus_framefmt *format;
> +	u32 val;
> +	bool needs_cmu_update = true;
> +	int ret;
> +
> +	state = v4l2_subdev_get_locked_active_state(&hm1246->sd);
> +	format = v4l2_subdev_state_get_format(state, 0);
> +
> +	if (ctrl->id == V4L2_CID_VBLANK) {
> +		s64 exposure_max;
> +
> +		exposure_max =
> +			format->height + ctrl->val - HM1246_COARSE_INTG_MARGIN;
> +		ret = __v4l2_ctrl_modify_range(hm1246->exposure_ctrl,
> +					       hm1246->exposure_ctrl->minimum,
> +					       exposure_max,
> +					       hm1246->exposure_ctrl->step,
> +					       exposure_max);
> +
> +		if (ret) {
> +			dev_err(hm1246->dev, "exposure ctrl range update failed\n");
> +			return ret;
> +		}
> +	}
> +
> +	if (!pm_runtime_get_if_active(hm1246->dev))
> +		return 0;
> +
> +	ret = 0;
> +	switch (ctrl->id) {
> +	case V4L2_CID_EXPOSURE:
> +		cci_write(hm1246->regmap, HM1246_COARSE_INTG_REG, ctrl->val,
> +			  &ret);
> +		break;
> +
> +	case V4L2_CID_ANALOGUE_GAIN:
> +		cci_write(hm1246->regmap, HM1246_ANALOG_GLOBAL_GAIN_REG,
> +			  ctrl->val, &ret);
> +		break;
> +
> +	case V4L2_CID_VBLANK:
> +		val = format->height + ctrl->val;
> +		cci_write(hm1246->regmap, HM1246_FRAME_LENGTH_LINES_REG, val,
> +			  &ret);
> +		break;
> +
> +	case V4L2_CID_HFLIP:
> +	case V4L2_CID_VFLIP:
> +		val = 0;
> +		if (hm1246->hflip_ctrl->val)
> +			val |= HM1246_IMAGE_ORIENTATION_HFLIP;
> +		if (hm1246->vflip_ctrl->val)
> +			val |= HM1246_IMAGE_ORIENTATION_VFLIP;
> +
> +		cci_write(hm1246->regmap, HM1246_IMAGE_ORIENTATION_REG, val,
> +			  &ret);
> +		break;
> +
> +	case V4L2_CID_TEST_PATTERN:
> +		ret = hm1246_test_pattern(hm1246, ctrl->val);
> +		needs_cmu_update = false;
> +		break;
> +
> +	default:
> +		ret = -EINVAL;
> +		needs_cmu_update = false;
> +		break;
> +	}
> +
> +	if (needs_cmu_update)
> +		cci_write(hm1246->regmap, HM1246_CMU_UPDATE_REG, 0, &ret);
> +
> +	pm_runtime_put(hm1246->dev);
> +
> +	return ret;
> +}
> +
> +static const struct v4l2_ctrl_ops hm1246_ctrl_ops = {
> +	.s_ctrl = hm1246_set_ctrl,
> +};
> +
> +static int hm1246_identify_module(struct hm1246 *hm1246)
> +{
> +	u64 model_id;
> +	int ret;
> +
> +	if (hm1246->identified)
> +		return 0;
> +
> +	ret = cci_read(hm1246->regmap, HM1246_MODEL_ID_REG, &model_id, NULL);
> +	if (ret)
> +		return ret;
> +
> +	if (model_id != HM1246_MODEL_ID) {
> +		dev_err(hm1246->dev, "model id mismatch: 0x%llx!=0x%x\n",
> +			model_id, HM1246_MODEL_ID);
> +		return -ENXIO;
> +	}
> +
> +	hm1246->identified = true;
> +
> +	return 0;
> +}
> +
> +static int hm1246_setup_moderegs(struct hm1246 *hm1246,
> +				 const struct hm1246_mode *mode)
> +{
> +	const struct hm1246_reg_list *reg_list = &mode->reg_list;
> +	const struct cci_reg_sequence modeaw[] = {
> +		{ HM1246_X_ADDR_START_REG, mode->rect.left },
> +		{ HM1246_Y_ADDR_START_REG, mode->rect.top },
> +		{ HM1246_X_ADDR_END_REG, mode->rect.width - 1 },
> +		{ HM1246_Y_ADDR_END_REG, mode->rect.height - 1 },
> +		{ HM1246_DWIN_XOFFSET_REG, mode->rect.left },
> +		{ HM1246_DWIN_YOFFSET_REG, mode->rect.top },
> +		{ HM1246_DWIN_XSIZE_REG, mode->rect.width },
> +		{ HM1246_DWIN_YSIZE_REG, mode->rect.height },
> +		{ HM1246_LINE_LENGTH_PCK_REG, mode->hts },
> +	};
> +	int ret = 0;
> +
> +	cci_multi_reg_write(hm1246->regmap, modeaw, ARRAY_SIZE(modeaw), &ret);
> +	cci_multi_reg_write(hm1246->regmap, reg_list->regs,
> +			    reg_list->num_of_regs, &ret);
> +
> +	return ret;
> +}
> +
> +static int hm1246_setup_bus(struct hm1246 *hm1246)
> +{
> +	u64 polarity_ctrl = 0, pclk_ctrl = 0;
> +	int ret = 0;
> +
> +	if (hm1246->mbus_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
> +		polarity_ctrl |= HM1246_POLARITY_CTRL_HSYNC;
> +
> +	if (hm1246->mbus_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
> +		polarity_ctrl |= HM1246_POLARITY_CTRL_VSYNC;
> +
> +	cci_write(hm1246->regmap, HM1246_POLARITY_CTRL_REG, polarity_ctrl,
> +		  &ret);
> +
> +	/*
> +	 * If the clock output polarity flag PCLK_CTRL[3] is set (high), the
> +	 * data lines change state on the falling edge of PCLK and should
> +	 * therefore be sampled on the rising edge.
> +	 * This is different than described in the data sheet.
> +	 */
> +	if (hm1246->mbus_flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
> +		pclk_ctrl |= HM1246_PCLK_CTRL_POL;
> +
> +	cci_write(hm1246->regmap, HM1246_PCLK_CTRL_REG, pclk_ctrl, &ret);
> +
> +	return ret;
> +}
> +
> +static int hm1246_enable_streams(struct v4l2_subdev *sd,
> +				 struct v4l2_subdev_state *state, u32 pad,
> +				 u64 streams_mask)
> +{
> +	struct hm1246 *hm1246 = to_hm1246(sd);
> +	const struct v4l2_mbus_framefmt *format;
> +	const struct hm1246_mode *mode;
> +	int ret;
> +
> +	format = v4l2_subdev_state_get_format(state, 0);
> +	mode = v4l2_find_nearest_size(hm1246_modes, ARRAY_SIZE(hm1246_modes),
> +				      rect.width, rect.height, format->width,
> +				      format->height);
> +
> +	ret = pm_runtime_resume_and_get(hm1246->dev);
> +	if (ret)
> +		return ret;
> +
> +	ret = hm1246_identify_module(hm1246);
> +	if (ret)
> +		goto err_rpm_put;
> +
> +	ret = hm1246_setup_pll(hm1246, mode);
> +	if (ret) {
> +		dev_err(hm1246->dev, "failed to setup PLL\n");
> +		goto err_rpm_put;
> +	}
> +
> +	ret = hm1246_setup_moderegs(hm1246, mode);
> +	if (ret)
> +		goto err_rpm_put;
> +
> +	ret = hm1246_setup_bus(hm1246);
> +	if (ret)
> +		goto err_rpm_put;
> +
> +	ret = __v4l2_ctrl_handler_setup(&hm1246->ctrls);
> +	if (ret) {
> +		dev_err(hm1246->dev, "failed to setup v4l2 controls\n");
> +		goto err_rpm_put;
> +	}
> +
> +	ret = cci_write(hm1246->regmap, HM1246_MODE_SELECT_REG,
> +			HM1246_MODE_SELECT_STREAM, NULL);
> +	if (ret)
> +		goto err_rpm_put;
> +
> +	/*
> +	 * Since mirroring may change the actual pixel format, it must not be
> +	 * changed during streaming.
> +	 */
> +	__v4l2_ctrl_grab(hm1246->vflip_ctrl, true);
> +	__v4l2_ctrl_grab(hm1246->hflip_ctrl, true);
> +
> +	return 0;
> +
> +err_rpm_put:
> +	pm_runtime_put_autosuspend(hm1246->dev);
> +
> +	return ret;
> +}
> +
> +static int hm1246_disable_streams(struct v4l2_subdev *sd,
> +				  struct v4l2_subdev_state *state, u32 pad,
> +				  u64 streams_mask)
> +{
> +	struct hm1246 *hm1246 = to_hm1246(sd);
> +	int ret;
> +
> +	ret = cci_write(hm1246->regmap, HM1246_MODE_SELECT_REG,
> +			HM1246_MODE_SELECT_STANDBY, NULL);
> +
> +	__v4l2_ctrl_grab(hm1246->vflip_ctrl, false);
> +	__v4l2_ctrl_grab(hm1246->hflip_ctrl, false);
> +
> +	pm_runtime_put_autosuspend(hm1246->dev);
> +
> +	return ret;
> +}
> +
> +static const struct v4l2_subdev_video_ops hm1246_video_ops = {
> +	.s_stream = v4l2_subdev_s_stream_helper,
> +};
> +
> +static const struct v4l2_subdev_pad_ops hm1246_subdev_pad_ops = {
> +	.enum_mbus_code = hm1246_enum_mbus_code,
> +	.enum_frame_size = hm1246_enum_frame_size,
> +	.get_fmt = v4l2_subdev_get_fmt,
> +	.set_fmt = hm1246_set_format,
> +	.get_selection = hm1246_get_selection,
> +	.enable_streams = hm1246_enable_streams,
> +	.disable_streams = hm1246_disable_streams,
> +};
> +
> +static const struct v4l2_subdev_ops hm1246_subdev_ops = {
> +	.video = &hm1246_video_ops,
> +	.pad = &hm1246_subdev_pad_ops,
> +};
> +
> +static const struct v4l2_subdev_internal_ops hm1246_internal_ops = {
> +	.init_state = hm1246_init_state,
> +};
> +
> +static int hm1246_get_regulators(struct device *dev, struct hm1246 *hm1246)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(hm1246_supply_names); i++)

You could declare i here.

> +		hm1246->supplies[i].supply = hm1246_supply_names[i];
> +
> +	return devm_regulator_bulk_get(dev, ARRAY_SIZE(hm1246_supply_names),
> +				       hm1246->supplies);
> +}
> +
> +static int hm1246_parse_fwnode(struct hm1246 *hm1246)
> +{
> +	struct fwnode_handle *endpoint;
> +	struct v4l2_fwnode_endpoint bus_cfg = {
> +		.bus_type = V4L2_MBUS_PARALLEL,
> +	};
> +	int ret;
> +
> +	endpoint = fwnode_graph_get_endpoint_by_id(dev_fwnode(hm1246->dev),
> +						   0, 0,
> +						   FWNODE_GRAPH_ENDPOINT_NEXT);
> +	if (!endpoint)
> +		return dev_err_probe(hm1246->dev, -EINVAL,
> +				     "missing endpoint node\n");

You can drop this check now; v4l2_fwnode_endpoint_alloc_parse() already
does it.

> +
> +	ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg);
> +	fwnode_handle_put(endpoint);
> +	if (ret)
> +		return dev_err_probe(hm1246->dev, ret,
> +				     "parsing endpoint node failed\n");
> +
> +	hm1246->mbus_flags = bus_cfg.bus.parallel.flags;
> +
> +	if (bus_cfg.nr_of_link_frequencies == 1)

I'd test for non-zero number: having more than one isn't an issue.

> +		hm1246->link_frequency = bus_cfg.link_frequencies[0];
> +
> +	v4l2_fwnode_endpoint_free(&bus_cfg);
> +
> +	if (!hm1246->link_frequency)
> +		return dev_err_probe(hm1246->dev, -EINVAL,
> +				     "one link frequency expected\n");
> +
> +	return 0;
> +}
> +
> +static int hm1246_init_controls(struct hm1246 *hm1246)
> +{
> +	const struct hm1246_mode *mode = &hm1246_modes[0];
> +	struct v4l2_fwnode_device_properties props;
> +	struct v4l2_ctrl_handler *ctrl_hdlr = &hm1246->ctrls;
> +	struct v4l2_ctrl *ctrl;
> +	s64 pixel_rate, exposure_max, vblank_min, hblank;
> +	int ret;
> +
> +	ret = v4l2_fwnode_device_parse(hm1246->dev, &props);
> +	if (ret)
> +		return ret;
> +
> +	v4l2_ctrl_handler_init(ctrl_hdlr, 11);
> +
> +	hm1246->hflip_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &hm1246_ctrl_ops,
> +					       V4L2_CID_HFLIP, 0, 1, 1, 0);
> +	if (hm1246->hflip_ctrl)
> +		hm1246->hflip_ctrl->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
> +
> +	hm1246->vflip_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &hm1246_ctrl_ops,
> +					       V4L2_CID_VFLIP, 0, 1, 1, 0);
> +	if (hm1246->vflip_ctrl)
> +		hm1246->vflip_ctrl->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
> +
> +	v4l2_ctrl_cluster(2, &hm1246->hflip_ctrl);
> +
> +	ctrl = v4l2_ctrl_new_int_menu(ctrl_hdlr, &hm1246_ctrl_ops,
> +				      V4L2_CID_LINK_FREQ,
> +				      0, 0,
> +				      &hm1246->link_frequency);
> +	if (ctrl)
> +		ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> +
> +	pixel_rate = div_u64(hm1246->link_frequency, mode->clocks_per_pixel);
> +	ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &hm1246_ctrl_ops,
> +				 V4L2_CID_PIXEL_RATE,
> +				 pixel_rate, pixel_rate, 1,
> +				 pixel_rate);
> +
> +	vblank_min = mode->vts_min - mode->rect.height;
> +	ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &hm1246_ctrl_ops,
> +				 V4L2_CID_VBLANK, vblank_min,
> +				 HM1246_VTS_MAX - mode->rect.height,
> +				 1, vblank_min);
> +
> +	hblank = mode->hts - mode->rect.width;
> +	ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &hm1246_ctrl_ops,
> +				 V4L2_CID_HBLANK, hblank, hblank,
> +				 1, hblank);
> +	if (ctrl)
> +		ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> +
> +	v4l2_ctrl_new_std(ctrl_hdlr, &hm1246_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
> +			  HM1246_ANALOG_GLOBAL_GAIN_MIN,
> +			  HM1246_ANALOG_GLOBAL_GAIN_MAX,
> +			  HM1246_ANALOG_GLOBAL_GAIN_STEP,
> +			  HM1246_ANALOG_GLOBAL_GAIN_MIN);
> +
> +	exposure_max = mode->vts_min - HM1246_COARSE_INTG_MARGIN;
> +	hm1246->exposure_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &hm1246_ctrl_ops,
> +						  V4L2_CID_EXPOSURE,
> +						  HM1246_COARSE_INTG_MIN,
> +						  exposure_max,
> +						  HM1246_COARSE_INTG_STEP,
> +						  exposure_max);
> +
> +	v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &hm1246_ctrl_ops,
> +				     V4L2_CID_TEST_PATTERN,
> +				     ARRAY_SIZE(hm1246_test_pattern_menu) - 1,
> +				     0, 0, hm1246_test_pattern_menu);
> +
> +	v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &hm1246_ctrl_ops, &props);
> +
> +	if (ctrl_hdlr->error) {
> +		v4l2_ctrl_handler_free(ctrl_hdlr);
> +		return ctrl_hdlr->error;
> +	}
> +
> +	hm1246->sd.ctrl_handler = ctrl_hdlr;
> +
> +	return 0;
> +}
> +
> +static int hm1246_probe(struct i2c_client *client)
> +{
> +	struct hm1246 *hm1246;
> +	int ret;
> +
> +	hm1246 = devm_kzalloc(&client->dev, sizeof(*hm1246), GFP_KERNEL);
> +	if (!hm1246)
> +		return -ENOMEM;
> +
> +	hm1246->dev = &client->dev;
> +
> +	ret = hm1246_parse_fwnode(hm1246);
> +	if (ret)
> +		return ret;
> +
> +	hm1246->regmap = devm_cci_regmap_init_i2c(client, 16);
> +	if (IS_ERR(hm1246->regmap))
> +		return dev_err_probe(hm1246->dev, PTR_ERR(hm1246->regmap),
> +				     "failed to init CCI\n");
> +
> +	hm1246->xclk = devm_v4l2_sensor_clk_get(hm1246->dev, NULL);
> +	if (IS_ERR(hm1246->xclk))
> +		return dev_err_probe(hm1246->dev, PTR_ERR(hm1246->xclk),
> +				     "failed to get xclk\n");
> +
> +	hm1246->xclk_freq = clk_get_rate(hm1246->xclk);
> +	if (hm1246->xclk_freq < HM1246_XCLK_MIN ||
> +	    hm1246->xclk_freq > HM1246_XCLK_MAX)
> +		return dev_err_probe(hm1246->dev, -EINVAL,
> +				     "xclk frequency out of range: %luHz\n",
> +				     hm1246->xclk_freq);
> +
> +	for (unsigned int i = 0; i < ARRAY_SIZE(hm1246_modes); i++) {
> +		ret = hm1246_calc_pll(hm1246->xclk_freq, hm1246->link_frequency,
> +				      hm1246_modes[i].clocks_per_pixel,
> +				      NULL, NULL, NULL);
> +		if (ret)
> +			return dev_err_probe(hm1246->dev, ret,
> +					     "no PLL setup for %lld Hz\n",
> +					     hm1246->link_frequency);
> +	}
> +
> +	ret = hm1246_get_regulators(hm1246->dev, hm1246);
> +	if (ret)
> +		return dev_err_probe(hm1246->dev, ret,
> +				     "failed to get regulators\n");
> +
> +	hm1246->reset = devm_reset_control_get_optional(hm1246->dev, NULL);
> +	if (IS_ERR(hm1246->reset))
> +		return dev_err_probe(hm1246->dev, PTR_ERR(hm1246->reset),
> +				     "failed to get reset\n");
> +	reset_control_assert(hm1246->reset);
> +
> +	v4l2_i2c_subdev_init(&hm1246->sd, client, &hm1246_subdev_ops);
> +	hm1246->sd.internal_ops = &hm1246_internal_ops;
> +
> +	ret = hm1246_init_controls(hm1246);
> +	if (ret)
> +		return dev_err_probe(hm1246->dev, ret,
> +				     "failed to init controls\n");
> +
> +	hm1246->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +	hm1246->pad.flags = MEDIA_PAD_FL_SOURCE;
> +	hm1246->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
> +
> +	ret = media_entity_pads_init(&hm1246->sd.entity, 1, &hm1246->pad);
> +	if (ret) {
> +		dev_err_probe(hm1246->dev, ret, "failed to init media pads\n");
> +		goto err_v4l2_ctrl_handler_free;
> +	}
> +
> +	hm1246->sd.state_lock = hm1246->ctrls.lock;
> +	ret = v4l2_subdev_init_finalize(&hm1246->sd);
> +	if (ret) {
> +		dev_err_probe(hm1246->dev, ret, "failed to init v4l2 subdev\n");
> +		goto err_media_entity_cleanup;
> +	}
> +
> +	pm_runtime_enable(hm1246->dev);
> +	pm_runtime_set_autosuspend_delay(hm1246->dev, 1000);
> +	pm_runtime_use_autosuspend(hm1246->dev);
> +	pm_runtime_idle(hm1246->dev);

No need to call pm_runtime_idle() as the device hasn't been resumed.

This Runtime PM flow only works on DT in any case. I think I'd add a
dependency to DT.

> +
> +	ret = v4l2_async_register_subdev_sensor(&hm1246->sd);
> +	if (ret) {
> +		dev_err_probe(hm1246->dev, ret,
> +			      "failed to register v4l2 subdev\n");
> +		goto err_subdev_cleanup;
> +	}
> +
> +	return 0;
> +
> +err_subdev_cleanup:
> +	v4l2_subdev_cleanup(&hm1246->sd);
> +	pm_runtime_disable(hm1246->dev);
> +	pm_runtime_set_suspended(hm1246->dev);
> +
> +err_media_entity_cleanup:
> +	media_entity_cleanup(&hm1246->sd.entity);
> +
> +err_v4l2_ctrl_handler_free:
> +	v4l2_ctrl_handler_free(&hm1246->ctrls);
> +
> +	return ret;
> +}
> +
> +static void hm1246_remove(struct i2c_client *client)
> +{
> +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
> +	struct hm1246 *hm1246 = to_hm1246(sd);
> +
> +	v4l2_async_unregister_subdev(&hm1246->sd);
> +	v4l2_subdev_cleanup(sd);
> +	media_entity_cleanup(&hm1246->sd.entity);
> +	v4l2_ctrl_handler_free(&hm1246->ctrls);
> +
> +	pm_runtime_disable(&client->dev);
> +	if (!pm_runtime_status_suspended(&client->dev)) {
> +		hm1246_power_off(hm1246->dev);
> +		pm_runtime_set_suspended(&client->dev);
> +	}
> +}
> +
> +static const struct of_device_id hm1246_of_match[] = {
> +	{ .compatible = "himax,hm1246" },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, hm1246_of_match);
> +
> +static DEFINE_RUNTIME_DEV_PM_OPS(hm1246_pm_ops,
> +				 hm1246_power_off, hm1246_power_on, NULL);
> +
> +static struct i2c_driver hm1246_i2c_driver = {
> +	.driver = {
> +		.of_match_table = hm1246_of_match,
> +		.pm = pm_ptr(&hm1246_pm_ops),
> +		.name = "hm1246",
> +	},
> +	.probe = hm1246_probe,
> +	.remove = hm1246_remove,
> +};
> +module_i2c_driver(hm1246_i2c_driver);
> +
> +MODULE_DESCRIPTION("Himax HM1246 camera driver");
> +MODULE_AUTHOR("Matthias Fend <matthias.fend@emfend.at>");
> +MODULE_LICENSE("GPL");
> 

-- 
Kind regards,

Sakari Ailus

^ permalink raw reply

* Re: [PATCH v6 0/6] phy: realtek: usb2: support for RTL9607C USB2 PHY
From: Rustam Adilov @ 2026-06-20 11:49 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Stanley Chang, Philipp Zabel, linux-phy, devicetree,
	linux-kernel
In-Reply-To: <20260520175728.720877-1-adilov@disroot.org>

Hello,

Just a gentle reminder as this patch series has been sitting idle
for month already. I do know there are a couple of reviews sent
by sashiko AI but they are only about pre-existing issues which
has nothing to do with this patch series.

And the issue about "[High] - The driver attempts to access the 
PHY's memory-mapped registers in `probe()` before the newly introduced
reset control is deasserted" is not the case because rtl9607_phy_cfg
contains the [4] = {0xe4, 0x6a} in page0 and thus doesn't trigger
phy memory reads during probe (in update_dc_driving_level).

Best,
Rustam

^ permalink raw reply

* Re: [PATCH v2 2/3] media: i2c: add imx576 image sensor driver
From: Laurent Pinchart @ 2026-06-20 13:27 UTC (permalink / raw)
  To: Himanshu Bhavani
  Cc: sakari.ailus, luca.weiss, Hardevsinh Palaniya,
	Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Konrad Dybcio, Hans Verkuil,
	Hans de Goede, Vladimir Zapolskiy, Elgin Perumbilly,
	Walter Werner Schneider, Kate Hsuan, Svyatoslav Ryhel,
	linux-media, devicetree, linux-kernel, linux-arm-msm
In-Reply-To: <20260619125439.55311-3-himanshu.bhavani@siliconsignals.io>

On Fri, Jun 19, 2026 at 06:24:32PM +0530, Himanshu Bhavani wrote:
> Add a v4l2 subdevice driver for the Sony imx576 sensor.
> 
> The Sony IMX576 image sensor with an active
> array size of 5760 x 4312
> 
> The following features are supported:
> - Manual exposure an gain control support
> - vblank/hblank control support
> - Supported resolution: 2880 x 2156 30fps (SRGGB10)

You've been asked in v1 to make this driver dynamically compute
registers instead of hardcoding modes. Please do so in v3. Nack on v2.

> Signed-off-by: Himanshu Bhavani <himanshu.bhavani@siliconsignals.io>
> ---
>  MAINTAINERS                |    1 +
>  drivers/media/i2c/Kconfig  |   10 +
>  drivers/media/i2c/Makefile |    1 +
>  drivers/media/i2c/imx576.c | 1034 ++++++++++++++++++++++++++++++++++++
>  4 files changed, 1046 insertions(+)
>  create mode 100644 drivers/media/i2c/imx576.c

[snip]

-- 
Regards,

Laurent Pinchart

^ permalink raw reply

* Re: [PATCH v3 0/2] cpufreq: spacemit: Add cpufreq support for K1 SoC
From: Gong Shuai @ 2026-06-20 14:23 UTC (permalink / raw)
  To: Shuwei Wu, Rafael J. Wysocki, Viresh Kumar, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Paul Walmsley, Palmer Dabbelt,
	Albert Ou, Alexandre Ghiti, Yixun Lan, Vincent Legoll
  Cc: linux-pm, linux-kernel, linux-riscv, spacemit, devicetree
In-Reply-To: <20260612-shadow-deps-v3-0-2f3ba88611ff@mailbox.org>

On 6/12/2026 5:51 PM, Shuwei Wu wrote:
> This series enables CPU frequency scaling for the SpacemiT K1 SoC.
> 
> K1 has two CPU cluster clocks but one shared CPU voltage rail. Add a
> small K1-specific cpufreq driver so the two clocks and the shared OPP
> transition can be handled together.
> 
> Tested on Banana Pi BPI-F3. The system boots with all eight CPUs online,
> the K1 cpufreq driver registers one policy for CPUs 0-7, and both CPU
> cluster clocks follow the selected cpufreq rate.
> 
> ~ # cat /sys/devices/system/cpu/online
> 0-7
> 
> ~ # ls /sys/devices/system/cpu/cpufreq
> policy0
> 
> ~ # cat /sys/devices/system/cpu/cpufreq/policy0/affected_cpus
> 0 1 2 3 4 5 6 7
> 
> ~ # cat /sys/devices/system/cpu/cpufreq/policy0/scaling_available_frequencies
> 614400 819000 1000000 1228800 1600000
> 
> With the userspace governor:
> ----------------------------------------------------------------------------
> Frequency    | scaling_cur | cpu_c0_core | cpu_c1_core | Real    | User
> (kHz)        | (kHz)       | (Hz)        | (Hz)        | (s)     | (s)
> -------------+-------------+-------------+-------------+---------+----------
> 1,600,000    | 1,600,000   | 1600000000  | 1600000000  | 1.80s   | 1.80s
> 1,228,800    | 1,228,800   | 1228800000  | 1228800000  | 2.33s   | 2.33s
> 1,000,000    | 1,000,000   | 1000000000  | 1000000000  | 2.88s   | 2.87s
>    819,000    |   819,000   |  819200000  |  819200000  | 3.53s   | 3.52s
>    614,400    |   614,400   |  614400000  |  614400000  | 4.72s   | 4.72s
> ----------------------------------------------------------------------------
> 
> Signed-off-by: Shuwei Wu <shuwei.wu@mailbox.org>
> ---
> Changes in v3:
> - Add a K1-specific cpufreq driver for the shared-rail, dual-clock topology
> - Use one shared CPU OPP table and one cpufreq policy for all CPUs
> - Link to v2: https://lore.kernel.org/r/20260410-shadow-deps-v2-0-4e16b8c0f60e@mailbox.org
> 
> Changes in v2:
> - Move OPP tables to dedicated k1-opp.dtsi
> - Enable OPP only on BPI-F3 with cpu-supply present
> - Link to v1: https://lore.kernel.org/r/20260308-shadow-deps-v1-0-0ceb5c7c07eb@mailbox.org
> 
> ---
> Shuwei Wu (2):
>        cpufreq: spacemit: Add K1 cpufreq driver
>        riscv: dts: spacemit: Add cpu scaling for K1 SoC
> 
>   arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts |  35 +++-
>   arch/riscv/boot/dts/spacemit/k1-opp.dtsi        |  70 +++++++
>   arch/riscv/boot/dts/spacemit/k1.dtsi            |   8 +
>   drivers/cpufreq/Kconfig                         |   4 +
>   drivers/cpufreq/Kconfig.riscv                   |  15 ++
>   drivers/cpufreq/Makefile                        |   3 +
>   drivers/cpufreq/cpufreq-dt-platdev.c            |   2 +
>   drivers/cpufreq/spacemit-k1-cpufreq.c           | 251 ++++++++++++++++++++++++
>   8 files changed, 387 insertions(+), 1 deletion(-)
> ---
> base-commit: 5164e95565d3fd508ca8a95351323f5716dfb695
> change-id: 20260307-shadow-deps-3582a78aa756
> prerequisite-patch-id: 154bd4f720ce5065d58b988de8f273207b44572e
> prerequisite-message-id: <20260206-spacemit-p1-v4-0-8f695d93811e@riscstar.com>
> prerequisite-patch-id: 5da3e75b18291a5540d4f66d7a0600fb8975ef62
> prerequisite-patch-id: bcf41917414ecef8cf743095d130f6004c32f6a5
> prerequisite-patch-id: cfe3800f8c791ec4c63e070af9628e88e0fc31b9
> prerequisite-message-id: <20260305-k1-clk-fix-v1-1-abca85d6e266@mailbox.org>
> prerequisite-patch-id: 7c7fb9f87dba019ece4c97c45750349a7cd28f3a
> 
> Best regards,


Hi Shuwei and Vincent,

I have tested this series on the OrangePi RV2 board (4GB) on top of
linux-next (next-20260619), with the v3 cpufreq series and Vincent's
patch "k1-orangepi-rv2: Add cpu scaling" applied.

The driver works as expected: all 8 CPUs are online and share a single
policy (policy0). Writing to
/sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed
correctly changes the frequency for all cores simultaneously, and all
scaling_setspeed files show the same value. The frequency transitions
are smooth, and performance (tested with a simple awk loop, following
Vincent's approach) scales linearly with the set frequency.

Both of you can add my tag:

Tested-by: Gong Shuai <gsh517025@gmail.com> # OrangePi-RV2

If you send a new version, I'm also willing to test it again.

Thanks for the work.

Regards,
Shuai



^ permalink raw reply


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