OpenCores
URL https://opencores.org/ocsvn/openrisc_me/openrisc_me/trunk

Subversion Repositories openrisc_me

[/] [openrisc/] [trunk/] [rtos/] [ecos-2.0/] [packages/] [services/] [power/] [common/] [v2_0/] [src/] [power.cxx] - Blame information for rev 446

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 27 unneback
//==========================================================================
2
//
3
//      power.cxx
4
//
5
//      Main implementation of power management support.
6
//
7
//==========================================================================
8
//####ECOSGPLCOPYRIGHTBEGIN####
9
// -------------------------------------------
10
// This file is part of eCos, the Embedded Configurable Operating System.
11
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
12
//
13
// eCos is free software; you can redistribute it and/or modify it under
14
// the terms of the GNU General Public License as published by the Free
15
// Software Foundation; either version 2 or (at your option) any later version.
16
//
17
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
18
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
19
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20
// for more details.
21
//
22
// You should have received a copy of the GNU General Public License along
23
// with eCos; if not, write to the Free Software Foundation, Inc.,
24
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25
//
26
// As a special exception, if other files instantiate templates or use macros
27
// or inline functions from this file, or you compile this file and link it
28
// with other works to produce a work based on this file, this file does not
29
// by itself cause the resulting work to be covered by the GNU General Public
30
// License. However the source code for this file must still be made available
31
// in accordance with section (3) of the GNU General Public License.
32
//
33
// This exception does not invalidate any other reasons why a work based on
34
// this file might be covered by the GNU General Public License.
35
//
36
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
37
// at http://sources.redhat.com/ecos/ecos-license/
38
// -------------------------------------------
39
//####ECOSGPLCOPYRIGHTEND####
40
//==========================================================================
41
//#####DESCRIPTIONBEGIN####
42
//
43
// Author(s):    bartv
44
// Contributors: bartv
45
// Date:         2001-06-18
46
//
47
//####DESCRIPTIONEND####
48
//
49
//==========================================================================
50
 
51
// Provide the external (non-inline) definitions of the inline functions
52
// in power.h so there's something available in C code when the compiler
53
// chooses not to inline
54
#define POWER_INLINE extern "C"
55
 
56
#include <pkgconf/power.h>
57
#include <cyg/power/power.h>
58
#include <cyg/infra/cyg_type.h>
59
#include <cyg/infra/cyg_ass.h>
60
#include <cyg/hal/hal_tables.h>
61
 
62
// ----------------------------------------------------------------------------
63
// Statics. Most of these are only relevant when a separate power
64
// management thread is being used. Some of these are exported, e.g.
65
// to allow the use of inline functions.
66
 
67
// The current power mode for the system as a whole.
68
PowerMode       __power_mode            = PowerMode_Active;
69
 
70
// The mode that the system should be running at.
71
PowerMode       __power_desired_mode    = PowerMode_Active;
72
 
73
// The policy callback function, if any.
74
__power_policy_callback_t __power_policy_callback = 0;
75
 
76
// This flag is used to abort a mode change. It allows a controller to
77
// call power_set_mode() while the mode is already being changed.
78
static volatile cyg_bool abort_mode_change = false;
79
 
80
#ifdef CYGPKG_POWER_THREAD
81
static unsigned char    power_thread_stack[CYGNUM_POWER_THREAD_STACKSIZE];
82
static cyg_thread       power_thread;
83
// The power management thread's handle is exported to support
84
// operations like changing the thread's priority.
85
cyg_handle_t     power_thread_handle;
86
 
87
// This semaphore is used to wake up the power management thread when there
88
// is work to be done.
89
static cyg_sem_t        power_thread_action;
90
 
91
#else
92
static cyg_bool         power_doing_it      = false;
93
static cyg_uint         power_todo_count    = 0;
94
#endif
95
 
