[bootlin/training-materials updates] master: kernel: Reorganize the DT/multiplexing/I2C labs (ba370156)

Miquel Raynal miquel.raynal at bootlin.com
Fri Mar 17 15:34:05 CET 2023


Repository : https://github.com/bootlin/training-materials
On branch  : master
Link       : https://github.com/bootlin/training-materials/commit/ba3701568f789b318fc49b3eb4138444682a1efb

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

commit ba3701568f789b318fc49b3eb4138444682a1efb
Author: Miquel Raynal <miquel.raynal at bootlin.com>
Date:   Fri Mar 17 13:47:20 2023 +0100

    kernel: Reorganize the DT/multiplexing/I2C labs
    
    Make the DT lab much earlier, as soon as the lecture is over.
    Then continue with the pinconf lecture and lab.
    And finally after covering the device model and the i2c API, write the
    driver and communicate with the driver.
    
    Signed-off-by: Miquel Raynal <miquel.raynal at bootlin.com>


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

ba3701568f789b318fc49b3eb4138444682a1efb
 .../kernel-i2c-communication.tex                   | 278 ++++++++-------------
 labs/kernel-i2c-describing-hardware/bbb-i2c.png    | Bin 0 -> 250107 bytes
 .../kernel-i2c-describing-hardware.tex             | 219 ++++++++++++++++
 .../kernel-i2c-device-model.tex                    | 180 -------------
 .../kernel-i2c-multiplexing.tex                    | 210 ++++++++++++++++
 mk/linux-kernel.mk                                 |   5 +-
 slides/kernel-hw-devices/kernel-hw-devices.tex     |  25 +-
 slides/kernel-i2c/kernel-i2c.tex                   |  24 +-
 slides/kernel-pinmuxing/kernel-pinmuxing.tex       |   4 +-
 9 files changed, 559 insertions(+), 386 deletions(-)

diff --git a/labs/kernel-i2c-communication/kernel-i2c-communication.tex b/labs/kernel-i2c-communication/kernel-i2c-communication.tex
index 68768c6c..edeaec45 100644
--- a/labs/kernel-i2c-communication/kernel-i2c-communication.tex
+++ b/labs/kernel-i2c-communication/kernel-i2c-communication.tex
@@ -1,219 +1,151 @@
-\subchapter{Using the I2C bus}{Objective: make the I2C bus work and
-use it to implement communication with the Nunchuk device}
+\subchapter{Using the I2C bus}
+{Objective: Use the I2C bus to implement communication with the Nunchuk device}
 
-After this lab, you will be able to:
+\section{Goals}
 
+After this lab, you will be able to:
 \begin{itemize}
-\item Declare pinctrl settings.
+\item Find your driver and device in \code{/sys}.
+\item Implement basic \code{probe()} and \code{remove()} driver
+  functions and make sure that they are called when there is a
+  device/driver match.
 \item Access I2C device registers through the bus.
 \end{itemize}
 
 \section{Setup}
 
 Stay in the \code{~/linux-kernel-labs/src/linux} directory for kernel and DTB
-compiling (stay in the \code{nunchuk} branch), and in
-\code{~/linux-kernel-labs/modules/nfsroot/root/nunchuk} for module compiling
-(use two different terminals).
+compiling (stay in the \code{nunchuk} branch).
+
+In a new terminal, go to
+\code{~/linux-kernel-labs/modules/nfsroot/root/nunchuk/}.  This directory
+contains a Makefile and an almost empty \code{nunchuk.c} file.
 
-\section{Remove debugging messages}
+\section{Exploring /dev}
 
-Now that we have checked that the \code{probe()} and \code{remove()} functions
-are called, remove the \kfunc{pr_info} messages that you added to
-trace the execution of these functions.
+Start by exploring \code{/dev} on your target system. Here are a few
+noteworthy device files that you will see:
 
-\section{Connecting the nunchuk}
+\begin{itemize}
+ \item {\em Terminal devices}: devices starting with \code{tty}.
+       Terminals are user interfaces taking text as
+       input and producing text as output, and are typically used by
+       interactive shells. In particular, you will find
+       \code{console} which matches the device specified through
+       \code{console=} in the kernel command line. You will also find
+       the {\tt ttyS0} device file.
+ \item {\em Pseudo-terminal devices}: devices starting with \code{pty},
+       used when you connect through SSH for example. Those are virtual
+       devices, but there are so many in \code{/dev} that we wanted
+       to give a description here.
+ \item {MMC device(s) and partitions}: devices starting with
+       \code{mmcblk}. You should here recognize the MMC device(s)
+       on your system and the associated partitions.
+ \item If you have a real board (not QEMU) and a USB stick, you could
+       plug it in and if your kernel was built with USB host and mass
+       storage support, you should see a new \code{sda} device appear,
+       together with the \code{sda<n>} devices for its partitions.
+\end{itemize}
 
-Take the nunchuk device provided by your instructor.
+Don't hesitate to explore \code{/dev} on your workstation too
+and ask any questions to your instructor.
 
-We will connect it to the second I2C port of the CPU (\code{i2c1}),
-which pins are available on the \code{P9} connector.
+\section{Exploring /sys}
 
-Now we can identify the 4 pins of the nunchuk connector:
+The next thing you can explore is the {\em Sysfs} filesystem.
 
-\begin{center}
-\includegraphics[width=4cm]{common/nunchuk-pinout.pdf}
-\end{center}
+A good place to start is \code{/sys/class}, which exposes devices
+classified by the kernel frameworks which manage them.
 
-Open the System Reference Manual that you downloaded earlier,
-and look for "connector P9" in the table of contents, and then
-follow the link to the corresponding section. Look at the table listing
-the pinout of the P9 connector.
+For example, go to \code{/sys/class/net}, and you will see all the
+networking interfaces on your system, whether they are internal,
+external or virtual ones.
 
-Now connect the nunchuk pins:
+Find which subdirectory corresponds to the network connection
+to your host system, and then check device properties such as:
 \begin{itemize}
