Merge tag 'v3.10.72' into linux-linaro-lsk
[firefly-linux-kernel-4.4.55.git] / drivers / cpuidle / arm_big_little.c
1 /*
2  * big.LITTLE CPU idle driver.
3  *
4  * Copyright (C) 2012 ARM Ltd.
5  * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.
10  */
11
12 #include <linux/arm-cci.h>
13 #include <linux/bitmap.h>
14 #include <linux/cpuidle.h>
15 #include <linux/cpu_pm.h>
16 #include <linux/clockchips.h>
17 #include <linux/debugfs.h>
18 #include <linux/hrtimer.h>
19 #include <linux/kernel.h>
20 #include <linux/module.h>
21 #include <linux/tick.h>
22 #include <linux/vexpress.h>
23 #include <asm/mcpm.h>
24 #include <asm/cpuidle.h>
25 #include <asm/cputype.h>
26 #include <asm/idmap.h>
27 #include <asm/proc-fns.h>
28 #include <asm/suspend.h>
29 #include <linux/of.h>
30
31 static int bl_cpuidle_simple_enter(struct cpuidle_device *dev,
32                 struct cpuidle_driver *drv, int index)
33 {
34         ktime_t time_start, time_end;
35         s64 diff;
36
37         time_start = ktime_get();
38
39         cpu_do_idle();
40
41         time_end = ktime_get();
42
43         local_irq_enable();
44
45         diff = ktime_to_us(ktime_sub(time_end, time_start));
46         if (diff > INT_MAX)
47                 diff = INT_MAX;
48
49         dev->last_residency = (int) diff;
50
51         return index;
52 }
53
54 static int bl_enter_powerdown(struct cpuidle_device *dev,
55                                 struct cpuidle_driver *drv, int idx);
56
57 static struct cpuidle_state bl_cpuidle_set[] __initdata = {
58         [0] = {
59                 .enter                  = bl_cpuidle_simple_enter,
60                 .exit_latency           = 1,
61                 .target_residency       = 1,
62                 .power_usage            = UINT_MAX,
63                 .flags                  = CPUIDLE_FLAG_TIME_VALID,
64                 .name                   = "WFI",
65                 .desc                   = "ARM WFI",
66         },
67         [1] = {
68                 .enter                  = bl_enter_powerdown,
69                 .exit_latency           = 300,
70                 .target_residency       = 1000,
71                 .flags                  = CPUIDLE_FLAG_TIME_VALID,
72                 .name                   = "C1",
73                 .desc                   = "ARM power down",
74         },
75 };
76
77 struct cpuidle_driver bl_idle_driver = {
78         .name = "bl_idle",
79         .owner = THIS_MODULE,
80         .safe_state_index = 0
81 };
82
83 static DEFINE_PER_CPU(struct cpuidle_device, bl_idle_dev);
84
85 static int notrace bl_powerdown_finisher(unsigned long arg)
86 {
87         unsigned int mpidr = read_cpuid_mpidr();
88         unsigned int cluster = (mpidr >> 8) & 0xf;
89         unsigned int cpu = mpidr & 0xf;
90
91         mcpm_set_entry_vector(cpu, cluster, cpu_resume);
92         mcpm_cpu_suspend(0);  /* 0 should be replaced with better value here */
93         return 1;
94 }
95
96 /*
97  * bl_enter_powerdown - Programs CPU to enter the specified state
98  * @dev: cpuidle device
99  * @drv: The target state to be programmed
100  * @idx: state index
101  *
102  * Called from the CPUidle framework to program the device to the
103  * specified target state selected by the governor.
104  */
105 static int bl_enter_powerdown(struct cpuidle_device *dev,
106                                 struct cpuidle_driver *drv, int idx)
107 {
108         struct timespec ts_preidle, ts_postidle, ts_idle;
109         int ret;
110
111         /* Used to keep track of the total time in idle */
112         getnstimeofday(&ts_preidle);
113
114         BUG_ON(!irqs_disabled());
115
116         cpu_pm_enter();
117
118         clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu);
119
120         ret = cpu_suspend((unsigned long) dev, bl_powerdown_finisher);
121         if (ret)
122                 BUG();
123
124         mcpm_cpu_powered_up();
125
126         clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
127
128         cpu_pm_exit();
129
130         getnstimeofday(&ts_postidle);
131         local_irq_enable();
132         ts_idle = timespec_sub(ts_postidle, ts_preidle);
133
134         dev->last_residency = ts_idle.tv_nsec / NSEC_PER_USEC +
135                                         ts_idle.tv_sec * USEC_PER_SEC;
136         return idx;
137 }
138
139 /*
140  * bl_idle_init
141  *
142  * Registers the bl specific cpuidle driver with the cpuidle
143  * framework with the valid set of states.
144  */
145 int __init bl_idle_init(void)
146 {
147         struct cpuidle_device *dev;
148         int i, cpu_id;
149         struct cpuidle_driver *drv = &bl_idle_driver;
150
151         if (!of_find_compatible_node(NULL, NULL, "arm,generic")) {
152                 pr_info("%s: No compatible node found\n", __func__);
153                 return -ENODEV;
154         }
155
156         drv->state_count = (sizeof(bl_cpuidle_set) /
157                                        sizeof(struct cpuidle_state));
158
159         for (i = 0; i < drv->state_count; i++) {
160                 memcpy(&drv->states[i], &bl_cpuidle_set[i],
161                                 sizeof(struct cpuidle_state));
162         }
163
164         cpuidle_register_driver(drv);
165
166         for_each_cpu(cpu_id, cpu_online_mask) {
167                 pr_err("CPUidle for CPU%d registered\n", cpu_id);
168                 dev = &per_cpu(bl_idle_dev, cpu_id);
169                 dev->cpu = cpu_id;
170
171                 dev->state_count = drv->state_count;
172
173                 if (cpuidle_register_device(dev)) {
174                         printk(KERN_ERR "%s: Cpuidle register device failed\n",
175                                __func__);
176                         return -EIO;
177                 }
178         }
179
180         return 0;
181 }
182
183 device_initcall(bl_idle_init);