96
// ----------------------------------------------------------------------------
97
// Synchronisation.
98
//
99
// There are two exported functions to worry about: power_set_mode()
100
// and power_set_controller_mode(). There are also two main scenarios:
101
// CYGPKG_POWER_THREAD enabled and CYGPKG_POWER_THREAD_DISABLED.
102
//
103
// If CYGPKG_POWER_THREAD is enabled then any external code may at any
104
// time invoke the exported functions. These are asynchronous calls.
105
// In addition when the power management thread invokes a power
106
// controller that controller may also call the exported functions,
107
// synchronously. In either scenario the calls can return before the
108
// operation has completed, hence the policy callback functionality.
109
//
110
// If CYGPKG_POWER_THREAD is disabled then there may be only one
111
// external call to the exported functions, and the operation must
112
// complete before that call returns. If there are multiple concurrent
113
// external calls then the behaviour of the system is undefined.
114
// Really. It is still possible for power controllers to call the
115
// exported functions synchronously, which complicates things
116
// somewhat.
117
//
118
// The CYGPKG_POWER_THREAD case is the easier to handle. The power
119
// management thread simply loops forever, waiting on a semaphore
120
// until there is some work to be done and then checking internal
121
// state to figure out what that work should be. Some care has to be
122
// taken that the internal state gets updated and read atomically,
123
// which can be achieved by cyg_scheduler_lock() and unlock() calls in
124
// strategic places. Obviously it is undesirable to keep these locks
125
// longer than is absolutely necessary since that would impact
126
// dispatch latency, and in particular power controllers must not be
127
// invoked with the scheduler locked because there are no specific
128
// restrictions on what a controller may or may not do.
129
//
130
// The call graph is something like:
131
//    power_thread_fn()     - the thread entry point, loops waiting on the semaphore
132
//    power_doit()          - do the real work. This can be either a global mode
133
//                            change or one or more individual controller mode changes.
134
//                            Either operation involves iterating through the controllers.
135
//    power_change_controller_mode() - manipulate an individual controller.
136
//
137
// There is one little complication. If during a power_doit()
138
// set_mode() loop there is a call to power_set_mode() then the
139
// current loop should be aborted. This is especially important when
140
// switching to off mode and a controller has decided to cancel this
141
// via another call to set_mode().
142
//
143
// If no separate thread is used then there will only ever be one
144
// external call. That will result in an invocation of
145
// power_nothread_doit(), which in turn calls power_doit() and
146
// power_change_controller_mode() as in the threaded case. A flag is
147
// used so that it is possible to distinguish between external and
148
// synchronous calls, and a counter ensures that synchronous calls are
149
// processed correctly. Recursion is avoided so that stack usage
150
// remains deterministic.
151
//    power_set_mode()/power_set_controller_mode()
152
//    power_nothread_doit()
153
//    power_doit()
154
//    power_change_controller_mode();
155
//
156
// The main fields in the power controller data structures to worry
157
// about are "mode", "desired_mode", and "change_this". "mode" is only
158
// manipulated by the power controller itself, and since all power
159
// controller accesses are serialized no problems arise.
160
// "desired_mode" and "change_this" are updated by power_set_mode()
161
// and power_set_controller_mode(), and read by power_doit(). If a separate
162
// thread is in use then the scheduler lock protects access to thse fields.
163
// Without a separate thread concurrency is not an issue. Obviously there
164
// are other fields and variables, but most of these will only be set during
165
// system start-up and the rest do not require any special attention.
166
 
167
// ----------------------------------------------------------------------------
168
// Do the real work.
169
//
170
// power_change_controller_mode() acts on a single controller. It is invoked only
171
// from power_doit(), either for a global mode change or for an individual mode change.
172
// It should be invoked with the scheduler unlocked - power_doit() is responsible for
173
// synchronizing with the external calls.
174
static inline void
175
power_change_controller_mode(PowerController* controller, PowerMode desired_mode, cyg_bool change_this)
176
{
177
    // The policy callback will want to know the previous power mode.
178
    PowerMode old_mode = controller->mode;
179
 
180
    // Invoke the mode change operation. Note that
181
    // controller->change_this and controller->desired_mode may have
182
    // been updated by now, but at some point they did have values
183
    // which required a mode change.
184
    (*controller->change_mode)(controller, desired_mode, change_this ? PowerModeChange_Controller : PowerModeChange_Global);
185
 
186
    // Report the results to higher-level code. It is unlikely that
187
    // the policy callback will be changed while the system is running,
188
    // but just in case somebody installs a null pointer between the
189
    // check and the call...
190
    void        (*callback)(PowerController*, PowerMode, PowerMode, PowerMode, PowerMode) = __power_policy_callback;
191
    if (0 != callback) {
192
        (*callback)(controller, old_mode, controller->mode, desired_mode, controller->desired_mode);
193
    }
194
}
195
 
