 My name is Marc Vashid and today I'm going to talk about doing the UBoot Loader port right with all the latest features. Just briefly about me, I work as a software engineer contractor. I'm one of the maintainers in the UBoot project. I, in that project I work on USB, a couple of stock ports of this IntelliQ scale, the Renaissance, ARCAR, and Altair OSUG FPGA. That's what I maintain. I also do some sort of work on the Linux kernel and open and beat it and I'm an FPGA hobbyist. So in case you have interest in any of these topics, please talk to me after this and tomorrow. I'm happy to discuss that stuff. Now, I decided to split this talk into multiple sections. First of all, if you have no idea what UBoot is, I would like to show you what that is and what it's capable of. Once we get through that, I would like to do a brief news flash, just to sync people up who did UBoot ports before to tell them what's new and so on very quickly. Once we get through that, I'll go through the basics like building UBoot from source, the structure of the UBoot directory, this sort of stuff, the basic things. Then I would like to look into device tree control and UBoot driver model. Once we go through these fundamental things, I will go into doing UBoot port because you'll have all the knowledge at that point to actually do a basic UBoot port. Now, since enabling all that stuff, we'll probably make UBoot grow up quite a bit. We'll talk about shrinking UBoot size at the end. That's what this talk is going to be about. First of all, UBoot boot loader. What is it? Well, a boot loader is a piece of code which is running super early on on your platform and it's responsible for initializing the platform. Only enough so it is capable of loading the next stage, which in case of a boot loader is usually an operating system kernel into memory and starting that next stage. This is what UBoot can do. It can behave as a boot loader, but it can also behave as much more. It can behave as a boot monitor. That means it gives you a shell and it can behave as a debug tool. You can fiddle around with memory, you can operate buses, you can access USB-I square C, SPI, storage, SATA, this sort of stuff. All that you can do with UBoot. It's a really powerful tool. Here's an example of slightly older version of UBoot. Actually, this is two releases old, but the sort of printout you get when you start a system which has UBoot on it doesn't really change. What you can see here is UBoot 2017.11. Actually, a new version, 2018.03, was released yesterday with, again, some improvements. Now, what else you see in this printout is CPU, type of the board, some sort of RAM, some sort of flash, this sort of stuff. We have also Ethernet, and ultimately we reached the boot monitor prompt where I'm dumping memory. This is just an example of showing you that you can do a lot of stuff with UBoot and how it looks when you power up a system which contains UBoot. Now, if you're asking for some sort of help online about UBoot, especially on IRC and in the mailing list, it's really convenient if you include the UBoot version, at least, which CPU it is and which board it is in the report. It makes it much easier for the UBoot developers to help you out, because if you don't do that, then the next question will be which version, which board, which CPU. And this is all conveniently, like, here in the printout. You can just, like, copy-paste that into your email and be done with it. It will make our life much easier. So that's what UBoot is about. Now, very quickly, about using UBoot, we are progressing in converting UBoot to a Driver Device 3 control and the Driver Model. That is, we are moving away from the old hard coding everything into binary to a much more flexible model of just supplying a Device 3 and having significantly better drivers in UBoot. I'll talk about that in detail in a bit. We recently got FE support. That means UBoot can behave as an FE library and provide FE services to, for example, Grub. So then you can kind of start an FE application like Grub, which is using this FE protocol to talk to UBoot and consume services like serial board access, block access, network access, that sort of stuff, which I believe might be interesting, especially for distro people. We also have distro boot command support, which is sort of a predecessor of that. It's just a standard UBoot environment, which, in case you are rolling out some sort of development kit you should enable, it allows it again, it lets distros expect something from the UBoot environment, some sort of standard, and they can tap into it and it's, again, much easier for the distro people. Now, we recently got support for applying Device 3 overlays and this is a bit of a tool for circumventing the current situation with Device 3 overlays loading in Linux. So what basically happens in Linux is that the Device 3 overlays support is all there, but the config fs, Device 3 overlays, the loader is not there, so it's kind of got moved into UBoot and UBoot is able to take Device 3, apply Device 3 overlays on top of that Device 3 and pass that to Linux instead. So you do not have to patch your kernel with the config fs loader. Now, even more recently, we got support for applying Device 3 overlays through the UBoot fit image. If this tells you nothing, it's probably not your sort of niche and you don't really have to get about that, so it doesn't have to stress you if you don't understand what I just said. And if you're interested in that, we can talk about it later, but it's unrelated to this talk. One last thing I want to talk about is that we now use Travis CI to do continuous integration on UBoot, and you can also do that yourself. If you're submitting patches, just get a GitHub account, enable Travis CI. The UBoot source is now contained, Travis.iml file, so when you push it to GitHub, it will just run the entire UBoot Source 3 and it will build it for multiple architectures just to make sure that your patch didn't break anything. It's really convenient, this sort of stuff, and it also makes it easier for you so you don't have to grab all the tool chains and just use the Travis CI nowadays. So that's it for the UBoot news. Now, let's get to building UBoot. So if you want to build UBoot, you obviously need to get the sources from somewhere. The master UBoot repo is available together at this address. It's also available through HTTP, so you can get it from there. If you also need tar balls, then they are available again there. It can just so happen that you are developing some sort of feature and you're probably talking to one of the maintainers of the subsystems, at which point they may tell you, okay, so I have this stuff on which I would like you to base your new feature on, and it's available in my maintainer repo, so the maintainer repos are available in this second address. Usually you're not going to need it, but anyways, that's what it is. So once you have the UBoot sources, you probably want to build them, and to build the UBoot sources, use the following method. So just change into the UBoot source directory, export these two variables. The first one selects the architecture for which you are building, so in case you are building, for example, for ARM, this will be ARC equals ARM. The other one is your cross-compiler toolchain prefix. For ARM, this will be something like ARM Linux dash, something like that. For MIPS, it's going to be like MIPS known or MIPS Linux, whatever, this sort of thing. Now, once you have the environment set up, you need to configure the UBoot sources, and for that, you need a supported configuration, which you can locate under the configs directory. Once you've located your configuration for your board, do make your board dev config, the UBoot sources will get configured, and then use make to compile it. It will produce some sort of binary, which you can then install on your system. There is one more detail to this. You can now compile UBoot as an user space Linux application with the sandbox dev config, at which point you do not need to export any of the ARC and cross-compile options. You just do make sandbox dev config and make, and it will generate UBoot elf binaries, a Linux application which you can then launch, and it will give you UBoot shell. So if you want to play around with UBoot shell without having any sort of development kit, you can do that with the sandbox. Okay. Now, if you want to add any sort of stuff into the UBoot, or if you want to hack on it, it's probably a good idea to know the directory structure of the UBoot source 3. It actually is designed in such a way that it matches Linux kernel for the most part. So the ARCH specific stuff is in ARCH directory, in ARCH slash something like ARM or MIPS, whatever, are the CPU specific things. If you have some sort of CPU vendor, then it goes into MacFoo here. Unlike Linux, the device 3 source files are in ARCH slash whatever slash DTS directly. It's not in ARM slash whatever slash boot. So that's a small difference there. We have board support in the board directory, which you won't find in the Linux kernel 3. And then the rest is very similar. So configs contain Kconfigized board DevConfig. The thing is, we are still in the process of migrating to Kconfig. So you'll find a couple of legacy config options in include slash configs still. This might be... This sort of duality might be a little confusing, but anyways, GitGribe will give you an idea where either of these is. Now, we have drivers and drivers. UBoot shell commands, implementation is in CMD nowadays. Common code is in common lib. Networking stack is in net. And file system code is in FS. It kind of is logically organized like. So you will also notice that if you look into the UBoot sources, there's a lot of Kconfig and Kbuild make files. So yeah, UBoot is nowadays migrating toward Kconfig and Kbuild. It's still kind of in progress, but we are just trying to move over. And that's why you still have a couple of config options which are not converted in include configs, unfortunately. But any new UBoot config options are Kconfig only. If you have some option which is not Kconfig only, you can verify that it's still not been converted by looking into this file. But if you're submitting any sort of new stuff, it has to be Kconfig only. Now, to give you an idea how such a Kconfig config option looks like, I just pulled out the driver's net Kconfig here. So in this file, we have a DMETH config option. If you like to make menu config, you would be able to find this in the menu config outpad. So this option enables driver model for internet. The entry is of type bool. If you look it up in like make menu config, you will be able to locate this sort of line in there. This specific config option depends on driver model. And you would be able to list out this sort of help in the, again, for example, make menu config. So this is all similar to the Linux kernel. You can find it all there. Now, there is this sort of small weird quirk in that if you add a Kconfig option, it will not have the config underscore prefix in the Kconfig file. But in the make files, it is all used with the config underscore prefix. And even if you look into your Kconfig dot config file, it will be with the config underscore prefix. So this is a little bit inconsistent, but that's how the Kconfig system is. This is something you should probably be careful about. Now, if you decide to add a new config option into Uboot, it's just a matter of adding it into the Kconfig or adding the matching Kconfig entry into the right file. If you have any sources attached to that, modify to make file, add the sources. It's that easy. Except in case it's some sort of hardware configuration option, then you should not add it into the Kconfig, but instead you should add it into device tree. Now, what is a device tree? Just a survey. How many of you know what a device tree is? Double check that. Okay, that's amazing. Yay. Super. But anyway, just for the record, device tree is data structure, which is used to describe hardware to the software. And it's especially useful if your hardware cannot be detected. Then you can describe the entire hardware structure in the device tree and pass this information to the software, and the software will know the topology of your hardware. Now, this device tree has, it's a standard and it's been around since a very long time. It is actually governed by the ePaper. And if you want to find more information about it, take a look at device3.org website. This is what the latest information are. Formally, the device tree is a tree-like structure, which has nodes in it. And each node can have multiple nodes underneath that. Or it can have key value pairs, device tree properties. Now instead of describing it elaborately like so, I can just give you an example which I pulled out from UBoot. And in fact, since device tree is a standard, both UBoot and Linux share the same device tree sources, which is the way it's supposed to be. So if you look at the device tree sources, you will see stuff like this. In this particular example, there is this include directive which is a special detail of UBoot Linux and so on. Because these projects run the device tree sources, before they do anything with them, through the C preprocessor, just to expand macros like what do we have here? Like the IRQ type level high and so on. So you don't have to encode raw numbers into the device tree source, but you can just use these symbolic names. So that's what happens first. And after that, what do we have here? So here is the root node. Here are a couple of device tree properties which say, okay, this is some sort of board. It's compatible with something. Then we have a CPU node here describing the CPU, which again is compatible with something. There are a couple of properties attached to that CPU node. And we have a PMU and so on and so forth. There is a small detail in here, which is called the p-handle and allows you to reference different nodes within the device tree. So it's referencing the CPU code there. And this makes it more of a graph than actually a tree. So device tree is kind of not really a tree, but it's tree like structure still. All right. And this can be used to describe the entire hardware topology to the software. Now, you would use this device tree in two ways. So first of all, if your platform is device tree capable, and your Linux kernel is device tree capable, you would be able to pass device tree to the Linux kernel. And while doing so, it actually does some modifications to the device tree like it looks up the aliases node and patches, for example, a Mac address into the device tree from the environment, which allows you to share the same Mac address between the Linux. So this is what happens. But this is kind of boring, right? The other way you would can use device tree is it can set up device tree access very early on in the you boot boot process, and then you would extract the hardware topology from the device tree. And this is governed by the config of control, you would configuration option. But for you to appreciate when the device tree access becomes available, like how early on it becomes available, we should take a look at you would early stages and how it actually boots up. So when you put on your platform and does its power sequencing and stuff, the CPU will start executing some code at the reset vector, which is likely usually you do. Now, the code which it will start executing lives somewhere in the art directory. And it's a piece of code which is architecture specific. And it's not generic, it's just specific to your CPU. Now it will execute that code, which will be just a small piece of it. And then it will jump into CRT0.S, which is setting up the C runtime. This is still assembly code, but this is a common assembly code for your architecture. Now once the C runtime is set up, the first C code can be executed. And this first C code which is executed lives in board underscore f.c. Now the board underscore f.c means that this is a code which is supposed to be executed from Flash. And this is a bit of a legacy naming, honestly. So nowadays, this is not the case, but there used to be times where this piece of code was running with a limited environment, usually with stack running in locked cache lines, sort of limited environment. Nowadays, it's running from RAM, but, well, you should expect that sort of limitation in that environment. So this code, the board underscore f code contains a list of functions which are executed in this limited environment. These functions are usually responsible for bringing up the RAM, starting it up. And ultimately, when these functions finish, Uber is allocated at the end of the RAM. And the code starts content in board underscore r.u, which is like the second half of the UBOOT boot process which executes functions which are running from RAM. And when the board underscore r.c completes, you will get the UBOOT autobooth prompt. And then you can interrupt it and enter the UBOOT shell, for example. Now, all the way at the beginning, like one of the first functions in board underscore f.c sets up the access to the flattened device 3 blob. So even at that point, so early on, the first function in the c code allows you to set up the access to the device 3. And from that on, you can extract information from the device 3 blob. Yeah, one minor detail. If you're ever debugging the UBOOT init process, take a look at the lib init call.c, which contains like two debug statements. And if you replace them with printf, UBOOT will give you a list of every single init call it makes. So if you're looking for something like UBOOT hangs somewhere in the middle of these init sequences, just replace them, the debug statements in the lib init call.c with printf. And you will know which was the last init statement in those lists, which still completed and where it actually, therefore, got stuck. It's really convenient, this sort of debug 8 there. So OK, now that, yeah, go ahead. Yeah, so the question is, if the device 3 is compiled into UBOOT, because UBOOT is not able to read it from the file system so early on, actually there are two ways to do it. You can embed the device 3, the flatten device 3 blob into the UBOOT, and it's part of a special section of the UBOOT binary. This is a config.of embed. Or you can have config.of separate, which allows you to attach flatten device 3 blob at the end of the UBOOT binary. It's not compiled into the UBOOT binary, it's really just concatenated to the end. And the target of this other option is that you are able to build single UBOOT binary but attach different device 3 blobs to it. Yeah, but it's definitely not reading it from the file system. It is one way or the other attached somehow to the UBOOT binary, yes. Thanks. Yeah, so let's continue. UBOOT device 3 access, right? So now that you have device 3 access set up in the UBOOT boot process, if you go up through the UBOOT source 3s, you will see that there are multiple ways to access the device 3. This can be confusing, but there are basically three ways to do it, using the FDT underscore functions, which are basically the raw lib FDT calls. And for the most part, not to use that because you just need to code around a lot of boilerplate code if you use these functions. It could be that you need to do something special with the device 3, and in that case, you may need to use those functions, but otherwise, you probably shouldn't use them. There are convenience wrappers around this, which are the FDT tag functions. So these just hide the boilerplate code and allow you to conveniently extract information from the control device 3. So if you are outside of the UBOOT driver model, these functions might be what you want to use to extract information from the device 3. Now, if you are within the UBOOT driver model, you should use the def underscore functions to extract information from the DT. So you see the def read functions allow you to extract information from the device 3 node associated with a specific driver. And if you are within that context, you should use these functions. So speaking of driver model, I didn't figure out a good way to describe what the UBOOT driver model is, but I can basically tell you where this stemmed from. So it was like six years ago when the hardware started to become more and more complex, and UBOOT at that point was still configured with a boatload of if-defs. And people started requiring stuff like, well, we have two different I2C buses using two different drivers, and UBOOT just doesn't have any way to support that. So we will just code this extra driver which supports our platform. And this sort of stuff started appearing in the UBOOT code base, and it just didn't scale. And it was becoming a massive mess. So at my university, we started the UBOOT driver model as a sort of semestral project. And ultimately, we got something going. And after that, Simon Glass from Google took over and started pushing more and more of that stuff into upstream. Nowadays, we have a really decent, but driver model without too much overhead, which is fitting very well into the bootloader. Now, the UBOOT driver model consists of three major object types in there. The first one is the UBOOT classes. Now, the UBOOT class is sort of an interface or sort of an object which provides the UBOOT as a consumer, some sort of API for a specific hardware class. Think of it like a serial UBOOT class is providing UBOOT with generic functions to put character, get character, test if there is character in FIFO, reconfigure about rate generator, that sort of thing. The UBOOT class is also tracking driver instances. So every single driver instance of that particular type that is like serial port driver instance would be registering with the serial port UBOOT class. So the other object which exists within the UBOOT driver model are drivers, which implement a specific register poking and a specific interface for UBOOT class. This is defined for each type of UBOOT drivers. And then there are devices or instances of drivers. So when you instantiate the driver, some sort of metadata gets allocated and it registers with the UBOOT class. That way, you can have both two different types of drivers instantiated at the same time in the running UBOOT as well as two drivers of the same type instantiated at the same time while UBOOT is running. And it's not a problem. If you need to select which of these drivers should be used, this is configured on the UBOOT class level. Now the UBOOT driver model core itself is designed to be massively lazy. This is because if you are booting, you want the boot loader to just initialize as little as possible of the hardware and then load the next stage, just start the next stage, and get going. You do not want to initialize stuff which you do not need, so that's why the driver model core is designed to be really lazy. Now, when the UBOOT is coming up and the driver model is initializing itself, the only thing which happens is that the driver model core instantiates a root driver as about it. If device tree control is enabled, then it scans the device tree and binds all the drivers under the root driver in the way that is described in the device tree. But it does not initialize anything at all. To give you an idea how the UBOOT root driver and all this other stuff is bound, we have this convenient DM3 command which will list all the drivers which are bound. That means attached into the UBOOT driver tree as well as those that are probed. Now, the driver is probed. That means the hardware is initialized only when someone is first time requesting its services. For example, if I'm loading anything from SDMMC, the first time I do any sort of block access, the SDMMC controller will get probed and initialized. And it could happen that this SDMMC controller will require, for example, clock. And then at that point, also the clock driver will be initialized for the first time. Now, every other time when I then reuse that hardware, it is not re-initialized again. It's just initialized the first time I access it. But right from the get go, it is available to UBOOT. So it is bound and UBOOT sees it as a device. It's just the first time it will be a little slower because the hardware needs to be initialized. In this case, what you see in this picture is that serial port is actually probed because this outpad is coming out on the serial port. So it has to be probed and it has to be started. What else do we see here? Pin configuration is also set up because serial port needs pin configuration. It also needs clocks so the clock are also in probed. Now, this slide just lists what actually is available to the UBOOT driver, what sort of modes of operation. So yeah, UBOOT driver can be bound. This is what happens by default. It is then probed only when required. One small detail is that when you are implementing a UBOOT driver, you usually do not have to implement the bind and unbind functions. There are generic implementations of those. It could only happen that if you have some sort of specific weird requirement that you will have to re-implement the bind functions. Otherwise, you can just leave them blank and let UBOOT just bind it using the generic code. You will have to implement probe function because this is what initializes the hardware. So there will be some register poking and so on. You should also implement the remove function. Now, remove function is responsible for shutting the hardware down before you leave UBOOT. And it could happen that if you do not implement it, your hardware will do something funny when Linux is trying to re-initialize that hardware. So you should implement that. Usually, this is useful when you have USB controllers. They have some sort of built-in TMA engine. If you do not shut it down, it could happen that when Linux restarts that controller, it will just trigger some sort of bus master access into DRAM and just corrupt DRAM. This could happen. So that's pretty much it for the UBOOT device tree control driver model and so on, the basics. And now, let's take a look at how do we do UBOOT board port. So if you're doing any sort of new UBOOT board port, please start small. I mean, if you put everything into the UBOOT port right away, you will not be able to debug it if something happens, because you will have so much complexity and so much stuff in there that it will be impossible. So start small just with, I don't know, serial port outpad so that you can see that the board is doing something at all. Now, even the serial port outpad is nowadays complex enough, because on the modern SOCs, you need to set up like pinmuxing, clock control, just to get any outpad on the serial port. But the thing is, you can kind of split this into multiple tasks. That is, you can pre-configure the clock and the pinmuxing within your board file and just focus on writing the serial port driver. Now, once you have the serial port driver implemented, you can slowly move on and implement the clock driver then remove the ad hoc clock configuration from your board file. And once you have that working, you can again write a pin control driver and remove the ad hoc pin configuration from your board file. So this is what you can do. You can get to split this task into multiple. So if you're adding any sort of new stuff into UBOOT, there are pretty much like three things you can add. The complete architecture support, if you do that, that sort of stuff goes into arc slash foo. For example, arc slash mapes would be a new mapes architecture support. If you're adding new architecture, it would be really a good idea if you also added at least a single board so that UBOOT continues integration can be done on that architecture so it doesn't start bit rotting. Now, if you're adding new board support, that will go into board slash manufacturer name slash board name. And what you need to fill in there is kconfig file, make file, and your board file.c. Although, if you're adding a new board, the board file.c should not contain anything pretty much. It should be just a placeholder file for the build system. Ideally, it should be empty. All of the stuff you should be adding should just be in drivers and in device tree. You will obviously need to also add a configuration file which will go into config slash your board devconfig and probably a couple of legacy configuration options which will go into includeconfigzyourboard.h. You also need to link the board's kconfig into your architecture kconfig file so that it will be a single line pointing to this kconfig file in arc slash something. The other thing you can add are obviously drivers which goes into drivers. OK, so let's take a look at how it looks when you add a driver. So if you want to implement a UBOOT driver model driver, you use the UBOOT driver macro to define such a driver, and you give it a single parameter which is the name of the driver. Now what this does actually behind the scenes is it generates a structure. It prefixes the, in this case, serial SH with a couple of extra information, but ultimately it generates a structure which is put into a special UBOOT section alongside the other drivers. So each of these driver structures must have a name of the driver, which is different than this one or can be different than the name of the structure, all right. You should come up with a sensible name, though, obviously. It has to have a UBOOT U-class which specifies what type of driver is this. In case of a serial board, driver is U-class serial. It must have also driver ops associated with it. This has to match the U-class. So these are the operations which the driver is providing to the U-BOOT. In case of a U-BOOT serial driver, it's like get and set character, and so on, this sort of stuff. I'll show you in a bit. In case the driver is device 3 probing capable, you will have OF match table for the device 3 compatible strings. Then obviously you need to have a probe function. Unlike the Linux kernel driver model, all the private data of the driver are pre-allocated when the driver is instantiated. So you should not use malloc in the probe function. There is no need for that. You can just have the pre-allocation of the private data. Now what else do we have here? Oh yeah, right. In case you want to support both device 3 probing and all the platform data probing, the U-BOOT driver model is capable of that. Actually, you can provide a function which converts device 3 data into platform data. Again, the platform data are pre-allocated here. And this is convenient because then you can take the device 3 data, pre-convert them into some sort of structure, and then pass that to the private function and the probe function. So neither of these functions has to mess with device 3 at all. Finally, there is this Flags N3, which is just saying that this is serial port driver. It should be available early on in the boot process. Yeah, this is the device 3 probing and how that works. So you have a table of compatible entries. That's the OF match here, similar to the Linux kernel conversion of the device 3 data. The platform data is implemented using this function, which in this example is pulling out the register offset or the base address of this platform from the device 3. And if it is valid, it is just setting it into the platform data. So this value will be available to the probe function and the bind function that can use that value immediately without having to pull it from the device 3 again. And yeah, the platform data are pre-allocated. So I'm just specifying in the U-boot to driver structure how much of a data I want to pre-allocate for the platform data. Now, finally, the implementation of ops looks like this. For the serial port, it's dmserial ops, which contain a getcbootc implementations, which just get and send character over the serial port. We have implementation of pending function, which is there to check whether there is a character pending in the UART 5.0. So if the UART received any character, it's like testc. And we have setbrg, which is setBoutRateGenerator. And actually, all these functions have to be implemented. Here is the implementation of the getc. Well, there should be a register poking in here, except it's calling some sort of generic function. So it's not explicitly there, but there would be some sort of memory rights and memory reads in this function implementation. Also, there is one more detail. In case you need the early serial console, before the driver model is running, UBoot supports that through config debug UART. But it's quite specific. And in case you're interested in that, just look up the config debug UART in UBoot. It's mostly if you have a dedicated debug UART, you can implement that. Most of the platforms do not implement it. This is how it looks like. If you enable this, for example, on the Atheros, it implements a couple of functions which explicitly init the complete serial hardware and then allow you to print on the serial port of the debug UART directly by just poking the registers. So it's a sort of a hack-ish. Anyway, the other thing I wanted to talk about is clock framework, which from the driver implementation point of view looks quite similar to the serial port. I mean, it just contains different UBoot U-class ID and different ops, which contain something like enable clock, disable clock, get rate, and set rate. So that's kind of boring. But what I would like to show you is how it looks from the consumer side. So a consumer of the clock framework is using CLK underscore functions. In this case, I am, again, pulling this out from the serial port driver. So what this does is, first of all, it looks up the clock based on the device tree entry in the serial node. So if the serial node contains the FCK clock entry, it will populate this through the clock. Now, if this works out, we will have a valid reference to the clock driver's clock handle. And we can do with that stuff like enable those clock and get their rate. This is what we can do with using the clock framework. Obviously, this platform would have to have implemented an instantiated clock driver. The last thing I want to talk about is pin control framework, which, again, from the driver implementation point of view is similar to the serial port driver, except the pin control framework is kind of two frameworks in one. So that might be a little confusing. The pin control framework implements two functionalities. One of them is pinmux, which allows you to select which of the blocks within the CPU gets mixed to a specific pin on the CPU package. And pincon, which allows you to select the properties of the specific CPU pin, like voltage, pull-up, pull-down, these sort of properties. Now, all of that from the consumer side is selected in one file swoop just by calling pin control select state. And the way this whole thing looks like is that in device tree, you have multiple entries, one for each possible pin configuration of a pin control group. And then the driver, which is the consumer of that pin control group, just selects one of these pin states. That is, if you call the pin control select state, it reconfigures an entire group of pins. It selects the multiplexing. It selects the voltage levels, the pull-ups, pull-downs, and so on. So you do not do it on a better pin basis. OK, that's pretty much it for the pin control. The other frameworks, they are also quite similar. They are pretty much converted, except for writing empty, which is missing a couple of things. But in general, most of Uboot is converted now to a driver model. If there are some legacy things which are not getting converted, they will most likely soon be removed. So in case you still have something which you care about in Uboot, which is not converted to device tree control and driver model, now is the time to do it and submit patches. But enabling driver model and device tree control will make Uboot grow up a bit, possibly considerably. So we should talk about reducing the size of Uboot. There is one thing which is kind of orthogonal to driver model and device tree control, which is Uboot SPL, because this has been in for quite a while. And the Uboot SPL is basically a small build of Uboot, which is responsible to fit, usually, into an on-chip RAM, initialize the DRAM, then load the Uboot, and execute the Uboot in RAM. And this is especially important for systems which have some sort of size limitations. Now the Uboot SPL can be configured in such a way that it doesn't have device tree control. It doesn't have driver model. These are optional in the Uboot SPL. And the Uboot SPL properties can be configured separately from the main Uboot. It has config underscore SPL options. So if you have board def config, you can select how the SPL will look like and how the Uboot will look like. It could also happen that you will run into something called Uboot TPL, which is like ternary program loader. I hope you will not run into it, but there are specific use cases where you need to strip down Uboot even further to like units of kilobytes, and this is what the TPL is for. But this is usually super board specific, and you really need to know what you are doing if you are getting yourself into the Uboot TPL stuff. Now, I said that in the SPL you can strip out the driver model and device tree control. This is possible, but there are also other ways to reduce the Uboot size. So on the SPL, if you use device tree control, all the nodes from the control device tree will be stripped if they don't contain Uboot DMP re-log entry. And this is to reduce the device tree blob size. It can be quite considerably big, the device tree blob. So what we do is we stripped out everything which is not needed for the Uboot SPL to actually initialize itself and load the next stage this way. Another thing is you can completely remove the device tree control and then probe all the drivers from your board file using platform data. So that's another option because the libftt itself is quite large in size. And yeah, this can help you reduce the Uboot size a little bit further. And this is pretty much it for this entire talk. I should probably wrap it up, but what I would suggest to you is if you're doing new Uboot talk, a new Uboot port, please use device tree control and driver model. Absolutely, reuse as much code as possible. So if there are drivers which you can reuse, just do that, extend them, submit patches. If you have any questions, take a look into the Uboot documentation in the doc directory. If you have questions which are not answered there or if you want to just some sort of real-time interaction with the Uboot developers, you're welcome to visit the Uboot IRC on FreeNote. You're also welcome to send any questions or patches to the mailing list. That would be very nice. And with that, I would like to thank you for your attention and do you have any questions? Yeah, go ahead. So the question is if the SPL is used, typically if you need to initialize DRAM in the Uboot image, yeah, that's it pretty much. So the systems on which Uboot SPL is used are those where the boot RAM does not initialize the DRAM. You need to load some sort of small piece of code into the on chip RAM, which does the initialization for you and then loads the next stage. And the thing I missed when I spoke about the Uboot SPL is that the Uboot SPL does not necessarily have to load Uboot proper. It can load Linux directly as well. That's called the Falcon boot mode. Thanks. Yeah. So the question is, with all the Uboot version, you have to initialize memory in the board file and if this can be placed into the device tree. This is actually platform-specific and there are platforms which do initialize memory controller based on information in the device tree already. So, yes. Yeah, go ahead. So the question is how difficult it is to take a Linux driver and port it over to Uboot. That depends on which subsystem you're reporting it to. The USB subsystem is trying to be held in sync with Linux kernel, so in that subsystem, it's not that difficult. You obviously need to do some sort of adjustments, but in general, it should not be that hard to do the porting if you have the code once. Yeah, if you're starting some sort of board port, then yeah, getting the serial port running, this is the most difficult one because you have no indication from the board that it's doing anything. That depends on the subsystem. Like I said, the USB is good in that it's mostly in sync with Linux. I believe we are now getting SPI nor subsystem imported from Linux, which will be very good. Then you will be able to just pull in all the SPI drivers from Linux easily. Thanks. Any other questions? Yeah, go ahead. So the question is, if you have secure boot enabled in U-boot, when does the signature checking happen? Well, I cannot really answer that because I don't know which platform you are using. So this is kind of specific to the platform. Usually what happens is that the bootrom on that platform is doing some sort of signature check of the next stage, which would be probably the U-boot SPL. And then the U-boot SPL is able to continue that sort of chain of trust and verify that U-boot has not been tampered with and move on from there to Linux and so on. So that's the generic way to do it. We can talk about that later if you want. Thanks. Any other questions? Yeah, go ahead. So the question is that the device tree files are both in the Linux kernel source tree and in the U-boot source tree and possibly in other places. And if there is any effort to actually unify them, so what happens on the U-boot size is we are synchronizing the device tree files from the Linux kernel. And yeah, ideally they should be pulled out from all these places and we should have like one single repository with device trees, but I believe that's a question for the device tree masters really when this is gonna happen. Well, device tree is supposed to be a stable ABI. Yes. Yeah, so the question is that there are a couple of places where the U-boot bindings did diverge from the Linux bindings, which I believe is true. I wouldn't be surprised by that. What's that? Regulator. Ouch, okay, yeah. So ideally, batches are welcome. Mm-hmm. Um. Um. U-boot is still a community project and unless someone does the cleanup and the work, then we are not getting anywhere with that. But yeah, if there is a problem in the regulator framework, it definitely should be fixed up to match the Linux bindings. It has to be done. I'll try to bring it up with the regulator maintainer. Thanks for letting me know about that. Yeah. So the question is if there is a possibility of having like multiple device tree attached to U-boot and U-boot can select that based on some sort of pin configuration, I believe there is something like that as a really new feature. So it happened like very recently that U-boot can have multiple device tree attached to it. I would grab in the device, in the U-boot source three, four multi underscore fit or multi underscore DTB or multi image, something like that. And it landed really very recently in U-boot. Yes. There is such a possibility. The other option which you could do if your boards are kind of similar to one another is apply device tree overlays on the U-boot live device tree. This is also possible nowadays with bit of effort. So then you can kind of factor out only those changes which you have specific to your different devices. Thanks. Any more questions? No more questions. So thank you very much.