-\item The \code{GND} pin to P9 pins 1 or 2 (\code{GND})
-\item The \code{PWR} pin to P9 pins 3 or 4 (\code{DC_3.3V})
-\item The \code{SCL} pin to P9 pin 17 (\code{I2C1_SCL})
-\item The \code{SDA} pin to P9 pin 18 (\code{I2C1_SDA})
+   \item \code{speed}: will show you whether this is a gigabit
+         or hundred megabit interface.
+   \item \code{address}: will show the device MAC address. No
+	 need to get it from a complex command!
+   \item \code{statistics/rx_bytes} will show you how many bytes
+	 were received on this interface.
 \end{itemize}
 
-\begin{center}
-\includegraphics[width=12cm]{common/bbb-connect-nunchuk.pdf}
-\end{center}
-
-\section{Find pin muxing configuration information for i2c1}
-
-As you found in the previous lab, we now managed to have our nunchuk
-device enumerated on the \code{i2c1} bus.
-
-However, to access the bus data and clock signals, we need to configure
-the pin muxing of the SoC.
-
-If you go back to the BeagleBone Black System Reference Manual, in the
-{\em Connector P9} section, you can see that the pins \code{17} and
-\code{18} that we are using correspond to pins \code{A16} and \code{B16}
-of the AM335 SoC. You can also see that such pins need to be configured
-as \code{MODE2} to get the functionality that we need (\code{I2C1_SCL}
-and \code{I2C1_SDA}).
-
-The second step is to open the CPU datasheet (\code{am3359.pdf}), and
-look for pin assignment information ({\em Pin Assignments} section).
-You will find that the processor is available through two types of
-packages: \code{ZCE} and \code{ZCZ}. If you have an original
-BeagleBoneBlack board, you can have a very close look at the CPU
-(with your glasses on!) and you will see that the CPU has \code{ZCZ} written
-on its lower right corner. On BeagleBoneBlack Wireless with the
-Octavo System In Package, you can no longer find such information.
-Anyway, the \code{ZCZ} package information applies to both types of
-boards.
-
-So, in the {\em ZCZ Package Pin Maps (Top View)} section\footnote{Caution: you
-won't be able to search the PDF file for this section name, for obscure
-reasons. At the time of this writing, this section is numbered 4.1.2.}, you can find
-hyperlinks to the descriptions of the \code{A16} and \code{B16} pins.
-That's where you can find reference pin muxing information for these
-pins.  You can find that the pin name for \code{A16} is \code{SPI0_CS0}
-and that the pin name for \code{B16} is \code{SPI0_D1}.
-You can also get confirmation that to obtain the (\code{I2C1_SCL} and
-\code{I2C1_SDA}) signals, you need to configure muxing mode number 2.
-You can also see that both pins support pull-up and pull-down
-modes\footnote{See \url{https://en.wikipedia.org/wiki/Pull-up_resistor}}
-(see the \code{PULLUP /DOWN TYPE} column).
-
-The next thing to do is to open the big TRM document and look for the
-address of the registers that control pin muxing. First, look for
-{\em L4\_WKUP Peripheral Memory Map} with your PDF reader search
-utility. You will find a table containing a
-\code{Control Module Registers} entry with its address:
-\code{0x44E1_0000}.
-
-Last but not least, look for the \code{SPI0_CS0} and \code{SPI0_D1}
-pin names, and you will find the offsets for the registers controlling
-muxing for these pins in the {\em CONTROL\_MODULE REGISTERS} table:
-respectively \code{0x95c} and \code{0x958}.
-
-We now know which registers we can write to to enable \code{i2c1}
-signals.
-
-\section{Add pinctrl properties to the Device Tree}
-
-Now that we know the register offsets, let's try to understand
-how they are used in existing code. For example, open the
-the Device Tree for the AM335x EVM board
-(\kfile{arch/arm/boot/dts/am335x-evm.dts}), which is using
-\code{i2c1} too. Look for \code{i2c1_pins}, and you will see how
-offsets are declared and what values they are given:
-
-{\small
-\begin{verbatim}
-i2c1_pins: pinmux_i2c1_pins {
-        pinctrl-single,pins = <
-                /* spi0_d1.i2c1_sda */
-                AM33XX_PADCONF(AM335X_PIN_SPI0_D1, PIN_INPUT_PULLUP, MUX_MODE2)
-                /* spi0_cs0.i2c1_scl */
-                AM33XX_PADCONF(AM335X_PIN_SPI0_CS0, PIN_INPUT_PULLUP, MUX_MODE2)
-        >;
-};
-\end{verbatim}
-}
+Don't hesitate to look for further interesting properties by yourself!
+
+You can also check whether \code{/sys/class/thermal} exists and is not
+empty on your system. That's the thermal framework, and it allows
+to access temperature measures from the thermal sensors on your system.
+
+Next, you can now explore all the buses (virtual or physical) available
+on your system, by checking the contents of \code{/sys/bus}.
 
-Here are details about the values:
+In particular, go to \code{/sys/bus/mmc/devices} to see all the
+MMC devices on your system. Go inside the directory for the first device
+and check several files (for example):
 
 \begin{itemize}
-\item \ksym{AM335X_PIN_SPI0_D1} and \ksym{AM335X_PIN_SPI0_CS0} offsets
-      in the Pin Controller registers to control muxing on the
-      corresponding package pins.
-\item \ksym{MUX_MODE2} corresponds to muxing mode 2, as explained in the
-      datasheet.
-\item \ksym{PIN_INPUT_PULLUP} puts the pin in pull-up mode (remember
-      that our pins support both pull-up and pull-down). By design, an
-      I2C line is never actively driven high, devices either pull the
-      line low or let it floating. As we plug our device directly on the
-      bus without more analog electronics, we need to enable the
-      internal pull-up.
+\item \code{serial}: the serial number for your device.
+\item \code{preferred_erase_size}: the preferred erase block for your
+      device. It's recommended that partitions start at multiples of this
+      size.
+\item \code{name}: the product name for your device. You could display
+      it in a user interface or log file, for example.
 \end{itemize}
 
-Now that pin muxing settings have been explained, edit your board
-DTS file to add the same definitions to enable pin muxing for \code{i2c1}.
-Don't forget that you don't have to repeat definitions that are
-already present in the \code{.dtsi} files. Just add new declarations, or
-settings that override common definitions.
+Don't hesitate to spend more time exploring \code{/sys} on your system
+and asking questions to your instructor.
 
-Rebuild and update your DTB, and eventually reboot the board.
+\section{Implement a basic I2C driver for the Nunchuk}
 
-\section{I2C bus tests}
+It is now time to start writing the first building blocks of the I2C
+driver for our Nunchuk.
 
-We will use the \code{i2cdetect} command to make sure that
-everything works fine for \code{i2c1}:
+Relying on explanations given during the lectures, fill the
+\code{nunchuk.c} file to implement:
 
-\begin{verbatim}
-# i2cdetect -l
-i2c-0	i2c       	OMAP I2C adapter                	I2C adapter
-i2c-2	i2c       	OMAP I2C adapter                	I2C adapter
-i2c-1	i2c       	OMAP I2C adapter                	I2C adapter
-\end{verbatim}
-
-\begin{verbatim}
-# i2cdetect -F 1
-Functionalities implemented by /dev/i2c-1:
-I2C                              yes
-SMBus Quick Command              no
-SMBus Send Byte                  yes
-SMBus Receive Byte               yes
-SMBus Write Byte                 yes
-SMBus Read Byte                  yes
-SMBus Write Word                 yes
-SMBus Read Word                  yes
-SMBus Process Call               yes
-SMBus Block Write                yes
-SMBus Block Read                 no
-SMBus Block Process Call         no
-SMBus PEC                        yes
-I2C Block Write                  yes
-I2C Block Read                   yes
-\end{verbatim}
+\begin{itemize}
+\item \code{probe()} and \code{remove()} functions that will
+      be called when a Nunchuk is found.
+      For the moment, just put a call to \kfunc{pr_info} inside
+      to confirm that these functions are called.
+\item Initialize a \ksym{i2c_driver} structure, and register
+      the i2c driver using it. Make sure that you use
+      a \code{compatible} property that matches the one in the
+      Device Tree.
+\end{itemize}
 
-You can see that the {\em SMBus Quick Commands} are not available on
-this driver, yet \code{i2cdetect} uses them by default to scan the i2c
-bus. You can use \code{i2cdetect -r} to use the usual set of i2c
-commands, and be able to detect the devices on your bus.
+You can now compile your module and reboot your board, to
+boot with the updated DTB.
 
-To test if everything works fine, run \code{i2cdetect -r 1}. This will
-scan the \code{i2c1} bus for devices. You should see a device at the
-address \code{0x52}. This is your nunchuk.
+\section{Driver tests}
 
-If everything works as expected, commit your Device Tree changes. This
-will be required to switch to another branch later:
+You can now load the \code{/root/nunchuk/nunchuk.ko} file.
+You need to check that the \code{probe()} function gets called
+then, and that the \code{remove()} function gets called too
+when you remove the module.
 
-\begin{verbatim}
-git commit -as
-\end{verbatim}
+List the contents of \code{/sys/bus/i2c/drivers/nunchuk}. You
+should now see a symbolic link corresponding to our new device.
 
-\begin{itemize}
-\item \code{git commit -a} adds all the files already known to
-      \code{git} to the commit.
-\item \code{git commit -s} adds a \code{Signed-off-by} line (required
-      for all contributions to the Linux kernel).
-\end{itemize}
+Also list \code{/sys/bus/i2c/devices/}. You should now see the
+Nunchuk device, which can be recognized through its \code{0052} address.
+Follow the link and you should see a symbolic link back to the Nunchuk
+driver.
 
 \section{Device initialization}
 
+Now that we have checked that the \code{probe()} and \code{remove()}
+functions are called, remove the messages that you added to trace the
+execution of these functions.
+
 The next step is to read the state of the nunchuk registers, to find out
 whether buttons are pressed or not, for example.
 
 Before being able to read nunchuk registers, the first thing to do is
 to send initialization commands to it. That's also a nice way of making
-sure i2c communication works as expected.
+sure I2C communication works as expected.
 
 In the probe routine (run every time a matching device is found):
 
diff --git a/labs/kernel-i2c-describing-hardware/bbb-i2c.png b/labs/kernel-i2c-describing-hardware/bbb-i2c.png
new file mode 100644
index 00000000..20bdba61
Binary files /dev/null and b/labs/kernel-i2c-describing-hardware/bbb-i2c.png differ
diff --git a/labs/kernel-i2c-describing-hardware/kernel-i2c-describing-hardware.tex b/labs/kernel-i2c-describing-hardware/kernel-i2c-describing-hardware.tex
new file mode 100644
index 00000000..95316ab5
--- /dev/null
+++ b/labs/kernel-i2c-describing-hardware/kernel-i2c-describing-hardware.tex
@@ -0,0 +1,219 @@
+\subchapter{Describing Hardware Devices}
+{Objective: learn how to describe hardware devices.}
+
+\section{Goals}
+
+Now that we covered the Device Tree theory, we can explore the list of
+existing devices and make new ones available. In particular, we will
+create a custom Device Tree to describe the few extensions we will make
+to our BBB.
+
+\section{Setup}
+
+Go to the \code{~/linux-kernel-labs/src/linux} directory. Check out the
+\code{5.10.bootlin} branch.
+
+Now create a new \code{nunchuk} branch starting from this branch,
+for your upcoming work on the Nunchuk driver.
+
+Download a useful document sharing useful details about the Nunchuk
+and its connector:\\
+\url{https://bootlin.com/labs/doc/nunchuk.pdf}
+
+\section{Create a custom device tree}
+
+To let the Linux kernel handle a new device, we need to add a
+description of this device in the board device tree.
+
+As the Beaglebone Black device tree is provided by the kernel community,
+and will continue to evolve on its own, we don't want to make changes
+directly to the device tree file for this board.
+
+The easiest way to customize the board DTS is to create a new DTS file
+that includes the Beaglebone Black or Black Wireless DTS, and add
+its own definitions.
+
+So, create a new \code{am335x-customboneblack.dts} file in which you
+just include the regular board DTS file. We will add further definitions
+in the next sections.
+
+\begin{verbatim}
+// SPDX-License-Identifier: GPL-2.0
+#include "am335x-boneblack-wireless.dts"
+\end{verbatim}
+
+Modify the \kfile{arch/arm/boot/dts/Makefile} file to add your custom
+Device Tree, and then have it compiled with (\code{make dtbs}). Now,
+copy the new DTB to the tftp server home directory, change the DTB file
+name in the U-Boot configuration\footnote{Tip: you just need to run
+\code{editenv bootcmd} and \code{saveenv}.}, and boot the board.
+
+\section{Setting the board's model name}
+
+Modify the custom Device Tree file to override the model name for your
+system. Set the \code{model} property to \code{Training Beagle Bone
+Black}. Don't hesitate to ask your instructor if you're not sure how.
+
+Recompile the device tree, and reboot the board with it. You should see
+the new model name in two different places:
+
+\begin{itemize}
+\item In the first kernel messages on the serial console.
+\item In \code{/sys/firmware/devicetree/base/model}. This can be
+      handy for a distribution to identify the device it's running on.
+\end{itemize}
+
+\section{Driving LEDs}
+
+The BBB features four user LEDs (\code{USR0}, \code{USR1}, \code{USR2},
+\code{USR3}) in the corner near the micro-USB connector. When the board
+is running, none of them is currently enabled.
+
+Start by looking at the different description files and look for a node
+that would be defining the LEDs.
+
+The four LEDs are actually supposed to be triggered by a driver matching
+the compatible \code{gpio-leds}. This is a generic driver which acts on
+LEDs connected to GPIOs. But as you can observe, despite being part of
+the in-use Device Tree, the LEDs remain off.
+
+If you look at the bindings documents
+\kfile{Documentation/devicetree/bindings/leds/common.yaml} and
+\kfile{Documentation/devicetree/bindings/leds/leds-gpio.yaml}, you'll
+notice we can easily tweak the \code{default-state} in order to see if
+forcing the default state make them bright.
+
+You will need to modify a core file in order to do that. But becauase we
+do not want to mess with a shared file, we might instead add a label to
+the node describing the LED we want to turn on and reference this new
+label in our custom DTS.
+
+Reboot the board using the new DTS and observe the LEDs default states
+change.
+
+If you look again at the common file defining the LEDs, they are
+actually all linked to a \code{linux,default-trigger}. If you want to
+see these triggers in action (and overwrite the default state), make
+sure your kernel is compiled with \kconfigval{CONFIG_LEDS_CLASS}{y},
+\kconfigval{CONFIG_LEDS_GPIO}{y} and
+\kconfigval{CONFIG_LEDS_TRIGGER_HEARTBEAT}{y}, for instance.
+
+\section{Managing I2C buses and devices}
+
+The next thing we want to do is connect an Nunchuk joystick
+to an I2C bus on our board. The I2C bus is very frequently used
+to connect all sorts of external devices. That's why we're covering
+it here.
+
+\subsection{Enabling an I2C bus}
+
+As shown on the below picture found on
+\url{https://elinux.org/Beagleboard:Cape_Expansion_Headers}, the
+BeagleBone Black has two I2C busses available on its expansion headers:
+I2C1 and I2C2. Another one exists (I2C0), but it's not
+available on the external headers.
+
+\includegraphics[width=0.7\textwidth]{labs/kernel-i2c-describing-hardware/bbb-i2c.png}
+
+In this lab, we will try to use I2C1, because it's more interesting to
+use than I2C2 which is already enabled by default.
+
+So, let's see which I2C buses are already enabled:
+
+\begin{bashinput}
+# i2cdetect -l
+i2c-2	i2c             OMAP I2C adapter                        I2C adapter
+i2c-0	i2c             OMAP I2C adapter                        I2C adapter
+\end{bashinput}
+
+Here you can see that I2C1 is missing.
+
+As the bus numbering scheme in Linux doesn't always match the one
+on the datasheets, let's check the base addresses of the registers
+of these controllers:
+
+\begin{bashinput}
+# ls -l /sys/bus/i2c/devices/i2c-*
+lrwxrwxrwx    1         0 Jan  1 00:59 /sys/bus/i2c/devices/i2c-0 -> ../../../devices/platform/ocp/44c00000.interconnect/44c00000.interconnect:segment at 200000/44e0b000.target-module/44e0b000.i2c/i2c-0
+lrwxrwxrwx    1         0 Jan  1 00:59 /sys/bus/i2c/devices/i2c-2 -> ../../../devices/platform/ocp/48000000.interconnect/48000000.interconnect:segment at 100000/4819c000.target-module/4819c000.i2c/i2c-2
+\end{bashinput}
+
+That's not completely straighforward, but you can suppose that:
+\begin{itemize}
+\item I2C0 is at address \code{0x44e0b000}
+\item I2C2 is at address \code{0x4819c000}
+\end{itemize}
+
+Now let's double check the addressings by looking at the
+\href{https://www.ti.com/lit/ug/spruh73q/spruh73q.pdf}{TI AM335x SoC
+datasheet}, in the \code{L4_WKUP Peripheral Memory Map} section:
+
+\begin{itemize}
+\item I2C0 is indeed at address \code{0x44e0b000}
+\item I2C1 is at address \code{0x4802a000}
+\item I2C2 is indeed at address \code{0x4819c000}
+\end{itemize}
+
+So, we are lucky that \code{i2c-0} in Linux corresponds to I2C0 in the
+datasheet, and that \code{i2c-2} corresponds to I2C2.
+We're just missing \code{i2c-1}.
+
+Fortunately, I2C1 is already defined in the one of the DTS includes
+used by the Device Tree for our board. In our case, that's in
+\kfile{arch/arm/boot/dts/am33xx-l4.dtsi}. Look by yourself in this
+file, and you will find its definition, but with \code{status =
+"disabled";}. This means that this I2C controller is not enabled yet,
+and it's up to boards using it to do so.
+
+Make a reference to this definition in your custom DTS and enable this
+bus. Also configure it to function at 100 KHz. Reboot your board with
+the update.
+
+Back to the running system, we can now see that there is one more
+I2C bus:
+
+\begin{bashinput}
+# i2cdetect -l
+i2c-1	i2c             OMAP I2C adapter                        I2C adapter
+i2c-2	i2c             OMAP I2C adapter                        I2C adapter
+i2c-0	i2c             OMAP I2C adapter                        I2C adapter
+\end{bashinput}
+
+Run the below command to confirm that the new bus has the same address
+as in the datasheet (\code{0x4802a000}):
+
+\bashcmd{ls -l /sys/bus/i2c/devices/i2c-1}
+
+\subsection{Declare the Nunchuk device}
+
+As a child node to the \code{i2c1} bus, now declare an I2C device
+for the Nunchuk, choosing \code{nintendo,nunchuk} for its \code{compatible}
+property. You will find the I2C slave address of the Nunchuk on the
+nunckuk document that we have downloaded earlier\footnote{This I2C slave
+address is enforced by the device itself. You can't change it.}.
+The node name should be \code{joystick at addr}, the convention for node
+names is \code{<device-type>@<addr>}.
+
+After updating the running Device Tree, explore
+\code{/sys/firmware/devicetree}, where every subdirectory corresponds to
+a DT node, and every file corresponds to a DT property. You can search
+for presence of the new \code{joystick} node:
+
+{\small
+\begin{verbatim}
+# find /sys/firmware/devicetree -name "*joystick*"
+/sys/firmware/devicetree/base/ocp/interconnect at 48000000/segment at 0/target-module at 2a000/i2c at 0/joystick at 52
+\end{verbatim}
+}
+
+You can also check the whole structure of the loaded Device Tree, using
+the Device Tree Compiler (\code{dtc}), which we put in the root
+filesystem:
+\begin{verbatim}
+# dtc -I fs /sys/firmware/devicetree/base/ > /tmp/dts
+# grep -C10 nunchuk /tmp/dts
+\end{verbatim}
+
+Once your new Device Tree seems correct, commit your changes. As you
+modified a shared file and a custom file, it is good practice to commit
+these changes in two different patches.
diff --git a/labs/kernel-i2c-device-model/kernel-i2c-device-model.tex b/labs/kernel-i2c-device-model/kernel-i2c-device-model.tex
deleted file mode 100644
index 2c46663a..00000000
--- a/labs/kernel-i2c-device-model/kernel-i2c-device-model.tex
+++ /dev/null
@@ -1,180 +0,0 @@
-\subchapter{Device Model - I2C device}{Objective: declare an I2C device
-  and basic driver hooks called when this device is detected}
-
-Throughout the upcoming labs, we will implement a driver for an I2C
-device, which offers the functionality of an I2C Nunchuk.
-
-After this lab, you will be able to:
-
-\begin{itemize}
-\item Add an I2C device to a device tree.
-\item Implement basic \code{probe()} and \code{remove()} driver
-functions and make sure that they are called when there is a
-device/driver match.
-\item Find your driver and device in \code{/sys}.
-\end{itemize}
-
-\section{Setup}
-
-Go to the \code{~/linux-kernel-labs/src/linux} directory. Check out the
-\code{5.10.bootlin} branch.
-
-Now create a new \code{nunchuk} branch starting from this branch,
-for your upcoming work on the Nunchuk driver.
-
-During this lab, we will start to implement a driver for a
-Nunchuk I2C device, but at this stage we won't need to connect
-it yet, as we will enable I2C on the right board pins in the
-next lab.
-
-Download a useful document sharing useful details about the Nunchuk
-and its connector:\\
-\url{https://bootlin.com/labs/doc/nunchuk.pdf}
-
-\section{Create a custom device tree}
-
-To let the Linux kernel handle a new device, we need to add a
-description of this device in the board device tree.
-
-As the Beaglebone Black device tree is provided by the kernel community,
-and will continue to evolve on its own, we don't want to make changes
-directly to the device tree file for this board.
-
-The easiest way to customize the board DTS is to create a new DTS file
-that includes the Beaglebone Black or Black Wireless DTS, and add
-its own definitions.
-
-So, create a new \code{am335x-customboneblack.dts} file in which you
-just include the regular board DTS file. We will add further definitions
-in the next sections.
-
-Now, modify the corresponding \code{Makefile} to make sure the
-new DTS is compiled automatically.
-
-\subsection{Enable the second I2C bus}
-
-We are first going to enable and configure the second I2C bus
-(\code{i2c1}).
-
-First, as an exercise, find the DTS include file defining (\code{i2c1})
-for the SoC in our board.
-
-Then, make a reference to this definition in your custom DTS and
-enable this bus. Also configure it to function at 100 KHz. That's
-enough so far!
-
-\subsection{Declare the Nunchuk device}
-
-As a child node to the \code{i2c1} bus, now declare an I2C device
-for the Nunchuk, choosing \code{nintendo,nunchuk} for its \code{compatible}
-property. You will find the I2C slave address of the Nunchuk on the
-nunckuk document that we have downloaded earlier\footnote{This I2C slave
-address is enforced by the device itself. You can't change it.}.
-The node name should be \code{joystick at addr}, the convention for node
-names is \code{<device-type>@<addr>}.
-
-\subsection{Checking the device tree on the running system}
-
-Now, just compile your DTB by asking the kernel Makefile to recompile
-only DTBs:
-
-\begin{verbatim}
-make dtbs
-\end{verbatim}
-
-Now, copy the new DTB to the tftp server home directory, change the DTB
-file name in the U-Boot configuration\footnote{Tip: you just need to run
-\code{editenv bootcmd} and \code{saveenv}.}, and boot the board.
-
-Through the \code{/sys/firmware/devicetree} directory, it is possible to check
-the Device Tree settings that your system has loaded. That's useful when
-you are not sure exactly which settings were actually loaded.
-
-For example, you can check the presence of a new \code{joystick} node in
-your device tree:
-
-{\small
-\begin{verbatim}
-# find /sys/firmware/devicetree -name "*joystick*"
-/sys/firmware/devicetree/base/ocp/interconnect at 48000000/segment at 0/target-module at 2a000/i2c at 0/joystick at 52
-\end{verbatim}
-}
-
-As the base address of the I2C1 controller registers was not explicited
-in the DTSI file (at least not in the corresponding node), you can now
-compute this address from the above line: it's \code{0x48000000 + 0x2a000
-= 0x4802a000}.
-
-Also find the same address in the big processor Technical Reference
-Manual\footnote{Tip: to do your search, put an underscore character
-in the middle of the address, as in \code{FFFF_FFFF}... that's how
-addresses are written in this document.}.
-
-Back to \code{/sys/firmware/devicetree/}, you can also check the whole
-structure of the loaded Device Tree, using the Device Tree Compiler
-(\code{dtc}), which we put in the root filesystem. That's better than
-checking the source files and includes in the source directory:
-
-\begin{verbatim}
-# dtc -I fs /sys/firmware/devicetree/base/ > /tmp/dts
-\end{verbatim}
-
-Look for \code{i2c1} and \code{joystick} in the output file,
-and see where the nodes are instantiated. Don't hesitate to ask your
-instructor for questions!
-
-\section{Implement a basic I2C driver for the Nunchuk}
-
-It is now time to start writing the first building blocks of the I2C
-driver for our Nunchuk.
-
-In a new terminal, go to
-\code{~/linux-kernel-labs/modules/nfsroot/root/nunchuk/}.  This directory
-contains a Makefile and an almost empty \code{nunchuk.c} file.
-
-Now, you can compile your out-of-tree module by running \code{make}. As
-the current directory is part of the NFS root that the board boots on,
-the generated \code{.ko} file will immediately be visible on the board
-too.
-
-Relying on explanations given during the lectures, fill the
-\code{nunchuk.c} file to implement:
-
-\begin{itemize}
-\item \code{probe()} and \code{remove()} functions that will
-      be called when a Nunchuk is found.
-      For the moment, just put a call to \kfunc{pr_info} inside
-      to confirm that these functions are called.
-\item Initialize a \ksym{i2c_driver} structure, and register
-      the i2c driver using it. Make sure that you use
-      a \code{compatible} property that matches the one in the
-      Device Tree.
-\end{itemize}
-
-You can now compile your module and reboot your board, to
-boot with the updated DTB.
-
-\section{Driver tests}
-
-You can now load the \code{/root/nunchuk/nunchuk.ko} file.
-You need to check that the \code{probe()} function gets called
-then, and that the \code{remove()} function gets called too
-when you remove the module.
-
-Once your new Device Tree and module work as expected, commit
-your DT changes in your Linux tree:
-
-\begin{verbatim}
-git commit -sa
-\end{verbatim}
-
-\section{Exploring /sys}
-
-Take a little time to explore \code{/sys}:
-
-\begin{itemize}
-\item Find the representation of your driver. That's a way
-      of finding the matching devices.
-\item Find the representation of your device, containing its name.
-      You will find a link to the driver too.
-\end{itemize}
diff --git a/labs/kernel-i2c-multiplexing/kernel-i2c-multiplexing.tex b/labs/kernel-i2c-multiplexing/kernel-i2c-multiplexing.tex
new file mode 100644
index 00000000..b3509ed5
--- /dev/null
+++ b/labs/kernel-i2c-multiplexing/kernel-i2c-multiplexing.tex
@@ -0,0 +1,210 @@
+\subchapter{Configuring the pin muxing}
+{Objective: learn how to declare and use a muxing state.}
+
+\section{Goals}
+
+As part of the previous lab, we enabled an I2C controller and described
+a device plugged on the bus. In this lab we will cover how to ensure a
+proper communication between the two and be able to declare and use
+pinctrl settings.
+
+\section{Setup}
+
+Continue using the \code{nunchuk} branch in the
+\code{~/linux-kernel-labs/src/linux} directory.
+
+\section{Probing the different busses}
+
+Now, let's use \code{i2cdetect}'s capability to probe a bus for
+devices. The I2C bus has no real discovery capability, but yet, the tool
+exploits a feature of the specification: when the master talks to a
+device, it starts by sending the target address on the bus and expects
+it to be acked by the relevant device. Iterating through all the
+possible addresses without sending anything after the address byte,
+looking for the presence of an Ack is what uses the tool to probe the
+devices. That is also why we get a warning when using it.
+
+Let's start by probing the bus associated to \code{i2c-0}:
+
+\begin{bashinput}
+# i2cdetect -r 0
+i2cdetect: WARNING! This program can confuse your I2C bus
+Continue? [y/N] y
+     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
+00:          -- -- -- -- -- -- -- -- -- -- -- -- --
+10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+20: -- -- -- -- UU -- -- -- -- -- -- -- -- -- -- --
+30: -- -- -- -- 34 -- -- -- -- -- -- -- -- -- -- --
+40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+50: 50 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+70: -- -- -- -- -- -- -- --
+\end{bashinput}
+
+We can see three devices on this internal bus:
+\begin{itemize}
+\item One at address \code{0x24}, indicated by \code{UU},
+      which means that there is a kernel driver actively
+      driving this device.
+\item Two other devices at addresses \code{0x34} and \code{0x50}.
+      We just know that they are currently not bound to a kernel driver.
+\end{itemize}
+
+Now try to probe I2C1 through \code{i2cdetect -r 1}.
+
+You will see that the command will fail to connect to
+the bus. That's because the corresponding signals are
+not exposed yet to the outside connectors through pin muxing.
+
+\section{Find pin muxing configuration information for i2c1}
+
+As you found in the previous lab, we now managed to have our nunchuk
+device enumerated on the \code{i2c1} bus.
+
+However, to access the bus data and clock signals, we need to configure
+the pin muxing of the SoC.
+
+If you go back to the BeagleBone Black System Reference Manual, in the
+{\em Connector P9} section, you can see that the pins \code{17} and
+\code{18} that we are using correspond to pins \code{A16} and \code{B16}
+of the AM335 SoC. You can also see that such pins need to be configured
+as \code{MODE2} to get the functionality that we need (\code{I2C1_SCL}
+and \code{I2C1_SDA}).
+
+The second step is to open the CPU datasheet (\code{am3359.pdf}), and
+look for pin assignment information ({\em Pin Assignments} section).
+You will find that the processor is available through two types of
+packages: \code{ZCE} and \code{ZCZ}. If you have an original
+BeagleBoneBlack board, you can have a very close look at the CPU
+(with your glasses on!) and you will see that the CPU has \code{ZCZ} written
+on its lower right corner. On BeagleBoneBlack Wireless with the
+Octavo System In Package, you can no longer find such information.
+Anyway, the \code{ZCZ} package information applies to both types of
+boards.
+
+So, in the {\em ZCZ Package Pin Maps (Top View)} section\footnote{Caution: you
+won't be able to search the PDF file for this section name, for obscure
+reasons. At the time of this writing, this section is numbered 4.1.2.}, you can find
+hyperlinks to the descriptions of the \code{A16} and \code{B16} pins.
+That's where you can find reference pin muxing information for these
+pins.  You can find that the pin name for \code{A16} is \code{SPI0_CS0}
+and that the pin name for \code{B16} is \code{SPI0_D1}.
+You can also get confirmation that to obtain the (\code{I2C1_SCL} and
+\code{I2C1_SDA}) signals, you need to configure muxing mode number 2.
+You can also see that both pins support pull-up and pull-down
+modes\footnote{See \url{https://en.wikipedia.org/wiki/Pull-up_resistor}}
+(see the \code{PULLUP /DOWN TYPE} column).
+
+The next thing to do is to open the big TRM document and look for the
+address of the registers that control pin muxing. First, look for
+{\em L4\_WKUP Peripheral Memory Map} with your PDF reader search
+utility. You will find a table containing a
+\code{Control Module Registers} entry with its address:
+\code{0x44E1_0000}.
+
+Last but not least, look for the \code{SPI0_CS0} and \code{SPI0_D1}
+pin names, and you will find the offsets for the registers controlling
+muxing for these pins in the {\em CONTROL\_MODULE REGISTERS} table:
+respectively \code{0x95c} and \code{0x958}.
+
+We now know which registers we can write to to enable \code{i2c1}
+signals.
+
+\section{Multiplexing the I2C controller outputs correctly}
+
+Now that we know the register offsets, let's try to understand
+how they are used in existing code. For example, open the
+the Device Tree for the AM335x EVM board
+(\kfile{arch/arm/boot/dts/am335x-evm.dts}), which is using
+\code{i2c1} too. Look for \code{i2c1_pins}, and you will see how
+offsets are declared and what values they are given:
+
+{\small
+\begin{verbatim}
+i2c1_pins: pinmux_i2c1_pins {
+        pinctrl-single,pins = <
+                /* spi0_d1.i2c1_sda */
+                AM33XX_PADCONF(AM335X_PIN_SPI0_D1, PIN_INPUT_PULLUP, MUX_MODE2)
+                /* spi0_cs0.i2c1_scl */
+                AM33XX_PADCONF(AM335X_PIN_SPI0_CS0, PIN_INPUT_PULLUP, MUX_MODE2)
+        >;
+};
+\end{verbatim}
+}
+
+Here are details about the values:
+
+\begin{itemize}
+\item \ksym{AM335X_PIN_SPI0_D1} and \ksym{AM335X_PIN_SPI0_CS0} offsets
+      in the Pin Controller registers to control muxing on the
+      corresponding package pins.
+\item \ksym{MUX_MODE2} corresponds to muxing mode 2, as explained in the
+      datasheet.
+\item \ksym{PIN_INPUT_PULLUP} puts the pin in pull-up mode (remember
+      that our pins support both pull-up and pull-down). By design, an
+      I2C line is never actively driven high, devices either pull the
+      line low or let it floating. As we plug our device directly on the
+      bus without more analog electronics, we need to enable the
+      internal pull-up.
+\end{itemize}
+
+Now that pin muxing settings have been explained, edit your board
+DTS file to add the same definitions to enable pin muxing for \code{i2c1}.
+Don't forget that you don't have to repeat definitions that are
+already present in the \code{.dtsi} files. Just add new declarations, or
+settings that override common definitions.
+
+Rebuild and update your DTB, and eventually reboot the board. You should
+now be able to probe your bus:
+
+\begin{bashinput}
+# i2cdetect -r 1
+i2cdetect: WARNING! This program can confuse your I2C bus
+Continue? [y/N] y
+     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
+00:          -- -- -- -- -- -- -- -- -- -- -- -- --
+10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+70: -- -- -- -- -- -- -- --
+\end{bashinput}
+
+No device is detected yet, because we did not wire the nunchuk yet.
+
+\subsection{Wiring the I2C device}
+
+Let's connect the Nunchuk provided by your instructor
+to the I2C1 bus on the board, using breadboard wires:
+
+\includegraphics[width=0.3\textwidth]{common/nunchuk-pinout.pdf}
+\includegraphics[width=0.7\textwidth]{common/bbb-connect-nunchuk.pdf}
+
+\begin{itemize}
+\item Connect the Nunchuk PWR pin to pin 4 (3V3) of connector P9
+\item Connect the Nunchuk GND pin to pin 1 (GND) of connector P9
+\item Connect the Nunchuk SCL pin to pin 17 of connector P9
+\item Connect the Nunchuk SDA pin to pin 18 of connector P9
+\end{itemize}
+
+If you didn't do any mistake, your new device should be detected at
+address \code{0x52}:
+
+\begin{bashinput}
+# i2cdetect -r 1
+i2cdetect: WARNING! This program can confuse your I2C bus
+Continue? [y/N] y
+     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
+00:          -- -- -- -- -- -- -- -- -- -- -- -- --
+10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+50: -- -- 52 -- -- -- -- -- -- -- -- -- -- -- -- --
+60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+70: -- -- -- -- -- -- -- --
+\end{bashinput}
+
+We will later compile an out-of-tree kernel module to support this device.
diff --git a/mk/linux-kernel.mk b/mk/linux-kernel.mk
index 14788f40..ddc5841e 100644
--- a/mk/linux-kernel.mk
+++ b/mk/linux-kernel.mk
@@ -21,9 +21,9 @@ LINUX_KERNEL_SLIDES = \
 		kernel-driver-development-modules \
 		kernel-driver-development-lab-modules \
 		kernel-hw-devices \
+		kernel-pinmuxing \
 		kernel-device-model \
 		kernel-i2c \
-		kernel-pinmuxing \
 		kernel-frameworks \
 		kernel-input \
 		kernel-driver-development-memory \
@@ -59,7 +59,8 @@ LINUX_KERNEL_LABS   = setup \
 		kernel-board-setup \
 		kernel-compiling-and-nfs-booting \
 		kernel-module-simple \
-		kernel-i2c-device-model \
+		kernel-i2c-describing-hardware \
+		kernel-i2c-multiplexing \
 		kernel-i2c-communication \
 		kernel-i2c-input-interface \
 		kernel-serial-iomem \
diff --git a/slides/kernel-hw-devices/kernel-hw-devices.tex b/slides/kernel-hw-devices/kernel-hw-devices.tex
index 04acf0df..02555c8c 100644
--- a/slides/kernel-hw-devices/kernel-hw-devices.tex
+++ b/slides/kernel-hw-devices/kernel-hw-devices.tex
@@ -1346,18 +1346,13 @@ uart0 at 4000c000 {
   \end{columns}
 \end{frame}
 
-% \setuplabframe
-% {Accessing hardware devices}
-% {
-%   Time to start the practical lab!
-%   \begin{itemize}
-%   \item Exploring the contents of \code{/dev} and \code{/sys} and the
-%     devices available on the embedded hardware platform.
-%   \item Using GPIOs and LEDs.
-%   \item Modifying the Device Tree to control pin multiplexing and
-%         declare an I2C-connected joystick.
-%   \item Adding support for a USB audio card using Linux kernel modules
-%   \item Adding support for the I2C-connected joystick through
-%         an out-of-tree module.
-%   \end{itemize}
-% }
+\setuplabframe
+{Accessing hardware devices}
+{
+  \begin{itemize}
+  \item Browse and update Device Trees.
+  \item Use GPIO LEDs.
+  \item Modify the Device Tree to enable an I2C controller and describe
+    an I2C device.
+  \end{itemize}
+}
diff --git a/slides/kernel-i2c/kernel-i2c.tex b/slides/kernel-i2c/kernel-i2c.tex
index 9baeb8db..ddf57e10 100644
--- a/slides/kernel-i2c/kernel-i2c.tex
+++ b/slides/kernel-i2c/kernel-i2c.tex
@@ -281,19 +281,6 @@ static int da311_remove(struct i2c_client *client)
   From \kfile{drivers/iio/accel/da311.c}
 \end{frame}
 
-\setuplabframe
-{Linux device model for an I2C driver}
-{
-  \begin{itemize}
-  \item Modify the Device Tree to instantiate an I2C device.
-  \item Implement a driver that registers as an I2C driver.
-  \item Make sure that the probe/remove functions are called
-        when there is a device/driver match.
-  \item Explore the {\em sysfs} entries related to your driver and
-    device.
-  \end{itemize}
-}
-
 \begin{frame}{Communicating with the I2C device: raw API}
   The most {\bf basic API} to communicate with the I2C device provides
   functions to either send or receive data:
@@ -460,3 +447,14 @@ From \kfile{drivers/input/touchscreen/st1232.c}
     \href{https://www.youtube.com/watch?v=g9-wgdesvwA}{video}).
   \end{itemize}
 \end{frame}
+
+\setuplabframe
+{Communicate with the Nunchuk}
+{
+  \begin{itemize}
+  \item Explore the content of \code{/dev} and \code{/sys} and the
+    devices available on the embedded hardware platform.
+  \item Implement a driver that registers as an I2C driver.
+  \item Communicate with the Nunchuk and extract data from it.
+  \end{itemize}
+}
diff --git a/slides/kernel-pinmuxing/kernel-pinmuxing.tex b/slides/kernel-pinmuxing/kernel-pinmuxing.tex
index 2af6d300..46b63fd6 100644
--- a/slides/kernel-pinmuxing/kernel-pinmuxing.tex
+++ b/slides/kernel-pinmuxing/kernel-pinmuxing.tex
@@ -173,14 +173,12 @@ i2c0: i2c at f8014000 {
 \end{frame}
 
 \setuplabframe
-{Communicate with the Nunchuk}
+{Setup pinmuxing to enable I2C communication}
 {
   \begin{itemize}
   \item Configure the pinmuxing for the I2C bus used to communicate
     with the Nunchuk
   \item Validate that the I2C communication works with user space
     tools.
-  \item Extend the I2C driver started in the previous lab to
-    communicate with the Nunchuk.
   \end{itemize}
 }




More information about the training-materials-updates mailing list