[FE training-materials-updates] kernel-serial-driver: Update to be usable with the beaglebone

Maxime Ripard maxime.ripard at free-electrons.com
Fri Sep 13 14:12:01 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=13b4ac734c84f2d6c4b9d5b053776b7aa24d135e

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

commit 13b4ac734c84f2d6c4b9d5b053776b7aa24d135e
Author: Maxime Ripard <maxime.ripard at free-electrons.com>
Date:   Fri Sep 13 14:11:31 2013 +0200

    kernel-serial-driver: Update to be usable with the beaglebone
    
    Signed-off-by: Maxime Ripard <maxime.ripard at free-electrons.com>


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

13b4ac734c84f2d6c4b9d5b053776b7aa24d135e
 labs/kernel-serial-driver/kernel-serial-driver.tex |  172 +++++++-------------
 1 file changed, 57 insertions(+), 115 deletions(-)

diff --git a/labs/kernel-serial-driver/kernel-serial-driver.tex b/labs/kernel-serial-driver/kernel-serial-driver.tex
index 934c3df..9b58d22 100644
--- a/labs/kernel-serial-driver/kernel-serial-driver.tex
+++ b/labs/kernel-serial-driver/kernel-serial-driver.tex
@@ -1,6 +1,6 @@
 \subchapter{Serial controller device driver programming}
   {Objective: Develop a serial device driver for the
-  AT91SAM9263 CPU from scratch}
+  AM335x SoC from scratch}
 
 \section{Warning}
 
@@ -20,11 +20,6 @@ this lab. Re-use the setup instructions of the lab on {\em Character
 Device Drivers} to get a kernel without the serial port driver and
 with {\em Network Console support}.
 
-For this lab, we advise you to work with Linux 3.0, as this lab and the
-solution that we will provide have been tested with this version.
-
-Therefore, you should download the Linux 3.0 sources.
-
 \section{Basic module}
 
 The serial core cannot be compiled inside the kernel without an
@@ -68,9 +63,6 @@ Instantiate a \code{uart_driver} structure with the following values:
 \item \code{owner}, \code{THIS_MODULE}
 \item \code{driver_name}, \code{fedrv} or any other string
 \item \code{dev_name}, \code{ttyS}
-\item \code{major}, \code{TTY_MAJOR} (this is the usual major for TTY devices)
-\item \code{minor}, \code{64} (this is the usual minor for TTY serial
-  devices, see \code{Documentation/devices.txt} in the kernel source tree)
 \item \code{nr}, \code{1} (we will support only one port)
 \end{itemize}
 
@@ -86,55 +78,32 @@ will integrate our driver in the device model.
 To do, so, first instantiate a \code{platform_driver} structure, with
 pointers to the \code{probe()} and \code{remove()} methods (for the 
 moment, you can just define these functions as returning a zero value)
-The driver name must be \code{atmel_usart} to match the device definitions in
-\code{arch/arm/mach-at91/}.
+The driver name can be anything you want.
 
-You should mark the \code{probe()} function with \code{__devinit} and
-the \code{remove()}
-function with \code{__devexit}. The \code{remove} operation should be declared as
-follows:
+You'll also need to reference an array of the device tree compatibles
+that will be handled by this driver. This array is an array of
+\code{struct of_device_id}, that you will reference in the
+\code{of_match_table} field of the driver structure. We will use the
+\code{ti,omap3-uart} compatible for this driver.
 
-\begin{verbatim}
-.remove = __devexit_p(fedrv_remove)
-\end{verbatim}
+Look at the device tree we use, and you'll see that this compatible is
+already in use there, so we don't need to modify the device tree in
+any way.
 
-So that if the driver is statically compiled, the
-\code{fedrv_remove()} function is not compiled in and the
-\code{.remove} pointer is \code{NULL}.
-
-Then, in the \emph{init} and \emph{exit} functions of the module, register and
-unregister the platform driver using
+Then, in the \emph{init} and \emph{exit} functions of the module,
+register and unregister the platform driver using
 \code{platform_driver_register()} and
 \code{platform_driver_unregister()}.
 
-Finally, you need to make a small modification to the
-kernel. Currently, the \code{atmel_usart} platform devices are only added
-if the Atmel serial port driver is compiled in. However, since we
-disabled this driver (because we are re-implementing it), we must
-modify the board code a little. So, in
-\code{arch/arm/mach-at91/at91sam9263_devices.c}, replace:
-
-\begin{verbatim}
-#if defined(CONFIG_SERIAL_ATMEL)
-\end{verbatim}
-
-by
-
-\begin{verbatim}
-#if defined (CONFIG_SERIAL_FEDRV) || defined (CONFIG_SERIAL_FEDRV_MODULE)
-\end{verbatim}
-
-(Assuming you named the option of your driver \code{SERIAL_FEDRV} in
-\code{drivers/tty/serial/Kconfig}).
-
 Then, recompile your kernel, re-flash it, and test your new
 module. You should see your \code{probe()} function being called
 (after adding a simple \code{dev_*()} message in it). And in
