
Dear Graeme Russ,
Hi Marek,
On Thu, May 3, 2012 at 1:18 PM, Marek Vasut marex@denx.de wrote:
Dear Graeme Russ,
Hi Marek,
Thanks for taking a look
+The INIT_FUNC macro allows initialisation functions (i.e. functions which are +executed before the main loop) to be easily added to the init sequence +
+Specifying an Initialisation Function and is Dependencies +--------------------------------------------------------- +The format of the INIT_FUNC macro is:
+INIT_FUNC(fn, grp, man_reqs, pre_reqs, pst_reqs)
+fn is the name of the init function to call. This function must have the +following prototype:
+int foo(void);
What if I want to pass some data to such a func ? Clearly, I can think of this being doable, but extra hard.
The idea is that no changes are being made to the existing init methodology.
Well ... what if I want to pass eg. pdata?
Do what we do now - Global Data is the only way to pass data between initialisation functions. Sure, this _might_ change in the future (but I doubt it) but we can deal with that later
Global data won't die, but they should shrink as much as possible ... so I don't like the idea of using global data that way.
+Each init function must return 0 to indicate success - any other return value +indicates failure and the init sequence will stop
+grp is the name of the group that the init function belongs to. grp may be +the same as fn for any individual init function, but between init functions, +fn and grp must be unique.
+The purpose of groups is to allow functions to be grouped together so other +functions can specify the group as a whole as a dependency rather than having +to list every function in the group in the dependency list + +man_reqs is a space seperated list of functions or groups that MUST exist and +MUST run BEFORE fn
+pre_reqs is a space seperated list of functions or groups that MAY exist and +(if they do) MUST run BEFORE fn
+pst_reqs is a space seperated list of functions or groups that MAY exist and +(if they do) MUST run AFTER fn
What's the point? Can't you create a kind of proxy object that the pst_reqs will have as a pre_req ?
Maybe you should create this:
INIT_FUNC(fn, grp, prereqs, postreqs) and for each function from prereqs and postreqs, specify per-function attributes via the GCC __attribute__(()) directive, like if the function must run before something or may run before something etc?
Eep, I would like to see that implemented - I'm sure it would be 'interesting'
Pervy and sadistic at least :-)
The way INIT_FUNC and friends work is to simply build a list of static strings (which we just happen to shove in a seperate section so they can be filtered in/out based on the link stage)
Yes, I got the understanding of it. I was more bothered by the man_reqs and pre_reqs, what if we soon need functions that are executed under another weird condition? And if you look at it the other way -- if you need function that's executed conditionally, why not wrap the condition into the pre_req (or some proxy object, whatever).
It's not about being executed conditionally - It's about 'I need to be initialised after 'X', but if 'X' does not exist, I can still initialise'
Network is one example - If you have a PCI network adapter, it must be intialised after PCI. But if the adapter is not PCI, you still want to be able to initialise it
So why don't we make function, that can depend on "OR-group" and/or "AND-group" then? Won't that be more logical? I mean, group where you must execute as much as possible, to be OR-group and group where you must execute everything to be AND-group.
Timer is another - Hopefully the timer infrastructure will soon be an arch independent API. But each arch (and potentially down to the board level) may have a role to play in getting the timer infrastucture running (FPGA, PLL, PIT intialiastion). So potentially, the common driver code would be intialised with:
Well, I hope to create unified timer api with the DM biz.
INIT_FUNC(timer_api, timer, arch_timer, board_timer, );
So the arch_timer function MUST exist and it MUST run before timer_api. The board specific timer initialisation is optional. If the board has timer related code, it MUST run before timer_api but timer_api can still run if board_timer does not exist (i.e. all the timer init done at the arch level)
Ok, I see. OR-groups won't fit in here?
Now depending on the arch and board, board_timer may be either:
INIT_FUNC(board_timer, timer, arch_timer, , timer_api);
i.e. arch -> board -> api
or
INIT_FUNC(board_timer, timer, , , arch_timer timer_api);
i.e. board -> arch -> api
NOTE: In both of the above example, including timer_api as a post-req is redundant. The timer_api INIT_FUNC definition already mandated the order. This is not a problem and including or excluding it makes no difference to the output. Of course, including timer_api in the above examples means we could have done:
INIT_FUNC(timer_api, timer, arch_timer, , );
But adding the redundant references makes it clearer when you are looking at that bit of code. The 'black-box' tool will warn you if you created a circular reference and will print out what the init sequence is that created the circular reference.
Neat.
Now, here is where the fun begins :) - Lets say you need timer support early, but you can't get the low level infrastructure for hi-res timers running until later...
board/vendor_me/common/timer.c INIT_FUNC(me_timer_init, me_late_timer, me_pit_init, , ); INIT_FUNC(me_pit_init, me_late_timer, , fpga_init pll_init, me_timer_init);
(again, me_late_timer in the second INIT_FUNC is redundant)
board/vendor_me/board_a/timer.c INIT_FUNC(fpga_init, me_late_timer, , , me_pit_init);
board/vendor_me/board_b/timer.c INIT_FUNC(pll_init, me_late_timer, , , me_pit_init);
(again, me_pit_init in these INIT_FUNCs is redundant)
board/vendor_me/board_c/timer.c /*
- This board has the PIT hard-wired to a crystal oscillator so there
- is now board_level init function - So we can actually initialise
- the PIT earlier :)
*/ int me_pit_early_init(void) { return me_pit_init(); } INIT_FUNC(me_pit_early_init, timer, arch_timer, , );
/*
- Don't need any late timer init - This will skip both me_timer_init and
- me_pit_init as they have been defined in the me_late_timer group
*/ INIT_SKIP(me_late_timer);
The idea is that me_late_timer() will perform some tweak to the timer API to redirect it to the hi-res clock source. The hi-res clock source is driven by a PIT common to all the boards manufacture by this vendor. But the PIT has a different raw clock source depending on a particular board Board A has an FPGA, board B has a PLL and board C uses a hard-wired crystal oscillator
I see.
+Skipping or Replacing a Function or Group +----------------------------------------- +Occassionally, a board may provide a completely seperate implementation for +an initialisation function that is provided in the common arch, SoC or +common code.
+SKIP_INIT(fn_or_group)
+After the initialisation function dependencies are calculated, all functions +and groups listed in any SKIP_INITs are removed - This may result in +dependent functions being removed - It is up to the board code developer +to ensure suitable replacements are in place
+REPLACE_INIT(old_fn_or_group, new_fn_or_group)
+Like SKIP_INIT but replaces on function with another (or one group with +another)
+Example: In the SoC code yoy may have
Yoy :)
Yes. The ultimate goal is to remove a heap of '#ifdef mess' and delegate the selection of initialiasation functions to where is is most appropriate. CPU init in ARCH code with board specific init in board code with the ARCH code 100% unaware of what the board is doing. This will also make it a lot easier for vendors to implement multi-arch solutions :)
Correct, alongside kbuild and driver model, we'll make an awesome bootloader. But fix that s/yoy/you/ please ;-)
Ah, I see now... Will do
+INIT_FUNC(init_cpu_f, RESET, , , );
+In the board code, you may want a slightly tweaked version, so you might +have:
+int my_new_init_cpu_f(void) +{
...
+} +REPLACE_INIT(init_cpu_f, my_new_init_cpu_f);
[snip]
diff --git a/tools/mkinitseq.c b/tools/mkinitseq.c new file mode 100644 index 0000000..b150de4 --- /dev/null +++ b/tools/mkinitseq.c @@ -0,0 +1,1512 @@
Ok, this is the worst part. I think this could be reimplemented in shell ;-)
Be my guest :) - Have a really good look at what is happening behind the scenes - Lots of list processing and checking. In particular, the processing of the REPLACE and SKIP macros would be particularly nasty.
That's what sed can do for you, can't it ?
So below - If you can show me (i.e. write the whole thing in shell) I might include it
I'll think about it ;-)
I have no problem with this being done in shell code. BUT if I have to do so in order for it to be accepted, then I'll bin this patch series right now. I'm more than happy to integrate somebody elses shell-based tool into the series. Sorry to be so blunt, but I do not have the time or energy to re-implement this myself.
Ooh, I smell flame-fuel :-) I'd certainly prefer to see this in shell, since I believe the substitutions and reordering of text might be easier there.
See above - be my guest ;)
My argument is that this is a black-box tool like gcc/ld. Once we prove it to do what it does correctly, we can 'leave it alone'(tm) and test cases are fairly trivial. You can even mock-up the input file in vi :)
Ewwwwww ... like gcc/ld ... or maybe like Windows (TM)(C)(R)(?) :-D
No, like FLOSS gcc/ld :P
:-)
And is a shell based implementation going to be any easier to understand, modify, debug, etc?
I wonder :-)
Well, if someone manages to do it in shell, we will see - Take a look at main() - I have been very carefull to layout the way the tool operates in a very linear way but ther lists are global, so sharing data between the shell stages will be a pain (pipes?)
Bash can do locals ;-)
+/*
- (C) Copyright 2012
- Graeme Russ graeme.russ@gmail.com
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of
- the License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- MA 02111-1307 USA
- */
+/**
- container_of - cast a member of a structure out to the containing
structure + * @ptr: the pointer to the member.
- @type: the type of the container struct this is embedded in.
- @member: the name of the member within the struct.
- */
+#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
Don't we already have this defined in uboot ?
mkinitseq is host-code so it only brings in host headers (not U-Boot headers) - If you don't have Linux kernel headers installed, you can't get this macro. Actually, even though I do have them, I had a hard time getting this macro in, so I gave up - Any hints?
Reimplement this thing in shell :-)
You already know my answer to this ;)
As an aside, I wonder if the kernel headers are required for the list macros?
Nope.
Good :)
Regards,
Graeme