[FE training-materials-updates] kernel: migrate serial labs to OMAP

Michael Opdenacker michael.opdenacker at free-electrons.com
Wed Oct 2 16:14:32 CEST 2013

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

On branch  : kernel-ng

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

commit dc49746534e2f1154ed31da16ba6ffbfe6332c18
Author: Michael Opdenacker <michael.opdenacker at free-electrons.com>
Date:   Wed Oct 2 16:12:33 2013 +0200

kernel: migrate serial labs to OMAP

- Changes implemented by Maxime Ripard
- Remove ssh
- Work with git
- Etc.

Signed-off-by: Michael Opdenacker <michael.opdenacker at free-electrons.com>

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

dc49746534e2f1154ed31da16ba6ffbfe6332c18
.../kernel-serial-interrupt.tex                    |  145 ++++++++------------
labs/kernel-serial-iomem/kernel-serial-iomem.tex   |   47 +++----
labs/kernel-serial-output/kernel-serial-output.tex |    2 +-
3 files changed, 84 insertions(+), 110 deletions(-)

diff --git a/labs/kernel-serial-interrupt/kernel-serial-interrupt.tex b/labs/kernel-serial-interrupt/kernel-serial-interrupt.tex
index d17f0af..b95e295 100644
--- a/labs/kernel-serial-interrupt/kernel-serial-interrupt.tex
+++ b/labs/kernel-serial-interrupt/kernel-serial-interrupt.tex
@@ -1,10 +1,12 @@
-\subchapter{Sleeping and handling interrupts}{Objective: learn how to register and implement a simple interrupt handler, and how to put a process to sleep and wake it up at a later point}
+\subchapter{Sleeping and handling interrupts}{Objective: learn how to
+  register and implement a simple interrupt handler, and how to put a
+  process to sleep and wake it up at a later point}

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 +20,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.
+
+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!
-
-
-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.
-
-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,

\section{Sleeping, waking up and communication}

@@ -135,21 +108,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.
-
-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.
+
+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.
diff --git a/labs/kernel-serial-iomem/kernel-serial-iomem.tex b/labs/kernel-serial-iomem/kernel-serial-iomem.tex
index bfe14b6..2731bde 100644
--- a/labs/kernel-serial-iomem/kernel-serial-iomem.tex
+++ b/labs/kernel-serial-iomem/kernel-serial-iomem.tex
@@ -2,7 +2,7 @@
write data from / to a hardware device}

Throughout the upcoming labs, we will implement a character driver
-allowing to write data to the serial port of the CALAO board, and to
+allowing to write data to the serial port of the BeagleBone, and to

After this lab, you will be able to:
@@ -25,18 +25,18 @@ After this lab, you will be able to:
Go to the \code{$HOME/felabs/linux/character} directory. As in the {\em Module development environment} lab, we will use a the -CALAO board booted from NFS. +BeagleBone booted from NFS. As in the previous labs, the target IP address will be \code{192.168.0.100}, and the host address will be \code{192.168.0.1}. -Extract the latest Linux 3.6.x kernel sources in the current +Extract the latest Linux 3.11.x kernel sources in the current directory, and configure them with the default configuration for -{\em at91sam9263} boards. +the {\em omap2plus} architecture. In this lab, we will develop our own driver for the board's serial port. The consequence is that we will have to disable the standard -AT91 serial port driver and will thus lose the serial console. +OMAP serial port driver and will thus lose the serial console. Instead of running commands through a shell on the serial line, we will access our target through SSH, a secure shell over the network. @@ -52,6 +52,8 @@ So, configure your kernel with: \item Netconsole support (\code{CONFIG_NETCONSOLE}) \end{itemize} +and disable \code{CONFIG_DEVTMPFS} + You will also have to update the kernel command line so that Linux loads the root filesystem over NFS from \code{/home/<user>/felabs/linux/character/nfsroot}. @@ -77,11 +79,11 @@ The \code{root} password is empty, just press \code{Enter}. Good job! \section{Disabling the serial driver and console} Now that everything works, rebuild your kernel without the serial port -driver (in \code{Device Drivers}$\rightarrow$\code{Character devices} -$\rightarrow$\code{Serial drivers}$\rightarrow$\code{AT91 / AT32 -on-chip serial port support}). Update your kernel. +driver (in \code{Device Drivers}$\rightarrow$+\code{Character devices}$\rightarrow$\code{Serial drivers} +$\rightarrow\$ \code{OMAP serial port support}). Update your kernel.

-You also need to replace \code{console=ttyS0} by the following option in
+You also need to replace \code{console=ttyO0} by the following option in
the kernel command line, to enable the network console:

\begin{verbatim}
@@ -110,9 +112,9 @@ code. Modify the \code{Makefile} so that it points to your kernel sources.
\section{Device initialization}

In the module initialization function, start by reserving the I/O
-memory region starting at address (\code{AT91_BASE_DBGU1}), for a
-size of \code{SZ_512} (512 bytes). The \code{AT91_*} constants are already defined
+memory region starting at address (\code{0x44e09000}), for a size of
+\code{SZ_512} (512 bytes). The \code{UART} constants are already
+defined in Linux kernel headers (\code{serial_reg.h}).

Compile your module, load it and make sure that this memory region
appears in \code{/proc/iomem}.
@@ -128,20 +130,19 @@ Implement a C routine taking one character as a parameter and writing
it to the serial port, using the following steps:

\begin{enumerate}
+\item Wait until the \code{UART_LSR_THRE} bit gets set in the
+  \code{UART_LSR} register (\code{UART_LSR} is an index number for
+  this register. You'll have to multiply it by 4 to get an offset in
+  the I/O memory region previously remapped). You can busy-wait for
+  this condition to happen. In the busy-wait loop, you can call the
+  \code{cpu_relax()} kernel function to ensure the compiler won't
+  optimise away this loop.

- \item Wait until the \code{ATMEL_US_TXRDY} bit gets set in
- the \code{ATMEL_US_CSR} register (\code{ATMEL_US_CSR} is an
- offset in the I/O memory region previously remapped). You can
- busy-wait for this condition to happen. In the busy-wait loop, you
- can call the \code{cpu_relax()} kernel function to relax the CPU
- during the wait.
-
- \item Write the character to the \code{ATMEL_US_THR} register.
+\item Write the character to the \code{UART_TX} register.

\end{enumerate}

-Note that all the I/O registers of the AT91 processor are 32 bits
-wide.
+Note that all the I/O registers of the AM335x SoC are 32 bits wide.

Add a call to this routine from your module init function. Recompile
your module and load it on the target. You should see the
@@ -153,6 +154,6 @@ the serial line by the board.
Remove your module and try to load it again. If the second attempt to
properly free the resources it allocated or register, either at module
-exit time, or after a failing during the module init function.  Check
+exit time, or after a failing during the module init function. Check
and fix your module init and exit functions if you have such a
problem.
diff --git a/labs/kernel-serial-output/kernel-serial-output.tex b/labs/kernel-serial-output/kernel-serial-output.tex
index 631e136..871d8ee 100644
--- a/labs/kernel-serial-output/kernel-serial-output.tex
+++ b/labs/kernel-serial-output/kernel-serial-output.tex
@@ -5,7 +5,7 @@ After this lab, you will be able to:

\begin{itemize}
\item Write a simple character driver, allowing to write data to the
-  serial port of your CALAO board.
+  serial port of your Beaglebone.
\item Write simple \code{file_operations} functions for a device,
including \code{ioctl} controls.
\item Copy data from user memory space to kernel memory space and