* [PATCH V2 0/6] Add Anbernic RG Vita-Pro
@ 2026-05-15 22:19 ` Chris Morgan
0 siblings, 0 replies; 19+ messages in thread
From: Chris Morgan @ 2026-05-15 22:19 UTC (permalink / raw)
To: linux-rockchip
Cc: devicetree, xsf, sre, simona, airlied, tzimmermann, mripard,
maarten.lankhorst, jesszhan0024, neil.armstrong, heiko, conor+dt,
krzk+dt, robh, Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add support for the Anbernic RG Vita-Pro, based on the Rockchip RK3576 SoC.
Changes Since V1:
- Updated documentation filename to matche compatible string.
- Corrected order of allocating IRQ in charger driver and switched
to using devm managed function to allocate work queue.
Chris Morgan (6):
dt-bindings: power: supply: sgm41542: document sgm41542
power: supply: sgm41542: Add SG Micro sgm41542 charger
dt-bindings: display: panel: document Anbernic TD4310 panel
drm/panel: anbernic-td4310: Add RG Vita Pro panel
dt-bindings: arm: rockchip: Add Anbernic RG Vita-Pro
arm64: dts: rockchip: Add Anbernic RG Vita-Pro
.../devicetree/bindings/arm/rockchip.yaml | 6 +
.../display/panel/anbernic,td4310.yaml | 64 +
.../power/supply/sgmicro,sgm41542.yaml | 99 ++
arch/arm64/boot/dts/rockchip/Makefile | 3 +
.../rockchip/rk3576-anbernic-rg-vita-pro.dts | 1317 +++++++++++++++++
drivers/gpu/drm/panel/Kconfig | 10 +
drivers/gpu/drm/panel/Makefile | 1 +
drivers/gpu/drm/panel/panel-anbernic-td4310.c | 291 ++++
drivers/power/supply/Kconfig | 8 +
drivers/power/supply/Makefile | 1 +
drivers/power/supply/sgm41542_charger.c | 1035 +++++++++++++
11 files changed, 2835 insertions(+)
create mode 100644 Documentation/devicetree/bindings/display/panel/anbernic,td4310.yaml
create mode 100644 Documentation/devicetree/bindings/power/supply/sgmicro,sgm41542.yaml
create mode 100644 arch/arm64/boot/dts/rockchip/rk3576-anbernic-rg-vita-pro.dts
create mode 100644 drivers/gpu/drm/panel/panel-anbernic-td4310.c
create mode 100644 drivers/power/supply/sgm41542_charger.c
--
2.43.0
_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH V2 0/6] Add Anbernic RG Vita-Pro
@ 2026-05-15 22:19 ` Chris Morgan
0 siblings, 0 replies; 19+ messages in thread
From: Chris Morgan @ 2026-05-15 22:19 UTC (permalink / raw)
To: linux-rockchip
Cc: devicetree, xsf, sre, simona, airlied, tzimmermann, mripard,
maarten.lankhorst, jesszhan0024, neil.armstrong, heiko, conor+dt,
krzk+dt, robh, Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add support for the Anbernic RG Vita-Pro, based on the Rockchip RK3576 SoC.
Changes Since V1:
- Updated documentation filename to matche compatible string.
- Corrected order of allocating IRQ in charger driver and switched
to using devm managed function to allocate work queue.
Chris Morgan (6):
dt-bindings: power: supply: sgm41542: document sgm41542
power: supply: sgm41542: Add SG Micro sgm41542 charger
dt-bindings: display: panel: document Anbernic TD4310 panel
drm/panel: anbernic-td4310: Add RG Vita Pro panel
dt-bindings: arm: rockchip: Add Anbernic RG Vita-Pro
arm64: dts: rockchip: Add Anbernic RG Vita-Pro
.../devicetree/bindings/arm/rockchip.yaml | 6 +
.../display/panel/anbernic,td4310.yaml | 64 +
.../power/supply/sgmicro,sgm41542.yaml | 99 ++
arch/arm64/boot/dts/rockchip/Makefile | 3 +
.../rockchip/rk3576-anbernic-rg-vita-pro.dts | 1317 +++++++++++++++++
drivers/gpu/drm/panel/Kconfig | 10 +
drivers/gpu/drm/panel/Makefile | 1 +
drivers/gpu/drm/panel/panel-anbernic-td4310.c | 291 ++++
drivers/power/supply/Kconfig | 8 +
drivers/power/supply/Makefile | 1 +
drivers/power/supply/sgm41542_charger.c | 1035 +++++++++++++
11 files changed, 2835 insertions(+)
create mode 100644 Documentation/devicetree/bindings/display/panel/anbernic,td4310.yaml
create mode 100644 Documentation/devicetree/bindings/power/supply/sgmicro,sgm41542.yaml
create mode 100644 arch/arm64/boot/dts/rockchip/rk3576-anbernic-rg-vita-pro.dts
create mode 100644 drivers/gpu/drm/panel/panel-anbernic-td4310.c
create mode 100644 drivers/power/supply/sgm41542_charger.c
--
2.43.0
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH V2 1/6] dt-bindings: power: supply: sgm41542: document sgm41542
2026-05-15 22:19 ` Chris Morgan
@ 2026-05-15 22:19 ` Chris Morgan
-1 siblings, 0 replies; 19+ messages in thread
From: Chris Morgan @ 2026-05-15 22:19 UTC (permalink / raw)
To: linux-rockchip
Cc: devicetree, xsf, sre, simona, airlied, tzimmermann, mripard,
maarten.lankhorst, jesszhan0024, neil.armstrong, heiko, conor+dt,
krzk+dt, robh, Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Document the SG Micro sgm41542 battery charger/boost converter.
The parameters of input-current-limit-microamp and
input-voltage-limit-microvolt are defined as such since they are in
common use among multiple bindings currently.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
.../power/supply/sgmicro,sgm41542.yaml | 99 +++++++++++++++++++
1 file changed, 99 insertions(+)
create mode 100644 Documentation/devicetree/bindings/power/supply/sgmicro,sgm41542.yaml
diff --git a/Documentation/devicetree/bindings/power/supply/sgmicro,sgm41542.yaml b/Documentation/devicetree/bindings/power/supply/sgmicro,sgm41542.yaml
new file mode 100644
index 000000000000..fa0d31e496f7
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/supply/sgmicro,sgm41542.yaml
@@ -0,0 +1,99 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/power/supply/sgmicro,sgm41542-charger.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: SGM41542 Battery Charger
+
+description:
+ The SGMicro SGM41542 is a single cell battery charger/boost controller.
+
+maintainers:
+ - Chris Morgan <macromorgan@hotmail.com>
+ - Xu Shengfei <xsf@rock-chips.com>
+
+properties:
+ compatible:
+ const: sgmicro,sgm41542
+
+ input-current-limit-microamp:
+ description:
+ Optional value to clamp the maximum input current limit to for
+ the device. If omitted, the default value for the hardware will
+ be used (2400000).
+ minimum: 100000
+ maximum: 3800000
+
+ input-voltage-limit-microvolt:
+ description:
+ Optional value to clamp the maximum input voltage limit to for
+ the device. If omitted, the default value for the hardware will
+ be used (4500000).
+ minimum: 3900000
+ maximum: 12000000
+
+ interrupts:
+ maxItems: 1
+
+ monitored-battery:
+ description: |
+ A phandle to a monitored battery node. Values for the following
+ are used by the driver and if not present will result in default
+ values being applied:
+ constant-charge-current-max-microamp
+ constant-charge-voltage-max-microvolt
+ charge-term-current-microamp
+ precharge-current-microamp
+
+ reg:
+ maxItems: 1
+
+ regulators:
+ type: object
+ properties:
+ otg-vbus:
+ type: object
+ description: OTG boost regulator
+ $ref: /schemas/regulator/regulator.yaml
+ unevaluatedProperties: false
+ additionalProperties: false
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ battery: battery {
+ compatible = "simple-battery";
+ constant-charge-current-max-microamp = <10000000>;
+ constant-charge-voltage-max-microvolt = <4350000>;
+ precharge-current-microamp = <180000>;
+ charge-term-current-microamp = <300000>;
+ };
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ charger@3b {
+ compatible = "sgmicro,sgm41542";
+ reg = <0x3b>;
+ input-current-limit-microamp = <3000000>;
+ input-voltage-limit-microvolt = <4500000>;
+ interrupt-parent = <&gpio0>;
+ interrupts = <26 IRQ_TYPE_EDGE_FALLING>;
+ monitored-battery = <&battery>;
+
+ regulators {
+ otg-vbus {
+ regulator-max-microvolt = <5000000>;
+ regulator-min-microvolt = <5000000>;
+ };
+ };
+ };
+ };
--
2.43.0
_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH V2 1/6] dt-bindings: power: supply: sgm41542: document sgm41542
@ 2026-05-15 22:19 ` Chris Morgan
0 siblings, 0 replies; 19+ messages in thread
From: Chris Morgan @ 2026-05-15 22:19 UTC (permalink / raw)
To: linux-rockchip
Cc: devicetree, xsf, sre, simona, airlied, tzimmermann, mripard,
maarten.lankhorst, jesszhan0024, neil.armstrong, heiko, conor+dt,
krzk+dt, robh, Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Document the SG Micro sgm41542 battery charger/boost converter.
The parameters of input-current-limit-microamp and
input-voltage-limit-microvolt are defined as such since they are in
common use among multiple bindings currently.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
.../power/supply/sgmicro,sgm41542.yaml | 99 +++++++++++++++++++
1 file changed, 99 insertions(+)
create mode 100644 Documentation/devicetree/bindings/power/supply/sgmicro,sgm41542.yaml
diff --git a/Documentation/devicetree/bindings/power/supply/sgmicro,sgm41542.yaml b/Documentation/devicetree/bindings/power/supply/sgmicro,sgm41542.yaml
new file mode 100644
index 000000000000..fa0d31e496f7
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/supply/sgmicro,sgm41542.yaml
@@ -0,0 +1,99 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/power/supply/sgmicro,sgm41542-charger.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: SGM41542 Battery Charger
+
+description:
+ The SGMicro SGM41542 is a single cell battery charger/boost controller.
+
+maintainers:
+ - Chris Morgan <macromorgan@hotmail.com>
+ - Xu Shengfei <xsf@rock-chips.com>
+
+properties:
+ compatible:
+ const: sgmicro,sgm41542
+
+ input-current-limit-microamp:
+ description:
+ Optional value to clamp the maximum input current limit to for
+ the device. If omitted, the default value for the hardware will
+ be used (2400000).
+ minimum: 100000
+ maximum: 3800000
+
+ input-voltage-limit-microvolt:
+ description:
+ Optional value to clamp the maximum input voltage limit to for
+ the device. If omitted, the default value for the hardware will
+ be used (4500000).
+ minimum: 3900000
+ maximum: 12000000
+
+ interrupts:
+ maxItems: 1
+
+ monitored-battery:
+ description: |
+ A phandle to a monitored battery node. Values for the following
+ are used by the driver and if not present will result in default
+ values being applied:
+ constant-charge-current-max-microamp
+ constant-charge-voltage-max-microvolt
+ charge-term-current-microamp
+ precharge-current-microamp
+
+ reg:
+ maxItems: 1
+
+ regulators:
+ type: object
+ properties:
+ otg-vbus:
+ type: object
+ description: OTG boost regulator
+ $ref: /schemas/regulator/regulator.yaml
+ unevaluatedProperties: false
+ additionalProperties: false
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ battery: battery {
+ compatible = "simple-battery";
+ constant-charge-current-max-microamp = <10000000>;
+ constant-charge-voltage-max-microvolt = <4350000>;
+ precharge-current-microamp = <180000>;
+ charge-term-current-microamp = <300000>;
+ };
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ charger@3b {
+ compatible = "sgmicro,sgm41542";
+ reg = <0x3b>;
+ input-current-limit-microamp = <3000000>;
+ input-voltage-limit-microvolt = <4500000>;
+ interrupt-parent = <&gpio0>;
+ interrupts = <26 IRQ_TYPE_EDGE_FALLING>;
+ monitored-battery = <&battery>;
+
+ regulators {
+ otg-vbus {
+ regulator-max-microvolt = <5000000>;
+ regulator-min-microvolt = <5000000>;
+ };
+ };
+ };
+ };
--
2.43.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH V2 2/6] power: supply: sgm41542: Add SG Micro sgm41542 charger
2026-05-15 22:19 ` Chris Morgan
@ 2026-05-15 22:19 ` Chris Morgan
-1 siblings, 0 replies; 19+ messages in thread
From: Chris Morgan @ 2026-05-15 22:19 UTC (permalink / raw)
To: linux-rockchip
Cc: devicetree, xsf, sre, simona, airlied, tzimmermann, mripard,
maarten.lankhorst, jesszhan0024, neil.armstrong, heiko, conor+dt,
krzk+dt, robh, Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add support for the SG Micro SGM41542 charger/boost converter.
Driver was adapted from Rockchip BSP driver [1] and confirmed
with vendor datasheet [2].
[1] https://github.com/rockchip-linux/kernel/blob/develop-6.6/drivers/power/supply/sgm41542_charger.c
[2] https://www.sg-micro.de/rect/assets/1e8de70b-657e-4156-be68-a64fdbe8e418/SGM41541_SGM41542.pdf
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
drivers/power/supply/Kconfig | 8 +
drivers/power/supply/Makefile | 1 +
drivers/power/supply/sgm41542_charger.c | 1035 +++++++++++++++++++++++
3 files changed, 1044 insertions(+)
create mode 100644 drivers/power/supply/sgm41542_charger.c
diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index 83392ed6a8da..57dae0913472 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -856,6 +856,14 @@ config CHARGER_RK817
help
Say Y to include support for Rockchip RK817 Battery Charger.
+config CHARGER_SGM41542
+ tristate "SGM41542 charger driver"
+ depends on I2C
+ depends on GPIOLIB || COMPILE_TEST
+ select REGMAP_I2C
+ help
+ Say Y to enable support for the SGM41542 battery charger.
+
config CHARGER_SMB347
tristate "Summit Microelectronics SMB3XX Battery Charger"
depends on I2C
diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
index 7ee839dca7f3..c376889db317 100644
--- a/drivers/power/supply/Makefile
+++ b/drivers/power/supply/Makefile
@@ -107,6 +107,7 @@ obj-$(CONFIG_CHARGER_BQ25890) += bq25890_charger.o
obj-$(CONFIG_CHARGER_BQ25980) += bq25980_charger.o
obj-$(CONFIG_CHARGER_BQ256XX) += bq256xx_charger.o
obj-$(CONFIG_CHARGER_RK817) += rk817_charger.o
+obj-$(CONFIG_CHARGER_SGM41542) += sgm41542_charger.o
obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o
obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o
obj-$(CONFIG_CHARGER_TPS65217) += tps65217_charger.o
diff --git a/drivers/power/supply/sgm41542_charger.c b/drivers/power/supply/sgm41542_charger.c
new file mode 100644
index 000000000000..1bc2eab55406
--- /dev/null
+++ b/drivers/power/supply/sgm41542_charger.c
@@ -0,0 +1,1035 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Chrager driver for Sgm4154x
+ *
+ * Copyright (c) 2026 Rockchip Electronics Co., Ltd.
+ *
+ * Author: Xu Shengfei <xsf@rock-chips.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/power_supply.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/machine.h>
+#include <linux/types.h>
+
+#define SGM4154X_MANUFACTURER "SGMICRO"
+#define SGM4154X_NAME "sgm41542"
+
+#define SGM4154X_CHRG_CTRL_0 0x00
+#define SGM4154X_HIZ_EN BIT(7)
+#define SGM4154X_IINDPM_I_MASK GENMASK(4, 0)
+#define SGM4154X_IINDPM_I_MIN_UA 100000
+#define SGM4154X_IINDPM_I_MAX_UA 3800000
+#define SGM4154X_IINDPM_STEP_UA 100000
+#define SGM4154X_IINDPM_DEF_UA 2400000
+
+#define SGM4154X_CHRG_CTRL_1 0x01
+#define SGM4154X_WDT_RST BIT(6)
+#define SGM4154X_OTG_EN BIT(5)
+#define SGM4154X_CHRG_EN BIT(4)
+
+#define SGM4154X_CHRG_CTRL_2 0x02
+#define SGM4154X_BOOST_LIM BIT(7)
+#define SGM4154X_ICHRG_CUR_MASK GENMASK(5, 0)
+#define SGM4154X_ICHRG_I_STEP_UA 60000
+#define SGM4154X_ICHRG_I_MIN_UA 0
+#define SGM4154X_ICHRG_I_MAX_UA 3780000
+#define SGM4154X_ICHRG_I_DEF_UA 2040000
+
+#define SGM4154X_CHRG_CTRL_3 0x03
+#define SGM4154X_PRECHRG_CUR_MASK GENMASK(7, 4)
+#define SGM4154X_PRECHRG_CURRENT_STEP_UA 60000
+#define SGM4154X_PRECHRG_I_MIN_UA 60000
+#define SGM4154X_PRECHRG_I_MAX_UA 780000
+#define SGM4154X_PRECHRG_I_DEF_UA 180000
+#define SGM4154X_TERMCHRG_CUR_MASK GENMASK(3, 0)
+#define SGM4154X_TERMCHRG_CURRENT_STEP_UA 60000
+#define SGM4154X_TERMCHRG_I_MIN_UA 60000
+#define SGM4154X_TERMCHRG_I_MAX_UA 960000
+#define SGM4154X_TERMCHRG_I_DEF_UA 180000
+
+#define SGM4154X_CHRG_CTRL_4 0x04
+#define SGM4154X_VREG_V_MASK GENMASK(7, 3)
+#define SGM4154X_VREG_V_MAX_UV 4624000
+#define SGM4154X_VREG_V_MIN_UV 3856000
+#define SGM4154X_VREG_V_DEF_UV 4208000
+#define SGM4154X_VREG_V_STEP_UV 32000
+#define SGM4154X_VRECHARGE BIT(0)
+#define SGM4154X_VRECHRG_STEP_MV 100
+#define SGM4154X_VRECHRG_OFFSET_MV 100
+
+#define SGM4154X_CHRG_CTRL_5 0x05
+#define SGM4154X_TERM_EN BIT(7)
+#define SGM4154X_WDT_TIMER_MASK GENMASK(5, 4)
+#define SGM4154X_WDT_TIMER_40S BIT(4)
+#define SGM4154X_WDT_TIMER_80S BIT(5)
+#define SGM4154X_WDT_TIMER_160S (BIT(4) | BIT(5))
+#define SGM4154X_WDT_TIMER_DISABLE 0
+
+#define SGM4154X_CHRG_CTRL_6 0x06
+#define SGM4154X_VAC_OVP_MASK GENMASK(7, 6)
+#define SGM4154X_OVP_14V (BIT(7) | BIT(6))
+#define SGM4154X_OVP_10_5V BIT(7)
+#define SGM4154X_OVP_6_5V BIT(6)
+#define SGM4154X_OVP_5_5V 0
+#define SGM4154X_OVP_DEFAULT SGM4154X_OVP_14V
+#define SGM4154X_BOOSTV GENMASK(5, 4)
+#define SGM4154X_VINDPM_V_MASK GENMASK(3, 0)
+#define SGM4154X_VINDPM_V_MIN_UV 3900000
+#define SGM4154X_VINDPM_V_MAX_UV 12000000
+#define SGM4154X_VINDPM_STEP_UV 100000
+#define SGM4154X_VINDPM_DEF_UV 4500000
+
+#define SGM4154X_CHRG_CTRL_7 0x07
+
+#define SGM4154X_CHRG_STAT 0x08
+#define SGM4154X_VBUS_STAT_MASK GENMASK(7, 5)
+#define SGM4154X_OTG_MODE (BIT(7) | BIT(6) | BIT(5))
+#define SGM4154X_NON_STANDARD (BIT(7) | BIT(6))
+#define SGM4154X_UNKNOWN (BIT(7) | BIT(5))
+#define SGM4154X_USB_DCP (BIT(6) | BIT(5))
+#define SGM4154X_USB_CDP BIT(6)
+#define SGM4154X_USB_SDP BIT(5)
+#define SGM4154X_NOT_CHRGING 0
+#define SGM4154X_CHG_STAT_MASK GENMASK(4, 3)
+#define SGM4154X_TERM_CHRG (BIT(4) | BIT(3))
+#define SGM4154X_FAST_CHRG BIT(4)
+#define SGM4154X_PRECHRG BIT(3)
+#define SGM4154X_PG_STAT BIT(2)
+#define SGM4154X_THERM_STAT BIT(1)
+#define SGM4154X_VSYS_STAT BIT(0)
+
+#define SGM4154X_CHRG_FAULT 0x09
+#define SGM4154X_TEMP_MASK GENMASK(2, 0)
+#define SGM4154X_TEMP_HOT (BIT(2) | BIT(1))
+#define SGM4154X_TEMP_COLD (BIT(2) | BIT(0))
+#define SGM4154X_TEMP_COOL (BIT(1) | BIT(0))
+#define SGM4154X_TEMP_WARM BIT(1)
+#define SGM4154X_TEMP_NORMAL BIT(0)
+
+#define SGM4154X_CHRG_CTRL_A 0x0a
+#define SGM4154X_VBUS_GOOD BIT(7)
+#define SGM4154X_VINDPM_INT_MASK BIT(1)
+#define SGM4154X_IINDPM_INT_MASK BIT(0)
+
+#define SGM4154X_CHRG_CTRL_B 0x0b
+#define SGM4154X_PN_ID (BIT(6) | BIT(5) | BIT(3))
+#define SGM4154X_PN_MASK GENMASK(6, 3)
+
+#define SGM4154X_CHRG_CTRL_C 0x0c
+
+#define SGM4154X_CHRG_CTRL_D 0x0d
+#define SGM4154X_JEITA_EN BIT(0)
+
+#define SGM4154X_INPUT_DET 0x0e
+#define SGM4154X_DPDM_ONGOING BIT(7)
+
+#define SGM4154X_CHRG_CTRL_F 0x0f
+#define SGM4154X_VINDPM_OS_MASK GENMASK(1, 0)
+
+#define SGM4154X_DEFAULT_INPUT_CUR (500 * 1000)
+
+struct sgm4154x_init_data {
+ int ilim; /* input current limit */
+ int vlim; /* minimum system voltage limit */
+ int iterm; /* termination current */
+ int iprechg; /* precharge current */
+ int max_ichg; /* maximum charge current */
+ int max_vreg; /* maximum charge voltage */
+};
+
+struct sgm4154x_state {
+ bool vsys_stat;
+ bool therm_stat;
+ bool online;
+ u8 chrg_stat;
+ bool chrg_en;
+ bool vbus_gd;
+ u8 chrg_type;
+ u8 health;
+ u8 chrg_fault;
+ u8 ntc_fault;
+};
+
+struct sgm4154x_device {
+ struct i2c_client *client;
+ struct device *dev;
+ struct power_supply *charger;
+ struct regmap *regmap;
+ char model_name[I2C_NAME_SIZE];
+ struct sgm4154x_init_data init_data;
+ struct sgm4154x_state state;
+ struct regulator_dev *otg_rdev;
+ bool watchdog_enable;
+ struct workqueue_struct *sgm_monitor_wq;
+ struct delayed_work sgm_delay_work;
+};
+
+enum SGM4154X_VINDPM_OS {
+ VINDPM_OS_3900MV,
+ VINDPM_OS_5900MV,
+ VINDPM_OS_7500MV,
+ VINDPM_OS_10500MV,
+};
+
+static int sgm4154x_set_term_curr(struct sgm4154x_device *sgm, int cur_ua)
+{
+ int reg_val;
+ int ret;
+
+ cur_ua = clamp(cur_ua, SGM4154X_TERMCHRG_I_MIN_UA, SGM4154X_TERMCHRG_I_MAX_UA);
+ reg_val = (cur_ua - SGM4154X_TERMCHRG_I_MIN_UA) / SGM4154X_TERMCHRG_CURRENT_STEP_UA;
+
+ ret = regmap_update_bits(sgm->regmap,
+ SGM4154X_CHRG_CTRL_3,
+ SGM4154X_TERMCHRG_CUR_MASK,
+ reg_val);
+ if (ret)
+ dev_err(sgm->dev, "set term current error!\n");
+
+ return ret;
+}
+
+static int sgm4154x_set_prechrg_curr(struct sgm4154x_device *sgm, int cur_ua)
+{
+ int reg_val;
+ int ret;
+
+ cur_ua = clamp(cur_ua, SGM4154X_PRECHRG_I_MIN_UA, SGM4154X_PRECHRG_I_MAX_UA);
+ reg_val = (cur_ua - SGM4154X_PRECHRG_I_MIN_UA) / SGM4154X_PRECHRG_CURRENT_STEP_UA;
+
+ reg_val = reg_val << 4;
+ ret = regmap_update_bits(sgm->regmap,
+ SGM4154X_CHRG_CTRL_3,
+ SGM4154X_PRECHRG_CUR_MASK,
+ reg_val);
+ if (ret)
+ dev_err(sgm->dev, "set precharge current error!\n");
+
+ return ret;
+}
+
+static int sgm4154x_set_ichrg_curr(struct sgm4154x_device *sgm, int cur_ua)
+{
+ int reg_val;
+ int ret;
+
+ cur_ua = clamp(cur_ua, SGM4154X_ICHRG_I_MIN_UA, SGM4154X_ICHRG_I_MAX_UA);
+ reg_val = cur_ua / SGM4154X_ICHRG_I_STEP_UA;
+
+ ret = regmap_update_bits(sgm->regmap,
+ SGM4154X_CHRG_CTRL_2,
+ SGM4154X_ICHRG_CUR_MASK,
+ reg_val);
+ if (ret)
+ dev_err(sgm->dev, "set icharge current error!\n");
+
+ return ret;
+}
+
+static int sgm4154x_get_ichrg_curr(struct sgm4154x_device *sgm)
+{
+ u32 reg;
+ int ret, val;
+
+ ret = regmap_read(sgm->regmap, SGM4154X_CHRG_CTRL_2, ®);
+ if (ret) {
+ dev_err(sgm->dev, "get charge current error!\n");
+ return ret;
+ }
+
+ val = FIELD_GET(SGM4154X_ICHRG_CUR_MASK, reg);
+
+ return val * SGM4154X_ICHRG_I_STEP_UA;
+}
+
+static int sgm4154x_set_chrg_volt(struct sgm4154x_device *sgm, int chrg_volt)
+{
+ int reg_val;
+ int ret;
+
+ /*
+ * Note that the value of 0x01111 represents a "special value"
+ * corresponding to 4352000uV instead of the expected 4336000uV,
+ * per the datasheet. All other values are as expected.
+ */
+ chrg_volt = clamp(chrg_volt, SGM4154X_VREG_V_MIN_UV, sgm->init_data.max_vreg);
+ reg_val = (chrg_volt - SGM4154X_VREG_V_MIN_UV) / SGM4154X_VREG_V_STEP_UV;
+ reg_val = reg_val << 3;
+ ret = regmap_update_bits(sgm->regmap,
+ SGM4154X_CHRG_CTRL_4,
+ SGM4154X_VREG_V_MASK,
+ reg_val);
+ if (ret)
+ dev_err(sgm->dev, "set charge voltage error!\n");
+
+ return ret;
+}
+
+static int sgm4154x_get_chrg_volt(struct sgm4154x_device *sgm)
+{
+ u32 reg;
+ int ret, val;
+
+ ret = regmap_read(sgm->regmap, SGM4154X_CHRG_CTRL_4, ®);
+ if (ret) {
+ dev_err(sgm->dev, "get charge voltage error!\n");
+ return ret;
+ }
+
+ val = FIELD_GET(SGM4154X_VREG_V_MASK, reg);
+
+ /*
+ * 0x01111 is a special value meaning 4352000uV, all other
+ * values are as expected based on the offset and step values.
+ */
+ if (val == 0x0f)
+ return 4352000;
+
+ return val * SGM4154X_VREG_V_STEP_UV + SGM4154X_VREG_V_MIN_UV;
+}
+
+static int sgm4154x_set_input_volt_lim(struct sgm4154x_device *sgm,
+ unsigned int vindpm)
+{
+ enum SGM4154X_VINDPM_OS os_val;
+ unsigned int offset;
+ u8 reg_val;
+ int ret;
+
+
+ if (vindpm < SGM4154X_VINDPM_V_MIN_UV ||
+ vindpm > SGM4154X_VINDPM_V_MAX_UV) {
+ dev_err(sgm->dev, "input voltage limit %u outside range\n", vindpm);
+ return -EINVAL;
+ }
+
+ if (vindpm < 5900000) {
+ os_val = VINDPM_OS_3900MV;
+ offset = 3900000;
+ } else if (vindpm >= 5900000 && vindpm < 7500000) {
+ os_val = VINDPM_OS_5900MV;
+ offset = 5900000;
+ } else if (vindpm >= 7500000 && vindpm < 10500000) {
+ os_val = VINDPM_OS_7500MV;
+ offset = 7500000;
+ } else {
+ os_val = VINDPM_OS_10500MV;
+ offset = 10500000;
+ }
+
+ ret = regmap_update_bits(sgm->regmap,
+ SGM4154X_CHRG_CTRL_F,
+ SGM4154X_VINDPM_OS_MASK,
+ os_val);
+ if (ret) {
+ dev_err(sgm->dev, "set vin dpm error!\n");
+ return ret;
+ }
+
+ reg_val = (vindpm - offset) / SGM4154X_VINDPM_STEP_UV;
+
+ ret = regmap_update_bits(sgm->regmap, SGM4154X_CHRG_CTRL_6,
+ SGM4154X_VINDPM_V_MASK, reg_val);
+ if (ret)
+ dev_err(sgm->dev, "input voltage error!\n");
+
+ return ret;
+}
+
+static int sgm4154x_set_input_curr_lim(struct sgm4154x_device *sgm, int iindpm)
+{
+ int reg_val;
+ int ret;
+
+ if (iindpm < SGM4154X_IINDPM_I_MIN_UA)
+ return -EINVAL;
+
+ /*
+ * Per the datasheet, values between 100000uA and 3100000uA work
+ * as expected with the register defined as having a step of
+ * 100000 and a min/max of 100000 (0x00) through 3100000 (0x1e).
+ * The register value of 0x1f however corresponds to 3800000uA not
+ * 3200000uA as one would expect.
+ */
+
+ if (iindpm > 3100000 && iindpm < SGM4154X_IINDPM_I_MAX_UA)
+ iindpm = 3100000;
+ if ((iindpm > SGM4154X_IINDPM_I_MAX_UA) || (iindpm > sgm->init_data.ilim))
+ iindpm = min(SGM4154X_IINDPM_I_MAX_UA, sgm->init_data.ilim);
+
+ reg_val = (iindpm - SGM4154X_IINDPM_I_MIN_UA) / SGM4154X_IINDPM_STEP_UA;
+
+ ret = regmap_update_bits(sgm->regmap,
+ SGM4154X_CHRG_CTRL_0,
+ SGM4154X_IINDPM_I_MASK,
+ reg_val);
+ if (ret)
+ dev_err(sgm->dev, "set input current limit error!\n");
+
+ return ret;
+}
+
+static int sgm4154x_get_input_curr_lim(struct sgm4154x_device *sgm)
+{
+ int ret;
+ int ilim;
+
+ ret = regmap_read(sgm->regmap, SGM4154X_CHRG_CTRL_0, &ilim);
+ if (ret) {
+ dev_err(sgm->dev, "get input current limit error!\n");
+ return ret;
+ }
+
+ /* Max value is not 3200000uA as expected but is 3800000uA */
+ if (SGM4154X_IINDPM_I_MASK == (ilim & SGM4154X_IINDPM_I_MASK))
+ return SGM4154X_IINDPM_I_MAX_UA;
+
+ ilim = (ilim & SGM4154X_IINDPM_I_MASK) * SGM4154X_IINDPM_STEP_UA + SGM4154X_IINDPM_I_MIN_UA;
+
+ return ilim;
+}
+
+static int sgm4154x_watchdog_timer_reset(struct sgm4154x_device *sgm)
+{
+ int ret;
+
+ ret = regmap_update_bits(sgm->regmap,
+ SGM4154X_CHRG_CTRL_1,
+ SGM4154X_WDT_RST,
+ SGM4154X_WDT_RST);
+
+ if (ret)
+ dev_err(sgm->dev, "set watchdog timer error!\n");
+
+ return ret;
+}
+
+static int sgm4154x_set_watchdog_timer(struct sgm4154x_device *sgm, int time)
+{
+ u8 reg_val;
+ int ret;
+
+ if (time == 0)
+ reg_val = SGM4154X_WDT_TIMER_DISABLE;
+ else if (time == 40)
+ reg_val = SGM4154X_WDT_TIMER_40S;
+ else if (time == 80)
+ reg_val = SGM4154X_WDT_TIMER_80S;
+ else
+ reg_val = SGM4154X_WDT_TIMER_160S;
+
+ ret = regmap_update_bits(sgm->regmap,
+ SGM4154X_CHRG_CTRL_5,
+ SGM4154X_WDT_TIMER_MASK,
+ reg_val);
+
+ if (ret) {
+ dev_err(sgm->dev, "set watchdog timer error!\n");
+ return ret;
+ }
+
+ if (time) {
+ if (!sgm->watchdog_enable)
+ queue_delayed_work(sgm->sgm_monitor_wq,
+ &sgm->sgm_delay_work,
+ msecs_to_jiffies(1000 * 5));
+ sgm->watchdog_enable = true;
+ } else {
+ sgm->watchdog_enable = false;
+ sgm4154x_watchdog_timer_reset(sgm);
+ }
+
+ return ret;
+}
+
+static int sgm4154x_enable_charger(struct sgm4154x_device *sgm)
+{
+ int ret;
+
+ ret = regmap_update_bits(sgm->regmap,
+ SGM4154X_CHRG_CTRL_1,
+ SGM4154X_CHRG_EN,
+ SGM4154X_CHRG_EN);
+ if (ret)
+ dev_err(sgm->dev, "enable charger error!\n");
+
+ return ret;
+}
+
+static int sgm4154x_disable_charger(struct sgm4154x_device *sgm)
+{
+ int ret;
+
+ ret = regmap_update_bits(sgm->regmap,
+ SGM4154X_CHRG_CTRL_1,
+ SGM4154X_CHRG_EN,
+ 0);
+ if (ret)
+ dev_err(sgm->dev, "disable charger error!\n");
+
+ return ret;
+}
+
+static int sgm4154x_set_vac_ovp(struct sgm4154x_device *sgm)
+{
+ int reg_val;
+ int ret;
+
+ reg_val = SGM4154X_OVP_DEFAULT & SGM4154X_VAC_OVP_MASK;
+
+ ret = regmap_update_bits(sgm->regmap,
+ SGM4154X_CHRG_CTRL_6,
+ SGM4154X_VAC_OVP_MASK,
+ reg_val);
+ if (ret)
+ dev_err(sgm->dev, "set vac ovp error!\n");
+
+ return ret;
+}
+
+static int sgm4154x_set_recharge_volt(struct sgm4154x_device *sgm, int recharge_volt)
+{
+ int reg_val;
+ int ret;
+
+ reg_val = (recharge_volt - SGM4154X_VRECHRG_OFFSET_MV) / SGM4154X_VRECHRG_STEP_MV;
+
+ ret = regmap_update_bits(sgm->regmap,
+ SGM4154X_CHRG_CTRL_4,
+ SGM4154X_VRECHARGE,
+ reg_val);
+ if (ret)
+ dev_err(sgm->dev, "set recharger error!\n");
+
+ return ret;
+}
+
+static int sgm4154x_get_state(struct sgm4154x_device *sgm,
+ struct sgm4154x_state *state)
+{
+ int reg, ret;
+
+ ret = regmap_read(sgm->regmap, SGM4154X_CHRG_STAT, ®);
+ if (ret) {
+ dev_err(sgm->dev, "read SGM4154X_CHRG_STAT fail\n");
+ return ret;
+ }
+ state->chrg_type = reg & SGM4154X_VBUS_STAT_MASK;
+ state->chrg_stat = reg & SGM4154X_CHG_STAT_MASK;
+ state->online = !!(reg & SGM4154X_PG_STAT);
+ state->therm_stat = !!(reg & SGM4154X_THERM_STAT);
+ state->vsys_stat = !!(reg & SGM4154X_VSYS_STAT);
+
+ ret = regmap_read(sgm->regmap, SGM4154X_CHRG_FAULT, ®);
+ if (ret) {
+ dev_err(sgm->dev, "read SGM4154X_CHRG_FAULT fail\n");
+ return ret;
+ }
+ state->chrg_fault = reg;
+ state->ntc_fault = reg & SGM4154X_TEMP_MASK;
+ state->health = state->ntc_fault;
+
+ ret = regmap_read(sgm->regmap, SGM4154X_CHRG_CTRL_A, ®);
+ if (ret) {
+ dev_err(sgm->dev, "read SGM4154X_CHRG_CTRL_A fail\n");
+ return ret;
+ }
+ state->vbus_gd = !!(reg & SGM4154X_VBUS_GOOD);
+
+ return ret;
+}
+
+static int sgm4154x_property_is_writeable(struct power_supply *psy,
+ enum power_supply_property prop)
+{
+ switch (prop) {
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+ case POWER_SUPPLY_PROP_ONLINE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int sgm4154x_charger_set_property(struct power_supply *psy,
+ enum power_supply_property prop,
+ const union power_supply_propval *val)
+{
+ struct sgm4154x_device *sgm = power_supply_get_drvdata(psy);
+ int ret = -EINVAL;
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ if (val->intval) {
+ ret = sgm4154x_enable_charger(sgm);
+ sgm4154x_set_watchdog_timer(sgm, SGM4154X_WDT_TIMER_40S);
+ } else {
+ sgm4154x_set_watchdog_timer(sgm, 0);
+ ret = sgm4154x_disable_charger(sgm);
+ }
+ break;
+
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+ ret = sgm4154x_set_input_curr_lim(sgm, val->intval);
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+ ret = sgm4154x_set_ichrg_curr(sgm, val->intval);
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+ ret = sgm4154x_set_chrg_volt(sgm, val->intval);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int sgm4154x_charger_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct sgm4154x_device *sgm = power_supply_get_drvdata(psy);
+ struct sgm4154x_state state;
+ int ret;
+
+ ret = sgm4154x_get_state(sgm, &state);
+ if (ret) {
+ dev_err(sgm->dev, "get state error!\n");
+ return ret;
+ }
+ sgm->state = state;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ if (!state.chrg_type || (state.chrg_type == SGM4154X_OTG_MODE))
+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ else if (!state.chrg_stat)
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ else if (state.chrg_stat == SGM4154X_TERM_CHRG)
+ val->intval = POWER_SUPPLY_STATUS_FULL;
+ else
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ switch (state.chrg_stat) {
+ case SGM4154X_PRECHRG:
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+ break;
+ case SGM4154X_FAST_CHRG:
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
+ break;
+ case SGM4154X_TERM_CHRG:
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+ break;
+ case SGM4154X_NOT_CHRGING:
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
+ break;
+ default:
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+ }
+ break;
+ case POWER_SUPPLY_PROP_MANUFACTURER:
+ val->strval = SGM4154X_MANUFACTURER;
+ break;
+
+ case POWER_SUPPLY_PROP_MODEL_NAME:
+ val->strval = SGM4154X_NAME;
+ break;
+
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = state.online;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = state.vbus_gd;
+ break;
+ case POWER_SUPPLY_PROP_TYPE:
+ val->intval = POWER_SUPPLY_TYPE_USB;
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+ val->intval = sgm4154x_get_chrg_volt(sgm);
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+ val->intval = sgm4154x_get_ichrg_curr(sgm);
+ break;
+ case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
+ val->intval = sgm->init_data.vlim;
+ break;
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+ val->intval = sgm4154x_get_input_curr_lim(sgm);
+ if (val->intval < 0)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static irqreturn_t sgm4154x_irq_handler_thread(int irq, void *private)
+{
+ struct sgm4154x_device *sgm4154x = private;
+ struct sgm4154x_state state;
+ int ret;
+
+ ret = sgm4154x_get_state(sgm4154x, &state);
+ if (ret) {
+ dev_err(sgm4154x->dev, "get state error!\n");
+ return IRQ_NONE;
+ }
+ sgm4154x->state = state;
+ if (state.vbus_gd) {
+ if (sgm4154x->init_data.ilim >= SGM4154X_DEFAULT_INPUT_CUR) {
+ ret = sgm4154x_set_input_curr_lim(sgm4154x, sgm4154x->init_data.ilim);
+ if (ret) {
+ dev_err(sgm4154x->dev, "set input current error!\n");
+ return IRQ_NONE;
+ }
+ }
+ }
+ power_supply_changed(sgm4154x->charger);
+
+ return IRQ_HANDLED;
+}
+
+static enum power_supply_property sgm4154x_power_supply_props[] = {
+ POWER_SUPPLY_PROP_TYPE,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT,
+ POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
+ POWER_SUPPLY_PROP_CHARGE_TYPE,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+ POWER_SUPPLY_PROP_PRESENT
+};
+
+static char *sgm4154x_charger_supplied_to[] = {
+ "battery-monitor",
+};
+
+static struct power_supply_desc sgm4154x_power_supply_desc = {
+ .name = "sgm4154x-charger",
+ .type = POWER_SUPPLY_TYPE_USB,
+ .properties = sgm4154x_power_supply_props,
+ .num_properties = ARRAY_SIZE(sgm4154x_power_supply_props),
+ .get_property = sgm4154x_charger_get_property,
+ .set_property = sgm4154x_charger_set_property,
+ .property_is_writeable = sgm4154x_property_is_writeable,
+};
+
+static const struct regmap_config sgm4154x_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = SGM4154X_CHRG_CTRL_F,
+ .cache_type = REGCACHE_NONE,
+};
+
+static int sgm4154x_power_supply_init(struct sgm4154x_device *sgm,
+ struct device *dev)
+{
+ struct power_supply_config psy_cfg = { .drv_data = sgm,
+ .fwnode = dev->fwnode, };
+
+ psy_cfg.supplied_to = sgm4154x_charger_supplied_to;
+ psy_cfg.num_supplicants = ARRAY_SIZE(sgm4154x_charger_supplied_to);
+ psy_cfg.fwnode = dev->fwnode;
+ sgm->charger = devm_power_supply_register(sgm->dev,
+ &sgm4154x_power_supply_desc,
+ &psy_cfg);
+ if (IS_ERR(sgm->charger))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int sgm4154x_hw_init(struct sgm4154x_device *sgm)
+{
+ struct power_supply_battery_info *bat_info;
+ int ret;
+ u32 val;
+
+ /*
+ * If unable to read devicetree info, use default/reset
+ * values from hardware.
+ */
+ sgm->init_data.iprechg = SGM4154X_PRECHRG_I_DEF_UA;
+ sgm->init_data.iterm = SGM4154X_TERMCHRG_I_DEF_UA;
+ sgm->init_data.max_ichg = SGM4154X_ICHRG_I_DEF_UA;
+ sgm->init_data.max_vreg = SGM4154X_VREG_V_DEF_UV;
+ sgm->init_data.vlim = SGM4154X_VINDPM_DEF_UV;
+ sgm->init_data.ilim = SGM4154X_IINDPM_DEF_UA;
+
+ ret = power_supply_get_battery_info(sgm->charger, &bat_info);
+ if (ret)
+ dev_warn(sgm->dev, "sgm4154x: cannot read battery info\n");
+ else {
+ if (bat_info->constant_charge_current_max_ua)
+ sgm->init_data.max_ichg = bat_info->constant_charge_current_max_ua;
+ if (bat_info->constant_charge_voltage_max_uv)
+ sgm->init_data.max_vreg = bat_info->constant_charge_voltage_max_uv;
+ if (bat_info->charge_term_current_ua)
+ sgm->init_data.iterm = bat_info->charge_term_current_ua;
+ if (bat_info->precharge_current_ua)
+ sgm->init_data.iprechg = bat_info->precharge_current_ua;
+ }
+
+ power_supply_put_battery_info(sgm->charger, bat_info);
+
+ ret = device_property_read_u32(sgm->dev,
+ "input-voltage-limit-microvolt",
+ &val);
+ if (!ret)
+ sgm->init_data.vlim = clamp(val, SGM4154X_VINDPM_V_MIN_UV,
+ SGM4154X_VINDPM_V_MAX_UV);
+
+ ret = device_property_read_u32(sgm->dev,
+ "input-current-limit-microamp",
+ &val);
+ if (!ret)
+ sgm->init_data.ilim = clamp(val, SGM4154X_IINDPM_I_MIN_UA,
+ SGM4154X_IINDPM_I_MAX_UA);
+
+ ret = sgm4154x_set_watchdog_timer(sgm, 0);
+ if (ret)
+ return ret;
+
+ ret = sgm4154x_set_prechrg_curr(sgm, sgm->init_data.iprechg);
+ if (ret)
+ return ret;
+
+ ret = sgm4154x_set_chrg_volt(sgm, sgm->init_data.max_vreg);
+ if (ret)
+ return ret;
+
+ ret = sgm4154x_set_term_curr(sgm, sgm->init_data.iterm);
+ if (ret)
+ return ret;
+
+ ret = sgm4154x_set_ichrg_curr(sgm, sgm->init_data.max_ichg);
+ if (ret)
+ return ret;
+
+ ret = sgm4154x_set_input_volt_lim(sgm, sgm->init_data.vlim);
+ if (ret)
+ return ret;
+
+ ret = sgm4154x_set_input_curr_lim(sgm, sgm->init_data.ilim);
+ if (ret)
+ return ret;
+
+ ret = sgm4154x_set_vac_ovp(sgm);
+ if (ret)
+ return ret;
+
+ regmap_update_bits(sgm->regmap,
+ SGM4154X_CHRG_CTRL_D,
+ SGM4154X_JEITA_EN,
+ 0);
+
+ regmap_update_bits(sgm->regmap,
+ SGM4154X_CHRG_CTRL_A,
+ SGM4154X_IINDPM_INT_MASK,
+ SGM4154X_IINDPM_INT_MASK);
+
+ regmap_update_bits(sgm->regmap,
+ SGM4154X_CHRG_CTRL_A,
+ SGM4154X_VINDPM_INT_MASK,
+ SGM4154X_VINDPM_INT_MASK);
+
+ /*
+ * Recharge voltage set to 200 by BSP driver instead of hardware
+ * default value of 100.
+ */
+ ret = sgm4154x_set_recharge_volt(sgm, 200);
+
+ return ret;
+}
+
+static const u32 sgm4154x_chg_otg_cur_ua[] = {
+ 1200000, 2000000,
+};
+
+static const struct regulator_ops sgm4154x_vbus_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_current_limit = regulator_set_current_limit_regmap,
+ .get_current_limit = regulator_get_current_limit_regmap,
+};
+
+static const struct regulator_desc sgm4154x_otg_rdesc = {
+ .of_match = "otg-vbus",
+ .name = "otg-vbus",
+ .regulators_node = of_match_ptr("regulators"),
+ .ops = &sgm4154x_vbus_ops,
+ .owner = THIS_MODULE,
+ .type = REGULATOR_VOLTAGE,
+ .min_uV = 4850000,
+ .uV_step = 150000,
+ .n_voltages = 4,
+ .vsel_reg = SGM4154X_CHRG_CTRL_6,
+ .vsel_mask = SGM4154X_BOOSTV,
+ .enable_reg = SGM4154X_CHRG_CTRL_1,
+ .enable_mask = SGM4154X_OTG_EN,
+ .curr_table = sgm4154x_chg_otg_cur_ua,
+ .n_current_limits = ARRAY_SIZE(sgm4154x_chg_otg_cur_ua),
+ .csel_reg = SGM4154X_CHRG_CTRL_2,
+ .csel_mask = SGM4154X_BOOST_LIM,
+};
+
+static int sgm4154x_vbus_regulator_register(struct sgm4154x_device *sgm)
+{
+ struct regulator_config config = {
+ .dev = sgm->dev,
+ .regmap = sgm->regmap,
+ .driver_data = sgm,
+ };
+
+ sgm->otg_rdev = devm_regulator_register(sgm->dev,
+ &sgm4154x_otg_rdesc,
+ &config);
+
+ return PTR_ERR_OR_ZERO(sgm->otg_rdev);
+}
+
+static int sgm4154x_hw_chipid_detect(struct sgm4154x_device *sgm)
+{
+ int ret;
+ int val;
+
+ ret = regmap_read(sgm->regmap, SGM4154X_CHRG_CTRL_B, &val);
+ if (ret)
+ return ret;
+
+ if ((val & SGM4154X_PN_MASK) != SGM4154X_PN_ID)
+ dev_warn(sgm->dev, "sgm4154x device ID mismatch\n");
+
+ return 0;
+}
+
+static void sgm_charger_work(struct work_struct *work)
+{
+ struct sgm4154x_device *sgm =
+ container_of(work,
+ struct sgm4154x_device,
+ sgm_delay_work.work);
+
+ sgm4154x_watchdog_timer_reset(sgm);
+ if (sgm->watchdog_enable)
+ queue_delayed_work(sgm->sgm_monitor_wq,
+ &sgm->sgm_delay_work,
+ msecs_to_jiffies(1000 * 5));
+}
+
+static int sgm4154x_probe(struct i2c_client *client)
+{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
+ struct device *dev = &client->dev;
+ struct sgm4154x_device *sgm;
+ int ret;
+
+ sgm = devm_kzalloc(dev, sizeof(*sgm), GFP_KERNEL);
+ if (!sgm)
+ return -ENOMEM;
+
+ sgm->client = client;
+ sgm->dev = dev;
+
+ strscpy(sgm->model_name, id->name, I2C_NAME_SIZE);
+
+ sgm->regmap = devm_regmap_init_i2c(client, &sgm4154x_regmap_config);
+ if (IS_ERR(sgm->regmap))
+ return dev_err_probe(dev, PTR_ERR(sgm->regmap),
+ "Failed to allocate register map\n");
+
+ i2c_set_clientdata(client, sgm);
+
+ ret = sgm4154x_hw_chipid_detect(sgm);
+ if (ret)
+ dev_err_probe(dev, ret, "Unable to read HW ID\n");
+
+ device_init_wakeup(dev, 1);
+
+ ret = sgm4154x_power_supply_init(sgm, dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to register power supply\n");
+
+ ret = sgm4154x_hw_init(sgm);
+ if (ret)
+ return dev_err_probe(dev, ret, "Cannot initialize the chip.\n");
+
+ if (client->irq) {
+ ret = devm_request_threaded_irq(dev, client->irq, NULL,
+ sgm4154x_irq_handler_thread,
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ "sgm41542-irq", sgm);
+ if (ret)
+ return ret;
+ enable_irq_wake(client->irq);
+ }
+
+ sgm->sgm_monitor_wq = devm_alloc_ordered_workqueue(dev, "%s",
+ WQ_MEM_RECLAIM | WQ_FREEZABLE, "sgm-monitor-wq");
+ if (!sgm->sgm_monitor_wq)
+ return -EINVAL;
+
+ INIT_DELAYED_WORK(&sgm->sgm_delay_work, sgm_charger_work);
+
+ sgm4154x_vbus_regulator_register(sgm);
+
+ return 0;
+}
+
+static void sgm4154x_charger_shutdown(struct i2c_client *client)
+{
+ struct sgm4154x_device *sgm = i2c_get_clientdata(client);
+
+ sgm4154x_set_prechrg_curr(sgm, SGM4154X_PRECHRG_I_DEF_UA);
+ sgm4154x_disable_charger(sgm);
+}
+
+static const struct i2c_device_id sgm4154x_i2c_ids[] = {
+ { "sgm41542" },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, sgm4154x_i2c_ids);
+
+static const struct of_device_id sgm4154x_of_match[] = {
+ { .compatible = "sgmicro,sgm41542", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, sgm4154x_of_match);
+
+static struct i2c_driver sgm4154x_driver = {
+ .driver = {
+ .name = "sgm4154x-charger",
+ .of_match_table = sgm4154x_of_match,
+ },
+ .probe = sgm4154x_probe,
+ .shutdown = sgm4154x_charger_shutdown,
+ .id_table = sgm4154x_i2c_ids,
+};
+
+module_i2c_driver(sgm4154x_driver);
+
+MODULE_AUTHOR("Xu Shengfei <xsf@rock-chips.com>");
+MODULE_DESCRIPTION("sgm4154x charger driver");
+MODULE_LICENSE("GPL");
--
2.43.0
_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH V2 2/6] power: supply: sgm41542: Add SG Micro sgm41542 charger
@ 2026-05-15 22:19 ` Chris Morgan
0 siblings, 0 replies; 19+ messages in thread
From: Chris Morgan @ 2026-05-15 22:19 UTC (permalink / raw)
To: linux-rockchip
Cc: devicetree, xsf, sre, simona, airlied, tzimmermann, mripard,
maarten.lankhorst, jesszhan0024, neil.armstrong, heiko, conor+dt,
krzk+dt, robh, Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add support for the SG Micro SGM41542 charger/boost converter.
Driver was adapted from Rockchip BSP driver [1] and confirmed
with vendor datasheet [2].
[1] https://github.com/rockchip-linux/kernel/blob/develop-6.6/drivers/power/supply/sgm41542_charger.c
[2] https://www.sg-micro.de/rect/assets/1e8de70b-657e-4156-be68-a64fdbe8e418/SGM41541_SGM41542.pdf
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
drivers/power/supply/Kconfig | 8 +
drivers/power/supply/Makefile | 1 +
drivers/power/supply/sgm41542_charger.c | 1035 +++++++++++++++++++++++
3 files changed, 1044 insertions(+)
create mode 100644 drivers/power/supply/sgm41542_charger.c
diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index 83392ed6a8da..57dae0913472 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -856,6 +856,14 @@ config CHARGER_RK817
help
Say Y to include support for Rockchip RK817 Battery Charger.
+config CHARGER_SGM41542
+ tristate "SGM41542 charger driver"
+ depends on I2C
+ depends on GPIOLIB || COMPILE_TEST
+ select REGMAP_I2C
+ help
+ Say Y to enable support for the SGM41542 battery charger.
+
config CHARGER_SMB347
tristate "Summit Microelectronics SMB3XX Battery Charger"
depends on I2C
diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
index 7ee839dca7f3..c376889db317 100644
--- a/drivers/power/supply/Makefile
+++ b/drivers/power/supply/Makefile
@@ -107,6 +107,7 @@ obj-$(CONFIG_CHARGER_BQ25890) += bq25890_charger.o
obj-$(CONFIG_CHARGER_BQ25980) += bq25980_charger.o
obj-$(CONFIG_CHARGER_BQ256XX) += bq256xx_charger.o
obj-$(CONFIG_CHARGER_RK817) += rk817_charger.o
+obj-$(CONFIG_CHARGER_SGM41542) += sgm41542_charger.o
obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o
obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o
obj-$(CONFIG_CHARGER_TPS65217) += tps65217_charger.o
diff --git a/drivers/power/supply/sgm41542_charger.c b/drivers/power/supply/sgm41542_charger.c
new file mode 100644
index 000000000000..1bc2eab55406
--- /dev/null
+++ b/drivers/power/supply/sgm41542_charger.c
@@ -0,0 +1,1035 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Chrager driver for Sgm4154x
+ *
+ * Copyright (c) 2026 Rockchip Electronics Co., Ltd.
+ *
+ * Author: Xu Shengfei <xsf@rock-chips.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/power_supply.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/machine.h>
+#include <linux/types.h>
+
+#define SGM4154X_MANUFACTURER "SGMICRO"
+#define SGM4154X_NAME "sgm41542"
+
+#define SGM4154X_CHRG_CTRL_0 0x00
+#define SGM4154X_HIZ_EN BIT(7)
+#define SGM4154X_IINDPM_I_MASK GENMASK(4, 0)
+#define SGM4154X_IINDPM_I_MIN_UA 100000
+#define SGM4154X_IINDPM_I_MAX_UA 3800000
+#define SGM4154X_IINDPM_STEP_UA 100000
+#define SGM4154X_IINDPM_DEF_UA 2400000
+
+#define SGM4154X_CHRG_CTRL_1 0x01
+#define SGM4154X_WDT_RST BIT(6)
+#define SGM4154X_OTG_EN BIT(5)
+#define SGM4154X_CHRG_EN BIT(4)
+
+#define SGM4154X_CHRG_CTRL_2 0x02
+#define SGM4154X_BOOST_LIM BIT(7)
+#define SGM4154X_ICHRG_CUR_MASK GENMASK(5, 0)
+#define SGM4154X_ICHRG_I_STEP_UA 60000
+#define SGM4154X_ICHRG_I_MIN_UA 0
+#define SGM4154X_ICHRG_I_MAX_UA 3780000
+#define SGM4154X_ICHRG_I_DEF_UA 2040000
+
+#define SGM4154X_CHRG_CTRL_3 0x03
+#define SGM4154X_PRECHRG_CUR_MASK GENMASK(7, 4)
+#define SGM4154X_PRECHRG_CURRENT_STEP_UA 60000
+#define SGM4154X_PRECHRG_I_MIN_UA 60000
+#define SGM4154X_PRECHRG_I_MAX_UA 780000
+#define SGM4154X_PRECHRG_I_DEF_UA 180000
+#define SGM4154X_TERMCHRG_CUR_MASK GENMASK(3, 0)
+#define SGM4154X_TERMCHRG_CURRENT_STEP_UA 60000
+#define SGM4154X_TERMCHRG_I_MIN_UA 60000
+#define SGM4154X_TERMCHRG_I_MAX_UA 960000
+#define SGM4154X_TERMCHRG_I_DEF_UA 180000
+
+#define SGM4154X_CHRG_CTRL_4 0x04
+#define SGM4154X_VREG_V_MASK GENMASK(7, 3)
+#define SGM4154X_VREG_V_MAX_UV 4624000
+#define SGM4154X_VREG_V_MIN_UV 3856000
+#define SGM4154X_VREG_V_DEF_UV 4208000
+#define SGM4154X_VREG_V_STEP_UV 32000
+#define SGM4154X_VRECHARGE BIT(0)
+#define SGM4154X_VRECHRG_STEP_MV 100
+#define SGM4154X_VRECHRG_OFFSET_MV 100
+
+#define SGM4154X_CHRG_CTRL_5 0x05
+#define SGM4154X_TERM_EN BIT(7)
+#define SGM4154X_WDT_TIMER_MASK GENMASK(5, 4)
+#define SGM4154X_WDT_TIMER_40S BIT(4)
+#define SGM4154X_WDT_TIMER_80S BIT(5)
+#define SGM4154X_WDT_TIMER_160S (BIT(4) | BIT(5))
+#define SGM4154X_WDT_TIMER_DISABLE 0
+
+#define SGM4154X_CHRG_CTRL_6 0x06
+#define SGM4154X_VAC_OVP_MASK GENMASK(7, 6)
+#define SGM4154X_OVP_14V (BIT(7) | BIT(6))
+#define SGM4154X_OVP_10_5V BIT(7)
+#define SGM4154X_OVP_6_5V BIT(6)
+#define SGM4154X_OVP_5_5V 0
+#define SGM4154X_OVP_DEFAULT SGM4154X_OVP_14V
+#define SGM4154X_BOOSTV GENMASK(5, 4)
+#define SGM4154X_VINDPM_V_MASK GENMASK(3, 0)
+#define SGM4154X_VINDPM_V_MIN_UV 3900000
+#define SGM4154X_VINDPM_V_MAX_UV 12000000
+#define SGM4154X_VINDPM_STEP_UV 100000
+#define SGM4154X_VINDPM_DEF_UV 4500000
+
+#define SGM4154X_CHRG_CTRL_7 0x07
+
+#define SGM4154X_CHRG_STAT 0x08
+#define SGM4154X_VBUS_STAT_MASK GENMASK(7, 5)
+#define SGM4154X_OTG_MODE (BIT(7) | BIT(6) | BIT(5))
+#define SGM4154X_NON_STANDARD (BIT(7) | BIT(6))
+#define SGM4154X_UNKNOWN (BIT(7) | BIT(5))
+#define SGM4154X_USB_DCP (BIT(6) | BIT(5))
+#define SGM4154X_USB_CDP BIT(6)
+#define SGM4154X_USB_SDP BIT(5)
+#define SGM4154X_NOT_CHRGING 0
+#define SGM4154X_CHG_STAT_MASK GENMASK(4, 3)
+#define SGM4154X_TERM_CHRG (BIT(4) | BIT(3))
+#define SGM4154X_FAST_CHRG BIT(4)
+#define SGM4154X_PRECHRG BIT(3)
+#define SGM4154X_PG_STAT BIT(2)
+#define SGM4154X_THERM_STAT BIT(1)
+#define SGM4154X_VSYS_STAT BIT(0)
+
+#define SGM4154X_CHRG_FAULT 0x09
+#define SGM4154X_TEMP_MASK GENMASK(2, 0)
+#define SGM4154X_TEMP_HOT (BIT(2) | BIT(1))
+#define SGM4154X_TEMP_COLD (BIT(2) | BIT(0))
+#define SGM4154X_TEMP_COOL (BIT(1) | BIT(0))
+#define SGM4154X_TEMP_WARM BIT(1)
+#define SGM4154X_TEMP_NORMAL BIT(0)
+
+#define SGM4154X_CHRG_CTRL_A 0x0a
+#define SGM4154X_VBUS_GOOD BIT(7)
+#define SGM4154X_VINDPM_INT_MASK BIT(1)
+#define SGM4154X_IINDPM_INT_MASK BIT(0)
+
+#define SGM4154X_CHRG_CTRL_B 0x0b
+#define SGM4154X_PN_ID (BIT(6) | BIT(5) | BIT(3))
+#define SGM4154X_PN_MASK GENMASK(6, 3)
+
+#define SGM4154X_CHRG_CTRL_C 0x0c
+
+#define SGM4154X_CHRG_CTRL_D 0x0d
+#define SGM4154X_JEITA_EN BIT(0)
+
+#define SGM4154X_INPUT_DET 0x0e
+#define SGM4154X_DPDM_ONGOING BIT(7)
+
+#define SGM4154X_CHRG_CTRL_F 0x0f
+#define SGM4154X_VINDPM_OS_MASK GENMASK(1, 0)
+
+#define SGM4154X_DEFAULT_INPUT_CUR (500 * 1000)
+
+struct sgm4154x_init_data {
+ int ilim; /* input current limit */
+ int vlim; /* minimum system voltage limit */
+ int iterm; /* termination current */
+ int iprechg; /* precharge current */
+ int max_ichg; /* maximum charge current */
+ int max_vreg; /* maximum charge voltage */
+};
+
+struct sgm4154x_state {
+ bool vsys_stat;
+ bool therm_stat;
+ bool online;
+ u8 chrg_stat;
+ bool chrg_en;
+ bool vbus_gd;
+ u8 chrg_type;
+ u8 health;
+ u8 chrg_fault;
+ u8 ntc_fault;
+};
+
+struct sgm4154x_device {
+ struct i2c_client *client;
+ struct device *dev;
+ struct power_supply *charger;
+ struct regmap *regmap;
+ char model_name[I2C_NAME_SIZE];
+ struct sgm4154x_init_data init_data;
+ struct sgm4154x_state state;
+ struct regulator_dev *otg_rdev;
+ bool watchdog_enable;
+ struct workqueue_struct *sgm_monitor_wq;
+ struct delayed_work sgm_delay_work;
+};
+
+enum SGM4154X_VINDPM_OS {
+ VINDPM_OS_3900MV,
+ VINDPM_OS_5900MV,
+ VINDPM_OS_7500MV,
+ VINDPM_OS_10500MV,
+};
+
+static int sgm4154x_set_term_curr(struct sgm4154x_device *sgm, int cur_ua)
+{
+ int reg_val;
+ int ret;
+
+ cur_ua = clamp(cur_ua, SGM4154X_TERMCHRG_I_MIN_UA, SGM4154X_TERMCHRG_I_MAX_UA);
+ reg_val = (cur_ua - SGM4154X_TERMCHRG_I_MIN_UA) / SGM4154X_TERMCHRG_CURRENT_STEP_UA;
+
+ ret = regmap_update_bits(sgm->regmap,
+ SGM4154X_CHRG_CTRL_3,
+ SGM4154X_TERMCHRG_CUR_MASK,
+ reg_val);
+ if (ret)
+ dev_err(sgm->dev, "set term current error!\n");
+
+ return ret;
+}
+
+static int sgm4154x_set_prechrg_curr(struct sgm4154x_device *sgm, int cur_ua)
+{
+ int reg_val;
+ int ret;
+
+ cur_ua = clamp(cur_ua, SGM4154X_PRECHRG_I_MIN_UA, SGM4154X_PRECHRG_I_MAX_UA);
+ reg_val = (cur_ua - SGM4154X_PRECHRG_I_MIN_UA) / SGM4154X_PRECHRG_CURRENT_STEP_UA;
+
+ reg_val = reg_val << 4;
+ ret = regmap_update_bits(sgm->regmap,
+ SGM4154X_CHRG_CTRL_3,
+ SGM4154X_PRECHRG_CUR_MASK,
+ reg_val);
+ if (ret)
+ dev_err(sgm->dev, "set precharge current error!\n");
+
+ return ret;
+}
+
+static int sgm4154x_set_ichrg_curr(struct sgm4154x_device *sgm, int cur_ua)
+{
+ int reg_val;
+ int ret;
+
+ cur_ua = clamp(cur_ua, SGM4154X_ICHRG_I_MIN_UA, SGM4154X_ICHRG_I_MAX_UA);
+ reg_val = cur_ua / SGM4154X_ICHRG_I_STEP_UA;
+
+ ret = regmap_update_bits(sgm->regmap,
+ SGM4154X_CHRG_CTRL_2,
+ SGM4154X_ICHRG_CUR_MASK,
+ reg_val);
+ if (ret)
+ dev_err(sgm->dev, "set icharge current error!\n");
+
+ return ret;
+}
+
+static int sgm4154x_get_ichrg_curr(struct sgm4154x_device *sgm)
+{
+ u32 reg;
+ int ret, val;
+
+ ret = regmap_read(sgm->regmap, SGM4154X_CHRG_CTRL_2, ®);
+ if (ret) {
+ dev_err(sgm->dev, "get charge current error!\n");
+ return ret;
+ }
+
+ val = FIELD_GET(SGM4154X_ICHRG_CUR_MASK, reg);
+
+ return val * SGM4154X_ICHRG_I_STEP_UA;
+}
+
+static int sgm4154x_set_chrg_volt(struct sgm4154x_device *sgm, int chrg_volt)
+{
+ int reg_val;
+ int ret;
+
+ /*
+ * Note that the value of 0x01111 represents a "special value"
+ * corresponding to 4352000uV instead of the expected 4336000uV,
+ * per the datasheet. All other values are as expected.
+ */
+ chrg_volt = clamp(chrg_volt, SGM4154X_VREG_V_MIN_UV, sgm->init_data.max_vreg);
+ reg_val = (chrg_volt - SGM4154X_VREG_V_MIN_UV) / SGM4154X_VREG_V_STEP_UV;
+ reg_val = reg_val << 3;
+ ret = regmap_update_bits(sgm->regmap,
+ SGM4154X_CHRG_CTRL_4,
+ SGM4154X_VREG_V_MASK,
+ reg_val);
+ if (ret)
+ dev_err(sgm->dev, "set charge voltage error!\n");
+
+ return ret;
+}
+
+static int sgm4154x_get_chrg_volt(struct sgm4154x_device *sgm)
+{
+ u32 reg;
+ int ret, val;
+
+ ret = regmap_read(sgm->regmap, SGM4154X_CHRG_CTRL_4, ®);
+ if (ret) {
+ dev_err(sgm->dev, "get charge voltage error!\n");
+ return ret;
+ }
+
+ val = FIELD_GET(SGM4154X_VREG_V_MASK, reg);
+
+ /*
+ * 0x01111 is a special value meaning 4352000uV, all other
+ * values are as expected based on the offset and step values.
+ */
+ if (val == 0x0f)
+ return 4352000;
+
+ return val * SGM4154X_VREG_V_STEP_UV + SGM4154X_VREG_V_MIN_UV;
+}
+
+static int sgm4154x_set_input_volt_lim(struct sgm4154x_device *sgm,
+ unsigned int vindpm)
+{
+ enum SGM4154X_VINDPM_OS os_val;
+ unsigned int offset;
+ u8 reg_val;
+ int ret;
+
+
+ if (vindpm < SGM4154X_VINDPM_V_MIN_UV ||
+ vindpm > SGM4154X_VINDPM_V_MAX_UV) {
+ dev_err(sgm->dev, "input voltage limit %u outside range\n", vindpm);
+ return -EINVAL;
+ }
+
+ if (vindpm < 5900000) {
+ os_val = VINDPM_OS_3900MV;
+ offset = 3900000;
+ } else if (vindpm >= 5900000 && vindpm < 7500000) {
+ os_val = VINDPM_OS_5900MV;
+ offset = 5900000;
+ } else if (vindpm >= 7500000 && vindpm < 10500000) {
+ os_val = VINDPM_OS_7500MV;
+ offset = 7500000;
+ } else {
+ os_val = VINDPM_OS_10500MV;
+ offset = 10500000;
+ }
+
+ ret = regmap_update_bits(sgm->regmap,
+ SGM4154X_CHRG_CTRL_F,
+ SGM4154X_VINDPM_OS_MASK,
+ os_val);
+ if (ret) {
+ dev_err(sgm->dev, "set vin dpm error!\n");
+ return ret;
+ }
+
+ reg_val = (vindpm - offset) / SGM4154X_VINDPM_STEP_UV;
+
+ ret = regmap_update_bits(sgm->regmap, SGM4154X_CHRG_CTRL_6,
+ SGM4154X_VINDPM_V_MASK, reg_val);
+ if (ret)
+ dev_err(sgm->dev, "input voltage error!\n");
+
+ return ret;
+}
+
+static int sgm4154x_set_input_curr_lim(struct sgm4154x_device *sgm, int iindpm)
+{
+ int reg_val;
+ int ret;
+
+ if (iindpm < SGM4154X_IINDPM_I_MIN_UA)
+ return -EINVAL;
+
+ /*
+ * Per the datasheet, values between 100000uA and 3100000uA work
+ * as expected with the register defined as having a step of
+ * 100000 and a min/max of 100000 (0x00) through 3100000 (0x1e).
+ * The register value of 0x1f however corresponds to 3800000uA not
+ * 3200000uA as one would expect.
+ */
+
+ if (iindpm > 3100000 && iindpm < SGM4154X_IINDPM_I_MAX_UA)
+ iindpm = 3100000;
+ if ((iindpm > SGM4154X_IINDPM_I_MAX_UA) || (iindpm > sgm->init_data.ilim))
+ iindpm = min(SGM4154X_IINDPM_I_MAX_UA, sgm->init_data.ilim);
+
+ reg_val = (iindpm - SGM4154X_IINDPM_I_MIN_UA) / SGM4154X_IINDPM_STEP_UA;
+
+ ret = regmap_update_bits(sgm->regmap,
+ SGM4154X_CHRG_CTRL_0,
+ SGM4154X_IINDPM_I_MASK,
+ reg_val);
+ if (ret)
+ dev_err(sgm->dev, "set input current limit error!\n");
+
+ return ret;
+}
+
+static int sgm4154x_get_input_curr_lim(struct sgm4154x_device *sgm)
+{
+ int ret;
+ int ilim;
+
+ ret = regmap_read(sgm->regmap, SGM4154X_CHRG_CTRL_0, &ilim);
+ if (ret) {
+ dev_err(sgm->dev, "get input current limit error!\n");
+ return ret;
+ }
+
+ /* Max value is not 3200000uA as expected but is 3800000uA */
+ if (SGM4154X_IINDPM_I_MASK == (ilim & SGM4154X_IINDPM_I_MASK))
+ return SGM4154X_IINDPM_I_MAX_UA;
+
+ ilim = (ilim & SGM4154X_IINDPM_I_MASK) * SGM4154X_IINDPM_STEP_UA + SGM4154X_IINDPM_I_MIN_UA;
+
+ return ilim;
+}
+
+static int sgm4154x_watchdog_timer_reset(struct sgm4154x_device *sgm)
+{
+ int ret;
+
+ ret = regmap_update_bits(sgm->regmap,
+ SGM4154X_CHRG_CTRL_1,
+ SGM4154X_WDT_RST,
+ SGM4154X_WDT_RST);
+
+ if (ret)
+ dev_err(sgm->dev, "set watchdog timer error!\n");
+
+ return ret;
+}
+
+static int sgm4154x_set_watchdog_timer(struct sgm4154x_device *sgm, int time)
+{
+ u8 reg_val;
+ int ret;
+
+ if (time == 0)
+ reg_val = SGM4154X_WDT_TIMER_DISABLE;
+ else if (time == 40)
+ reg_val = SGM4154X_WDT_TIMER_40S;
+ else if (time == 80)
+ reg_val = SGM4154X_WDT_TIMER_80S;
+ else
+ reg_val = SGM4154X_WDT_TIMER_160S;
+
+ ret = regmap_update_bits(sgm->regmap,
+ SGM4154X_CHRG_CTRL_5,
+ SGM4154X_WDT_TIMER_MASK,
+ reg_val);
+
+ if (ret) {
+ dev_err(sgm->dev, "set watchdog timer error!\n");
+ return ret;
+ }
+
+ if (time) {
+ if (!sgm->watchdog_enable)
+ queue_delayed_work(sgm->sgm_monitor_wq,
+ &sgm->sgm_delay_work,
+ msecs_to_jiffies(1000 * 5));
+ sgm->watchdog_enable = true;
+ } else {
+ sgm->watchdog_enable = false;
+ sgm4154x_watchdog_timer_reset(sgm);
+ }
+
+ return ret;
+}
+
+static int sgm4154x_enable_charger(struct sgm4154x_device *sgm)
+{
+ int ret;
+
+ ret = regmap_update_bits(sgm->regmap,
+ SGM4154X_CHRG_CTRL_1,
+ SGM4154X_CHRG_EN,
+ SGM4154X_CHRG_EN);
+ if (ret)
+ dev_err(sgm->dev, "enable charger error!\n");
+
+ return ret;
+}
+
+static int sgm4154x_disable_charger(struct sgm4154x_device *sgm)
+{
+ int ret;
+
+ ret = regmap_update_bits(sgm->regmap,
+ SGM4154X_CHRG_CTRL_1,
+ SGM4154X_CHRG_EN,
+ 0);
+ if (ret)
+ dev_err(sgm->dev, "disable charger error!\n");
+
+ return ret;
+}
+
+static int sgm4154x_set_vac_ovp(struct sgm4154x_device *sgm)
+{
+ int reg_val;
+ int ret;
+
+ reg_val = SGM4154X_OVP_DEFAULT & SGM4154X_VAC_OVP_MASK;
+
+ ret = regmap_update_bits(sgm->regmap,
+ SGM4154X_CHRG_CTRL_6,
+ SGM4154X_VAC_OVP_MASK,
+ reg_val);
+ if (ret)
+ dev_err(sgm->dev, "set vac ovp error!\n");
+
+ return ret;
+}
+
+static int sgm4154x_set_recharge_volt(struct sgm4154x_device *sgm, int recharge_volt)
+{
+ int reg_val;
+ int ret;
+
+ reg_val = (recharge_volt - SGM4154X_VRECHRG_OFFSET_MV) / SGM4154X_VRECHRG_STEP_MV;
+
+ ret = regmap_update_bits(sgm->regmap,
+ SGM4154X_CHRG_CTRL_4,
+ SGM4154X_VRECHARGE,
+ reg_val);
+ if (ret)
+ dev_err(sgm->dev, "set recharger error!\n");
+
+ return ret;
+}
+
+static int sgm4154x_get_state(struct sgm4154x_device *sgm,
+ struct sgm4154x_state *state)
+{
+ int reg, ret;
+
+ ret = regmap_read(sgm->regmap, SGM4154X_CHRG_STAT, ®);
+ if (ret) {
+ dev_err(sgm->dev, "read SGM4154X_CHRG_STAT fail\n");
+ return ret;
+ }
+ state->chrg_type = reg & SGM4154X_VBUS_STAT_MASK;
+ state->chrg_stat = reg & SGM4154X_CHG_STAT_MASK;
+ state->online = !!(reg & SGM4154X_PG_STAT);
+ state->therm_stat = !!(reg & SGM4154X_THERM_STAT);
+ state->vsys_stat = !!(reg & SGM4154X_VSYS_STAT);
+
+ ret = regmap_read(sgm->regmap, SGM4154X_CHRG_FAULT, ®);
+ if (ret) {
+ dev_err(sgm->dev, "read SGM4154X_CHRG_FAULT fail\n");
+ return ret;
+ }
+ state->chrg_fault = reg;
+ state->ntc_fault = reg & SGM4154X_TEMP_MASK;
+ state->health = state->ntc_fault;
+
+ ret = regmap_read(sgm->regmap, SGM4154X_CHRG_CTRL_A, ®);
+ if (ret) {
+ dev_err(sgm->dev, "read SGM4154X_CHRG_CTRL_A fail\n");
+ return ret;
+ }
+ state->vbus_gd = !!(reg & SGM4154X_VBUS_GOOD);
+
+ return ret;
+}
+
+static int sgm4154x_property_is_writeable(struct power_supply *psy,
+ enum power_supply_property prop)
+{
+ switch (prop) {
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+ case POWER_SUPPLY_PROP_ONLINE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int sgm4154x_charger_set_property(struct power_supply *psy,
+ enum power_supply_property prop,
+ const union power_supply_propval *val)
+{
+ struct sgm4154x_device *sgm = power_supply_get_drvdata(psy);
+ int ret = -EINVAL;
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ if (val->intval) {
+ ret = sgm4154x_enable_charger(sgm);
+ sgm4154x_set_watchdog_timer(sgm, SGM4154X_WDT_TIMER_40S);
+ } else {
+ sgm4154x_set_watchdog_timer(sgm, 0);
+ ret = sgm4154x_disable_charger(sgm);
+ }
+ break;
+
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+ ret = sgm4154x_set_input_curr_lim(sgm, val->intval);
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+ ret = sgm4154x_set_ichrg_curr(sgm, val->intval);
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+ ret = sgm4154x_set_chrg_volt(sgm, val->intval);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int sgm4154x_charger_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct sgm4154x_device *sgm = power_supply_get_drvdata(psy);
+ struct sgm4154x_state state;
+ int ret;
+
+ ret = sgm4154x_get_state(sgm, &state);
+ if (ret) {
+ dev_err(sgm->dev, "get state error!\n");
+ return ret;
+ }
+ sgm->state = state;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ if (!state.chrg_type || (state.chrg_type == SGM4154X_OTG_MODE))
+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ else if (!state.chrg_stat)
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ else if (state.chrg_stat == SGM4154X_TERM_CHRG)
+ val->intval = POWER_SUPPLY_STATUS_FULL;
+ else
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ switch (state.chrg_stat) {
+ case SGM4154X_PRECHRG:
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+ break;
+ case SGM4154X_FAST_CHRG:
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
+ break;
+ case SGM4154X_TERM_CHRG:
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+ break;
+ case SGM4154X_NOT_CHRGING:
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
+ break;
+ default:
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+ }
+ break;
+ case POWER_SUPPLY_PROP_MANUFACTURER:
+ val->strval = SGM4154X_MANUFACTURER;
+ break;
+
+ case POWER_SUPPLY_PROP_MODEL_NAME:
+ val->strval = SGM4154X_NAME;
+ break;
+
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = state.online;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = state.vbus_gd;
+ break;
+ case POWER_SUPPLY_PROP_TYPE:
+ val->intval = POWER_SUPPLY_TYPE_USB;
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+ val->intval = sgm4154x_get_chrg_volt(sgm);
+ break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+ val->intval = sgm4154x_get_ichrg_curr(sgm);
+ break;
+ case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
+ val->intval = sgm->init_data.vlim;
+ break;
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+ val->intval = sgm4154x_get_input_curr_lim(sgm);
+ if (val->intval < 0)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static irqreturn_t sgm4154x_irq_handler_thread(int irq, void *private)
+{
+ struct sgm4154x_device *sgm4154x = private;
+ struct sgm4154x_state state;
+ int ret;
+
+ ret = sgm4154x_get_state(sgm4154x, &state);
+ if (ret) {
+ dev_err(sgm4154x->dev, "get state error!\n");
+ return IRQ_NONE;
+ }
+ sgm4154x->state = state;
+ if (state.vbus_gd) {
+ if (sgm4154x->init_data.ilim >= SGM4154X_DEFAULT_INPUT_CUR) {
+ ret = sgm4154x_set_input_curr_lim(sgm4154x, sgm4154x->init_data.ilim);
+ if (ret) {
+ dev_err(sgm4154x->dev, "set input current error!\n");
+ return IRQ_NONE;
+ }
+ }
+ }
+ power_supply_changed(sgm4154x->charger);
+
+ return IRQ_HANDLED;
+}
+
+static enum power_supply_property sgm4154x_power_supply_props[] = {
+ POWER_SUPPLY_PROP_TYPE,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT,
+ POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
+ POWER_SUPPLY_PROP_CHARGE_TYPE,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+ POWER_SUPPLY_PROP_PRESENT
+};
+
+static char *sgm4154x_charger_supplied_to[] = {
+ "battery-monitor",
+};
+
+static struct power_supply_desc sgm4154x_power_supply_desc = {
+ .name = "sgm4154x-charger",
+ .type = POWER_SUPPLY_TYPE_USB,
+ .properties = sgm4154x_power_supply_props,
+ .num_properties = ARRAY_SIZE(sgm4154x_power_supply_props),
+ .get_property = sgm4154x_charger_get_property,
+ .set_property = sgm4154x_charger_set_property,
+ .property_is_writeable = sgm4154x_property_is_writeable,
+};
+
+static const struct regmap_config sgm4154x_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = SGM4154X_CHRG_CTRL_F,
+ .cache_type = REGCACHE_NONE,
+};
+
+static int sgm4154x_power_supply_init(struct sgm4154x_device *sgm,
+ struct device *dev)
+{
+ struct power_supply_config psy_cfg = { .drv_data = sgm,
+ .fwnode = dev->fwnode, };
+
+ psy_cfg.supplied_to = sgm4154x_charger_supplied_to;
+ psy_cfg.num_supplicants = ARRAY_SIZE(sgm4154x_charger_supplied_to);
+ psy_cfg.fwnode = dev->fwnode;
+ sgm->charger = devm_power_supply_register(sgm->dev,
+ &sgm4154x_power_supply_desc,
+ &psy_cfg);
+ if (IS_ERR(sgm->charger))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int sgm4154x_hw_init(struct sgm4154x_device *sgm)
+{
+ struct power_supply_battery_info *bat_info;
+ int ret;
+ u32 val;
+
+ /*
+ * If unable to read devicetree info, use default/reset
+ * values from hardware.
+ */
+ sgm->init_data.iprechg = SGM4154X_PRECHRG_I_DEF_UA;
+ sgm->init_data.iterm = SGM4154X_TERMCHRG_I_DEF_UA;
+ sgm->init_data.max_ichg = SGM4154X_ICHRG_I_DEF_UA;
+ sgm->init_data.max_vreg = SGM4154X_VREG_V_DEF_UV;
+ sgm->init_data.vlim = SGM4154X_VINDPM_DEF_UV;
+ sgm->init_data.ilim = SGM4154X_IINDPM_DEF_UA;
+
+ ret = power_supply_get_battery_info(sgm->charger, &bat_info);
+ if (ret)
+ dev_warn(sgm->dev, "sgm4154x: cannot read battery info\n");
+ else {
+ if (bat_info->constant_charge_current_max_ua)
+ sgm->init_data.max_ichg = bat_info->constant_charge_current_max_ua;
+ if (bat_info->constant_charge_voltage_max_uv)
+ sgm->init_data.max_vreg = bat_info->constant_charge_voltage_max_uv;
+ if (bat_info->charge_term_current_ua)
+ sgm->init_data.iterm = bat_info->charge_term_current_ua;
+ if (bat_info->precharge_current_ua)
+ sgm->init_data.iprechg = bat_info->precharge_current_ua;
+ }
+
+ power_supply_put_battery_info(sgm->charger, bat_info);
+
+ ret = device_property_read_u32(sgm->dev,
+ "input-voltage-limit-microvolt",
+ &val);
+ if (!ret)
+ sgm->init_data.vlim = clamp(val, SGM4154X_VINDPM_V_MIN_UV,
+ SGM4154X_VINDPM_V_MAX_UV);
+
+ ret = device_property_read_u32(sgm->dev,
+ "input-current-limit-microamp",
+ &val);
+ if (!ret)
+ sgm->init_data.ilim = clamp(val, SGM4154X_IINDPM_I_MIN_UA,
+ SGM4154X_IINDPM_I_MAX_UA);
+
+ ret = sgm4154x_set_watchdog_timer(sgm, 0);
+ if (ret)
+ return ret;
+
+ ret = sgm4154x_set_prechrg_curr(sgm, sgm->init_data.iprechg);
+ if (ret)
+ return ret;
+
+ ret = sgm4154x_set_chrg_volt(sgm, sgm->init_data.max_vreg);
+ if (ret)
+ return ret;
+
+ ret = sgm4154x_set_term_curr(sgm, sgm->init_data.iterm);
+ if (ret)
+ return ret;
+
+ ret = sgm4154x_set_ichrg_curr(sgm, sgm->init_data.max_ichg);
+ if (ret)
+ return ret;
+
+ ret = sgm4154x_set_input_volt_lim(sgm, sgm->init_data.vlim);
+ if (ret)
+ return ret;
+
+ ret = sgm4154x_set_input_curr_lim(sgm, sgm->init_data.ilim);
+ if (ret)
+ return ret;
+
+ ret = sgm4154x_set_vac_ovp(sgm);
+ if (ret)
+ return ret;
+
+ regmap_update_bits(sgm->regmap,
+ SGM4154X_CHRG_CTRL_D,
+ SGM4154X_JEITA_EN,
+ 0);
+
+ regmap_update_bits(sgm->regmap,
+ SGM4154X_CHRG_CTRL_A,
+ SGM4154X_IINDPM_INT_MASK,
+ SGM4154X_IINDPM_INT_MASK);
+
+ regmap_update_bits(sgm->regmap,
+ SGM4154X_CHRG_CTRL_A,
+ SGM4154X_VINDPM_INT_MASK,
+ SGM4154X_VINDPM_INT_MASK);
+
+ /*
+ * Recharge voltage set to 200 by BSP driver instead of hardware
+ * default value of 100.
+ */
+ ret = sgm4154x_set_recharge_volt(sgm, 200);
+
+ return ret;
+}
+
+static const u32 sgm4154x_chg_otg_cur_ua[] = {
+ 1200000, 2000000,
+};
+
+static const struct regulator_ops sgm4154x_vbus_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_current_limit = regulator_set_current_limit_regmap,
+ .get_current_limit = regulator_get_current_limit_regmap,
+};
+
+static const struct regulator_desc sgm4154x_otg_rdesc = {
+ .of_match = "otg-vbus",
+ .name = "otg-vbus",
+ .regulators_node = of_match_ptr("regulators"),
+ .ops = &sgm4154x_vbus_ops,
+ .owner = THIS_MODULE,
+ .type = REGULATOR_VOLTAGE,
+ .min_uV = 4850000,
+ .uV_step = 150000,
+ .n_voltages = 4,
+ .vsel_reg = SGM4154X_CHRG_CTRL_6,
+ .vsel_mask = SGM4154X_BOOSTV,
+ .enable_reg = SGM4154X_CHRG_CTRL_1,
+ .enable_mask = SGM4154X_OTG_EN,
+ .curr_table = sgm4154x_chg_otg_cur_ua,
+ .n_current_limits = ARRAY_SIZE(sgm4154x_chg_otg_cur_ua),
+ .csel_reg = SGM4154X_CHRG_CTRL_2,
+ .csel_mask = SGM4154X_BOOST_LIM,
+};
+
+static int sgm4154x_vbus_regulator_register(struct sgm4154x_device *sgm)
+{
+ struct regulator_config config = {
+ .dev = sgm->dev,
+ .regmap = sgm->regmap,
+ .driver_data = sgm,
+ };
+
+ sgm->otg_rdev = devm_regulator_register(sgm->dev,
+ &sgm4154x_otg_rdesc,
+ &config);
+
+ return PTR_ERR_OR_ZERO(sgm->otg_rdev);
+}
+
+static int sgm4154x_hw_chipid_detect(struct sgm4154x_device *sgm)
+{
+ int ret;
+ int val;
+
+ ret = regmap_read(sgm->regmap, SGM4154X_CHRG_CTRL_B, &val);
+ if (ret)
+ return ret;
+
+ if ((val & SGM4154X_PN_MASK) != SGM4154X_PN_ID)
+ dev_warn(sgm->dev, "sgm4154x device ID mismatch\n");
+
+ return 0;
+}
+
+static void sgm_charger_work(struct work_struct *work)
+{
+ struct sgm4154x_device *sgm =
+ container_of(work,
+ struct sgm4154x_device,
+ sgm_delay_work.work);
+
+ sgm4154x_watchdog_timer_reset(sgm);
+ if (sgm->watchdog_enable)
+ queue_delayed_work(sgm->sgm_monitor_wq,
+ &sgm->sgm_delay_work,
+ msecs_to_jiffies(1000 * 5));
+}
+
+static int sgm4154x_probe(struct i2c_client *client)
+{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
+ struct device *dev = &client->dev;
+ struct sgm4154x_device *sgm;
+ int ret;
+
+ sgm = devm_kzalloc(dev, sizeof(*sgm), GFP_KERNEL);
+ if (!sgm)
+ return -ENOMEM;
+
+ sgm->client = client;
+ sgm->dev = dev;
+
+ strscpy(sgm->model_name, id->name, I2C_NAME_SIZE);
+
+ sgm->regmap = devm_regmap_init_i2c(client, &sgm4154x_regmap_config);
+ if (IS_ERR(sgm->regmap))
+ return dev_err_probe(dev, PTR_ERR(sgm->regmap),
+ "Failed to allocate register map\n");
+
+ i2c_set_clientdata(client, sgm);
+
+ ret = sgm4154x_hw_chipid_detect(sgm);
+ if (ret)
+ dev_err_probe(dev, ret, "Unable to read HW ID\n");
+
+ device_init_wakeup(dev, 1);
+
+ ret = sgm4154x_power_supply_init(sgm, dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to register power supply\n");
+
+ ret = sgm4154x_hw_init(sgm);
+ if (ret)
+ return dev_err_probe(dev, ret, "Cannot initialize the chip.\n");
+
+ if (client->irq) {
+ ret = devm_request_threaded_irq(dev, client->irq, NULL,
+ sgm4154x_irq_handler_thread,
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ "sgm41542-irq", sgm);
+ if (ret)
+ return ret;
+ enable_irq_wake(client->irq);
+ }
+
+ sgm->sgm_monitor_wq = devm_alloc_ordered_workqueue(dev, "%s",
+ WQ_MEM_RECLAIM | WQ_FREEZABLE, "sgm-monitor-wq");
+ if (!sgm->sgm_monitor_wq)
+ return -EINVAL;
+
+ INIT_DELAYED_WORK(&sgm->sgm_delay_work, sgm_charger_work);
+
+ sgm4154x_vbus_regulator_register(sgm);
+
+ return 0;
+}
+
+static void sgm4154x_charger_shutdown(struct i2c_client *client)
+{
+ struct sgm4154x_device *sgm = i2c_get_clientdata(client);
+
+ sgm4154x_set_prechrg_curr(sgm, SGM4154X_PRECHRG_I_DEF_UA);
+ sgm4154x_disable_charger(sgm);
+}
+
+static const struct i2c_device_id sgm4154x_i2c_ids[] = {
+ { "sgm41542" },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, sgm4154x_i2c_ids);
+
+static const struct of_device_id sgm4154x_of_match[] = {
+ { .compatible = "sgmicro,sgm41542", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, sgm4154x_of_match);
+
+static struct i2c_driver sgm4154x_driver = {
+ .driver = {
+ .name = "sgm4154x-charger",
+ .of_match_table = sgm4154x_of_match,
+ },
+ .probe = sgm4154x_probe,
+ .shutdown = sgm4154x_charger_shutdown,
+ .id_table = sgm4154x_i2c_ids,
+};
+
+module_i2c_driver(sgm4154x_driver);
+
+MODULE_AUTHOR("Xu Shengfei <xsf@rock-chips.com>");
+MODULE_DESCRIPTION("sgm4154x charger driver");
+MODULE_LICENSE("GPL");
--
2.43.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH V2 3/6] dt-bindings: display: panel: document Anbernic TD4310 panel
2026-05-15 22:19 ` Chris Morgan
@ 2026-05-15 22:19 ` Chris Morgan
-1 siblings, 0 replies; 19+ messages in thread
From: Chris Morgan @ 2026-05-15 22:19 UTC (permalink / raw)
To: linux-rockchip
Cc: devicetree, xsf, sre, simona, airlied, tzimmermann, mripard,
maarten.lankhorst, jesszhan0024, neil.armstrong, heiko, conor+dt,
krzk+dt, robh, Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
The panel used by Anbernic in the RG Vita-Pro is a DSI panel based
on the TD4310 controller IC. It measures approximately 5.5 inches
diagonally and is 1080x1920 in resolution.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
.../display/panel/anbernic,td4310.yaml | 64 +++++++++++++++++++
1 file changed, 64 insertions(+)
create mode 100644 Documentation/devicetree/bindings/display/panel/anbernic,td4310.yaml
diff --git a/Documentation/devicetree/bindings/display/panel/anbernic,td4310.yaml b/Documentation/devicetree/bindings/display/panel/anbernic,td4310.yaml
new file mode 100644
index 000000000000..5068dd6b2d52
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/anbernic,td4310.yaml
@@ -0,0 +1,64 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/panel/anbernic,td4310.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Anbernic TD4310 Based Panels
+
+maintainers:
+ - Chris Morgan <macromorgan@hotmail.com>
+
+description:
+ Anbernic TD4310 Based Panels, such as the RG-Vita-Pro panel
+ (a 1080x1920 5.5 inch panel).
+
+allOf:
+ - $ref: panel-common.yaml#
+
+properties:
+ compatible:
+ items:
+ - const: anbernic,panel-vita-pro
+
+ reg:
+ maxItems: 1
+
+ vdd-supply:
+ description: Panel power supply
+
+required:
+ - compatible
+ - port
+ - reg
+ - reset-gpios
+ - vdd-supply
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ dsi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ panel@0 {
+ compatible = "anbernic,panel-vita-pro";
+ reg = <0>;
+ backlight = <&backlight>;
+ enable-gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>;
+ reset-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
+ rotation = <270>;
+ vdd-supply = <&vdd_lcd>;
+
+ port {
+ panel_in: endpoint {
+ remote-endpoint = <&dsi_out>;
+ };
+ };
+ };
+ };
+
+...
--
2.43.0
_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH V2 3/6] dt-bindings: display: panel: document Anbernic TD4310 panel
@ 2026-05-15 22:19 ` Chris Morgan
0 siblings, 0 replies; 19+ messages in thread
From: Chris Morgan @ 2026-05-15 22:19 UTC (permalink / raw)
To: linux-rockchip
Cc: devicetree, xsf, sre, simona, airlied, tzimmermann, mripard,
maarten.lankhorst, jesszhan0024, neil.armstrong, heiko, conor+dt,
krzk+dt, robh, Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
The panel used by Anbernic in the RG Vita-Pro is a DSI panel based
on the TD4310 controller IC. It measures approximately 5.5 inches
diagonally and is 1080x1920 in resolution.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
.../display/panel/anbernic,td4310.yaml | 64 +++++++++++++++++++
1 file changed, 64 insertions(+)
create mode 100644 Documentation/devicetree/bindings/display/panel/anbernic,td4310.yaml
diff --git a/Documentation/devicetree/bindings/display/panel/anbernic,td4310.yaml b/Documentation/devicetree/bindings/display/panel/anbernic,td4310.yaml
new file mode 100644
index 000000000000..5068dd6b2d52
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/anbernic,td4310.yaml
@@ -0,0 +1,64 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/panel/anbernic,td4310.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Anbernic TD4310 Based Panels
+
+maintainers:
+ - Chris Morgan <macromorgan@hotmail.com>
+
+description:
+ Anbernic TD4310 Based Panels, such as the RG-Vita-Pro panel
+ (a 1080x1920 5.5 inch panel).
+
+allOf:
+ - $ref: panel-common.yaml#
+
+properties:
+ compatible:
+ items:
+ - const: anbernic,panel-vita-pro
+
+ reg:
+ maxItems: 1
+
+ vdd-supply:
+ description: Panel power supply
+
+required:
+ - compatible
+ - port
+ - reg
+ - reset-gpios
+ - vdd-supply
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ dsi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ panel@0 {
+ compatible = "anbernic,panel-vita-pro";
+ reg = <0>;
+ backlight = <&backlight>;
+ enable-gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>;
+ reset-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
+ rotation = <270>;
+ vdd-supply = <&vdd_lcd>;
+
+ port {
+ panel_in: endpoint {
+ remote-endpoint = <&dsi_out>;
+ };
+ };
+ };
+ };
+
+...
--
2.43.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH V2 4/6] drm/panel: anbernic-td4310: Add RG Vita Pro panel
2026-05-15 22:19 ` Chris Morgan
@ 2026-05-15 22:19 ` Chris Morgan
-1 siblings, 0 replies; 19+ messages in thread
From: Chris Morgan @ 2026-05-15 22:19 UTC (permalink / raw)
To: linux-rockchip
Cc: devicetree, xsf, sre, simona, airlied, tzimmermann, mripard,
maarten.lankhorst, jesszhan0024, neil.armstrong, heiko, conor+dt,
krzk+dt, robh, Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
The panel used by Anbernic in the RG Vita-Pro is a DSI panel based
on the TD4310 controller IC. It measures approximately 5.5 inches
diagonally and is 1080x1920 in resolution.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
drivers/gpu/drm/panel/Kconfig | 10 +
drivers/gpu/drm/panel/Makefile | 1 +
drivers/gpu/drm/panel/panel-anbernic-td4310.c | 291 ++++++++++++++++++
3 files changed, 302 insertions(+)
create mode 100644 drivers/gpu/drm/panel/panel-anbernic-td4310.c
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index d592f4f4b939..61dd00297ecc 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -17,6 +17,16 @@ config DRM_PANEL_ABT_Y030XX067A
Y030XX067A 320x480 3.0" panel as found in the YLM RG-280M, RG-300
and RG-99 handheld gaming consoles.
+config DRM_PANEL_ANBERNIC_TD4310
+ tristate "Anbernic TD4310 LCD panel"
+ depends on GPIOLIB && OF
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ help
+ Say Y here to enable support for Anbernic designed panels with the
+ TD4310 panel controller such as the ones used on the Anbernic RG
+ Vita Pro.
+
config DRM_PANEL_ARM_VERSATILE
tristate "ARM Versatile panel driver"
depends on OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index a4291dc3905b..9d8f70c9de3e 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_DRM_PANEL_ABT_Y030XX067A) += panel-abt-y030xx067a.o
+obj-$(CONFIG_DRM_PANEL_ANBERNIC_TD4310) += panel-anbernic-td4310.o
obj-$(CONFIG_DRM_PANEL_ARM_VERSATILE) += panel-arm-versatile.o
obj-$(CONFIG_DRM_PANEL_ASUS_Z00T_TM5P5_NT35596) += panel-asus-z00t-tm5p5-n35596.o
obj-$(CONFIG_DRM_PANEL_AUO_A030JTN01) += panel-auo-a030jtn01.o
diff --git a/drivers/gpu/drm/panel/panel-anbernic-td4310.c b/drivers/gpu/drm/panel/panel-anbernic-td4310.c
new file mode 100644
index 000000000000..733c68ff9771
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-anbernic-td4310.c
@@ -0,0 +1,291 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for Anbernic panels with TD4310 panel controller.
+ *
+ * Copyright (C) 2026 Chris Morgan <macromorgan@hotmail.com>
+ *
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+
+#include <video/mipi_display.h>
+
+struct anbernic_panel_td4310_info {
+ const struct drm_display_mode *display_modes;
+ unsigned int num_modes;
+ u16 width_mm;
+ u16 height_mm;
+ u32 bus_flags;
+ unsigned long mode_flags;
+ u32 format;
+ u32 lanes;
+ u16 prepare_delay;
+ u16 reset_delay;
+ u16 init_delay;
+ u16 enable_delay;
+ u16 disable_delay;
+ u16 unprepare_delay;
+};
+
+struct anbernic_panel_td4310 {
+ struct device *dev;
+ struct mipi_dsi_device *dsi;
+ struct drm_panel panel;
+ const struct anbernic_panel_td4310_info *panel_info;
+ struct gpio_desc *reset_gpio;
+ struct gpio_desc *enable_gpio;
+ struct regulator *vdd;
+ enum drm_panel_orientation orientation;
+};
+
+static inline struct anbernic_panel_td4310 *panel_to_anbernic_panel_td4310(struct drm_panel *panel)
+{
+ return container_of(panel, struct anbernic_panel_td4310, panel);
+}
+
+static int panel_anbernic_td4310_prepare(struct drm_panel *panel)
+{
+ struct anbernic_panel_td4310 *ctx = panel_to_anbernic_panel_td4310(panel);
+ struct mipi_dsi_device *dsi = ctx->dsi;
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
+ int ret;
+
+ ret = regulator_enable(ctx->vdd);
+ if (ret)
+ return ret;
+
+ if (ctx->enable_gpio) {
+ ret = gpiod_set_value_cansleep(ctx->enable_gpio, 1);
+ if (ret)
+ goto err_enable;
+ };
+
+ gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+ mipi_dsi_msleep(&dsi_ctx, 10);
+
+ ret = gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ if (ret)
+ goto err_reset;
+ if (ctx->panel_info->reset_delay)
+ mipi_dsi_msleep(&dsi_ctx, ctx->panel_info->reset_delay);
+ ret = gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+ if (ret)
+ goto err_reset;
+
+ if (ctx->panel_info->enable_delay)
+ mipi_dsi_msleep(&dsi_ctx, ctx->panel_info->enable_delay);
+
+ mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 100);
+ mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 100);
+
+ return dsi_ctx.accum_err;
+
+err_reset:
+ gpiod_set_value_cansleep(ctx->enable_gpio, 0);
+err_enable:
+ regulator_disable(ctx->vdd);
+ return ret;
+}
+
+static int panel_anbernic_td4310_unprepare(struct drm_panel *panel)
+{
+ struct anbernic_panel_td4310 *ctx = panel_to_anbernic_panel_td4310(panel);
+ struct mipi_dsi_device *dsi = ctx->dsi;
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
+
+ mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 120);
+ mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 120);
+
+ if (ctx->enable_gpio)
+ gpiod_set_value_cansleep(ctx->enable_gpio, 0);
+
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+
+ regulator_disable(ctx->vdd);
+
+ return 0;
+}
+
+static int panel_anbernic_td4310_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct anbernic_panel_td4310 *ctx = panel_to_anbernic_panel_td4310(panel);
+ const struct anbernic_panel_td4310_info *panel_info = ctx->panel_info;
+ struct drm_display_mode *mode;
+ unsigned int i;
+
+ for (i = 0; i < panel_info->num_modes; i++) {
+ mode = drm_mode_duplicate(connector->dev,
+ &panel_info->display_modes[i]);
+ if (!mode)
+ return -ENOMEM;
+
+ drm_mode_set_name(mode);
+
+ mode->type = DRM_MODE_TYPE_DRIVER;
+ if (panel_info->num_modes == 1)
+ mode->type |= DRM_MODE_TYPE_PREFERRED;
+
+ drm_mode_probed_add(connector, mode);
+ }
+
+ connector->display_info.bpc = 8;
+ connector->display_info.width_mm = panel_info->width_mm;
+ connector->display_info.height_mm = panel_info->height_mm;
+ connector->display_info.bus_flags = panel_info->bus_flags;
+
+ return panel_info->num_modes;
+}
+
+static enum drm_panel_orientation panel_anbernic_td4310_get_orientation(struct drm_panel *panel)
+{
+ struct anbernic_panel_td4310 *ctx = panel_to_anbernic_panel_td4310(panel);
+
+ return ctx->orientation;
+}
+
+static const struct drm_panel_funcs panel_anbernic_td4310_funcs = {
+ .prepare = panel_anbernic_td4310_prepare,
+ .unprepare = panel_anbernic_td4310_unprepare,
+ .get_modes = panel_anbernic_td4310_get_modes,
+ .get_orientation = panel_anbernic_td4310_get_orientation,
+};
+
+static int panel_anbernic_td4310_probe(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ struct anbernic_panel_td4310 *ctx;
+ int ret;
+
+ ctx = devm_drm_panel_alloc(dev, struct anbernic_panel_td4310, panel,
+ &panel_anbernic_td4310_funcs,
+ DRM_MODE_CONNECTOR_DSI);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ ctx->dev = dev;
+
+ ctx->panel_info = of_device_get_match_data(dev);
+ if (!ctx->panel_info)
+ return -EINVAL;
+
+ ret = of_drm_get_panel_orientation(dev->of_node, &ctx->orientation);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to get panel orientation\n");
+
+ ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(ctx->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
+ "Cannot get reset gpio\n");
+
+ ctx->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH);
+ if (IS_ERR(ctx->enable_gpio))
+ return dev_err_probe(dev, PTR_ERR(ctx->enable_gpio),
+ "Cannot get enable gpio\n");
+
+ ctx->vdd = devm_regulator_get(dev, "vdd");
+ if (IS_ERR(ctx->vdd))
+ return dev_err_probe(dev, PTR_ERR(ctx->vdd),
+ "Failed to request vdd regulator\n");
+
+ ctx->dsi = dsi;
+ mipi_dsi_set_drvdata(dsi, ctx);
+
+ dsi->lanes = ctx->panel_info->lanes;
+ dsi->format = ctx->panel_info->format;
+ dsi->mode_flags = ctx->panel_info->mode_flags;
+
+ ret = drm_panel_of_backlight(&ctx->panel);
+ if (ret)
+ return ret;
+
+ drm_panel_add(&ctx->panel);
+
+ ret = mipi_dsi_attach(dsi);
+ if (ret < 0) {
+ dev_err(dev, "mipi_dsi_attach failed: %d\n", ret);
+ drm_panel_remove(&ctx->panel);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void panel_anbernic_td4310_remove(struct mipi_dsi_device *dsi)
+{
+ struct anbernic_panel_td4310 *ctx = mipi_dsi_get_drvdata(dsi);
+ int ret;
+
+ ret = mipi_dsi_detach(dsi);
+ if (ret < 0)
+ dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
+
+ drm_panel_remove(&ctx->panel);
+}
+
+static const struct drm_display_mode anbernic_vitapro_modes[] = {
+ {
+ .clock = 140020,
+ .hdisplay = 1080,
+ .hsync_start = 1080 + 50,
+ .hsync_end = 1080 + 50 + 4,
+ .htotal = 1080 + 50 + 4 + 50,
+ .vdisplay = 1920,
+ .vsync_start = 1920 + 15,
+ .vsync_end = 1920 + 15 + 4,
+ .vtotal = 1920 + 15 + 4 + 32,
+ .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+ },
+};
+
+static const struct anbernic_panel_td4310_info anbernic_vitapro_info = {
+ .display_modes = anbernic_vitapro_modes,
+ .num_modes = ARRAY_SIZE(anbernic_vitapro_modes),
+ .width_mm = 69,
+ .height_mm = 121,
+ .bus_flags = DRM_BUS_FLAG_DE_LOW | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE,
+ .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
+ MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET |
+ MIPI_DSI_CLOCK_NON_CONTINUOUS,
+ .format = MIPI_DSI_FMT_RGB888,
+ .lanes = 4,
+ .prepare_delay = 50,
+ .reset_delay = 220,
+ .enable_delay = 120,
+ .disable_delay = 50,
+ .unprepare_delay = 20,
+};
+
+static const struct of_device_id panel_anbernic_td4310_of_match[] = {
+ {
+ .compatible = "anbernic,panel-vita-pro",
+ .data = &anbernic_vitapro_info,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, panel_anbernic_td4310_of_match);
+
+static struct mipi_dsi_driver anbernic_panel_td4310_driver = {
+ .driver = {
+ .name = "panel-anbernic-td4310",
+ .of_match_table = panel_anbernic_td4310_of_match,
+ },
+ .probe = panel_anbernic_td4310_probe,
+ .remove = panel_anbernic_td4310_remove,
+};
+module_mipi_dsi_driver(anbernic_panel_td4310_driver);
+
+MODULE_AUTHOR("Chris Morgan <macromorgan@hotmail.com>");
+MODULE_DESCRIPTION("DRM driver for Anbernic TD4310 MIPI DSI panels");
+MODULE_LICENSE("GPL");
--
2.43.0
_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH V2 4/6] drm/panel: anbernic-td4310: Add RG Vita Pro panel
@ 2026-05-15 22:19 ` Chris Morgan
0 siblings, 0 replies; 19+ messages in thread
From: Chris Morgan @ 2026-05-15 22:19 UTC (permalink / raw)
To: linux-rockchip
Cc: devicetree, xsf, sre, simona, airlied, tzimmermann, mripard,
maarten.lankhorst, jesszhan0024, neil.armstrong, heiko, conor+dt,
krzk+dt, robh, Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
The panel used by Anbernic in the RG Vita-Pro is a DSI panel based
on the TD4310 controller IC. It measures approximately 5.5 inches
diagonally and is 1080x1920 in resolution.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
drivers/gpu/drm/panel/Kconfig | 10 +
drivers/gpu/drm/panel/Makefile | 1 +
drivers/gpu/drm/panel/panel-anbernic-td4310.c | 291 ++++++++++++++++++
3 files changed, 302 insertions(+)
create mode 100644 drivers/gpu/drm/panel/panel-anbernic-td4310.c
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index d592f4f4b939..61dd00297ecc 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -17,6 +17,16 @@ config DRM_PANEL_ABT_Y030XX067A
Y030XX067A 320x480 3.0" panel as found in the YLM RG-280M, RG-300
and RG-99 handheld gaming consoles.
+config DRM_PANEL_ANBERNIC_TD4310
+ tristate "Anbernic TD4310 LCD panel"
+ depends on GPIOLIB && OF
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ help
+ Say Y here to enable support for Anbernic designed panels with the
+ TD4310 panel controller such as the ones used on the Anbernic RG
+ Vita Pro.
+
config DRM_PANEL_ARM_VERSATILE
tristate "ARM Versatile panel driver"
depends on OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index a4291dc3905b..9d8f70c9de3e 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_DRM_PANEL_ABT_Y030XX067A) += panel-abt-y030xx067a.o
+obj-$(CONFIG_DRM_PANEL_ANBERNIC_TD4310) += panel-anbernic-td4310.o
obj-$(CONFIG_DRM_PANEL_ARM_VERSATILE) += panel-arm-versatile.o
obj-$(CONFIG_DRM_PANEL_ASUS_Z00T_TM5P5_NT35596) += panel-asus-z00t-tm5p5-n35596.o
obj-$(CONFIG_DRM_PANEL_AUO_A030JTN01) += panel-auo-a030jtn01.o
diff --git a/drivers/gpu/drm/panel/panel-anbernic-td4310.c b/drivers/gpu/drm/panel/panel-anbernic-td4310.c
new file mode 100644
index 000000000000..733c68ff9771
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-anbernic-td4310.c
@@ -0,0 +1,291 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for Anbernic panels with TD4310 panel controller.
+ *
+ * Copyright (C) 2026 Chris Morgan <macromorgan@hotmail.com>
+ *
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+
+#include <video/mipi_display.h>
+
+struct anbernic_panel_td4310_info {
+ const struct drm_display_mode *display_modes;
+ unsigned int num_modes;
+ u16 width_mm;
+ u16 height_mm;
+ u32 bus_flags;
+ unsigned long mode_flags;
+ u32 format;
+ u32 lanes;
+ u16 prepare_delay;
+ u16 reset_delay;
+ u16 init_delay;
+ u16 enable_delay;
+ u16 disable_delay;
+ u16 unprepare_delay;
+};
+
+struct anbernic_panel_td4310 {
+ struct device *dev;
+ struct mipi_dsi_device *dsi;
+ struct drm_panel panel;
+ const struct anbernic_panel_td4310_info *panel_info;
+ struct gpio_desc *reset_gpio;
+ struct gpio_desc *enable_gpio;
+ struct regulator *vdd;
+ enum drm_panel_orientation orientation;
+};
+
+static inline struct anbernic_panel_td4310 *panel_to_anbernic_panel_td4310(struct drm_panel *panel)
+{
+ return container_of(panel, struct anbernic_panel_td4310, panel);
+}
+
+static int panel_anbernic_td4310_prepare(struct drm_panel *panel)
+{
+ struct anbernic_panel_td4310 *ctx = panel_to_anbernic_panel_td4310(panel);
+ struct mipi_dsi_device *dsi = ctx->dsi;
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
+ int ret;
+
+ ret = regulator_enable(ctx->vdd);
+ if (ret)
+ return ret;
+
+ if (ctx->enable_gpio) {
+ ret = gpiod_set_value_cansleep(ctx->enable_gpio, 1);
+ if (ret)
+ goto err_enable;
+ };
+
+ gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+ mipi_dsi_msleep(&dsi_ctx, 10);
+
+ ret = gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ if (ret)
+ goto err_reset;
+ if (ctx->panel_info->reset_delay)
+ mipi_dsi_msleep(&dsi_ctx, ctx->panel_info->reset_delay);
+ ret = gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+ if (ret)
+ goto err_reset;
+
+ if (ctx->panel_info->enable_delay)
+ mipi_dsi_msleep(&dsi_ctx, ctx->panel_info->enable_delay);
+
+ mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 100);
+ mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 100);
+
+ return dsi_ctx.accum_err;
+
+err_reset:
+ gpiod_set_value_cansleep(ctx->enable_gpio, 0);
+err_enable:
+ regulator_disable(ctx->vdd);
+ return ret;
+}
+
+static int panel_anbernic_td4310_unprepare(struct drm_panel *panel)
+{
+ struct anbernic_panel_td4310 *ctx = panel_to_anbernic_panel_td4310(panel);
+ struct mipi_dsi_device *dsi = ctx->dsi;
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
+
+ mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 120);
+ mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 120);
+
+ if (ctx->enable_gpio)
+ gpiod_set_value_cansleep(ctx->enable_gpio, 0);
+
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+
+ regulator_disable(ctx->vdd);
+
+ return 0;
+}
+
+static int panel_anbernic_td4310_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct anbernic_panel_td4310 *ctx = panel_to_anbernic_panel_td4310(panel);
+ const struct anbernic_panel_td4310_info *panel_info = ctx->panel_info;
+ struct drm_display_mode *mode;
+ unsigned int i;
+
+ for (i = 0; i < panel_info->num_modes; i++) {
+ mode = drm_mode_duplicate(connector->dev,
+ &panel_info->display_modes[i]);
+ if (!mode)
+ return -ENOMEM;
+
+ drm_mode_set_name(mode);
+
+ mode->type = DRM_MODE_TYPE_DRIVER;
+ if (panel_info->num_modes == 1)
+ mode->type |= DRM_MODE_TYPE_PREFERRED;
+
+ drm_mode_probed_add(connector, mode);
+ }
+
+ connector->display_info.bpc = 8;
+ connector->display_info.width_mm = panel_info->width_mm;
+ connector->display_info.height_mm = panel_info->height_mm;
+ connector->display_info.bus_flags = panel_info->bus_flags;
+
+ return panel_info->num_modes;
+}
+
+static enum drm_panel_orientation panel_anbernic_td4310_get_orientation(struct drm_panel *panel)
+{
+ struct anbernic_panel_td4310 *ctx = panel_to_anbernic_panel_td4310(panel);
+
+ return ctx->orientation;
+}
+
+static const struct drm_panel_funcs panel_anbernic_td4310_funcs = {
+ .prepare = panel_anbernic_td4310_prepare,
+ .unprepare = panel_anbernic_td4310_unprepare,
+ .get_modes = panel_anbernic_td4310_get_modes,
+ .get_orientation = panel_anbernic_td4310_get_orientation,
+};
+
+static int panel_anbernic_td4310_probe(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ struct anbernic_panel_td4310 *ctx;
+ int ret;
+
+ ctx = devm_drm_panel_alloc(dev, struct anbernic_panel_td4310, panel,
+ &panel_anbernic_td4310_funcs,
+ DRM_MODE_CONNECTOR_DSI);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ ctx->dev = dev;
+
+ ctx->panel_info = of_device_get_match_data(dev);
+ if (!ctx->panel_info)
+ return -EINVAL;
+
+ ret = of_drm_get_panel_orientation(dev->of_node, &ctx->orientation);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to get panel orientation\n");
+
+ ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(ctx->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
+ "Cannot get reset gpio\n");
+
+ ctx->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH);
+ if (IS_ERR(ctx->enable_gpio))
+ return dev_err_probe(dev, PTR_ERR(ctx->enable_gpio),
+ "Cannot get enable gpio\n");
+
+ ctx->vdd = devm_regulator_get(dev, "vdd");
+ if (IS_ERR(ctx->vdd))
+ return dev_err_probe(dev, PTR_ERR(ctx->vdd),
+ "Failed to request vdd regulator\n");
+
+ ctx->dsi = dsi;
+ mipi_dsi_set_drvdata(dsi, ctx);
+
+ dsi->lanes = ctx->panel_info->lanes;
+ dsi->format = ctx->panel_info->format;
+ dsi->mode_flags = ctx->panel_info->mode_flags;
+
+ ret = drm_panel_of_backlight(&ctx->panel);
+ if (ret)
+ return ret;
+
+ drm_panel_add(&ctx->panel);
+
+ ret = mipi_dsi_attach(dsi);
+ if (ret < 0) {
+ dev_err(dev, "mipi_dsi_attach failed: %d\n", ret);
+ drm_panel_remove(&ctx->panel);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void panel_anbernic_td4310_remove(struct mipi_dsi_device *dsi)
+{
+ struct anbernic_panel_td4310 *ctx = mipi_dsi_get_drvdata(dsi);
+ int ret;
+
+ ret = mipi_dsi_detach(dsi);
+ if (ret < 0)
+ dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
+
+ drm_panel_remove(&ctx->panel);
+}
+
+static const struct drm_display_mode anbernic_vitapro_modes[] = {
+ {
+ .clock = 140020,
+ .hdisplay = 1080,
+ .hsync_start = 1080 + 50,
+ .hsync_end = 1080 + 50 + 4,
+ .htotal = 1080 + 50 + 4 + 50,
+ .vdisplay = 1920,
+ .vsync_start = 1920 + 15,
+ .vsync_end = 1920 + 15 + 4,
+ .vtotal = 1920 + 15 + 4 + 32,
+ .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+ },
+};
+
+static const struct anbernic_panel_td4310_info anbernic_vitapro_info = {
+ .display_modes = anbernic_vitapro_modes,
+ .num_modes = ARRAY_SIZE(anbernic_vitapro_modes),
+ .width_mm = 69,
+ .height_mm = 121,
+ .bus_flags = DRM_BUS_FLAG_DE_LOW | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE,
+ .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
+ MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET |
+ MIPI_DSI_CLOCK_NON_CONTINUOUS,
+ .format = MIPI_DSI_FMT_RGB888,
+ .lanes = 4,
+ .prepare_delay = 50,
+ .reset_delay = 220,
+ .enable_delay = 120,
+ .disable_delay = 50,
+ .unprepare_delay = 20,
+};
+
+static const struct of_device_id panel_anbernic_td4310_of_match[] = {
+ {
+ .compatible = "anbernic,panel-vita-pro",
+ .data = &anbernic_vitapro_info,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, panel_anbernic_td4310_of_match);
+
+static struct mipi_dsi_driver anbernic_panel_td4310_driver = {
+ .driver = {
+ .name = "panel-anbernic-td4310",
+ .of_match_table = panel_anbernic_td4310_of_match,
+ },
+ .probe = panel_anbernic_td4310_probe,
+ .remove = panel_anbernic_td4310_remove,
+};
+module_mipi_dsi_driver(anbernic_panel_td4310_driver);
+
+MODULE_AUTHOR("Chris Morgan <macromorgan@hotmail.com>");
+MODULE_DESCRIPTION("DRM driver for Anbernic TD4310 MIPI DSI panels");
+MODULE_LICENSE("GPL");
--
2.43.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH V2 5/6] dt-bindings: arm: rockchip: Add Anbernic RG Vita-Pro
2026-05-15 22:19 ` Chris Morgan
@ 2026-05-15 22:19 ` Chris Morgan
-1 siblings, 0 replies; 19+ messages in thread
From: Chris Morgan @ 2026-05-15 22:19 UTC (permalink / raw)
To: linux-rockchip
Cc: devicetree, xsf, sre, simona, airlied, tzimmermann, mripard,
maarten.lankhorst, jesszhan0024, neil.armstrong, heiko, conor+dt,
krzk+dt, robh, Chris Morgan, Krzysztof Kozlowski
From: Chris Morgan <macromorgan@hotmail.com>
Add compatible string for the Anbernic RG Vita-Pro.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
---
Documentation/devicetree/bindings/arm/rockchip.yaml | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/Documentation/devicetree/bindings/arm/rockchip.yaml b/Documentation/devicetree/bindings/arm/rockchip.yaml
index 1a9dde18626d..b8babe9d3a26 100644
--- a/Documentation/devicetree/bindings/arm/rockchip.yaml
+++ b/Documentation/devicetree/bindings/arm/rockchip.yaml
@@ -66,6 +66,12 @@ properties:
- anbernic,rg-ds
- const: rockchip,rk3568
+ - description: Anbernic RK3576 Handheld Gaming Console
+ items:
+ - enum:
+ - anbernic,rg-vita-pro
+ - const: rockchip,rk3576
+
- description: Ariaboard Photonicat
items:
- const: ariaboard,photonicat
--
2.43.0
_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH V2 5/6] dt-bindings: arm: rockchip: Add Anbernic RG Vita-Pro
@ 2026-05-15 22:19 ` Chris Morgan
0 siblings, 0 replies; 19+ messages in thread
From: Chris Morgan @ 2026-05-15 22:19 UTC (permalink / raw)
To: linux-rockchip
Cc: devicetree, xsf, sre, simona, airlied, tzimmermann, mripard,
maarten.lankhorst, jesszhan0024, neil.armstrong, heiko, conor+dt,
krzk+dt, robh, Chris Morgan, Krzysztof Kozlowski
From: Chris Morgan <macromorgan@hotmail.com>
Add compatible string for the Anbernic RG Vita-Pro.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
---
Documentation/devicetree/bindings/arm/rockchip.yaml | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/Documentation/devicetree/bindings/arm/rockchip.yaml b/Documentation/devicetree/bindings/arm/rockchip.yaml
index 1a9dde18626d..b8babe9d3a26 100644
--- a/Documentation/devicetree/bindings/arm/rockchip.yaml
+++ b/Documentation/devicetree/bindings/arm/rockchip.yaml
@@ -66,6 +66,12 @@ properties:
- anbernic,rg-ds
- const: rockchip,rk3568
+ - description: Anbernic RK3576 Handheld Gaming Console
+ items:
+ - enum:
+ - anbernic,rg-vita-pro
+ - const: rockchip,rk3576
+
- description: Ariaboard Photonicat
items:
- const: ariaboard,photonicat
--
2.43.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH V2 6/6] arm64: dts: rockchip: Add Anbernic RG Vita-Pro
2026-05-15 22:19 ` Chris Morgan
@ 2026-05-15 22:19 ` Chris Morgan
-1 siblings, 0 replies; 19+ messages in thread
From: Chris Morgan @ 2026-05-15 22:19 UTC (permalink / raw)
To: linux-rockchip
Cc: devicetree, xsf, sre, simona, airlied, tzimmermann, mripard,
maarten.lankhorst, jesszhan0024, neil.armstrong, heiko, conor+dt,
krzk+dt, robh, Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add device tree for the Anbernic RG Vita-Pro, based on the Rockchip
RK3576. All hardware is currently working except for the accelerometer,
the touchscreen, the battery monitor, and DisplayPort over USB-C. PWM
is also missing currently, and as a result the LEDs and panel backlight
are currently controlled via GPIO but will be changed to PWM once
supported.
The Anbernic RG Vita-Pro includes the following hardware:
- A 1080x1920 DSI display with touch.
- 21 buttons.
- 2 Analog joysticks controlled via userspace.
- 3 LEDs.
- 64GB eMMC, 2 SDMMC slots.
- RTL8852BE WiFi/Bluetooth combo
- 1 USB 3.0 USB-C port in OTG mode.
- 3.5mm headphone jack with play button support.
- 5000mAH battery
The following hardware has incomplete driver support and is not yet
working:
- An Invensense icm42607p accelerometer.
- A Cellwise cw221x battery monitor.
- A Synaptics i2c touchscreen.
- DisplayPort over USB-C alt-mode.
- PWM controller.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
arch/arm64/boot/dts/rockchip/Makefile | 3 +
.../rockchip/rk3576-anbernic-rg-vita-pro.dts | 1317 +++++++++++++++++
2 files changed, 1320 insertions(+)
create mode 100644 arch/arm64/boot/dts/rockchip/rk3576-anbernic-rg-vita-pro.dts
diff --git a/arch/arm64/boot/dts/rockchip/Makefile b/arch/arm64/boot/dts/rockchip/Makefile
index cb55c6b70d0e..78c3778eaf4e 100644
--- a/arch/arm64/boot/dts/rockchip/Makefile
+++ b/arch/arm64/boot/dts/rockchip/Makefile
@@ -165,6 +165,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-wolfvision-pf5.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-wolfvision-pf5-display-vz.dtbo
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-wolfvision-pf5-io-expander.dtbo
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3576-100ask-dshanpi-a1.dtb
+dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3576-anbernic-rg-vita-pro.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3576-armsom-sige5.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3576-armsom-sige5-v1.2-wifibt.dtbo
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3576-evb1-v10.dtb
@@ -215,6 +216,8 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-coolpi-4b.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-evb1-v10.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-gameforce-ace.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-indiedroid-nova.dtb
+dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-indiedroid-nova-tower.dtb
+dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-indiedroid-nova-test.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-khadas-edge2.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-nanopi-r6s.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-nanopi-r6c.dtb
diff --git a/arch/arm64/boot/dts/rockchip/rk3576-anbernic-rg-vita-pro.dts b/arch/arm64/boot/dts/rockchip/rk3576-anbernic-rg-vita-pro.dts
new file mode 100644
index 000000000000..e0a90719bdca
--- /dev/null
+++ b/arch/arm64/boot/dts/rockchip/rk3576-anbernic-rg-vita-pro.dts
@@ -0,0 +1,1317 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (c) 2026 Chris Morgan <macromorgan@hotmail.com>
+ */
+
+/dts-v1/;
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/linux-event-codes.h>
+#include <dt-bindings/leds/common.h>
+#include <dt-bindings/pinctrl/rockchip.h>
+#include <dt-bindings/pwm/pwm.h>
+#include <dt-bindings/soc/rockchip,vop2.h>
+#include <dt-bindings/usb/pd.h>
+
+#include "rk3576.dtsi"
+
+/ {
+ model = "Anbernic RG Vita Pro";
+ chassis-type = "handset";
+ compatible = "anbernic,rg-vita-pro", "rockchip,rk3576";
+
+ aliases {
+ mmc0 = &sdhci;
+ mmc1 = &sdmmc;
+ mmc2 = &sdio;
+ };
+
+ chosen {
+ stdout-path = "serial0:1500000n8";
+ };
+
+ adc_keys_home: adc-keys-home {
+ compatible = "adc-keys";
+ io-channel-names = "buttons";
+ io-channels = <&saradc 1>;
+ keyup-threshold-microvolt = <1800000>;
+ poll-interval = <60>;
+
+ button-home {
+ label = "HOME";
+ linux,code = <KEY_HOME>;
+ press-threshold-microvolt = <1750>;
+ };
+ };
+
+ adc_keys_play: adc-keys-play {
+ compatible = "adc-keys";
+ io-channel-names = "buttons";
+ io-channels = <&saradc 3>;
+ keyup-threshold-microvolt = <1300000>;
+ poll-interval = <60>;
+
+ button-play {
+ label = "PLAY";
+ linux,code = <KEY_PLAYPAUSE>;
+ press-threshold-microvolt = <1750>;
+ };
+ };
+
+ /*
+ * Values from BSP, except for internal resistance which was set
+ * to 80 micro ohms which is more likely 80000 micro ohms.
+ */
+ battery: battery {
+ compatible = "simple-battery";
+ charge-full-design-microamp-hours = <8000000>;
+ charge-term-current-microamp = <300000>;
+ constant-charge-current-max-microamp = <10000000>;
+ constant-charge-voltage-max-microvolt = <4350000>;
+ factory-internal-resistance-micro-ohms = <80000>;
+ precharge-current-microamp = <180000>;
+ precharge-upper-limit-microvolt = <3600000>;
+ voltage-max-design-microvolt = <4350000>;
+ voltage-min-design-microvolt = <3000000>;
+ };
+
+ hp_amp: audio-amplifier {
+ compatible = "simple-audio-amplifier";
+ enable-gpios = <&gpio0 RK_PC3 GPIO_ACTIVE_HIGH>;
+ pinctrl-0 = <&hp_amp_enable_h>;
+ pinctrl-names = "default";
+ sound-name-prefix = "Headphone Amp";
+ };
+
+ /*
+ * LEDs and Backlights can be controlled as a PWM, but PWM
+ * driver is not yet available.
+ */
+ gpio_backlight: backlight {
+ compatible = "gpio-backlight";
+ gpios = <&gpio2 RK_PC4 GPIO_ACTIVE_HIGH>;
+ pinctrl-0 = <&led_backlight_h>;
+ pinctrl-names = "default";
+ };
+
+ gpio_leds: leds {
+ compatible = "gpio-leds";
+ pinctrl-0 = <&led_amber_h>, <&led_green_h>, <&led_red_h>;
+ pinctrl-names = "default";
+
+ charging-led {
+ color = <LED_COLOR_ID_AMBER>;
+ function = LED_FUNCTION_CHARGING;
+ gpios = <&gpio3 RK_PA4 GPIO_ACTIVE_HIGH>;
+ };
+
+ full-led {
+ color = <LED_COLOR_ID_GREEN>;
+ function = LED_FUNCTION_POWER;
+ gpios = <&gpio3 RK_PA5 GPIO_ACTIVE_HIGH>;
+ };
+
+ bat-low-led {
+ color = <LED_COLOR_ID_RED>;
+ function = LED_FUNCTION_ALARM;
+ gpios = <&gpio3 RK_PA6 GPIO_ACTIVE_HIGH>;
+ };
+ };
+
+ gpio_keys_control: gpio-keys-control {
+ compatible = "gpio-keys";
+ pinctrl-0 = <&gamepad_keys_l>;
+ pinctrl-names = "default";
+
+ button-a {
+ gpios = <&gpio3 RK_PB6 GPIO_ACTIVE_LOW>;
+ label = "EAST";
+ linux,code = <BTN_EAST>;
+ };
+
+ button-b {
+ gpios = <&gpio3 RK_PB7 GPIO_ACTIVE_LOW>;
+ label = "SOUTH";
+ linux,code = <BTN_SOUTH>;
+ };
+
+ button-down {
+ gpios = <&gpio3 RK_PB1 GPIO_ACTIVE_LOW>;
+ label = "DPAD-DOWN";
+ linux,code = <BTN_DPAD_DOWN>;
+ };
+
+ button-l1 {
+ gpios = <&gpio3 RK_PD7 GPIO_ACTIVE_LOW>;
+ label = "TL";
+ linux,code = <BTN_TL>;
+ };
+
+ button-l2 {
+ gpios = <&gpio3 RK_PC1 GPIO_ACTIVE_LOW>;
+ label = "TL2";
+ linux,code = <BTN_TL2>;
+ };
+
+ button-left {
+ gpios = <&gpio3 RK_PB2 GPIO_ACTIVE_LOW>;
+ label = "DPAD-LEFT";
+ linux,code = <BTN_DPAD_LEFT>;
+ };
+
+ button-menu {
+ gpios = <&gpio3 RK_PA7 GPIO_ACTIVE_LOW>;
+ label = "MENU";
+ linux,code = <BTN_MODE>;
+ };
+
+ button-right {
+ gpios = <&gpio3 RK_PB3 GPIO_ACTIVE_LOW>;
+ label = "DPAD-RIGHT";
+ linux,code = <BTN_DPAD_RIGHT>;
+ };
+
+ button-r1 {
+ gpios = <&gpio3 RK_PC2 GPIO_ACTIVE_LOW>;
+ label = "T2";
+ linux,code = <BTN_TR>;
+ };
+
+ button-r2 {
+ gpios = <&gpio3 RK_PC3 GPIO_ACTIVE_LOW>;
+ label = "TR2";
+ linux,code = <BTN_TR2>;
+ };
+
+ button-select {
+ gpios = <&gpio3 RK_PD6 GPIO_ACTIVE_LOW>;
+ label = "SELECT";
+ linux,code = <BTN_SELECT>;
+ };
+
+ button-start {
+ gpios = <&gpio3 RK_PD3 GPIO_ACTIVE_LOW>;
+ label = "START";
+ linux,code = <BTN_START>;
+ };
+
+ button-thumbl {
+ gpios = <&gpio3 RK_PC0 GPIO_ACTIVE_LOW>;
+ label = "THUMBL";
+ linux,code = <BTN_THUMBL>;
+ };
+
+ button-thumbr {
+ gpios = <&gpio3 RK_PC4 GPIO_ACTIVE_LOW>;
+ label = "THUMBR";
+ linux,code = <BTN_THUMBR>;
+ };
+
+ button-up {
+ gpios = <&gpio3 RK_PB0 GPIO_ACTIVE_LOW>;
+ label = "DPAD-UP";
+ linux,code = <BTN_DPAD_UP>;
+ };
+
+ button-x {
+ gpios = <&gpio3 RK_PB4 GPIO_ACTIVE_LOW>;
+ label = "NORTH";
+ linux,code = <BTN_NORTH>;
+ };
+
+ button-y {
+ gpios = <&gpio3 RK_PB5 GPIO_ACTIVE_LOW>;
+ label = "WEST";
+ linux,code = <BTN_WEST>;
+ };
+ };
+
+ gpio_keys_volume: gpio-keys-volume {
+ compatible = "gpio-keys";
+ autorepeat;
+ pinctrl-0 = <&vol_keys_l>;
+ pinctrl-names = "default";
+
+ vol-down-key {
+ gpios = <&gpio3 RK_PD1 GPIO_ACTIVE_LOW>;
+ label = "VOLUMEDOWN";
+ linux,code = <KEY_VOLUMEDOWN>;
+ };
+
+ vol-up-key {
+ gpios = <&gpio3 RK_PD2 GPIO_ACTIVE_LOW>;
+ label = "VOLUMEUP";
+ linux,code = <KEY_VOLUMEUP>;
+ };
+ };
+
+ hdmi-con {
+ compatible = "hdmi-connector";
+ type = "c";
+
+ port {
+ hdmi_con_in: endpoint {
+ remote-endpoint = <&hdmi_out_con>;
+ };
+ };
+ };
+
+ rfkill {
+ compatible = "rfkill-gpio";
+ pinctrl-names = "default";
+ pinctrl-0 = <&wifi_en_h>;
+ radio-type = "wlan";
+ shutdown-gpios = <&gpio0 RK_PC6 GPIO_ACTIVE_HIGH>;
+ };
+
+ sound: sound {
+ compatible = "simple-audio-card";
+ pinctrl-0 = <&hp_det>;
+ pinctrl-names = "default";
+ simple-audio-card,aux-devs = <&aw87391_pa_l>, <&aw87391_pa_r>,
+ <&hp_amp>;
+ simple-audio-card,format = "i2s";
+ simple-audio-card,hp-det-gpios = <&gpio4 RK_PA4 GPIO_ACTIVE_HIGH>;
+ simple-audio-card,mclk-fs = <256>;
+ simple-audio-card,name = "rockchip,es8388-codec";
+ simple-audio-card,pin-switches = "Headphones", "Internal Speakers";
+ simple-audio-card,routing =
+ "LINPUT1", "Mic Jack",
+ "LINPUT2", "Mic Jack",
+ "RINPUT1", "Onboard Microphone",
+ "RINPUT2", "Onboard Microphone",
+ "Headphones", "Headphone Amp OUTL",
+ "Headphones", "Headphone Amp OUTR",
+ "Headphone Amp INL", "LOUT1",
+ "Headphone Amp INR", "ROUT1",
+ "Internal Speakers", "Left Amp OUT",
+ "Internal Speakers", "Right Amp OUT",
+ "Left Amp IN", "LOUT2",
+ "Right Amp IN", "ROUT2";
+ simple-audio-card,widgets =
+ "Microphone", "Mic Jack",
+ "Microphone", "Onboard Microphone",
+ "Headphone", "Headphones",
+ "Speaker", "Internal Speakers";
+ status = "okay";
+
+ simple-audio-card,codec {
+ sound-dai = <&es8388>;
+ system-clock-frequency = <12288000>;
+ };
+
+ simple-audio-card,cpu {
+ sound-dai = <&sai1>;
+ };
+ };
+
+ vcc_1v1_nldo_s3: regulator-vcc-1v1-nldo-s3 {
+ compatible = "regulator-fixed";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-max-microvolt = <1100000>;
+ regulator-min-microvolt = <1100000>;
+ regulator-name = "vcc_1v1_nldo_s3";
+ vin-supply = <&vcc_3v8_sys>;
+ };
+
+ vcc_2v0_pldo_s3: regulator-vcc-2v0-pldo-s3 {
+ compatible = "regulator-fixed";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-max-microvolt = <2000000>;
+ regulator-min-microvolt = <2000000>;
+ regulator-name = "vcc_2v0_pldo_s3";
+ vin-supply = <&vcc_3v8_sys>;
+ };
+
+ vcc_3v8_sys: regulator-vcc-3v8-sys {
+ compatible = "regulator-fixed";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-max-microvolt = <3800000>;
+ regulator-min-microvolt = <3800000>;
+ regulator-name = "vcc_3v8_sys";
+ };
+
+ vcc3v3_sd_s0: regulator-vcc3v3-sd-s0 {
+ compatible = "regulator-fixed";
+ enable-active-high;
+ gpio = <&gpio0 RK_PB6 GPIO_ACTIVE_HIGH>;
+ pinctrl-0 = <&vcc_3v3_sd_s0_h>;
+ pinctrl-names = "default";
+ regulator-max-microvolt = <3300000>;
+ regulator-min-microvolt = <3300000>;
+ regulator-name = "vcc3v3_sd_s0";
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vcc_sdio: regulator-vcc-sdio {
+ compatible = "regulator-gpio";
+ regulator-name = "vcc_sdio";
+ gpios = <&gpio0 RK_PB4 GPIO_ACTIVE_HIGH>;
+ pinctrl-0 = <&vcc_sdio_h>;
+ pinctrl-names = "default";
+ regulator-max-microvolt = <3300000>;
+ regulator-min-microvolt = <1800000>;
+ states = <1800000 0>, <3300000 1>;
+ };
+
+ vcc_wifi: regulator-vcc-wifi {
+ compatible = "regulator-fixed";
+ regulator-name = "vcc_wifi";
+ enable-active-high;
+ gpios = <&gpio1 RK_PD2 GPIO_ACTIVE_HIGH>;
+ pinctrl-0 = <&vcc_wifi_h>;
+ pinctrl-names = "default";
+ regulator-max-microvolt = <3300000>;
+ regulator-min-microvolt = <3300000>;
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ };
+ };
+
+ vdd_lcd: regulator-vdd-lcd {
+ compatible = "regulator-fixed";
+ regulator-name = "vdd_lcd";
+ regulator-always-on;
+ enable-active-high;
+ gpios = <&gpio0 RK_PB0 GPIO_ACTIVE_HIGH>;
+ pinctrl-0 = <&vdd_lcd_h>;
+ pinctrl-names = "default";
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+};
+
+&combphy0_ps {
+ status = "okay";
+};
+
+&cpu_b0 {
+ cpu-supply = <&vdd_cpu_big_s0>;
+};
+
+&cpu_b1 {
+ cpu-supply = <&vdd_cpu_big_s0>;
+};
+
+&cpu_b2 {
+ cpu-supply = <&vdd_cpu_big_s0>;
+};
+
+&cpu_b3 {
+ cpu-supply = <&vdd_cpu_big_s0>;
+};
+
+&cpu_l0 {
+ cpu-supply = <&vdd_cpu_lit_s0>;
+};
+
+&cpu_l1 {
+ cpu-supply = <&vdd_cpu_lit_s0>;
+};
+
+&cpu_l2 {
+ cpu-supply = <&vdd_cpu_lit_s0>;
+};
+
+&cpu_l3 {
+ cpu-supply = <&vdd_cpu_lit_s0>;
+};
+
+&dsi {
+ status = "okay";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ panel: panel@0 {
+ compatible = "anbernic,panel-vita-pro";
+ reg = <0>;
+ backlight = <&gpio_backlight>;
+ enable-gpios = <&gpio0 RK_PB1 GPIO_ACTIVE_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&lcd_enable>, <&lcd_rst>;
+ reset-gpios = <&gpio0 RK_PB5 GPIO_ACTIVE_LOW>;
+ rotation = <270>;
+ vdd-supply = <&vdd_lcd>;
+
+ port {
+ mipi_in_panel: endpoint {
+ remote-endpoint = <&mipi_out_panel>;
+ };
+ };
+ };
+};
+
+&dsi_in {
+ dsi_in_vp1: endpoint {
+ remote-endpoint = <&vp1_out_dsi>;
+ };
+};
+
+&dsi_out {
+ mipi_out_panel: endpoint {
+ remote-endpoint = <&mipi_in_panel>;
+ };
+};
+
+&gpu {
+ mali-supply = <&vdd_gpu_s0>;
+ status = "okay";
+};
+
+&hdmi {
+ status = "okay";
+};
+
+&hdmi_in {
+ hdmi_in_vp0: endpoint {
+ remote-endpoint = <&vp0_out_hdmi>;
+ };
+};
+
+&hdmi_out {
+ hdmi_out_con: endpoint {
+ remote-endpoint = <&hdmi_con_in>;
+ };
+};
+
+&hdmi_sound {
+ status = "okay";
+};
+
+&hdptxphy {
+ status = "okay";
+};
+
+&i2c0 {
+ pinctrl-0 = <&i2c0m1_xfer>;
+ pinctrl-names = "default";
+ status = "okay";
+
+ /* synaptics,dsx-i2c touchscreen at 0x70 */
+};
+
+&i2c1 {
+ status = "okay";
+
+ pmic@23 {
+ compatible = "rockchip,rk806";
+ reg = <0x23>;
+ #gpio-cells = <2>;
+ gpio-controller;
+ interrupt-parent = <&gpio0>;
+ interrupts = <RK_PA6 IRQ_TYPE_LEVEL_LOW>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pmic_pins
+ &rk806_dvs1_null
+ &rk806_dvs2_null
+ &rk806_dvs3_null>;
+ system-power-controller;
+ vcc1-supply = <&vcc_3v8_sys>;
+ vcc2-supply = <&vcc_3v8_sys>;
+ vcc3-supply = <&vcc_3v8_sys>;
+ vcc4-supply = <&vcc_3v8_sys>;
+ vcc5-supply = <&vcc_3v8_sys>;
+ vcc6-supply = <&vcc_3v8_sys>;
+ vcc7-supply = <&vcc_3v8_sys>;
+ vcc8-supply = <&vcc_3v8_sys>;
+ vcc9-supply = <&vcc_3v8_sys>;
+ vcc10-supply = <&vcc_3v8_sys>;
+ vcc11-supply = <&vcc_2v0_pldo_s3>;
+ vcc12-supply = <&vcc_3v8_sys>;
+ vcc13-supply = <&vcc_1v1_nldo_s3>;
+ vcc14-supply = <&vcc_1v1_nldo_s3>;
+ vcca-supply = <&vcc_3v8_sys>;
+
+ rk806_dvs1_null: dvs1-null-pins {
+ pins = "gpio_pwrctrl1";
+ function = "pin_fun0";
+ };
+
+ rk806_dvs1_pwrdn: dvs1-pwrdn-pins {
+ pins = "gpio_pwrctrl1";
+ function = "pin_fun2";
+ };
+
+ rk806_dvs1_rst: dvs1-rst-pins {
+ pins = "gpio_pwrctrl1";
+ function = "pin_fun3";
+ };
+
+ rk806_dvs1_slp: dvs1-slp-pins {
+ pins = "gpio_pwrctrl1";
+ function = "pin_fun1";
+ };
+
+ rk806_dvs2_dvs: dvs2-dvs-pins {
+ pins = "gpio_pwrctrl2";
+ function = "pin_fun4";
+ };
+
+ rk806_dvs2_gpio: dvs2-gpio-pins {
+ pins = "gpio_pwrctrl2";
+ function = "pin_fun5";
+ };
+
+ rk806_dvs2_null: dvs2-null-pins {
+ pins = "gpio_pwrctrl2";
+ function = "pin_fun0";
+ };
+
+ rk806_dvs2_pwrdn: dvs2-pwrdn-pins {
+ pins = "gpio_pwrctrl2";
+ function = "pin_fun2";
+ };
+
+ rk806_dvs2_rst: dvs2-rst-pins {
+ pins = "gpio_pwrctrl2";
+ function = "pin_fun3";
+ };
+
+ rk806_dvs2_slp: dvs2-slp-pins {
+ pins = "gpio_pwrctrl2";
+ function = "pin_fun1";
+ };
+
+ rk806_dvs3_dvs: dvs3-dvs-pins {
+ pins = "gpio_pwrctrl3";
+ function = "pin_fun4";
+ };
+
+ rk806_dvs3_gpio: dvs3-gpio-pins {
+ pins = "gpio_pwrctrl3";
+ function = "pin_fun5";
+ };
+
+ rk806_dvs3_null: dvs3-null-pins {
+ pins = "gpio_pwrctrl3";
+ function = "pin_fun0";
+ };
+
+ rk806_dvs3_pwrdn: dvs3-pwrdn-pins {
+ pins = "gpio_pwrctrl3";
+ function = "pin_fun2";
+ };
+
+ rk806_dvs3_rst: dvs3-rst-pins {
+ pins = "gpio_pwrctrl3";
+ function = "pin_fun3";
+ };
+
+ rk806_dvs3_slp: dvs3-slp-pins {
+ pins = "gpio_pwrctrl3";
+ function = "pin_fun1";
+ };
+
+ regulators {
+ vdd_cpu_big_s0: dcdc-reg1 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-enable-ramp-delay = <400>;
+ regulator-min-microvolt = <550000>;
+ regulator-max-microvolt = <950000>;
+ regulator-name = "vdd_cpu_big_s0";
+ regulator-ramp-delay = <12500>;
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vdd_npu_s0: dcdc-reg2 {
+ regulator-boot-on;
+ regulator-enable-ramp-delay = <400>;
+ regulator-min-microvolt = <550000>;
+ regulator-max-microvolt = <950000>;
+ regulator-name = "vdd_npu_s0";
+ regulator-ramp-delay = <12500>;
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vdd_cpu_lit_s0: dcdc-reg3 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <550000>;
+ regulator-max-microvolt = <950000>;
+ regulator-name = "vdd_cpu_lit_s0";
+ regulator-ramp-delay = <12500>;
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ regulator-suspend-microvolt = <750000>;
+ };
+ };
+
+ vcc_3v3_s3: dcdc-reg4 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-name = "vcc_3v3_s3";
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ regulator-suspend-microvolt = <3300000>;
+ };
+ };
+
+ vdd_gpu_s0: dcdc-reg5 {
+ regulator-boot-on;
+ regulator-enable-ramp-delay = <400>;
+ regulator-min-microvolt = <550000>;
+ regulator-max-microvolt = <900000>;
+ regulator-name = "vdd_gpu_s0";
+ regulator-ramp-delay = <12500>;
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ regulator-suspend-microvolt = <850000>;
+ };
+ };
+
+ vddq_ddr_s0: dcdc-reg6 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-name = "vddq_ddr_s0";
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vdd_logic_s0: dcdc-reg7 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <550000>;
+ regulator-max-microvolt = <800000>;
+ regulator-name = "vdd_logic_s0";
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vcc_1v8_s3: dcdc-reg8 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "vcc_1v8_s3";
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ regulator-suspend-microvolt = <1800000>;
+ };
+ };
+
+ vdd2_ddr_s3: dcdc-reg9 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-name = "vdd2_ddr_s3";
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ };
+ };
+
+ vdd_ddr_s0: dcdc-reg10 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <550000>;
+ regulator-max-microvolt = <1200000>;
+ regulator-name = "vdd_ddr_s0";
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vcca_1v8_s0: pldo-reg1 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "vcca_1v8_s0";
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vcca1v8_pldo2_s0: pldo-reg2 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "vcca1v8_pldo2_s0";
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vdda_1v2_s0: pldo-reg3 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ regulator-name = "vdda_1v2_s0";
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vcca_3v3_s0: pldo-reg4 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-name = "vcca_3v3_s0";
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vccio_sd_s0: pldo-reg5 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-name = "vccio_sd_s0";
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ };
+ };
+
+ vcca1v8_pldo6_s3: pldo-reg6 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "vcca1v8_pldo6_s3";
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ regulator-suspend-microvolt = <1800000>;
+ };
+ };
+
+ vdd_0v75_s3: nldo-reg1 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <750000>;
+ regulator-max-microvolt = <750000>;
+ regulator-name = "vdd_0v75_s3";
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ regulator-suspend-microvolt = <750000>;
+ };
+ };
+
+ vdda_ddr_pll_s0: nldo-reg2 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <850000>;
+ regulator-max-microvolt = <850000>;
+ regulator-name = "vdda_ddr_pll_s0";
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vdda0v75_hdmi_s0: nldo-reg3 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <750000>;
+ regulator-max-microvolt = <750000>;
+ regulator-name = "vdda0v75_hdmi_s0";
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vdda_0v85_s0: nldo-reg4 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <850000>;
+ regulator-max-microvolt = <850000>;
+ regulator-name = "vdda_0v85_s0";
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vdda_0v75_s0: nldo-reg5 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <750000>;
+ regulator-max-microvolt = <750000>;
+ regulator-name = "vdda_0v75_s0";
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+ };
+ };
+};
+
+&i2c2 {
+ status = "okay";
+
+ /* inv,icm42607 IMU at 0x68 */
+};
+
+&i2c3 {
+ status = "okay";
+
+ es8388: audio-codec@10 {
+ compatible = "everest,es8388", "everest,es8328";
+ reg = <0x10>;
+ assigned-clock-rates = <12288000>;
+ assigned-clocks = <&cru CLK_SAI1_MCLKOUT_TO_IO>;
+ AVDD-supply = <&vcca_3v3_s0>;
+ clocks = <&cru CLK_SAI1_MCLKOUT_TO_IO>;
+ DVDD-supply = <&vcc_3v3_s3>;
+ HPVDD-supply = <&vcca_3v3_s0>;
+ pinctrl-0 = <&sai1m0_mclk>;
+ pinctrl-names = "default";
+ PVDD-supply = <&vcc_3v3_s3>;
+ #sound-dai-cells = <0>;
+ };
+
+ aw87391_pa_l: audio-codec@58 {
+ compatible = "anbernic,rgds-amp", "awinic,aw87391";
+ reg = <0x58>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Left Amp";
+ };
+
+ aw87391_pa_r: audio-codec@5b {
+ compatible = "anbernic,rgds-amp", "awinic,aw87391";
+ reg = <0x5b>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Right Amp";
+ };
+};
+
+&i2c6 {
+ pinctrl-0 = <&i2c6m3_xfer>;
+ pinctrl-names = "default";
+ status = "okay";
+
+ sgm41542: charger@3b {
+ compatible = "sgmicro,sgm41542";
+ reg = <0x3b>;
+ input-current-limit-microamp = <3000000>;
+ input-voltage-limit-microvolt = <4500000>;
+ interrupt-parent = <&gpio0>;
+ interrupts = <RK_PD2 IRQ_TYPE_EDGE_FALLING>;
+ pinctrl-0 = <&charger_irq>;
+ pinctrl-names = "default";
+ monitored-battery = <&battery>;
+ status = "okay";
+
+ regulators {
+ vbus5v0_typec: otg-vbus {
+ regulator-name = "vbus5v0_typec";
+ regulator-max-microvolt = <5000000>;
+ regulator-min-microvolt = <5000000>;
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+ };
+ };
+
+ /* Unused iSmartWare SW2001 encryption device at 0x3c */
+
+ husb311: typec-portc@4e {
+ compatible = "hynetek,husb311", "richtek,rt1711h";
+ reg = <0x4e>;
+ interrupt-parent = <&gpio0>;
+ interrupts = <RK_PD1 IRQ_TYPE_LEVEL_LOW>;
+ pinctrl-0 = <&usbc_int>;
+ pinctrl-names = "default";
+ status = "okay";
+ vbus-supply = <&vbus5v0_typec>;
+
+ connector {
+ compatible = "usb-c-connector";
+ label = "USB-C";
+ data-role = "dual";
+ op-sink-microwatt = <10000000>;
+ power-role = "dual";
+ try-power-role = "sink";
+
+ sink-pdos = <PDO_FIXED(5000, 3000, PDO_FIXED_USB_COMM)
+ PDO_FIXED(9000, 2000, PDO_FIXED_USB_COMM)>;
+ source-pdos = <PDO_FIXED(5000, 3000, PDO_FIXED_USB_COMM)>;
+
+ altmodes {
+ displayport {
+ svid = /bits/ 16 <0xff01>;
+ vdo = <0xffffffff>;
+ };
+ };
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ usbc0_hs_ep: endpoint {
+ remote-endpoint = <&usb_drd0_hs_ep>;
+ };
+ };
+ port@1 {
+ reg = <1>;
+ usbc0_ss_ep: endpoint {
+ remote-endpoint = <&usb_drd0_ss_ep>;
+ };
+ };
+ port@2 {
+ reg = <2>;
+ usbc0_dp_ep: endpoint {
+ remote-endpoint = <&usbdp_phy_ep>;
+ };
+ };
+ };
+ };
+ };
+
+ hym8563: rtc@51 {
+ compatible = "haoyu,hym8563";
+ reg = <0x51>;
+ #clock-cells = <0>;
+ clock-output-names = "hym8563";
+ interrupt-parent = <&gpio0>;
+ interrupts = <RK_PA0 IRQ_TYPE_LEVEL_LOW>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&hym8563_int>;
+ wakeup-source;
+ };
+
+ /* cellwise,cw221X battery manager at 0x64 */
+};
+
+&mipidcphy {
+ status = "okay";
+};
+
+&pcie0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pcie0_perstn>;
+ reset-gpios = <&gpio2 RK_PB4 GPIO_ACTIVE_HIGH>;
+ vpcie3v3-supply = <&vcc_wifi>;
+ status = "okay";
+};
+
+&pinctrl {
+ audio {
+ hp_amp_enable_h: hp-amp-enable {
+ rockchip,pins = <0 RK_PC3 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+
+ hp_det: hp-det {
+ rockchip,pins = <4 RK_PA4 RK_FUNC_GPIO &pcfg_pull_up>;
+ };
+ };
+
+ bluetooth {
+ bt_reset_gpio: bt-reset-pin {
+ rockchip,pins = <2 RK_PB6 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+
+ bt_wake_gpio: bt-wake-pin {
+ rockchip,pins = <2 RK_PB5 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+
+ bt_wake_host_irq: bt-wake-host-irq {
+ rockchip,pins = <2 RK_PC1 RK_FUNC_GPIO &pcfg_pull_up>;
+ };
+ };
+
+ charger {
+ charger_irq: charger-irq {
+ rockchip,pins = <0 RK_PD2 RK_FUNC_GPIO &pcfg_pull_up>;
+ };
+ };
+
+ display {
+ lcd_enable: lcd-enable {
+ rockchip,pins = <0 RK_PB1 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+
+ lcd_rst: lcd-rst {
+ rockchip,pins = <0 RK_PB5 RK_FUNC_GPIO &pcfg_pull_down>;
+ };
+ };
+
+ gpio-keys {
+ vol_keys_l: vol-keys_l {
+ rockchip,pins =
+ <3 RK_PD1 RK_FUNC_GPIO &pcfg_pull_up>,
+ <3 RK_PD2 RK_FUNC_GPIO &pcfg_pull_up>;
+ };
+
+ gamepad_keys_l: gamepad-keys-l {
+ rockchip,pins =
+ <3 RK_PA7 RK_FUNC_GPIO &pcfg_pull_up>,
+ <3 RK_PB0 RK_FUNC_GPIO &pcfg_pull_up>,
+ <3 RK_PB1 RK_FUNC_GPIO &pcfg_pull_up>,
+ <3 RK_PB2 RK_FUNC_GPIO &pcfg_pull_up>,
+ <3 RK_PB3 RK_FUNC_GPIO &pcfg_pull_up>,
+ <3 RK_PB4 RK_FUNC_GPIO &pcfg_pull_up>,
+ <3 RK_PB5 RK_FUNC_GPIO &pcfg_pull_up>,
+ <3 RK_PB6 RK_FUNC_GPIO &pcfg_pull_up>,
+ <3 RK_PB7 RK_FUNC_GPIO &pcfg_pull_up>,
+ <3 RK_PC0 RK_FUNC_GPIO &pcfg_pull_up>,
+ <3 RK_PC1 RK_FUNC_GPIO &pcfg_pull_up>,
+ <3 RK_PC2 RK_FUNC_GPIO &pcfg_pull_up>,
+ <3 RK_PC3 RK_FUNC_GPIO &pcfg_pull_up>,
+ <3 RK_PC4 RK_FUNC_GPIO &pcfg_pull_up>,
+ <3 RK_PD3 RK_FUNC_GPIO &pcfg_pull_up>,
+ <3 RK_PD6 RK_FUNC_GPIO &pcfg_pull_up>,
+ <3 RK_PD7 RK_FUNC_GPIO &pcfg_pull_up>;
+ };
+ };
+
+ gpio-leds {
+ led_amber_h: led-amber-h {
+ rockchip,pins = <3 RK_PA4 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+
+ led_backlight_h: led-backlight-h {
+ rockchip,pins = <2 RK_PC4 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+
+ led_green_h: led-green-h {
+ rockchip,pins = <3 RK_PA5 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+
+ led_red_h: led-red-h {
+ rockchip,pins = <3 RK_PA6 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+ };
+
+ pcie {
+ pcie0_perstn: pcie0-perstn {
+ rockchip,pins = <2 RK_PB4 RK_FUNC_GPIO &pcfg_pull_up>;
+ };
+ };
+
+ regulator {
+ vcc_3v3_sd_s0_h: vcc-3v3-sd-s0-h {
+ rockchip,pins = <0 RK_PB6 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+
+ vcc_sdio_h: vcc-sdio-h {
+ rockchip,pins = <0 RK_PB4 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+
+ vcc_wifi_h: vcc-wifi-h {
+ rockchip,pins = <1 RK_PD2 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+
+ vdd_lcd_h: vdd-lcd-h {
+ rockchip,pins = <0 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+ };
+
+ rtc {
+ hym8563_int: hym8563-int {
+ rockchip,pins = <0 RK_PA0 RK_FUNC_GPIO &pcfg_pull_up>;
+ };
+ };
+
+ touch {
+ touch_int: touch-int {
+ rockchip,pins = <0 RK_PC5 RK_FUNC_GPIO &pcfg_pull_up>;
+ };
+ };
+
+ usb {
+ usbc_int: usbc-int {
+ rockchip,pins = <0 RK_PD1 RK_FUNC_GPIO &pcfg_pull_up>;
+ };
+
+ usbc0_sbu1: usbc0-sbu1 {
+ rockchip,pins = <4 RK_PB6 RK_FUNC_GPIO &pcfg_pull_down>;
+ };
+
+ usbc0_sbu2: usbc0-sbu2 {
+ rockchip,pins = <4 RK_PB7 RK_FUNC_GPIO &pcfg_pull_down>;
+ };
+ };
+
+ wifi {
+ wifi_en_h: wifi-en-h {
+ rockchip,pins = <0 RK_PC6 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+ };
+};
+
+&sai1 {
+ pinctrl-0 = <&sai1m0_lrck
+ &sai1m0_sclk
+ &sai1m0_sdi0
+ &sai1m0_sdo0>;
+ pinctrl-names = "default";
+ status = "okay";
+};
+
+&sai6 {
+ status = "okay";
+};
+
+&saradc {
+ vref-supply = <&vcca1v8_pldo2_s0>;
+ status = "okay";
+};
+
+&sdio {
+ bus-width = <4>;
+ cap-mmc-highspeed;
+ cap-sd-highspeed;
+ disable-wp;
+ max-frequency = <150000000>;
+ no-mmc;
+ no-sdio;
+ pinctrl-0 = <&sdmmc1m0_bus4>, <&sdmmc1m0_clk>,
+ <&sdmmc1m0_cmd>, <&sdmmc1m0_det>;
+ pinctrl-names = "default";
+ sd-uhs-sdr12;
+ sd-uhs-sdr25;
+ sd-uhs-sdr50;
+ sd-uhs-sdr104;
+ vmmc-supply = <&vcc3v3_sd_s0>;
+ vqmmc-supply = <&vcc_sdio>;
+ status = "okay";
+};
+
+&sdhci {
+ bus-width = <8>;
+ full-pwr-cycle-in-suspend;
+ no-sd;
+ no-sdio;
+ non-removable;
+ mmc-hs400-1_8v;
+ mmc-hs400-enhanced-strobe;
+ status = "okay";
+};
+
+&sdmmc {
+ bus-width = <4>;
+ cap-mmc-highspeed;
+ cap-sd-highspeed;
+ disable-wp;
+ max-frequency = <150000000>;
+ no-sdio;
+ no-mmc;
+ pinctrl-0 = <&sdmmc0_bus4>, <&sdmmc0_clk>,
+ <&sdmmc0_cmd>, <&sdmmc0_det>;
+ pinctrl-names = "default";
+ sd-uhs-sdr12;
+ sd-uhs-sdr25;
+ sd-uhs-sdr50;
+ sd-uhs-sdr104;
+ vmmc-supply = <&vcc3v3_sd_s0>;
+ vqmmc-supply = <&vccio_sd_s0>;
+ status = "okay";
+};
+
+&spi1 {
+ num-cs = <1>;
+ pinctrl-0 = <&spi1m2_pins>, <&spi1m2_csn0>;
+ status = "okay";
+
+ /* SPI controlled MCU for joystick and joystick LEDs. */
+};
+
+&vop {
+ status = "okay";
+};
+
+&vop_mmu {
+ status = "okay";
+};
+
+&vp0 {
+ vp0_out_hdmi: endpoint@ROCKCHIP_VOP2_EP_HDMI0 {
+ reg = <ROCKCHIP_VOP2_EP_HDMI0>;
+ remote-endpoint = <&hdmi_in_vp0>;
+ };
+};
+
+&vp1 {
+ vp1_out_dsi: endpoint@ROCKCHIP_VOP2_EP_MIPI0 {
+ reg = <ROCKCHIP_VOP2_EP_MIPI0>;
+ remote-endpoint = <&dsi_in_vp1>;
+ };
+};
+
+&u2phy0 {
+ status = "okay";
+};
+
+&u2phy0_otg {
+ status = "okay";
+};
+
+&uart0 {
+ status = "okay";
+};
+
+&uart6 {
+ pinctrl-0 = <&uart6m1_xfer>, <&uart6m1_ctsn>, <&uart6m1_rtsn>;
+ pinctrl-names = "default";
+ uart-has-rtscts;
+ status = "okay";
+
+ bluetooth {
+ compatible = "realtek,rtl8822cs-bt";
+ device-wake-gpios = <&gpio2 RK_PB6 GPIO_ACTIVE_HIGH>;
+ enable-gpios = <&gpio2 RK_PB5 GPIO_ACTIVE_HIGH>;
+ host-wake-gpios = <&gpio2 RK_PC1 GPIO_ACTIVE_HIGH>;
+ pinctrl-0 = <&bt_reset_gpio>, <&bt_wake_gpio>, <&bt_wake_host_irq>;
+ pinctrl-names = "default";
+ };
+};
+
+&usbdp_phy {
+ mode-switch;
+ orientation-switch;
+ pinctrl-names = "default";
+ pinctrl-0 = <&usbc0_sbu1 &usbc0_sbu2>;
+ sbu1-dc-gpios = <&gpio4 RK_PB6 GPIO_ACTIVE_HIGH>;
+ sbu2-dc-gpios = <&gpio4 RK_PB7 GPIO_ACTIVE_HIGH>;
+ status = "okay";
+
+ port {
+ usbdp_phy_ep: endpoint {
+ remote-endpoint = <&usbc0_dp_ep>;
+ };
+ };
+};
+
+&usb_drd0_dwc3 {
+ usb-role-switch;
+ dr_mode = "otg";
+ status = "okay";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ usb_drd0_hs_ep: endpoint {
+ remote-endpoint = <&usbc0_hs_ep>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+ usb_drd0_ss_ep: endpoint {
+ remote-endpoint = <&usbc0_ss_ep>;
+ };
+ };
+ };
+};
--
2.43.0
_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH V2 6/6] arm64: dts: rockchip: Add Anbernic RG Vita-Pro
@ 2026-05-15 22:19 ` Chris Morgan
0 siblings, 0 replies; 19+ messages in thread
From: Chris Morgan @ 2026-05-15 22:19 UTC (permalink / raw)
To: linux-rockchip
Cc: devicetree, xsf, sre, simona, airlied, tzimmermann, mripard,
maarten.lankhorst, jesszhan0024, neil.armstrong, heiko, conor+dt,
krzk+dt, robh, Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add device tree for the Anbernic RG Vita-Pro, based on the Rockchip
RK3576. All hardware is currently working except for the accelerometer,
the touchscreen, the battery monitor, and DisplayPort over USB-C. PWM
is also missing currently, and as a result the LEDs and panel backlight
are currently controlled via GPIO but will be changed to PWM once
supported.
The Anbernic RG Vita-Pro includes the following hardware:
- A 1080x1920 DSI display with touch.
- 21 buttons.
- 2 Analog joysticks controlled via userspace.
- 3 LEDs.
- 64GB eMMC, 2 SDMMC slots.
- RTL8852BE WiFi/Bluetooth combo
- 1 USB 3.0 USB-C port in OTG mode.
- 3.5mm headphone jack with play button support.
- 5000mAH battery
The following hardware has incomplete driver support and is not yet
working:
- An Invensense icm42607p accelerometer.
- A Cellwise cw221x battery monitor.
- A Synaptics i2c touchscreen.
- DisplayPort over USB-C alt-mode.
- PWM controller.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
arch/arm64/boot/dts/rockchip/Makefile | 3 +
.../rockchip/rk3576-anbernic-rg-vita-pro.dts | 1317 +++++++++++++++++
2 files changed, 1320 insertions(+)
create mode 100644 arch/arm64/boot/dts/rockchip/rk3576-anbernic-rg-vita-pro.dts
diff --git a/arch/arm64/boot/dts/rockchip/Makefile b/arch/arm64/boot/dts/rockchip/Makefile
index cb55c6b70d0e..78c3778eaf4e 100644
--- a/arch/arm64/boot/dts/rockchip/Makefile
+++ b/arch/arm64/boot/dts/rockchip/Makefile
@@ -165,6 +165,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-wolfvision-pf5.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-wolfvision-pf5-display-vz.dtbo
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3568-wolfvision-pf5-io-expander.dtbo
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3576-100ask-dshanpi-a1.dtb
+dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3576-anbernic-rg-vita-pro.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3576-armsom-sige5.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3576-armsom-sige5-v1.2-wifibt.dtbo
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3576-evb1-v10.dtb
@@ -215,6 +216,8 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-coolpi-4b.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-evb1-v10.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-gameforce-ace.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-indiedroid-nova.dtb
+dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-indiedroid-nova-tower.dtb
+dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-indiedroid-nova-test.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-khadas-edge2.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-nanopi-r6s.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-nanopi-r6c.dtb
diff --git a/arch/arm64/boot/dts/rockchip/rk3576-anbernic-rg-vita-pro.dts b/arch/arm64/boot/dts/rockchip/rk3576-anbernic-rg-vita-pro.dts
new file mode 100644
index 000000000000..e0a90719bdca
--- /dev/null
+++ b/arch/arm64/boot/dts/rockchip/rk3576-anbernic-rg-vita-pro.dts
@@ -0,0 +1,1317 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (c) 2026 Chris Morgan <macromorgan@hotmail.com>
+ */
+
+/dts-v1/;
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/linux-event-codes.h>
+#include <dt-bindings/leds/common.h>
+#include <dt-bindings/pinctrl/rockchip.h>
+#include <dt-bindings/pwm/pwm.h>
+#include <dt-bindings/soc/rockchip,vop2.h>
+#include <dt-bindings/usb/pd.h>
+
+#include "rk3576.dtsi"
+
+/ {
+ model = "Anbernic RG Vita Pro";
+ chassis-type = "handset";
+ compatible = "anbernic,rg-vita-pro", "rockchip,rk3576";
+
+ aliases {
+ mmc0 = &sdhci;
+ mmc1 = &sdmmc;
+ mmc2 = &sdio;
+ };
+
+ chosen {
+ stdout-path = "serial0:1500000n8";
+ };
+
+ adc_keys_home: adc-keys-home {
+ compatible = "adc-keys";
+ io-channel-names = "buttons";
+ io-channels = <&saradc 1>;
+ keyup-threshold-microvolt = <1800000>;
+ poll-interval = <60>;
+
+ button-home {
+ label = "HOME";
+ linux,code = <KEY_HOME>;
+ press-threshold-microvolt = <1750>;
+ };
+ };
+
+ adc_keys_play: adc-keys-play {
+ compatible = "adc-keys";
+ io-channel-names = "buttons";
+ io-channels = <&saradc 3>;
+ keyup-threshold-microvolt = <1300000>;
+ poll-interval = <60>;
+
+ button-play {
+ label = "PLAY";
+ linux,code = <KEY_PLAYPAUSE>;
+ press-threshold-microvolt = <1750>;
+ };
+ };
+
+ /*
+ * Values from BSP, except for internal resistance which was set
+ * to 80 micro ohms which is more likely 80000 micro ohms.
+ */
+ battery: battery {
+ compatible = "simple-battery";
+ charge-full-design-microamp-hours = <8000000>;
+ charge-term-current-microamp = <300000>;
+ constant-charge-current-max-microamp = <10000000>;
+ constant-charge-voltage-max-microvolt = <4350000>;
+ factory-internal-resistance-micro-ohms = <80000>;
+ precharge-current-microamp = <180000>;
+ precharge-upper-limit-microvolt = <3600000>;
+ voltage-max-design-microvolt = <4350000>;
+ voltage-min-design-microvolt = <3000000>;
+ };
+
+ hp_amp: audio-amplifier {
+ compatible = "simple-audio-amplifier";
+ enable-gpios = <&gpio0 RK_PC3 GPIO_ACTIVE_HIGH>;
+ pinctrl-0 = <&hp_amp_enable_h>;
+ pinctrl-names = "default";
+ sound-name-prefix = "Headphone Amp";
+ };
+
+ /*
+ * LEDs and Backlights can be controlled as a PWM, but PWM
+ * driver is not yet available.
+ */
+ gpio_backlight: backlight {
+ compatible = "gpio-backlight";
+ gpios = <&gpio2 RK_PC4 GPIO_ACTIVE_HIGH>;
+ pinctrl-0 = <&led_backlight_h>;
+ pinctrl-names = "default";
+ };
+
+ gpio_leds: leds {
+ compatible = "gpio-leds";
+ pinctrl-0 = <&led_amber_h>, <&led_green_h>, <&led_red_h>;
+ pinctrl-names = "default";
+
+ charging-led {
+ color = <LED_COLOR_ID_AMBER>;
+ function = LED_FUNCTION_CHARGING;
+ gpios = <&gpio3 RK_PA4 GPIO_ACTIVE_HIGH>;
+ };
+
+ full-led {
+ color = <LED_COLOR_ID_GREEN>;
+ function = LED_FUNCTION_POWER;
+ gpios = <&gpio3 RK_PA5 GPIO_ACTIVE_HIGH>;
+ };
+
+ bat-low-led {
+ color = <LED_COLOR_ID_RED>;
+ function = LED_FUNCTION_ALARM;
+ gpios = <&gpio3 RK_PA6 GPIO_ACTIVE_HIGH>;
+ };
+ };
+
+ gpio_keys_control: gpio-keys-control {
+ compatible = "gpio-keys";
+ pinctrl-0 = <&gamepad_keys_l>;
+ pinctrl-names = "default";
+
+ button-a {
+ gpios = <&gpio3 RK_PB6 GPIO_ACTIVE_LOW>;
+ label = "EAST";
+ linux,code = <BTN_EAST>;
+ };
+
+ button-b {
+ gpios = <&gpio3 RK_PB7 GPIO_ACTIVE_LOW>;
+ label = "SOUTH";
+ linux,code = <BTN_SOUTH>;
+ };
+
+ button-down {
+ gpios = <&gpio3 RK_PB1 GPIO_ACTIVE_LOW>;
+ label = "DPAD-DOWN";
+ linux,code = <BTN_DPAD_DOWN>;
+ };
+
+ button-l1 {
+ gpios = <&gpio3 RK_PD7 GPIO_ACTIVE_LOW>;
+ label = "TL";
+ linux,code = <BTN_TL>;
+ };
+
+ button-l2 {
+ gpios = <&gpio3 RK_PC1 GPIO_ACTIVE_LOW>;
+ label = "TL2";
+ linux,code = <BTN_TL2>;
+ };
+
+ button-left {
+ gpios = <&gpio3 RK_PB2 GPIO_ACTIVE_LOW>;
+ label = "DPAD-LEFT";
+ linux,code = <BTN_DPAD_LEFT>;
+ };
+
+ button-menu {
+ gpios = <&gpio3 RK_PA7 GPIO_ACTIVE_LOW>;
+ label = "MENU";
+ linux,code = <BTN_MODE>;
+ };
+
+ button-right {
+ gpios = <&gpio3 RK_PB3 GPIO_ACTIVE_LOW>;
+ label = "DPAD-RIGHT";
+ linux,code = <BTN_DPAD_RIGHT>;
+ };
+
+ button-r1 {
+ gpios = <&gpio3 RK_PC2 GPIO_ACTIVE_LOW>;
+ label = "T2";
+ linux,code = <BTN_TR>;
+ };
+
+ button-r2 {
+ gpios = <&gpio3 RK_PC3 GPIO_ACTIVE_LOW>;
+ label = "TR2";
+ linux,code = <BTN_TR2>;
+ };
+
+ button-select {
+ gpios = <&gpio3 RK_PD6 GPIO_ACTIVE_LOW>;
+ label = "SELECT";
+ linux,code = <BTN_SELECT>;
+ };
+
+ button-start {
+ gpios = <&gpio3 RK_PD3 GPIO_ACTIVE_LOW>;
+ label = "START";
+ linux,code = <BTN_START>;
+ };
+
+ button-thumbl {
+ gpios = <&gpio3 RK_PC0 GPIO_ACTIVE_LOW>;
+ label = "THUMBL";
+ linux,code = <BTN_THUMBL>;
+ };
+
+ button-thumbr {
+ gpios = <&gpio3 RK_PC4 GPIO_ACTIVE_LOW>;
+ label = "THUMBR";
+ linux,code = <BTN_THUMBR>;
+ };
+
+ button-up {
+ gpios = <&gpio3 RK_PB0 GPIO_ACTIVE_LOW>;
+ label = "DPAD-UP";
+ linux,code = <BTN_DPAD_UP>;
+ };
+
+ button-x {
+ gpios = <&gpio3 RK_PB4 GPIO_ACTIVE_LOW>;
+ label = "NORTH";
+ linux,code = <BTN_NORTH>;
+ };
+
+ button-y {
+ gpios = <&gpio3 RK_PB5 GPIO_ACTIVE_LOW>;
+ label = "WEST";
+ linux,code = <BTN_WEST>;
+ };
+ };
+
+ gpio_keys_volume: gpio-keys-volume {
+ compatible = "gpio-keys";
+ autorepeat;
+ pinctrl-0 = <&vol_keys_l>;
+ pinctrl-names = "default";
+
+ vol-down-key {
+ gpios = <&gpio3 RK_PD1 GPIO_ACTIVE_LOW>;
+ label = "VOLUMEDOWN";
+ linux,code = <KEY_VOLUMEDOWN>;
+ };
+
+ vol-up-key {
+ gpios = <&gpio3 RK_PD2 GPIO_ACTIVE_LOW>;
+ label = "VOLUMEUP";
+ linux,code = <KEY_VOLUMEUP>;
+ };
+ };
+
+ hdmi-con {
+ compatible = "hdmi-connector";
+ type = "c";
+
+ port {
+ hdmi_con_in: endpoint {
+ remote-endpoint = <&hdmi_out_con>;
+ };
+ };
+ };
+
+ rfkill {
+ compatible = "rfkill-gpio";
+ pinctrl-names = "default";
+ pinctrl-0 = <&wifi_en_h>;
+ radio-type = "wlan";
+ shutdown-gpios = <&gpio0 RK_PC6 GPIO_ACTIVE_HIGH>;
+ };
+
+ sound: sound {
+ compatible = "simple-audio-card";
+ pinctrl-0 = <&hp_det>;
+ pinctrl-names = "default";
+ simple-audio-card,aux-devs = <&aw87391_pa_l>, <&aw87391_pa_r>,
+ <&hp_amp>;
+ simple-audio-card,format = "i2s";
+ simple-audio-card,hp-det-gpios = <&gpio4 RK_PA4 GPIO_ACTIVE_HIGH>;
+ simple-audio-card,mclk-fs = <256>;
+ simple-audio-card,name = "rockchip,es8388-codec";
+ simple-audio-card,pin-switches = "Headphones", "Internal Speakers";
+ simple-audio-card,routing =
+ "LINPUT1", "Mic Jack",
+ "LINPUT2", "Mic Jack",
+ "RINPUT1", "Onboard Microphone",
+ "RINPUT2", "Onboard Microphone",
+ "Headphones", "Headphone Amp OUTL",
+ "Headphones", "Headphone Amp OUTR",
+ "Headphone Amp INL", "LOUT1",
+ "Headphone Amp INR", "ROUT1",
+ "Internal Speakers", "Left Amp OUT",
+ "Internal Speakers", "Right Amp OUT",
+ "Left Amp IN", "LOUT2",
+ "Right Amp IN", "ROUT2";
+ simple-audio-card,widgets =
+ "Microphone", "Mic Jack",
+ "Microphone", "Onboard Microphone",
+ "Headphone", "Headphones",
+ "Speaker", "Internal Speakers";
+ status = "okay";
+
+ simple-audio-card,codec {
+ sound-dai = <&es8388>;
+ system-clock-frequency = <12288000>;
+ };
+
+ simple-audio-card,cpu {
+ sound-dai = <&sai1>;
+ };
+ };
+
+ vcc_1v1_nldo_s3: regulator-vcc-1v1-nldo-s3 {
+ compatible = "regulator-fixed";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-max-microvolt = <1100000>;
+ regulator-min-microvolt = <1100000>;
+ regulator-name = "vcc_1v1_nldo_s3";
+ vin-supply = <&vcc_3v8_sys>;
+ };
+
+ vcc_2v0_pldo_s3: regulator-vcc-2v0-pldo-s3 {
+ compatible = "regulator-fixed";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-max-microvolt = <2000000>;
+ regulator-min-microvolt = <2000000>;
+ regulator-name = "vcc_2v0_pldo_s3";
+ vin-supply = <&vcc_3v8_sys>;
+ };
+
+ vcc_3v8_sys: regulator-vcc-3v8-sys {
+ compatible = "regulator-fixed";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-max-microvolt = <3800000>;
+ regulator-min-microvolt = <3800000>;
+ regulator-name = "vcc_3v8_sys";
+ };
+
+ vcc3v3_sd_s0: regulator-vcc3v3-sd-s0 {
+ compatible = "regulator-fixed";
+ enable-active-high;
+ gpio = <&gpio0 RK_PB6 GPIO_ACTIVE_HIGH>;
+ pinctrl-0 = <&vcc_3v3_sd_s0_h>;
+ pinctrl-names = "default";
+ regulator-max-microvolt = <3300000>;
+ regulator-min-microvolt = <3300000>;
+ regulator-name = "vcc3v3_sd_s0";
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vcc_sdio: regulator-vcc-sdio {
+ compatible = "regulator-gpio";
+ regulator-name = "vcc_sdio";
+ gpios = <&gpio0 RK_PB4 GPIO_ACTIVE_HIGH>;
+ pinctrl-0 = <&vcc_sdio_h>;
+ pinctrl-names = "default";
+ regulator-max-microvolt = <3300000>;
+ regulator-min-microvolt = <1800000>;
+ states = <1800000 0>, <3300000 1>;
+ };
+
+ vcc_wifi: regulator-vcc-wifi {
+ compatible = "regulator-fixed";
+ regulator-name = "vcc_wifi";
+ enable-active-high;
+ gpios = <&gpio1 RK_PD2 GPIO_ACTIVE_HIGH>;
+ pinctrl-0 = <&vcc_wifi_h>;
+ pinctrl-names = "default";
+ regulator-max-microvolt = <3300000>;
+ regulator-min-microvolt = <3300000>;
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ };
+ };
+
+ vdd_lcd: regulator-vdd-lcd {
+ compatible = "regulator-fixed";
+ regulator-name = "vdd_lcd";
+ regulator-always-on;
+ enable-active-high;
+ gpios = <&gpio0 RK_PB0 GPIO_ACTIVE_HIGH>;
+ pinctrl-0 = <&vdd_lcd_h>;
+ pinctrl-names = "default";
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+};
+
+&combphy0_ps {
+ status = "okay";
+};
+
+&cpu_b0 {
+ cpu-supply = <&vdd_cpu_big_s0>;
+};
+
+&cpu_b1 {
+ cpu-supply = <&vdd_cpu_big_s0>;
+};
+
+&cpu_b2 {
+ cpu-supply = <&vdd_cpu_big_s0>;
+};
+
+&cpu_b3 {
+ cpu-supply = <&vdd_cpu_big_s0>;
+};
+
+&cpu_l0 {
+ cpu-supply = <&vdd_cpu_lit_s0>;
+};
+
+&cpu_l1 {
+ cpu-supply = <&vdd_cpu_lit_s0>;
+};
+
+&cpu_l2 {
+ cpu-supply = <&vdd_cpu_lit_s0>;
+};
+
+&cpu_l3 {
+ cpu-supply = <&vdd_cpu_lit_s0>;
+};
+
+&dsi {
+ status = "okay";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ panel: panel@0 {
+ compatible = "anbernic,panel-vita-pro";
+ reg = <0>;
+ backlight = <&gpio_backlight>;
+ enable-gpios = <&gpio0 RK_PB1 GPIO_ACTIVE_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&lcd_enable>, <&lcd_rst>;
+ reset-gpios = <&gpio0 RK_PB5 GPIO_ACTIVE_LOW>;
+ rotation = <270>;
+ vdd-supply = <&vdd_lcd>;
+
+ port {
+ mipi_in_panel: endpoint {
+ remote-endpoint = <&mipi_out_panel>;
+ };
+ };
+ };
+};
+
+&dsi_in {
+ dsi_in_vp1: endpoint {
+ remote-endpoint = <&vp1_out_dsi>;
+ };
+};
+
+&dsi_out {
+ mipi_out_panel: endpoint {
+ remote-endpoint = <&mipi_in_panel>;
+ };
+};
+
+&gpu {
+ mali-supply = <&vdd_gpu_s0>;
+ status = "okay";
+};
+
+&hdmi {
+ status = "okay";
+};
+
+&hdmi_in {
+ hdmi_in_vp0: endpoint {
+ remote-endpoint = <&vp0_out_hdmi>;
+ };
+};
+
+&hdmi_out {
+ hdmi_out_con: endpoint {
+ remote-endpoint = <&hdmi_con_in>;
+ };
+};
+
+&hdmi_sound {
+ status = "okay";
+};
+
+&hdptxphy {
+ status = "okay";
+};
+
+&i2c0 {
+ pinctrl-0 = <&i2c0m1_xfer>;
+ pinctrl-names = "default";
+ status = "okay";
+
+ /* synaptics,dsx-i2c touchscreen at 0x70 */
+};
+
+&i2c1 {
+ status = "okay";
+
+ pmic@23 {
+ compatible = "rockchip,rk806";
+ reg = <0x23>;
+ #gpio-cells = <2>;
+ gpio-controller;
+ interrupt-parent = <&gpio0>;
+ interrupts = <RK_PA6 IRQ_TYPE_LEVEL_LOW>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pmic_pins
+ &rk806_dvs1_null
+ &rk806_dvs2_null
+ &rk806_dvs3_null>;
+ system-power-controller;
+ vcc1-supply = <&vcc_3v8_sys>;
+ vcc2-supply = <&vcc_3v8_sys>;
+ vcc3-supply = <&vcc_3v8_sys>;
+ vcc4-supply = <&vcc_3v8_sys>;
+ vcc5-supply = <&vcc_3v8_sys>;
+ vcc6-supply = <&vcc_3v8_sys>;
+ vcc7-supply = <&vcc_3v8_sys>;
+ vcc8-supply = <&vcc_3v8_sys>;
+ vcc9-supply = <&vcc_3v8_sys>;
+ vcc10-supply = <&vcc_3v8_sys>;
+ vcc11-supply = <&vcc_2v0_pldo_s3>;
+ vcc12-supply = <&vcc_3v8_sys>;
+ vcc13-supply = <&vcc_1v1_nldo_s3>;
+ vcc14-supply = <&vcc_1v1_nldo_s3>;
+ vcca-supply = <&vcc_3v8_sys>;
+
+ rk806_dvs1_null: dvs1-null-pins {
+ pins = "gpio_pwrctrl1";
+ function = "pin_fun0";
+ };
+
+ rk806_dvs1_pwrdn: dvs1-pwrdn-pins {
+ pins = "gpio_pwrctrl1";
+ function = "pin_fun2";
+ };
+
+ rk806_dvs1_rst: dvs1-rst-pins {
+ pins = "gpio_pwrctrl1";
+ function = "pin_fun3";
+ };
+
+ rk806_dvs1_slp: dvs1-slp-pins {
+ pins = "gpio_pwrctrl1";
+ function = "pin_fun1";
+ };
+
+ rk806_dvs2_dvs: dvs2-dvs-pins {
+ pins = "gpio_pwrctrl2";
+ function = "pin_fun4";
+ };
+
+ rk806_dvs2_gpio: dvs2-gpio-pins {
+ pins = "gpio_pwrctrl2";
+ function = "pin_fun5";
+ };
+
+ rk806_dvs2_null: dvs2-null-pins {
+ pins = "gpio_pwrctrl2";
+ function = "pin_fun0";
+ };
+
+ rk806_dvs2_pwrdn: dvs2-pwrdn-pins {
+ pins = "gpio_pwrctrl2";
+ function = "pin_fun2";
+ };
+
+ rk806_dvs2_rst: dvs2-rst-pins {
+ pins = "gpio_pwrctrl2";
+ function = "pin_fun3";
+ };
+
+ rk806_dvs2_slp: dvs2-slp-pins {
+ pins = "gpio_pwrctrl2";
+ function = "pin_fun1";
+ };
+
+ rk806_dvs3_dvs: dvs3-dvs-pins {
+ pins = "gpio_pwrctrl3";
+ function = "pin_fun4";
+ };
+
+ rk806_dvs3_gpio: dvs3-gpio-pins {
+ pins = "gpio_pwrctrl3";
+ function = "pin_fun5";
+ };
+
+ rk806_dvs3_null: dvs3-null-pins {
+ pins = "gpio_pwrctrl3";
+ function = "pin_fun0";
+ };
+
+ rk806_dvs3_pwrdn: dvs3-pwrdn-pins {
+ pins = "gpio_pwrctrl3";
+ function = "pin_fun2";
+ };
+
+ rk806_dvs3_rst: dvs3-rst-pins {
+ pins = "gpio_pwrctrl3";
+ function = "pin_fun3";
+ };
+
+ rk806_dvs3_slp: dvs3-slp-pins {
+ pins = "gpio_pwrctrl3";
+ function = "pin_fun1";
+ };
+
+ regulators {
+ vdd_cpu_big_s0: dcdc-reg1 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-enable-ramp-delay = <400>;
+ regulator-min-microvolt = <550000>;
+ regulator-max-microvolt = <950000>;
+ regulator-name = "vdd_cpu_big_s0";
+ regulator-ramp-delay = <12500>;
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vdd_npu_s0: dcdc-reg2 {
+ regulator-boot-on;
+ regulator-enable-ramp-delay = <400>;
+ regulator-min-microvolt = <550000>;
+ regulator-max-microvolt = <950000>;
+ regulator-name = "vdd_npu_s0";
+ regulator-ramp-delay = <12500>;
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vdd_cpu_lit_s0: dcdc-reg3 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <550000>;
+ regulator-max-microvolt = <950000>;
+ regulator-name = "vdd_cpu_lit_s0";
+ regulator-ramp-delay = <12500>;
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ regulator-suspend-microvolt = <750000>;
+ };
+ };
+
+ vcc_3v3_s3: dcdc-reg4 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-name = "vcc_3v3_s3";
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ regulator-suspend-microvolt = <3300000>;
+ };
+ };
+
+ vdd_gpu_s0: dcdc-reg5 {
+ regulator-boot-on;
+ regulator-enable-ramp-delay = <400>;
+ regulator-min-microvolt = <550000>;
+ regulator-max-microvolt = <900000>;
+ regulator-name = "vdd_gpu_s0";
+ regulator-ramp-delay = <12500>;
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ regulator-suspend-microvolt = <850000>;
+ };
+ };
+
+ vddq_ddr_s0: dcdc-reg6 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-name = "vddq_ddr_s0";
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vdd_logic_s0: dcdc-reg7 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <550000>;
+ regulator-max-microvolt = <800000>;
+ regulator-name = "vdd_logic_s0";
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vcc_1v8_s3: dcdc-reg8 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "vcc_1v8_s3";
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ regulator-suspend-microvolt = <1800000>;
+ };
+ };
+
+ vdd2_ddr_s3: dcdc-reg9 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-name = "vdd2_ddr_s3";
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ };
+ };
+
+ vdd_ddr_s0: dcdc-reg10 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <550000>;
+ regulator-max-microvolt = <1200000>;
+ regulator-name = "vdd_ddr_s0";
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vcca_1v8_s0: pldo-reg1 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "vcca_1v8_s0";
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vcca1v8_pldo2_s0: pldo-reg2 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "vcca1v8_pldo2_s0";
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vdda_1v2_s0: pldo-reg3 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ regulator-name = "vdda_1v2_s0";
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vcca_3v3_s0: pldo-reg4 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-name = "vcca_3v3_s0";
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vccio_sd_s0: pldo-reg5 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-name = "vccio_sd_s0";
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ };
+ };
+
+ vcca1v8_pldo6_s3: pldo-reg6 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "vcca1v8_pldo6_s3";
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ regulator-suspend-microvolt = <1800000>;
+ };
+ };
+
+ vdd_0v75_s3: nldo-reg1 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <750000>;
+ regulator-max-microvolt = <750000>;
+ regulator-name = "vdd_0v75_s3";
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ regulator-suspend-microvolt = <750000>;
+ };
+ };
+
+ vdda_ddr_pll_s0: nldo-reg2 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <850000>;
+ regulator-max-microvolt = <850000>;
+ regulator-name = "vdda_ddr_pll_s0";
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vdda0v75_hdmi_s0: nldo-reg3 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <750000>;
+ regulator-max-microvolt = <750000>;
+ regulator-name = "vdda0v75_hdmi_s0";
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vdda_0v85_s0: nldo-reg4 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <850000>;
+ regulator-max-microvolt = <850000>;
+ regulator-name = "vdda_0v85_s0";
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vdda_0v75_s0: nldo-reg5 {
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <750000>;
+ regulator-max-microvolt = <750000>;
+ regulator-name = "vdda_0v75_s0";
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+ };
+ };
+};
+
+&i2c2 {
+ status = "okay";
+
+ /* inv,icm42607 IMU at 0x68 */
+};
+
+&i2c3 {
+ status = "okay";
+
+ es8388: audio-codec@10 {
+ compatible = "everest,es8388", "everest,es8328";
+ reg = <0x10>;
+ assigned-clock-rates = <12288000>;
+ assigned-clocks = <&cru CLK_SAI1_MCLKOUT_TO_IO>;
+ AVDD-supply = <&vcca_3v3_s0>;
+ clocks = <&cru CLK_SAI1_MCLKOUT_TO_IO>;
+ DVDD-supply = <&vcc_3v3_s3>;
+ HPVDD-supply = <&vcca_3v3_s0>;
+ pinctrl-0 = <&sai1m0_mclk>;
+ pinctrl-names = "default";
+ PVDD-supply = <&vcc_3v3_s3>;
+ #sound-dai-cells = <0>;
+ };
+
+ aw87391_pa_l: audio-codec@58 {
+ compatible = "anbernic,rgds-amp", "awinic,aw87391";
+ reg = <0x58>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Left Amp";
+ };
+
+ aw87391_pa_r: audio-codec@5b {
+ compatible = "anbernic,rgds-amp", "awinic,aw87391";
+ reg = <0x5b>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "Right Amp";
+ };
+};
+
+&i2c6 {
+ pinctrl-0 = <&i2c6m3_xfer>;
+ pinctrl-names = "default";
+ status = "okay";
+
+ sgm41542: charger@3b {
+ compatible = "sgmicro,sgm41542";
+ reg = <0x3b>;
+ input-current-limit-microamp = <3000000>;
+ input-voltage-limit-microvolt = <4500000>;
+ interrupt-parent = <&gpio0>;
+ interrupts = <RK_PD2 IRQ_TYPE_EDGE_FALLING>;
+ pinctrl-0 = <&charger_irq>;
+ pinctrl-names = "default";
+ monitored-battery = <&battery>;
+ status = "okay";
+
+ regulators {
+ vbus5v0_typec: otg-vbus {
+ regulator-name = "vbus5v0_typec";
+ regulator-max-microvolt = <5000000>;
+ regulator-min-microvolt = <5000000>;
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+ };
+ };
+
+ /* Unused iSmartWare SW2001 encryption device at 0x3c */
+
+ husb311: typec-portc@4e {
+ compatible = "hynetek,husb311", "richtek,rt1711h";
+ reg = <0x4e>;
+ interrupt-parent = <&gpio0>;
+ interrupts = <RK_PD1 IRQ_TYPE_LEVEL_LOW>;
+ pinctrl-0 = <&usbc_int>;
+ pinctrl-names = "default";
+ status = "okay";
+ vbus-supply = <&vbus5v0_typec>;
+
+ connector {
+ compatible = "usb-c-connector";
+ label = "USB-C";
+ data-role = "dual";
+ op-sink-microwatt = <10000000>;
+ power-role = "dual";
+ try-power-role = "sink";
+
+ sink-pdos = <PDO_FIXED(5000, 3000, PDO_FIXED_USB_COMM)
+ PDO_FIXED(9000, 2000, PDO_FIXED_USB_COMM)>;
+ source-pdos = <PDO_FIXED(5000, 3000, PDO_FIXED_USB_COMM)>;
+
+ altmodes {
+ displayport {
+ svid = /bits/ 16 <0xff01>;
+ vdo = <0xffffffff>;
+ };
+ };
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ usbc0_hs_ep: endpoint {
+ remote-endpoint = <&usb_drd0_hs_ep>;
+ };
+ };
+ port@1 {
+ reg = <1>;
+ usbc0_ss_ep: endpoint {
+ remote-endpoint = <&usb_drd0_ss_ep>;
+ };
+ };
+ port@2 {
+ reg = <2>;
+ usbc0_dp_ep: endpoint {
+ remote-endpoint = <&usbdp_phy_ep>;
+ };
+ };
+ };
+ };
+ };
+
+ hym8563: rtc@51 {
+ compatible = "haoyu,hym8563";
+ reg = <0x51>;
+ #clock-cells = <0>;
+ clock-output-names = "hym8563";
+ interrupt-parent = <&gpio0>;
+ interrupts = <RK_PA0 IRQ_TYPE_LEVEL_LOW>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&hym8563_int>;
+ wakeup-source;
+ };
+
+ /* cellwise,cw221X battery manager at 0x64 */
+};
+
+&mipidcphy {
+ status = "okay";
+};
+
+&pcie0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pcie0_perstn>;
+ reset-gpios = <&gpio2 RK_PB4 GPIO_ACTIVE_HIGH>;
+ vpcie3v3-supply = <&vcc_wifi>;
+ status = "okay";
+};
+
+&pinctrl {
+ audio {
+ hp_amp_enable_h: hp-amp-enable {
+ rockchip,pins = <0 RK_PC3 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+
+ hp_det: hp-det {
+ rockchip,pins = <4 RK_PA4 RK_FUNC_GPIO &pcfg_pull_up>;
+ };
+ };
+
+ bluetooth {
+ bt_reset_gpio: bt-reset-pin {
+ rockchip,pins = <2 RK_PB6 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+
+ bt_wake_gpio: bt-wake-pin {
+ rockchip,pins = <2 RK_PB5 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+
+ bt_wake_host_irq: bt-wake-host-irq {
+ rockchip,pins = <2 RK_PC1 RK_FUNC_GPIO &pcfg_pull_up>;
+ };
+ };
+
+ charger {
+ charger_irq: charger-irq {
+ rockchip,pins = <0 RK_PD2 RK_FUNC_GPIO &pcfg_pull_up>;
+ };
+ };
+
+ display {
+ lcd_enable: lcd-enable {
+ rockchip,pins = <0 RK_PB1 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+
+ lcd_rst: lcd-rst {
+ rockchip,pins = <0 RK_PB5 RK_FUNC_GPIO &pcfg_pull_down>;
+ };
+ };
+
+ gpio-keys {
+ vol_keys_l: vol-keys_l {
+ rockchip,pins =
+ <3 RK_PD1 RK_FUNC_GPIO &pcfg_pull_up>,
+ <3 RK_PD2 RK_FUNC_GPIO &pcfg_pull_up>;
+ };
+
+ gamepad_keys_l: gamepad-keys-l {
+ rockchip,pins =
+ <3 RK_PA7 RK_FUNC_GPIO &pcfg_pull_up>,
+ <3 RK_PB0 RK_FUNC_GPIO &pcfg_pull_up>,
+ <3 RK_PB1 RK_FUNC_GPIO &pcfg_pull_up>,
+ <3 RK_PB2 RK_FUNC_GPIO &pcfg_pull_up>,
+ <3 RK_PB3 RK_FUNC_GPIO &pcfg_pull_up>,
+ <3 RK_PB4 RK_FUNC_GPIO &pcfg_pull_up>,
+ <3 RK_PB5 RK_FUNC_GPIO &pcfg_pull_up>,
+ <3 RK_PB6 RK_FUNC_GPIO &pcfg_pull_up>,
+ <3 RK_PB7 RK_FUNC_GPIO &pcfg_pull_up>,
+ <3 RK_PC0 RK_FUNC_GPIO &pcfg_pull_up>,
+ <3 RK_PC1 RK_FUNC_GPIO &pcfg_pull_up>,
+ <3 RK_PC2 RK_FUNC_GPIO &pcfg_pull_up>,
+ <3 RK_PC3 RK_FUNC_GPIO &pcfg_pull_up>,
+ <3 RK_PC4 RK_FUNC_GPIO &pcfg_pull_up>,
+ <3 RK_PD3 RK_FUNC_GPIO &pcfg_pull_up>,
+ <3 RK_PD6 RK_FUNC_GPIO &pcfg_pull_up>,
+ <3 RK_PD7 RK_FUNC_GPIO &pcfg_pull_up>;
+ };
+ };
+
+ gpio-leds {
+ led_amber_h: led-amber-h {
+ rockchip,pins = <3 RK_PA4 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+
+ led_backlight_h: led-backlight-h {
+ rockchip,pins = <2 RK_PC4 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+
+ led_green_h: led-green-h {
+ rockchip,pins = <3 RK_PA5 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+
+ led_red_h: led-red-h {
+ rockchip,pins = <3 RK_PA6 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+ };
+
+ pcie {
+ pcie0_perstn: pcie0-perstn {
+ rockchip,pins = <2 RK_PB4 RK_FUNC_GPIO &pcfg_pull_up>;
+ };
+ };
+
+ regulator {
+ vcc_3v3_sd_s0_h: vcc-3v3-sd-s0-h {
+ rockchip,pins = <0 RK_PB6 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+
+ vcc_sdio_h: vcc-sdio-h {
+ rockchip,pins = <0 RK_PB4 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+
+ vcc_wifi_h: vcc-wifi-h {
+ rockchip,pins = <1 RK_PD2 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+
+ vdd_lcd_h: vdd-lcd-h {
+ rockchip,pins = <0 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+ };
+
+ rtc {
+ hym8563_int: hym8563-int {
+ rockchip,pins = <0 RK_PA0 RK_FUNC_GPIO &pcfg_pull_up>;
+ };
+ };
+
+ touch {
+ touch_int: touch-int {
+ rockchip,pins = <0 RK_PC5 RK_FUNC_GPIO &pcfg_pull_up>;
+ };
+ };
+
+ usb {
+ usbc_int: usbc-int {
+ rockchip,pins = <0 RK_PD1 RK_FUNC_GPIO &pcfg_pull_up>;
+ };
+
+ usbc0_sbu1: usbc0-sbu1 {
+ rockchip,pins = <4 RK_PB6 RK_FUNC_GPIO &pcfg_pull_down>;
+ };
+
+ usbc0_sbu2: usbc0-sbu2 {
+ rockchip,pins = <4 RK_PB7 RK_FUNC_GPIO &pcfg_pull_down>;
+ };
+ };
+
+ wifi {
+ wifi_en_h: wifi-en-h {
+ rockchip,pins = <0 RK_PC6 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+ };
+};
+
+&sai1 {
+ pinctrl-0 = <&sai1m0_lrck
+ &sai1m0_sclk
+ &sai1m0_sdi0
+ &sai1m0_sdo0>;
+ pinctrl-names = "default";
+ status = "okay";
+};
+
+&sai6 {
+ status = "okay";
+};
+
+&saradc {
+ vref-supply = <&vcca1v8_pldo2_s0>;
+ status = "okay";
+};
+
+&sdio {
+ bus-width = <4>;
+ cap-mmc-highspeed;
+ cap-sd-highspeed;
+ disable-wp;
+ max-frequency = <150000000>;
+ no-mmc;
+ no-sdio;
+ pinctrl-0 = <&sdmmc1m0_bus4>, <&sdmmc1m0_clk>,
+ <&sdmmc1m0_cmd>, <&sdmmc1m0_det>;
+ pinctrl-names = "default";
+ sd-uhs-sdr12;
+ sd-uhs-sdr25;
+ sd-uhs-sdr50;
+ sd-uhs-sdr104;
+ vmmc-supply = <&vcc3v3_sd_s0>;
+ vqmmc-supply = <&vcc_sdio>;
+ status = "okay";
+};
+
+&sdhci {
+ bus-width = <8>;
+ full-pwr-cycle-in-suspend;
+ no-sd;
+ no-sdio;
+ non-removable;
+ mmc-hs400-1_8v;
+ mmc-hs400-enhanced-strobe;
+ status = "okay";
+};
+
+&sdmmc {
+ bus-width = <4>;
+ cap-mmc-highspeed;
+ cap-sd-highspeed;
+ disable-wp;
+ max-frequency = <150000000>;
+ no-sdio;
+ no-mmc;
+ pinctrl-0 = <&sdmmc0_bus4>, <&sdmmc0_clk>,
+ <&sdmmc0_cmd>, <&sdmmc0_det>;
+ pinctrl-names = "default";
+ sd-uhs-sdr12;
+ sd-uhs-sdr25;
+ sd-uhs-sdr50;
+ sd-uhs-sdr104;
+ vmmc-supply = <&vcc3v3_sd_s0>;
+ vqmmc-supply = <&vccio_sd_s0>;
+ status = "okay";
+};
+
+&spi1 {
+ num-cs = <1>;
+ pinctrl-0 = <&spi1m2_pins>, <&spi1m2_csn0>;
+ status = "okay";
+
+ /* SPI controlled MCU for joystick and joystick LEDs. */
+};
+
+&vop {
+ status = "okay";
+};
+
+&vop_mmu {
+ status = "okay";
+};
+
+&vp0 {
+ vp0_out_hdmi: endpoint@ROCKCHIP_VOP2_EP_HDMI0 {
+ reg = <ROCKCHIP_VOP2_EP_HDMI0>;
+ remote-endpoint = <&hdmi_in_vp0>;
+ };
+};
+
+&vp1 {
+ vp1_out_dsi: endpoint@ROCKCHIP_VOP2_EP_MIPI0 {
+ reg = <ROCKCHIP_VOP2_EP_MIPI0>;
+ remote-endpoint = <&dsi_in_vp1>;
+ };
+};
+
+&u2phy0 {
+ status = "okay";
+};
+
+&u2phy0_otg {
+ status = "okay";
+};
+
+&uart0 {
+ status = "okay";
+};
+
+&uart6 {
+ pinctrl-0 = <&uart6m1_xfer>, <&uart6m1_ctsn>, <&uart6m1_rtsn>;
+ pinctrl-names = "default";
+ uart-has-rtscts;
+ status = "okay";
+
+ bluetooth {
+ compatible = "realtek,rtl8822cs-bt";
+ device-wake-gpios = <&gpio2 RK_PB6 GPIO_ACTIVE_HIGH>;
+ enable-gpios = <&gpio2 RK_PB5 GPIO_ACTIVE_HIGH>;
+ host-wake-gpios = <&gpio2 RK_PC1 GPIO_ACTIVE_HIGH>;
+ pinctrl-0 = <&bt_reset_gpio>, <&bt_wake_gpio>, <&bt_wake_host_irq>;
+ pinctrl-names = "default";
+ };
+};
+
+&usbdp_phy {
+ mode-switch;
+ orientation-switch;
+ pinctrl-names = "default";
+ pinctrl-0 = <&usbc0_sbu1 &usbc0_sbu2>;
+ sbu1-dc-gpios = <&gpio4 RK_PB6 GPIO_ACTIVE_HIGH>;
+ sbu2-dc-gpios = <&gpio4 RK_PB7 GPIO_ACTIVE_HIGH>;
+ status = "okay";
+
+ port {
+ usbdp_phy_ep: endpoint {
+ remote-endpoint = <&usbc0_dp_ep>;
+ };
+ };
+};
+
+&usb_drd0_dwc3 {
+ usb-role-switch;
+ dr_mode = "otg";
+ status = "okay";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ usb_drd0_hs_ep: endpoint {
+ remote-endpoint = <&usbc0_hs_ep>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+ usb_drd0_ss_ep: endpoint {
+ remote-endpoint = <&usbc0_ss_ep>;
+ };
+ };
+ };
+};
--
2.43.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH V2 1/6] dt-bindings: power: supply: sgm41542: document sgm41542
2026-05-15 22:19 ` Chris Morgan
(?)
@ 2026-05-15 22:26 ` sashiko-bot
-1 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-05-15 22:26 UTC (permalink / raw)
To: Chris Morgan; +Cc: krzk+dt, conor+dt, robh, devicetree
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The `$id` field in the YAML binding does not match the file path, which breaks the schema cross-reference system.
--
commit e462413669a11b133f4d59f276a9c7d14b146226
Author: Chris Morgan <macromorgan@hotmail.com>
dt-bindings: power: supply: sgm41542: document sgm41542
This commit documents the device tree bindings for the SG Micro sgm41542
single-cell battery charger and boost controller. It establishes properties
for the hardware's input limits, interrupts, regulators, and monitored
battery values.
> diff --git a/Documentation/devicetree/bindings/power/supply/sgmicro,sgm41542.yaml b/Documentation/devicetree/bindings/power/supply/sgmicro,sgm41542.yaml
> new file mode 100644
> index 0000000000000..fa0d31e496f79
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/power/supply/sgmicro,sgm41542.yaml
> @@ -0,0 +1,99 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/power/supply/sgmicro,sgm41542-charger.yaml#
Does the $id path need to exactly match the file name? The file is named
sgmicro,sgm41542.yaml, but the $id includes an extra -charger suffix.
A mismatch here can break the schema cross-reference system and cause
dt_binding_check to silently skip validation.
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260515221947.299229-1-macroalpha82@gmail.com?part=1
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH V2 2/6] power: supply: sgm41542: Add SG Micro sgm41542 charger
2026-05-15 22:19 ` Chris Morgan
(?)
@ 2026-05-15 22:48 ` sashiko-bot
-1 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-05-15 22:48 UTC (permalink / raw)
To: Chris Morgan; +Cc: robh, conor+dt, devicetree, krzk+dt
Thank you for your contribution! Sashiko AI review found 8 potential issue(s) to consider:
- [Critical] Uninitialized pointer dereference causes kernel panic on missing battery info
- [High] Missing `.remove` callback leads to delayed work timer Use-After-Free
- [High] Race condition exposing uninitialized structures to userspace
- [High] Input current limit configuration truncates 3.8A to 600mA
- [High] Input voltage limit calculation wrap-around incorrectly applies low voltage bounds
- [High] Charge voltage calculation maps 4.336V request to 4.352V, risking battery overcharge
- [Medium] Watchdog timer logic applies incorrect bitmask instead of integer duration
- [Low] Ignored devm_regulator_register return value masks initialization failures
--
commit 974520d45c64c2d48e4b5e84a0082620a313d9d4
Author: Chris Morgan <macromorgan@hotmail.com>
power: supply: sgm41542: Add SG Micro sgm41542 charger
Add support for the SG Micro SGM41542 charger/boost converter.
Driver was adapted from Rockchip BSP driver and confirmed
with vendor datasheet.
Link: https://github.com/rockchip-linux/kernel/blob/develop-6.6/drivers/power/supply/sgm41542_charger.c
Link: https://www.sg-micro.de/rect/assets/1e8de70b-657e-4156-be68-a64fdbe8e418/SGM41541_SGM41542.pdf
> diff --git a/drivers/power/supply/sgm41542_charger.c b/drivers/power/supply/sgm41542_charger.c
> new file mode 100644
> index 0000000000000..1bc2eab554065
> --- /dev/null
> +++ b/drivers/power/supply/sgm41542_charger.c
[ ... ]
> +static int sgm4154x_set_chrg_volt(struct sgm4154x_device *sgm, int chrg_volt)
> +{
> + int reg_val;
> + int ret;
> +
> + /*
> + * Note that the value of 0x01111 represents a "special value"
> + * corresponding to 4352000uV instead of the expected 4336000uV,
> + * per the datasheet. All other values are as expected.
> + */
> + chrg_volt = clamp(chrg_volt, SGM4154X_VREG_V_MIN_UV, sgm->init_data.max_vreg);
> + reg_val = (chrg_volt - SGM4154X_VREG_V_MIN_UV) / SGM4154X_VREG_V_STEP_UV;
> + reg_val = reg_val << 3;
Could this apply a charge voltage higher than requested?
If chrg_volt is exactly 4336000, reg_val becomes 15 (0x0f). According to the
comment and sgm4154x_get_chrg_volt(), 0x0f maps to 4352000uV. Might this
overcharge the battery by 16mV?
[ ... ]
> +static int sgm4154x_set_input_volt_lim(struct sgm4154x_device *sgm,
> + unsigned int vindpm)
> +{
> + enum SGM4154X_VINDPM_OS os_val;
> + unsigned int offset;
> + u8 reg_val;
> + int ret;
> +
> +
> + if (vindpm < SGM4154X_VINDPM_V_MIN_UV ||
> + vindpm > SGM4154X_VINDPM_V_MAX_UV) {
> + dev_err(sgm->dev, "input voltage limit %u outside range\n", vindpm);
> + return -EINVAL;
> + }
> +
> + if (vindpm < 5900000) {
> + os_val = VINDPM_OS_3900MV;
> + offset = 3900000;
> + } else if (vindpm >= 5900000 && vindpm < 7500000) {
> + os_val = VINDPM_OS_5900MV;
> + offset = 5900000;
> + } else if (vindpm >= 7500000 && vindpm < 10500000) {
> + os_val = VINDPM_OS_7500MV;
> + offset = 7500000;
> + } else {
> + os_val = VINDPM_OS_10500MV;
> + offset = 10500000;
> + }
> +
> + ret = regmap_update_bits(sgm->regmap,
> + SGM4154X_CHRG_CTRL_F,
> + SGM4154X_VINDPM_OS_MASK,
> + os_val);
> + if (ret) {
> + dev_err(sgm->dev, "set vin dpm error!\n");
> + return ret;
> + }
> +
> + reg_val = (vindpm - offset) / SGM4154X_VINDPM_STEP_UV;
> +
> + ret = regmap_update_bits(sgm->regmap, SGM4154X_CHRG_CTRL_6,
> + SGM4154X_VINDPM_V_MASK, reg_val);
Is it possible for reg_val to wrap around the register mask here?
If vindpm is 5800000, it falls into the first range (vindpm < 5900000). The
calculation yields a reg_val of 19 (0x13). Since SGM4154X_VINDPM_V_MASK is
only 4 bits (GENMASK(3, 0)), updating the bits will truncate it to 3. This
would set the limit to 4.2V instead of clamping it to the highest supported
value in this range.
[ ... ]
> +static int sgm4154x_set_input_curr_lim(struct sgm4154x_device *sgm, int iindpm)
> +{
> + int reg_val;
> + int ret;
> +
> + if (iindpm < SGM4154X_IINDPM_I_MIN_UA)
> + return -EINVAL;
> +
> + /*
> + * Per the datasheet, values between 100000uA and 3100000uA work
> + * as expected with the register defined as having a step of
> + * 100000 and a min/max of 100000 (0x00) through 3100000 (0x1e).
> + * The register value of 0x1f however corresponds to 3800000uA not
> + * 3200000uA as one would expect.
> + */
> +
> + if (iindpm > 3100000 && iindpm < SGM4154X_IINDPM_I_MAX_UA)
> + iindpm = 3100000;
> + if ((iindpm > SGM4154X_IINDPM_I_MAX_UA) || (iindpm > sgm->init_data.ilim))
> + iindpm = min(SGM4154X_IINDPM_I_MAX_UA, sgm->init_data.ilim);
> +
> + reg_val = (iindpm - SGM4154X_IINDPM_I_MIN_UA) / SGM4154X_IINDPM_STEP_UA;
> +
> + ret = regmap_update_bits(sgm->regmap,
> + SGM4154X_CHRG_CTRL_0,
> + SGM4154X_IINDPM_I_MASK,
> + reg_val);
Could this calculation truncate for the 3.8A setting?
If iindpm is exactly 3800000, reg_val evaluates to 37 (0x25). Since
SGM4154X_IINDPM_I_MASK is GENMASK(4, 0) (0x1f), this truncates to 0x05.
Might this configure the charger to 600mA instead of the intended 3.8A?
[ ... ]
> +static int sgm4154x_set_watchdog_timer(struct sgm4154x_device *sgm, int time)
> +{
> + u8 reg_val;
> + int ret;
> +
> + if (time == 0)
> + reg_val = SGM4154X_WDT_TIMER_DISABLE;
> + else if (time == 40)
> + reg_val = SGM4154X_WDT_TIMER_40S;
> + else if (time == 80)
> + reg_val = SGM4154X_WDT_TIMER_80S;
> + else
> + reg_val = SGM4154X_WDT_TIMER_160S;
[ ... ]
> +static int sgm4154x_charger_set_property(struct power_supply *psy,
> + enum power_supply_property prop,
> + const union power_supply_propval *val)
> +{
> + struct sgm4154x_device *sgm = power_supply_get_drvdata(psy);
> + int ret = -EINVAL;
> +
> + switch (prop) {
> + case POWER_SUPPLY_PROP_ONLINE:
> + if (val->intval) {
> + ret = sgm4154x_enable_charger(sgm);
> + sgm4154x_set_watchdog_timer(sgm, SGM4154X_WDT_TIMER_40S);
Will this pass the correct duration to sgm4154x_set_watchdog_timer()?
SGM4154X_WDT_TIMER_40S is defined as a bitmask (BIT(4), which is 16).
Since the check inside sgm4154x_set_watchdog_timer() looks for exactly 40,
might passing 16 fall through to the else branch and unintentionally set
a 160-second watchdog?
[ ... ]
> +static int sgm4154x_hw_init(struct sgm4154x_device *sgm)
> +{
> + struct power_supply_battery_info *bat_info;
> + int ret;
> + u32 val;
> +
> + /*
> + * If unable to read devicetree info, use default/reset
> + * values from hardware.
> + */
> + sgm->init_data.iprechg = SGM4154X_PRECHRG_I_DEF_UA;
> + sgm->init_data.iterm = SGM4154X_TERMCHRG_I_DEF_UA;
> + sgm->init_data.max_ichg = SGM4154X_ICHRG_I_DEF_UA;
> + sgm->init_data.max_vreg = SGM4154X_VREG_V_DEF_UV;
> + sgm->init_data.vlim = SGM4154X_VINDPM_DEF_UV;
> + sgm->init_data.ilim = SGM4154X_IINDPM_DEF_UA;
> +
> + ret = power_supply_get_battery_info(sgm->charger, &bat_info);
> + if (ret)
> + dev_warn(sgm->dev, "sgm4154x: cannot read battery info\n");
> + else {
> + if (bat_info->constant_charge_current_max_ua)
> + sgm->init_data.max_ichg = bat_info->constant_charge_current_max_ua;
> + if (bat_info->constant_charge_voltage_max_uv)
> + sgm->init_data.max_vreg = bat_info->constant_charge_voltage_max_uv;
> + if (bat_info->charge_term_current_ua)
> + sgm->init_data.iterm = bat_info->charge_term_current_ua;
> + if (bat_info->precharge_current_ua)
> + sgm->init_data.iprechg = bat_info->precharge_current_ua;
> + }
> +
> + power_supply_put_battery_info(sgm->charger, bat_info);
Does this code dereference an uninitialized pointer?
If power_supply_get_battery_info() returns an error, bat_info remains
uninitialized. Calling power_supply_put_battery_info() with that pointer
outside of the success block will try to free its internal tables using
garbage data.
[ ... ]
> +static int sgm4154x_probe(struct i2c_client *client)
> +{
> + const struct i2c_device_id *id = i2c_client_get_device_id(client);
> + struct device *dev = &client->dev;
> + struct sgm4154x_device *sgm;
> + int ret;
> +
> + sgm = devm_kzalloc(dev, sizeof(*sgm), GFP_KERNEL);
> + if (!sgm)
> + return -ENOMEM;
> +
> + sgm->client = client;
> + sgm->dev = dev;
> +
> + strscpy(sgm->model_name, id->name, I2C_NAME_SIZE);
> +
> + sgm->regmap = devm_regmap_init_i2c(client, &sgm4154x_regmap_config);
> + if (IS_ERR(sgm->regmap))
> + return dev_err_probe(dev, PTR_ERR(sgm->regmap),
> + "Failed to allocate register map\n");
> +
> + i2c_set_clientdata(client, sgm);
> +
> + ret = sgm4154x_hw_chipid_detect(sgm);
> + if (ret)
> + dev_err_probe(dev, ret, "Unable to read HW ID\n");
> +
> + device_init_wakeup(dev, 1);
> +
> + ret = sgm4154x_power_supply_init(sgm, dev);
> + if (ret)
> + return dev_err_probe(dev, ret, "Failed to register power supply\n");
> +
> + ret = sgm4154x_hw_init(sgm);
> + if (ret)
> + return dev_err_probe(dev, ret, "Cannot initialize the chip.\n");
> +
> + if (client->irq) {
> + ret = devm_request_threaded_irq(dev, client->irq, NULL,
> + sgm4154x_irq_handler_thread,
> + IRQF_TRIGGER_FALLING |
> + IRQF_ONESHOT,
> + "sgm41542-irq", sgm);
> + if (ret)
> + return ret;
> + enable_irq_wake(client->irq);
> + }
> +
> + sgm->sgm_monitor_wq = devm_alloc_ordered_workqueue(dev, "%s",
> + WQ_MEM_RECLAIM | WQ_FREEZABLE, "sgm-monitor-wq");
Could this result in a race condition exposing uninitialized structures?
sgm4154x_power_supply_init() makes the sysfs interface available before the
workqueue or sgm_delay_work are initialized here. If userspace writes to the
ONLINE property in this small window, it triggers sgm4154x_set_watchdog_timer()
which queues the uninitialized work to a NULL workqueue.
[ ... ]
> + sgm4154x_vbus_regulator_register(sgm);
> +
> + return 0;
> +}
Does this silently ignore regulator registration failures?
If sgm4154x_vbus_regulator_register() fails and returns an error code
(like -EPROBE_DEFER), the return value is dropped and the function returns 0.
[ ... ]
> +static struct i2c_driver sgm4154x_driver = {
> + .driver = {
> + .name = "sgm4154x-charger",
> + .of_match_table = sgm4154x_of_match,
> + },
> + .probe = sgm4154x_probe,
> + .shutdown = sgm4154x_charger_shutdown,
> + .id_table = sgm4154x_i2c_ids,
> +};
Is it possible for the delayed work timer to cause a Use-After-Free?
Since there is no .remove callback and the driver uses devm allocation for
struct sgm4154x_device and the workqueue, devres will automatically free
them when the module unbinds. However, the driver does not explicitly cancel
the delayed work item (sgm->sgm_delay_work). If the timer fires after the
driver structures are freed, it will dereference freed memory.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260515221947.299229-1-macroalpha82@gmail.com?part=2
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH V2 3/6] dt-bindings: display: panel: document Anbernic TD4310 panel
2026-05-15 22:19 ` Chris Morgan
(?)
@ 2026-05-15 22:55 ` sashiko-bot
-1 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-05-15 22:55 UTC (permalink / raw)
To: Chris Morgan; +Cc: krzk+dt, devicetree, conor+dt, robh
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The YAML filename does not match the compatible string.
--
commit 5f66eb8e2fe14ce244cbb7ee82f13643cfae18e7
Author: Chris Morgan <macromorgan@hotmail.com>
dt-bindings: display: panel: document Anbernic TD4310 panel
This commit introduces a new device tree binding for the TD4310-based panel
used in the Anbernic RG Vita-Pro device.
> diff --git a/Documentation/devicetree/bindings/display/panel/anbernic,td4310.yaml b/Documentation/devicetree/bindings/display/panel/anbernic,td4310.yaml
> new file mode 100644
> index 0000000000000..5068dd6b2d524
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/panel/anbernic,td4310.yaml
[ ... ]
> +properties:
> + compatible:
> + items:
> + - const: anbernic,panel-vita-pro
This isn't a bug, but the YAML filename is anbernic,td4310.yaml while the
only defined compatible string is anbernic,panel-vita-pro.
According to device tree schema guidelines, the YAML filename should generally
match the compatible string.
Should the filename be renamed to anbernic,panel-vita-pro.yaml, or should
anbernic,td4310 be added as a compatible string here?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260515221947.299229-1-macroalpha82@gmail.com?part=3
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH V2 4/6] drm/panel: anbernic-td4310: Add RG Vita Pro panel
2026-05-15 22:19 ` Chris Morgan
(?)
@ 2026-05-15 23:10 ` sashiko-bot
-1 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-05-15 23:10 UTC (permalink / raw)
To: Chris Morgan; +Cc: conor+dt, robh, krzk+dt, devicetree
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] Resource leak and hardware state mismatch on prepare error path.
- [High] Enable GPIO is incorrectly asserted during probe before the VDD regulator is enabled.
- [Medium] Panel-specific delay configuration fields are ignored in favor of hardcoded values.
- [Low] Extraneous semicolon after if-statement block.
--
commit b88e27c6a433bac0f5f02ad289ba084e02f359ed
Author: Chris Morgan <macromorgan@hotmail.com>
drm/panel: anbernic-td4310: Add RG Vita Pro panel
This commit adds support for the TD4310 DSI panel used in the Anbernic RG
Vita Pro handheld console. It introduces a new panel driver with power
sequencing, DCS initialization, and display mode definitions.
> diff --git a/drivers/gpu/drm/panel/panel-anbernic-td4310.c b/drivers/gpu/drm/panel/panel-anbernic-td4310.c
> new file mode 100644
> index 0000000000000..733c68ff9771b
> --- /dev/null
> +++ b/drivers/gpu/drm/panel/panel-anbernic-td4310.c
[ ... ]
> +static int panel_anbernic_td4310_prepare(struct drm_panel *panel)
> +{
> + struct anbernic_panel_td4310 *ctx = panel_to_anbernic_panel_td4310(panel);
> + struct mipi_dsi_device *dsi = ctx->dsi;
> + struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
> + int ret;
> +
> + ret = regulator_enable(ctx->vdd);
> + if (ret)
> + return ret;
> +
> + if (ctx->enable_gpio) {
> + ret = gpiod_set_value_cansleep(ctx->enable_gpio, 1);
> + if (ret)
> + goto err_enable;
> + };
This isn't a bug, but is there an extra semicolon here after the if block?
> +
> + gpiod_set_value_cansleep(ctx->reset_gpio, 0);
> + mipi_dsi_msleep(&dsi_ctx, 10);
> +
> + ret = gpiod_set_value_cansleep(ctx->reset_gpio, 1);
> + if (ret)
> + goto err_reset;
> + if (ctx->panel_info->reset_delay)
> + mipi_dsi_msleep(&dsi_ctx, ctx->panel_info->reset_delay);
> + ret = gpiod_set_value_cansleep(ctx->reset_gpio, 0);
> + if (ret)
> + goto err_reset;
> +
> + if (ctx->panel_info->enable_delay)
> + mipi_dsi_msleep(&dsi_ctx, ctx->panel_info->enable_delay);
> +
> + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
> + mipi_dsi_msleep(&dsi_ctx, 100);
Does this code ignore the prepare_delay and init_delay fields defined in
anbernic_panel_td4310_info by using a hardcoded 100ms sleep?
> + mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
> + mipi_dsi_msleep(&dsi_ctx, 100);
> +
> + return dsi_ctx.accum_err;
If mipi_dsi_dcs_exit_sleep_mode_multi or mipi_dsi_dcs_set_display_on_multi
encounters an error and populates dsi_ctx.accum_err, does this return bypass
the err_reset and err_enable cleanup paths?
If the DRM framework does not call unprepare when prepare fails, does
this leave the vdd regulator enabled indefinitely and the GPIOs in their
active states?
> +
> +err_reset:
> + gpiod_set_value_cansleep(ctx->enable_gpio, 0);
> +err_enable:
> + regulator_disable(ctx->vdd);
> + return ret;
> +}
> +
> +static int panel_anbernic_td4310_unprepare(struct drm_panel *panel)
> +{
> + struct anbernic_panel_td4310 *ctx = panel_to_anbernic_panel_td4310(panel);
> + struct mipi_dsi_device *dsi = ctx->dsi;
> + struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
> +
> + mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
> + mipi_dsi_msleep(&dsi_ctx, 120);
> + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
> + mipi_dsi_msleep(&dsi_ctx, 120);
Are the disable_delay and unprepare_delay fields defined in
anbernic_panel_td4310_info intended to be used here instead of the hardcoded
120ms sleeps?
[ ... ]
> +static int panel_anbernic_td4310_probe(struct mipi_dsi_device *dsi)
> +{
> + struct device *dev = &dsi->dev;
> + struct anbernic_panel_td4310 *ctx;
> + int ret;
[ ... ]
> + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
> + if (IS_ERR(ctx->reset_gpio))
> + return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
> + "Cannot get reset gpio\n");
> +
> + ctx->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH);
Does initializing enable_gpio with GPIOD_OUT_HIGH assert the GPIO
immediately during probe, before the vdd regulator is enabled in
panel_anbernic_td4310_prepare?
Could driving control pins active while the main IC power supply is off
cause leakage current or latch-up through ESD diodes?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260515221947.299229-1-macroalpha82@gmail.com?part=4
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH V2 6/6] arm64: dts: rockchip: Add Anbernic RG Vita-Pro
2026-05-15 22:19 ` Chris Morgan
(?)
@ 2026-05-15 23:31 ` sashiko-bot
-1 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-05-15 23:31 UTC (permalink / raw)
To: Chris Morgan; +Cc: devicetree, conor+dt, krzk+dt, robh
Thank you for your contribution! Sashiko AI review found 12 potential issue(s) to consider:
- [High] Missing DTB source files for unrelated Makefile additions.
- [High] Undefined `&pmic_pins` phandle causes compilation failure.
- [High] PCIe reset GPIO polarity is incorrectly configured as active-high.
- [High] Bluetooth GPIO pins are mismatched with their pinctrl definitions.
- [High] Hardware description contradicts the commit message regarding the Bluetooth module.
- [High] Battery charge current is dangerously high and capacity contradicts the commit message.
- [Medium] SPI bus lacks a child device node, breaking userspace joystick control.
- [Medium] Incorrect `pinctrl` assignment for the `&sdio` controller.
- [Medium] Contradictory regulator states defined for `vdd_cpu_lit_s0` and `vdd_gpu_s0`.
- [Medium] Misplaced `vbus-supply` property in the Type-C Port Controller node.
- [Medium] LCD panel supply regulator is incorrectly marked as always-on.
- [Low] Undocumented enablement of HDMI components.
--
commit f6eae2256befc6eb3871393bb039d8a06e21e7b0
Author: Chris Morgan <macromorgan@hotmail.com>
arm64: dts: rockchip: Add Anbernic RG Vita-Pro
This commit adds the device tree for the Anbernic RG Vita-Pro, based on the
Rockchip RK3576. It includes support for the display, buttons, joysticks,
LEDs, eMMC, SDMMC, WiFi/Bluetooth, and battery.
> The Anbernic RG Vita-Pro includes the following hardware:
[ ... ]
> - RTL8852BE WiFi/Bluetooth combo
The commit message claims the device uses an RTL8852BE Wi-Fi/Bluetooth combo,
which interfaces Bluetooth over USB. However, the device tree defines a
realtek,rtl8822cs-bt compatible string under a UART interface (&uart6).
Does this hardware mismatch cause the probe to fail?
> - 5000mAH battery
The commit message specifies a 5000mAh battery, but the device tree specifies
a charge-full-design-microamp-hours of 8000000 (8000mAh).
Additionally, the commit message completely omits HDMI from its detailed list
of hardware, but the device tree enables HDMI components (hdmi, hdptxphy,
and hdmi-con). Is the commit message missing this hardware description, or
is this leftover code?
> diff --git a/arch/arm64/boot/dts/rockchip/Makefile b/arch/arm64/boot/dts/rockchip/Makefile
> index cb55c6b70d0e5..78c3778eaf4e7 100644
> --- a/arch/arm64/boot/dts/rockchip/Makefile
> +++ b/arch/arm64/boot/dts/rockchip/Makefile
[ ... ]
> @@ -215,6 +216,8 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-coolpi-4b.dtb
> dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-evb1-v10.dtb
> dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-gameforce-ace.dtb
> dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-indiedroid-nova.dtb
> +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-indiedroid-nova-tower.dtb
> +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3588s-indiedroid-nova-test.dtb
The .dts source files for these two additions are missing from the patch.
Will this cause the kernel build to fail when make dtbs is executed?
> diff --git a/arch/arm64/boot/dts/rockchip/rk3576-anbernic-rg-vita-pro.dts b/arch/arm64/boot/dts/rockchip/rk3576-anbernic-rg-vita-pro.dts
> new file mode 100644
> index 0000000000000..e0a90719bdcaf
> --- /dev/null
> +++ b/arch/arm64/boot/dts/rockchip/rk3576-anbernic-rg-vita-pro.dts
[ ... ]
> + battery: battery {
> + compatible = "simple-battery";
> + charge-full-design-microamp-hours = <8000000>;
> + charge-term-current-microamp = <300000>;
> + constant-charge-current-max-microamp = <10000000>;
The constant-charge-current-max-microamp is set to 10 Amps (10000000). Does
this exceed the physical limits of the hardware and pose a safety risk for a
handheld single-cell battery?
[ ... ]
> + vdd_lcd: regulator-vdd-lcd {
> + compatible = "regulator-fixed";
> + regulator-name = "vdd_lcd";
> + regulator-always-on;
By marking this regulator as always-on, the panel's power rail cannot be shut
down by the DRM/panel driver framework during screen blanking or system
suspend. Does this result in continuous unnecessary battery drain?
[ ... ]
> + pmic@23 {
> + compatible = "rockchip,rk806";
> + reg = <0x23>;
> + #gpio-cells = <2>;
> + gpio-controller;
> + interrupt-parent = <&gpio0>;
> + interrupts = <RK_PA6 IRQ_TYPE_LEVEL_LOW>;
> + pinctrl-names = "default";
> + pinctrl-0 = <&pmic_pins
> + &rk806_dvs1_null
This pinctrl-0 property references &pmic_pins, but this node does not appear
to be defined in the .dts file. Will this missing definition cause a fatal
device tree compilation error?
[ ... ]
> + vdd_cpu_lit_s0: dcdc-reg3 {
> + regulator-always-on;
> + regulator-boot-on;
> + regulator-min-microvolt = <550000>;
> + regulator-max-microvolt = <950000>;
> + regulator-name = "vdd_cpu_lit_s0";
> + regulator-ramp-delay = <12500>;
> + regulator-state-mem {
> + regulator-off-in-suspend;
> + regulator-suspend-microvolt = <750000>;
> + };
> + };
Is it semantically valid to request a specific voltage level
(regulator-suspend-microvolt) during suspend for a regulator that is explicitly
commanded to shut off (regulator-off-in-suspend)? Can this lead to undefined
behavior in the PMIC framework?
[ ... ]
> + vdd_gpu_s0: dcdc-reg5 {
> + regulator-boot-on;
> + regulator-enable-ramp-delay = <400>;
> + regulator-min-microvolt = <550000>;
> + regulator-max-microvolt = <900000>;
> + regulator-name = "vdd_gpu_s0";
> + regulator-ramp-delay = <12500>;
> + regulator-state-mem {
> + regulator-off-in-suspend;
> + regulator-suspend-microvolt = <850000>;
> + };
> + };
Similarly, could this combination of regulator-off-in-suspend and
regulator-suspend-microvolt for the GPU regulator cause issues during suspend?
[ ... ]
> + husb311: typec-portc@4e {
> + compatible = "hynetek,husb311", "richtek,rt1711h";
> + reg = <0x4e>;
> + interrupt-parent = <&gpio0>;
> + interrupts = <RK_PD1 IRQ_TYPE_LEVEL_LOW>;
> + pinctrl-0 = <&usbc_int>;
> + pinctrl-names = "default";
> + status = "okay";
> + vbus-supply = <&vbus5v0_typec>;
> +
> + connector {
According to standard USB Type-C bindings, shouldn't the vbus-supply property
reside inside the connector child node? Will the TCPM framework fail to find
the VBUS regulator if it is placed in the root of the husb311 node, preventing
the device from supplying power in OTG mode?
[ ... ]
> +&pcie0 {
> + pinctrl-names = "default";
> + pinctrl-0 = <&pcie0_perstn>;
> + reset-gpios = <&gpio2 RK_PB4 GPIO_ACTIVE_HIGH>;
The PCIe specification dictates that the PERST# reset signal is active-low,
which is supported by the pcie0_perstn pin name. By configuring it as
active-high, will the PCIe controller invert the reset logic and permanently
hold the Wi-Fi card in reset, causing link training to fail?
[ ... ]
> +&sdio {
> + bus-width = <4>;
> + cap-mmc-highspeed;
> + cap-sd-highspeed;
> + disable-wp;
> + max-frequency = <150000000>;
> + no-mmc;
> + no-sdio;
> + pinctrl-0 = <&sdmmc1m0_bus4>, <&sdmmc1m0_clk>,
> + <&sdmmc1m0_cmd>, <&sdmmc1m0_det>;
The &sdio node configures its pins using sdmmc1m0 properties. Since the SDIO
controller is a distinct IP block from SDMMC1, does routing the SDIO block
through pins intended for another controller disconnect it from the physical
SD card slot?
[ ... ]
> +&spi1 {
> + num-cs = <1>;
> + pinctrl-0 = <&spi1m2_pins>, <&spi1m2_csn0>;
> + status = "okay";
> +
> + /* SPI controlled MCU for joystick and joystick LEDs. */
> +};
The &spi1 bus is enabled for userspace-controlled analog joysticks, but no
child device node (such as spidev or a dedicated driver compatible) is defined.
Since the Linux SPI core will not instantiate any devices on an empty bus, does
this completely prevent userspace from communicating with the microcontroller?
[ ... ]
> +&uart6 {
> + pinctrl-0 = <&uart6m1_xfer>, <&uart6m1_ctsn>, <&uart6m1_rtsn>;
> + pinctrl-names = "default";
> + uart-has-rtscts;
> + status = "okay";
> +
> + bluetooth {
> + compatible = "realtek,rtl8822cs-bt";
> + device-wake-gpios = <&gpio2 RK_PB6 GPIO_ACTIVE_HIGH>;
> + enable-gpios = <&gpio2 RK_PB5 GPIO_ACTIVE_HIGH>;
> + host-wake-gpios = <&gpio2 RK_PC1 GPIO_ACTIVE_HIGH>;
The enable-gpios is assigned to RK_PB5 and device-wake-gpios to RK_PB6.
However, the corresponding pinctrl configurations map RK_PB5 to bt_wake_gpio
and RK_PB6 to bt_reset_gpio.
Does this logical swap mean the driver will toggle the wake line when it
intends to reset the device, causing Bluetooth initialization to fail?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260515221947.299229-1-macroalpha82@gmail.com?part=6
^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2026-05-15 23:31 UTC | newest]
Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-15 22:19 [PATCH V2 0/6] Add Anbernic RG Vita-Pro Chris Morgan
2026-05-15 22:19 ` Chris Morgan
2026-05-15 22:19 ` [PATCH V2 1/6] dt-bindings: power: supply: sgm41542: document sgm41542 Chris Morgan
2026-05-15 22:19 ` Chris Morgan
2026-05-15 22:26 ` sashiko-bot
2026-05-15 22:19 ` [PATCH V2 2/6] power: supply: sgm41542: Add SG Micro sgm41542 charger Chris Morgan
2026-05-15 22:19 ` Chris Morgan
2026-05-15 22:48 ` sashiko-bot
2026-05-15 22:19 ` [PATCH V2 3/6] dt-bindings: display: panel: document Anbernic TD4310 panel Chris Morgan
2026-05-15 22:19 ` Chris Morgan
2026-05-15 22:55 ` sashiko-bot
2026-05-15 22:19 ` [PATCH V2 4/6] drm/panel: anbernic-td4310: Add RG Vita Pro panel Chris Morgan
2026-05-15 22:19 ` Chris Morgan
2026-05-15 23:10 ` sashiko-bot
2026-05-15 22:19 ` [PATCH V2 5/6] dt-bindings: arm: rockchip: Add Anbernic RG Vita-Pro Chris Morgan
2026-05-15 22:19 ` Chris Morgan
2026-05-15 22:19 ` [PATCH V2 6/6] arm64: dts: " Chris Morgan
2026-05-15 22:19 ` Chris Morgan
2026-05-15 23:31 ` sashiko-bot
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.