[FE training-materials-updates] kernel-serial-interrupt: Update the instructions for the Beaglebone

Maxime Ripard maxime.ripard at free-electrons.com
Fri Sep 6 16:28:36 CEST 2013


Repository : git://git.free-electrons.com/training-materials.git

On branch  : kernel-bbb
Link       : http://git.free-electrons.com/training-materials/commit/?id=f21933a792c36776c575beaf9ae9f3f1db50d09f

>---------------------------------------------------------------

commit f21933a792c36776c575beaf9ae9f3f1db50d09f
Author: Maxime Ripard <maxime.ripard at free-electrons.com>
Date:   Fri Sep 6 16:27:37 2013 +0200

    kernel-serial-interrupt: Update the instructions for the Beaglebone
    
    Signed-off-by: Maxime Ripard <maxime.ripard at free-electrons.com>


>---------------------------------------------------------------

f21933a792c36776c575beaf9ae9f3f1db50d09f
 .../kernel-serial-interrupt.tex                    |  141 ++++++++------------
 1 file changed, 56 insertions(+), 85 deletions(-)

diff --git a/labs/kernel-serial-interrupt/kernel-serial-interrupt.tex b/labs/kernel-serial-interrupt/kernel-serial-interrupt.tex
index d17f0af..de5ae6c 100644
--- a/labs/kernel-serial-interrupt/kernel-serial-interrupt.tex
+++ b/labs/kernel-serial-interrupt/kernel-serial-interrupt.tex
@@ -4,7 +4,7 @@ During this lab, you will:
 
 \begin{itemize}
 \item Register an interrupt handler for the serial controller of the
-  Calao board
+  Beaglebone
 \item See how Linux handles shared interrupt lines
 \item Implement the read() operation of the serial port driver to put
   the process to sleep when no data are available
@@ -18,87 +18,58 @@ During this lab, you will:
 
 This lab is a continuation of the {\em Output-only character driver
   lab}, so we'll re-use the code in
-\code{$HOME/felabs/linux/character}. Your Calao board should boot over
-NFS and mount \code{/home/<user>/felabs/linux/character/nfsroot/} as the root
-filesystem.
-
-For demonstration purposes, we need to ensure that the tick system will
-use the shared IRQ. So configure your kernel and {\em disable} TC
-Block Clocksource (\code{CONFIG_ATMEL_TCB_CLKSRC}). Then compile
-and boot your new kernel.
+\code{$HOME/felabs/linux/character}. Your Beaglebone should boot over
+NFS and mount \code{/home/<user>/felabs/linux/character/nfsroot/} as
+the root filesystem.
 
 \section{Register the handler}
 
 First, declare an interrupt handler function. Then, in the module
-initialization function, register this handler to IRQ number
-\code{NR_IRQS_LEGACY + AT91_ID_SYS}. Note that this IRQ is shared,
-so the appropriate flags must be passed at registration time.
-
-Then, in the interrupt handler, just print a message and
-return \code{IRQ_NONE} (to tell the kernel that we haven't handled
-the interrupt).
-
-Compile and load your module. Look at the kernel logs: they are full
-of our message indicating that interrupts are occurring, even if we
-are not receiving from the serial port! It shows you that interrupt
-handlers on shared IRQ lines are all called every time an interrupt
-occurs.
+initialization function, we need to register this handler to an IRQ
+number.
+
+Nowadays, Linux is using a virtual IRQ number that it derives from the
+hardware interrupt number. This virtual number is created through the
+\code{irqdomain} mechanism. The hardware IRQ number to use is
+\code{72} for the UART. You can get a virtual IRQ number by using the
+\code{irq_create_mapping} function.
+
+Then, pass the newly created virtual interrupt to \code{request_irq}
+along with the interrupt handler to register your interrupt in the
+kernel. Note that this IRQ is shared, so the appropriate flags must be
+passed at registration time.
+
+Then, in the interrupt handler, just print a message and return
+\code{IRQ_HANDLED} (to tell the kernel that we have handled the
+interrupt).
+
+You'll also need to enable the interrupt when receiving a new
+character. To do so, in the initialization of the module, write the
+\code{UART_IER_RDI} bit in the \code{UART_IER} register.
+
+Compile and load your module. Send a character on the serial link, and
+look at the kernel logs: they are full of our message indicating that
+interrupts are occurring, even if we only sent one character! It shows
+you that interrupt handlers should do a little bit more when an
+interrupt occurs.
 
 \section{Enable and filter the interrupts}
 
