[U-Boot] Some Driver Model Ponderings
Jon Loeliger
loeliger at gmail.com
Wed May 28 17:22:52 CEST 2014
Hi U-Booters,
Over the past few days, I had an off-line conversation with Simon Glass
on the topic of the Driver Model. At his request, I've edited that
conversation into this piece of email for the U-Boot List in the
hopes that others in the community might also benefit.
Already, some documentation patches have been offered as a result.
http://patchwork.ozlabs.org/patch/352165/
But try to follow along. This is a conversation that occured over
several back-and-forth pieces of email. I've edited and spliced.
Hopefully, all the important pieces and parts are present without
offense or malice.
I start with the plea:
Hi Simon,
I write to you directly in an effort to
A) Get a new GPIO driver into U-Boot using the new Driver Model
B) Hopefully answer many lingering questions rapidly
the background:
Backing up a half step now... I need a PL061 ARM GPIO driver.
I have exactly one GPIO I need to drive. But I have 7 GPIO blocks
of 8 bits each. Rather than just hacking in yet another GPIO
hackery into the general U-Boot fray, I thought I'd try the new
driver model approach.
So I think I want, and have written, a simple PL061 driver that
fits these standard operations:
static const struct dm_gpio_ops gpio_pl061_ops = {
.request = pl061_gpio_request,
.free = pl061_gpio_free,
.direction_input = pl061_gpio_direction_input,
.direction_output = pl061_gpio_direction_output,
.get_value = pl061_gpio_get_value,
.set_value = pl061_gpio_set_value,
.get_state = pl061_gpio_get_state,
};
Each of those routines takes a struct device that is mapped into an
instance of the struct pl061_priv like so:
struct pl061_priv *priv = dev_get_priv(dev);
My priv structure is this:
struct pl061_priv {
u32 regs; /* GPIO bit-masked I/O space, controller */
u32 pad_regs; /* Pad control */
u32 gpio_base; /* GPIO range implemented by this device. */
u32 gpio_count;
};
Now, I want to build up 7 of these with different bases for the
'regs', and different segments of the global linear GPIO space,
8-bits apiece.
I am resonably convinced that the UCLASS_GPIO is going to help me do
this. And is supposed to. It is some combination of the probe,
bind, setup that I am not quite understanding yet.
At this point, Simon states that another reference implementation
has been pushed:
I just pushed us-gpioc to
http://git.denx.de/u-boot-x86.git
which includes another conversion - drivers/gpio/s5p_gpio.c.
It might be a bit closer to what you need.
So, my UCLASS related questions haven't all been answered yet,
but I also had questions about the relationships between the
various bits of data along the way: uc_priv, platdata, priv:
I'm not quite understanding the relationship of my "pl061_priv"
data to the so-called platdata.
Simon clarifies that:
priv is your device's private data, used while the device is running
(i.e. it is set up when the device is probed, and deleted when the
device is removed)
platdata is like Linux platform data. It provides the information
needed to start up the device - like its register addresses or
configuration options.
I make two observations about those two paragraphs that then
go on to cause a new documentation patch to be submitted.
My first observation is that nowhere in the as-yet-written
documentation abou the Driver Model is there any statement
about the lifetime of the data pieces. Until that first paragraph.
So I ask for more complete data-lifetime documentation.
Secondly, I also observe that there is no data-flow documented.
What is the sequence of data as it transitions through the
various bind, probe, use, and release phases?
Simon offers to document some data-lifetime, but quotes some
existing platdata documentation too:
" platdata (data which tells the driver how
to operate on a particular platform)"
Which just sets me off:
This is *exactly* the wording I am arguing against. :-)
That uses the same word to define the word. We need
*other* words to describe what is meant by "platform data"
in this context. Something like what you wrote in the
mail to me:
Examples of platform data might include:
- the base address of the IP blocks register space
- configuration options
- board-level physical switch settings
- something else obvious
- when using DTS, data parsed out of the device node
And perhaps a description of why it is needed in the first place:
The device driver needs instance data for about a device's
running state but may not be able to determine it. The
platform data convey's the specific device setup data to
an instance of the private device driver data that it will
not otherwise be able to determine on its own.
Ultimately, all that crap ends up in a pretty good doc patch!
Anyway, from my original piece of mail, I ramble on:
And I'm not understanding how I get my uc_priv for GPIOs to do the
glue layer for what I need. And then when I think I get it, I see
that after the bind step, all the GPIOs get renumbered! Why? I
think I wanted to manually set those up directly myself. And,
furthermore, since I really only have *one* GPIO I need to
manipulate, I don't really need to instantiate all 7 instance, but
just the one instance that holds the one GPIO pin I need. (Which
happens to be in Bank 3.)
Simon replies:
The renumbering is done by the uclass. Provided that you set up you
devices correctly this should just work, and will give you the
numbering you wanted anyway.
I think this issue of renumbering lingers and needs to be addressed.
I'm not happy with the renumbering, so I push back:
I don't want a renumbering. I want to be able to declare *just* the
one GPIO I need. It happens to be in bank 3 to handle the global
GPIO space 24 through 31. I need GPIO 31. But if the renumbering
happens, it will need 0 -7, 8-15, 16-23 in the uclist prior to bank
3 for GPIOs 24-31. I want one uclass list item to hold the one GPIO
bank that I need for the range 24-31. But I *also* want the ability
to allow a future GPIO bank addition to seemlessly fit into the list
for bank 6 when I need to add a GPIO from that bank too. The uclass
should happily handle a sparse representation of the list of banks
and global GPIO ranges. The thing stopping that is willie-nillie
renumbering that isn't even necessary in the first place.
If something needs to renumber, or for that matter, renumber
according to a *different* scheme, why not make an optional uclass
call-out function for "post-processing" and identify where its
lifetime and sequencing it takes place.
Simon offers:
Well that's the way it works at present. I'm not sure why you
wouldn't just support all the GPIOs from the start - that way you
can upstream the code!
Also note that you can give GPIOs bank names A, B, C, D, etc. which
might help you.
Which again, just torques my nipples. So I rant:
Wow. Seriously? I don't even know where to begin with
that response. This isn't a matter of upstreaming. I get that.
This isn't about upstreaming. And upstreaming should never be
the justification for leaving bad code design in place. In fact,
this is exactly the opposite reasoning at work here. That is,
I'm trying to get this driver upstreamed (said that from the
onset of this discussion), but the renumbering behind my back
will get in the way.
This is a problem with the code performing totally unexpected
behavior in a non-obvious, behind your back and broken way.
Let's try an analogy with the Linux Kernel IRQ scheme.
You might think of this UCLASS GPIO scheme as being quite similar
to the linear mapping space of the irqchips. I set up a series
of small IRQ IP blocks that handle ranges of 32 IRQs, which in
turn are mapped onto a larger, global space of IRQs maintained
by the Linux Kernel. If I set up a series of IRQ blocks, mapping
each one to IRQs:
Bank 0 is IRQ 64-95
Bank 1 is IRQ 0-31
Bank 2 is IRQ 32-63
and as I finished the last registration of the irqchip the
kernel came along and remapped them behind my back as this:
Bank 0 is IRQ 0-31
Bank 1 is IRQ 32-63
Bank 2 is IRQ 64-95
there would be hell to pay!
So what the kernel did was provide a non-static mapping for
the IRQs and irqchips. They allocatge the IRQs on the fly
and allow each irqchip to make its own mapping. That way
they never have to worry about re-organizing the global IRQ
space, and never have to worry about the individual irqchip
space needing to be shuffled as well.
I assume you can see where I am headed here now. The problem
with our proposed DM for GPIOs is that the global space is a
fixed linear space. You have to honor the original registration
contract when the uc_privs were created and filled in.
If you want to *sort* the uc_priv blocks into a nicely orderd
list so that, say, lookup operations might be faster, that is
fine. Maybe even good.
But you can't renumber the regions and ranges behind my back.
Unless I am completely missing something here, doing so breaks
the association between the UC_GPIO class range mappings and
the corresponding base register of the IP block.
So blah. We still need to address the capricious renumbering
in the UC_GPIO space.
Simon explains:
Fair enough, but the GPIO uclass does not support dis-contiguous
ordering.
The numbering is owned by the board not the driver. For example,
another board with the same SoC as you are using might have an I2C
expander with 16 GPIOs. It might want these GPIOs to go first or
last. Neither driver can determine the ordering. Also the intent is
that you should use names in your U-Boot scripts.
I'm not sold here yet. Perhaps we are talking past each other here.
The GPIO class *could* support both discontinguous and sparse sets.
In fact it would if only they weren't renumbered capriciously.
My original confusion continues with so many "So, ..." issues:
So, I saw your patch for the tegra GPIO conversion. Well timed!
But it leaves me a bit baffled due to your apparent two-layers of
PORTs and BANKS, and plat data. Some of which I don't think I
should need. (?)
So, I read through your gpio_tegra_bind(), and I don't understand
what the "parent" device is. Where'd that come from? Who called
this with what arguemnt? All leading to what is really happening in
that device_bind() call?
Simon says(1):
The parent device is empty - it has no actual GPIOs, but is just a
parent for all the real GPIOs devices. Driver model supports child
devices.
I'm still baffled on the "artificial parent" issue:
Yes, I get that. And an I2C bus might have a I2C MUX device in
the mix as well, and etc. And a DMA controller might have N
sub-channel devices, etc.
I don't understand how you are creating and organizing what
should be (perhaps only in my case) just a list of 7 UC_GPIO
nodes. I think the UC_CLASS for GPIOs has 7 allocated uc_priv
instances in a list, one for each of my 7 banks of GPIOs.
There is no hierarchy needed anywhere here, right?
It looks to me like you have an extra UC_GPIO node, with no
actual {uc_priv->gpio_base, ->gpio_count} set on it, so that
it just occupies an extra instance on the UC_CLASS UC_GPIO list.
And, I think, the only reason for that node is to house a
platdata structure that has a parent base register.
(Which, in the case of the s5p GPIO code you pointed me toward,
baffles me a bit: I expected to see the base reg for each block
parsed out of the DTS node for that block. Instead it looks like
they are formed by doing the "bank++" to step through large
register block structures for each bank. Urg.)
Simon confirms my interpretation and the "bank++" approach, but
unfortunately doesn't explain either.
Yes that's right - it is just a parent device, with no actual GPIOs
in it.
and:
That's the way the hardware is implemented on Exynos (and Tegra BTW).
Hrm.
Meanwhile, another "So,..." issue from my original mail:
So, where did I get multiple uc_priv structures? One for each of my
needed 7 instances? Do I allocate them in a bind function? I
thought I wanted something like this, but I need one uc_priv for
each instance still:
int gpio_pl061_bind(struct device *dev)
{
struct gpio_dev_priv *uc_priv = dev->uclass_priv;
int bank;
for (bank = 0; bank < RAPID_N_GPIO_BANKS; ++bank) {
/* Is uc_priv allocated here? */
uc_priv->gpio_base = bank * PL061_GPIOS_PER_BANK;
uc_priv->gpio_count = PL061_GPIOS_PER_BANK;
}
return 0;
}
Simon points out several flaws with my approach:
dev->uclass_priv is allocated for you before probe() is called. bind
just means that driver model knows about the device - it doesn't
mean that it is ready for use yet. You mustn't touch uc_priv in the
bind function since it doesn't exist yet.
and notes that the two uc_priv-> assignments instead belong in the
probe() function. I think I understand and write:
OK, But then how does the probe code know what bank to operate on?
And the answer to that now appears to be "via the platdata." Right?
And who/where was platdata allocated and filled in? In the bind,
right?
So bind() first, get reg base and global GIO base. Fill those into
plat data. Then probe() where plat-data is copied into both a newly
allocated priv, and the uc_priv GPIO fields.
I am not sure when or how the bind() function is called:
Will this bind be called once, or somehow 7 times?
And cryptically, Simon replies:
Well if you call device_bind() you can create new devices,
which is what the tegra/exynos drivers do.
And I am now adding a bit of explanation about this comment that I think
I have deduced after reading the as-yet-to-be-written new documentation
patch that Simon will have submitted later.(2)
Here's the unwritten thing: I think your driver bind() function is
supposed to call the lower-layer device_bind().
Simon then adds:
bind() creates the struct device. It puts in the platdata pointer
if it has one (i.e. if there was a U_BOOT_DEVICE() declaration).
For device tree, it doesn't do that - see ofdata_to_platdata.
and offers a doc patch to clarify this some more.
OK, so, on to compiling issues....
First off, it appears that I will need to define my new
CONFIG_GPIO_PL061 to get my driver file. Good. Got that. And then
also CONFIG_DM_GPIO so that I get the Driver Model GPIO uclass
layer. Good. I want that generic mapping of global GPIO numbers
into my specific device... Check.
But then it wants me to also suddenly define
CONFIG_SANDBOX_GPIO_COUNT. Whichi I think is broken. I should
never have to define something for another configuration (sandbox).
If I have to somehow accommodate some new unit testing scenario,
that should be something *else* standalone.
And somehow I am missing a bunch of symbols still too: uclass_get,
uclass_first_device, dev_get_prand weirdly iv, all the sandbox GPIO
functions too.
Oh, right, I also, also need CONFIG_DM. Hrm. That causes list code
to be compiled and it looks totally sloppy and has compilation
errors all over it still. Still getting test/dm/gpio.o in the mix
and likely shouldn't.
And, about the time I sent that rambling series of issues,
Simon was furiously developing this series of patches:
Yes, this is a bug. You should apply the driver model patches from
my tegra series v3, for example:
http://patchwork.ozlabs.org/patch/347491/
And yes, applying the patch
"dm: Allow driver model tests only for sandbox"
was vastly helpful.
Mid-conversation, I trump up a few new questions:
- When in the overall U-Boot flow are the devices bound
and subsequently probed to make them available?
- The DTS file. I have a DTS that I am passing on to Linux
*through* U-Boot on the bootz command line. Is the same
DTS file that being used to setup and configure U-Boot now?
Or does U-Boot have its *own* (perhaps different) DTS?
Specifically, are the GPIO nodes in my Linux DTS file
the *same* nodes that might now be probed by U-Boot?
If so, then the compatibles have to agree. That is,
I'll have to use "arm,pl061". This sort of note also
needs to be present in the DM README.
Simon responds:
Yes you should use the same file. Perhaps README.fdt-control would
be a good place for that. We want U-Boot to use the same bindings as
Linux.
But I think my point was missed. It shouldn't be "the same file"
with "the same data". I think it should be the actual DTB as already
stored in flash waiting to be handed off to Linux.
I wrote:
That's not what is happening, though. Yesterday I worked through
the craziness that is CONFIG_OF_{EMBED,SEPARATE}. It caused me
to create a duplicate DTS, totally different than the one that
I pass to Linux already.
My Linux DTB is over in my flash, in an area of memory that is
perfectly accessible to U-Boot if it wanted to read it. But it
thinks it can't. So I have an OF_EMBED or OF_SEPARATE choice.
Either of those cause an entirely new copy of the DTS, located
in the U-Boot source tree now, to be DTC'ed and glued to the
u-boot image. I don't want that at all.
And having now worked through the issues around CONFIG_OF_SEPARATE
and CONFIG_OF_EMBED, I think there needs to be a third alternative
that says something like CONFIG_OF_SHARED_IN_FLASH. Dunno. More work.
I've also had some difficulty gettin the DTB to be parsed. Further
investigation revealed that my DTB is being found and parsed, but
that only the "top level" nodes are being used for "bind"operations.
As my GPIO nodes are second-level, under an SOC node, they are not
being found and bound. Again, more work there.
I close with a plea:
You know. :-)
Any help for the weary?
Thanks,
jdl
And so does Simon:
It would be good to cc this to the list - please considering doing
that so that everyone benefits. You are not missing anything obvious
and I'm sure others will benefit from your efforts.
Regards,
Simon
I offer to do the dirty work:
Simon, thank you! This really helped me. I don't want to sign this
address up on the U-Boot list, so I will cut-n-paste this onto my
gmail address and post a *slightly* modified version to the list!
And, with this mail, I have answered Simon's final plea:
Let's move this discussion to the list, as the issues you raise
above are much better discussed in public. Driver model is in the
very early stages and others will have comments also.
Regards,
Simon
Thanks,
jdl
(1) -- See what I did there?
(2) -- Are you following my timeline still? :-)
More information about the U-Boot
mailing list