1 /* linux/arch/arm/mach-s5pv310/pm-hotplug.c
3 * Copyright (c) 2010 Samsung Electronics Co., Ltd.
4 * http://www.samsung.com/
6 * S5PV310 - Dynamic CPU hotpluging
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
13 #include <linux/init.h>
14 #include <linux/interrupt.h>
15 #include <linux/irq.h>
16 #include <linux/ioport.h>
17 #include <linux/delay.h>
18 #include <linux/serial_core.h>
20 #include <linux/platform_device.h>
21 #include <linux/cpu.h>
22 #include <linux/percpu.h>
23 #include <linux/ktime.h>
24 #include <linux/tick.h>
25 #include <linux/kernel_stat.h>
26 #include <linux/sched.h>
27 #include <linux/cpufreq.h>
29 #include <plat/map-base.h>
30 #include <plat/gpio-cfg.h>
32 #include <mach/regs-gpio.h>
33 #include <mach/regs-irq.h>
34 #include <linux/gpio.h>
36 #define CHECK_DELAY (HZ / 5)
37 #define TRANS_LOAD_L 20
38 #define TRANS_LOAD_H 60
40 #define HOTPLUG_UNLOCKED 0
41 #define HOTPLUG_LOCKED 1
43 static struct workqueue_struct *hotplug_wq;
45 static struct delayed_work hotplug_work;
47 static unsigned int hotpluging_rate;
48 module_param_named(rate, hotpluging_rate, uint, 0644);
49 static unsigned int user_lock;
50 module_param_named(lock, user_lock, uint, 0644);
51 static unsigned int trans_load_l = TRANS_LOAD_L;
52 module_param_named(loadl, trans_load_l, uint, 0644);
53 static unsigned int trans_load_h = TRANS_LOAD_H;
54 module_param_named(loadh, trans_load_h, uint, 0644);
56 struct cpu_time_info {
57 cputime64_t prev_cpu_idle;
58 cputime64_t prev_cpu_wall;
62 static DEFINE_PER_CPU(struct cpu_time_info, hotplug_cpu_time);
64 /* To synchronize with cpuidle.c */
65 extern spinlock_t idle_lock;
66 extern bool hotplugging;
69 static int skip_pm_hotplug;
71 static void hotplug_timer(struct work_struct *work);
72 void s5pv310_pm_hotplug_tickle(void)
77 /* Sync issue vs Overhead */
78 if (skip_pm_hotplug == 0) {
80 cancel_delayed_work(&hotplug_work);
81 hotplug_timer(&hotplug_work.work);
85 EXPORT_SYMBOL(s5pv310_pm_hotplug_tickle);
87 static void hotplug_timer(struct work_struct *work)
89 unsigned int i, avg_load = 0, load = 0;
90 unsigned int cur_freq;
95 for_each_online_cpu(i) {
96 struct cpu_time_info *tmp_info;
97 cputime64_t cur_wall_time, cur_idle_time;
98 unsigned int idle_time, wall_time;
100 tmp_info = &per_cpu(hotplug_cpu_time, i);
102 cur_idle_time = get_cpu_idle_time_us(i, &cur_wall_time);
104 idle_time = (unsigned int)cputime64_sub(cur_idle_time,
105 tmp_info->prev_cpu_idle);
106 tmp_info->prev_cpu_idle = cur_idle_time;
108 wall_time = (unsigned int)cputime64_sub(cur_wall_time,
109 tmp_info->prev_cpu_wall);
110 tmp_info->prev_cpu_wall = cur_wall_time;
112 if(wall_time < idle_time)
115 tmp_info->load = 100 * (wall_time - idle_time) / wall_time;
117 load += tmp_info->load;
120 avg_load = load / num_online_cpus();
122 cur_freq = cpufreq_get(0);
124 spin_lock(&idle_lock);
126 /* Multiple pm-hotplug routines exist. Remove this. */
127 spin_unlock(&idle_lock);
133 spin_unlock(&idle_lock);
134 schedule(); /* FIXME: Sometimes, an indefinite loop incurs. */
135 spin_lock(&idle_lock);
137 spin_unlock(&idle_lock);
139 if (skip_pm_hotplug == 0) {
140 if ((avg_load < trans_load_l) && (cpu_online(1) == 1) &&
141 (cur_freq <= 200 * 1000))
144 else if ((avg_load > trans_load_h) && (cpu_online(1) == 0))
146 } else if (cpu_online(1) == 0)
149 spin_lock(&idle_lock);
151 spin_unlock(&idle_lock);
154 if (skip_pm_hotplug > 0)
157 if (!delayed_work_pending(&hotplug_work))
158 queue_delayed_work_on(0, hotplug_wq, &hotplug_work,
162 static int __init s5pv310_pm_hotplug_init(void)
164 printk(KERN_INFO "SMDKV310 PM-hotplug init function\n");
165 hotplug_wq = create_freezeable_workqueue("dynamic hotplug");
167 printk(KERN_ERR "Creation of hotplug work failed\n");
171 hotpluging_rate = CHECK_DELAY;
174 INIT_DELAYED_WORK_DEFERRABLE(&hotplug_work, hotplug_timer);
175 queue_delayed_work_on(0, hotplug_wq, &hotplug_work, hotpluging_rate);
179 late_initcall(s5pv310_pm_hotplug_init);
181 static ssize_t user_lock_show(struct device *dev, struct device_attribute *attr,
184 return sprintf(buf, "%u\n", user_lock);
187 static ssize_t user_lock_store(struct device *dev, struct device_attribute *attr,
188 const char *buf, size_t count)
192 ret = sscanf(buf, "%u", &user_lock);
198 static DEVICE_ATTR(user_lock, 0644, user_lock_show, user_lock_store);
200 static ssize_t pm_hotplug_rate_show(struct device *dev, struct device_attribute *attr,
203 return sprintf(buf, "%u\n", hotpluging_rate);
206 static ssize_t pm_hotplug_rate_store(struct device *dev, struct device_attribute *attr,
207 const char *buf, size_t count)
211 ret = sscanf(buf, "%u", &hotpluging_rate);
217 static DEVICE_ATTR(pm_hotplug_rate, 0644, pm_hotplug_rate_show,
218 pm_hotplug_rate_store);
220 static int sysfs_pm_hotplug_create(struct device *dev)
224 ret = device_create_file(dev, &dev_attr_user_lock);
229 ret = device_create_file(dev, &dev_attr_pm_hotplug_rate);
238 device_remove_file(dev, &dev_attr_user_lock);
242 static void sysfs_pm_hotplug_remove(struct device *dev)
244 device_remove_file(dev, &dev_attr_user_lock);
245 device_remove_file(dev, &dev_attr_pm_hotplug_rate);
248 static struct platform_device s5pv310_pm_hotplug_device = {
249 .name = "s5pv310-dynamic-cpu-hotplug",
253 static int __init s5pv310_pm_hotplug_device_init(void)
258 ret = platform_device_register(&s5pv310_pm_hotplug_device);
261 printk(KERN_ERR "failed at(%d)\n", __LINE__);
265 ret = sysfs_pm_hotplug_create(&s5pv310_pm_hotplug_device.dev);
268 printk(KERN_ERR "failed at(%d)\n", __LINE__);
272 printk(KERN_INFO "s5pv310_pm_hotplug_device_init: %d\n", ret);
277 platform_device_unregister(&s5pv310_pm_hotplug_device);
281 late_initcall(s5pv310_pm_hotplug_device_init);