-In fact, at the moment, reception and interrupts are not enabled at
-the level of the serial port controller. So in the initialization
-function of the module:
-\begin{itemize}
-\item Write \code{ATMEL_US_RSTSTA | ATMEL_US_RSTRX} to the
-  \code{ATMEL_US_CR} register
-\item Write \code{ATMEL_US_TXEN | ATMEL_US_RXEN} to the
-  \code{ATMEL_US_CR} register
-\item Write \code{ATMEL_US_RXRDY} to the \code{ATMEL_US_IER}
-  register (IER stands for Interrupt Enable Register).
-\end{itemize}
-
-Now, in our interrupt handler we want to filter out the interrupts
-that come from the serial controller. To do so, read the value of the
-\code{ATMEL_US_CSR} register and the value of the
-\code{ATMEL_US_IMR} register. If the result of a {\em binary and}
-operation between these two values is different from zero, then it
-means that the interrupt is coming from our serial controller.
+In fact, the hardware will replay the interrupt until you acknowledge
+it. Linux will only dispatch the interrupt event to the rightful
+handler, hoping that this handler will acknowledge it. What we
+experienced here is called an \code{interrupt flood}.
 
-If the interrupt comes from our serial port controller, print a
-message and return \code{IRQ_HANDLED}. If the interrupt doesn't come from
-our serial port controller, just return \code{IRQ_NONE} without printing a
-message.
+Now, in our interrupt handler, we want to acknowledge the
+interrupt. On the UART controller we drive, it's done simply by
+reading the content of the \code{UART_RX} register, which holds the
+next character received. You can display the value you read to see
+that the driver will receive whatever character you sent.
 
 Compile and load your driver. Have a look at the kernel messages. You
-should no longer be flooded with interrupt messages.
-
-Start \code{picocom} on \code{/dev/ttyUSB0}. Press one character (nothing will
-appear since the target system is not echoing back what we're
-typing). Then, in the kernel log, you should see the message of our
-interrupt handler. If not, check your code once again and ask your
-instructor for clarification!
-
-\section{Read the received characters}
-
-You can read the received characters by reading the \code{ATMEL_US_RHR}
-register using \code{readl()}. It must be done in code that loops until the
-\code{ATMEL_US_RXRDY} bit of the \code{ATMEL_US_CSR} register goes back to
-zero. This method of operation allows to read several characters in a
-single interrupt.
-
-Note that our hardware doesn't give us any special register to
-acknowledge interrupts. What happens is that interrupts are
-acknowledged (allowing more interrupts to be sent in the future), when
-the driver accesses the \code{ATMEL_US_RHR} register to read each
-character.
-
-For each received character, print a message containing the character.
-Compile and load your driver.
-
-From \code{picocom} on \code{/dev/ttyUSB0} on the host, send characters
-to the target. The kernel messages on the target should properly tell
-you which characters are being received.
+should no longer be flooded with interrupt messages. In the kernel
+log, you should see the message of our interrupt handler. If not,
+check your code once again and ask your instructor for clarification!
 
 \section{Sleeping, waking up and communication}
 
@@ -135,21 +106,21 @@ character, store it in the userspace buffer, update the
 read one character at a time, even if the userspace application
 requested more than one).
 
-Now, what happens in our \code{read()} function if no character is available
-for reading (i.e, if \code{serial_buf_wr} is equal to \code{serial_buf_rd})? We
-should put the process to sleep!
+Now, what happens in our \code{read()} function if no character is
+available for reading (i.e, if \code{serial_buf_wr} is equal to
+\code{serial_buf_rd})? We should put the process to sleep!
 
 To do so, declare a global wait queue in our driver, named for example
-\code{serial_wait}. In the \code{read()} function, use \code{wait_event_interruptible()}
-to wait until \code{serial_buf_wr} is different from \code{serial_buf_rd}. And
-in the interrupt handler, after storing the received characters in the
-circular buffer, use \code{wake_up()} to wake up all processes waiting on
-the wait queue.
-
-Compile and load your driver. Run \code{cat /dev/serial} on the target,
-and then in Picocom on the development workstation side, type some
-characters. They should appear on the remote side if everything works
-correctly!
+\code{serial_wait}. In the \code{read()} function, use
+\code{wait_event_interruptible()} to wait until \code{serial_buf_wr}
+is different from \code{serial_buf_rd}. And in the interrupt handler,
+after storing the received characters in the circular buffer, use
+\code{wake_up()} to wake up all processes waiting on the wait queue.
+
+Compile and load your driver. Run \code{cat /dev/serial} on the
+target, and then in Picocom on the development workstation side, type
+some characters. They should appear on the remote side if everything
+works correctly!
 
 Don't be surprised if the keys you type in Picocom don't appear on the
 screen. This happens because they are not echoed back by the target.



More information about the training-materials-updates mailing list