0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016 #include <linux/cpu_pm.h>
0017 #include <linux/cpuidle.h>
0018 #include <linux/module.h>
0019 #include <linux/of.h>
0020 #include <linux/suspend.h>
0021 #include <linux/platform_device.h>
0022 #include <asm/cpuidle.h>
0023
0024 #define MVEBU_V7_FLAG_DEEP_IDLE 0x10000
0025
0026 static int (*mvebu_v7_cpu_suspend)(int);
0027
0028 static int mvebu_v7_enter_idle(struct cpuidle_device *dev,
0029 struct cpuidle_driver *drv,
0030 int index)
0031 {
0032 int ret;
0033 bool deepidle = false;
0034 cpu_pm_enter();
0035
0036 if (drv->states[index].flags & MVEBU_V7_FLAG_DEEP_IDLE)
0037 deepidle = true;
0038
0039 ret = mvebu_v7_cpu_suspend(deepidle);
0040 cpu_pm_exit();
0041
0042 if (ret)
0043 return ret;
0044
0045 return index;
0046 }
0047
0048 static struct cpuidle_driver armadaxp_idle_driver = {
0049 .name = "armada_xp_idle",
0050 .states[0] = ARM_CPUIDLE_WFI_STATE,
0051 .states[1] = {
0052 .enter = mvebu_v7_enter_idle,
0053 .exit_latency = 100,
0054 .power_usage = 50,
0055 .target_residency = 1000,
0056 .name = "MV CPU IDLE",
0057 .desc = "CPU power down",
0058 },
0059 .states[2] = {
0060 .enter = mvebu_v7_enter_idle,
0061 .exit_latency = 1000,
0062 .power_usage = 5,
0063 .target_residency = 10000,
0064 .flags = MVEBU_V7_FLAG_DEEP_IDLE,
0065 .name = "MV CPU DEEP IDLE",
0066 .desc = "CPU and L2 Fabric power down",
0067 },
0068 .state_count = 3,
0069 };
0070
0071 static struct cpuidle_driver armada370_idle_driver = {
0072 .name = "armada_370_idle",
0073 .states[0] = ARM_CPUIDLE_WFI_STATE,
0074 .states[1] = {
0075 .enter = mvebu_v7_enter_idle,
0076 .exit_latency = 100,
0077 .power_usage = 5,
0078 .target_residency = 1000,
0079 .flags = MVEBU_V7_FLAG_DEEP_IDLE,
0080 .name = "Deep Idle",
0081 .desc = "CPU and L2 Fabric power down",
0082 },
0083 .state_count = 2,
0084 };
0085
0086 static struct cpuidle_driver armada38x_idle_driver = {
0087 .name = "armada_38x_idle",
0088 .states[0] = ARM_CPUIDLE_WFI_STATE,
0089 .states[1] = {
0090 .enter = mvebu_v7_enter_idle,
0091 .exit_latency = 10,
0092 .power_usage = 5,
0093 .target_residency = 100,
0094 .name = "Idle",
0095 .desc = "CPU and SCU power down",
0096 },
0097 .state_count = 2,
0098 };
0099
0100 static int mvebu_v7_cpuidle_probe(struct platform_device *pdev)
0101 {
0102 const struct platform_device_id *id = pdev->id_entry;
0103
0104 if (!id)
0105 return -EINVAL;
0106
0107 mvebu_v7_cpu_suspend = pdev->dev.platform_data;
0108
0109 return cpuidle_register((struct cpuidle_driver *)id->driver_data, NULL);
0110 }
0111
0112 static const struct platform_device_id mvebu_cpuidle_ids[] = {
0113 {
0114 .name = "cpuidle-armada-xp",
0115 .driver_data = (unsigned long)&armadaxp_idle_driver,
0116 }, {
0117 .name = "cpuidle-armada-370",
0118 .driver_data = (unsigned long)&armada370_idle_driver,
0119 }, {
0120 .name = "cpuidle-armada-38x",
0121 .driver_data = (unsigned long)&armada38x_idle_driver,
0122 },
0123 {}
0124 };
0125
0126 static struct platform_driver mvebu_cpuidle_driver = {
0127 .probe = mvebu_v7_cpuidle_probe,
0128 .driver = {
0129 .name = "cpuidle-mbevu",
0130 .suppress_bind_attrs = true,
0131 },
0132 .id_table = mvebu_cpuidle_ids,
0133 };
0134
0135 builtin_platform_driver(mvebu_cpuidle_driver);
0136
0137 MODULE_AUTHOR("Gregory CLEMENT <gregory.clement@free-electrons.com>");
0138 MODULE_DESCRIPTION("Marvell EBU v7 cpuidle driver");
0139 MODULE_LICENSE("GPL");