196
// power_doit() is responsible for a single iteration over the various controllers,
197
// aborting if there is a global mode change during the current iteration. The
198
// calling code, either power_thread_fn() or power_nothread_doit(), will take
199
// care of the higher-level iterating while there is work to be done.
200
//
201
// If a global mode change has been requested then the order in which the controllers
202
// are invoked is significant: front->back for lowering power modes, back->front for
203
// a higher power mode. If there are individual changes to be processed then
204
// arbitrarily front->back is used as well.
205
static inline void
206
power_doit()
207
{
208
    PowerController*    controller;
209
 
210
    abort_mode_change   = false;
211
 
212
    if (__power_desired_mode < __power_mode) {
213
        // The new mode is more active than the old one, so start with
214
        // the power controllers at the back of the table.
215
        for (controller = &(__POWER_END__) - 1; !abort_mode_change && (controller >= &(__POWER__[0])); controller--) {
216
            PowerMode   desired_mode;
217
            cyg_bool    change_this;
218
 
219
#ifdef CYGPKG_POWER_THREAD
220
            // Read the desired_mode and change_this flags atomically.
221
            cyg_scheduler_lock();
222
            desired_mode = controller->desired_mode;
223
            change_this  = controller->change_this;
224
            cyg_scheduler_unlock();
225
#else
226
            desired_mode = controller->desired_mode;
227
            change_this  = controller->change_this;
228
#endif
229
            // If this controller is not running at the desired mode, change it.
230
            if (desired_mode != controller->mode) {
231
                power_change_controller_mode(controller, desired_mode, change_this);
232
            }
233
        }
234
    } else {    // __power_desired_mode >= __power_mode.
235
        // Either a global mode change to a less active mode, or
236
        // one or more individual controller changes. Other than
237
        // iterating in a different direction, the code is the same
238
        // as above.
239
        for (controller = &(__POWER__[0]); !abort_mode_change && (controller != &(__POWER_END__)); controller++) {
240
            PowerMode   desired_mode;
241
            cyg_bool    change_this;
242
 
243
#ifdef CYGPKG_POWER_THREAD
244
            cyg_scheduler_lock();
245
            desired_mode = controller->desired_mode;
246
            change_this  = controller->change_this;
247
            cyg_scheduler_unlock();
248
#else
249
            desired_mode = controller->desired_mode;
250
            change_this  = controller->change_this;
251
#endif
252
            if (desired_mode != controller->mode) {
253
                power_change_controller_mode(controller, desired_mode, change_this);
254
            }
255
        }
256
    }
257
 
258
    // All of the controllers have been invoked. If there have been no
259
    // intervening calls to power_set_mode() (which would have updated
260
    // abort_mode_change) then we must now be running at the desired
261
    // global mode.
262
    if (!abort_mode_change) {
263
        __power_mode = __power_desired_mode;
264
    }
265
}
266
 
267
#ifdef CYGPKG_POWER_THREAD
268
static void
269
power_thread_fn(cyg_addrword_t param)
270
{
271
    for (;;) {
272
        // Currently idle. Wait for a request to change power modes.
273
        cyg_semaphore_wait(&power_thread_action);
274
        power_doit();
275
    }
276
}
277
#else
278
static inline void
279
power_nothread_doit()
280
{
281
    power_todo_count++;
282
    if (!power_doing_it) {
283
        power_doing_it = true;
284
        do {
285
            power_doit();
286
        } while (--power_todo_count > 0);
287
        power_doing_it = false;
288
    }
289
}
290
#endif
291
 
292
// ----------------------------------------------------------------------------
293
// The exported calls.
294
 
295
extern "C" void
296
power_set_controller_mode(PowerController* controller, PowerMode new_mode)
297
{
298
#ifdef CYGPKG_POWER_THREAD
299
    cyg_scheduler_lock();   // Protect against concurrent calls
300
#endif
301
 
302
    controller->desired_mode    = new_mode;
303
    controller->change_this     = true;
304
 
305
#ifdef CYGPKG_POWER_THREAD    
306
    cyg_scheduler_unlock();
307
    cyg_semaphore_post(&power_thread_action);
308
#else
309
    power_nothread_doit();
310
#endif    
311
}
312
 
313
extern "C" void
314
power_set_mode(PowerMode new_mode)
315
{
316
    PowerController*        controller;
317
 
318
#ifdef CYGPKG_POWER_THREAD
319
    cyg_scheduler_lock();
320
#endif
321
 
322
    __power_desired_mode    = new_mode;
323
    abort_mode_change       = true;
324
    // Update each controller. Most importantly, clear the
325
    // "change_this" flag in every power controller. The net result is
326
    // that power_set_mode() overrides any power_set_controller_mode()
327
    // operations that have not yet been processed, but future
328
    // power_set_controller_mode() calls will have the desired effect.
329
    for (controller = &(__POWER__[0]); controller != &(__POWER_END__); controller++) {
330
        if (controller->attached) {
331
            controller->change_this     = 0;
332
            controller->desired_mode    = new_mode;
333
        }
334
    }
335
 
336
#ifdef CYGPKG_POWER_THREAD    
337
    cyg_scheduler_unlock();
338
    cyg_semaphore_post(&power_thread_action);
339
#else
340
    power_nothread_doit();
341
#endif    
342
}
343
 
344
// ----------------------------------------------------------------------------
345
// Power management initialization. This gets called from
346
// power_data.cxx using a prioritized constructors. Doing this way
347
// minimizes the amount of data that is going to end up in libextras.a
348
// and hence in the final executable, allowing linker garbage collection
349
// to clean up as much as possible. The main operation here is to start
350
// up a separate power management thread when configured to do so.
351
//
352
// If no separate thread is being used then no run-time initialization
353
// is needed.
354
#ifdef CYGPKG_POWER_THREAD
355
extern "C" void
356
power_init(void)
357
{
358
    cyg_semaphore_init(&power_thread_action, 0);
359
    cyg_thread_create(CYGNUM_POWER_THREAD_PRIORITY,
360
                      &power_thread_fn,
361
                      (cyg_addrword_t) 0,
362
                      "Power management thread",
363
                      power_thread_stack,
364
                      CYGNUM_POWER_THREAD_STACKSIZE,
365
                      &power_thread_handle,
366
                      &power_thread
367
        );
368
    cyg_thread_resume(power_thread_handle);
369
}
370
#endif    
371
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.