-\code{/sys/devices/platform/}, you should see the device
-\code{atmel_usart.0}. This directory contains a symbolic link driver
-to the \code{atmel_usart} driver. If you follow this symbolic link,
-you should discover that the \code{atmel_usart} driver is implemented
-by the \code{fedrv} module. Congratulations!
+\code{/sys/bus/platform/devices}, you should see the device
+\code{44e09000.serial}. This directory contains a symbolic link driver
+to a folder named after driver name you set in the
+\code{platform_driver} structure. If you follow this symbolic link,
+you should discover that this driver is implemented by your
+module. Congratulations!
 
 \section{Registering the port}
 
@@ -190,11 +159,11 @@ In the \code{remove()} method:
 \end{itemize}
 
 Now, when testing your driver, in
-\code{/sys/devices/platform/atmel_usart.0/}, you should have a
+\code{/sys/bus/platform/devices/44e09000.serial}, you should have a
 \code{tty} directory, which itself contains a \code{ttyS0}
 directory. Similarly, if you go in \code{/sys/class/tty/ttyS0}, you
 should see that the \code{ttyS0} device is handled by
-\code{atmel_usart.0}. Good!
+\code{44e09000.serial}. Good!
 
 Before going on, we need to disable the \code{getty} process that the
 \code{init} process is trying to start on \code{/dev/ttyS0}. Edit the
@@ -214,26 +183,22 @@ In the \code{probe()} operation, let's define a few more things in the
 
 \item \code{->fifosize}, to \code{1} (this is hardware-dependent)
 
-\item \code{->iotype} should be \code{UPIO_MEM} because we are
+\item \code{->iotype} should be \code{UPIO_MEM32} because we are
   accessing the hardware through memory-mapped registers
 
 \item \code{->flags} should be \code{UPF_BOOT_AUTOCONF} so that the
   \code{config_port()} operation gets called to do the configuration
 
-\item \code{->mapbase} should be \code{pdev->resource[0].start}, this is
-  the address of the memory-mapped registers
-
-\item \code{->membase} should be set to \code{data->regs}, where
-  \code{data} is the device-specific platform data structure associated
-  to the device. In our
-  case, it's a \code{atmel_uart_data} structure, available through
-  \code{pdev->dev.platform_data}. In the case of the first serial port
-  \code{data->regs} is non-zero and contains the virtual address at
-  which the registers have been remapped. For the other serial ports,
-  we would have to \code{ioremap()} them.
-
 \end{itemize}
 
+You'll then need to fill the \code{->mapbase} and \code{->membase}
+field. To do this, retrieve the physical base address with the
+\code{platform_get_resource} function, and use the \code{->start}
+field of the structure you just retrieved to set
+\code{->mapbase}. Then, use this structure again to call
+\code{ioremap} and assign the value returned by it to
+\code{->membase}.
+
 Then, we need to create stubs for a fairly large number of
 operations. Even if we don't implement anything inside these
 operations for the moment, the \code{serial_core} layer requires these
@@ -261,22 +226,23 @@ serial port type:
 
 \begin{itemize}
 
-\item In the \code{config_port()} operation, if \code{flags & UART_CONFIG_TYPE}
-  is true, then set \code{port->type = PORT_ATMEL}. There is a global
-  list of serial port types, and we are re-using the existing
-  definition.
+\item In the \code{config_port()} operation, if
+  \code{flags & UART_CONFIG_TYPE} is true, then set
+  \code{port->type = PORT_OMAP}. There is a global list of serial port
+  types, and we are re-using the existing definition.
 
-\item In the \code{type()} operation, if \code{port->type} is \code{PORT_ATMEL} return a
-  string like \code{ATMEL_SERIAL}, otherwise return \code{NULL}.
+\item In the \code{type()} operation, if \code{port->type} is
+  \code{PORT_OMAP} return a string like \code{OMAP_SERIAL},
+  otherwise return \code{NULL}.
 
 \end{itemize}
 
 Now, for the transmission itself, we will first implement
