forked from Minki/linux
e99e88a9d2
This converts all remaining cases of the old setup_timer() API into using timer_setup(), where the callback argument is the structure already holding the struct timer_list. These should have no behavioral changes, since they just change which pointer is passed into the callback with the same available pointers after conversion. It handles the following examples, in addition to some other variations. Casting from unsigned long: void my_callback(unsigned long data) { struct something *ptr = (struct something *)data; ... } ... setup_timer(&ptr->my_timer, my_callback, ptr); and forced object casts: void my_callback(struct something *ptr) { ... } ... setup_timer(&ptr->my_timer, my_callback, (unsigned long)ptr); become: void my_callback(struct timer_list *t) { struct something *ptr = from_timer(ptr, t, my_timer); ... } ... timer_setup(&ptr->my_timer, my_callback, 0); Direct function assignments: void my_callback(unsigned long data) { struct something *ptr = (struct something *)data; ... } ... ptr->my_timer.function = my_callback; have a temporary cast added, along with converting the args: void my_callback(struct timer_list *t) { struct something *ptr = from_timer(ptr, t, my_timer); ... } ... ptr->my_timer.function = (TIMER_FUNC_TYPE)my_callback; And finally, callbacks without a data assignment: void my_callback(unsigned long data) { ... } ... setup_timer(&ptr->my_timer, my_callback, 0); have their argument renamed to verify they're unused during conversion: void my_callback(struct timer_list *unused) { ... } ... timer_setup(&ptr->my_timer, my_callback, 0); The conversion is done with the following Coccinelle script: spatch --very-quiet --all-includes --include-headers \ -I ./arch/x86/include -I ./arch/x86/include/generated \ -I ./include -I ./arch/x86/include/uapi \ -I ./arch/x86/include/generated/uapi -I ./include/uapi \ -I ./include/generated/uapi --include ./include/linux/kconfig.h \ --dir . \ --cocci-file ~/src/data/timer_setup.cocci @fix_address_of@ expression e; @@ setup_timer( -&(e) +&e , ...) // Update any raw setup_timer() usages that have a NULL callback, but // would otherwise match change_timer_function_usage, since the latter // will update all function assignments done in the face of a NULL // function initialization in setup_timer(). @change_timer_function_usage_NULL@ expression _E; identifier _timer; type _cast_data; @@ ( -setup_timer(&_E->_timer, NULL, _E); +timer_setup(&_E->_timer, NULL, 0); | -setup_timer(&_E->_timer, NULL, (_cast_data)_E); +timer_setup(&_E->_timer, NULL, 0); | -setup_timer(&_E._timer, NULL, &_E); +timer_setup(&_E._timer, NULL, 0); | -setup_timer(&_E._timer, NULL, (_cast_data)&_E); +timer_setup(&_E._timer, NULL, 0); ) @change_timer_function_usage@ expression _E; identifier _timer; struct timer_list _stl; identifier _callback; type _cast_func, _cast_data; @@ ( -setup_timer(&_E->_timer, _callback, _E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, &_callback, _E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, _callback, (_cast_data)_E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, &_callback, (_cast_data)_E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, (_cast_func)_callback, _E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, (_cast_func)&_callback, _E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, (_cast_func)_callback, (_cast_data)_E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, (_cast_func)&_callback, (_cast_data)_E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E._timer, _callback, (_cast_data)_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, _callback, (_cast_data)&_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, &_callback, (_cast_data)_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, &_callback, (_cast_data)&_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)&_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)&_E); +timer_setup(&_E._timer, _callback, 0); | _E->_timer@_stl.function = _callback; | _E->_timer@_stl.function = &_callback; | _E->_timer@_stl.function = (_cast_func)_callback; | _E->_timer@_stl.function = (_cast_func)&_callback; | _E._timer@_stl.function = _callback; | _E._timer@_stl.function = &_callback; | _E._timer@_stl.function = (_cast_func)_callback; | _E._timer@_stl.function = (_cast_func)&_callback; ) // callback(unsigned long arg) @change_callback_handle_cast depends on change_timer_function_usage@ identifier change_timer_function_usage._callback; identifier change_timer_function_usage._timer; type _origtype; identifier _origarg; type _handletype; identifier _handle; @@ void _callback( -_origtype _origarg +struct timer_list *t ) { ( ... when != _origarg _handletype *_handle = -(_handletype *)_origarg; +from_timer(_handle, t, _timer); ... when != _origarg | ... when != _origarg _handletype *_handle = -(void *)_origarg; +from_timer(_handle, t, _timer); ... when != _origarg | ... when != _origarg _handletype *_handle; ... when != _handle _handle = -(_handletype *)_origarg; +from_timer(_handle, t, _timer); ... when != _origarg | ... when != _origarg _handletype *_handle; ... when != _handle _handle = -(void *)_origarg; +from_timer(_handle, t, _timer); ... when != _origarg ) } // callback(unsigned long arg) without existing variable @change_callback_handle_cast_no_arg depends on change_timer_function_usage && !change_callback_handle_cast@ identifier change_timer_function_usage._callback; identifier change_timer_function_usage._timer; type _origtype; identifier _origarg; type _handletype; @@ void _callback( -_origtype _origarg +struct timer_list *t ) { + _handletype *_origarg = from_timer(_origarg, t, _timer); + ... when != _origarg - (_handletype *)_origarg + _origarg ... when != _origarg } // Avoid already converted callbacks. @match_callback_converted depends on change_timer_function_usage && !change_callback_handle_cast && !change_callback_handle_cast_no_arg@ identifier change_timer_function_usage._callback; identifier t; @@ void _callback(struct timer_list *t) { ... } // callback(struct something *handle) @change_callback_handle_arg depends on change_timer_function_usage && !match_callback_converted && !change_callback_handle_cast && !change_callback_handle_cast_no_arg@ identifier change_timer_function_usage._callback; identifier change_timer_function_usage._timer; type _handletype; identifier _handle; @@ void _callback( -_handletype *_handle +struct timer_list *t ) { + _handletype *_handle = from_timer(_handle, t, _timer); ... } // If change_callback_handle_arg ran on an empty function, remove // the added handler. @unchange_callback_handle_arg depends on change_timer_function_usage && change_callback_handle_arg@ identifier change_timer_function_usage._callback; identifier change_timer_function_usage._timer; type _handletype; identifier _handle; identifier t; @@ void _callback(struct timer_list *t) { - _handletype *_handle = from_timer(_handle, t, _timer); } // We only want to refactor the setup_timer() data argument if we've found // the matching callback. This undoes changes in change_timer_function_usage. @unchange_timer_function_usage depends on change_timer_function_usage && !change_callback_handle_cast && !change_callback_handle_cast_no_arg && !change_callback_handle_arg@ expression change_timer_function_usage._E; identifier change_timer_function_usage._timer; identifier change_timer_function_usage._callback; type change_timer_function_usage._cast_data; @@ ( -timer_setup(&_E->_timer, _callback, 0); +setup_timer(&_E->_timer, _callback, (_cast_data)_E); | -timer_setup(&_E._timer, _callback, 0); +setup_timer(&_E._timer, _callback, (_cast_data)&_E); ) // If we fixed a callback from a .function assignment, fix the // assignment cast now. @change_timer_function_assignment depends on change_timer_function_usage && (change_callback_handle_cast || change_callback_handle_cast_no_arg || change_callback_handle_arg)@ expression change_timer_function_usage._E; identifier change_timer_function_usage._timer; identifier change_timer_function_usage._callback; type _cast_func; typedef TIMER_FUNC_TYPE; @@ ( _E->_timer.function = -_callback +(TIMER_FUNC_TYPE)_callback ; | _E->_timer.function = -&_callback +(TIMER_FUNC_TYPE)_callback ; | _E->_timer.function = -(_cast_func)_callback; +(TIMER_FUNC_TYPE)_callback ; | _E->_timer.function = -(_cast_func)&_callback +(TIMER_FUNC_TYPE)_callback ; | _E._timer.function = -_callback +(TIMER_FUNC_TYPE)_callback ; | _E._timer.function = -&_callback; +(TIMER_FUNC_TYPE)_callback ; | _E._timer.function = -(_cast_func)_callback +(TIMER_FUNC_TYPE)_callback ; | _E._timer.function = -(_cast_func)&_callback +(TIMER_FUNC_TYPE)_callback ; ) // Sometimes timer functions are called directly. Replace matched args. @change_timer_function_calls depends on change_timer_function_usage && (change_callback_handle_cast || change_callback_handle_cast_no_arg || change_callback_handle_arg)@ expression _E; identifier change_timer_function_usage._timer; identifier change_timer_function_usage._callback; type _cast_data; @@ _callback( ( -(_cast_data)_E +&_E->_timer | -(_cast_data)&_E +&_E._timer | -_E +&_E->_timer ) ) // If a timer has been configured without a data argument, it can be // converted without regard to the callback argument, since it is unused. @match_timer_function_unused_data@ expression _E; identifier _timer; identifier _callback; @@ ( -setup_timer(&_E->_timer, _callback, 0); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, _callback, 0L); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, _callback, 0UL); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E._timer, _callback, 0); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, _callback, 0L); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, _callback, 0UL); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_timer, _callback, 0); +timer_setup(&_timer, _callback, 0); | -setup_timer(&_timer, _callback, 0L); +timer_setup(&_timer, _callback, 0); | -setup_timer(&_timer, _callback, 0UL); +timer_setup(&_timer, _callback, 0); | -setup_timer(_timer, _callback, 0); +timer_setup(_timer, _callback, 0); | -setup_timer(_timer, _callback, 0L); +timer_setup(_timer, _callback, 0); | -setup_timer(_timer, _callback, 0UL); +timer_setup(_timer, _callback, 0); ) @change_callback_unused_data depends on match_timer_function_unused_data@ identifier match_timer_function_unused_data._callback; type _origtype; identifier _origarg; @@ void _callback( -_origtype _origarg +struct timer_list *unused ) { ... when != _origarg } Signed-off-by: Kees Cook <keescook@chromium.org>
271 lines
6.2 KiB
C
271 lines
6.2 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* temp.c Thermal management for cpu's with Thermal Assist Units
|
|
*
|
|
* Written by Troy Benjegerdes <hozer@drgw.net>
|
|
*
|
|
* TODO:
|
|
* dynamic power management to limit peak CPU temp (using ICTC)
|
|
* calibration???
|
|
*
|
|
* Silly, crazy ideas: use cpu load (from scheduler) and ICTC to extend battery
|
|
* life in portables, and add a 'performance/watt' metric somewhere in /proc
|
|
*/
|
|
|
|
#include <linux/errno.h>
|
|
#include <linux/jiffies.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/param.h>
|
|
#include <linux/string.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/init.h>
|
|
|
|
#include <asm/io.h>
|
|
#include <asm/reg.h>
|
|
#include <asm/nvram.h>
|
|
#include <asm/cache.h>
|
|
#include <asm/8xx_immap.h>
|
|
#include <asm/machdep.h>
|
|
|
|
static struct tau_temp
|
|
{
|
|
int interrupts;
|
|
unsigned char low;
|
|
unsigned char high;
|
|
unsigned char grew;
|
|
} tau[NR_CPUS];
|
|
|
|
struct timer_list tau_timer;
|
|
|
|
#undef DEBUG
|
|
|
|
/* TODO: put these in a /proc interface, with some sanity checks, and maybe
|
|
* dynamic adjustment to minimize # of interrupts */
|
|
/* configurable values for step size and how much to expand the window when
|
|
* we get an interrupt. These are based on the limit that was out of range */
|
|
#define step_size 2 /* step size when temp goes out of range */
|
|
#define window_expand 1 /* expand the window by this much */
|
|
/* configurable values for shrinking the window */
|
|
#define shrink_timer 2*HZ /* period between shrinking the window */
|
|
#define min_window 2 /* minimum window size, degrees C */
|
|
|
|
void set_thresholds(unsigned long cpu)
|
|
{
|
|
#ifdef CONFIG_TAU_INT
|
|
/*
|
|
* setup THRM1,
|
|
* threshold, valid bit, enable interrupts, interrupt when below threshold
|
|
*/
|
|
mtspr(SPRN_THRM1, THRM1_THRES(tau[cpu].low) | THRM1_V | THRM1_TIE | THRM1_TID);
|
|
|
|
/* setup THRM2,
|
|
* threshold, valid bit, enable interrupts, interrupt when above threshold
|
|
*/
|
|
mtspr (SPRN_THRM2, THRM1_THRES(tau[cpu].high) | THRM1_V | THRM1_TIE);
|
|
#else
|
|
/* same thing but don't enable interrupts */
|
|
mtspr(SPRN_THRM1, THRM1_THRES(tau[cpu].low) | THRM1_V | THRM1_TID);
|
|
mtspr(SPRN_THRM2, THRM1_THRES(tau[cpu].high) | THRM1_V);
|
|
#endif
|
|
}
|
|
|
|
void TAUupdate(int cpu)
|
|
{
|
|
unsigned thrm;
|
|
|
|
#ifdef DEBUG
|
|
printk("TAUupdate ");
|
|
#endif
|
|
|
|
/* if both thresholds are crossed, the step_sizes cancel out
|
|
* and the window winds up getting expanded twice. */
|
|
if((thrm = mfspr(SPRN_THRM1)) & THRM1_TIV){ /* is valid? */
|
|
if(thrm & THRM1_TIN){ /* crossed low threshold */
|
|
if (tau[cpu].low >= step_size){
|
|
tau[cpu].low -= step_size;
|
|
tau[cpu].high -= (step_size - window_expand);
|
|
}
|
|
tau[cpu].grew = 1;
|
|
#ifdef DEBUG
|
|
printk("low threshold crossed ");
|
|
#endif
|
|
}
|
|
}
|
|
if((thrm = mfspr(SPRN_THRM2)) & THRM1_TIV){ /* is valid? */
|
|
if(thrm & THRM1_TIN){ /* crossed high threshold */
|
|
if (tau[cpu].high <= 127-step_size){
|
|
tau[cpu].low += (step_size - window_expand);
|
|
tau[cpu].high += step_size;
|
|
}
|
|
tau[cpu].grew = 1;
|
|
#ifdef DEBUG
|
|
printk("high threshold crossed ");
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
printk("grew = %d\n", tau[cpu].grew);
|
|
#endif
|
|
|
|
#ifndef CONFIG_TAU_INT /* tau_timeout will do this if not using interrupts */
|
|
set_thresholds(cpu);
|
|
#endif
|
|
|
|
}
|
|
|
|
#ifdef CONFIG_TAU_INT
|
|
/*
|
|
* TAU interrupts - called when we have a thermal assist unit interrupt
|
|
* with interrupts disabled
|
|
*/
|
|
|
|
void TAUException(struct pt_regs * regs)
|
|
{
|
|
int cpu = smp_processor_id();
|
|
|
|
irq_enter();
|
|
tau[cpu].interrupts++;
|
|
|
|
TAUupdate(cpu);
|
|
|
|
irq_exit();
|
|
}
|
|
#endif /* CONFIG_TAU_INT */
|
|
|
|
static void tau_timeout(void * info)
|
|
{
|
|
int cpu;
|
|
unsigned long flags;
|
|
int size;
|
|
int shrink;
|
|
|
|
/* disabling interrupts *should* be okay */
|
|
local_irq_save(flags);
|
|
cpu = smp_processor_id();
|
|
|
|
#ifndef CONFIG_TAU_INT
|
|
TAUupdate(cpu);
|
|
#endif
|
|
|
|
size = tau[cpu].high - tau[cpu].low;
|
|
if (size > min_window && ! tau[cpu].grew) {
|
|
/* do an exponential shrink of half the amount currently over size */
|
|
shrink = (2 + size - min_window) / 4;
|
|
if (shrink) {
|
|
tau[cpu].low += shrink;
|
|
tau[cpu].high -= shrink;
|
|
} else { /* size must have been min_window + 1 */
|
|
tau[cpu].low += 1;
|
|
#if 1 /* debug */
|
|
if ((tau[cpu].high - tau[cpu].low) != min_window){
|
|
printk(KERN_ERR "temp.c: line %d, logic error\n", __LINE__);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
tau[cpu].grew = 0;
|
|
|
|
set_thresholds(cpu);
|
|
|
|
/*
|
|
* Do the enable every time, since otherwise a bunch of (relatively)
|
|
* complex sleep code needs to be added. One mtspr every time
|
|
* tau_timeout is called is probably not a big deal.
|
|
*
|
|
* Enable thermal sensor and set up sample interval timer
|
|
* need 20 us to do the compare.. until a nice 'cpu_speed' function
|
|
* call is implemented, just assume a 500 mhz clock. It doesn't really
|
|
* matter if we take too long for a compare since it's all interrupt
|
|
* driven anyway.
|
|
*
|
|
* use a extra long time.. (60 us @ 500 mhz)
|
|
*/
|
|
mtspr(SPRN_THRM3, THRM3_SITV(500*60) | THRM3_E);
|
|
|
|
local_irq_restore(flags);
|
|
}
|
|
|
|
static void tau_timeout_smp(struct timer_list *unused)
|
|
{
|
|
|
|
/* schedule ourselves to be run again */
|
|
mod_timer(&tau_timer, jiffies + shrink_timer) ;
|
|
on_each_cpu(tau_timeout, NULL, 0);
|
|
}
|
|
|
|
/*
|
|
* setup the TAU
|
|
*
|
|
* Set things up to use THRM1 as a temperature lower bound, and THRM2 as an upper bound.
|
|
* Start off at zero
|
|
*/
|
|
|
|
int tau_initialized = 0;
|
|
|
|
void __init TAU_init_smp(void * info)
|
|
{
|
|
unsigned long cpu = smp_processor_id();
|
|
|
|
/* set these to a reasonable value and let the timer shrink the
|
|
* window */
|
|
tau[cpu].low = 5;
|
|
tau[cpu].high = 120;
|
|
|
|
set_thresholds(cpu);
|
|
}
|
|
|
|
int __init TAU_init(void)
|
|
{
|
|
/* We assume in SMP that if one CPU has TAU support, they
|
|
* all have it --BenH
|
|
*/
|
|
if (!cpu_has_feature(CPU_FTR_TAU)) {
|
|
printk("Thermal assist unit not available\n");
|
|
tau_initialized = 0;
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* first, set up the window shrinking timer */
|
|
timer_setup(&tau_timer, tau_timeout_smp, 0);
|
|
tau_timer.expires = jiffies + shrink_timer;
|
|
add_timer(&tau_timer);
|
|
|
|
on_each_cpu(TAU_init_smp, NULL, 0);
|
|
|
|
printk("Thermal assist unit ");
|
|
#ifdef CONFIG_TAU_INT
|
|
printk("using interrupts, ");
|
|
#else
|
|
printk("using timers, ");
|
|
#endif
|
|
printk("shrink_timer: %d jiffies\n", shrink_timer);
|
|
tau_initialized = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
__initcall(TAU_init);
|
|
|
|
/*
|
|
* return current temp
|
|
*/
|
|
|
|
u32 cpu_temp_both(unsigned long cpu)
|
|
{
|
|
return ((tau[cpu].high << 16) | tau[cpu].low);
|
|
}
|
|
|
|
int cpu_temp(unsigned long cpu)
|
|
{
|
|
return ((tau[cpu].high + tau[cpu].low) / 2);
|
|
}
|
|
|
|
int tau_interrupts(unsigned long cpu)
|
|
{
|
|
return (tau[cpu].interrupts);
|
|
}
|