1 | /* |
2 | * Marvell Armada 370, 38x and XP SoC cpuidle driver |
3 | * |
4 | * Copyright (C) 2014 Marvell |
5 | * |
6 | * Nadav Haklai <nadavh@marvell.com> |
7 | * Gregory CLEMENT <gregory.clement@free-electrons.com> |
8 | * |
9 | * This file is licensed under the terms of the GNU General Public |
10 | * License version 2. This program is licensed "as is" without any |
11 | * warranty of any kind, whether express or implied. |
12 | * |
13 | * Maintainer: Gregory CLEMENT <gregory.clement@free-electrons.com> |
14 | */ |
15 | |
16 | #include <linux/cpu_pm.h> |
17 | #include <linux/cpuidle.h> |
18 | #include <linux/module.h> |
19 | #include <linux/of.h> |
20 | #include <linux/suspend.h> |
21 | #include <linux/platform_device.h> |
22 | #include <asm/cpuidle.h> |
23 | |
24 | #define MVEBU_V7_FLAG_DEEP_IDLE 0x10000 |
25 | |
26 | static int (*mvebu_v7_cpu_suspend)(int); |
27 | |
28 | static __cpuidle int mvebu_v7_enter_idle(struct cpuidle_device *dev, |
29 | struct cpuidle_driver *drv, |
30 | int index) |
31 | { |
32 | int ret; |
33 | bool deepidle = false; |
34 | cpu_pm_enter(); |
35 | |
36 | if (drv->states[index].flags & MVEBU_V7_FLAG_DEEP_IDLE) |
37 | deepidle = true; |
38 | |
39 | ct_cpuidle_enter(); |
40 | ret = mvebu_v7_cpu_suspend(deepidle); |
41 | ct_cpuidle_exit(); |
42 | |
43 | cpu_pm_exit(); |
44 | |
45 | if (ret) |
46 | return ret; |
47 | |
48 | return index; |
49 | } |
50 | |
51 | static struct cpuidle_driver armadaxp_idle_driver = { |
52 | .name = "armada_xp_idle" , |
53 | .states[0] = ARM_CPUIDLE_WFI_STATE, |
54 | .states[1] = { |
55 | .enter = mvebu_v7_enter_idle, |
56 | .exit_latency = 100, |
57 | .power_usage = 50, |
58 | .target_residency = 1000, |
59 | .flags = CPUIDLE_FLAG_RCU_IDLE, |
60 | .name = "MV CPU IDLE" , |
61 | .desc = "CPU power down" , |
62 | }, |
63 | .states[2] = { |
64 | .enter = mvebu_v7_enter_idle, |
65 | .exit_latency = 1000, |
66 | .power_usage = 5, |
67 | .target_residency = 10000, |
68 | .flags = MVEBU_V7_FLAG_DEEP_IDLE | CPUIDLE_FLAG_RCU_IDLE, |
69 | .name = "MV CPU DEEP IDLE" , |
70 | .desc = "CPU and L2 Fabric power down" , |
71 | }, |
72 | .state_count = 3, |
73 | }; |
74 | |
75 | static struct cpuidle_driver armada370_idle_driver = { |
76 | .name = "armada_370_idle" , |
77 | .states[0] = ARM_CPUIDLE_WFI_STATE, |
78 | .states[1] = { |
79 | .enter = mvebu_v7_enter_idle, |
80 | .exit_latency = 100, |
81 | .power_usage = 5, |
82 | .target_residency = 1000, |
83 | .flags = MVEBU_V7_FLAG_DEEP_IDLE | CPUIDLE_FLAG_RCU_IDLE, |
84 | .name = "Deep Idle" , |
85 | .desc = "CPU and L2 Fabric power down" , |
86 | }, |
87 | .state_count = 2, |
88 | }; |
89 | |
90 | static struct cpuidle_driver armada38x_idle_driver = { |
91 | .name = "armada_38x_idle" , |
92 | .states[0] = ARM_CPUIDLE_WFI_STATE, |
93 | .states[1] = { |
94 | .enter = mvebu_v7_enter_idle, |
95 | .exit_latency = 10, |
96 | .power_usage = 5, |
97 | .target_residency = 100, |
98 | .flags = CPUIDLE_FLAG_RCU_IDLE, |
99 | .name = "Idle" , |
100 | .desc = "CPU and SCU power down" , |
101 | }, |
102 | .state_count = 2, |
103 | }; |
104 | |
105 | static int mvebu_v7_cpuidle_probe(struct platform_device *pdev) |
106 | { |
107 | const struct platform_device_id *id = pdev->id_entry; |
108 | |
109 | if (!id) |
110 | return -EINVAL; |
111 | |
112 | mvebu_v7_cpu_suspend = pdev->dev.platform_data; |
113 | |
114 | return cpuidle_register(drv: (struct cpuidle_driver *)id->driver_data, NULL); |
115 | } |
116 | |
117 | static const struct platform_device_id mvebu_cpuidle_ids[] = { |
118 | { |
119 | .name = "cpuidle-armada-xp" , |
120 | .driver_data = (unsigned long)&armadaxp_idle_driver, |
121 | }, { |
122 | .name = "cpuidle-armada-370" , |
123 | .driver_data = (unsigned long)&armada370_idle_driver, |
124 | }, { |
125 | .name = "cpuidle-armada-38x" , |
126 | .driver_data = (unsigned long)&armada38x_idle_driver, |
127 | }, |
128 | {} |
129 | }; |
130 | |
131 | static struct platform_driver mvebu_cpuidle_driver = { |
132 | .probe = mvebu_v7_cpuidle_probe, |
133 | .driver = { |
134 | .name = "cpuidle-mbevu" , |
135 | .suppress_bind_attrs = true, |
136 | }, |
137 | .id_table = mvebu_cpuidle_ids, |
138 | }; |
139 | |
140 | builtin_platform_driver(mvebu_cpuidle_driver); |
141 | |
142 | MODULE_AUTHOR("Gregory CLEMENT <gregory.clement@free-electrons.com>" ); |
143 | MODULE_DESCRIPTION("Marvell EBU v7 cpuidle driver" ); |
144 | MODULE_LICENSE("GPL" ); |
145 | |