upload tizen1.0 source
[kernel/linux-2.6.36.git] / arch / arm / mach-s5pv310 / pm-hotplug.c
1 /* linux/arch/arm/mach-s5pv310/pm-hotplug.c
2  *
3  * Copyright (c) 2010 Samsung Electronics Co., Ltd.
4  *              http://www.samsung.com/
5  *
6  * S5PV310 - Dynamic CPU hotpluging
7  *
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.
11 */
12
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>
19 #include <linux/io.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>
28
29 #include <plat/map-base.h>
30 #include <plat/gpio-cfg.h>
31
32 #include <mach/regs-gpio.h>
33 #include <mach/regs-irq.h>
34 #include <linux/gpio.h>
35
36 #define CHECK_DELAY     (HZ / 5)
37 #define TRANS_LOAD_L    20
38 #define TRANS_LOAD_H    60
39
40 #define HOTPLUG_UNLOCKED 0
41 #define HOTPLUG_LOCKED 1
42
43 static struct workqueue_struct *hotplug_wq;
44
45 static struct delayed_work hotplug_work;
46
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);
55
56 struct cpu_time_info {
57         cputime64_t prev_cpu_idle;
58         cputime64_t prev_cpu_wall;
59         unsigned int load;
60 };
61
62 static DEFINE_PER_CPU(struct cpu_time_info, hotplug_cpu_time);
63
64 /* To synchronize with cpuidle.c */
65 extern spinlock_t idle_lock;
66 extern bool hotplugging;
67 extern bool idling;
68
69 static int skip_pm_hotplug;
70
71 static void hotplug_timer(struct work_struct *work);
72 void s5pv310_pm_hotplug_tickle(void)
73 {
74         if (!hotplug_wq)
75                 return;
76
77         /* Sync issue vs Overhead */
78         if (skip_pm_hotplug == 0) {
79                 skip_pm_hotplug = 2;
80                 cancel_delayed_work(&hotplug_work);
81                 hotplug_timer(&hotplug_work.work);
82         } else
83                 skip_pm_hotplug = 2;
84 }
85 EXPORT_SYMBOL(s5pv310_pm_hotplug_tickle);
86
87 static void hotplug_timer(struct work_struct *work)
88 {
89         unsigned int i, avg_load = 0, load = 0;
90         unsigned int cur_freq;
91
92         if (user_lock == 1)
93                 goto no_hotplug;
94
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;
99
100                 tmp_info = &per_cpu(hotplug_cpu_time, i);
101
102                 cur_idle_time = get_cpu_idle_time_us(i, &cur_wall_time);
103
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;
107
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;
111
112                 if(wall_time < idle_time)
113                         goto no_hotplug;
114
115                 tmp_info->load = 100 * (wall_time - idle_time) / wall_time;
116
117                 load += tmp_info->load;
118         }
119
120         avg_load = load / num_online_cpus();
121
122         cur_freq = cpufreq_get(0);
123
124         spin_lock(&idle_lock);
125         if (hotplugging) {
126                 /* Multiple pm-hotplug routines exist. Remove this. */
127                 spin_unlock(&idle_lock);
128                 return;
129         }
130
131         hotplugging = true;
132         while (idling) {
133                 spin_unlock(&idle_lock);
134                 schedule(); /* FIXME: Sometimes, an indefinite loop incurs. */
135                 spin_lock(&idle_lock);
136         }
137         spin_unlock(&idle_lock);
138
139         if (skip_pm_hotplug == 0) {
140                 if ((avg_load < trans_load_l) && (cpu_online(1) == 1) &&
141                     (cur_freq <= 200 * 1000))
142                         cpu_down(1);
143
144                 else if ((avg_load > trans_load_h) && (cpu_online(1) == 0))
145                         cpu_up(1);
146         } else if (cpu_online(1) == 0)
147                 cpu_up(1);
148
149         spin_lock(&idle_lock);
150         hotplugging = false;
151         spin_unlock(&idle_lock);
152
153 no_hotplug:
154         if (skip_pm_hotplug > 0)
155                 skip_pm_hotplug--;
156
157         if (!delayed_work_pending(&hotplug_work))
158                 queue_delayed_work_on(0, hotplug_wq, &hotplug_work,
159                                       hotpluging_rate);
160 }
161
162 static int __init s5pv310_pm_hotplug_init(void)
163 {
164         printk(KERN_INFO "SMDKV310 PM-hotplug init function\n");
165         hotplug_wq = create_freezeable_workqueue("dynamic hotplug");
166         if (!hotplug_wq) {
167                 printk(KERN_ERR "Creation of hotplug work failed\n");
168                 return -EFAULT;
169         }
170
171         hotpluging_rate = CHECK_DELAY;
172         user_lock = 0;
173
174         INIT_DELAYED_WORK_DEFERRABLE(&hotplug_work, hotplug_timer);
175         queue_delayed_work_on(0, hotplug_wq, &hotplug_work, hotpluging_rate);
176
177         return 0;
178 }
179 late_initcall(s5pv310_pm_hotplug_init);
180
181 static ssize_t user_lock_show(struct device *dev, struct device_attribute *attr,
182         char *buf)
183 {
184         return sprintf(buf, "%u\n", user_lock);
185 }
186
187 static ssize_t user_lock_store(struct device *dev, struct device_attribute *attr,
188         const char *buf, size_t count)
189 {
190         int ret;
191
192         ret = sscanf(buf, "%u", &user_lock);
193         if (ret != 1)
194                 return -EINVAL;
195
196         return count;
197 }
198 static DEVICE_ATTR(user_lock, 0644, user_lock_show, user_lock_store);
199
200 static ssize_t pm_hotplug_rate_show(struct device *dev, struct device_attribute *attr,
201         char *buf)
202 {
203         return sprintf(buf, "%u\n", hotpluging_rate);
204 }
205
206 static ssize_t pm_hotplug_rate_store(struct device *dev, struct device_attribute *attr,
207         const char *buf, size_t count)
208 {
209         int ret;
210
211         ret = sscanf(buf, "%u", &hotpluging_rate);
212         if (ret != 0)
213                 return -EINVAL;
214
215         return count;
216 }
217 static DEVICE_ATTR(pm_hotplug_rate, 0644, pm_hotplug_rate_show,
218                                 pm_hotplug_rate_store);
219
220 static int sysfs_pm_hotplug_create(struct device *dev)
221 {
222         int ret;
223
224         ret = device_create_file(dev, &dev_attr_user_lock);
225
226         if (ret)
227                 return ret;
228
229         ret = device_create_file(dev, &dev_attr_pm_hotplug_rate);
230
231         if (ret)
232         goto failed;
233
234         return ret;
235
236
237 failed:
238         device_remove_file(dev, &dev_attr_user_lock);
239
240         return ret;
241 }
242 static void sysfs_pm_hotplug_remove(struct device *dev)
243 {
244         device_remove_file(dev, &dev_attr_user_lock);
245         device_remove_file(dev, &dev_attr_pm_hotplug_rate);
246 }
247
248 static struct platform_device s5pv310_pm_hotplug_device = {
249         .name   = "s5pv310-dynamic-cpu-hotplug",
250         .id     = -1,
251 };
252
253 static int __init s5pv310_pm_hotplug_device_init(void)
254 {
255         int ret;
256
257         skip_pm_hotplug = 0;
258         ret = platform_device_register(&s5pv310_pm_hotplug_device);
259
260         if (ret) {
261                 printk(KERN_ERR "failed at(%d)\n", __LINE__);
262                 return ret;
263         }
264
265         ret = sysfs_pm_hotplug_create(&s5pv310_pm_hotplug_device.dev);
266
267         if (ret) {
268                 printk(KERN_ERR "failed at(%d)\n", __LINE__);
269                 goto sysfs_err;
270         }
271
272         printk(KERN_INFO "s5pv310_pm_hotplug_device_init: %d\n", ret);
273
274         return ret;
275
276 sysfs_err:
277         platform_device_unregister(&s5pv310_pm_hotplug_device);
278         return ret;
279 }
280
281 late_initcall(s5pv310_pm_hotplug_device_init);