1 Introduction to S5PV210 Interrupt Controller
S5PV210 is a Cortex-A8-based Soc launched by Samsung. Its internal integrated interrupt controller is made up of 4 ARM PrimeCell PL192 vector interrupt controllers daisy-chain, each PL192 VIC (Vectored Interrupt Controller) Supports 32 interrupt sources, so it supports up to 128. S5PV210 uses 93 of them. The so-called "vector" means that when an interrupt occurs, the software can directly obtain the interrupt service routine ISR (Interrupt Service Routine) set in advance from the VIC. For a single-core CPU such as the S5PV210, the PL192 is a relatively efficient VIC. The connection diagram on the hardware looks like this:
In the figure, the more important and important signals connected between the various VICs have been marked out by the landlord. There are only two input signals connected to the ARM CPU, IRQ and FIQ.
2 What kind of interrupt will be sent to the CPU for processing?
There are two types of interrupts. One is a real interrupt triggered by external hardware, and the other is a soft interrupt, called softInt. It is triggered by software writing a register. The corresponding register is VICSOFTINT. When one of the bits is not 0, VIC receives a softInt. SoftInt is generally used in debugging and is used to manually generate an interrupt.
The following figure clearly describes the steps of the interrupt request:
The inputs of the left OR gate are VICINTSOURCE and VICSoftInt. VICINTSOURCE comes from the outside and is a real interrupt generated by other Soc internal modules or Soc. VICSoftInt is inside the block diagram and is generated by VIC itself. No matter which of these two happens, you will see the existence of this interrupt source at VICRawInterrupt. The register corresponding to VICRawInterrupt is:
The Samsung official description here seems a bit problematic, status of FIQ interrupt before masking. In fact, the status of IRQ can also be seen.
Let's look at the Interrupt Requst Logic diagram again. The input of the AND gate in the middle is VICRawInterrupt and VICIntEnable from the left. VICIntEnable indicates that the interrupt source has been enabled. There are two corresponding registers, as shown below.
Writing a 1 to a bit in the VICENABLE register indicates that an interrupt is enabled, and a 1 to a bit in VICINTCLEAR indicates that an interrupt is to be disabled. Both of these registers have a feature, and writing 0 to the corresponding bit has no effect. I don't know why, many interrupt controllers use this method, it can be done with a VICENABLE register, write 1 to enable, write 0 to disable
VICIntSelect determines the right-most selector of the Interrupt Requst Logic diagram is the output IRQ and FIQ. This is well understood. The corresponding register is:
All interrupt sources are set to IRQ interrupt by default
3 What should I do after receiving an interrupt?
After the ARM CPU receives the interrupt trigger, the hardware will automatically change the CPSR to SPSR_irq before the interrupt, save the PC to LR_irq, set the lower 5 bits of the CPSR to 0x12 (IRQ mode), and turn off the IRQ enable bit (CPSR bit 7 Set to 1), and then the ARM will transfer the PC to the 0x18 offset of the interrupt vector table and start executing the IRQ entry function. The IRQ Handler starts by saving the register scene and then jumps to the real IRQ Handler. The above contents are the same for all ARM CPUs and have fixed actions.
After arriving at the IRQ Handler, the software first needs to know which interrupt occurred. The method of S5PV210 is to traverse VIC0IRQSTATUS ~ VIC3IRQSTATUS, which is not 0, which indicates that an interrupt has occurred in the VIC (VICIRQSTATUS is the rightmost output signal in the Interrupt request logic diagram).
Then read the VICADDRESS corresponding to the interrupted VIC. At this time, the interrupt controller hardware has selected the interrupt with the highest priority for us and pushed its ISR into the VICADDRESS register. We can get it directly and execute it. Like the old-fashioned method, iterates through each bit in VICIRQSTATUS, finds the one that is not 0, and maps it to a specific ISR, which is much more efficient.
At the end of the ISR, the interrupt needs to be cleared to tell the hardware (Uart0 and Interrupt controller) that I finished processing the interrupt this time and can receive the next interrupt. Samsung officially requires that All VICADDRESS be written as 0. Don't worry about clearing the interrupts of VIC0, 2, 3, because the source of the interruption is still there, and it will be triggered again.
When user clears interrupt pending, user must write 0 to all the VICADDRESS registers (VIC0ADDRESS, VIC1ADDRESS, VIC2ADDRESS, and VIC3ADDRESS).
For vector and priority, please refer to this blog post S5PV210 Interrupt Controller Detailed Explanation (2): Vector and Priority.
4 Programming using interrupts
With the previous theoretical knowledge, the use of programming interruptions will come naturally. Take UART0 as an example, it is connected to the No. 10 interrupt source of VIC1.
The initialization code is as follows:
void uart0_irq_init (void)
{
VIC1INTSELECT & = ~ (1 << 10); / * Set as IRQ interrupt * /
VIC1INTENABLE | = 1 << 10; / * enable interrupt * /
VIC1VECTADDR10 = (unsigned int) Uart0_ISR; / * Set ISR * /
}
The code at the interrupt vector table is:
IRQ_Handler:
sub lr, lr, # 4 / * 1.Calculate the return address * /
stmfd sp !, {r0-r12, lr} / * 2. Protect the scene * /
bl do_irq / * 3. Handle exception * /
Ranch
ldmfd sp !, {r0-r12, pc} ^ / * 4. Restore the scene ^ means restore spsr to cpsr * /
void do_irq (void)
{
void (* isr) (void) = NULL;
if (0! = VIC0IRQSTATUS)
isr = VIC0ADDRESS;
else if (0! = VIC1IRQSTATUS) // If uart0 interrupt occurs, VIC1IRQSTATUS is not 0
isr = VIC1ADDRESS; // VIC1ADDRESS gives what was written to VIC1VECTADDR10 before
else if (0! = VIC2IRQSTATUS)
isr = VIC2ADDRESS;
else if (0! = VIC3IRQSTATUS)
isr = VIC3ADDRESS;
if (isr)
{
(* isr) ();
VIC0ADDRESS = 0; / * Clear interrupt vector * /
VIC1ADDRESS = 0;
VIC2ADDRESS = 0;
VIC3ADDRESS = 0;
}
}
The last is Uar0IRQ's interrupt service function Uart0_ISR
void Uart0_ISR (void)
{
/ * Check the exactly reason for uart0, tx rx or error
Do something for it * /
URP0 = 0xf; / * Clear the interrupt source in UART0, URP0 register address is 0xE2900030 * /
}