-\code{tx_empty()}. In this function, read the register \code{ATMEL_US_CSR} from
-the hardware (note: the virtual base address of the registers is in
-\code{port->membase}). If the \code{ATMEL_US_TXEMPTY} bit is set, it means that the
-port is ready to transmit, therefore return \code{TIOCSER_TEMT}, otherwise
-return \code{0}.
+\code{tx_empty()}. In this function, read the register \code{UART_LSR}
+from the hardware (note: the virtual base address of the registers is
+in \code{port->membase}). If the \code{UART_LSR_TEMT} bit is set, it
+means that the port is ready to transmit, therefore return
+\code{TIOCSER_TEMT}, otherwise return \code{0}.
 
 Then, the \code{start_tx()} function will do the transmission
 itself. Iterate until the transmission buffer is empty (use
@@ -288,9 +254,9 @@ itself. Iterate until the transmission buffer is empty (use
 \item increment \code{port->icount.tx}
 \end{itemize}
 
-The auxiliary function should wait until bit \code{ATMEL_US_TXRDY}
-gets set in the \code{ATMEL_US_CSR} register, and then send the
-character through the \code{ATMEL_US_THR} register.
+The auxiliary function should wait until bit \code{UART_LSR_THRE}
+gets set in the \code{UART_LSR} register, and then send the
+character through the \code{UART_TX} register.
 
 Then, compile and load your driver. You should now be able to do:
 
@@ -303,16 +269,16 @@ echo "foo" > /dev/ttyS0
 The last part to make our driver usable is to implement reception.
 
 We first need to modify the \code{probe()} method to set
-\code{port->irq} to \code{pdev->resource[1].start} so that we fetch
-the IRQ number from the board-specific platform device definition.
+\code{port->irq} to the result of \code{platform_get_irq} so that we
+fetch the IRQ number from the board-specific platform device
+definition.
 
 Then, in the \code{startup()} operation, do the following steps:
 
 \begin{itemize}
 
 \item Disable all interrupts in the serial controller by writing
-  \code{~0UL}\footnote{That's \code{0xffffffff} on a 32 bit CPU}
-  to the \code{ATMEL_US_IDR} register.
+  \code{0} to the \code{UART_IER} register.
 
 \item Register the \code{port->irq} IRQ channel with a
   \code{fedrv_interrupt()} interrupt handler.
@@ -320,14 +286,8 @@ Then, in the \code{startup()} operation, do the following steps:
   get a pointer to \code{port} in the interrupt handler. Make it a shared
   interrupt.
 
-\item Reset the serial controller by writing \code{ATMEL_US_RSTSTA |
-  ATMEL_US_RSTRX} to the \code{ATMEL_US_CR} register
-
-\item Enable transmission and reception by writing \code{ATMEL_US_TXEN |
-  ATMEL_US_RXEN} to the \code{ATMEL_US_CR} register
-
-\item Enable interrupts on reception by writing \code{ATMEL_US_RXRDY} to
-  the \code{ATMEL_US_IER} register
+\item Enable interrupts on reception by writing \code{UART_IER_RDI} to
+  the \code{UART_IER} register
 
 \end{itemize}
 
@@ -335,8 +295,8 @@ Similarly, in the \code{shutdown()} operation, do:
 
 \begin{itemize}
 
-\item Disable all interrupts by writing \code{~0UL} to the
-  \code{ATMEL_US_IDR} register
+\item Disable all interrupts by writing \code{0} to the
+  \code{UART_IER} register
 
 \item Free the IRQ channel using \code{free_irq()}
 
@@ -346,32 +306,14 @@ Then, in the interrupt handler, do the following:
 
 \begin{itemize}
 
-\item Read the \code{ATMEL_US_CSR} register to get the controller
-  status and perform the logical and of this value with the enabled
-  interrupts by reading the \code{ATMEL_US_IMR} register. If the
-  resulting value is \code{0}, then the interrupt was not for us, return
-  \code{IRQ_NONE}.
-
-\item If the result value has bit \code{ATMEL_US_RXRDY} set, call an
-  auxiliary function \code{fedrv_rx_chars()} to receive the characters.
-
-\end{itemize}
-
-Finally, we have to implement the \code{fedrv_rx_chars()}
-function. This function should read the \code{ATMEL_US_CSR}
-register, and while \code{ATMEL_US_RXRDY} is set in this register,
-loop to read characters the following way:
-
-\begin{itemize}
-
-\item Read one character from the \code{ATMEL_US_RHR} register
+\item Read one character from the \code{UART_RX} register
 
 \item Increment \code{port->icount.rx}
 
-\item Call \code{uart_insert_char()} with the value of the status
-  register, overrun to \code{ATMEL_US_OVRE}, and the flag set to
-  \code{TTY_NORMAL} (we don't handle break characters, frame or
-  parity errors, etc. for the moment)
+\item Call \code{uart_insert_char()} with the value of the register
+  \code{UART_LSR}, overrun to \code{UART_LSR_OE}, and the flag set to
+  \code{TTY_NORMAL} (we don't handle break characters, frame or parity
+  errors, etc. for the moment)
 
 \end{itemize}
 



More information about the training-materials-updates mailing list