diff --git a/docs/nanopc-T6.md b/docs/nanopc-T6.md index e537774..084eedf 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 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. 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) @@ -97,7 +97,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..8aacc22 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 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 @@ -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/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 { 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..81ee063 100644 --- a/nix/builders/nanopc-t6/rk806-friendlyarm-reset-compat.patch +++ b/nix/builders/nanopc-t6/rk806-friendlyarm-reset-compat.patch @@ -1,14 +1,14 @@ --- 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); - if (ret) - break; -+ if (device_property_read_u32(dev, "pmic-reset-func", &rst_fun) && -+ device_property_read_u32(dev, "rockchip,reset-mode", &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/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