1 /* #define ITC_PREEMPT_HACK */
2 /* uncomment the above if APC blatantly lies and PREEMPTION is enabled */
3 #include <linux/signal.h>
4 #include <linux/sched.h>
5 #include <linux/interrupt.h>
7 #include <linux/version.h>
8 #include <linux/vmalloc.h>
9 #include <linux/module.h>
10 #include <linux/delay.h>
11 #include <linux/init.h>
12 #include <linux/slab.h>
13 #include <linux/smp.h>
17 #include <asm/system.h>
18 #include <asm/uaccess.h>
21 #include <asm/machdep.h>
22 #define pm_idle ppc_md.power_save
25 #if !(defined CONFIG_X86 || defined CONFIG_6xx)
26 #error Support for this architecture is not written yet
29 #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 6, 0)
30 #include <linux/wrapper.h>
31 #include <linux/smp_lock.h>
33 #error Support for SMP on 2.4 series of kernels is not written yet
35 #define num_present_cpus() 1
36 #define num_online_cpus() 1
37 /* #define cpu_online(n) 1 */
38 #define cpu_present(n) 1
41 #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 4, 20)
42 #define iminor(inode) MINOR((inode)->i_rdev)
44 #define iminor(inode) minor((inode)->i_rdev)
49 #define itc_enter_bkl() do { \
53 #define itc_leave_bkl() do { \
58 #define itc_enter_bkl lock_kernel
59 #define itc_leave_bkl unlock_kernel
60 #ifdef ITC_PREEMPT_HACK
61 #error Attempt to enable ITC_PREEMPT_HACK on non preemptible kernel
65 MODULE_DESCRIPTION ("Idle time collector");
67 static void (*idle_func
) (void);
70 /* there are many ways to prevent gcc from complaining about module_param
71 and function pointer vs long, but let's not */
72 #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 6, 0)
73 MODULE_PARM (idle_func
, "l");
75 module_param (idle_func
, long, 0777);
77 MODULE_PARM_DESC (idle_func
, "address of default idle function");
81 static spinlock_t lock
= SPIN_LOCK_UNLOCKED
;
83 static void (*orig_pm_idle
) (void);
84 static unsigned int itc_major
;
88 struct timeval cumm_sleep_time
;
89 struct timeval sleep_started
;
93 static struct itc global_itc
[NR_CPUS
];
95 /**********************************************************************
99 **********************************************************************/
101 cpeamb (struct timeval
*c
, struct timeval
*a
, struct timeval
*b
)
103 __typeof (c
->tv_sec
) sec
= a
->tv_sec
- b
->tv_sec
;
104 __typeof (c
->tv_usec
) usec
= a
->tv_usec
- b
->tv_usec
;
109 usec
= 1000000 + usec
;
112 if (c
->tv_usec
> 1000000)
114 c
->tv_sec
+= sec
+ 1;
115 c
->tv_usec
-= 1000000;
123 #if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 6, 0)
125 /**********************************************************************
127 * Dummy to make sure we are unloaded properly
129 **********************************************************************/
131 dummy_wakeup (void *unused
)
133 printk (KERN_DEBUG
"waking up %d\n", smp_processor_id ());
139 /**********************************************************************
143 **********************************************************************/
144 #if LINUX_VERSION_CODE > KERNEL_VERSION (2, 6, 0)
149 void default_idle (void);
153 itc_monotonic (struct timeval
*tv
)
155 do_gettimeofday (tv
);
165 #ifdef ITC_PREEMPT_HACK
169 /* printk ("idle in %d\n", smp_processor_id ()); */
170 spin_lock_irqsave (&lock
, flags
);
171 itc
= &global_itc
[smp_processor_id ()];
172 itc_monotonic (&itc
->sleep_started
);
174 spin_unlock_irqrestore (&lock
, flags
);
203 spin_lock_irqsave (&lock
, flags
);
205 cpeamb (&itc
->cumm_sleep_time
, &tv
, &itc
->sleep_started
);
207 spin_unlock_irqrestore (&lock
, flags
);
208 /* printk ("idle out %d\n", smp_processor_id ()); */
210 #ifdef ITC_PREEMPT_HACK
215 /**********************************************************************
219 **********************************************************************/
221 itc_open (struct inode
* inode
, struct file
* file
);
224 itc_release (struct inode
* inode
, struct file
* file
);
227 itc_ioctl (struct inode
* inode
, struct file
* file
,
228 unsigned int cmd
, unsigned long arg
);
231 itc_read (struct file
* file
, char * buf
, size_t count
, loff_t
* ppos
);
233 static struct file_operations itc_fops
=
235 .owner
= THIS_MODULE
,
237 .release
= itc_release
,
244 itc_release (struct inode
* inode
, struct file
* filp
)
247 pm_idle
= orig_pm_idle
;
249 #if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 6, 0)
251 on_each_cpu (dummy_wakeup
, NULL
, 0, 1);
257 itc_open (struct inode
* inode
, struct file
* filp
)
260 const struct file_operations
*old_fops
= filp
->f_op
;
261 unsigned int minor
= iminor (inode
);
268 /* old_fops = filp->f_op; */
269 filp
->f_op
= fops_get (&itc_fops
);
273 if (pm_idle
!= itc_idle
)
275 orig_pm_idle
= pm_idle
;
284 itc_read (struct file
*file
, char * buf
, size_t count
, loff_t
* ppos
)
287 size_t itemsize
= sizeof (global_itc
[0].cumm_sleep_time
);
290 struct itc
*itc
= &global_itc
[0];
292 if (count
< itemsize
* num_present_cpus ())
295 "attempt to read something funny %d expected %d(%d,%d)\n",
296 count
, itemsize
* num_present_cpus (),
297 itemsize
, num_present_cpus ());
301 spin_lock_irqsave (&lock
, flags
);
302 for (i
= 0; i
< NR_CPUS
; ++i
, ++itc
)
311 cpeamb (&itc
->cumm_sleep_time
, &tv
, &itc
->sleep_started
);
312 itc
->sleep_started
.tv_sec
= tv
.tv_sec
;
313 itc
->sleep_started
.tv_usec
= tv
.tv_usec
;
316 if (copy_to_user (buf
, &itc
->cumm_sleep_time
, itemsize
))
318 printk (KERN_ERR
"failed to write %zu bytes to %p\n",
327 spin_unlock_irqrestore (&lock
, flags
);
332 /**********************************************************************
336 **********************************************************************/
338 itc_ioctl (struct inode
* inode
, struct file
* filp
,
339 unsigned int cmd
, unsigned long arg
)
344 /**********************************************************************
348 **********************************************************************/
355 if (!pm_idle
&& !idle_func
)
359 "itc: no idle function\n"
360 "itc: boot kernel with idle=halt option\n"
361 "itc: or specify idle_func (modprobe itc idle_func=<address>)\n");
366 err
= register_chrdev (itc_major
, DEVNAME
, &itc_fops
);
367 if (err
< 0 || ((itc_major
&& err
) || (!itc_major
&& !err
)))
369 printk (KERN_ERR
"itc: register_chrdev failed itc_major=%d err=%d\n",
379 orig_pm_idle
= pm_idle
;
382 "itc: driver loaded pm_idle=%p default_idle=%p, idle_func=%p\n",
391 printk (KERN_DEBUG
"itc: CPUs(%d present=%d online=%d)"
401 #ifdef CONFIG_PREEMPT
405 NR_CPUS
, num_present_cpus (), num_online_cpus ());
409 /**********************************************************************
413 **********************************************************************/
417 printk (KERN_DEBUG
"itc: unloading (resetting pm_idle to %p)\n",
419 unregister_chrdev (itc_major
, DEVNAME
);
420 printk (KERN_DEBUG
"itc: unloaded\n");