
Hi Simon,
On Wed, Aug 17, 2011 at 1:25 PM, Simon Glass sjg@chromium.org wrote:
Hi,
When U-Boot starts up it initializes all the peripherals which are enabled This might include USB, I2C, SD/MMC, LCD, Ethernet, UARTs, etc.
To save time, reduce the amount of code executed and thereby improve security, we are wanting to initialize only some peripherals at the start. For example we might start up I2C and SC/MMC.
Later when it becomes clear that USB and Ethernet are needed we want to init those also. I suppose the easiest way of describing this is that we want to have some 'level 0' init, then perhaps move to level 1 and init peripherals at that level, then perhaps to level 2, etc. Quite often we will only get to level 1 before jumping to the kernel.
Well technically, the device init should only be done either
a) U-Boot core code needs to access the device (to read the environment for example) b) When a U-Boot command first uses the device b) When doing final init ready for the OS kernel
At the moment the U-Boot init code is spread around a bit - there are files called board.c within arch/xx/cpu/, board/xx/ and arch/xx/lib/. So it is hard to make this change just within a board file. Some other files have big lists of init calls like stdio.c and serial.c.
Yes, it is a mess
I am thinking of creating a way of specifying a list of peripherals which should be inited in each level, then moving all init code into a function which knows how to init each peripheral. Then we can call init_level(2) for example, to start up peripherals in that level. Perhaps level 0 would be reserved for pre-relocation init.
I am interested in any thoughts that people have about this and whether it might be implemented in a generally useful way. I am assuming that a driver registration / initcall approach would be a bridge too far for U-Boot at the moment, so I am thinking of a function with a big 'switch' statement.
I would prefer a device level 'start' and 'stop' hooks so when a command or the U-Boot core needs to use a particular device it calls device->start(), uses the device then calls device->stop(). Devices that are in use permanently (timers and console ports are prime examples), device->stop() is never called (or maybe called proir to entering the OS kernel). This would be a massive architectural change for U-Boot and leads into a 'driver framework'
The other related issue is that I notice that the arch/xxx/lib/board.c files are different but have a lot of common code. Is there some scope for starting a common/board.c file which slowly builds up common functionality so that over time we end up with only the architecture-specific code in the arch/lib/xxx/board.c? If so these these two goals could perhaps be progressed together.
Now that relocation has settled down and looks to be working in a much more platform independent manner, board_init_f() and board_init_r() should be able to be moved into /lib/ with any arch or board specific functionality carved out into init functions. Have a look at x86 and you'll see that board_init_f() and board_init_r() work in a very simlar manner to each other. I don't think this is going to be particularly difficult to implement. I am, however, still scratching my head trying to figure out how to create a simple mechanism for dynamic init sequence building (even if it is done at compile time). By this I mean a standard arch level init sequence and a way of allowing boards to insert init functions at arbitrary points in the init sequence without a mass of #ifdef's in /lib/board.c and without have each and every board having to define the whole init sequence in /board/init.c. At the moment it's done by reserving some specific points in the init sequence where U-Boot calls into the board-specific init functions. If the board has no need for such a call, it still needs to implement a do nothing stub function. I'm sure someone can come up with some clever pre-processor macro-foo to do something like this:
In board/foo.c:
int init_board_gpio() { /* Do Stuff */ } BOARD_INIT(init_board_leds, 214);
int init_board_leds() { /* Do Stuff */ } /* Init board LEDs after the boards GPIO has been initialised */ BOARD_INIT(init_board_leds, 215);
In arch/xxx/sdram.c
int init_sdram() { /* Do Stuff */ } /* SDRAM gets intialised really early (but not before timers) */ BOARD_INIT(init_sdram, 10);
In arch/xxx/<soc>/timer.c
int init_timers() { /* Do Stuff */ } /* Timers are needed early */ BOARD_INITinit_timers, 9);
You could even #define some of the steps with nice gaps between:
#define INIT_STEP_CPU 1 #define INIT_STEP_TIMERS INIT_STEP_CPU + 10 #define INIT_STEP_SDARM INIT_STEP_TIMERS + 10
But maybe it's all wishful thinking
Regards,
Graeme