From 282195c2886f0296095f3c7f784dbd522f6e2184 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Feb 2026 18:20:45 +0000 Subject: [PATCH 01/11] Initial plan From 8cf55c0414ef5985554bd5624c221d7553f82e87 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Feb 2026 18:24:35 +0000 Subject: [PATCH 02/11] fix(nanopc-t6): apply rk806 core compat patches with valid hunks Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com> --- docs/nanopc-T6.md | 6 ++++-- nix/builders/nanopc-t6/base.nix | 4 ++++ nix/builders/nanopc-t6/rk806-disable-slave-restart.patch | 2 +- nix/builders/nanopc-t6/rk806-friendlyarm-reset-compat.patch | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/nanopc-T6.md b/docs/nanopc-T6.md index e537774..aba5e60 100644 --- a/docs/nanopc-T6.md +++ b/docs/nanopc-T6.md @@ -69,7 +69,9 @@ For the current NanoPC-T6 image path, the kernel comes from `nabam/nixos-rockchi 1. Keep mainline/nabam kernel and align reset configuration with FriendlyARM semantics. 2. Keep OP-TEE `firmware`/`reserved-memory` nodes in DTS patch. -3. Mirror FriendlyARM reset semantics by setting `pmic-reset-func = <1>` in DTS and adding a compatibility kernel patch that prefers `pmic-reset-func` (falls back to `rockchip,reset-mode`, defaults to mode `0` if neither is set). +3. Mirror FriendlyARM reset semantics by setting `pmic-reset-func = <1>` in DTS and adding compatibility kernel patches in `rk8xx-core.c` that: + - prefer `pmic-reset-func` (fall back to `rockchip,reset-mode`, default `0`), + - disable `RK806_SLAVE_RESTART_FUN` (matching FriendlyARM `rk806-core.c` behavior). ### 3. Mask ROM Button (SARADC) @@ -97,7 +99,7 @@ Earlier attempts tried: 5. Setting `rockchip,reset-mode = <1>` — was never actually tested due to malformed patch (build failed) 6. Setting `rockchip,reset-mode = <0>` — mode 0 (restart PMU) tested but didn't work; SoC may not reset if caps hold voltage 7. Removing `rockchip,reset-mode` entirely — tested but didn't work; letting U-Boot config persist wasn't enough because the MFD driver's `pre_init_reg` still modifies SYS_CFG3 (enables SLAVE_RESTART_FUN) -8. **Current path**: Use nabam mainline kernel + RK806 reset compatibility patches mirroring FriendlyARM semantics +8. **Current path**: Use nabam mainline kernel + RK806 core compatibility patches mirroring FriendlyARM semantics ## Device peripheral firmware diff --git a/nix/builders/nanopc-t6/base.nix b/nix/builders/nanopc-t6/base.nix index 5444d2d..49d0611 100644 --- a/nix/builders/nanopc-t6/base.nix +++ b/nix/builders/nanopc-t6/base.nix @@ -88,6 +88,10 @@ name = "rk806-friendlyarm-reset-compat.patch"; patch = ./rk806-friendlyarm-reset-compat.patch; } + { + name = "rk806-disable-slave-restart.patch"; + patch = ./rk806-disable-slave-restart.patch; + } ]; }); in diff --git a/nix/builders/nanopc-t6/rk806-disable-slave-restart.patch b/nix/builders/nanopc-t6/rk806-disable-slave-restart.patch index ece4770..5abb82d 100644 --- a/nix/builders/nanopc-t6/rk806-disable-slave-restart.patch +++ b/nix/builders/nanopc-t6/rk806-disable-slave-restart.patch @@ -1,6 +1,6 @@ --- a/drivers/mfd/rk8xx-core.c +++ b/drivers/mfd/rk8xx-core.c -@@ -154,7 +154,7 @@ +@@ -154,6 +154,6 @@ static const struct rk808_reg_data rk806_pre_init_reg[] = { { RK806_GPIO_INT_CONFIG, RK806_INT_POL_MSK, RK806_INT_POL_L }, - { RK806_SYS_CFG3, RK806_SLAVE_RESTART_FUN_MSK, RK806_SLAVE_RESTART_FUN_EN }, diff --git a/nix/builders/nanopc-t6/rk806-friendlyarm-reset-compat.patch b/nix/builders/nanopc-t6/rk806-friendlyarm-reset-compat.patch index ee860cd..621dc5b 100644 --- a/nix/builders/nanopc-t6/rk806-friendlyarm-reset-compat.patch +++ b/nix/builders/nanopc-t6/rk806-friendlyarm-reset-compat.patch @@ -1,6 +1,6 @@ --- a/drivers/mfd/rk8xx-core.c +++ b/drivers/mfd/rk8xx-core.c -@@ -716,8 +716,11 @@ int rk8xx_probe(struct device *dev, int variant, unsigned int irq, struct regma +@@ -716,8 +716,8 @@ int rk8xx_probe(struct device *dev, int variant, unsigned int irq, struct regma dual_support = IRQF_SHARED; - ret = device_property_read_u32(dev, "rockchip,reset-mode", &rst_fun); From 1b61bbfa00b4caf69f2c730023b5a36bdfd259d7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Feb 2026 21:30:56 +0000 Subject: [PATCH 03/11] fix(nanopc-t6): drop OP-TEE DT reservation from T6 patch to restore HDMI path Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com> --- docs/nanopc-T6.md | 3 +-- .../nanopc-t6/rk3588-nanopc-t6.dtsi.patch | 25 ------------------- 2 files changed, 1 insertion(+), 27 deletions(-) diff --git a/docs/nanopc-T6.md b/docs/nanopc-T6.md index aba5e60..f141541 100644 --- a/docs/nanopc-T6.md +++ b/docs/nanopc-T6.md @@ -68,8 +68,7 @@ For the current NanoPC-T6 image path, the kernel comes from `nabam/nixos-rockchi **Current approach**: 1. Keep mainline/nabam kernel and align reset configuration with FriendlyARM semantics. -2. Keep OP-TEE `firmware`/`reserved-memory` nodes in DTS patch. -3. Mirror FriendlyARM reset semantics by setting `pmic-reset-func = <1>` in DTS and adding compatibility kernel patches in `rk8xx-core.c` that: +2. Mirror FriendlyARM reset semantics by setting `pmic-reset-func = <1>` in DTS and adding compatibility kernel patches in `rk8xx-core.c` that: - prefer `pmic-reset-func` (fall back to `rockchip,reset-mode`, default `0`), - disable `RK806_SLAVE_RESTART_FUN` (matching FriendlyARM `rk806-core.c` behavior). diff --git a/nix/builders/nanopc-t6/rk3588-nanopc-t6.dtsi.patch b/nix/builders/nanopc-t6/rk3588-nanopc-t6.dtsi.patch index 35445de..601da7d 100644 --- a/nix/builders/nanopc-t6/rk3588-nanopc-t6.dtsi.patch +++ b/nix/builders/nanopc-t6/rk3588-nanopc-t6.dtsi.patch @@ -1,30 +1,5 @@ --- a/arch/arm64/boot/dts/rockchip/rk3588-nanopc-t6.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-nanopc-t6.dtsi -@@ -224,6 +224,24 @@ - regulator-min-microvolt = <3300000>; - regulator-name = "vcc3v3_sd_s0"; - vin-supply = <&vcc_3v3_s3>; -+ }; -+ -+ firmware { -+ optee { -+ compatible = "linaro,optee-tz"; -+ method = "smc"; -+ }; -+ }; -+ -+ reserved-memory { -+ #address-cells = <2>; -+ #size-cells = <2>; -+ ranges; -+ -+ optee@30000000 { -+ reg = <0x0 0x30000000 0x0 0x2000000>; -+ no-map; -+ }; - }; - }; - @@ -775,6 +793,9 @@ system-power-controller; From 9ef9f4b42ffb6d8fe426bd6148bd56d2ae00b708 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Feb 2026 21:34:03 +0000 Subject: [PATCH 04/11] chore: update plan for friendlyarm rk806-core patch-in Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com> --- docs/nanopc-T6.md | 3 ++- nix/builders/nanopc-t6/base.nix | 2 +- .../nanopc-t6/rk3588-nanopc-t6.dtsi.patch | 25 +++++++++++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/docs/nanopc-T6.md b/docs/nanopc-T6.md index f141541..aba5e60 100644 --- a/docs/nanopc-T6.md +++ b/docs/nanopc-T6.md @@ -68,7 +68,8 @@ For the current NanoPC-T6 image path, the kernel comes from `nabam/nixos-rockchi **Current approach**: 1. Keep mainline/nabam kernel and align reset configuration with FriendlyARM semantics. -2. Mirror FriendlyARM reset semantics by setting `pmic-reset-func = <1>` in DTS and adding compatibility kernel patches in `rk8xx-core.c` that: +2. Keep OP-TEE `firmware`/`reserved-memory` nodes in DTS patch. +3. Mirror FriendlyARM reset semantics by setting `pmic-reset-func = <1>` in DTS and adding compatibility kernel patches in `rk8xx-core.c` that: - prefer `pmic-reset-func` (fall back to `rockchip,reset-mode`, default `0`), - disable `RK806_SLAVE_RESTART_FUN` (matching FriendlyARM `rk806-core.c` behavior). diff --git a/nix/builders/nanopc-t6/base.nix b/nix/builders/nanopc-t6/base.nix index 49d0611..a2c4211 100644 --- a/nix/builders/nanopc-t6/base.nix +++ b/nix/builders/nanopc-t6/base.nix @@ -72,7 +72,7 @@ let # Use nabam's mainline-based rockchip kernel (linux_latest with rockchip config). # Merge our RK806 options and append compatibility patches directly. - baseKernel = inputs.rockchip.legacyPackages.aarch64-linux.kernel_linux_latest_rockchip_unstable; + baseKernel = inputs.rockchip.legacyPackages.aarch64-linux.kernel_linux_latest_rockchip; customKernel = baseKernel.kernel.override (prev: { structuredExtraConfig = (prev.structuredExtraConfig or {}) // (with lib.kernel; { MFD_RK8XX_SPI = yes; # RK806 PMIC MFD driver via SPI diff --git a/nix/builders/nanopc-t6/rk3588-nanopc-t6.dtsi.patch b/nix/builders/nanopc-t6/rk3588-nanopc-t6.dtsi.patch index 601da7d..35445de 100644 --- a/nix/builders/nanopc-t6/rk3588-nanopc-t6.dtsi.patch +++ b/nix/builders/nanopc-t6/rk3588-nanopc-t6.dtsi.patch @@ -1,5 +1,30 @@ --- a/arch/arm64/boot/dts/rockchip/rk3588-nanopc-t6.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-nanopc-t6.dtsi +@@ -224,6 +224,24 @@ + regulator-min-microvolt = <3300000>; + regulator-name = "vcc3v3_sd_s0"; + vin-supply = <&vcc_3v3_s3>; ++ }; ++ ++ firmware { ++ optee { ++ compatible = "linaro,optee-tz"; ++ method = "smc"; ++ }; ++ }; ++ ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ optee@30000000 { ++ reg = <0x0 0x30000000 0x0 0x2000000>; ++ no-map; ++ }; + }; + }; + @@ -775,6 +793,9 @@ system-power-controller; From 8f1d938bb88a4edd14c967e60c92fcf994e9228c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Feb 2026 21:35:33 +0000 Subject: [PATCH 05/11] fix(nanopc-t6): align mainline rk806 path with friendlyarm rk806-core reset behavior Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com> --- docs/nanopc-T6.md | 4 +--- nix/builders/nanopc-t6/base.nix | 4 ---- nix/builders/nanopc-t6/rk806-friendlyarm-reset-compat.patch | 5 ++--- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/docs/nanopc-T6.md b/docs/nanopc-T6.md index aba5e60..6120ba1 100644 --- a/docs/nanopc-T6.md +++ b/docs/nanopc-T6.md @@ -69,9 +69,7 @@ For the current NanoPC-T6 image path, the kernel comes from `nabam/nixos-rockchi 1. Keep mainline/nabam kernel and align reset configuration with FriendlyARM semantics. 2. Keep OP-TEE `firmware`/`reserved-memory` nodes in DTS patch. -3. Mirror FriendlyARM reset semantics by setting `pmic-reset-func = <1>` in DTS and adding compatibility kernel patches in `rk8xx-core.c` that: - - prefer `pmic-reset-func` (fall back to `rockchip,reset-mode`, default `0`), - - disable `RK806_SLAVE_RESTART_FUN` (matching FriendlyARM `rk806-core.c` behavior). +3. Mirror FriendlyARM reset semantics by setting `pmic-reset-func = <1>` in DTS and patching `rk8xx-core.c` to follow FriendlyARM `rk806-core.c` reset configuration behavior (`pmic-reset-func`, default `0` when missing). ### 3. Mask ROM Button (SARADC) diff --git a/nix/builders/nanopc-t6/base.nix b/nix/builders/nanopc-t6/base.nix index a2c4211..f4958e2 100644 --- a/nix/builders/nanopc-t6/base.nix +++ b/nix/builders/nanopc-t6/base.nix @@ -88,10 +88,6 @@ name = "rk806-friendlyarm-reset-compat.patch"; patch = ./rk806-friendlyarm-reset-compat.patch; } - { - name = "rk806-disable-slave-restart.patch"; - patch = ./rk806-disable-slave-restart.patch; - } ]; }); in diff --git a/nix/builders/nanopc-t6/rk806-friendlyarm-reset-compat.patch b/nix/builders/nanopc-t6/rk806-friendlyarm-reset-compat.patch index 621dc5b..850530f 100644 --- a/nix/builders/nanopc-t6/rk806-friendlyarm-reset-compat.patch +++ b/nix/builders/nanopc-t6/rk806-friendlyarm-reset-compat.patch @@ -1,13 +1,12 @@ --- a/drivers/mfd/rk8xx-core.c +++ b/drivers/mfd/rk8xx-core.c -@@ -716,8 +716,8 @@ int rk8xx_probe(struct device *dev, int variant, unsigned int irq, struct regma +@@ -716,8 +716,7 @@ int rk8xx_probe(struct device *dev, int variant, unsigned int irq, struct regma dual_support = IRQF_SHARED; - ret = device_property_read_u32(dev, "rockchip,reset-mode", &rst_fun); - if (ret) - break; -+ if (device_property_read_u32(dev, "pmic-reset-func", &rst_fun) && -+ device_property_read_u32(dev, "rockchip,reset-mode", &rst_fun)) ++ if (device_property_read_u32(dev, "pmic-reset-func", &rst_fun)) + rst_fun = 0; ret = regmap_update_bits(rk808->regmap, RK806_SYS_CFG3, RK806_RST_FUN_MSK, From 66018300f356f4f6fe0523c6680ab9a818435a3f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Feb 2026 22:25:35 +0000 Subject: [PATCH 06/11] feat(nanopc-t6): patch full friendlyarm rk806-core module into nabam kernel path Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com> --- docs/nanopc-T6.md | 2 +- nix/builders/nanopc-t6/base.nix | 4 +- .../nanopc-t6/rk806-full-module.patch | 1828 +++++++++++++++++ 3 files changed, 1831 insertions(+), 3 deletions(-) create mode 100644 nix/builders/nanopc-t6/rk806-full-module.patch diff --git a/docs/nanopc-T6.md b/docs/nanopc-T6.md index 6120ba1..71635b8 100644 --- a/docs/nanopc-T6.md +++ b/docs/nanopc-T6.md @@ -69,7 +69,7 @@ For the current NanoPC-T6 image path, the kernel comes from `nabam/nixos-rockchi 1. Keep mainline/nabam kernel and align reset configuration with FriendlyARM semantics. 2. Keep OP-TEE `firmware`/`reserved-memory` nodes in DTS patch. -3. Mirror FriendlyARM reset semantics by setting `pmic-reset-func = <1>` in DTS and patching `rk8xx-core.c` to follow FriendlyARM `rk806-core.c` reset configuration behavior (`pmic-reset-func`, default `0` when missing). +3. Patch in the full FriendlyARM `rk806-core.c` module stack on top of the nabam kernel path (including `rk806-core.c`, `rk806.h`, and RK806 SPI wiring), while keeping `pmic-reset-func = <1>` in DTS. ### 3. Mask ROM Button (SARADC) diff --git a/nix/builders/nanopc-t6/base.nix b/nix/builders/nanopc-t6/base.nix index f4958e2..bd8de3d 100644 --- a/nix/builders/nanopc-t6/base.nix +++ b/nix/builders/nanopc-t6/base.nix @@ -85,8 +85,8 @@ patch = ./rk3588-nanopc-t6.dtsi.patch; } { - name = "rk806-friendlyarm-reset-compat.patch"; - patch = ./rk806-friendlyarm-reset-compat.patch; + name = "rk806-full-module.patch"; + patch = ./rk806-full-module.patch; } ]; }); diff --git a/nix/builders/nanopc-t6/rk806-full-module.patch b/nix/builders/nanopc-t6/rk806-full-module.patch new file mode 100644 index 0000000..477be7e --- /dev/null +++ b/nix/builders/nanopc-t6/rk806-full-module.patch @@ -0,0 +1,1828 @@ +--- a/include/linux/mfd/rk806.h ++++ b/include/linux/mfd/rk806.h +@@ -0,0 +1,556 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (c) 2021 Rockchip Electronics Co., Ltd. ++ */ ++ ++#ifndef __LINUX_REGULATOR_RK806_H ++#define __LINUX_REGULATOR_RK806_H ++ ++#include ++#include ++#include ++#include ++ ++#define RK806_POWER_EN0 0x0 ++#define RK806_POWER_EN1 0x1 ++#define RK806_POWER_EN2 0x2 ++#define RK806_POWER_EN3 0x3 ++#define RK806_POWER_EN4 0x4 ++#define RK806_POWER_EN5 0x5 ++#define RK806_POWER_SLP_EN0 0x6 ++#define RK806_POWER_SLP_EN1 0x7 ++#define RK806_POWER_SLP_EN2 0x8 ++#define RK806_POWER_DISCHRG_EN0 0x9 ++#define RK806_POWER_DISCHRG_EN1 0xA ++#define RK806_POWER_DISCHRG_EN2 0xB ++#define RK806_BUCK_FB_CONFIG 0xC ++#define RK806_SLP_LP_CONFIG 0xD ++#define RK806_POWER_FPWM_EN0 0xE ++#define RK806_POWER_FPWM_EN1 0xF ++#define RK806_BUCK1_CONFIG 0x10 ++#define RK806_BUCK2_CONFIG 0x11 ++#define RK806_BUCK3_CONFIG 0x12 ++#define RK806_BUCK4_CONFIG 0x13 ++#define RK806_BUCK5_CONFIG 0x14 ++#define RK806_BUCK6_CONFIG 0x15 ++#define RK806_BUCK7_CONFIG 0x16 ++#define RK806_BUCK8_CONFIG 0x17 ++#define RK806_BUCK9_CONFIG 0x18 ++#define RK806_BUCK10_CONFIG 0x19 ++#define RK806_BUCK1_ON_VSEL 0x1A ++#define RK806_BUCK2_ON_VSEL 0x1B ++#define RK806_BUCK3_ON_VSEL 0x1C ++#define RK806_BUCK4_ON_VSEL 0x1D ++#define RK806_BUCK5_ON_VSEL 0x1E ++#define RK806_BUCK6_ON_VSEL 0x1F ++#define RK806_BUCK7_ON_VSEL 0x20 ++#define RK806_BUCK8_ON_VSEL 0x21 ++#define RK806_BUCK9_ON_VSEL 0x22 ++#define RK806_BUCK10_ON_VSEL 0x23 ++#define RK806_BUCK1_SLP_VSEL 0x24 ++#define RK806_BUCK2_SLP_VSEL 0x25 ++#define RK806_BUCK3_SLP_VSEL 0x26 ++#define RK806_BUCK4_SLP_VSEL 0x27 ++#define RK806_BUCK5_SLP_VSEL 0x28 ++#define RK806_BUCK6_SLP_VSEL 0x29 ++#define RK806_BUCK7_SLP_VSEL 0x2A ++#define RK806_BUCK8_SLP_VSEL 0x2B ++#define RK806_BUCK9_SLP_VSEL 0x2D ++#define RK806_BUCK10_SLP_VSEL 0x2E ++#define RK806_BUCK_DEBUG1 0x30 ++#define RK806_BUCK_DEBUG2 0x31 ++#define RK806_BUCK_DEBUG3 0x32 ++#define RK806_BUCK_DEBUG4 0x33 ++#define RK806_BUCK_DEBUG5 0x34 ++#define RK806_BUCK_DEBUG6 0x35 ++#define RK806_BUCK_DEBUG7 0x36 ++#define RK806_BUCK_DEBUG8 0x37 ++#define RK806_BUCK_DEBUG9 0x38 ++#define RK806_BUCK_DEBUG10 0x39 ++#define RK806_BUCK_DEBUG11 0x3A ++#define RK806_BUCK_DEBUG12 0x3B ++#define RK806_BUCK_DEBUG13 0x3C ++#define RK806_BUCK_DEBUG14 0x3D ++#define RK806_BUCK_DEBUG15 0x3E ++#define RK806_BUCK_DEBUG16 0x3F ++#define RK806_BUCK_DEBUG17 0x40 ++#define RK806_BUCK_DEBUG18 0x41 ++#define RK806_NLDO_IMAX 0x42 ++#define RK806_NLDO1_ON_VSEL 0x43 ++#define RK806_NLDO2_ON_VSEL 0x44 ++#define RK806_NLDO3_ON_VSEL 0x45 ++#define RK806_NLDO4_ON_VSEL 0x46 ++#define RK806_NLDO5_ON_VSEL 0x47 ++#define RK806_NLDO1_SLP_VSEL 0x48 ++#define RK806_NLDO2_SLP_VSEL 0x49 ++#define RK806_NLDO3_SLP_VSEL 0x4A ++#define RK806_NLDO4_SLP_VSEL 0x4B ++#define RK806_NLDO5_SLP_VSEL 0x4C ++#define RK806_PLDO_IMAX 0x4D ++#define RK806_PLDO1_ON_VSEL 0x4E ++#define RK806_PLDO2_ON_VSEL 0x4F ++#define RK806_PLDO3_ON_VSEL 0x50 ++#define RK806_PLDO4_ON_VSEL 0x51 ++#define RK806_PLDO5_ON_VSEL 0x52 ++#define RK806_PLDO6_ON_VSEL 0x53 ++#define RK806_PLDO1_SLP_VSEL 0x54 ++#define RK806_PLDO2_SLP_VSEL 0x55 ++#define RK806_PLDO3_SLP_VSEL 0x56 ++#define RK806_PLDO4_SLP_VSEL 0x57 ++#define RK806_PLDO5_SLP_VSEL 0x58 ++#define RK806_PLDO6_SLP_VSEL 0x59 ++#define RK806_CHIP_NAME 0x5A ++#define RK806_CHIP_VER 0x5B ++#define RK806_OTP_VER 0x5C ++#define RK806_SYS_STS 0x5D ++#define RK806_SYS_CFG0 0x5E ++#define RK806_SYS_CFG1 0x5F ++#define RK806_SYS_OPTION 0x61 ++#define RK806_SLEEP_CONFIG0 0x62 ++#define RK806_SLEEP_CONFIG1 0x63 ++#define RK806_SLEEP_CTR_SEL0 0x64 ++#define RK806_SLEEP_CTR_SEL1 0x65 ++#define RK806_SLEEP_CTR_SEL2 0x66 ++#define RK806_SLEEP_CTR_SEL3 0x67 ++#define RK806_SLEEP_CTR_SEL4 0x68 ++#define RK806_SLEEP_CTR_SEL5 0x69 ++#define RK806_DVS_CTRL_SEL0 0x6A ++#define RK806_DVS_CTRL_SEL1 0x6B ++#define RK806_DVS_CTRL_SEL2 0x6C ++#define RK806_DVS_CTRL_SEL3 0x6D ++#define RK806_DVS_CTRL_SEL4 0x6E ++#define RK806_DVS_CTRL_SEL5 0x6F ++#define RK806_DVS_START_CTRL 0x70 ++#define RK806_SLEEP_GPIO 0x71 ++#define RK806_SYS_CFG3 0x72 ++#define RK806_ON_SOURCE 0x74 ++#define RK806_OFF_SOURCE 0x75 ++#define RK806_PWRON_KEY 0x76 ++#define RK806_INT_STS0 0x77 ++#define RK806_INT_MSK0 0x78 ++#define RK806_INT_STS1 0x79 ++#define RK806_INT_MSK1 0x7A ++#define RK806_GPIO_INT_CONFIG 0x7B ++#define RK806_DATA_REG0 0x7C ++#define RK806_DATA_REG1 0x7D ++#define RK806_DATA_REG2 0x7E ++#define RK806_DATA_REG3 0x7F ++#define RK806_DATA_REG4 0x80 ++#define RK806_DATA_REG5 0x81 ++#define RK806_DATA_REG6 0x82 ++#define RK806_DATA_REG7 0x83 ++#define RK806_DATA_REG8 0x84 ++#define RK806_DATA_REG9 0x85 ++#define RK806_DATA_REG10 0x86 ++#define RK806_DATA_REG11 0x87 ++#define RK806_DATA_REG12 0x88 ++#define RK806_DATA_REG13 0x89 ++#define RK806_DATA_REG14 0x8A ++#define RK806_DATA_REG15 0x8B ++#define RK806_TM_REG 0x8C ++#define RK806_OTP_EN_REG 0x8D ++#define RK806_FUNC_OTP_EN_REG 0x8E ++#define RK806_TEST_REG1 0x8F ++#define RK806_TEST_REG2 0x90 ++#define RK806_TEST_REG3 0x91 ++#define RK806_TEST_REG4 0x92 ++#define RK806_TEST_REG5 0x93 ++#define RK806_BUCK_VSEL_OTP_REG0 0x94 ++#define RK806_BUCK_VSEL_OTP_REG1 0x95 ++#define RK806_BUCK_VSEL_OTP_REG2 0x96 ++#define RK806_BUCK_VSEL_OTP_REG3 0x97 ++#define RK806_BUCK_VSEL_OTP_REG4 0x98 ++#define RK806_BUCK_VSEL_OTP_REG5 0x99 ++#define RK806_BUCK_VSEL_OTP_REG6 0x9A ++#define RK806_BUCK_VSEL_OTP_REG7 0x9B ++#define RK806_BUCK_VSEL_OTP_REG8 0x9C ++#define RK806_BUCK_VSEL_OTP_REG9 0x9D ++#define RK806_NLDO1_VSEL_OTP_REG0 0x9E ++#define RK806_NLDO1_VSEL_OTP_REG1 0x9F ++#define RK806_NLDO1_VSEL_OTP_REG2 0xA0 ++#define RK806_NLDO1_VSEL_OTP_REG3 0xA1 ++#define RK806_NLDO1_VSEL_OTP_REG4 0xA2 ++#define RK806_PLDO_VSEL_OTP_REG0 0xA3 ++#define RK806_PLDO_VSEL_OTP_REG1 0xA4 ++#define RK806_PLDO_VSEL_OTP_REG2 0xA5 ++#define RK806_PLDO_VSEL_OTP_REG3 0xA6 ++#define RK806_PLDO_VSEL_OTP_REG4 0xA7 ++#define RK806_PLDO_VSEL_OTP_REG5 0xA8 ++#define RK806_BUCK_EN_OTP_REG1 0xA9 ++#define RK806_NLDO_EN_OTP_REG1 0xAA ++#define RK806_PLDO_EN_OTP_REG1 0xAB ++#define RK806_BUCK_FB_RES_OTP_REG1 0xAC ++#define RK806_OTP_RESEV_REG0 0xAD ++#define RK806_OTP_RESEV_REG1 0xAE ++#define RK806_OTP_RESEV_REG2 0xAF ++#define RK806_OTP_RESEV_REG3 0xB0 ++#define RK806_OTP_RESEV_REG4 0xB1 ++#define RK806_BUCK_SEQ_REG0 0xB2 ++#define RK806_BUCK_SEQ_REG1 0xB3 ++#define RK806_BUCK_SEQ_REG2 0xB4 ++#define RK806_BUCK_SEQ_REG3 0xB5 ++#define RK806_BUCK_SEQ_REG4 0xB6 ++#define RK806_BUCK_SEQ_REG5 0xB7 ++#define RK806_BUCK_SEQ_REG6 0xB8 ++#define RK806_BUCK_SEQ_REG7 0xB9 ++#define RK806_BUCK_SEQ_REG8 0xBA ++#define RK806_BUCK_SEQ_REG9 0xBB ++#define RK806_BUCK_SEQ_REG10 0xBC ++#define RK806_BUCK_SEQ_REG11 0xBD ++#define RK806_BUCK_SEQ_REG12 0xBE ++#define RK806_BUCK_SEQ_REG13 0xBF ++#define RK806_BUCK_SEQ_REG14 0xC0 ++#define RK806_BUCK_SEQ_REG15 0xC1 ++#define RK806_BUCK_SEQ_REG16 0xC2 ++#define RK806_BUCK_SEQ_REG17 0xC3 ++#define RK806_HK_TRIM_REG1 0xC4 ++#define RK806_HK_TRIM_REG2 0xC5 ++#define RK806_BUCK_REF_TRIM_REG1 0xC6 ++#define RK806_BUCK_REF_TRIM_REG2 0xC7 ++#define RK806_BUCK_REF_TRIM_REG3 0xC8 ++#define RK806_BUCK_REF_TRIM_REG4 0xC9 ++#define RK806_BUCK_REF_TRIM_REG5 0xCA ++#define RK806_BUCK_OSC_TRIM_REG1 0xCB ++#define RK806_BUCK_OSC_TRIM_REG2 0xCC ++#define RK806_BUCK_OSC_TRIM_REG3 0xCD ++#define RK806_BUCK_OSC_TRIM_REG4 0xCE ++#define RK806_BUCK_OSC_TRIM_REG5 0xCF ++#define RK806_BUCK_TRIM_ZCDIOS_REG1 0xD0 ++#define RK806_BUCK_TRIM_ZCDIOS_REG2 0xD1 ++#define RK806_NLDO_TRIM_REG1 0xD2 ++#define RK806_NLDO_TRIM_REG2 0xD3 ++#define RK806_NLDO_TRIM_REG3 0xD4 ++#define RK806_PLDO_TRIM_REG1 0xD5 ++#define RK806_PLDO_TRIM_REG2 0xD6 ++#define RK806_PLDO_TRIM_REG3 0xD7 ++#define RK806_TRIM_ICOMP_REG1 0xD8 ++#define RK806_TRIM_ICOMP_REG2 0xD9 ++#define RK806_EFUSE_CONTROL_REGH 0xDA ++#define RK806_FUSE_PROG_REG 0xDB ++#define RK806_MAIN_FSM_STS_REG 0xDD ++#define RK806_FSM_REG 0xDE ++#define RK806_TOP_RESEV_OFFR 0xEC ++#define RK806_TOP_RESEV_POR 0xED ++#define RK806_BUCK_VRSN_REG1 0xEE ++#define RK806_BUCK_VRSN_REG2 0xEF ++#define RK806_NLDO_RLOAD_SEL_REG1 0xF0 ++#define RK806_PLDO_RLOAD_SEL_REG1 0xF1 ++#define RK806_PLDO_RLOAD_SEL_REG2 0xF2 ++#define RK806_BUCK_CMIN_MX_REG1 0xF3 ++#define RK806_BUCK_CMIN_MX_REG2 0xF4 ++#define RK806_BUCK_FREQ_SET_REG1 0xF5 ++#define RK806_BUCK_FREQ_SET_REG2 0xF6 ++#define RK806_BUCK_RS_MEABS_REG1 0xF7 ++#define RK806_BUCK_RS_MEABS_REG2 0xF8 ++#define RK806_BUCK_RS_ZDLEB_REG1 0xF9 ++#define RK806_BUCK_RS_ZDLEB_REG2 0xFA ++#define RK806_BUCK_RSERVE_REG1 0xFB ++#define RK806_BUCK_RSERVE_REG2 0xFC ++#define RK806_BUCK_RSERVE_REG3 0xFD ++#define RK806_BUCK_RSERVE_REG4 0xFE ++#define RK806_BUCK_RSERVE_REG5 0xFF ++ ++/* INT_STS Register field definitions */ ++#define RK806_INT_STS_PWRON_FALL BIT(0) ++#define RK806_INT_STS_PWRON_RISE BIT(1) ++#define RK806_INT_STS_PWRON BIT(2) ++#define RK806_INT_STS_PWRON_LP BIT(3) ++#define RK806_INT_STS_HOTDIE BIT(4) ++#define RK806_INT_STS_VDC_RISE BIT(5) ++#define RK806_INT_STS_VDC_FALL BIT(6) ++#define RK806_INT_STS_VB_LO BIT(7) ++#define RK806_INT_STS_REV0 BIT(0) ++#define RK806_INT_STS_REV1 BIT(1) ++#define RK806_INT_STS_REV2 BIT(2) ++#define RK806_INT_STS_CRC_ERROR BIT(3) ++#define RK806_INT_STS_SLP3_GPIO BIT(4) ++#define RK806_INT_STS_SLP2_GPIO BIT(5) ++#define RK806_INT_STS_SLP1_GPIO BIT(6) ++#define RK806_INT_STS_WDT BIT(7) ++ ++/* spi command */ ++#define RK806_CMD_READ 0 ++#define RK806_CMD_WRITE BIT(7) ++#define RK806_CMD_CRC_EN BIT(6) ++#define RK806_CMD_CRC_DIS 0 ++#define RK806_CMD_LEN_MSK 0x0f ++#define RK806_REG_H 0x00 ++ ++#define VERSION_AB 0x01 ++ ++#define RK806_READ_BUFFER_SIZE 32 ++ ++enum rk806_reg_id { ++ RK806_ID_DCDC1 = 0, ++ RK806_ID_DCDC2, ++ RK806_ID_DCDC3, ++ RK806_ID_DCDC4, ++ RK806_ID_DCDC5, ++ RK806_ID_DCDC6, ++ RK806_ID_DCDC7, ++ RK806_ID_DCDC8, ++ RK806_ID_DCDC9, ++ RK806_ID_DCDC10, ++ ++ RK806_ID_NLDO1, ++ RK806_ID_NLDO2, ++ RK806_ID_NLDO3, ++ RK806_ID_NLDO4, ++ RK806_ID_NLDO5, ++ ++ RK806_ID_PLDO1, ++ RK806_ID_PLDO2, ++ RK806_ID_PLDO3, ++ RK806_ID_PLDO4, ++ RK806_ID_PLDO5, ++ RK806_ID_PLDO6, ++ RK806_ID_END, ++}; ++ ++/* Define the rk806 IRQ numbers */ ++enum rk806_irqs { ++ /* INT_STS0 registers */ ++ RK806_IRQ_PWRON_FALL, ++ RK806_IRQ_PWRON_RISE, ++ RK806_IRQ_PWRON, ++ RK806_IRQ_PWRON_LP, ++ RK806_IRQ_HOTDIE, ++ RK806_IRQ_VDC_RISE, ++ RK806_IRQ_VDC_FALL, ++ RK806_IRQ_VB_LO, ++ ++ /* INT_STS0 registers */ ++ RK806_IRQ_REV0, ++ RK806_IRQ_REV1, ++ RK806_IRQ_REV2, ++ RK806_IRQ_CRC_ERROR, ++ RK806_IRQ_SLP3_GPIO, ++ RK806_IRQ_SLP2_GPIO, ++ RK806_IRQ_SLP1_GPIO, ++ RK806_IRQ_WDT, ++}; ++ ++/* VCC1 low voltage threshold */ ++enum rk806_lv_sel { ++ VB_LO_SEL_2800, ++ VB_LO_SEL_2900, ++ VB_LO_SEL_3000, ++ VB_LO_SEL_3100, ++ VB_LO_SEL_3200, ++ VB_LO_SEL_3300, ++ VB_LO_SEL_3400, ++ VB_LO_SEL_3500, ++}; ++ ++/* system shut down voltage select */ ++enum rk806_uv_sel { ++ VB_UV_SEL_2700, ++ VB_UV_SEL_2800, ++ VB_UV_SEL_2900, ++ VB_UV_SEL_3000, ++ VB_UV_SEL_3100, ++ VB_UV_SEL_3200, ++ VB_UV_SEL_3300, ++ VB_UV_SEL_3400, ++}; ++ ++/* pin function */ ++enum rk806_pwrctrl_fun { ++ PWRCTRL_NULL_FUN, ++ PWRCTRL_SLP_FUN, ++ PWRCTRL_POWOFF_FUN, ++ PWRCTRL_RST_FUN, ++ PWRCTRL_DVS_FUN, ++ PWRCTRL_GPIO_FUN, ++}; ++ ++/* pin pol */ ++enum rk806_pin_level { ++ POL_LOW, ++ POL_HIGH, ++}; ++ ++enum rk806_vsel_ctr_sel { ++ CTR_BY_NO_EFFECT, ++ CTR_BY_PWRCTRL1, ++ CTR_BY_PWRCTRL2, ++ CTR_BY_PWRCTRL3, ++}; ++ ++enum rk806_dvs_ctr_sel { ++ CTR_SEL_NO_EFFECT, ++ CTR_SEL_DVS_START1, ++ CTR_SEL_DVS_START2, ++ CTR_SEL_DVS_START3, ++}; ++ ++enum rk806_pin_dr_sel { ++ RK806_PIN_INPUT, ++ RK806_PIN_OUTPUT, ++}; ++ ++enum rk806_int_pol { ++ RK806_INT_POL_LOW, ++ RK806_INT_POL_HIGH, ++}; ++ ++enum rk806_int_fun { ++ RK806_INT_ONLY, ++ RK806_INT_ADN_WKUP, ++}; ++ ++enum rk806_dvs_mode { ++ RK806_DVS_NOT_SUPPORT, ++ RK806_DVS_START1, ++ RK806_DVS_START2, ++ RK806_DVS_START3, ++ RK806_DVS_PWRCTRL1, ++ RK806_DVS_PWRCTRL2, ++ RK806_DVS_PWRCTRL3, ++ RK806_DVS_START_PWRCTR1, ++ RK806_DVS_START_PWRCTR2, ++ RK806_DVS_START_PWRCTR3, ++ RK806_DVS_END, ++}; ++ ++enum rk806_fields { ++ CHIP_NAME_H, CHIP_NAME_L, CHIP_VER, OTP_VER, ++ POWER_EN0, POWER_EN1, POWER_EN2, POWER_EN3, POWER_EN4, POWER_EN5, ++ BUCK4_EN_MASK, BUCK3_EN_MASK, BUCK2_EN_MASK, BUCK1_EN_MASK, ++ BUCK4_EN, BUCK3_EN, BUCK2_EN, BUCK1_EN, ++ BUCK8_EN_MASK, BUCK7_EN_MASK, BUCK6_EN_MASK, BUCK5_EN_MASK, ++ BUCK8_EN, BUCK7_EN, BUCK6_EN, BUCK5_EN, ++ BUCK10_EN_MASK, BUCK9_EN_MASK, BUCK10_EN, BUCK9_EN, ++ NLDO4_EN_MASK, NLDO3_EN_MASK, NLDO2_EN_MASK, NLDO1_EN_MASK, ++ NLDO4_EN, NLDO3_EN, NLDO2_EN, NLDO1_EN, ++ PLDO4_EN_MASK, PLDO3_EN_MASK, PLDO2_EN_MASK, PLDO1_EN_MASK, ++ PLDO4_EN, PLDO3_EN, PLDO2_EN, PLDO1_EN, ++ NLDO5_EN_MASK, PLDO6_EN_MASK, PLDO5_EN_MASK, ++ NLDO5_EN, PLDO6_EN, PLDO5_EN, ++ BUCK8_SLP_EN, BUCK7_SLP_EN, BUCK6_SLP_EN, BUCK5_SLP_EN, BUCK4_SLP_EN, ++ BUCK3_SLP_EN, BUCK2_SLP_EN, BUCK1_SLP_EN, ++ BUCK10_SLP_EN, BUCK9_SLP_EN, NLDO5_SLP_EN, NLDO4_SLP_EN, NLDO3_SLP_EN, ++ NLDO2_SLP_EN, NLDO1_SLP_EN, ++ PLDO6_SLP_EN, PLDO5_SLP_EN, PLDO4_SLP_EN, PLDO3_SLP_EN, ++ PLDO2_SLP_EN, PLDO1_SLP_EN, ++ BUCK1_ON_VSEL, BUCK2_ON_VSEL, BUCK3_ON_VSEL, BUCK4_ON_VSEL, BUCK5_ON_VSEL, ++ BUCK6_ON_VSEL, BUCK7_ON_VSEL, BUCK8_ON_VSEL, BUCK9_ON_VSEL, BUCK10_ON_VSEL, ++ BUCK1_SLP_VSEL, BUCK2_SLP_VSEL, BUCK3_SLP_VSEL, BUCK4_SLP_VSEL, BUCK5_SLP_VSEL, ++ BUCK6_SLP_VSEL, BUCK7_SLP_VSEL, BUCK8_SLP_VSEL, BUCK9_SLP_VSEL, BUCK10_SLP_VSEL, ++ NLDO1_ON_VSEL, NLDO2_ON_VSEL, NLDO3_ON_VSEL, NLDO4_ON_VSEL, NLDO5_ON_VSEL, ++ NLDO1_SLP_VSEL, NLDO2_SLP_VSEL, NLDO3_SLP_VSEL, NLDO4_SLP_VSEL, NLDO5_SLP_VSEL, ++ PLDO1_ON_VSEL, PLDO2_ON_VSEL, PLDO3_ON_VSEL, PLDO4_ON_VSEL, PLDO5_ON_VSEL, ++ PLDO6_ON_VSEL, ++ PLDO1_SLP_VSEL, PLDO2_SLP_VSEL, PLDO3_SLP_VSEL, PLDO4_SLP_VSEL, PLDO5_SLP_VSEL, ++ PLDO6_SLP_VSEL, ++ BUCK1_RATE, BUCK2_RATE, BUCK3_RATE, BUCK4_RATE, BUCK5_RATE, BUCK6_RATE, ++ BUCK7_RATE, BUCK8_RATE, BUCK9_RATE, BUCK10_RATE, ++ PWRON_STS, VDC_STS, VB_UV_STSS, VB_LO_STS, HOTDIE_STS, TSD_STS, VB_OV_STS, ++ VB_UV_DLY, VB_UV_SEL, VB_LO_ACT, VB_LO_SEL, ++ ABNORDET_EN, TSD_TEMP, HOTDIE_TMP, SYS_OV_SD_EN, SYS_OV_SD_DLY_SEL, DLY_ABN_SHORT, ++ VCCXDET_DIS, OSC_TC, ENB2_2M, ENB_32K, ++ PWRCTRL1_FUN, PWRCTRL2_FUN, PWRCTRL3_FUN, ++ PWRCTRL1_POL, PWRCTRL2_POL, PWRCTRL3_POL, ++ BUCK1_VSEL_CTR_SEL, BUCK2_VSEL_CTR_SEL, BUCK3_VSEL_CTR_SEL, BUCK4_VSEL_CTR_SEL, ++ BUCK5_VSEL_CTR_SEL, BUCK6_VSEL_CTR_SEL, BUCK7_VSEL_CTR_SEL, BUCK8_VSEL_CTR_SEL, ++ BUCK9_VSEL_CTR_SEL, BUCK10_VSEL_CTR_SEL, ++ NLDO1_VSEL_CTR_SEL, NLDO2_VSEL_CTR_SEL, NLDO3_VSEL_CTR_SEL, NLDO4_VSEL_CTR_SEL, ++ NLDO5_VSEL_CTR_SEL, ++ PLDO1_VSEL_CTR_SEL, PLDO2_VSEL_CTR_SEL, PLDO3_VSEL_CTR_SEL, PLDO4_VSEL_CTR_SEL, ++ PLDO5_VSEL_CTR_SEL, PLDO6_VSEL_CTR_SEL, ++ BUCK1_DVS_CTR_SEL, BUCK2_DVS_CTR_SEL, BUCK3_DVS_CTR_SEL, BUCK4_DVS_CTR_SEL, ++ BUCK5_DVS_CTR_SEL, BUCK6_DVS_CTR_SEL, BUCK7_DVS_CTR_SEL, BUCK8_DVS_CTR_SEL, ++ BUCK9_DVS_CTR_SEL, BUCK10_DVS_CTR_SEL, ++ NLDO1_DVS_CTR_SEL, NLDO2_DVS_CTR_SEL, NLDO3_DVS_CTR_SEL, NLDO4_DVS_CTR_SEL, ++ NLDO5_DVS_CTR_SEL, ++ PLDO1_DVS_CTR_SEL, PLDO2_DVS_CTR_SEL, PLDO3_DVS_CTR_SEL, PLDO4_DVS_CTR_SEL, ++ PLDO5_DVS_CTR_SEL, PLDO6_DVS_CTR_SEL, ++ DVS_START1, DVS_START2, DVS_START3, ++ SLP3_DATA, SLP2_DATA, SLP1_DATA, SLP3_DR, SLP2_DR, SLP1_DR, ++ ++ RST_FUN, DEV_RST, DEV_SLP, SLAVE_RESTART_FUN, DEV_OFF, ++ WDT_CLR, WDT_EN, WDT_SET, ON_SOURCE, OFF_SOURCE, ++ ON_PWRON, ON_VDC, RESTART_RESETB, RESTART_PWRON_LP, RESTART_SLP, ++ RESTART_DEV_RST, RESTART_WDT, ++ OFF_SLP, VB_SYS_OV, OFF_TSD, OFF_DEV_OFF, OFF_PWRON_LP, OFF_VB_LO, ++ PWRON_ON_TIME, PWRON_LP_ACT, PWRON_LP_OFF_TIME, PWRON_LP_TM_SEL, PWRON_DB_SEL, ++ VB_LO_INT, VDC_FALL_INT, VDC_RISE_INT, HOTDIE_INT, PWRON_LP_INT, PWRON_INT, ++ PWRON_RISE_INT, PWRON_FALL_INT, ++ VB_LO_IM, VDC_FALL_INT_IM, VDC_RISE_IM, HOTDIE_IM, PWRON_LP_IM, ++ PWRON_IM, PWRON_RISE_INT_IM, PWRON_FALL_INT_IM, ++ WDT_INT, SLP1_GPIO_INT, SLP2_GPIO_INT, SLP3_GPIO_INT, ++ WDT_INT_IM, SLP1_GPIO_IM, SLP2_GPIO_IM, SLP3_GPIO_IM, ++ INT_FUNCTION, INT_POL, INT_FC_EN, ++ LDO_RATE, BUCK1_RATE2, BUCK2_RATE2, BUCK3_RATE2, BUCK4_RATE2, ++ BUCK5_RATE2, BUCK6_RATE2, BUCK7_RATE2, BUCK8_RATE2, BUCK9_RATE2, ++ BUCK10_RATE2, ++ ++ BUCK1_SEQ, BUCK2_SEQ, BUCK3_SEQ, BUCK4_SEQ, BUCK5_SEQ, ++ BUCK6_SEQ, BUCK7_SEQ, BUCK8_SEQ, BUCK9_SEQ, BUCK10_SEQ, ++ PLDO1_01_SEQ, PLDO1_23_SEQ, PLDO1_45_SEQ, ++ PLDO2_01_SEQ, PLDO2_23_SEQ, PLDO2_45_SEQ, ++ PLDO3_01_SEQ, PLDO3_23_SEQ, PLDO3_45_SEQ, ++ PLDO4_SEQ, PLDO5_SEQ, ++ PLDO6_01_SEQ, PLDO6_23_SEQ, PLDO6_45_SEQ, ++ NLDO1_SEQ, NLDO2_SEQ, NLDO3_SEQ, NLDO4_SEQ, NLDO5_SEQ, ++ ++ F_MAX_FIELDS ++}; ++ ++struct rk806_platform_data { ++ int low_voltage_threshold; ++ int shutdown_voltage_threshold; ++ int force_shutdown_enable; ++ int shutdown_temperture_threshold; ++ int hotdie_temperture_threshold; ++ int vdc_wakeup_enable; ++ ++ int shutown_by_pwrctrln; ++ int *shutdown_sequence; ++ int *vb_shutdown_sequence; ++ int *dvs_control_suspend; ++ ++ int support_shutdown_sequence; ++ int support_vb_sequence; ++ int support_dvs_control_suspend; ++}; ++ ++struct rk806_pin_info { ++ struct pinctrl *p; ++ struct pinctrl_state *default_st; ++ struct pinctrl_state *power_off; ++ struct pinctrl_state *reset; ++ struct pinctrl_state *sleep; ++ struct pinctrl_state *dvs; ++}; ++ ++/* ++ * struct rk806 - state holder for the rk806 driver ++ * ++ * Device data may be used to access the rk806 chip ++ */ ++struct rk806 { ++ struct device *dev; ++ struct regmap *regmap; ++ struct regmap_field *rmap_fields[F_MAX_FIELDS]; ++ /* IRQ Data */ ++ int irq; ++ struct regmap_irq_chip_data *irq_data; ++ struct rk806_platform_data *pdata; ++ struct rk806_pin_info *pins; ++ u8 read_buf[RK806_READ_BUFFER_SIZE]; ++}; ++ ++extern const struct regmap_config rk806_regmap_config; ++extern const struct of_device_id rk806_of_match[]; ++int rk806_device_init(struct rk806 *rk806); ++int rk806_device_exit(struct rk806 *rk806); ++int rk806_core_suspend(struct device *dev); ++int rk806_core_resume(struct device *dev); ++int rk806_field_write(struct rk806 *rk806, ++ enum rk806_fields field_id, ++ unsigned int val); ++int rk806_field_read(struct rk806 *rk806, ++ enum rk806_fields field_id); ++void rk806_shutdown_requence_config(struct rk806 *rk806); ++#endif /* __LINUX_REGULATOR_RK806_H */ +--- a/drivers/mfd/rk806-core.c ++++ b/drivers/mfd/rk806-core.c +@@ -0,0 +1,1031 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * MFD core driver for Rockchip RK806 ++ * ++ * Copyright (c) 2021 Rockchip Electronics Co., Ltd. ++ * ++ * Author: Xu Shengfei ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define TSD_TEMP_140 0x00 ++#define TSD_TEMP_160 0x01 ++#define VB_LO_ACT_SD 0x00 ++#define VB_LO_ACT_INT 0x01 ++#define VB_LO_3400MV 3400 ++ ++static const struct reg_field rk806_reg_fields[] = { ++ [POWER_EN0] = REG_FIELD(0x00, 0, 7), ++ [POWER_EN1] = REG_FIELD(0x01, 0, 7), ++ [POWER_EN2] = REG_FIELD(0x02, 0, 7), ++ [POWER_EN3] = REG_FIELD(0x03, 0, 7), ++ [POWER_EN4] = REG_FIELD(0x04, 0, 7), ++ [POWER_EN5] = REG_FIELD(0x05, 0, 7), ++ ++ [BUCK4_EN_MASK] = REG_FIELD(0x00, 7, 7), ++ [BUCK3_EN_MASK] = REG_FIELD(0x00, 6, 6), ++ [BUCK2_EN_MASK] = REG_FIELD(0x00, 5, 5), ++ [BUCK1_EN_MASK] = REG_FIELD(0x00, 4, 4), ++ [BUCK4_EN] = REG_FIELD(0X00, 3, 3), ++ [BUCK3_EN] = REG_FIELD(0X00, 2, 2), ++ [BUCK2_EN] = REG_FIELD(0X00, 1, 1), ++ [BUCK1_EN] = REG_FIELD(0X00, 0, 0), ++ [BUCK8_EN_MASK] = REG_FIELD(0x01, 7, 7), ++ [BUCK7_EN_MASK] = REG_FIELD(0x01, 6, 6), ++ [BUCK6_EN_MASK] = REG_FIELD(0x01, 5, 5), ++ [BUCK5_EN_MASK] = REG_FIELD(0x01, 4, 4), ++ [BUCK8_EN] = REG_FIELD(0x01, 3, 3), ++ [BUCK7_EN] = REG_FIELD(0x01, 2, 2), ++ [BUCK6_EN] = REG_FIELD(0x01, 1, 1), ++ [BUCK5_EN] = REG_FIELD(0x01, 0, 0), ++ [BUCK10_EN_MASK] = REG_FIELD(0x02, 5, 5), ++ [BUCK9_EN_MASK] = REG_FIELD(0x02, 4, 4), ++ [BUCK10_EN] = REG_FIELD(0x02, 1, 1), ++ [BUCK9_EN] = REG_FIELD(0x02, 0, 0), ++ [NLDO4_EN_MASK] = REG_FIELD(0x03, 7, 7), ++ [NLDO3_EN_MASK] = REG_FIELD(0x03, 6, 6), ++ [NLDO2_EN_MASK] = REG_FIELD(0x03, 5, 5), ++ [NLDO1_EN_MASK] = REG_FIELD(0x03, 4, 4), ++ [NLDO4_EN] = REG_FIELD(0x03, 3, 3), ++ [NLDO3_EN] = REG_FIELD(0x03, 2, 2), ++ [NLDO2_EN] = REG_FIELD(0x03, 1, 1), ++ [NLDO1_EN] = REG_FIELD(0x03, 0, 0), ++ ++ [PLDO3_EN_MASK] = REG_FIELD(0x04, 7, 7), ++ [PLDO2_EN_MASK] = REG_FIELD(0x04, 6, 6), ++ [PLDO1_EN_MASK] = REG_FIELD(0x04, 5, 5), ++ [PLDO6_EN_MASK] = REG_FIELD(0x04, 4, 4), ++ [PLDO3_EN] = REG_FIELD(0x04, 3, 3), ++ [PLDO2_EN] = REG_FIELD(0x04, 2, 2), ++ [PLDO1_EN] = REG_FIELD(0x04, 1, 1), ++ [PLDO6_EN] = REG_FIELD(0x04, 0, 0), ++ ++ [NLDO5_EN_MASK] = REG_FIELD(0x05, 6, 6), ++ [PLDO5_EN_MASK] = REG_FIELD(0x05, 5, 5), ++ [PLDO4_EN_MASK] = REG_FIELD(0x05, 4, 4), ++ [NLDO5_EN] = REG_FIELD(0x05, 2, 2), ++ [PLDO5_EN] = REG_FIELD(0x05, 1, 1), ++ [PLDO4_EN] = REG_FIELD(0x05, 0, 0), ++ ++ [BUCK8_SLP_EN] = REG_FIELD(0x06, 7, 7), ++ [BUCK7_SLP_EN] = REG_FIELD(0x06, 6, 6), ++ [BUCK6_SLP_EN] = REG_FIELD(0x06, 5, 5), ++ [BUCK5_SLP_EN] = REG_FIELD(0x06, 4, 4), ++ [BUCK4_SLP_EN] = REG_FIELD(0x06, 3, 3), ++ [BUCK3_SLP_EN] = REG_FIELD(0x06, 2, 2), ++ [BUCK2_SLP_EN] = REG_FIELD(0x06, 1, 1), ++ [BUCK1_SLP_EN] = REG_FIELD(0x06, 0, 0), ++ ++ [BUCK10_SLP_EN] = REG_FIELD(0x07, 7, 7), ++ [BUCK9_SLP_EN] = REG_FIELD(0x07, 6, 6), ++ [NLDO5_SLP_EN] = REG_FIELD(0x07, 4, 4), ++ [NLDO4_SLP_EN] = REG_FIELD(0x07, 3, 3), ++ [NLDO3_SLP_EN] = REG_FIELD(0x07, 2, 2), ++ [NLDO2_SLP_EN] = REG_FIELD(0x07, 1, 1), ++ [NLDO1_SLP_EN] = REG_FIELD(0x07, 0, 0), ++ ++ [PLDO5_SLP_EN] = REG_FIELD(0x08, 5, 5), ++ [PLDO4_SLP_EN] = REG_FIELD(0x08, 4, 4), ++ [PLDO3_SLP_EN] = REG_FIELD(0x08, 3, 3), ++ [PLDO2_SLP_EN] = REG_FIELD(0x08, 2, 2), ++ [PLDO1_SLP_EN] = REG_FIELD(0x08, 1, 1), ++ [PLDO6_SLP_EN] = REG_FIELD(0x08, 0, 0), ++ ++ [BUCK1_RATE] = REG_FIELD(0x10, 6, 7), ++ [BUCK2_RATE] = REG_FIELD(0x11, 6, 7), ++ [BUCK3_RATE] = REG_FIELD(0x12, 6, 7), ++ [BUCK4_RATE] = REG_FIELD(0x13, 6, 7), ++ [BUCK5_RATE] = REG_FIELD(0x14, 6, 7), ++ [BUCK6_RATE] = REG_FIELD(0x15, 6, 7), ++ [BUCK7_RATE] = REG_FIELD(0x16, 6, 7), ++ [BUCK8_RATE] = REG_FIELD(0x17, 6, 7), ++ [BUCK9_RATE] = REG_FIELD(0x18, 6, 7), ++ [BUCK10_RATE] = REG_FIELD(0x19, 6, 7), ++ ++ [BUCK1_ON_VSEL] = REG_FIELD(0x1A, 0, 7), ++ [BUCK2_ON_VSEL] = REG_FIELD(0x1B, 0, 7), ++ [BUCK3_ON_VSEL] = REG_FIELD(0x1C, 0, 7), ++ [BUCK4_ON_VSEL] = REG_FIELD(0x1D, 0, 7), ++ [BUCK5_ON_VSEL] = REG_FIELD(0x1E, 0, 7), ++ [BUCK6_ON_VSEL] = REG_FIELD(0x1F, 0, 7), ++ [BUCK7_ON_VSEL] = REG_FIELD(0x20, 0, 7), ++ [BUCK8_ON_VSEL] = REG_FIELD(0x21, 0, 7), ++ [BUCK9_ON_VSEL] = REG_FIELD(0x22, 0, 7), ++ [BUCK10_ON_VSEL] = REG_FIELD(0x23, 0, 7), ++ ++ [BUCK1_SLP_VSEL] = REG_FIELD(0x24, 0, 7), ++ [BUCK2_SLP_VSEL] = REG_FIELD(0x25, 0, 7), ++ [BUCK3_SLP_VSEL] = REG_FIELD(0x26, 0, 7), ++ [BUCK4_SLP_VSEL] = REG_FIELD(0x27, 0, 7), ++ [BUCK5_SLP_VSEL] = REG_FIELD(0x28, 0, 7), ++ [BUCK6_SLP_VSEL] = REG_FIELD(0x29, 0, 7), ++ [BUCK7_SLP_VSEL] = REG_FIELD(0x2A, 0, 7), ++ [BUCK8_SLP_VSEL] = REG_FIELD(0x2B, 0, 7), ++ [BUCK9_SLP_VSEL] = REG_FIELD(0x2C, 0, 7), ++ [BUCK10_SLP_VSEL] = REG_FIELD(0x2D, 0, 7), ++ ++ [NLDO1_ON_VSEL] = REG_FIELD(0x43, 0, 7), ++ [NLDO2_ON_VSEL] = REG_FIELD(0x44, 0, 7), ++ [NLDO3_ON_VSEL] = REG_FIELD(0x45, 0, 7), ++ [NLDO4_ON_VSEL] = REG_FIELD(0x46, 0, 7), ++ [NLDO5_ON_VSEL] = REG_FIELD(0x47, 0, 7), ++ [NLDO1_SLP_VSEL] = REG_FIELD(0x48, 0, 7), ++ [NLDO2_SLP_VSEL] = REG_FIELD(0x49, 0, 7), ++ [NLDO3_SLP_VSEL] = REG_FIELD(0x4A, 0, 7), ++ [NLDO4_SLP_VSEL] = REG_FIELD(0x4B, 0, 7), ++ [NLDO5_SLP_VSEL] = REG_FIELD(0x4C, 0, 7), ++ ++ [PLDO1_ON_VSEL] = REG_FIELD(0x4E, 0, 7), ++ [PLDO2_ON_VSEL] = REG_FIELD(0x4F, 0, 7), ++ [PLDO3_ON_VSEL] = REG_FIELD(0x50, 0, 7), ++ [PLDO4_ON_VSEL] = REG_FIELD(0x51, 0, 7), ++ [PLDO5_ON_VSEL] = REG_FIELD(0x52, 0, 7), ++ [PLDO6_ON_VSEL] = REG_FIELD(0x53, 0, 7), ++ ++ [PLDO1_SLP_VSEL] = REG_FIELD(0x54, 0, 7), ++ [PLDO2_SLP_VSEL] = REG_FIELD(0x55, 0, 7), ++ [PLDO3_SLP_VSEL] = REG_FIELD(0x56, 0, 7), ++ [PLDO4_SLP_VSEL] = REG_FIELD(0x57, 0, 7), ++ [PLDO5_SLP_VSEL] = REG_FIELD(0x58, 0, 7), ++ [PLDO6_SLP_VSEL] = REG_FIELD(0x59, 0, 7), ++ ++ [CHIP_NAME_H] = REG_FIELD(0x5A, 0, 7), ++ [CHIP_NAME_L] = REG_FIELD(0x5B, 4, 7), ++ [CHIP_VER] = REG_FIELD(0x5B, 0, 3), ++ [OTP_VER] = REG_FIELD(0x5C, 0, 3), ++ /* SYS_STS */ ++ [PWRON_STS] = REG_FIELD(0x5D, 7, 7), ++ [VDC_STS] = REG_FIELD(0x5D, 6, 6), ++ [VB_UV_STSS] = REG_FIELD(0x5D, 5, 5), ++ [VB_LO_STS] = REG_FIELD(0x5D, 4, 4), ++ [HOTDIE_STS] = REG_FIELD(0x5D, 3, 3), ++ [TSD_STS] = REG_FIELD(0x5D, 2, 2), ++ [VB_OV_STS] = REG_FIELD(0x5D, 0, 0), ++ /* SYS_CFG0 */ ++ [VB_UV_DLY] = REG_FIELD(0x5E, 7, 7), ++ [VB_UV_SEL] = REG_FIELD(0x5E, 4, 6), ++ [VB_LO_ACT] = REG_FIELD(0x5E, 3, 3), ++ [VB_LO_SEL] = REG_FIELD(0x5E, 0, 2), ++ /* SYS_CFG1 */ ++ [ABNORDET_EN] = REG_FIELD(0x5F, 7, 7), ++ [TSD_TEMP] = REG_FIELD(0x5F, 6, 6), ++ [HOTDIE_TMP] = REG_FIELD(0x5F, 4, 5), ++ [SYS_OV_SD_EN] = REG_FIELD(0x5F, 3, 3), ++ [SYS_OV_SD_DLY_SEL] = REG_FIELD(0x5F, 2, 2), ++ [DLY_ABN_SHORT] = REG_FIELD(0x5F, 0, 1), ++ /* SYS_OPTION */ ++ [VCCXDET_DIS] = REG_FIELD(0x61, 4, 5), ++ [OSC_TC] = REG_FIELD(0x61, 2, 3), ++ [ENB2_2M] = REG_FIELD(0x61, 1, 1), ++ [ENB_32K] = REG_FIELD(0x61, 0, 0), ++ /* SLEEP_CONFIG0 */ ++ [PWRCTRL2_POL] = REG_FIELD(0x62, 7, 7), ++ [PWRCTRL2_FUN] = REG_FIELD(0x62, 4, 6), ++ [PWRCTRL1_POL] = REG_FIELD(0x62, 3, 3), ++ [PWRCTRL1_FUN] = REG_FIELD(0x62, 0, 2), ++ /* SLEEP_CONFIG1 */ ++ [PWRCTRL3_POL] = REG_FIELD(0x63, 3, 3), ++ [PWRCTRL3_FUN] = REG_FIELD(0x63, 0, 2), ++ /* SLEEP_VSEL_CTR_SEL0 */ ++ [BUCK4_VSEL_CTR_SEL] = REG_FIELD(0x65, 4, 5), ++ [BUCK3_VSEL_CTR_SEL] = REG_FIELD(0x65, 0, 1), ++ [BUCK2_VSEL_CTR_SEL] = REG_FIELD(0x64, 4, 5), ++ [BUCK1_VSEL_CTR_SEL] = REG_FIELD(0x64, 0, 1), ++ /* SLEEP_VSEL_CTR_SEL1 */ ++ [BUCK8_VSEL_CTR_SEL] = REG_FIELD(0x67, 4, 5), ++ [BUCK7_VSEL_CTR_SEL] = REG_FIELD(0x67, 0, 1), ++ [BUCK6_VSEL_CTR_SEL] = REG_FIELD(0x66, 4, 5), ++ [BUCK5_VSEL_CTR_SEL] = REG_FIELD(0x66, 0, 1), ++ /* SLEEP_VSEL_CTR_SEL2 */ ++ [NLDO2_VSEL_CTR_SEL] = REG_FIELD(0x69, 4, 5), ++ [NLDO1_VSEL_CTR_SEL] = REG_FIELD(0x69, 0, 1), ++ [BUCK10_VSEL_CTR_SEL] = REG_FIELD(0x68, 4, 5), ++ [BUCK9_VSEL_CTR_SEL] = REG_FIELD(0x68, 0, 1), ++ /* SLEEP_VSEL_CTR_SEL3 */ ++ [NLDO5_VSEL_CTR_SEL] = REG_FIELD(0x6b, 0, 1), ++ [NLDO4_VSEL_CTR_SEL] = REG_FIELD(0x6a, 4, 5), ++ [NLDO3_VSEL_CTR_SEL] = REG_FIELD(0x6a, 0, 1), ++ /* SLEEP_VSEL_CTR_SEL4 */ ++ [PLDO4_VSEL_CTR_SEL] = REG_FIELD(0x6d, 4, 5), ++ [PLDO3_VSEL_CTR_SEL] = REG_FIELD(0x6d, 0, 1), ++ [PLDO2_VSEL_CTR_SEL] = REG_FIELD(0x6c, 4, 5), ++ [PLDO1_VSEL_CTR_SEL] = REG_FIELD(0x6c, 0, 1), ++ /* SLEEP_VSEL_CTR_SEL5 */ ++ [PLDO6_VSEL_CTR_SEL] = REG_FIELD(0x6e, 4, 5), ++ [PLDO5_VSEL_CTR_SEL] = REG_FIELD(0x6e, 0, 1), ++ /* DVS_CTRL_SEL0 */ ++ [BUCK4_DVS_CTR_SEL] = REG_FIELD(0x65, 6, 7), ++ [BUCK3_DVS_CTR_SEL] = REG_FIELD(0x65, 2, 3), ++ [BUCK2_DVS_CTR_SEL] = REG_FIELD(0x64, 6, 7), ++ [BUCK1_DVS_CTR_SEL] = REG_FIELD(0x64, 2, 3), ++ /* DVS_CTRL_SEL1*/ ++ [BUCK8_DVS_CTR_SEL] = REG_FIELD(0x67, 6, 7), ++ [BUCK7_DVS_CTR_SEL] = REG_FIELD(0x67, 2, 3), ++ [BUCK6_DVS_CTR_SEL] = REG_FIELD(0x66, 6, 7), ++ [BUCK5_DVS_CTR_SEL] = REG_FIELD(0x66, 2, 3), ++ /* DVS_CTRL_SEL2 */ ++ [NLDO2_DVS_CTR_SEL] = REG_FIELD(0x69, 6, 7), ++ [NLDO1_DVS_CTR_SEL] = REG_FIELD(0x69, 2, 3), ++ [BUCK10_DVS_CTR_SEL] = REG_FIELD(0x68, 6, 7), ++ [BUCK9_DVS_CTR_SEL] = REG_FIELD(0x68, 2, 3), ++ /* DVS_CTRL_SEL3 */ ++ [NLDO5_DVS_CTR_SEL] = REG_FIELD(0x6b, 2, 3), ++ [NLDO4_DVS_CTR_SEL] = REG_FIELD(0x6a, 6, 7), ++ [NLDO3_DVS_CTR_SEL] = REG_FIELD(0x6a, 2, 3), ++ /* DVS_CTRL_SEL4 */ ++ [PLDO4_DVS_CTR_SEL] = REG_FIELD(0x6d, 6, 7), ++ [PLDO3_DVS_CTR_SEL] = REG_FIELD(0x6d, 2, 3), ++ [PLDO2_DVS_CTR_SEL] = REG_FIELD(0x6c, 6, 7), ++ [PLDO1_DVS_CTR_SEL] = REG_FIELD(0x6c, 2, 3), ++ /* DVS_CTRL_SEL5 */ ++ [PLDO6_DVS_CTR_SEL] = REG_FIELD(0x6e, 6, 7), ++ [PLDO5_DVS_CTR_SEL] = REG_FIELD(0x6e, 2, 3), ++ /* DVS_START_CTRL */ ++ [DVS_START3] = REG_FIELD(0x70, 2, 2), ++ [DVS_START2] = REG_FIELD(0x70, 1, 1), ++ [DVS_START1] = REG_FIELD(0x70, 0, 0), ++ /* SLEEP_GPIO */ ++ [SLP3_DATA] = REG_FIELD(0x71, 6, 6), ++ [SLP2_DATA] = REG_FIELD(0x71, 5, 5), ++ [SLP1_DATA] = REG_FIELD(0x71, 4, 4), ++ [SLP3_DR] = REG_FIELD(0x71, 2, 2), ++ [SLP2_DR] = REG_FIELD(0x71, 1, 1), ++ [SLP1_DR] = REG_FIELD(0x71, 0, 0), ++ /* SYS_CFG3 */ ++ [RST_FUN] = REG_FIELD(0x72, 6, 7), ++ [DEV_RST] = REG_FIELD(0x72, 5, 5), ++ [DEV_SLP] = REG_FIELD(0x72, 4, 4), ++ [SLAVE_RESTART_FUN] = REG_FIELD(0x72, 1, 1), ++ [DEV_OFF] = REG_FIELD(0x72, 0, 0), ++ [WDT_CLR] = REG_FIELD(0x73, 4, 4), ++ [WDT_EN] = REG_FIELD(0x73, 3, 3), ++ [WDT_SET] = REG_FIELD(0x73, 0, 3), ++ [ON_SOURCE] = REG_FIELD(0x74, 0, 7), ++ [OFF_SOURCE] = REG_FIELD(0x75, 0, 7), ++ /* PWRON_KEY */ ++ [PWRON_ON_TIME] = REG_FIELD(0x76, 7, 7), ++ [PWRON_LP_ACT] = REG_FIELD(0x76, 6, 6), ++ [PWRON_LP_OFF_TIME] = REG_FIELD(0x76, 4, 5), ++ [PWRON_LP_TM_SEL] = REG_FIELD(0x76, 2, 3), ++ [PWRON_DB_SEL] = REG_FIELD(0x76, 0, 1), ++ ++ /* GPIO_INT_CONFIG */ ++ [INT_FUNCTION] = REG_FIELD(0x7b, 2, 2), ++ [INT_POL] = REG_FIELD(0x7b, 1, 1), ++ [INT_FC_EN] = REG_FIELD(0x7b, 0, 0), ++ ++ [BUCK1_SEQ] = REG_FIELD(0xB2, 0, 5), ++ [BUCK2_SEQ] = REG_FIELD(0xB3, 0, 5), ++ [BUCK3_SEQ] = REG_FIELD(0xB4, 0, 5), ++ [BUCK4_SEQ] = REG_FIELD(0xB5, 0, 5), ++ [BUCK5_SEQ] = REG_FIELD(0xB6, 0, 5), ++ [BUCK6_SEQ] = REG_FIELD(0xB7, 0, 5), ++ [BUCK7_SEQ] = REG_FIELD(0xB8, 0, 5), ++ [BUCK8_SEQ] = REG_FIELD(0xB9, 0, 5), ++ [BUCK9_SEQ] = REG_FIELD(0xBA, 0, 5), ++ [BUCK10_SEQ] = REG_FIELD(0xBB, 0, 5), ++ ++ [NLDO1_SEQ] = REG_FIELD(0xBC, 0, 5), ++ [NLDO2_SEQ] = REG_FIELD(0xBD, 0, 5), ++ [NLDO3_SEQ] = REG_FIELD(0xBE, 0, 5), ++ [NLDO4_SEQ] = REG_FIELD(0xBF, 0, 5), ++ [NLDO5_SEQ] = REG_FIELD(0xC0, 0, 5), ++ ++ [PLDO6_45_SEQ] = REG_FIELD(0xB5, 6, 7), ++ [PLDO6_23_SEQ] = REG_FIELD(0xB6, 6, 7), ++ [PLDO6_01_SEQ] = REG_FIELD(0xB7, 6, 7), ++ ++ [PLDO1_45_SEQ] = REG_FIELD(0xB8, 6, 7), ++ [PLDO1_23_SEQ] = REG_FIELD(0xB9, 6, 7), ++ [PLDO1_01_SEQ] = REG_FIELD(0xBA, 6, 7), ++ ++ [PLDO2_45_SEQ] = REG_FIELD(0xBB, 6, 7), ++ [PLDO2_23_SEQ] = REG_FIELD(0xBC, 6, 7), ++ [PLDO2_01_SEQ] = REG_FIELD(0xBD, 6, 7), ++ ++ [PLDO3_45_SEQ] = REG_FIELD(0xBE, 6, 7), ++ [PLDO3_23_SEQ] = REG_FIELD(0xBF, 6, 7), ++ [PLDO3_01_SEQ] = REG_FIELD(0xC0, 6, 7), ++ ++ [PLDO4_SEQ] = REG_FIELD(0xC1, 0, 5), ++ [PLDO5_SEQ] = REG_FIELD(0xC2, 0, 5), ++ ++ [BUCK9_RATE2] = REG_FIELD(0xEA, 0, 0), ++ [BUCK10_RATE2] = REG_FIELD(0xEA, 1, 1), ++ [LDO_RATE] = REG_FIELD(0xEA, 3, 5), ++ [BUCK1_RATE2] = REG_FIELD(0xEB, 0, 0), ++ [BUCK2_RATE2] = REG_FIELD(0xEB, 1, 1), ++ [BUCK3_RATE2] = REG_FIELD(0xEB, 2, 2), ++ [BUCK4_RATE2] = REG_FIELD(0xEB, 3, 3), ++ [BUCK5_RATE2] = REG_FIELD(0xEB, 4, 4), ++ [BUCK6_RATE2] = REG_FIELD(0xEB, 5, 5), ++ [BUCK7_RATE2] = REG_FIELD(0xEB, 6, 6), ++ [BUCK8_RATE2] = REG_FIELD(0xEB, 7, 7), ++}; ++ ++static struct resource rk806_pwrkey_resources[] = { ++ DEFINE_RES_IRQ(RK806_IRQ_PWRON_FALL), ++ DEFINE_RES_IRQ(RK806_IRQ_PWRON_RISE), ++}; ++ ++static const struct mfd_cell rk806_cells[] = { ++ { .name = "rk805-pinctrl", }, ++ { ++ .name = "rk805-pwrkey", ++ .num_resources = ARRAY_SIZE(rk806_pwrkey_resources), ++ .resources = &rk806_pwrkey_resources[0], ++ }, ++ { .name = "rk808-regulator", }, ++ ++}; ++ ++static const struct regmap_irq rk806_irqs[] = { ++ /* INT_STS0 IRQs */ ++ REGMAP_IRQ_REG(RK806_IRQ_PWRON_FALL, 0, RK806_INT_STS_PWRON_FALL), ++ REGMAP_IRQ_REG(RK806_IRQ_PWRON_RISE, 0, RK806_INT_STS_PWRON_RISE), ++ REGMAP_IRQ_REG(RK806_IRQ_PWRON, 0, RK806_INT_STS_PWRON), ++ REGMAP_IRQ_REG(RK806_IRQ_PWRON_LP, 0, RK806_INT_STS_PWRON_LP), ++ REGMAP_IRQ_REG(RK806_IRQ_HOTDIE, 0, RK806_INT_STS_HOTDIE), ++ REGMAP_IRQ_REG(RK806_IRQ_VDC_RISE, 0, RK806_INT_STS_VDC_RISE), ++ REGMAP_IRQ_REG(RK806_IRQ_VDC_FALL, 0, RK806_INT_STS_VDC_FALL), ++ REGMAP_IRQ_REG(RK806_IRQ_VB_LO, 0, RK806_INT_STS_VB_LO), ++ /* INT_STS1 IRQs */ ++ REGMAP_IRQ_REG(RK806_IRQ_REV0, 1, RK806_INT_STS_REV0), ++ REGMAP_IRQ_REG(RK806_IRQ_REV1, 1, RK806_INT_STS_REV1), ++ REGMAP_IRQ_REG(RK806_IRQ_REV2, 1, RK806_INT_STS_REV2), ++ REGMAP_IRQ_REG(RK806_IRQ_CRC_ERROR, 1, RK806_INT_STS_CRC_ERROR), ++ REGMAP_IRQ_REG(RK806_IRQ_SLP3_GPIO, 1, RK806_INT_STS_SLP3_GPIO), ++ REGMAP_IRQ_REG(RK806_IRQ_SLP2_GPIO, 1, RK806_INT_STS_SLP2_GPIO), ++ REGMAP_IRQ_REG(RK806_IRQ_SLP1_GPIO, 1, RK806_INT_STS_SLP1_GPIO), ++ REGMAP_IRQ_REG(RK806_IRQ_WDT, 1, RK806_INT_STS_WDT), ++}; ++ ++static struct regmap_irq_chip rk806_irq_chip = { ++ .name = "rk806", ++ .irqs = rk806_irqs, ++ .num_irqs = ARRAY_SIZE(rk806_irqs), ++ .num_regs = 2, ++ .irq_reg_stride = 2, ++ .mask_base = RK806_INT_MSK0, ++ .status_base = RK806_INT_STS0, ++ .ack_base = RK806_INT_STS0, ++ .init_ack_masked = true, ++}; ++ ++static const struct regmap_range rk806_yes_ranges[] = { ++ /* regmap_reg_range(RK806_INT_STS0, RK806_GPIO_INT_CONFIG), */ ++ regmap_reg_range(RK806_POWER_EN0, RK806_POWER_EN5), ++ regmap_reg_range(0x70, 0x7a), ++}; ++ ++static const struct regmap_access_table rk806_volatile_table = { ++ .yes_ranges = rk806_yes_ranges, ++ .n_yes_ranges = ARRAY_SIZE(rk806_yes_ranges), ++}; ++ ++const struct regmap_config rk806_regmap_config = { ++ .reg_bits = 8, ++ .val_bits = 8, ++ .cache_type = REGCACHE_RBTREE, ++ .volatile_table = &rk806_volatile_table, ++}; ++EXPORT_SYMBOL_GPL(rk806_regmap_config); ++ ++static struct kobject *rk806_kobj[2]; ++static struct rk806 *rk806_master; ++static struct rk806 *rk806_slaver; ++ ++static ssize_t rk806_master_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, ++ size_t count) ++{ ++ u32 input[2], addr, data; ++ struct rk806 *rk806; ++ char cmd; ++ int ret; ++ ++ ret = sscanf(buf, "%c ", &cmd); ++ if (ret != 1) { ++ pr_err("Unknown command\n"); ++ goto out; ++ } ++ ++ rk806 = rk806_master; ++ if (!rk806) { ++ pr_err("error! rk806 master is NULL\n"); ++ return 0; ++ } ++ ++ switch (cmd) { ++ case 'w': ++ ret = sscanf(buf, "%c %x %x", &cmd, &input[0], &input[1]); ++ if (ret != 3) { ++ pr_err("error! cmd format: echo w [addr] [value]\n"); ++ goto out; ++ }; ++ ++ addr = input[0] & 0xff; ++ data = input[1] & 0xff; ++ pr_info("cmd : %c %x %x\n\n", cmd, input[0], input[1]); ++ ++ regmap_write(rk806->regmap, addr, data); ++ regmap_read(rk806->regmap, addr, &data); ++ pr_info("new: %x %x\n", addr, data); ++ break; ++ case 'r': ++ ret = sscanf(buf, "%c %x ", &cmd, &input[0]); ++ if (ret != 2) { ++ pr_err("error! cmd format: echo r [addr]\n"); ++ goto out; ++ }; ++ ++ pr_info("cmd : %c %x\n\n", cmd, input[0]); ++ addr = input[0] & 0xff; ++ ++ regmap_read(rk806->regmap, addr, &data); ++ pr_info("%x %x\n", input[0], data); ++ break; ++ default: ++ pr_err("Unknown command\n"); ++ break; ++ } ++ ++out: ++ return count; ++} ++ ++static ssize_t rk806_slaver_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, ++ size_t count) ++{ ++ u32 input[2], addr, data; ++ struct rk806 *rk806; ++ char cmd; ++ int ret; ++ ++ ret = sscanf(buf, "%c ", &cmd); ++ if (ret != 1) { ++ pr_err("Unknown command\n"); ++ goto out; ++ } ++ ++ rk806 = rk806_slaver; ++ if (!rk806) { ++ pr_err("error! rk806 slaver is NULL\n"); ++ return 0; ++ } ++ ++ switch (cmd) { ++ case 'w': ++ ret = sscanf(buf, "%c %x %x", &cmd, &input[0], &input[1]); ++ if (ret != 3) { ++ pr_err("error! cmd format: echo w [addr] [value]\n"); ++ goto out; ++ }; ++ ++ addr = input[0] & 0xff; ++ data = input[1] & 0xff; ++ pr_info("cmd : %c %x %x\n\n", cmd, input[0], input[1]); ++ ++ regmap_write(rk806->regmap, addr, data); ++ regmap_read(rk806->regmap, addr, &data); ++ pr_info("new: %x %x\n", addr, data); ++ break; ++ case 'r': ++ ret = sscanf(buf, "%c %x ", &cmd, &input[0]); ++ if (ret != 2) { ++ pr_err("error! cmd format: echo r [addr]\n"); ++ goto out; ++ }; ++ pr_info("cmd : %c %x\n\n", cmd, input[0]); ++ ++ addr = input[0] & 0xff; ++ regmap_read(rk806->regmap, addr, &data); ++ pr_info("%x %x\n", input[0], data); ++ break; ++ default: ++ pr_err("Unknown command\n"); ++ break; ++ } ++ ++out: ++ return count; ++} ++ ++static struct device_attribute rk806_master_attrs = ++ __ATTR(debug, 0200, NULL, rk806_master_store); ++ ++static struct device_attribute rk806_slaver_attrs = ++ __ATTR(debug, 0200, NULL, rk806_slaver_store); ++ ++int rk806_field_read(struct rk806 *rk806, ++ enum rk806_fields field_id) ++{ ++ int ret; ++ int val; ++ ++ ret = regmap_field_read(rk806->rmap_fields[field_id], &val); ++ if (ret < 0) ++ return ret; ++ ++ return val; ++} ++EXPORT_SYMBOL_GPL(rk806_field_read); ++ ++int rk806_field_write(struct rk806 *rk806, ++ enum rk806_fields field_id, ++ unsigned int val) ++{ ++ return regmap_field_write(rk806->rmap_fields[field_id], val); ++} ++EXPORT_SYMBOL_GPL(rk806_field_write); ++ ++static void rk806_vb_requence_config(struct rk806 *rk806) ++{ ++ struct rk806_platform_data *pdata = rk806->pdata; ++ int i; ++ ++ if (!pdata->support_vb_sequence || !pdata->vb_shutdown_sequence) ++ return; ++ ++ for (i = RK806_ID_DCDC1; i <= RK806_ID_DCDC10; i++) ++ rk806_field_write(rk806, BUCK1_SEQ + i, pdata->vb_shutdown_sequence[i]); ++ ++ for (i = RK806_ID_NLDO1; i <= RK806_ID_NLDO5; i++) ++ rk806_field_write(rk806, ++ NLDO1_SEQ + (i - RK806_ID_NLDO1), ++ pdata->vb_shutdown_sequence[i]); ++ ++ rk806_field_write(rk806, PLDO1_01_SEQ, pdata->vb_shutdown_sequence[RK806_ID_PLDO1]); ++ rk806_field_write(rk806, PLDO2_01_SEQ, pdata->vb_shutdown_sequence[RK806_ID_PLDO2]); ++ rk806_field_write(rk806, PLDO3_01_SEQ, pdata->vb_shutdown_sequence[RK806_ID_PLDO3]); ++ rk806_field_write(rk806, PLDO4_SEQ, pdata->vb_shutdown_sequence[RK806_ID_PLDO4]); ++ rk806_field_write(rk806, PLDO5_SEQ, pdata->vb_shutdown_sequence[RK806_ID_PLDO5]); ++ rk806_field_write(rk806, PLDO6_01_SEQ, pdata->vb_shutdown_sequence[RK806_ID_PLDO6]); ++} ++ ++void rk806_shutdown_requence_config(struct rk806 *rk806) ++{ ++ struct rk806_platform_data *pdata = rk806->pdata; ++ int i; ++ ++ if (!pdata->support_shutdown_sequence || !pdata->shutdown_sequence) ++ return; ++ ++ for (i = RK806_ID_DCDC1; i <= RK806_ID_DCDC10; i++) ++ rk806_field_write(rk806, BUCK1_SEQ + i, pdata->shutdown_sequence[i]); ++ ++ for (i = RK806_ID_NLDO1; i <= RK806_ID_NLDO5; i++) ++ rk806_field_write(rk806, ++ NLDO1_SEQ + (i - RK806_ID_NLDO1), ++ pdata->shutdown_sequence[i]); ++ ++ rk806_field_write(rk806, PLDO1_01_SEQ, pdata->shutdown_sequence[RK806_ID_PLDO1]); ++ rk806_field_write(rk806, PLDO2_01_SEQ, pdata->shutdown_sequence[RK806_ID_PLDO2]); ++ rk806_field_write(rk806, PLDO3_01_SEQ, pdata->shutdown_sequence[RK806_ID_PLDO3]); ++ rk806_field_write(rk806, PLDO4_SEQ, pdata->shutdown_sequence[RK806_ID_PLDO4]); ++ rk806_field_write(rk806, PLDO5_SEQ, pdata->shutdown_sequence[RK806_ID_PLDO5]); ++ rk806_field_write(rk806, PLDO6_01_SEQ, pdata->shutdown_sequence[RK806_ID_PLDO6]); ++} ++EXPORT_SYMBOL_GPL(rk806_shutdown_requence_config); ++ ++static void rk806_irq_init(struct rk806 *rk806) ++{ ++ /* INT pin polarity active low */ ++ rk806_field_write(rk806, INT_POL, RK806_INT_POL_LOW); ++} ++ ++static int rk806_pinctrl_init(struct rk806 *rk806) ++{ ++ struct device *dev = rk806->dev; ++ ++ rk806->pins = devm_kzalloc(dev, ++ sizeof(struct rk806_pin_info), ++ GFP_KERNEL); ++ if (!rk806->pins) ++ return -ENOMEM; ++ ++ rk806->pins->p = devm_pinctrl_get(dev); ++ if (IS_ERR(rk806->pins->p)) { ++ rk806->pins->p = NULL; ++ dev_err(dev, "no pinctrl handle\n"); ++ return 0; ++ } ++ ++ rk806->pins->default_st = pinctrl_lookup_state(rk806->pins->p, ++ PINCTRL_STATE_DEFAULT); ++ ++ if (IS_ERR(rk806->pins->default_st)) ++ dev_err(dev, "no default pinctrl state\n"); ++ ++ rk806->pins->power_off = pinctrl_lookup_state(rk806->pins->p, ++ "pmic-power-off"); ++ if (IS_ERR(rk806->pins->power_off)) { ++ rk806->pins->power_off = NULL; ++ dev_err(dev, "no power-off pinctrl state\n"); ++ } ++ ++ rk806->pins->sleep = pinctrl_lookup_state(rk806->pins->p, ++ "pmic-sleep"); ++ if (IS_ERR(rk806->pins->sleep)) { ++ rk806->pins->sleep = NULL; ++ dev_err(dev, "no sleep-setting state\n"); ++ } ++ ++ rk806->pins->reset = pinctrl_lookup_state(rk806->pins->p, ++ "pmic-reset"); ++ if (IS_ERR(rk806->pins->reset)) { ++ rk806->pins->reset = NULL; ++ dev_err(dev, "no reset-setting pinctrl state\n"); ++ } ++ ++ rk806->pins->dvs = pinctrl_lookup_state(rk806->pins->p, ++ "pmic-dvs"); ++ if (IS_ERR(rk806->pins->dvs)) { ++ rk806->pins->dvs = NULL; ++ dev_err(dev, "no dvs-setting pinctrl state\n"); ++ } ++ ++ return 0; ++} ++ ++static void rk806_vb_force_shutdown_init(struct rk806 *rk806) ++{ ++ struct rk806_platform_data *pdata = rk806->pdata; ++ ++ rk806_vb_requence_config(rk806); ++ rk806_field_write(rk806, VB_LO_ACT, VB_LO_ACT_SD); ++ rk806_field_write(rk806, VB_LO_SEL, ++ (pdata->low_voltage_threshold - 2800) / 100); ++} ++ ++static irqreturn_t rk806_vdc_irq(int irq, void *data) ++{ ++ struct rk806 *rk806 = data; ++ ++ pm_wakeup_dev_event(rk806->dev, 2000, false); ++ return IRQ_HANDLED; ++} ++ ++static void rk806_vdc_irqs_init(struct rk806 *rk806) ++{ ++ int ret, vdc_irq_fall, vdc_irq_rise; ++ ++ if (!rk806->pdata->vdc_wakeup_enable) ++ return; ++ ++ vdc_irq_rise = regmap_irq_get_virq(rk806->irq_data, RK806_IRQ_VDC_RISE); ++ if (vdc_irq_rise < 0) { ++ dev_err(rk806->dev, "RK806: IRQ_VDC_RISE get failed!\n"); ++ return; ++ } ++ ++ ret = devm_request_threaded_irq(rk806->dev, vdc_irq_rise, ++ NULL, ++ rk806_vdc_irq, ++ IRQF_TRIGGER_HIGH | IRQF_ONESHOT, ++ "rk806_vdc_rise", rk806); ++ if (ret) { ++ dev_err(rk806->dev, "rk806: vdc_irq_rise request failed!\n"); ++ return; ++ } ++ enable_irq_wake(vdc_irq_rise); ++ ++ vdc_irq_fall = regmap_irq_get_virq(rk806->irq_data, RK806_IRQ_VDC_FALL); ++ if (vdc_irq_fall < 0) { ++ dev_err(rk806->dev, "RK806: IRQ_VDC_FALL get failed!\n"); ++ return; ++ } ++ ++ ret = devm_request_threaded_irq(rk806->dev, vdc_irq_fall, ++ NULL, ++ rk806_vdc_irq, ++ IRQF_TRIGGER_HIGH | IRQF_ONESHOT, ++ "rk806_vdc_fall", rk806); ++ if (ret) { ++ dev_err(rk806->dev, "rk806: vdc_irq_fall request failed!\n"); ++ return; ++ } ++ enable_irq_wake(vdc_irq_fall); ++ device_init_wakeup(rk806->dev, true); ++} ++ ++ ++static int rk806_parse_dt(struct rk806 *rk806) ++{ ++ struct rk806_platform_data *pdata; ++ struct device *dev = rk806->dev; ++ int rst_fun; ++ int ret; ++ ++ pdata = rk806->pdata; ++ ++ pdata->shutdown_voltage_threshold = 2700; ++ pdata->shutdown_temperture_threshold = 160; ++ pdata->hotdie_temperture_threshold = 115; ++ pdata->force_shutdown_enable = 1; ++ pdata->vdc_wakeup_enable = 0; ++ ++ ret = device_property_read_u32(dev, ++ "low_voltage_threshold", ++ &pdata->low_voltage_threshold); ++ if (ret < 0) { ++ pdata->low_voltage_threshold = VB_LO_3400MV; ++ dev_info(dev, "low_voltage_threshold missing!\n"); ++ } else { ++ if ((pdata->low_voltage_threshold > 3500) || ++ (pdata->low_voltage_threshold < 2800)) { ++ dev_err(dev, "low_voltage_threshold out [2800 3500]!\n"); ++ pdata->low_voltage_threshold = VB_LO_3400MV; ++ } ++ } ++ ret = device_property_read_u32(dev, ++ "shutdown_voltage_threshold", ++ &pdata->shutdown_voltage_threshold); ++ if (ret < 0) { ++ pdata->force_shutdown_enable = 0; ++ dev_info(dev, "shutdown_voltage_threshold missing!\n"); ++ } ++ ++ if ((pdata->shutdown_voltage_threshold > 3400) || ++ (pdata->shutdown_voltage_threshold < 2700)) { ++ dev_err(dev, "shutdown_voltage_threshold out [2700 3400]!\n"); ++ pdata->shutdown_voltage_threshold = 2700; ++ } ++ ++ ret = device_property_read_u32(dev, ++ "shutdown_temperture_threshold", ++ &pdata->shutdown_temperture_threshold); ++ if (ret < 0) ++ dev_info(dev, "shutdown_temperture_threshold missing!\n"); ++ ++ ret = device_property_read_u32(dev, ++ "hotdie_temperture_threshold", ++ &pdata->hotdie_temperture_threshold); ++ if (ret < 0) ++ dev_info(dev, "hotdie_temperture_threshold missing!\n"); ++ ++ ret = device_property_read_u32(dev, "pmic-reset-func", &rst_fun); ++ if (ret < 0) { ++ dev_info(dev, "pmic-reset-func missing!\n"); ++ rk806_field_write(rk806, RST_FUN, 0x00); ++ } else ++ rk806_field_write(rk806, RST_FUN, rst_fun); ++ ++ /* PWRON_ON_TIME: 0:500mS; 1:20mS */ ++ if (device_property_read_bool(dev, "pwron-on-time-500ms")) ++ rk806_field_write(rk806, PWRON_ON_TIME, 0x00); ++ ++ if (device_property_read_bool(dev, "vdc-wakeup-enable")) ++ pdata->vdc_wakeup_enable = 1; ++ ++ ret = device_property_read_u32(dev, ++ "shutown_by_pwrctrln", ++ &pdata->shutown_by_pwrctrln); ++ if (ret < 0) { ++ dev_info(dev, "shutown_by_pwrctrln missing!\n"); ++ pdata->shutown_by_pwrctrln = 1; ++ } ++ if ((pdata->shutown_by_pwrctrln < 1) || (pdata->shutown_by_pwrctrln > 3)) { ++ dev_err(dev, "shutown_by_pwrctrln out [1 3]!\n"); ++ pdata->shutown_by_pwrctrln = 1; ++ } ++ ++ pdata->shutdown_sequence = devm_kzalloc(dev, ++ RK806_ID_END * sizeof(int), ++ GFP_KERNEL); ++ if (!pdata->shutdown_sequence) ++ return -EINVAL; ++ ++ pdata->support_shutdown_sequence = 1; ++ ret = device_property_read_u32_array(dev, ++ "shutdown-sequence", ++ pdata->shutdown_sequence, ++ RK806_ID_END); ++ if (ret) { ++ dev_info(dev, "shutdown-sequence missing!\n"); ++ pdata->support_shutdown_sequence = 0; ++ } ++ ++ pdata->vb_shutdown_sequence = devm_kzalloc(dev, ++ RK806_ID_END * sizeof(int), ++ GFP_KERNEL); ++ if (!pdata->vb_shutdown_sequence) ++ return -EINVAL; ++ ++ pdata->support_vb_sequence = 1; ++ ret = device_property_read_u32_array(dev, ++ "vb-shutdown-sequence", ++ pdata->vb_shutdown_sequence, ++ RK806_ID_END); ++ if (ret) { ++ pdata->support_vb_sequence = 0; ++ dev_info(dev, "vb-shutdown-sequence missing!\n"); ++ } ++ ++ pdata->dvs_control_suspend = devm_kzalloc(dev, ++ RK806_ID_END * sizeof(int), ++ GFP_KERNEL); ++ if (!pdata->dvs_control_suspend) ++ return -EINVAL; ++ ++ pdata->support_dvs_control_suspend = 1; ++ ret = device_property_read_u32_array(dev, ++ "dvs-suspend-control-by", ++ pdata->dvs_control_suspend, ++ RK806_ID_END); ++ if (ret) { ++ pdata->support_dvs_control_suspend = 0; ++ dev_info(dev, "dvs-suspend-control-by missing!\n"); ++ } ++ ++ return 0; ++} ++ ++static int rk806_init(struct rk806 *rk806) ++{ ++ struct rk806_platform_data *pdata; ++ int vb_uv_sel; ++ ++ pdata = rk806->pdata; ++ ++ if (pdata->force_shutdown_enable) { ++ if (pdata->shutdown_voltage_threshold <= 2700) ++ vb_uv_sel = VB_UV_SEL_2700; ++ else ++ vb_uv_sel = (pdata->shutdown_voltage_threshold - 2700) / 100; ++ ++ rk806_field_write(rk806, VB_UV_SEL, vb_uv_sel); ++ } ++ ++ if (pdata->hotdie_temperture_threshold >= 160) ++ rk806_field_write(rk806, TSD_TEMP, TSD_TEMP_160); ++ ++ /* When the slave chip goes through a shutdown process, it will automatically trigger a restart */ ++ rk806_field_write(rk806, SLAVE_RESTART_FUN, 0x01); ++ /* Digital output 2MHz clock force enable */ ++ rk806_field_write(rk806, ENB2_2M, 0x01); ++ ++ rk806_vb_force_shutdown_init(rk806); ++ rk806_vdc_irqs_init(rk806); ++ ++ return 0; ++} ++ ++int rk806_device_init(struct rk806 *rk806) ++{ ++ struct device_node *np = rk806->dev->of_node; ++ struct rk806_platform_data *pdata; ++ int name_h, name_l, chip_ver, otp_ver; ++ int on_source, off_source; ++ int ret; ++ int i; ++ ++ pdata = devm_kzalloc(rk806->dev, sizeof(*pdata), GFP_KERNEL); ++ if (!pdata) ++ return -ENOMEM; ++ ++ rk806->pdata = pdata; ++ ++ for (i = 0; i < ARRAY_SIZE(rk806_reg_fields); i++) { ++ const struct reg_field *reg_fields = rk806_reg_fields; ++ ++ rk806->rmap_fields[i] = ++ devm_regmap_field_alloc(rk806->dev, ++ rk806->regmap, ++ reg_fields[i]); ++ if (IS_ERR(rk806->rmap_fields[i])) { ++ dev_err(rk806->dev, "cannot allocate regmap field\n"); ++ return PTR_ERR(rk806->rmap_fields[i]); ++ } ++ } ++ ++ name_h = rk806_field_read(rk806, CHIP_NAME_H); ++ name_l = rk806_field_read(rk806, CHIP_NAME_L); ++ chip_ver = rk806_field_read(rk806, CHIP_VER); ++ otp_ver = rk806_field_read(rk806, OTP_VER); ++ dev_info(rk806->dev, "chip id: RK%x%x,ver:0x%x, 0x%x\n", ++ name_h, name_l, chip_ver, otp_ver); ++ if (chip_ver == VERSION_AB) ++ rk806_field_write(rk806, ABNORDET_EN, 0x01); ++ ++ on_source = rk806_field_read(rk806, ON_SOURCE); ++ off_source = rk806_field_read(rk806, OFF_SOURCE); ++ dev_info(rk806->dev, "ON: 0x%x OFF:0x%x\n", on_source, off_source); ++ ++ rk806_parse_dt(rk806); ++ ++ rk806_irq_init(rk806); ++ ret = devm_regmap_add_irq_chip(rk806->dev, ++ rk806->regmap, ++ rk806->irq, ++ IRQF_ONESHOT | IRQF_SHARED, ++ 0, ++ &rk806_irq_chip, ++ &rk806->irq_data); ++ if (ret) { ++ dev_err(rk806->dev, "Failed to add IRQ chip: err = %d\n", ret); ++ return ret; ++ } ++ enable_irq_wake(rk806->irq); ++ ++ ret = devm_mfd_add_devices(rk806->dev, ++ PLATFORM_DEVID_AUTO, ++ rk806_cells, ++ ARRAY_SIZE(rk806_cells), ++ NULL, ++ 0, ++ regmap_irq_get_domain(rk806->irq_data)); ++ if (ret < 0) { ++ dev_err(rk806->dev, "mfd_add_devices failed: %d\n", ret); ++ return ret; ++ } ++ ++ rk806_pinctrl_init(rk806); ++ rk806_init(rk806); ++ ++ if (strcmp(np->name, "rk806slave")) { ++ rk806_kobj[0] = kobject_create_and_add(np->name, NULL); ++ if (rk806_kobj[0]) { ++ ret = sysfs_create_file(rk806_kobj[0], &rk806_master_attrs.attr); ++ if (ret) ++ dev_err(rk806->dev, "create %s sysfs error\n", np->name); ++ else ++ rk806_master = rk806; ++ } ++ } else { ++ rk806_kobj[1] = kobject_create_and_add(np->name, NULL); ++ if (rk806_kobj[1]) { ++ ret = sysfs_create_file(rk806_kobj[1], &rk806_slaver_attrs.attr); ++ if (ret) ++ dev_err(rk806->dev, "create %s sysfs error\n", np->name); ++ else ++ rk806_slaver = rk806; ++ } ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(rk806_device_init); ++ ++int rk806_core_suspend(struct device *dev) ++{ ++ struct rk806 *rk806 = dev_get_drvdata(dev); ++ ++ disable_irq(rk806->irq); ++ enable_irq_wake(rk806->irq); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(rk806_core_suspend); ++ ++int rk806_core_resume(struct device *dev) ++{ ++ struct rk806 *rk806 = dev_get_drvdata(dev); ++ ++ enable_irq(rk806->irq); ++ disable_irq_wake(rk806->irq); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(rk806_core_resume); ++ ++int rk806_device_exit(struct rk806 *rk806) ++{ ++ struct device_node *np = rk806->dev->of_node; ++ ++ if (strcmp(np->name, "rk806slave")) { ++ if (rk806_kobj[0]) { ++ sysfs_remove_file(rk806_kobj[0], &rk806_master_attrs.attr); ++ kobject_put(rk806_kobj[0]); ++ } ++ } else { ++ if (rk806_kobj[1]) { ++ sysfs_remove_file(rk806_kobj[1], &rk806_slaver_attrs.attr); ++ kobject_put(rk806_kobj[1]); ++ } ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(rk806_device_exit); ++ ++const struct of_device_id rk806_of_match[] = { ++ { .compatible = "rockchip,rk806", }, ++ { } ++}; ++EXPORT_SYMBOL_GPL(rk806_of_match); ++ ++MODULE_AUTHOR("Xu Shengfei "); ++MODULE_DESCRIPTION("rk806 MFD Driver"); ++MODULE_LICENSE("GPL v2"); +--- a/drivers/mfd/rk8xx-spi.c ++++ b/drivers/mfd/rk8xx-spi.c +@@ -1,124 +1,154 @@ + // SPDX-License-Identifier: GPL-2.0 + /* +- * Rockchip RK806 Core (SPI) driver ++ * SPI access driver for rockchip rk806 + * + * Copyright (c) 2021 Rockchip Electronics Co., Ltd. +- * Copyright (c) 2023 Collabora Ltd. + * + * Author: Xu Shengfei +- * Author: Sebastian Reichel + */ + +-#include +-#include +-#include ++#include + #include + #include + #include + +-#define RK806_ADDR_SIZE 2 +-#define RK806_CMD_WITH_SIZE(CMD, VALUE_BYTES) \ +- (RK806_CMD_##CMD | RK806_CMD_CRC_DIS | (VALUE_BYTES - 1)) ++static int rk806_spi_write(struct spi_device *spi, ++ char addr, ++ const char *data, ++ size_t data_len) ++{ ++ char write_cmd = RK806_CMD_WRITE | (data_len - 1); ++ char addrh = RK806_REG_H; ++ struct spi_message m; ++ int buffer, ret = 0; ++ struct spi_transfer write_cmd_packet = { ++ .tx_buf = &buffer, ++ .len = sizeof(buffer), ++ }; + +-static const struct regmap_range rk806_volatile_ranges[] = { +- regmap_reg_range(RK806_POWER_EN0, RK806_POWER_EN5), +- regmap_reg_range(RK806_DVS_START_CTRL, RK806_INT_MSK1), +-}; ++ buffer = write_cmd | (addr << 8) | (addrh << 16) | (*data << 24); + +-static const struct regmap_access_table rk806_volatile_table = { +- .yes_ranges = rk806_volatile_ranges, +- .n_yes_ranges = ARRAY_SIZE(rk806_volatile_ranges), +-}; ++ spi_message_init(&m); ++ spi_message_add_tail(&write_cmd_packet, &m); ++ ret = spi_sync(spi, &m); ++ return ret; ++} + +-static const struct regmap_config rk806_regmap_config_spi = { +- .reg_bits = 16, +- .val_bits = 8, +- .max_register = RK806_BUCK_RSERVE_REG5, +- .cache_type = REGCACHE_MAPLE, +- .volatile_table = &rk806_volatile_table, +-}; +- +-static int rk806_spi_bus_write(void *context, const void *vdata, size_t count) ++static int rk806_spi_bus_write(void *context, const void *data, size_t count) + { + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); +- struct spi_transfer xfer[2] = { 0 }; +- /* data and thus count includes the register address */ +- size_t val_size = count - RK806_ADDR_SIZE; +- char cmd; ++ char buf[2]; + +- if (val_size < 1 || val_size > (RK806_CMD_LEN_MSK + 1)) ++ if (count < 2) { ++ dev_err(&spi->dev, "regmap write err!\n"); + return -EINVAL; ++ } ++ memcpy(buf, data, 2); + +- cmd = RK806_CMD_WITH_SIZE(WRITE, val_size); +- +- xfer[0].tx_buf = &cmd; +- xfer[0].len = sizeof(cmd); +- xfer[1].tx_buf = vdata; +- xfer[1].len = count; +- +- return spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer)); ++ return rk806_spi_write(spi, buf[0], &buf[1], (count - 1)); + } + +-static int rk806_spi_bus_read(void *context, const void *vreg, size_t reg_size, +- void *val, size_t val_size) ++static int rk806_spi_bus_read(void *context, ++ const void *reg, ++ size_t reg_size, ++ void *val, ++ size_t val_size) + { + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); +- char txbuf[3] = { 0 }; ++ struct rk806 *rk806 = spi_get_drvdata(spi); ++ char txbuf[2] = { 0 }; ++ int status; ++ u8 *local_buf; ++ char addr; + +- if (reg_size != RK806_ADDR_SIZE || +- val_size < 1 || val_size > (RK806_CMD_LEN_MSK + 1)) ++ if (reg_size != sizeof(char) || val_size < 1) + return -EINVAL; + +- /* TX buffer contains command byte followed by two address bytes */ +- txbuf[0] = RK806_CMD_WITH_SIZE(READ, val_size); +- memcpy(txbuf+1, vreg, reg_size); ++ if (val_size >= RK806_READ_BUFFER_SIZE) { ++ local_buf = kmalloc(1 + val_size, GFP_KERNEL); ++ if (!local_buf) ++ return -ENOMEM; ++ } else { ++ local_buf = rk806->read_buf; ++ } ++ /* Copy address to read from into first element of SPI buffer. */ ++ memcpy(&addr, reg, sizeof(char)); + +- return spi_write_then_read(spi, txbuf, sizeof(txbuf), val, val_size); ++ txbuf[0] = RK806_CMD_READ | (val_size - 1); ++ txbuf[1] = addr; ++ ++ status = spi_write_then_read(spi, txbuf, 2, local_buf, val_size + 1); ++ ++ if (status == 0) ++ memcpy(val, &local_buf[1], val_size); ++ ++ if (val_size >= RK806_READ_BUFFER_SIZE) ++ kfree(local_buf); ++ ++ return status; + } + + static const struct regmap_bus rk806_regmap_bus_spi = { + .write = rk806_spi_bus_write, + .read = rk806_spi_bus_read, +- .reg_format_endian_default = REGMAP_ENDIAN_LITTLE, ++ .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, ++ .val_format_endian_default = REGMAP_ENDIAN_NATIVE, + }; + + static int rk8xx_spi_probe(struct spi_device *spi) + { +- struct regmap *regmap; ++ struct rk806 *rk806; + +- regmap = devm_regmap_init(&spi->dev, &rk806_regmap_bus_spi, +- &spi->dev, &rk806_regmap_config_spi); +- if (IS_ERR(regmap)) +- return dev_err_probe(&spi->dev, PTR_ERR(regmap), +- "Failed to init regmap\n"); ++ rk806 = devm_kzalloc(&spi->dev, sizeof(*rk806), GFP_KERNEL); ++ if (!rk806) ++ return -ENOMEM; + +- return rk8xx_probe(&spi->dev, RK806_ID, spi->irq, regmap); ++ spi_set_drvdata(spi, rk806); ++ rk806->dev = &spi->dev; ++ rk806->irq = spi->irq; ++ ++ rk806->regmap = devm_regmap_init(&spi->dev, ++ &rk806_regmap_bus_spi, ++ &spi->dev, ++ &rk806_regmap_config); ++ if (IS_ERR(rk806->regmap)) { ++ dev_err(rk806->dev, "Failed to initialize register map\n"); ++ return PTR_ERR(rk806->regmap); ++ } ++ ++ return rk806_device_init(rk806); + } + +-static const struct of_device_id rk8xx_spi_of_match[] = { +- { .compatible = "rockchip,rk806", }, +- { } +-}; +-MODULE_DEVICE_TABLE(of, rk8xx_spi_of_match); ++static void rk8xx_spi_remove(struct spi_device *spi) ++{ ++ struct rk806 *rk806 = spi_get_drvdata(spi); ++ ++ rk806_device_exit(rk806); ++} ++ ++static DEFINE_SIMPLE_DEV_PM_OPS(rk8xx_spi_pm_ops, rk806_core_suspend, rk806_core_resume); + + static const struct spi_device_id rk8xx_spi_id_table[] = { + { "rk806", 0 }, +- { } ++ { /* sentinel */ } + }; +-MODULE_DEVICE_TABLE(spi, rk8xx_spi_id_table); ++MODULE_DEVICE_TABLE(spi, rk8xx_spi_id_table); + + static struct spi_driver rk8xx_spi_driver = { + .driver = { +- .name = "rk8xx-spi", +- .of_match_table = rk8xx_spi_of_match, ++ .name = "rk806", ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(rk806_of_match), ++ .pm = pm_sleep_ptr(&rk8xx_spi_pm_ops), + }, + .probe = rk8xx_spi_probe, ++ .remove = rk8xx_spi_remove, + .id_table = rk8xx_spi_id_table, + }; + module_spi_driver(rk8xx_spi_driver); + + MODULE_AUTHOR("Xu Shengfei "); +-MODULE_DESCRIPTION("RK8xx SPI PMIC driver"); +-MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("RK8xx SPI PMIC driver (RK806 full module)"); ++MODULE_LICENSE("GPL v2"); +--- a/drivers/mfd/Makefile ++++ b/drivers/mfd/Makefile +@@ -231,7 +231,7 @@ + obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o + obj-$(CONFIG_MFD_NTXEC) += ntxec.o + obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o +-obj-$(CONFIG_MFD_RK8XX) += rk8xx-core.o ++obj-$(CONFIG_MFD_RK8XX) += rk8xx-core.o rk806-core.o + obj-$(CONFIG_MFD_RK8XX_I2C) += rk8xx-i2c.o + obj-$(CONFIG_MFD_RK8XX_SPI) += rk8xx-spi.o + obj-$(CONFIG_MFD_RN5T618) += rn5t618.o From c49081b8455c18ecf5c491701f923cecdbcf92b8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 26 Feb 2026 14:44:26 +0000 Subject: [PATCH 07/11] fix(nanopc-t6): fallback to available rockchip kernel attribute Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com> --- nix/builders/nanopc-t6/base.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nix/builders/nanopc-t6/base.nix b/nix/builders/nanopc-t6/base.nix index bd8de3d..c4dc0e6 100644 --- a/nix/builders/nanopc-t6/base.nix +++ b/nix/builders/nanopc-t6/base.nix @@ -72,7 +72,7 @@ let # Use nabam's mainline-based rockchip kernel (linux_latest with rockchip config). # Merge our RK806 options and append compatibility patches directly. - baseKernel = inputs.rockchip.legacyPackages.aarch64-linux.kernel_linux_latest_rockchip; + baseKernel = inputs.rockchip.legacyPackages.aarch64-linux.kernel_linux_latest_rockchip or inputs.rockchip.legacyPackages.aarch64-linux.kernel_linux_latest_rockchip_unstable; customKernel = baseKernel.kernel.override (prev: { structuredExtraConfig = (prev.structuredExtraConfig or {}) // (with lib.kernel; { MFD_RK8XX_SPI = yes; # RK806 PMIC MFD driver via SPI From 0d32fd112940e7780432659045302a3549705d7c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 26 Feb 2026 20:46:36 +0000 Subject: [PATCH 08/11] fix(nanopc-t6): restore focused rk806 compat patches Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com> --- docs/nanopc-T6.md | 2 +- nix/builders/nanopc-t6/base.nix | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/nanopc-T6.md b/docs/nanopc-T6.md index 71635b8..e824cf3 100644 --- a/docs/nanopc-T6.md +++ b/docs/nanopc-T6.md @@ -69,7 +69,7 @@ For the current NanoPC-T6 image path, the kernel comes from `nabam/nixos-rockchi 1. Keep mainline/nabam kernel and align reset configuration with FriendlyARM semantics. 2. Keep OP-TEE `firmware`/`reserved-memory` nodes in DTS patch. -3. Patch in the full FriendlyARM `rk806-core.c` module stack on top of the nabam kernel path (including `rk806-core.c`, `rk806.h`, and RK806 SPI wiring), while keeping `pmic-reset-func = <1>` in DTS. +3. Mirror FriendlyARM reset semantics by setting `pmic-reset-func = <1>` in DTS and applying focused RK806 compatibility patches in `rk8xx-core.c`. ### 3. Mask ROM Button (SARADC) diff --git a/nix/builders/nanopc-t6/base.nix b/nix/builders/nanopc-t6/base.nix index c4dc0e6..8aacc22 100644 --- a/nix/builders/nanopc-t6/base.nix +++ b/nix/builders/nanopc-t6/base.nix @@ -85,8 +85,12 @@ patch = ./rk3588-nanopc-t6.dtsi.patch; } { - name = "rk806-full-module.patch"; - patch = ./rk806-full-module.patch; + name = "rk806-friendlyarm-reset-compat.patch"; + patch = ./rk806-friendlyarm-reset-compat.patch; + } + { + name = "rk806-disable-slave-restart.patch"; + patch = ./rk806-disable-slave-restart.patch; } ]; }); From 4a57979ca351fc54f87aecfb4d1f3cd433c1e676 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Mar 2026 19:54:18 +0000 Subject: [PATCH 09/11] feat(nanopc-t6): conditionally backport u-boot rk806 reset-mode support Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com> --- docs/nanopc-T6.md | 1 + nix/builders/nanopc-t6/base.nix | 6 ++ .../nanopc-t6/uboot-rk806-reset-mode.patch | 102 ++++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 nix/builders/nanopc-t6/uboot-rk806-reset-mode.patch diff --git a/docs/nanopc-T6.md b/docs/nanopc-T6.md index e824cf3..3888bfb 100644 --- a/docs/nanopc-T6.md +++ b/docs/nanopc-T6.md @@ -70,6 +70,7 @@ For the current NanoPC-T6 image path, the kernel comes from `nabam/nixos-rockchi 1. Keep mainline/nabam kernel and align reset configuration with FriendlyARM semantics. 2. Keep OP-TEE `firmware`/`reserved-memory` nodes in DTS patch. 3. Mirror FriendlyARM reset semantics by setting `pmic-reset-func = <1>` in DTS and applying focused RK806 compatibility patches in `rk8xx-core.c`. +4. Backport U-Boot commit `1961bba4bc34` (`power: rk8xx: allow to customize RK806 reset mode`) so `rockchip,reset-mode` is applied in bootloader PMIC init too. ### 3. Mask ROM Button (SARADC) diff --git a/nix/builders/nanopc-t6/base.nix b/nix/builders/nanopc-t6/base.nix index 8aacc22..224084f 100644 --- a/nix/builders/nanopc-t6/base.nix +++ b/nix/builders/nanopc-t6/base.nix @@ -30,6 +30,12 @@ uBootNanoPCT6 = super.buildUBoot { defconfig = "nanopc-t6-rk3588_defconfig"; extraMeta.platforms = [ "aarch64-linux" ]; + postPatch = '' + # Backport U-Boot RK806 reset-mode support only when missing. + if ! grep -q "RK806_RST_FUN_MSK" include/power/rk8xx_pmic.h; then + patch -p1 < ${./uboot-rk806-reset-mode.patch} + fi + ''; extraMakeFlags = [ "BL31=${pkgs.armTrustedFirmwareRK3588}/bl31.elf" "ROCKCHIP_TPL=${pkgs.rkbin.TPL_RK3588}" diff --git a/nix/builders/nanopc-t6/uboot-rk806-reset-mode.patch b/nix/builders/nanopc-t6/uboot-rk806-reset-mode.patch new file mode 100644 index 0000000..f212c75 --- /dev/null +++ b/nix/builders/nanopc-t6/uboot-rk806-reset-mode.patch @@ -0,0 +1,102 @@ +From 1961bba4bc341573db98a6fd34eac771e914681f Mon Sep 17 00:00:00 2001 +From: Quentin Schulz +Date: Wed, 13 Aug 2025 16:07:43 +0200 +Subject: [PATCH] power: rk8xx: allow to customize RK806 reset mode + +The RK806 PMIC has a bitfield for configuring the restart/reset behavior +(which I assume Rockchip calls "function") whenever the PMIC is reset +either programmatically (c.f. DEV_RST in the datasheet) or via PWRCTRL +or RESETB pins. + +For RK806, the following values are possible for RST_FUN: + +0b00 means "Restart PMU" +0b01 means "Reset all the power off reset registers, forcing +the state to switch to ACTIVE mode" +0b10 means "Reset all the power off reset registers, forcing +the state to switch to ACTIVE mode, and simultaneously +pull down the RESETB PIN for 5mS before releasing" +0b11 means the same as for 0b10 just above. + +This adds the appropriate logic in the driver to parse the new +rockchip,reset-mode DT property to pass this information. It just +happens that the values in the binding match the values to write in the +bitfield so no mapping is necessary. + +For backward compatibility reasons, if the property is missing we set it +to 0b10 (i.e. BIT(7)) like before this commit was merged instead of +leaving it untouched like in the kernel driver. + +Note that this does nothing useful for U-Boot at the moment as the ways +to reset the device (e.g. via `reset` command) doesn't interact with the +RK8xx PMIC and simply does a CPU reset. +Considering the upstream Linux kernel left this register untouched until +(assumed) v6.17[1], this is useful for cases in which the U-Boot +bootloader has this patch (and running with a DT with +rockchip,reset-mode property set) and running an upstream kernel before +(assumed) v6.17, or alternatively later without the property in the +kernel DT. + +[1] https://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git/commit/?id=87b48d86b77686013f5c2a8866ed299312b671db + +Signed-off-by: Quentin Schulz +Reviewed-by: Kever Yang +--- + drivers/power/pmic/rk8xx.c | 21 ++++++++++++--------- + include/power/rk8xx_pmic.h | 2 ++ + 2 files changed, 14 insertions(+), 9 deletions(-) + +diff --git a/drivers/power/pmic/rk8xx.c b/drivers/power/pmic/rk8xx.c +index 3bc696d4caaa..d11f7a7886e8 100644 +--- a/drivers/power/pmic/rk8xx.c ++++ b/drivers/power/pmic/rk8xx.c +@@ -89,11 +89,6 @@ void rk8xx_off_for_plugin(struct udevice *dev) + } + } + +-static struct reg_data rk806_init_reg[] = { +-/* RST_FUN */ +-{ RK806_REG_SYS_CFG3, BIT(7), GENMASK(7, 6)}, +-}; +- + static struct reg_data rk817_init_reg[] = { + /* enable the under-voltage protection, + * the under-voltage protection will shutdown the LDO3 and reset the PMIC +@@ -306,12 +301,20 @@ static int rk8xx_probe(struct udevice *dev) + value = (power_en2 & 0x0f) | ((power_en3 & 0x0f) << 4); + pmic_reg_write(dev, RK817_POWER_EN_SAVE1, value); + break; +-case RK806_ID: ++case RK806_ID: { ++u32 rst_fun = 2; ++ + on_source = RK806_ON_SOURCE; + off_source = RK806_OFF_SOURCE; +-init_data = rk806_init_reg; +-init_data_num = ARRAY_SIZE(rk806_init_reg); +-break; ++ ++ret = dev_read_u32(dev, "rockchip,reset-mode", &rst_fun); ++if (ret) ++debug("rockchip,reset-mode property missing, defaulting to %d\n", ++ rst_fun); ++ ++pmic_clrsetbits(dev, RK806_REG_SYS_CFG3, RK806_RST_FUN_MSK, ++FIELD_PREP(RK806_RST_FUN_MSK, rst_fun)); ++break; } + default: + printf("Unknown PMIC: RK%x!!\n", show_variant); + return -EINVAL; +diff --git a/include/power/rk8xx_pmic.h b/include/power/rk8xx_pmic.h +index 31221aa46b60..913b6ebe6d96 100644 +--- a/include/power/rk8xx_pmic.h ++++ b/include/power/rk8xx_pmic.h +@@ -212,6 +212,8 @@ enum { + #define RK817_POWER_EN_SAVE00x99 + #define RK817_POWER_EN_SAVE10xa4 + ++#define RK806_RST_FUN_MSKGENMASK(7, 6) ++ + #define RK806_POWER_EN(x)(0x00 + (x)) + /* POWER_ENx register lower 4 bits are write-protected unless the associated top bit is set */ + #define RK806_POWER_EN_CLRSETBITS(bit, val)(((val) << (bit)) | (1 << ((bit) + 4))) From 72f3029939487f62747564bf1c1151fb59339915 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Mar 2026 20:09:24 +0000 Subject: [PATCH 10/11] fix(nanopc-t6): apply u-boot reset-mode semantics in linux patch only Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com> --- docs/nanopc-T6.md | 3 +- nix/builders/nanopc-t6/base.nix | 6 -- .../rk806-friendlyarm-reset-compat.patch | 7 +- .../nanopc-t6/uboot-rk806-reset-mode.patch | 102 ------------------ 4 files changed, 5 insertions(+), 113 deletions(-) delete mode 100644 nix/builders/nanopc-t6/uboot-rk806-reset-mode.patch diff --git a/docs/nanopc-T6.md b/docs/nanopc-T6.md index 3888bfb..084eedf 100644 --- a/docs/nanopc-T6.md +++ b/docs/nanopc-T6.md @@ -69,8 +69,7 @@ For the current NanoPC-T6 image path, the kernel comes from `nabam/nixos-rockchi 1. Keep mainline/nabam kernel and align reset configuration with FriendlyARM semantics. 2. Keep OP-TEE `firmware`/`reserved-memory` nodes in DTS patch. -3. Mirror FriendlyARM reset semantics by setting `pmic-reset-func = <1>` in DTS and applying focused RK806 compatibility patches in `rk8xx-core.c`. -4. Backport U-Boot commit `1961bba4bc34` (`power: rk8xx: allow to customize RK806 reset mode`) so `rockchip,reset-mode` is applied in bootloader PMIC init too. +3. Apply focused RK806 compatibility patches in Linux `rk8xx-core.c` so `rockchip,reset-mode` is honored there and defaults to mode `2` when the property is missing (matching the U-Boot commit behavior). ### 3. Mask ROM Button (SARADC) diff --git a/nix/builders/nanopc-t6/base.nix b/nix/builders/nanopc-t6/base.nix index 224084f..8aacc22 100644 --- a/nix/builders/nanopc-t6/base.nix +++ b/nix/builders/nanopc-t6/base.nix @@ -30,12 +30,6 @@ uBootNanoPCT6 = super.buildUBoot { defconfig = "nanopc-t6-rk3588_defconfig"; extraMeta.platforms = [ "aarch64-linux" ]; - postPatch = '' - # Backport U-Boot RK806 reset-mode support only when missing. - if ! grep -q "RK806_RST_FUN_MSK" include/power/rk8xx_pmic.h; then - patch -p1 < ${./uboot-rk806-reset-mode.patch} - fi - ''; extraMakeFlags = [ "BL31=${pkgs.armTrustedFirmwareRK3588}/bl31.elf" "ROCKCHIP_TPL=${pkgs.rkbin.TPL_RK3588}" diff --git a/nix/builders/nanopc-t6/rk806-friendlyarm-reset-compat.patch b/nix/builders/nanopc-t6/rk806-friendlyarm-reset-compat.patch index 850530f..81ee063 100644 --- a/nix/builders/nanopc-t6/rk806-friendlyarm-reset-compat.patch +++ b/nix/builders/nanopc-t6/rk806-friendlyarm-reset-compat.patch @@ -1,13 +1,14 @@ --- a/drivers/mfd/rk8xx-core.c +++ b/drivers/mfd/rk8xx-core.c -@@ -716,8 +716,7 @@ int rk8xx_probe(struct device *dev, int variant, unsigned int irq, struct regma +@@ -716,8 +716,8 @@ int rk8xx_probe(struct device *dev, int variant, unsigned int irq, struct regma dual_support = IRQF_SHARED; - ret = device_property_read_u32(dev, "rockchip,reset-mode", &rst_fun); - if (ret) - break; -+ if (device_property_read_u32(dev, "pmic-reset-func", &rst_fun)) -+ rst_fun = 0; ++ ret = device_property_read_u32(dev, "rockchip,reset-mode", &rst_fun); ++ if (ret) ++ rst_fun = 2; ret = regmap_update_bits(rk808->regmap, RK806_SYS_CFG3, RK806_RST_FUN_MSK, FIELD_PREP(RK806_RST_FUN_MSK, rst_fun)); diff --git a/nix/builders/nanopc-t6/uboot-rk806-reset-mode.patch b/nix/builders/nanopc-t6/uboot-rk806-reset-mode.patch deleted file mode 100644 index f212c75..0000000 --- a/nix/builders/nanopc-t6/uboot-rk806-reset-mode.patch +++ /dev/null @@ -1,102 +0,0 @@ -From 1961bba4bc341573db98a6fd34eac771e914681f Mon Sep 17 00:00:00 2001 -From: Quentin Schulz -Date: Wed, 13 Aug 2025 16:07:43 +0200 -Subject: [PATCH] power: rk8xx: allow to customize RK806 reset mode - -The RK806 PMIC has a bitfield for configuring the restart/reset behavior -(which I assume Rockchip calls "function") whenever the PMIC is reset -either programmatically (c.f. DEV_RST in the datasheet) or via PWRCTRL -or RESETB pins. - -For RK806, the following values are possible for RST_FUN: - -0b00 means "Restart PMU" -0b01 means "Reset all the power off reset registers, forcing -the state to switch to ACTIVE mode" -0b10 means "Reset all the power off reset registers, forcing -the state to switch to ACTIVE mode, and simultaneously -pull down the RESETB PIN for 5mS before releasing" -0b11 means the same as for 0b10 just above. - -This adds the appropriate logic in the driver to parse the new -rockchip,reset-mode DT property to pass this information. It just -happens that the values in the binding match the values to write in the -bitfield so no mapping is necessary. - -For backward compatibility reasons, if the property is missing we set it -to 0b10 (i.e. BIT(7)) like before this commit was merged instead of -leaving it untouched like in the kernel driver. - -Note that this does nothing useful for U-Boot at the moment as the ways -to reset the device (e.g. via `reset` command) doesn't interact with the -RK8xx PMIC and simply does a CPU reset. -Considering the upstream Linux kernel left this register untouched until -(assumed) v6.17[1], this is useful for cases in which the U-Boot -bootloader has this patch (and running with a DT with -rockchip,reset-mode property set) and running an upstream kernel before -(assumed) v6.17, or alternatively later without the property in the -kernel DT. - -[1] https://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git/commit/?id=87b48d86b77686013f5c2a8866ed299312b671db - -Signed-off-by: Quentin Schulz -Reviewed-by: Kever Yang ---- - drivers/power/pmic/rk8xx.c | 21 ++++++++++++--------- - include/power/rk8xx_pmic.h | 2 ++ - 2 files changed, 14 insertions(+), 9 deletions(-) - -diff --git a/drivers/power/pmic/rk8xx.c b/drivers/power/pmic/rk8xx.c -index 3bc696d4caaa..d11f7a7886e8 100644 ---- a/drivers/power/pmic/rk8xx.c -+++ b/drivers/power/pmic/rk8xx.c -@@ -89,11 +89,6 @@ void rk8xx_off_for_plugin(struct udevice *dev) - } - } - --static struct reg_data rk806_init_reg[] = { --/* RST_FUN */ --{ RK806_REG_SYS_CFG3, BIT(7), GENMASK(7, 6)}, --}; -- - static struct reg_data rk817_init_reg[] = { - /* enable the under-voltage protection, - * the under-voltage protection will shutdown the LDO3 and reset the PMIC -@@ -306,12 +301,20 @@ static int rk8xx_probe(struct udevice *dev) - value = (power_en2 & 0x0f) | ((power_en3 & 0x0f) << 4); - pmic_reg_write(dev, RK817_POWER_EN_SAVE1, value); - break; --case RK806_ID: -+case RK806_ID: { -+u32 rst_fun = 2; -+ - on_source = RK806_ON_SOURCE; - off_source = RK806_OFF_SOURCE; --init_data = rk806_init_reg; --init_data_num = ARRAY_SIZE(rk806_init_reg); --break; -+ -+ret = dev_read_u32(dev, "rockchip,reset-mode", &rst_fun); -+if (ret) -+debug("rockchip,reset-mode property missing, defaulting to %d\n", -+ rst_fun); -+ -+pmic_clrsetbits(dev, RK806_REG_SYS_CFG3, RK806_RST_FUN_MSK, -+FIELD_PREP(RK806_RST_FUN_MSK, rst_fun)); -+break; } - default: - printf("Unknown PMIC: RK%x!!\n", show_variant); - return -EINVAL; -diff --git a/include/power/rk8xx_pmic.h b/include/power/rk8xx_pmic.h -index 31221aa46b60..913b6ebe6d96 100644 ---- a/include/power/rk8xx_pmic.h -+++ b/include/power/rk8xx_pmic.h -@@ -212,6 +212,8 @@ enum { - #define RK817_POWER_EN_SAVE00x99 - #define RK817_POWER_EN_SAVE10xa4 - -+#define RK806_RST_FUN_MSKGENMASK(7, 6) -+ - #define RK806_POWER_EN(x)(0x00 + (x)) - /* POWER_ENx register lower 4 bits are write-protected unless the associated top bit is set */ - #define RK806_POWER_EN_CLRSETBITS(bit, val)(((val) << (bit)) | (1 << ((bit) + 4))) From 216242602447f91db46e50d133eb0c6bb781fa3d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Mar 2026 20:30:19 +0000 Subject: [PATCH 11/11] chore(nanopc-t6): validate all active patches and drop stale unused patch Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com> --- .../rk3588-nanopi6-common.dtsi.patch | 27 ------------------- 1 file changed, 27 deletions(-) delete mode 100644 nix/builders/nanopc-t6/rk3588-nanopi6-common.dtsi.patch diff --git a/nix/builders/nanopc-t6/rk3588-nanopi6-common.dtsi.patch b/nix/builders/nanopc-t6/rk3588-nanopi6-common.dtsi.patch deleted file mode 100644 index 56d6daf..0000000 --- a/nix/builders/nanopc-t6/rk3588-nanopi6-common.dtsi.patch +++ /dev/null @@ -1,27 +0,0 @@ ---- a/arch/arm64/boot/dts/rockchip/rk3588-nanopi6-common.dtsi -+++ b/arch/arm64/boot/dts/rockchip/rk3588-nanopi6-common.dtsi -@@ -124,6 +124,24 @@ pwm_backlight: pwm-backlight { - test-power { - status = "okay"; - }; -+ -+ firmware { -+ optee { -+ compatible = "linaro,optee-tz"; -+ method = "smc"; -+ }; -+ }; -+ -+ reserved-memory { -+ #address-cells = <2>; -+ #size-cells = <2>; -+ ranges; -+ -+ optee@30000000 { -+ reg = <0x0 0x30000000 0x0 0x2000000>; -+ no-map; -+ }; -+ }; - }; - - &av1d_mmu {