From 806d8a6477ab058a56ef5408b10758128050c762 Mon Sep 17 00:00:00 2001 From: Qiufang Dai Date: Thu, 16 Mar 2017 19:02:23 +0800 Subject: [PATCH] PM / sleep: GX family PM driver inital import PD#138714: GX family PM driver inital import Enable cpu idle. Change-Id: Ifd2d80dbef835926bdadc95d75a0cd4e3b388eb2 Signed-off-by: Qiufang Dai Signed-off-by: Yan Wang --- MAINTAINERS | 5 + arch/arm64/boot/dts/amlogic/mesongxl.dtsi | 42 +++-- arch/arm64/boot/dts/amlogic/mesongxm.dtsi | 77 ++++++-- arch/arm64/configs/meson64_defconfig | 5 +- drivers/amlogic/Kconfig | 3 + drivers/amlogic/Makefile | 2 + drivers/amlogic/pm/Kconfig | 31 ++++ drivers/amlogic/pm/Makefile | 1 + drivers/amlogic/pm/gx_pm.c | 213 ++++++++++++++++++++++ 9 files changed, 344 insertions(+), 35 deletions(-) create mode 100644 drivers/amlogic/pm/Kconfig create mode 100644 drivers/amlogic/pm/Makefile create mode 100644 drivers/amlogic/pm/gx_pm.c diff --git a/MAINTAINERS b/MAINTAINERS index 789b313a76b9..538dccd35c7c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13673,6 +13673,7 @@ M: Frank Chen F: drivers/staging/android/logger.c F: drivers/staging/android/logger.h +<<<<<<< HEAD AMLOGIC AMLVIDEO2 DRIVER M: Guosong Zhou F: arch/arm64/configs/meson64_defconfig @@ -13730,3 +13731,7 @@ F: drivers/amlogic/media/video_processor/Kconfig F: drivers/amlogic/media/video_processor/Makefile F: drivers/amlogic/media/video_processor/ppmgr/* F: include/linux/amlogic/media/ppmgr/* + +AMLOGIC PM/SLEEP DRIVER SUPPORT +M: Qiufang Dai +F: drivers/amlogic/pm/* diff --git a/arch/arm64/boot/dts/amlogic/mesongxl.dtsi b/arch/arm64/boot/dts/amlogic/mesongxl.dtsi index 14b07c23fae9..7a319dfe933c 100644 --- a/arch/arm64/boot/dts/amlogic/mesongxl.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesongxl.dtsi @@ -52,7 +52,8 @@ enable-method = "psci"; clocks = <&scpi_dvfs 0>; clock-names = "cpu-cluster.0"; - cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; + cpu-idle-states = <&SYSTEM_SLEEP_0>; + /*cpu-idle-states = <&CPU_SLEEP_0 &SYSTEM_SLEEP_0>;*/ }; CPU1:cpu@1 { @@ -62,7 +63,8 @@ enable-method = "psci"; clocks = <&scpi_dvfs 0>; clock-names = "cpu-cluster.0"; - cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; + cpu-idle-states = <&SYSTEM_SLEEP_0>; + /*cpu-idle-states = <&CPU_SLEEP_0 &SYSTEM_SLEEP_0>;*/ }; CPU2:cpu@2 { device_type = "cpu"; @@ -71,7 +73,8 @@ enable-method = "psci"; clocks = <&scpi_dvfs 0>; clock-names = "cpu-cluster.0"; - cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; + cpu-idle-states = <&SYSTEM_SLEEP_0>; + /*cpu-idle-states = <&CPU_SLEEP_0 &SYSTEM_SLEEP_0>;*/ }; CPU3:cpu@3 { @@ -81,29 +84,30 @@ enable-method = "psci"; clocks = <&scpi_dvfs 0>; clock-names = "cpu-cluster.0"; - cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; + cpu-idle-states = <&SYSTEM_SLEEP_0>; + /*cpu-idle-states = <&CPU_SLEEP_0 &SYSTEM_SLEEP_0>;*/ }; idle-states { entry-method = "arm,psci"; - +/* CPU_SLEEP_0: cpu-sleep-0 { - compatible = "arm, idle-state"; + compatible = "arm,idle-state"; arm,psci-suspend-param = <0x0010000>; local-timer-stop; entry-latency-us = <3000>; exit-latency-us = <3000>; min-residency-us = <8000>; }; - - CLUSTER_SLEEP_0: cluster-sleep-0 { - compatible = "arm, idle-state"; - arm,psci-suspend-param = <0x1010000>; +*/ + SYSTEM_SLEEP_0: system-sleep-0 { + compatible = "arm,idle-state"; + arm,psci-suspend-param = <0x0010000>; local-timer-stop; - entry-latency-us = <3000>; - exit-latency-us = <3000>; - min-residency-us = <15000>; + entry-latency-us = <0x3fffffff>; + exit-latency-us = <0x40000000>; + min-residency-us = <0xffffffff>; }; }; }; @@ -146,12 +150,14 @@ }; psci { - compatible = "arm,psci"; + compatible = "arm,psci-0.2"; method = "smc"; - cpu_suspend = <0xC4000001>; - cpu_off = <0x84000002>; - cpu_on = <0xC4000003>; - migrate = <0xC4000005>; + }; + + meson_suspend:pm{ + compatible = "amlogic, pm"; + device_name = "aml_pm"; + reg = <0x0 0xc810023c 0x0 0x4>; }; secmon { diff --git a/arch/arm64/boot/dts/amlogic/mesongxm.dtsi b/arch/arm64/boot/dts/amlogic/mesongxm.dtsi index 7a3e60d4fe95..fc5fe6feab12 100644 --- a/arch/arm64/boot/dts/amlogic/mesongxm.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesongxm.dtsi @@ -66,7 +66,11 @@ enable-method = "psci"; clocks = <&scpi_dvfs 0>; clock-names = "cpu-cluster.0"; - cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; + cpu-idle-states = <&SYSTEM_SLEEP_0>; +/* + * cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0 + * &SYSTEM_SLEEP_0>; + */ }; CPU1:cpu@1 { @@ -76,8 +80,13 @@ enable-method = "psci"; clocks = <&scpi_dvfs 0>; clock-names = "cpu-cluster.0"; - cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; + cpu-idle-states = <&SYSTEM_SLEEP_0>; +/* + * cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0 + * &SYSTEM_SLEEP_0>; + */ }; + CPU2:cpu@2 { device_type = "cpu"; compatible = "arm,cortex-a53","arm,armv8"; @@ -85,7 +94,11 @@ enable-method = "psci"; clocks = <&scpi_dvfs 0>; clock-names = "cpu-cluster.0"; - cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; + cpu-idle-states = <&SYSTEM_SLEEP_0>; +/* + * cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0 + * &SYSTEM_SLEEP_0>; + */ }; CPU3:cpu@3 { @@ -95,7 +108,11 @@ enable-method = "psci"; clocks = <&scpi_dvfs 0>; clock-names = "cpu-cluster.0"; - cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; + cpu-idle-states = <&SYSTEM_SLEEP_0>; +/* + * cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0 + * &SYSTEM_SLEEP_0>; + */ }; CPU4:cpu@100 { @@ -105,7 +122,11 @@ enable-method = "psci"; clocks = <&scpi_dvfs 1>; clock-names = "cpu-cluster.1"; - cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; + cpu-idle-states = <&SYSTEM_SLEEP_0>; +/* + * cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0 + * &SYSTEM_SLEEP_0>; + */ }; CPU5:cpu@101 { @@ -115,7 +136,11 @@ enable-method = "psci"; clocks = <&scpi_dvfs 1>; clock-names = "cpu-cluster.1"; - cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; + cpu-idle-states = <&SYSTEM_SLEEP_0>; +/* + * cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0 + * &SYSTEM_SLEEP_0>; + */ }; CPU6:cpu@102 { device_type = "cpu"; @@ -124,7 +149,11 @@ enable-method = "psci"; clocks = <&scpi_dvfs 1>; clock-names = "cpu-cluster.1"; - cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; + cpu-idle-states = <&SYSTEM_SLEEP_0>; +/* + * cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0 + * &SYSTEM_SLEEP_0>; + */ }; CPU7:cpu@103 { @@ -134,14 +163,18 @@ enable-method = "psci"; clocks = <&scpi_dvfs 1>; clock-names = "cpu-cluster.1"; - cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; + cpu-idle-states = <&SYSTEM_SLEEP_0>; +/* + * cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0 + * &SYSTEM_SLEEP_0>; + */ }; idle-states { entry-method = "arm,psci"; - +/* CPU_SLEEP_0: cpu-sleep-0 { - compatible = "arm, idle-state"; + compatible = "arm,idle-state"; arm,psci-suspend-param = <0x0010000>; local-timer-stop; entry-latency-us = <3000>; @@ -150,13 +183,22 @@ }; CLUSTER_SLEEP_0: cluster-sleep-0 { - compatible = "arm, idle-state"; + compatible = "arm,idle-state"; arm,psci-suspend-param = <0x1010000>; local-timer-stop; entry-latency-us = <3000>; exit-latency-us = <3000>; min-residency-us = <15000>; }; +*/ + SYSTEM_SLEEP_0: system-sleep-0 { + compatible = "arm,idle-state"; + arm,psci-suspend-param = <0x1010000>; + local-timer-stop; + entry-latency-us = <0x3fffffff>; + exit-latency-us = <0x40000000>; + min-residency-us = <0xffffffff>; + }; }; }; @@ -198,12 +240,15 @@ }; psci { - compatible = "arm,psci"; + compatible = "arm,psci-0.2"; method = "smc"; - cpu_suspend = <0xC4000001>; - cpu_off = <0x84000002>; - cpu_on = <0xC4000003>; - migrate = <0xC4000005>; + }; + + meson_suspend:pm{ + compatible = "amlogic, pm"; + device_name = "aml_pm"; + reg = <0x0 0xc81000a8 0x0 0x4 + 0x0 0xc810023c 0x0 0x4>; }; secmon { diff --git a/arch/arm64/configs/meson64_defconfig b/arch/arm64/configs/meson64_defconfig index 1f3cd43eacc3..5215b813be2d 100644 --- a/arch/arm64/configs/meson64_defconfig +++ b/arch/arm64/configs/meson64_defconfig @@ -28,7 +28,9 @@ CONFIG_ZSMALLOC=y CONFIG_SECCOMP=y CONFIG_BINFMT_MISC=y CONFIG_COMPAT=y +CONFIG_PM_WAKELOCKS=y CONFIG_CPU_IDLE=y +CONFIG_ARM_CPUIDLE=y CONFIG_CPU_FREQ=y CONFIG_CPU_FREQ_GOV_INTERACTIVE=y CONFIG_ARM_BIG_LITTLE_CPUFREQ=y @@ -177,8 +179,8 @@ CONFIG_AMLOGIC_REG_ACCESS=y CONFIG_AMLOGIC_TIMER=y CONFIG_AMLOGIC_BC_TIMER=y CONFIG_AMLOGIC_CLK=y -CONFIG_AMLOGIC_GX_CLK=y CONFIG_AMLOGIC_COMMON_CLK_SCPI=y +CONFIG_AMLOGIC_GX_CLK=y CONFIG_AMLOGIC_CRYPTO=y CONFIG_AMLOGIC_INPUT=y CONFIG_AMLOGIC_INPUT_KEYBOARD=y @@ -233,6 +235,7 @@ CONFIG_AMLOGIC_CPUCORE_THERMAL=y CONFIG_AMLOGIC_GPU_THERMAL=y CONFIG_AMLOGIC_GPUCORE_THERMAL=y CONFIG_AMLOGIC_AUDIO_DSP=y +CONFIG_AMLOGIC_GX_SUSPEND=y CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y diff --git a/drivers/amlogic/Kconfig b/drivers/amlogic/Kconfig index 4e1debadf2ca..9671268648dc 100644 --- a/drivers/amlogic/Kconfig +++ b/drivers/amlogic/Kconfig @@ -68,5 +68,8 @@ source "drivers/amlogic/key_manage/Kconfig" source "drivers/amlogic/thermal/Kconfig" source "drivers/amlogic/audiodsp/Kconfig" + +source "drivers/amlogic/pm/Kconfig" + endmenu endif diff --git a/drivers/amlogic/Makefile b/drivers/amlogic/Makefile index 1ba8295efedb..467c3d75abfb 100644 --- a/drivers/amlogic/Makefile +++ b/drivers/amlogic/Makefile @@ -64,3 +64,5 @@ obj-$(CONFIG_AMLOGIC_KEY_MANAGE) += key_manage/ obj-$(CONFIG_AMLOGIC_TEMP_SENSOR) += thermal/ obj-$(CONFIG_AMLOGIC_AUDIO_DSP) += audiodsp/ + +obj-$(CONFIG_AMLOGIC_GX_SUSPEND) += pm/ diff --git a/drivers/amlogic/pm/Kconfig b/drivers/amlogic/pm/Kconfig new file mode 100644 index 000000000000..9e405cc7748b --- /dev/null +++ b/drivers/amlogic/pm/Kconfig @@ -0,0 +1,31 @@ +# Amlogic pm + +menu "Meson core pm driver" + +config AMLOGIC_GX_SUSPEND + bool "Meson gx chips pm driver" + depends on CPU_IDLE + depends on PM_WAKELOCKS + depends on ARM64 + default n + help + This is the Amlogic suspend driver for 64bit family chips + It provides PM suspend entry to ATF. It invoke ATF via idle routine. + It support PSCIv0.2 or newer version. + + If you want this support, you should say Y here. + +config AMLOGIC_MX_SUSPEND + bool "Meson mx chips pm driver" + depends on CPU_IDLE + depends on PM_WAKELOCKS + depends on ARM + default n + help + This is the Amlogic suspend driver for 32bit family chips + It provides PM suspend entry to suspend firmware. + It invoke suspend firmware via idle routine. + + If you want this support, you should say Y here. + +endmenu diff --git a/drivers/amlogic/pm/Makefile b/drivers/amlogic/pm/Makefile new file mode 100644 index 000000000000..874c6c82af45 --- /dev/null +++ b/drivers/amlogic/pm/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_AMLOGIC_GX_SUSPEND) += gx_pm.o diff --git a/drivers/amlogic/pm/gx_pm.c b/drivers/amlogic/pm/gx_pm.c new file mode 100644 index 000000000000..5191cb7891ff --- /dev/null +++ b/drivers/amlogic/pm/gx_pm.c @@ -0,0 +1,213 @@ +/* + * drivers/amlogic/pm/gx_pm.c + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef unsigned long (psci_fn)(unsigned long, unsigned long, + unsigned long, unsigned long); + +static unsigned long __invoke_psci_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.a0; +} + +static u32 psci_get_version(void) +{ + return __invoke_psci_fn_smc(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0); +} + + +#undef pr_fmt +#define pr_fmt(fmt) "gxbb_pm: " fmt + +static void __iomem *exit_reg; +static int max_idle_lvl; + +/* + *0x10000 : bit[16]=1:control cpu suspend to power down + *cpu_suspend(0, meson_system_suspend); + */ + +static void meson_gx_suspend(void) +{ + pr_info("enter meson_pm_suspend!\n"); + arm_cpuidle_suspend(max_idle_lvl); +/* cpu_suspend(0, meson_system_suspend); + */ + pr_info("... wake up\n"); +} + +static int meson_pm_prepare(void) +{ + pr_info("enter meson_pm_prepare!\n"); + return 0; +} + +static int meson_gx_enter(suspend_state_t state) +{ + int ret = 0; + + switch (state) { + case PM_SUSPEND_STANDBY: + case PM_SUSPEND_MEM: + meson_gx_suspend(); + break; + default: + ret = -EINVAL; + } + return ret; +} + +static void meson_pm_finish(void) +{ + pr_info("enter meson_pm_finish!\n"); +} +unsigned int get_resume_method(void) +{ + unsigned int val = 0; + + if (exit_reg) + val = (readl(exit_reg) >> 28) & 0xf; + return val; +} +EXPORT_SYMBOL(get_resume_method); + +static const struct platform_suspend_ops meson_gx_ops = { + .enter = meson_gx_enter, + .prepare = meson_pm_prepare, + .finish = meson_pm_finish, + .valid = suspend_valid_only_mem, +}; + +static unsigned int suspend_reason; +ssize_t suspend_reason_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + unsigned int len; + + len = sprintf(buf, "%d\n", suspend_reason); + + return len; +} +ssize_t suspend_reason_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + + ret = kstrtouint(buf, 0, &suspend_reason); + + switch (ret) { + case 1: + __invoke_psci_fn_smc(0x82000042, suspend_reason, 0, 0); + break; + default: + return -EINVAL; + } + return count; +} + +DEVICE_ATTR(suspend_reason, 0664, suspend_reason_show, suspend_reason_store); + +static int __init meson_pm_probe(struct platform_device *pdev) +{ + struct device_node *cpu_node; + struct device_node *state_node; + int count = 0; + u32 ver = psci_get_version(); + + pr_info("enter meson_pm_probe!\n"); + + if (PSCI_VERSION_MAJOR(ver) == 0 && PSCI_VERSION_MINOR(ver) == 2) { + cpu_node = of_get_cpu_node(0, NULL); + if (!cpu_node) { + pr_info("cpu node get fail!\n"); + return -1; + } + while ((state_node = of_parse_phandle(cpu_node, + "cpu-idle-states", count))) { + count++; + of_node_put(state_node); + } + if (count) + max_idle_lvl = count; + else { + max_idle_lvl = -1; + pr_info("get system suspend level fail!\n"); + return -1; + } + pr_info("system suspend level: %d\n", max_idle_lvl); + suspend_set_ops(&meson_gx_ops); + } + + exit_reg = of_iomap(pdev->dev.of_node, 0); + device_create_file(&pdev->dev, &dev_attr_suspend_reason); + pr_info("meson_pm_probe done\n"); + return 0; +} + +static int __exit meson_pm_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id amlogic_pm_dt_match[] = { + {.compatible = "amlogic, pm", + }, +}; + +static struct platform_driver meson_pm_driver = { + .driver = { + .name = "pm-meson", + .owner = THIS_MODULE, + .of_match_table = amlogic_pm_dt_match, + }, + .remove = __exit_p(meson_pm_remove), +}; + +static int __init meson_pm_init(void) +{ + return platform_driver_probe(&meson_pm_driver, meson_pm_probe); +} + +late_initcall(meson_pm_init); -- 2.20.1