Attach an idle interrupt handler
#include <sys/neutrino.h> int InterruptHookIdle2( void (*handler)( unsigned cpu, struct syspage_entry *spp, struct _idle_hook *ihp ), unsigned flags );
libc
Use the -l c option to qcc to link against this library. This library is usually included automatically.
The InterruptHookIdle2() kernel call attaches the specified handler to the _NTO_HOOK_IDLE synthetic interrupt, which is generated when a processor becomes idle. This handler is typically used to implement power management features. The simplest idle handler consists of a halt instruction.
Before calling this function, the thread must have the PROCMGR_AID_INTERRUPT ability enabled. For more information, see procmgr_ability(). Otherwise the attachment fails with an error code of EPERM.
To detach the interrupt handler, call InterruptDetach(); you can't pass NULL for the handler argument.
The arguments to the handler function are:
The handler function is invoked with a very limited amount of stack space, so it shouldn't use large automatic arrays. It's also invoked in such a manner that it can be aborted at any time and restarted from the beginning, with no user notification. It isn't allowed to make any OS service calls (e.g., message passing).
The _idle_hook structure is defined in <sys/neutrino.h> as follows:
struct _idle_hook { unsigned hook_size; unsigned cmd; unsigned mode; unsigned latency; uint64_t next_fire; uint64_t curr_time; uint64_t tod_adjust; unsigned resp; struct { unsigned length; unsigned scale; } time; struct sigevent trigger; unsigned *intrs; unsigned block_stack_size; };
The members include:
See below for more details.
The _NTO_IH_RESP_DELIVER_INTRS bit must be set in the resp field for the kernel to check the contents of the array. See below for details on how to determine the bit(s) to be set.
The idle hook handler may be called up to three times per entry to or exit from the sleep state, depending on how the handler function responds to each of the previous calls. The kernel sets the cmd field for each invocation:
In this call, the handler should set up the chip registers to enter the selected sleep state. The handler isn't allowed to actually enter a sleep state or block waiting for an interrupt or event.
The usual activity for the handler at this point is to simply respond that it wants wants to be called with _NTO_IH_CMD_SLEEP_BLOCK at the appropriate time. However, if there is any long-running hardware preparation for low-power mode (e.g., writing to a register, then either waiting or polling a register until ready), you can use this callback to kick things off, so that the hardware will likely to be ready when the callback for _NTO_IH_CMD_SLEEP_BLOCK is called, when the hardware can be fully configured for low-power mode.
In this call, the handler can continue whatever setup is needed from the first call, and it should actually do what's needed to enter the selected sleep state. If all that's needed to actually enter the sleep state is for the chip to execute its halt instruction (HLT on x86, WFI on ARM, etc.), then that's all the handler has to do.
At this point, an interrupt has occurred that has released the CPU from its sleep state. The handler should restore whatever state it needs to.
This time, the callback is invoked from an interrupt context, with interrupts enabled, before the interrupt-specific ISR is invoked. You should provide information regarding missed time to the idle hook response before the low-power code transfers execution back to the OS.
For each of these calls, the handler fills in the resp field with a bitset indicating what the kernel needs to do:
Bit | Description |
---|---|
_NTO_IH_RESP_NEEDS_BLOCK | If set after the first handler call, the kernel will invoke the handler with the _NTO_IH_CMD_SLEEP_BLOCK state. |
_NTO_IH_RESP_NEEDS_WAKEUP | If set after the first handler call (or second, if the second call has been requested), the kernel will invoke the handler with the _NTO_IH_CMD_SLEEP_WAKEUP state. |
_NTO_IH_RESP_NEEDS_ONLINE | If the selected mode is such that an interprocessor interrupt (IPI) isn't sufficient to cause the CPU to respond (e.g., it's offline), the handler should set this bit. The kernel, when it decides to online the CPU, invokes the idle hook (on a different CPU) with the cmd field set to _NTO_IH_CMD_SLEEP_ONLINE. The mode field holds the CPU number that is being returned to operation. |
_NTO_IH_RESP_SYNC_TIME | After the handler returns, the kernel will add time.length * time.scale nanoseconds to the SYSPAGE_ENTRY(qtime)->nsec field. |
_NTO_IH_RESP_SYNC_TLB | After the handler returns, the kernel will resynchronize the translation lookaside buffer (TLB) state
(the TLB wasn't being notified of modifications from other CPU's during the sleep state).
The kernel crashes if it gets this response. |
_NTO_IH_RESP_SUGGEST_OFFLINE | This allows the handler to suggest to the kernel that it might be a good idea to offline this CPU. The CPU must be marked as being available for dynamic offlining, and the kernel is free to ignore the suggestion (and it currently does). |
_NTO_IH_RESP_SLEEP_MODE_REACHED | This bit indicates that the sleep mode requested was actually entered by the hardware. The kernel doesn't do anything with this bit, but it can be used to gather statistics on how much power savings are being obtained. |
_NTO_IH_RESP_DELIVER_INTRS | If set, this bit indicates that the handler turned on bits in the intrs array, and the kernel needs to perform interrupt delivery processing for the indicated interrupts. |
Indicating interrupts for delivery processing
If an unmasked interrupt occurs while the CPU is asleep and, for some reason, it will not cause CPU interrupt exception processing to occur when the CPU wakes up, the hook code must tell the kernel about the interrupt so that it can arrange to invoke the handlers (InterruptAttach() or InterruptAttachArray()) or deliver the sigevents (InterruptAttachEvent()) for that level.
The handler indicates this by turning on the appropriate bit(s) in the intrs array and setting _NTO_IH_RESP_DELIVER_INTRS in the resp field.
To determine the corresponding bit for each interrupt, examine the system page's intrinfo section and look at the number of interrupts that each controller is responsible for. If the first controller is responsible for N interrupts, then the interrupt numbers are 0 ... (N-1). If the second controller has M interrupts, then its numbers are N ... (N + M - 1), and so on. If you want to indicate that interrupt X has occurred, set the following bit:
idle_hook_ptr->intrs[X / (sizeof(unsigned)*CHAR_BIT)] |= 1U << (X % (sizeof(unsigned)*CHAR_BIT));
Blocking states
This call doesn't block.
An interrupt function ID, or -1 if an error occurs (errno is set).
Use the returned ID with InterruptDetach() to detach this interrupt handler.
Safety: | |
---|---|
Cancellation point | No |
Interrupt handler | No |
Signal handler | Yes |
Thread | Yes |