From c7b16d749d1746e3ab081f8550c3aba252969d26 Mon Sep 17 00:00:00 2001 From: Dan Johansen Date: Mon, 1 Jun 2020 23:14:16 +0200 Subject: [PATCH] add suspend to rk3399/PBP --- .../soc/rockchip/rockchip-pm-config.txt | 40 +++ .../boot/dts/rockchip/rk3399-pinebook-pro.dts | 35 +++ arch/arm64/boot/dts/rockchip/rk3399.dtsi | 24 ++ drivers/firmware/Kconfig | 7 + drivers/firmware/Makefile | 1 + drivers/firmware/rockchip_sip.c | 263 ++++++++++++++++++ drivers/regulator/core.c | 49 +++- drivers/soc/rockchip/Kconfig | 6 + drivers/soc/rockchip/Makefile | 1 + drivers/soc/rockchip/rockchip_pm_config.c | 213 ++++++++++++++ include/dt-bindings/suspend/rockchip-rk3399.h | 60 ++++ include/linux/regulator/driver.h | 3 + include/linux/rockchip/rockchip_sip.h | 149 ++++++++++ 13 files changed, 847 insertions(+), 4 deletions(-) create mode 100644 Documentation/devicetree/bindings/soc/rockchip/rockchip-pm-config.txt create mode 100644 drivers/firmware/rockchip_sip.c create mode 100644 drivers/soc/rockchip/rockchip_pm_config.c create mode 100644 include/dt-bindings/suspend/rockchip-rk3399.h create mode 100644 include/linux/rockchip/rockchip_sip.h diff --git a/Documentation/devicetree/bindings/soc/rockchip/rockchip-pm-config.txt b/Documentation/devicetree/bindings/soc/rockchip/rockchip-pm-config.txt new file mode 100644 index 000000000000..e15af33d3a9f --- /dev/null +++ b/Documentation/devicetree/bindings/soc/rockchip/rockchip-pm-config.txt @@ -0,0 +1,40 @@ +* the suspend mode config + +- compatible: "rockchip,pm-config" + Compatibility with rk3399 + +- rockchip,sleep-mode-config : the sleep mode config, + ARMOFF, OSC disabled ... + +- rockchip,wakeup-config: the wake up sourece enable. + GPIO, USB, SD... + +- rockchip,pwm-regulator-config: the pwm regulator name. + +Example: + rockchip_suspend: rockchip_suspend { + compatible = "rockchip,pm-rk3399"; + status = "okay"; + rockchip,sleep-mode-config = < + (0 + | RKPM_SLP_ARMPD + | RKPM_SLP_PERILPPD + | RKPM_SLP_DDR_RET + | RKPM_SLP_PLLPD + | RKPM_SLP_OSC_DIS + | RKPM_SLP_CENTER_PD + | RKPM_SLP_AP_PWROFF + ) + >; + rockchip,wakeup-config = < + (0 | + RKPM_GPIO_WKUP_EN | + RKPM_PWM_WKUP_EN) + >; + rockchip,pwm-regulator-config = < + (0 | + PWM2_REGULATOR_EN + ) + >; + }; + diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts index c49982dfd8fc..9643cae1537a 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts +++ b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts @@ -19,9 +19,15 @@ / { compatible = "pine64,pinebook-pro", "rockchip,rk3399"; chosen { + bootargs = "earlycon=uart8250,mmio32,0xff1a0000"; stdout-path = "serial2:1500000n8"; }; + memory { + device_type = "memory"; + reg = <0x0 0x00200000 0x0 0xf7e00000>; + }; + backlight: edp-backlight { compatible = "pwm-backlight"; power-supply = <&vcc_12v>; @@ -125,6 +131,12 @@ sdio_pwrseq: sdio-pwrseq { reset-gpios = <&gpio0 RK_PB2 GPIO_ACTIVE_LOW>; }; + /* first 128k(0xff8d0000~0xff8f0000) for ddr and ATF */ + sram@ff8d0000 { + compatible = "mmio-sram"; + reg = <0x0 0xff8d0000 0x0 0x20000>; /* 128k */ + }; + /* Audio components */ es8316-sound { compatible = "simple-audio-card"; @@ -906,6 +918,29 @@ &pwm2 { status = "okay"; }; +&rockchip_suspend { + status = "okay"; + rockchip,sleep-debug-en = <1>; + rockchip,sleep-mode-config = < + (0 + | RKPM_SLP_ARMPD + | RKPM_SLP_PERILPPD + | RKPM_SLP_DDR_RET + | RKPM_SLP_PLLPD + | RKPM_SLP_CENTER_PD + | RKPM_SLP_AP_PWROFF + ) + >; + rockchip,pwm-regulator-config = < + (0 + | PWM2_REGULATOR_EN + ) + >; + rockchip,power-ctrl = + <&gpio1 RK_PC1 GPIO_ACTIVE_HIGH>, + <&gpio1 RK_PB6 GPIO_ACTIVE_HIGH>; +}; + &saradc { vref-supply = <&vcca1v8_s3>; status = "okay"; diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi index 1448f358ed0a..a91fc5c8b43b 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi @@ -9,6 +9,7 @@ #include #include #include +#include #include / { @@ -2652,4 +2653,27 @@ pcie_clkreqnb_cpm: pci-clkreqnb-cpm { }; }; + + rockchip_suspend: rockchip-suspend { + compatible = "rockchip,pm-rk3399"; + status = "disabled"; + rockchip,sleep-debug-en = <0>; + rockchip,virtual-poweroff = <0>; + rockchip,sleep-mode-config = < + (0 + | RKPM_SLP_ARMPD + | RKPM_SLP_PERILPPD + | RKPM_SLP_DDR_RET + | RKPM_SLP_PLLPD + | RKPM_SLP_OSC_DIS + | RKPM_SLP_CENTER_PD + | RKPM_SLP_AP_PWROFF + ) + >; + rockchip,wakeup-config = < + (0 + | RKPM_GPIO_WKUP_EN + ) + >; + }; }; diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 8007d4aa76dc..37f8039a5e27 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -296,6 +296,13 @@ other manufacturing data and also utilize the Entropy Bit Generator for hardware random number generation. +config ROCKCHIP_SIP + bool "Rockchip SIP interface" + depends on ARM64 && ARM_PSCI_FW + help + Say Y here if you want to enable SIP callbacks for Rockchip platforms + This option enables support for communicating with the ATF. + source "drivers/firmware/arm_ffa/Kconfig" source "drivers/firmware/broadcom/Kconfig" source "drivers/firmware/google/Kconfig" diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index e9fb838af4df..575f45d55939 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -29,6 +29,7 @@ obj-y += meson/ obj-$(CONFIG_GOOGLE_FIRMWARE) += google/ obj-$(CONFIG_EFI) += efi/ obj-$(CONFIG_UEFI_CPER) += efi/ +obj-$(CONFIG_ROCKCHIP_SIP) += rockchip_sip.o obj-y += imx/ obj-y += tegra/ obj-y += xilinx/ diff --git a/drivers/firmware/rockchip_sip.c b/drivers/firmware/rockchip_sip.c new file mode 100644 index 000000000000..6844c5bad5b1 --- /dev/null +++ b/drivers/firmware/rockchip_sip.c @@ -0,0 +1,263 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Copyright (C) 2016, Fuzhou Rockchip Electronics Co., Ltd + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_64BIT +#define PSCI_FN_NATIVE(version, name) PSCI_##version##_FN64_##name +#else +#define PSCI_FN_NATIVE(version, name) PSCI_##version##_FN_##name +#endif + +#define SIZE_PAGE(n) ((n) << 12) + +static struct arm_smccc_res __invoke_sip_fn_smc(unsigned long function_id, + unsigned long arg0, + unsigned long arg1, + unsigned long arg2) +{ + struct arm_smccc_res res; + + arm_smccc_smc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res); + return res; +} + +struct arm_smccc_res sip_smc_ddr_cfg(u32 arg0, u32 arg1, u32 arg2) +{ + return __invoke_sip_fn_smc(SIP_DDR_CFG, arg0, arg1, arg2); +} + +struct arm_smccc_res sip_smc_get_atf_version(void) +{ + return __invoke_sip_fn_smc(SIP_ATF_VERSION, 0, 0, 0); +} + +struct arm_smccc_res sip_smc_get_sip_version(void) +{ + return __invoke_sip_fn_smc(SIP_SIP_VERSION, 0, 0, 0); +} + +int sip_smc_set_suspend_mode(u32 ctrl, u32 config1, u32 config2) +{ + struct arm_smccc_res res; + + res = __invoke_sip_fn_smc(SIP_SUSPEND_MODE, ctrl, config1, config2); + return res.a0; +} + +int sip_smc_virtual_poweroff(void) +{ + struct arm_smccc_res res; + + res = __invoke_sip_fn_smc(PSCI_FN_NATIVE(1_0, SYSTEM_SUSPEND), 0, 0, 0); + return res.a0; +} + +struct arm_smccc_res sip_smc_request_share_mem(u32 page_num, + share_page_type_t page_type) +{ + struct arm_smccc_res res; + unsigned long share_mem_phy; + + res = __invoke_sip_fn_smc(SIP_SHARE_MEM, page_num, page_type, 0); + if (IS_SIP_ERROR(res.a0)) + goto error; + + share_mem_phy = res.a1; + res.a1 = (unsigned long)ioremap(share_mem_phy, SIZE_PAGE(page_num)); + +error: + return res; +} + +struct arm_smccc_res sip_smc_mcu_el3fiq(u32 arg0, u32 arg1, u32 arg2) +{ + return __invoke_sip_fn_smc(SIP_MCU_EL3FIQ_CFG, arg0, arg1, arg2); +} + +/************************** fiq debugger **************************************/ +#ifdef CONFIG_ARM64 +#define SIP_UARTDBG_FN SIP_UARTDBG_CFG64 +#else +#define SIP_UARTDBG_FN SIP_UARTDBG_CFG +#endif + +static int fiq_sip_enabled; +static int fiq_target_cpu; +static phys_addr_t ft_fiq_mem_phy; +static void __iomem *ft_fiq_mem_base; +static void (*sip_fiq_debugger_uart_irq_tf)(struct pt_regs _pt_regs, + unsigned long cpu); +int sip_fiq_debugger_is_enabled(void) +{ + return fiq_sip_enabled; +} + +static struct pt_regs sip_fiq_debugger_get_pt_regs(void *reg_base, + unsigned long sp_el1) +{ + struct pt_regs fiq_pt_regs; + +#ifdef CONFIG_ARM64 + /* copy cpu context */ + memcpy(&fiq_pt_regs, reg_base, 8 * 31); + + /* copy pstate */ + memcpy(&fiq_pt_regs.pstate, reg_base + 0x110, 8); + + /* EL1 mode */ + if (fiq_pt_regs.pstate & 0x10) + memcpy(&fiq_pt_regs.sp, reg_base + 0xf8, 8); + /* EL0 mode */ + else + fiq_pt_regs.sp = sp_el1; + + /* copy pc */ + memcpy(&fiq_pt_regs.pc, reg_base + 0x118, 8); +#else + struct sm_nsec_ctx *nsec_ctx = reg_base; + + fiq_pt_regs.ARM_r0 = nsec_ctx->r0; + fiq_pt_regs.ARM_r1 = nsec_ctx->r1; + fiq_pt_regs.ARM_r2 = nsec_ctx->r2; + fiq_pt_regs.ARM_r3 = nsec_ctx->r3; + fiq_pt_regs.ARM_r4 = nsec_ctx->r4; + fiq_pt_regs.ARM_r5 = nsec_ctx->r5; + fiq_pt_regs.ARM_r6 = nsec_ctx->r6; + fiq_pt_regs.ARM_r7 = nsec_ctx->r7; + fiq_pt_regs.ARM_r8 = nsec_ctx->r8; + fiq_pt_regs.ARM_r9 = nsec_ctx->r9; + fiq_pt_regs.ARM_r10 = nsec_ctx->r10; + fiq_pt_regs.ARM_fp = nsec_ctx->r11; + fiq_pt_regs.ARM_ip = nsec_ctx->r12; + fiq_pt_regs.ARM_sp = nsec_ctx->svc_sp; + fiq_pt_regs.ARM_lr = nsec_ctx->svc_lr; + fiq_pt_regs.ARM_pc = nsec_ctx->mon_lr; + fiq_pt_regs.ARM_cpsr = nsec_ctx->mon_spsr; +#endif + + return fiq_pt_regs; +} + +static void sip_fiq_debugger_uart_irq_tf_cb(unsigned long sp_el1, + unsigned long offset, + unsigned long cpu) +{ + struct pt_regs fiq_pt_regs; + char *cpu_context; + + /* calling fiq handler */ + if (ft_fiq_mem_base) { + cpu_context = (char *)ft_fiq_mem_base + offset; + fiq_pt_regs = sip_fiq_debugger_get_pt_regs(cpu_context, sp_el1); + sip_fiq_debugger_uart_irq_tf(fiq_pt_regs, cpu); + } + + /* fiq handler done, return to EL3(then EL3 return to EL1 entry) */ + __invoke_sip_fn_smc(SIP_UARTDBG_FN, 0, 0, UARTDBG_CFG_OSHDL_TO_OS); +} + +int sip_fiq_debugger_uart_irq_tf_init(u32 irq_id, void *callback_fn) +{ + struct arm_smccc_res res; + + fiq_target_cpu = 0; + + /* init fiq debugger callback */ + sip_fiq_debugger_uart_irq_tf = callback_fn; + res = __invoke_sip_fn_smc(SIP_UARTDBG_FN, irq_id, + (unsigned long)sip_fiq_debugger_uart_irq_tf_cb, + UARTDBG_CFG_INIT); + if (IS_SIP_ERROR(res.a0)) { + pr_err("%s error: %d\n", __func__, (int)res.a0); + return res.a0; + } + + /* share memory ioremap */ + if (!ft_fiq_mem_base) { + ft_fiq_mem_phy = res.a1; + ft_fiq_mem_base = ioremap(ft_fiq_mem_phy, + FIQ_UARTDBG_SHARE_MEM_SIZE); + if (!ft_fiq_mem_base) { + pr_err("%s: share memory ioremap failed\n", __func__); + return -ENOMEM; + } + } + + fiq_sip_enabled = 1; + + return SIP_RET_SUCCESS; +} + +int sip_fiq_debugger_switch_cpu(u32 cpu) +{ + struct arm_smccc_res res; + + fiq_target_cpu = cpu; + res = __invoke_sip_fn_smc(SIP_UARTDBG_FN, cpu_logical_map(cpu), + 0, UARTDBG_CFG_OSHDL_CPUSW); + return res.a0; +} + +void sip_fiq_debugger_enable_debug(bool enable) +{ + unsigned long val; + + val = enable ? UARTDBG_CFG_OSHDL_DEBUG_ENABLE : + UARTDBG_CFG_OSHDL_DEBUG_DISABLE; + + __invoke_sip_fn_smc(SIP_UARTDBG_FN, 0, 0, val); +} + +int sip_fiq_debugger_set_print_port(u32 port_phyaddr, u32 baudrate) +{ + struct arm_smccc_res res; + + res = __invoke_sip_fn_smc(SIP_UARTDBG_FN, port_phyaddr, baudrate, + UARTDBG_CFG_PRINT_PORT); + return res.a0; +} + +int sip_fiq_debugger_request_share_memory(void) +{ + struct arm_smccc_res res; + + /* request page share memory */ + res = sip_smc_request_share_mem(FIQ_UARTDBG_PAGE_NUMS, + SHARE_PAGE_TYPE_UARTDBG); + if (IS_SIP_ERROR(res.a0)) + return res.a0; + + return SIP_RET_SUCCESS; +} + +int sip_fiq_debugger_get_target_cpu(void) +{ + return fiq_target_cpu; +} + +void sip_fiq_debugger_enable_fiq(bool enable, uint32_t tgt_cpu) +{ + u32 en; + + fiq_target_cpu = tgt_cpu; + en = enable ? UARTDBG_CFG_FIQ_ENABEL : UARTDBG_CFG_FIQ_DISABEL; + __invoke_sip_fn_smc(SIP_UARTDBG_FN, tgt_cpu, 0, en); +} + diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 7486f6e4e613..d46a267ea309 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -5268,6 +5268,14 @@ void regulator_unregister(struct regulator_dev *rdev) EXPORT_SYMBOL_GPL(regulator_unregister); #ifdef CONFIG_SUSPEND +static inline int can_enable(struct regulator_dev *rdev) { + return rdev->ena_pin || rdev->desc->ops->enable; +} + +static inline int can_disable(struct regulator_dev *rdev) { + return rdev->ena_pin || rdev->desc->ops->disable; +} + /** * regulator_suspend - prepare regulators for system wide suspend * @dev: ``&struct device`` pointer that is passed to _regulator_suspend() @@ -5430,6 +5438,21 @@ static int regulator_suspend(struct device *dev) regulator_lock(rdev); ret = __suspend_set_state(rdev, rstate); + + if (rstate->enabled == ENABLE_IN_SUSPEND && can_enable(rdev)) { + if (!rdev->desc->ops->set_suspend_enable) { + rdev->resume_state = _regulator_is_enabled(rdev); + rdev_info(rdev, "Entering suspend %d, enabling forcibly, was %s\n", state, rdev->resume_state ? "on" : "off"); + ret = _regulator_do_enable(rdev); + } + } else if (rstate->enabled == DISABLE_IN_SUSPEND && can_disable(rdev)) { + if (!rdev->desc->ops->set_suspend_disable) { + rdev->resume_state = _regulator_is_enabled(rdev); + rdev_info(rdev, "Entering suspend %d, disabling forcibly, was %s\n", state, rdev->resume_state ? "on" : "off"); + ret = _regulator_do_disable(rdev); + } + } + regulator_unlock(rdev); return ret; @@ -5452,9 +5475,19 @@ static int regulator_resume(struct device *dev) regulator_lock(rdev); - if (rstate->enabled == ENABLE_IN_SUSPEND || - rstate->enabled == DISABLE_IN_SUSPEND) - ret = rdev->desc->ops->resume(rdev); + if (rstate->enabled == ENABLE_IN_SUSPEND || rstate->enabled == DISABLE_IN_SUSPEND) { + if (rdev->desc->ops->resume) { + ret = rdev->desc->ops->resume(rdev); + } else if ((rstate->enabled == ENABLE_IN_SUSPEND && !rdev->desc->ops->set_suspend_enable) || + (rstate->enabled == DISABLE_IN_SUSPEND && !rdev->desc->ops->set_suspend_disable)) { + rdev_info(rdev, "Resuming, restoring state to %s\n", rdev->resume_state ? "on" : "off"); + if (rdev->resume_state && can_enable(rdev)) { + ret = _regulator_do_enable(rdev); + } else if (!rdev->resume_state && can_disable(rdev)) { + ret = _regulator_do_disable(rdev); + } + } + } regulator_unlock(rdev); diff --git a/drivers/soc/rockchip/Kconfig b/drivers/soc/rockchip/Kconfig index b71b73bf5fc5..bfadbecd0df8 100644 --- a/drivers/soc/rockchip/Kconfig +++ b/drivers/soc/rockchip/Kconfig @@ -26,4 +26,10 @@ config ROCKCHIP_PM_DOMAINS If unsure, say N. +config ROCKCHIP_SUSPEND_MODE + bool "Rockchip suspend mode config" + depends on ROCKCHIP_SIP + help + Say Y here if you want to set the suspend mode to the ATF. + endif diff --git a/drivers/soc/rockchip/Makefile b/drivers/soc/rockchip/Makefile index afca0a4c4b72..a15c0a395a33 100644 --- a/drivers/soc/rockchip/Makefile +++ b/drivers/soc/rockchip/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_ROCKCHIP_IODOMAIN) += io-domain.o obj-$(CONFIG_ROCKCHIP_PM_DOMAINS) += pm_domains.o obj-$(CONFIG_ROCKCHIP_DTPM) += dtpm.o +obj-$(CONFIG_ROCKCHIP_SUSPEND_MODE) += rockchip_pm_config.o diff --git a/drivers/soc/rockchip/rockchip_pm_config.c b/drivers/soc/rockchip/rockchip_pm_config.c new file mode 100644 index 000000000000..4a3038f22639 --- /dev/null +++ b/drivers/soc/rockchip/rockchip_pm_config.c @@ -0,0 +1,213 @@ +/* + * Rockchip Generic power configuration support. + * + * Copyright (c) 2017 ROCKCHIP, Co. Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PM_INVALID_GPIO 0xffff + +static const struct of_device_id pm_match_table[] = { + { .compatible = "rockchip,pm-rk3399",}, + { }, +}; + +#define MAX_PWRKEY_NUMS 20 +#define MAX_NUM_KEYS 60 + +struct rkxx_remote_key_table { + int scancode; + int keycode; +}; + +static int parse_ir_pwrkeys(unsigned int *pwrkey, int size, int *nkey) +{ + struct device_node *node; + struct device_node *child_node; + struct rkxx_remote_key_table key_table[MAX_NUM_KEYS]; + int i; + int len = 0, nbuttons; + int num = 0; + u32 usercode, scancode; + + for_each_node_by_name(node, "pwm") { + for_each_child_of_node(node, child_node) { + if (of_property_read_u32(child_node, + "rockchip,usercode", + &usercode)) + break; + + if (of_get_property(child_node, + "rockchip,key_table", + &len) == NULL || + len <= 0) + break; + + len = len < sizeof(key_table) ? len : sizeof(key_table); + len /= sizeof(u32); + if (of_property_read_u32_array(child_node, + "rockchip,key_table", + (u32 *)key_table, + len)) + break; + + nbuttons = len / 2; + for (i = 0; i < nbuttons && num < size; ++i) { + if (key_table[i].keycode == KEY_POWER) { + scancode = key_table[i].scancode; + pr_debug("usercode=%x, key=%x\n", + usercode, scancode); + pwrkey[num] = (usercode & 0xffff) << 16; + pwrkey[num] |= (scancode & 0xff) << 8; + ++num; + } + } + } + } + + *nkey = num; + + return num ? 0 : -1; +} + +static void rockchip_pm_virt_pwroff_prepare(void) +{ + int error; + int i, nkey; + u32 power_key[MAX_PWRKEY_NUMS]; + + if ((parse_ir_pwrkeys(power_key, ARRAY_SIZE(power_key), &nkey))) { + pr_err("Parse ir powerkey code failed!\n"); + return; + } + + for (i = 0; i < nkey; ++i) + sip_smc_set_suspend_mode(VIRTUAL_POWEROFF, 1, power_key[i]); + + regulator_suspend_prepare(PM_SUSPEND_MEM); + + error = freeze_secondary_cpus(0); + if (error) { + pr_err("Freeze secondary cpus failed!\n"); + return; + } + + sip_smc_set_suspend_mode(VIRTUAL_POWEROFF, 0, 1); + sip_smc_virtual_poweroff(); +} + +static int __init pm_config_init(struct platform_device *pdev) +{ + const struct of_device_id *match_id; + struct device_node *node; + u32 mode_config = 0; + u32 wakeup_config = 0; + u32 pwm_regulator_config = 0; + int gpio_temp[10]; + u32 sleep_debug_en = 0; + u32 apios_suspend = 0; + u32 virtual_poweroff_en = 0; + enum of_gpio_flags flags; + int i = 0; + int length; + + match_id = of_match_node(pm_match_table, pdev->dev.of_node); + if (!match_id) + return -ENODEV; + + node = of_find_node_by_name(NULL, "rockchip-suspend"); + + if (IS_ERR_OR_NULL(node)) { + dev_err(&pdev->dev, "%s dev node err\n", __func__); + return -ENODEV; + } + + if (of_property_read_u32_array(node, + "rockchip,sleep-mode-config", + &mode_config, 1)) + dev_warn(&pdev->dev, "not set sleep mode config\n"); + else + sip_smc_set_suspend_mode(SUSPEND_MODE_CONFIG, mode_config, 0); + + if (of_property_read_u32_array(node, + "rockchip,wakeup-config", + &wakeup_config, 1)) + dev_warn(&pdev->dev, "not set wakeup-config\n"); + else + sip_smc_set_suspend_mode(WKUP_SOURCE_CONFIG, wakeup_config, 0); + + if (of_property_read_u32_array(node, + "rockchip,pwm-regulator-config", + &pwm_regulator_config, 1)) + dev_warn(&pdev->dev, "not set pwm-regulator-config\n"); + else + sip_smc_set_suspend_mode(PWM_REGULATOR_CONFIG, + pwm_regulator_config, + 0); + + length = of_gpio_named_count(node, "rockchip,power-ctrl"); + + if (length > 0 && length < 10) { + for (i = 0; i < length; i++) { + gpio_temp[i] = of_get_named_gpio_flags(node, + "rockchip,power-ctrl", + i, + &flags); + if (!gpio_is_valid(gpio_temp[i])) + break; + sip_smc_set_suspend_mode(GPIO_POWER_CONFIG, + i, + gpio_temp[i]); + } + } + sip_smc_set_suspend_mode(GPIO_POWER_CONFIG, i, PM_INVALID_GPIO); + + if (!of_property_read_u32_array(node, + "rockchip,sleep-debug-en", + &sleep_debug_en, 1)) + sip_smc_set_suspend_mode(SUSPEND_DEBUG_ENABLE, + sleep_debug_en, + 0); + + if (!of_property_read_u32_array(node, + "rockchip,apios-suspend", + &apios_suspend, 1)) + sip_smc_set_suspend_mode(APIOS_SUSPEND_CONFIG, + apios_suspend, + 0); + + if (!of_property_read_u32_array(node, + "rockchip,virtual-poweroff", + &virtual_poweroff_en, 1) && + virtual_poweroff_en) + pm_power_off_prepare = rockchip_pm_virt_pwroff_prepare; + + return 0; +} + +static struct platform_driver pm_driver = { + .driver = { + .name = "rockchip-pm", + .of_match_table = pm_match_table, + }, +}; + +static int __init rockchip_pm_drv_register(void) +{ + return platform_driver_probe(&pm_driver, pm_config_init); +} +subsys_initcall(rockchip_pm_drv_register); + diff --git a/include/dt-bindings/suspend/rockchip-rk3399.h b/include/dt-bindings/suspend/rockchip-rk3399.h new file mode 100644 index 000000000000..de23670b43ea --- /dev/null +++ b/include/dt-bindings/suspend/rockchip-rk3399.h @@ -0,0 +1,60 @@ +/* + * Header providing constants for Rockchip suspend bindings. + * + * Copyright (C) 2017, Fuzhou Rockchip Electronics Co., Ltd + * Author: Tony.Xie + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __DT_BINDINGS_SUSPEND_ROCKCHIP_RK3399_H__ +#define __DT_BINDINGS_SUSPEND_ROCKCHIP_RK3399_H__ + +/* the suspend mode */ +#define RKPM_SLP_WFI (1 << 0) +#define RKPM_SLP_ARMPD (1 << 1) +#define RKPM_SLP_PERILPPD (1 << 2) +#define RKPM_SLP_DDR_RET (1 << 3) +#define RKPM_SLP_PLLPD (1 << 4) +#define RKPM_SLP_OSC_DIS (1 << 5) +#define RKPM_SLP_CENTER_PD (1 << 6) +#define RKPM_SLP_AP_PWROFF (1 << 7) + +/* the wake up source */ +#define RKPM_CLUSTER_L_WKUP_EN (1 << 0) +#define RKPM_CLUSTER_B_WKUPB_EN (1 << 1) +#define RKPM_GPIO_WKUP_EN (1 << 2) +#define RKPM_SDIO_WKUP_EN (1 << 3) +#define RKPM_SDMMC_WKUP_EN (1 << 4) +#define RKPM_TIMER_WKUP_EN (1 << 6) +#define RKPM_USB_WKUP_EN (1 << 7) +#define RKPM_SFT_WKUP_EN (1 << 8) +#define RKPM_WDT_M0_WKUP_EN (1 << 9) +#define RKPM_TIME_OUT_WKUP_EN (1 << 10) +#define RKPM_PWM_WKUP_EN (1 << 11) +#define RKPM_PCIE_WKUP_EN (1 << 13) + +/* the pwm regulator */ +#define PWM0_REGULATOR_EN (1 << 0) +#define PWM1_REGULATOR_EN (1 << 1) +#define PWM2_REGULATOR_EN (1 << 2) +#define PWM3A_REGULATOR_EN (1 << 3) +#define PWM3B_REGULATOR_EN (1 << 4) + +/* the APIO voltage domain */ +#define RKPM_APIO0_SUSPEND (1 << 0) +#define RKPM_APIO1_SUSPEND (1 << 1) +#define RKPM_APIO2_SUSPEND (1 << 2) +#define RKPM_APIO3_SUSPEND (1 << 3) +#define RKPM_APIO4_SUSPEND (1 << 4) +#define RKPM_APIO5_SUSPEND (1 << 5) + +#endif diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 29d920516e0b..549b68c58e68 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -632,6 +632,9 @@ /* time when this regulator was disabled last time */ ktime_t last_off; + + /* state when resuming */ + int resume_state; int cached_err; bool use_cached_err; spinlock_t err_lock; diff --git a/include/linux/rockchip/rockchip_sip.h b/include/linux/rockchip/rockchip_sip.h new file mode 100644 index 000000000000..9e8bbb2251c6 --- /dev/null +++ b/include/linux/rockchip/rockchip_sip.h @@ -0,0 +1,149 @@ +/* Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __ROCKCHIP_SIP_H +#define __ROCKCHIP_SIP_H + +#include +#include + +/* SMC function IDs for SiP Service queries, compatible with kernel-3.10 */ +#define SIP_ATF_VERSION 0x82000001 +#define SIP_ACCESS_REG 0x82000002 +#define SIP_SUSPEND_MODE 0x82000003 +#define SIP_PENDING_CPUS 0x82000004 +#define SIP_UARTDBG_CFG 0x82000005 +#define SIP_UARTDBG_CFG64 0xc2000005 +#define SIP_MCU_EL3FIQ_CFG 0x82000006 +#define SIP_ACCESS_CHIP_STATE64 0xc2000006 +#define SIP_SECURE_MEM_CONFIG 0x82000007 +#define SIP_ACCESS_CHIP_EXTRA_STATE64 0xc2000007 +#define SIP_DDR_CFG 0x82000008 +#define SIP_SHARE_MEM 0x82000009 +#define SIP_SIP_VERSION 0x8200000a +#define SIP_REMOTECTL_CFG 0x8200000b + +/* Trust firmware version */ +#define ATF_VER_MAJOR(ver) (((ver) >> 16) & 0xffff) +#define ATF_VER_MINOR(ver) (((ver) >> 0) & 0xffff) + +/* SIP_ACCESS_REG: read or write */ +#define SECURE_REG_RD 0x0 +#define SECURE_REG_WR 0x1 + +/* Fiq debugger share memory: 8KB enough */ +#define FIQ_UARTDBG_PAGE_NUMS 2 +#define FIQ_UARTDBG_SHARE_MEM_SIZE ((FIQ_UARTDBG_PAGE_NUMS) * 4096) + +/* Error return code */ +#define IS_SIP_ERROR(x) (!!(x)) + +#define SIP_RET_SUCCESS 0 +#define SIP_RET_SMC_UNKNOWN -1 +#define SIP_RET_NOT_SUPPORTED -2 +#define SIP_RET_INVALID_PARAMS -3 +#define SIP_RET_INVALID_ADDRESS -4 +#define SIP_RET_DENIED -5 + +/* SIP_UARTDBG_CFG64 call types */ +#define UARTDBG_CFG_INIT 0xf0 +#define UARTDBG_CFG_OSHDL_TO_OS 0xf1 +#define UARTDBG_CFG_OSHDL_CPUSW 0xf3 +#define UARTDBG_CFG_OSHDL_DEBUG_ENABLE 0xf4 +#define UARTDBG_CFG_OSHDL_DEBUG_DISABLE 0xf5 +#define UARTDBG_CFG_PRINT_PORT 0xf7 +#define UARTDBG_CFG_FIQ_ENABEL 0xf8 +#define UARTDBG_CFG_FIQ_DISABEL 0xf9 + +/* SIP_SUSPEND_MODE32 call types */ +#define SUSPEND_MODE_CONFIG 0x01 +#define WKUP_SOURCE_CONFIG 0x02 +#define PWM_REGULATOR_CONFIG 0x03 +#define GPIO_POWER_CONFIG 0x04 +#define SUSPEND_DEBUG_ENABLE 0x05 +#define APIOS_SUSPEND_CONFIG 0x06 +#define VIRTUAL_POWEROFF 0x07 + +/* SIP_REMOTECTL_CFG call types */ +#define REMOTECTL_SET_IRQ 0xf0 +#define REMOTECTL_SET_PWM_CH 0xf1 +#define REMOTECTL_SET_PWRKEY 0xf2 +#define REMOTECTL_GET_WAKEUP_STATE 0xf3 +#define REMOTECTL_ENABLE 0xf4 +/* wakeup state */ +#define REMOTECTL_PWRKEY_WAKEUP 0xdeadbeaf + +/* Share mem page types */ +typedef enum { + SHARE_PAGE_TYPE_INVALID = 0, + SHARE_PAGE_TYPE_UARTDBG, + SHARE_PAGE_TYPE_MAX, +} share_page_type_t; + +/* + * Rules: struct arm_smccc_res contains result and data, details: + * + * a0: error code(0: success, !0: error); + * a1~a3: data + */ +struct arm_smccc_res sip_smc_get_atf_version(void); +struct arm_smccc_res sip_smc_get_sip_version(void); +struct arm_smccc_res sip_smc_ddr_cfg(u32 arg0, u32 arg1, u32 arg2); +struct arm_smccc_res sip_smc_request_share_mem(u32 page_num, + share_page_type_t page_type); +struct arm_smccc_res sip_smc_mcu_el3fiq(u32 arg0, u32 arg1, u32 arg2); + +int sip_smc_set_suspend_mode(u32 ctrl, u32 config1, u32 config2); +int sip_smc_virtual_poweroff(void); +/***************************fiq debugger **************************************/ +void sip_fiq_debugger_enable_fiq(bool enable, uint32_t tgt_cpu); +void sip_fiq_debugger_enable_debug(bool enable); +int sip_fiq_debugger_uart_irq_tf_init(u32 irq_id, void *callback_fn); +int sip_fiq_debugger_set_print_port(u32 port_phyaddr, u32 baudrate); +int sip_fiq_debugger_request_share_memory(void); +int sip_fiq_debugger_get_target_cpu(void); +int sip_fiq_debugger_switch_cpu(u32 cpu); +int sip_fiq_debugger_is_enabled(void); + +/* optee cpu_context */ +struct sm_nsec_ctx { + u32 usr_sp; + u32 usr_lr; + u32 irq_spsr; + u32 irq_sp; + u32 irq_lr; + u32 svc_spsr; + u32 svc_sp; + u32 svc_lr; + u32 abt_spsr; + u32 abt_sp; + u32 abt_lr; + u32 und_spsr; + u32 und_sp; + u32 und_lr; + u32 mon_lr; + u32 mon_spsr; + u32 r4; + u32 r5; + u32 r6; + u32 r7; + u32 r8; + u32 r9; + u32 r10; + u32 r11; + u32 r12; + u32 r0; + u32 r1; + u32 r2; + u32 r3; +}; + +#endif -- 2.26.2