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 <qiufang.dai@amlogic.com>
Signed-off-by: Yan Wang <yan.wang@amlogic.com>
This commit is contained in:
Qiufang Dai
2017-03-16 19:02:23 +08:00
parent 6780402686
commit 806d8a6477
9 changed files with 344 additions and 35 deletions

View File

@@ -13673,6 +13673,7 @@ M: Frank Chen <frank.chen@amlogic.com>
F: drivers/staging/android/logger.c
F: drivers/staging/android/logger.h
<<<<<<< HEAD
AMLOGIC AMLVIDEO2 DRIVER
M: Guosong Zhou <guosong.zhou@amlogic.com>
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 <qiufang.dai@amlogic.com>
F: drivers/amlogic/pm/*

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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

View File

@@ -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

View File

@@ -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/

View File

@@ -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

View File

@@ -0,0 +1 @@
obj-$(CONFIG_AMLOGIC_GX_SUSPEND) += gx_pm.o

213
drivers/amlogic/pm/gx_pm.c Normal file
View File

@@ -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 <linux/pm.h>
#include <linux/suspend.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/clk.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/amlogic/iomap.h>
#include <linux/init.h>
#include <linux/of.h>
#include <linux/psci.h>
#include <asm/compiler.h>
#include <linux/errno.h>
#include <linux/suspend.h>
#include <asm/suspend.h>
#include <linux/of_address.h>
#include <linux/input.h>
#include <asm/cpuidle.h>
#include <uapi/linux/psci.h>
#include <linux/arm-smccc.h>
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);