2 * big.LITTLE CPU idle driver.
4 * Copyright (C) 2012 ARM Ltd.
5 * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
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.
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>
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>
31 static int bl_cpuidle_simple_enter(struct cpuidle_device *dev,
32 struct cpuidle_driver *drv, int index)
34 ktime_t time_start, time_end;
37 time_start = ktime_get();
41 time_end = ktime_get();
45 diff = ktime_to_us(ktime_sub(time_end, time_start));
49 dev->last_residency = (int) diff;
54 static int bl_enter_powerdown(struct cpuidle_device *dev,
55 struct cpuidle_driver *drv, int idx);
57 static struct cpuidle_state bl_cpuidle_set[] __initdata = {
59 .enter = bl_cpuidle_simple_enter,
61 .target_residency = 1,
62 .power_usage = UINT_MAX,
63 .flags = CPUIDLE_FLAG_TIME_VALID,
68 .enter = bl_enter_powerdown,
70 .target_residency = 1000,
71 .flags = CPUIDLE_FLAG_TIME_VALID,
73 .desc = "ARM power down",
77 struct cpuidle_driver bl_idle_driver = {
83 static DEFINE_PER_CPU(struct cpuidle_device, bl_idle_dev);
85 static int notrace bl_powerdown_finisher(unsigned long arg)
87 unsigned int mpidr = read_cpuid_mpidr();
88 unsigned int cluster = (mpidr >> 8) & 0xf;
89 unsigned int cpu = mpidr & 0xf;
91 mcpm_set_entry_vector(cpu, cluster, cpu_resume);
92 mcpm_cpu_suspend(0); /* 0 should be replaced with better value here */
97 * bl_enter_powerdown - Programs CPU to enter the specified state
98 * @dev: cpuidle device
99 * @drv: The target state to be programmed
102 * Called from the CPUidle framework to program the device to the
103 * specified target state selected by the governor.
105 static int bl_enter_powerdown(struct cpuidle_device *dev,
106 struct cpuidle_driver *drv, int idx)
108 struct timespec ts_preidle, ts_postidle, ts_idle;
111 /* Used to keep track of the total time in idle */
112 getnstimeofday(&ts_preidle);
114 BUG_ON(!irqs_disabled());
118 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu);
120 ret = cpu_suspend((unsigned long) dev, bl_powerdown_finisher);
124 mcpm_cpu_powered_up();
126 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
130 getnstimeofday(&ts_postidle);
132 ts_idle = timespec_sub(ts_postidle, ts_preidle);
134 dev->last_residency = ts_idle.tv_nsec / NSEC_PER_USEC +
135 ts_idle.tv_sec * USEC_PER_SEC;
142 * Registers the bl specific cpuidle driver with the cpuidle
143 * framework with the valid set of states.
145 int __init bl_idle_init(void)
147 struct cpuidle_device *dev;
149 struct cpuidle_driver *drv = &bl_idle_driver;
151 if (!of_find_compatible_node(NULL, NULL, "arm,generic")) {
152 pr_info("%s: No compatible node found\n", __func__);
156 drv->state_count = (sizeof(bl_cpuidle_set) /
157 sizeof(struct cpuidle_state));
159 for (i = 0; i < drv->state_count; i++) {
160 memcpy(&drv->states[i], &bl_cpuidle_set[i],
161 sizeof(struct cpuidle_state));
164 cpuidle_register_driver(drv);
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);
171 dev->state_count = drv->state_count;
173 if (cpuidle_register_device(dev)) {
174 printk(KERN_ERR "%s: Cpuidle register device failed\n",
183 device_initcall(bl_idle_init);