Updated: October 28, 2024 |
A major contributor to the power consumption of a CPU is the frequency at which it is clocked. The power-management API allows you to manage this.
Power management can be divided into two phases:
Generally, you set up power management early on in the life of your system. This setup doesn't have to be completed before you allow users to begin using the running system, however. That is, if you must meet some fast boot requirements, such as presenting a working camera to a user within n milliseconds, you can defer setting up power management until after you have met these requirements (see the Boot Optimization Guide).
When you set up your power management, you:
To set up or change your power-management policies, in the process you use for your power management, call ChannelCreate() and then ConnectAttach() to create and attach to a channel. Then, for each CPU:
For the purposes of power management (and, therefore, in this documentation) a CPU cluster isn't necessarily equivalent to a CPU cluster as defined in the SoC documentation for your board. It is simply a set of CPUs you create so you can manage their power characteristics together. A CPU cluster may include only a single CPU or multiple CPUs.
If your board's SoC supports setting the frequencies for its CPU clusters independently or supports other CPU clusters, then it may be useful to create your power-management CPU clusters to match the SoC CPU clusters.
After you have informed the kernel of the SoC's power-management characteristics and the policies you want to implement, the kernel will monitor CPU cluster loads at the operating points specified in the power-management policies.
When the kernel detects that a CPU cluster is overloaded or underused, it sends a sigevent to your power-management process. This process should then:
With the new information it receives through the call to PowerSetActive(), the kernel will continue to monitor CPU cluster loads and to send the power-management process new sigevents as required.
If it needs to know a CPU's current active frequency, your power-management process can call PowerGetActive().
While the system is running, you may need to change the operating points you communicated to the kernel with your initial power management setup. Specifically:
To make these changes to CPU cluster characteristics:
If you need to re-instate some permissable operating points, you can modify the frequency_mask bitmask and repeat the procedure.
The program below illustrates how the power-management API can be used:
#include <stdio.h> #include <unistd.h> #include <errno.h> #include <sys/siginfo.h> #include <sys/neutrino.h> #include <sys/syspage.h> int main() { struct nto_power_parameter parm = {0}; int r; int coid; int chid; int pp[3]; unsigned cluster; unsigned i; chid = ChannelCreate(0); if(chid == -1) { printf("chid failed %d\n", errno); return 1; } coid = ConnectAttach(0, getpid(), chid, _NTO_SIDE_CHANNEL, 0); if(coid == -1) { printf("coid failed %d\n", errno); return 1; } parm.parameter_type = 0; parm.clusterid = -1; parm.u.cluster.frequency_mask = 0; cluster = PowerParameter(0, sizeof(parm), &parm, NULL); printf("PowerParm (cluster) got %d, %d\n", cluster, errno); for(i = 0; i < _syspage_ptr->num_cpu; ++i) { parm.parameter_type = _NTO_PPT_DEFINE_CPU; parm.clusterid = cluster; parm.u.cpu.cpuid = i; parm.u.cpu.unloaded = 75; parm.u.cpu.loaded.nonburst = 97; parm.u.cpu.loaded.burst = 20; pp[0] = PowerParameter(0, sizeof(parm), &parm, NULL); printf("PowerParm (cpu%d) got %d, %d\n", i, pp[0], errno); } parm.parameter_type = _NTO_PPT_DEFINE_FREQ; parm.clusterid = cluster; parm.u.freq.performance = 100; parm.u.freq.low.load = 10; parm.u.freq.low.duration = 100; // ms parm.u.freq.high.load = 90; parm.u.freq.high.duration = 200; // ms parm.u.freq.max_cores_online_hint = 0; SIGEV_PULSE_INIT(&parm.u.freq.ev, coid, 10, 1, 0); SIGEV_MAKE_UPDATEABLE(&parm.u.freq.ev); pp[0] = PowerParameter(0, sizeof(parm), &parm, NULL); printf("PowerParm got %d, %d\n", pp[0], errno); parm.u.freq.performance = 200; parm.u.freq.low.load = 15; parm.u.freq.low.duration = 150; // ms parm.u.freq.high.load = 80; parm.u.freq.high.duration = 50; // ms SIGEV_PULSE_INIT(&parm.u.freq.ev, coid, 10, 1, 0); SIGEV_MAKE_UPDATEABLE(&parm.u.freq.ev); pp[1] = PowerParameter(0, sizeof(parm), &parm, NULL); printf("PowerParm got %d, %d\n", pp[1], errno); parm.u.freq.performance = 300; parm.u.freq.low.load = 20; parm.u.freq.low.duration = 400; // ms parm.u.freq.high.load = 75; parm.u.freq.high.duration = 500; // ms SIGEV_PULSE_INIT(&parm.u.freq.ev, coid, 10, 1, 0); SIGEV_MAKE_UPDATEABLE(&parm.u.freq.ev); pp[2] = PowerParameter(0, sizeof(parm), &parm, NULL); printf("PowerParm got %d, %d\n", pp[2], errno); r = PowerSetActive(pp[0]); printf("PowerSA 1 got r=%d, errno=%d\n", r, errno); struct _pulse p; for( ;; ) { MsgReceivePulse(chid, &p, sizeof(p), NULL); printf("EV code=%d val=0x%x, curr=%d: ", p.code, p.value.sival_int, PowerGetActive(0)); unsigned id = p.value.sival_int >> _NTO_PFR_PARM_ID_SHIFT; r = PowerSetActive(id); printf("PowerSA 2 (%d) got r=%d, errno=%d\n", id, r, errno); } return 0; }