From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754804Ab3KFQ0z (ORCPT ); Wed, 6 Nov 2013 11:26:55 -0500 Received: from demumfd001.nsn-inter.net ([93.183.12.32]:11968 "EHLO demumfd001.nsn-inter.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750811Ab3KFQ0v (ORCPT ); Wed, 6 Nov 2013 11:26:51 -0500 Message-ID: <527A6DAC.6000200@nsn.com> Date: Wed, 06 Nov 2013 17:26:20 +0100 From: Ionut Nicu User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:17.0) Gecko/20130801 Thunderbird/17.0.8 MIME-Version: 1.0 To: Alexander Sverdlin CC: ext Pantelis Antoniou , Grant Likely , Rob Herring , Stephen Warren , Matt Porter , Koen Kooi , Alison Chaiken , Dinh Nguyen , Jan Lubbe , Michael Stickel , Guenter Roeck , Dirk Behme , Alan Tull , Sascha Hauer , Michael Bohan , Michal Simek , Matt Ranostay , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: Re: [PATCH 2/3] OF: Introduce DT overlay support. References: <1383676898-29819-1-git-send-email-panto@antoniou-consulting.com> <1383676898-29819-3-git-send-email-panto@antoniou-consulting.com> <527A67B7.8040808@nsn.com> In-Reply-To: <527A67B7.8040808@nsn.com> Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit X-purgate-type: clean X-purgate-Ad: Categorized by eleven eXpurgate (R) http://www.eleven.de X-purgate: clean X-purgate: This mail is considered clean (visit http://www.eleven.de for further information) X-purgate-size: 38467 X-purgate-ID: 151667::1383755181-00004A43-741B8574/0-0/0-0 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Hi, On 06.11.2013 17:00, Alexander Sverdlin wrote: > Hi! > > On 05/11/13 19:41, ext Pantelis Antoniou wrote: >> Introduce DT overlay support. >> Using this functionality it is possible to dynamically overlay a part of >> the kernel's tree with another tree that's been dynamically loaded. >> It is also possible to remove node and properties. >> >> Note that the I2C client devices are 'special', as in they're not platform >> devices. They need to be registered with an I2C specific method. >> >> In general I2C is just no good platform device citizen and needs >> special casing. >> >> Signed-off-by: Pantelis Antoniou > > Reviewed-by: Alexander Sverdlin > I'm using this patch for a few months without any problems, so: Tested-by: Ionut Nicu >> --- >> Documentation/devicetree/overlay-notes.txt | 179 ++++++ >> drivers/of/Kconfig | 10 + >> drivers/of/Makefile | 1 + >> drivers/of/overlay.c | 869 +++++++++++++++++++++++++++++ >> include/linux/of.h | 113 ++++ >> 5 files changed, 1172 insertions(+) >> create mode 100644 Documentation/devicetree/overlay-notes.txt >> create mode 100644 drivers/of/overlay.c >> >> diff --git a/Documentation/devicetree/overlay-notes.txt b/Documentation/devicetree/overlay-notes.txt >> new file mode 100644 >> index 0000000..5289cbb >> --- /dev/null >> +++ b/Documentation/devicetree/overlay-notes.txt >> @@ -0,0 +1,179 @@ >> +Device Tree Overlay Notes >> +------------------------- >> + >> +This document describes the implementation of the in-kernel >> +device tree overlay functionality residing in drivers/of/overlay.c and is a >> +companion document to Documentation/devicetree/dt-object-internal.txt[1] & >> +Documentation/devicetree/dynamic-resolution-notes.txt[2] >> + >> +How overlays work >> +----------------- >> + >> +A Device Tree's overlay purpose is to modify the kernel's live tree, and >> +have the modification affecting the state of the the kernel in a way that >> +is reflecting the changes. >> +Since the kernel mainly deals with devices, any new device node that result >> +in an active device should have it created while if the device node is either >> +disabled or removed all together, the affected device should be deregistered. >> + >> +Lets take an example where we have a foo board with the following base tree >> +which is taken from [1]. >> + >> +---- foo.dts ----------------------------------------------------------------- >> + /* FOO platform */ >> + / { >> + compatible = "corp,foo"; >> + >> + /* shared resources */ >> + res: res { >> + }; >> + >> + /* On chip peripherals */ >> + ocp: ocp { >> + /* peripherals that are always instantiated */ >> + peripheral1 { ... }; >> + } >> + }; >> +---- foo.dts ----------------------------------------------------------------- >> + >> +The overlay bar.dts, when loaded (and resolved as described in [2]) should >> + >> +---- bar.dts ----------------------------------------------------------------- >> +/plugin/; /* allow undefined label references and record them */ >> +/ { >> + .... /* various properties for loader use; i.e. part id etc. */ >> + fragment@0 { >> + target = <&ocp>; >> + __overlay__ { >> + /* bar peripheral */ >> + bar { >> + compatible = "corp,bar"; >> + ... /* various properties and child nodes */ >> + } >> + }; >> + }; >> +}; >> +---- bar.dts ----------------------------------------------------------------- >> + >> +result in foo+bar.dts >> + >> +---- foo+bar.dts ------------------------------------------------------------- >> + /* FOO platform + bar peripheral */ >> + / { >> + compatible = "corp,foo"; >> + >> + /* shared resources */ >> + res: res { >> + }; >> + >> + /* On chip peripherals */ >> + ocp: ocp { >> + /* peripherals that are always instantiated */ >> + peripheral1 { ... }; >> + >> + /* bar peripheral */ >> + bar { >> + compatible = "corp,bar"; >> + ... /* various properties and child nodes */ >> + } >> + } >> + }; >> +---- foo+bar.dts ------------------------------------------------------------- >> + >> +As a result of the the overlay, a new device node (bar) has been created >> +so a bar platform device will be registered and if a matching device driver >> +is loaded the device will be created as expected. >> + >> +Overlay in-kernel API >> +--------------------- >> + >> +The steps typically required to get an overlay to work are as follows: >> + >> +1. Use of_build_overlay_info() to create an array of initialized and >> +ready to use of_overlay_info structures. >> +2. Call of_overlay() to apply the overlays declared in the array. >> +3. If the overlay needs to be removed, call of_overlay_revert(). >> +4. Finally release the memory taken by the overlay info array by >> +of_free_overlay_info(). >> + >> +/** >> + * of_build_overlay_info - Build an overlay info array >> + * @tree: Device node containing all the overlays >> + * @cntp: Pointer to where the overlay info count will be help >> + * @ovinfop: Pointer to the pointer of an overlay info structure. >> + * >> + * Helper function that given a tree containing overlay information, >> + * allocates and builds an overlay info array containing it, ready >> + * for use using of_overlay. >> + * >> + * Returns 0 on success with the @cntp @ovinfop pointers valid, >> + * while on error a negative error value is returned. >> + */ >> +int of_build_overlay_info(struct device_node *tree, >> + int *cntp, struct of_overlay_info **ovinfop); >> + >> +/** >> + * of_free_overlay_info - Free an overlay info array >> + * @count: Number of of_overlay_info's >> + * @ovinfo_tab: Array of overlay_info's to free >> + * >> + * Releases the memory of a previously allocate ovinfo array >> + * by of_build_overlay_info. >> + * Returns 0, or an error if the arguments are bogus. >> + */ >> +int of_free_overlay_info(int count, struct of_overlay_info *ovinfo_tab); >> + >> +/** >> + * of_overlay - Apply @count overlays pointed at by @ovinfo_tab >> + * @count: Number of of_overlay_info's >> + * @ovinfo_tab: Array of overlay_info's to apply >> + * >> + * Applies the overlays given, while handling all error conditions >> + * appropriately. Either the operation succeeds, or if it fails the >> + * live tree is reverted to the state before the attempt. >> + * Returns 0, or an error if the overlay attempt failed. >> + */ >> +int of_overlay(int count, struct of_overlay_info *ovinfo_tab); >> + >> +/** >> + * of_overlay_revert - Revert a previously applied overlay >> + * @count: Number of of_overlay_info's >> + * @ovinfo_tab: Array of overlay_info's to apply >> + * >> + * Revert a previous overlay. The state of the live tree >> + * is reverted to the one before the overlay. >> + * Returns 0, or an error if the overlay table is not given. >> + */ >> +int of_overlay_revert(int count, struct of_overlay_info *ovinfo_tab); >> + >> +Overlay DTS Format >> +------------------ >> + >> +The DTS of an overlay should have the following format: >> + >> +{ >> + /* ignored properties by the overlay */ >> + >> + fragment@0 { /* first child node */ >> + target=; /* target of the overlay */ >> + __overlay__ { >> + property-a; /* add property-a to the target */ >> + -property-b; /* remove property-b from target */ >> + node-a { /* add to an existing, or create a node-a */ >> + ... >> + }; >> + -node-b { /* remove an existing node-b */ >> + ... >> + }; >> + }; >> + } >> + fragment@1 { /* second child node */ >> + ... >> + }; >> + /* more fragments follow */ >> +} >> + >> +It should be noted that the DT overlay format described is the one expected >> +by the of_build_overlay_info() function, which is a helper function. There >> +is nothing stopping someone coming up with his own DTS format and that will >> +end up filling in the fields of the of_overlay_info array. >> diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig >> index 2a00ae5..c2d5596 100644 >> --- a/drivers/of/Kconfig >> +++ b/drivers/of/Kconfig >> @@ -83,4 +83,14 @@ config OF_RESOLVE >> Enable OF dynamic resolution support. This allows you to >> load Device Tree object fragments are run time. >> >> +config OF_OVERLAY >> + bool "OF overlay support" >> + depends on OF >> + select OF_DYNAMIC >> + select OF_DEVICE >> + select OF_RESOLVE >> + help >> + OpenFirmware overlay support. Allows you to modify on runtime the >> + live tree using overlays. >> + >> endmenu # OF >> diff --git a/drivers/of/Makefile b/drivers/of/Makefile >> index 93da457..ca466e4 100644 >> --- a/drivers/of/Makefile >> +++ b/drivers/of/Makefile >> @@ -10,3 +10,4 @@ obj-$(CONFIG_OF_PCI) += of_pci.o >> obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o >> obj-$(CONFIG_OF_MTD) += of_mtd.o >> obj-$(CONFIG_OF_RESOLVE) += resolver.o >> +obj-$(CONFIG_OF_OVERLAY) += overlay.o >> diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c >> new file mode 100644 >> index 0000000..5bed1f4 >> --- /dev/null >> +++ b/drivers/of/overlay.c >> @@ -0,0 +1,869 @@ >> +/* >> + * Functions for working with device tree overlays >> + * >> + * Copyright (C) 2012 Pantelis Antoniou >> + * Copyright (C) 2012 Texas Instruments Inc. >> + * >> + * This program is free software; you can redistribute it and/or >> + * modify it under the terms of the GNU General Public License >> + * version 2 as published by the Free Software Foundation. >> + */ >> +#undef DEBUG >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> + >> +/** >> + * Apply a single overlay node recursively. >> + * >> + * Property or node names that start with '-' signal that >> + * the property/node is to be removed. >> + * >> + * All the property notifiers are appropriately called. >> + * Note that the in case of an error the target node is left >> + * in a inconsistent state. Error recovery should be performed >> + * by recording the modification using the of notifiers. >> + */ >> +static int of_overlay_apply_one(struct device_node *target, >> + const struct device_node *overlay) >> +{ >> + const char *pname, *cname; >> + struct device_node *child, *tchild; >> + struct property *prop, *propn, *tprop; >> + int remove; >> + char *full_name; >> + const char *suffix; >> + int ret; >> + >> + /* sanity checks */ >> + if (target == NULL || overlay == NULL) >> + return -EINVAL; >> + >> + for_each_property_of_node(overlay, prop) { >> + >> + /* don't touch, 'name' */ >> + if (of_prop_cmp(prop->name, "name") == 0) >> + continue; >> + >> + /* default is add */ >> + remove = 0; >> + pname = prop->name; >> + if (*pname == '-') { /* skip, - notes removal */ >> + pname++; >> + remove = 1; >> + propn = NULL; >> + } else { >> + propn = __of_copy_property(prop, >> + GFP_KERNEL); >> + if (propn == NULL) >> + return -ENOMEM; >> + } >> + >> + tprop = of_find_property(target, pname, NULL); >> + >> + /* found? */ >> + if (tprop != NULL) { >> + if (propn != NULL) >> + ret = of_update_property(target, propn); >> + else >> + ret = of_remove_property(target, tprop); >> + } else { >> + if (propn != NULL) >> + ret = of_add_property(target, propn); >> + else >> + ret = 0; >> + } >> + if (ret != 0) >> + return ret; >> + } >> + >> + __for_each_child_of_node(overlay, child) { >> + >> + /* default is add */ >> + remove = 0; >> + cname = child->name; >> + if (*cname == '-') { /* skip, - notes removal */ >> + cname++; >> + remove = 1; >> + } >> + >> + /* special case for nodes with a suffix */ >> + suffix = strrchr(child->full_name, '@'); >> + if (suffix != NULL) { >> + cname = kbasename(child->full_name); >> + WARN_ON(cname == NULL); /* sanity check */ >> + if (cname == NULL) >> + continue; >> + if (*cname == '-') >> + cname++; >> + } >> + >> + tchild = of_get_child_by_name(target, cname); >> + if (tchild != NULL) { >> + >> + if (!remove) { >> + >> + /* apply overlay recursively */ >> + ret = of_overlay_apply_one(tchild, child); >> + of_node_put(tchild); >> + >> + if (ret != 0) >> + return ret; >> + >> + } else { >> + >> + ret = of_detach_node(tchild); >> + of_node_put(tchild); >> + } >> + >> + } else { >> + >> + if (!remove) { >> + full_name = kasprintf(GFP_KERNEL, "%s/%s", >> + target->full_name, cname); >> + if (full_name == NULL) >> + return -ENOMEM; >> + >> + /* create empty tree as a target */ >> + tchild = __of_create_empty_node(cname, >> + child->type, full_name, >> + child->phandle, GFP_KERNEL); >> + >> + /* free either way */ >> + kfree(full_name); >> + >> + if (tchild == NULL) >> + return -ENOMEM; >> + >> + /* point to parent */ >> + tchild->parent = target; >> + >> + ret = of_attach_node(tchild); >> + if (ret != 0) >> + return ret; >> + >> + /* apply the overlay */ >> + ret = of_overlay_apply_one(tchild, child); >> + if (ret != 0) { >> + __of_free_tree(tchild); >> + return ret; >> + } >> + } >> + } >> + } >> + >> + return 0; >> +} >> + >> +/** >> + * Lookup an overlay device entry >> + */ >> +struct of_overlay_device_entry *of_overlay_device_entry_lookup( >> + struct of_overlay_info *ovinfo, struct device_node *node) >> +{ >> + struct of_overlay_device_entry *de; >> + >> + /* no need for locks, we'de under the ovinfo->lock */ >> + list_for_each_entry(de, &ovinfo->de_list, node) { >> + if (de->np == node) >> + return de; >> + } >> + return NULL; >> +} >> + >> +/** >> + * Add an overlay log entry >> + */ >> +static int of_overlay_log_entry_entry_add(struct of_overlay_info *ovinfo, >> + unsigned long action, struct device_node *dn, >> + struct property *prop) >> +{ >> + struct of_overlay_log_entry *le; >> + >> + /* check */ >> + if (ovinfo == NULL || dn == NULL) >> + return -EINVAL; >> + >> + le = kzalloc(sizeof(*le), GFP_KERNEL); >> + if (le == NULL) { >> + pr_err("%s: Failed to allocate\n", __func__); >> + return -ENOMEM; >> + } >> + >> + /* get a reference to the node */ >> + le->action = action; >> + le->np = of_node_get(dn); >> + le->prop = prop; >> + >> + if (action == OF_RECONFIG_UPDATE_PROPERTY && prop) >> + le->old_prop = of_find_property(dn, prop->name, NULL); >> + >> + list_add_tail(&le->node, &ovinfo->le_list); >> + >> + return 0; >> +} >> + >> +/** >> + * Add an overlay device entry >> + */ >> +static void of_overlay_device_entry_entry_add(struct of_overlay_info *ovinfo, >> + struct device_node *node, >> + struct platform_device *pdev, struct i2c_client *client, >> + int prevstate, int state) >> +{ >> + struct of_overlay_device_entry *de; >> + int fresh; >> + >> + /* check */ >> + if (ovinfo == NULL) >> + return; >> + >> + fresh = 0; >> + de = of_overlay_device_entry_lookup(ovinfo, node); >> + if (de == NULL) { >> + de = kzalloc(sizeof(*de), GFP_KERNEL); >> + if (de == NULL) { >> + pr_err("%s: Failed to allocate\n", __func__); >> + return; >> + } >> + fresh = 1; >> + de->prevstate = -1; >> + } >> + >> + if (de->np == NULL) >> + de->np = of_node_get(node); >> + if (de->pdev == NULL && pdev) >> + de->pdev = of_dev_get(pdev); >> + if (de->client == NULL && client) >> + de->client = i2c_use_client(client); >> + if (fresh) >> + de->prevstate = prevstate; >> + de->state = state; >> + >> + if (fresh) >> + list_add_tail(&de->node, &ovinfo->de_list); >> +} >> + >> +/** >> + * Overlay OF notifier >> + * >> + * Called every time there's a property/node modification >> + * Every modification causes a log entry addition, while >> + * any modification that causes a node's state to change >> + * from/to disabled to/from enabled causes a device entry >> + * addition. >> + */ >> +static int of_overlay_notify(struct notifier_block *nb, >> + unsigned long action, void *arg) >> +{ >> + struct of_overlay_info *ovinfo; >> + struct device_node *node; >> + struct property *prop, *sprop, *cprop; >> + struct of_prop_reconfig *pr; >> + struct platform_device *pdev; >> + struct i2c_client *client; >> + struct device_node *tnode; >> + int depth; >> + int prevstate, state; >> + int err = 0; >> + >> + ovinfo = container_of(nb, struct of_overlay_info, notifier); >> + >> + /* prep vars */ >> + switch (action) { >> + case OF_RECONFIG_ATTACH_NODE: >> + case OF_RECONFIG_DETACH_NODE: >> + node = arg; >> + if (node == NULL) >> + return notifier_from_errno(-EINVAL); >> + prop = NULL; >> + break; >> + case OF_RECONFIG_ADD_PROPERTY: >> + case OF_RECONFIG_REMOVE_PROPERTY: >> + case OF_RECONFIG_UPDATE_PROPERTY: >> + pr = arg; >> + if (pr == NULL) >> + return notifier_from_errno(-EINVAL); >> + node = pr->dn; >> + if (node == NULL) >> + return notifier_from_errno(-EINVAL); >> + prop = pr->prop; >> + if (prop == NULL) >> + return notifier_from_errno(-EINVAL); >> + break; >> + default: >> + return notifier_from_errno(0); >> + } >> + >> + /* add to the log */ >> + err = of_overlay_log_entry_entry_add(ovinfo, action, node, prop); >> + if (err != 0) >> + return notifier_from_errno(err); >> + >> + /* come up with the device entry (if any) */ >> + pdev = NULL; >> + client = NULL; >> + state = 0; >> + prevstate = 0; >> + >> + /* determine the state the node will end up */ >> + switch (action) { >> + case OF_RECONFIG_ATTACH_NODE: >> + /* we demand that a compatible node is present */ >> + state = of_find_property(node, "compatible", NULL) && >> + of_device_is_available(node); >> + break; >> + case OF_RECONFIG_DETACH_NODE: >> + prevstate = of_find_property(node, "compatible", NULL) && >> + of_device_is_available(node); >> + state = 0; >> + pdev = of_find_device_by_node(node); >> + client = of_find_i2c_device_by_node(node); >> + break; >> + case OF_RECONFIG_ADD_PROPERTY: >> + case OF_RECONFIG_REMOVE_PROPERTY: >> + case OF_RECONFIG_UPDATE_PROPERTY: >> + /* either one cause a change in state */ >> + if (strcmp(prop->name, "status") != 0 && >> + strcmp(prop->name, "compatible") != 0) >> + return notifier_from_errno(0); >> + >> + if (strcmp(prop->name, "status") == 0) { >> + /* status */ >> + cprop = of_find_property(node, "compatible", NULL); >> + sprop = action != OF_RECONFIG_REMOVE_PROPERTY ? >> + prop : NULL; >> + } else { >> + /* compatible */ >> + sprop = of_find_property(node, "status", NULL); >> + cprop = action != OF_RECONFIG_REMOVE_PROPERTY ? >> + prop : NULL; >> + } >> + >> + prevstate = of_find_property(node, "compatible", NULL) && >> + of_device_is_available(node); >> + state = cprop && cprop->length > 0 && >> + (!sprop || (sprop->length > 0 && >> + (strcmp(sprop->value, "okay") == 0 || >> + strcmp(sprop->value, "ok") == 0))); >> + break; >> + >> + default: >> + return notifier_from_errno(0); >> + } >> + >> + /* find depth */ >> + depth = 1; >> + tnode = node; >> + while (tnode != NULL && tnode != ovinfo->target) { >> + tnode = tnode->parent; >> + depth++; >> + } >> + >> + /* respect overlay's maximum depth */ >> + if (ovinfo->device_depth != 0 && depth > ovinfo->device_depth) { >> + pr_debug("OF: skipping device creation for node=%s depth=%d\n", >> + node->name, depth); >> + goto out; >> + } >> + >> + if (state == 0) { >> + pdev = of_find_device_by_node(node); >> + client = of_find_i2c_device_by_node(node); >> + } >> + >> + of_overlay_device_entry_entry_add(ovinfo, node, pdev, client, >> + prevstate, state); >> +out: >> + >> + return notifier_from_errno(err); >> +} >> + >> +/** >> + * Prepare for the overlay, for now it just registers the >> + * notifier. >> + */ >> +static int of_overlay_prep_one(struct of_overlay_info *ovinfo) >> +{ >> + int err; >> + >> + err = of_reconfig_notifier_register(&ovinfo->notifier); >> + if (err != 0) { >> + pr_err("%s: failed to register notifier for '%s'\n", >> + __func__, ovinfo->target->full_name); >> + return err; >> + } >> + return 0; >> +} >> + >> +static int of_overlay_device_entry_change(struct of_overlay_info *ovinfo, >> + struct of_overlay_device_entry *de, int revert) >> +{ >> + struct i2c_adapter *adap = NULL; >> + struct i2c_client *client; >> + struct platform_device *pdev, *parent_pdev = NULL; >> + int state; >> + >> + state = !!de->state ^ !!revert; >> + >> + if (de->np && de->np->parent) { >> + pr_debug("%s: parent is %s\n", >> + __func__, de->np->parent->full_name); >> + adap = of_find_i2c_adapter_by_node(de->np->parent); >> + if (adap == NULL) >> + parent_pdev = of_find_device_by_node(de->np->parent); >> + } >> + >> + if (state) { >> + >> + /* try to see if it's an I2C client node */ >> + if (adap == NULL) { >> + >> + pr_debug("%s: creating new platform device " >> + "new_node='%s' %p\n", >> + __func__, de->np->full_name, de->np); >> + >> + pdev = of_platform_device_create(de->np, NULL, >> + parent_pdev ? &parent_pdev->dev : NULL); >> + if (pdev == NULL) { >> + pr_warn("%s: Failed to create platform device " >> + "for '%s'\n", >> + __func__, de->np->full_name); >> + } else >> + de->pdev = pdev; >> + >> + } else { >> + >> + client = of_find_i2c_device_by_node(de->np); >> + if (client != NULL) { >> + /* bus already created the device; do nothing */ >> + put_device(&client->dev); >> + } else { >> + pr_debug("%s: creating new i2c_client device " >> + "new_node='%s' %p\n", >> + __func__, de->np->full_name, de->np); >> + >> + client = of_i2c_register_device(adap, de->np); >> + >> + if (client == NULL) { >> + pr_warn("%s: Failed to create i2c client device " >> + "for '%s'\n", >> + __func__, de->np->full_name); >> + } else >> + de->client = client; >> + } >> + } >> + >> + } else { >> + >> + if (de->pdev) { >> + pr_debug("%s: removing pdev %s\n", __func__, >> + dev_name(&de->pdev->dev)); >> + platform_device_unregister(de->pdev); >> + de->pdev = NULL; >> + } >> + >> + if (de->client) { >> + pr_debug("%s: removing i2c client %s\n", __func__, >> + dev_name(&de->client->dev)); >> + i2c_unregister_device(de->client); >> + de->client = NULL; >> + } >> + } >> + >> + if (adap) >> + put_device(&adap->dev); >> + >> + if (parent_pdev) >> + of_dev_put(parent_pdev); >> + >> + return 0; >> +} >> + >> +/** >> + * Revert one overlay >> + * Either due to an error, or due to normal overlay removal. >> + * Using the log entries, we revert any change to the live tree. >> + * In the same manner, using the device entries we enable/disable >> + * the platform devices appropriately. >> + */ >> +static void of_overlay_revert_one(struct of_overlay_info *ovinfo) >> +{ >> + struct of_overlay_device_entry *de, *den; >> + struct of_overlay_log_entry *le, *len; >> + struct property *prop, **propp; >> + int ret; >> + unsigned long flags; >> + >> + if (!ovinfo || !ovinfo->target || !ovinfo->overlay) >> + return; >> + >> + pr_debug("%s: Reverting overlay on '%s'\n", __func__, >> + ovinfo->target->full_name); >> + >> + /* overlay applied correctly, now create/destroy pdevs */ >> + list_for_each_entry_safe_reverse(de, den, &ovinfo->de_list, node) { >> + of_overlay_device_entry_change(ovinfo, de, 1); >> + of_node_put(de->np); >> + list_del(&de->node); >> + kfree(de); >> + } >> + >> + list_for_each_entry_safe_reverse(le, len, &ovinfo->le_list, node) { >> + >> + ret = 0; >> + switch (le->action) { >> + case OF_RECONFIG_ATTACH_NODE: >> + pr_debug("Reverting ATTACH_NODE %s\n", >> + le->np->full_name); >> + ret = of_detach_node(le->np); >> + break; >> + >> + case OF_RECONFIG_DETACH_NODE: >> + pr_debug("Reverting DETACH_NODE %s\n", >> + le->np->full_name); >> + ret = of_attach_node(le->np); >> + break; >> + >> + case OF_RECONFIG_ADD_PROPERTY: >> + pr_debug("Reverting ADD_PROPERTY %s %s\n", >> + le->np->full_name, le->prop->name); >> + ret = of_remove_property(le->np, le->prop); >> + break; >> + >> + case OF_RECONFIG_REMOVE_PROPERTY: >> + case OF_RECONFIG_UPDATE_PROPERTY: >> + >> + pr_debug("Reverting %s_PROPERTY %s %s\n", >> + le->action == OF_RECONFIG_REMOVE_PROPERTY ? >> + "REMOVE" : "UPDATE", >> + le->np->full_name, le->prop->name); >> + >> + /* property is possibly on deadprops (avoid alloc) */ >> + raw_spin_lock_irqsave(&devtree_lock, flags); >> + prop = le->action == OF_RECONFIG_REMOVE_PROPERTY ? >> + le->prop : le->old_prop; >> + propp = &le->np->deadprops; >> + while (*propp != NULL) { >> + if (*propp == prop) >> + break; >> + propp = &(*propp)->next; >> + } >> + if (*propp != NULL) { >> + /* remove it from deadprops */ >> + (*propp)->next = prop->next; >> + raw_spin_unlock_irqrestore(&devtree_lock, >> + flags); >> + } else { >> + raw_spin_unlock_irqrestore(&devtree_lock, >> + flags); >> + /* not found, just make a copy */ >> + prop = __of_copy_property(prop, GFP_KERNEL); >> + if (prop == NULL) { >> + pr_err("%s: Failed to copy property\n", >> + __func__); >> + break; >> + } >> + } >> + >> + if (le->action == OF_RECONFIG_REMOVE_PROPERTY) >> + ret = of_add_property(le->np, prop); >> + else >> + ret = of_update_property(le->np, prop); >> + break; >> + >> + default: >> + /* nothing */ >> + break; >> + } >> + >> + if (ret != 0) >> + pr_err("%s: revert on node %s Failed!\n", >> + __func__, le->np->full_name); >> + >> + of_node_put(le->np); >> + >> + list_del(&le->node); >> + >> + kfree(le); >> + } >> +} >> + >> +/** >> + * Perform the post overlay work. >> + * >> + * We unregister the notifier, and in the case on an error we >> + * revert the overlay. >> + * If the overlay applied correctly, we iterare over the device entries >> + * and create/destroy the platform devices appropriately. >> + */ >> +static int of_overlay_post_one(struct of_overlay_info *ovinfo, int err) >> +{ >> + struct of_overlay_device_entry *de, *den; >> + >> + of_reconfig_notifier_unregister(&ovinfo->notifier); >> + >> + if (err != 0) { >> + /* revert this (possible partially applied) overlay */ >> + of_overlay_revert_one(ovinfo); >> + return 0; >> + } >> + >> + /* overlay applied correctly, now create/destroy pdevs */ >> + list_for_each_entry_safe(de, den, &ovinfo->de_list, node) { >> + >> + /* no state change? just remove this entry */ >> + if (de->prevstate == de->state) { >> + of_node_put(de->np); >> + list_del(&de->node); >> + kfree(de); >> + } else { >> + of_overlay_device_entry_change(ovinfo, de, 0); >> + } >> + } >> + >> + return 0; >> +} >> + >> +/** >> + * of_overlay - Apply @count overlays pointed at by @ovinfo_tab >> + * @count: Number of of_overlay_info's >> + * @ovinfo_tab: Array of overlay_info's to apply >> + * >> + * Applies the overlays given, while handling all error conditions >> + * appropriately. Either the operation succeeds, or if it fails the >> + * live tree is reverted to the state before the attempt. >> + * Returns 0, or an error if the overlay attempt failed. >> + */ >> +int of_overlay(int count, struct of_overlay_info *ovinfo_tab) >> +{ >> + struct of_overlay_info *ovinfo; >> + int i, err; >> + >> + if (!ovinfo_tab) >> + return -EINVAL; >> + >> + /* first we apply the overlays atomically */ >> + for (i = 0; i < count; i++) { >> + >> + ovinfo = &ovinfo_tab[i]; >> + >> + mutex_lock(&ovinfo->lock); >> + >> + err = of_overlay_prep_one(ovinfo); >> + if (err == 0) >> + err = of_overlay_apply_one(ovinfo->target, >> + ovinfo->overlay); >> + of_overlay_post_one(ovinfo, err); >> + >> + mutex_unlock(&ovinfo->lock); >> + >> + if (err != 0) { >> + pr_err("%s: overlay failed '%s'\n", >> + __func__, ovinfo->target->full_name); >> + goto err_fail; >> + } >> + } >> + >> + return 0; >> + >> +err_fail: >> + while (--i >= 0) { >> + ovinfo = &ovinfo_tab[i]; >> + >> + mutex_lock(&ovinfo->lock); >> + of_overlay_revert_one(ovinfo); >> + mutex_unlock(&ovinfo->lock); >> + } >> + >> + return err; >> +} >> + >> +/** >> + * of_overlay_revert - Revert a previously applied overlay >> + * @count: Number of of_overlay_info's >> + * @ovinfo_tab: Array of overlay_info's to apply >> + * >> + * Revert a previous overlay. The state of the live tree >> + * is reverted to the one before the overlay. >> + * Returns 0, or an error if the overlay table is not given. >> + */ >> +int of_overlay_revert(int count, struct of_overlay_info *ovinfo_tab) >> +{ >> + struct of_overlay_info *ovinfo; >> + int i; >> + >> + if (!ovinfo_tab) >> + return -EINVAL; >> + >> + /* revert the overlays in reverse */ >> + for (i = count - 1; i >= 0; i--) { >> + >> + ovinfo = &ovinfo_tab[i]; >> + >> + mutex_lock(&ovinfo->lock); >> + of_overlay_revert_one(ovinfo); >> + mutex_unlock(&ovinfo->lock); >> + >> + } >> + >> + return 0; >> +} >> + >> +/** >> + * of_init_overlay_info - Initialize a single of_overlay_info structure >> + * @ovinfo: Pointer to the overlay info structure to initialize >> + * >> + * Initialize a single overlay info structure. >> + */ >> +void of_init_overlay_info(struct of_overlay_info *ovinfo) >> +{ >> + memset(ovinfo, 0, sizeof(ovinfo)); >> + mutex_init(&ovinfo->lock); >> + INIT_LIST_HEAD(&ovinfo->de_list); >> + INIT_LIST_HEAD(&ovinfo->le_list); >> + >> + ovinfo->notifier.notifier_call = of_overlay_notify; >> +} >> + >> +/** >> + * of_fill_overlay_info - Fill an overlay info structure >> + * @info_node: Device node containing the overlay >> + * @ovinfo: Pointer to the overlay info structure to fill >> + * >> + * Fills an overlay info structure with the overlay information >> + * from a device node. This device node must have a target property >> + * which contains a phandle of the overlay target node, and an >> + * __overlay__ child node which has the overlay contents. >> + * Both ovinfo->target & ovinfo->overlay have their references taken. >> + * >> + * Returns 0 on success, or a negative error value. >> + */ >> +int of_fill_overlay_info(struct device_node *info_node, >> + struct of_overlay_info *ovinfo) >> +{ >> + u32 val; >> + int ret; >> + >> + if (!info_node || !ovinfo) >> + return -EINVAL; >> + >> + ret = of_property_read_u32(info_node, "target", &val); >> + if (ret != 0) >> + goto err_fail; >> + >> + ovinfo->target = of_find_node_by_phandle(val); >> + if (ovinfo->target == NULL) >> + goto err_fail; >> + >> + ovinfo->overlay = of_get_child_by_name(info_node, "__overlay__"); >> + if (ovinfo->overlay == NULL) >> + goto err_fail; >> + >> + ret = of_property_read_u32(info_node, "depth", &val); >> + if (ret == 0) >> + ovinfo->device_depth = val; >> + else >> + ovinfo->device_depth = 0; >> + >> + >> + return 0; >> + >> +err_fail: >> + of_node_put(ovinfo->target); >> + of_node_put(ovinfo->overlay); >> + >> + memset(ovinfo, 0, sizeof(*ovinfo)); >> + return -EINVAL; >> +} >> + >> +/** >> + * of_build_overlay_info - Build an overlay info array >> + * @tree: Device node containing all the overlays >> + * @cntp: Pointer to where the overlay info count will be help >> + * @ovinfop: Pointer to the pointer of an overlay info structure. >> + * >> + * Helper function that given a tree containing overlay information, >> + * allocates and builds an overlay info array containing it, ready >> + * for use using of_overlay. >> + * >> + * Returns 0 on success with the @cntp @ovinfop pointers valid, >> + * while on error a negative error value is returned. >> + */ >> +int of_build_overlay_info(struct device_node *tree, >> + int *cntp, struct of_overlay_info **ovinfop) >> +{ >> + struct device_node *node; >> + struct of_overlay_info *ovinfo; >> + int cnt, err; >> + >> + if (tree == NULL || cntp == NULL || ovinfop == NULL) >> + return -EINVAL; >> + >> + /* worst case; every child is a node */ >> + cnt = 0; >> + for_each_child_of_node(tree, node) >> + cnt++; >> + >> + ovinfo = kzalloc(cnt * sizeof(*ovinfo), GFP_KERNEL); >> + if (ovinfo == NULL) >> + return -ENOMEM; >> + >> + cnt = 0; >> + for_each_child_of_node(tree, node) { >> + >> + of_init_overlay_info(&ovinfo[cnt]); >> + err = of_fill_overlay_info(node, &ovinfo[cnt]); >> + if (err == 0) >> + cnt++; >> + } >> + >> + /* if nothing filled, return error */ >> + if (cnt == 0) { >> + kfree(ovinfo); >> + return -ENODEV; >> + } >> + >> + *cntp = cnt; >> + *ovinfop = ovinfo; >> + >> + return 0; >> +} >> + >> +/** >> + * of_free_overlay_info - Free an overlay info array >> + * @count: Number of of_overlay_info's >> + * @ovinfo_tab: Array of overlay_info's to free >> + * >> + * Releases the memory of a previously allocate ovinfo array >> + * by of_build_overlay_info. >> + * Returns 0, or an error if the arguments are bogus. >> + */ >> +int of_free_overlay_info(int count, struct of_overlay_info *ovinfo_tab) >> +{ >> + struct of_overlay_info *ovinfo; >> + int i; >> + >> + if (!ovinfo_tab || count < 0) >> + return -EINVAL; >> + >> + /* do it in reverse */ >> + for (i = count - 1; i >= 0; i--) { >> + ovinfo = &ovinfo_tab[i]; >> + >> + of_node_put(ovinfo->target); >> + of_node_put(ovinfo->overlay); >> + } >> + kfree(ovinfo_tab); >> + >> + return 0; >> +} >> + >> diff --git a/include/linux/of.h b/include/linux/of.h >> index 22d42e5..d9c8130 100644 >> --- a/include/linux/of.h >> +++ b/include/linux/of.h >> @@ -23,6 +23,7 @@ >> #include >> #include >> #include >> +#include >> >> #include >> #include >> @@ -738,4 +739,116 @@ static inline int of_resolve(struct device_node *resolve) >> >> #endif >> >> +/** >> + * Overlay support >> + */ >> + >> +/** >> + * struct of_overlay_log_entry - Holds a DT log entry >> + * @node: list_head for the log list >> + * @action: notifier action >> + * @np: pointer to the device node affected >> + * @prop: pointer to the property affected >> + * @old_prop: hold a pointer to the original property >> + * >> + * Every modification of the device tree during application of the >> + * overlay is held in a list of of_overlay_log_entry structures. >> + * That way we can recover from a partial application, or we can >> + * revert the overlay properly. >> + */ >> +struct of_overlay_log_entry { >> + struct list_head node; >> + unsigned long action; >> + struct device_node *np; >> + struct property *prop; >> + struct property *old_prop; >> +}; >> + >> +/** >> + * struct of_overlay_device_entry - Holds an overlay device entry >> + * @node: list_head for the device list >> + * @np: device node pointer to the device node affected >> + * @pdev: pointer to the platform device affected >> + * @client: pointer to the I2C client device affected >> + * @state: new device state >> + * @prevstate: previous device state >> + * >> + * When the overlay results in a device node's state to change this >> + * fact is recorded in a list of device entries. After the overlay >> + * is applied we can create/destroy the platform devices according >> + * to the new state of the live tree. >> + */ >> +struct of_overlay_device_entry { >> + struct list_head node; >> + struct device_node *np; >> + struct platform_device *pdev; >> + struct i2c_client *client; >> + int prevstate; >> + int state; >> +}; >> + >> +/** >> + * struct of_overlay_info - Holds a single overlay info >> + * @target: target of the overlay operation >> + * @overlay: pointer to the overlay contents node >> + * @lock: Lock to hold when accessing the lists >> + * @le_list: List of the overlay logs >> + * @de_list: List of the overlay records >> + * @notifier: of reconfiguration notifier >> + * >> + * Holds a single overlay state, including all the overlay logs & >> + * records. >> + */ >> +struct of_overlay_info { >> + struct device_node *target; >> + struct device_node *overlay; >> + struct mutex lock; >> + struct list_head le_list; >> + struct list_head de_list; >> + struct notifier_block notifier; >> + int device_depth; >> +}; >> + >> +#ifdef CONFIG_OF_OVERLAY >> + >> +int of_overlay(int count, struct of_overlay_info *ovinfo_tab); >> +int of_overlay_revert(int count, struct of_overlay_info *ovinfo_tab); >> + >> +int of_fill_overlay_info(struct device_node *info_node, >> + struct of_overlay_info *ovinfo); >> +int of_build_overlay_info(struct device_node *tree, >> + int *cntp, struct of_overlay_info **ovinfop); >> +int of_free_overlay_info(int cnt, struct of_overlay_info *ovinfo); >> + >> +#else >> + >> +static inline int of_overlay(int count, struct of_overlay_info *ovinfo_tab) >> +{ >> + return -ENOTSUPP; >> +} >> + >> +static inline int of_overlay_revert(int count, struct of_overlay_info *ovinfo_tab) >> +{ >> + return -ENOTSUPP; >> +} >> + >> +static inline int of_fill_overlay_info(struct device_node *info_node, >> + struct of_overlay_info *ovinfo) >> +{ >> + return -ENOTSUPP; >> +} >> + >> +static inline int of_build_overlay_info(struct device_node *tree, >> + int *cntp, struct of_overlay_info **ovinfop) >> +{ >> + return -ENOTSUPP; >> +} >> + >> +int of_free_overlay_info(int cnt, struct of_overlay_info *ovinfo) >> +{ >> + return -ENOTSUPP; >> +} >> + >> +#endif >> + >> #endif /* _LINUX_OF_H */ >> >