[RFC PATCH 06/10] MIPS: Octeon: Initialize and fixup device tree.
David Daney
ddaney at caviumnetworks.com
Thu Feb 24 05:40:32 EST 2011
On 02/23/2011 09:41 AM, Grant Likely wrote:
> On Tue, Feb 22, 2011 at 12:57:50PM -0800, David Daney wrote:
>> Signed-off-by: David Daney<ddaney at caviumnetworks.com>
>> ---
>> arch/mips/Kconfig | 2 +
>> arch/mips/cavium-octeon/octeon-platform.c | 280 +++++++++++++++++++++++++++++
>> arch/mips/cavium-octeon/setup.c | 17 ++
>> 3 files changed, 299 insertions(+), 0 deletions(-)
>
> I've got an odd feeling of foreboding about this patch. It makes me
> nervous, but I can't articulate why yet. Gut-wise I'd rather see the
> device tree pruned/fixed up before it gets unflattened,
I chose to work on the unflattened form because there were already
functions to do it. I didn't see anything that would make manipulating
the flattened form easy.
I agree that working on the unflattened form would be best. At a minium
the /proc/device-tree structure would better reflect reality.
What do you think about adding some helper functions to drivers/of/fdt.c
for the manipulation of the flattened form?
> or for the
> kernel to have a separate .dtb linked in for each legacy platform.
I think there are too many variants to make this viable.
> I
> need to think about this some more....
>
> I've made some comments below anyway.
And I will respond. Although if I end up modifying the flattened form,
it will all change.
>
[...]
>> +
>> +static int __init set_phy_addr_prop(struct device_node *n, int phy)
>> +{
>> + u32 *vp;
>> + struct property *old_p;
>> + struct property *p = kzalloc(sizeof(struct device_node) + sizeof(u32), GFP_KERNEL);
>> + if (!p)
>> + return -ENOMEM;
>> + /* The value will immediatly follow the node in memory. */
>> + vp = (u32 *)(&p[1]);
>
> This is unsafe (I was on the losing end of an argument when I tried to
> do exactly the same thing). If you want to allocate 2 things with one
> appended to the other, then you need to define a structure
> with the two element in it and allocate the size of that structure.
Weird. alloc_netdev() does this, so it is not unheard of.
>
>> + p->name = "reg";
>> + p->length = sizeof(u32);
>> + p->value = vp;
>> +
>> + *vp = cpu_to_be32((u32)phy);
>
> phy is already an integer. Why the cast?
>
An oversight.
>> +
>> + old_p = of_find_property(n, "reg", NULL);
>> + if (old_p)
>> + prom_remove_property(n, old_p);
>> + return prom_add_property(n, p);
>
> Would it not be more efficient to change the value in the existing reg
> property instead of doing this allocation song-and-dance?
>
I think I did it this way to try to get /proc/device-tree to reflect the
new value.
>> +}
>> +
>> +static int __init set_mac_addr_prop(struct device_node *n, u64 mac)
>> +{
>> + u8 *vp;
>> + struct property *old_p;
>> + struct property *p = kzalloc(sizeof(struct device_node) + 6, GFP_KERNEL);
>> + if (!p)
>> + return -ENOMEM;
>> + /* The value will immediatly follow the node in memory. */
>> + vp = (u8 *)(&p[1]);
>> + p->name = "local-mac-address";
>> + p->length = 6;
>> + p->value = vp;
>> +
>> + vp[0] = (mac>> 40)& 0xff;
>> + vp[1] = (mac>> 32)& 0xff;
>> + vp[2] = (mac>> 24)& 0xff;
>> + vp[3] = (mac>> 16)& 0xff;
>> + vp[4] = (mac>> 8)& 0xff;
>> + vp[5] = mac& 0xff;
>> +
>> + old_p = of_find_property(n, "local-mac-address", NULL);
>> + if (old_p)
>> + prom_remove_property(n, old_p);
>> + return prom_add_property(n, p);
>
> Same comments apply to this function.
>
>> +}
>> +
>> +static struct device_node * __init octeon_of_get_child(const struct device_node *parent,
>> + int reg_val)
>> +{
>> + struct device_node *node = NULL;
>> + int size;
>> + const __be32 *addr;
>> +
>> + for (;;) {
>> + node = of_get_next_child(parent, node);
>
> Use for_each_child_of_node() here.
OK.
>
>> + if (!node)
>> + break;
>> + addr = of_get_property(node, "reg",&size);
>> + if (addr&& (be32_to_cpu(*addr) == reg_val))
>
> be32_to_cpup(addr)
>
Right.
>> + break;
>> + }
>> + return node;
>> +}
>> +
>> +int __init octeon_prune_device_tree(void)
>> +{
>> + int i, p, max_port;
>> + const char *node_path;
>> + char name_buffer[20];
>> + struct device_node *aliases;
>> + struct device_node *pip;
>> + struct device_node *iface;
>> + struct device_node *eth;
>> + struct device_node *node;
>> +
>> + aliases = of_find_node_by_path("/aliases");
>> + if (!aliases) {
>> + pr_err("Error: No /aliases node in device tree.");
>> + return -EINVAL;
>> + }
>> +
>> + if (OCTEON_IS_MODEL(OCTEON_CN52XX) || OCTEON_IS_MODEL(OCTEON_CN63XX))
>> + max_port = 2;
>> + else if (OCTEON_IS_MODEL(OCTEON_CN56XX))
>> + max_port = 1;
>> + else
>> + max_port = 0;
>> +
>> + for (i = 0; i< 2; i++) {
>> + struct device_node *mgmt;
>> + snprintf(name_buffer, sizeof(name_buffer),
>> + "ethernet-mgmt%d", i);
>> + node_path = of_get_property(aliases, name_buffer, NULL);
>> + if (node_path) {
>> + mgmt = of_find_node_by_path(node_path);
>
> of_find_node_by_path() needs to be fixed to also accept alias values
> so that a string that starts with a '/' is a full path, but no leading
> '/' means start with an alias. This code will lose a level of
> indentation if you can make that change to the common code.
>
I will consider making that change.
>> + if (!mgmt)
>> + continue;
>> + if (i>= max_port) {
>> + pr_notice("Deleting mgmt%d\n", i);
>> + node = of_parse_phandle(mgmt, "phy-handle", 0);
>> + if (node) {
>> + of_detach_node(node);
>> + of_node_put(node);
>> + }
>> + of_node_put(node);
>> +
>> + of_detach_node(mgmt);
>> + of_node_put(mgmt);
>> + }
>> + of_node_put(mgmt);
>> + }
>> + }
>> +
>> + node_path = of_get_property(aliases, "pip", NULL);
>> + if (node_path&& (pip = of_find_node_by_path(node_path))) {
>> + for (i = 0; i< 4; i++) {
>> + cvmx_helper_interface_enumerate(i);
>> + iface = octeon_of_get_child(pip, i);
>> + if (!iface)
>> + continue;
>> + for (p = 0; p< 4; p++) {
>> + eth = octeon_of_get_child(iface, p);
>> + if (!eth)
>> + continue;
>> + node = of_parse_phandle(eth, "phy-handle", 0);
>> + if (p< cvmx_helper_ports_on_interface(i)) {
>> + int phy = cvmx_helper_board_get_mii_address(16 * i + p);
>> + if (node&& phy< 0) {
>> + struct property *p = of_find_property(eth, "phy-handle", NULL);
>> + of_detach_node(node);
>> + of_node_put(node);
>> + prom_remove_property(eth, p);
>> + }
>
> There is a lot of nesting here; could this be refactored?
Perhaps. It really is a three deep nesting though.
>
>> + } else {
[...]
>> +}
>> +arch_initcall(octeon_fix_device_tree);
>
> Calling this from an initcall really makes me nervous. I'm worried
> about ordering issues. Why can this code not be part of the prune
> routine above?
>
Again, done to try to make /proc/device-tree reflect reality.
Thanks for looking at it. I will generate another version of the patches.
David Daney
More information about the devicetree-discuss
mailing list