From patchwork Wed Oct 26 14:57:52 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Antoine Tenart X-Patchwork-Id: 9397427 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id B16E860236 for ; Wed, 26 Oct 2016 15:01:38 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 901A2285F3 for ; Wed, 26 Oct 2016 15:01:38 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 84D3029BA2; Wed, 26 Oct 2016 15:01:38 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.2 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_MED autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id E3E72285F3 for ; Wed, 26 Oct 2016 15:01:37 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.85_2 #1 (Red Hat Linux)) id 1bzPgG-0005s5-Pu; Wed, 26 Oct 2016 14:59:52 +0000 Received: from up.free-electrons.com ([163.172.77.33] helo=mail.free-electrons.com) by bombadil.infradead.org with esmtp (Exim 4.85_2 #1 (Red Hat Linux)) id 1bzPer-00051n-E3 for linux-arm-kernel@lists.infradead.org; Wed, 26 Oct 2016 14:58:33 +0000 Received: by mail.free-electrons.com (Postfix, from userid 110) id 1E7CD20C55; Wed, 26 Oct 2016 16:58:00 +0200 (CEST) Received: from localhost (unknown [163.172.35.26]) by mail.free-electrons.com (Postfix) with ESMTPSA id D69D820790; Wed, 26 Oct 2016 16:57:59 +0200 (CEST) From: Antoine Tenart To: maxime.ripard@free-electrons.com, pantelis.antoniou@konsulko.com, mark.rutland@arm.com, sboyd@codeaurora.org Subject: [RFC PATCH 1/5] of: introduce the overlay manager Date: Wed, 26 Oct 2016 16:57:52 +0200 Message-Id: <20161026145756.21689-2-antoine.tenart@free-electrons.com> X-Mailer: git-send-email 2.10.1 In-Reply-To: <20161026145756.21689-1-antoine.tenart@free-electrons.com> References: <20161026145756.21689-1-antoine.tenart@free-electrons.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20161026_075825_928854_07A6C1A7 X-CRM114-Status: GOOD ( 21.42 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: thomas.petazzoni@free-electrons.com, devicetree@vger.kernel.org, Antoine Tenart , linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP The overlay manager is an in-kernel library helping to handle dt overlay loading when using capes. Signed-off-by: Antoine Tenart --- drivers/of/Kconfig | 2 + drivers/of/Makefile | 1 + drivers/of/overlay-manager/Kconfig | 6 + drivers/of/overlay-manager/Makefile | 1 + drivers/of/overlay-manager/overlay-manager.c | 199 +++++++++++++++++++++++++++ include/linux/overlay-manager.h | 38 +++++ 6 files changed, 247 insertions(+) create mode 100644 drivers/of/overlay-manager/Kconfig create mode 100644 drivers/of/overlay-manager/Makefile create mode 100644 drivers/of/overlay-manager/overlay-manager.c create mode 100644 include/linux/overlay-manager.h diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index bc07ad30c9bf..e57aeaf0bf4f 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -116,4 +116,6 @@ config OF_OVERLAY config OF_NUMA bool +source "drivers/of/overlay-manager/Kconfig" + endif # OF diff --git a/drivers/of/Makefile b/drivers/of/Makefile index d7efd9d458aa..d738fd41271f 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_OF_OVERLAY) += overlay.o obj-$(CONFIG_OF_NUMA) += of_numa.o obj-$(CONFIG_OF_UNITTEST) += unittest-data/ +obj-y += overlay-manager/ diff --git a/drivers/of/overlay-manager/Kconfig b/drivers/of/overlay-manager/Kconfig new file mode 100644 index 000000000000..eeb76054dcb8 --- /dev/null +++ b/drivers/of/overlay-manager/Kconfig @@ -0,0 +1,6 @@ +config OF_OVERLAY_MGR + bool "Device Tree Overlay Manager" + depends on OF_OVERLAY + help + Enable the overlay manager to handle automatic overlay loading when + devices are detected. diff --git a/drivers/of/overlay-manager/Makefile b/drivers/of/overlay-manager/Makefile new file mode 100644 index 000000000000..86d2b53950e7 --- /dev/null +++ b/drivers/of/overlay-manager/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_OF_OVERLAY_MGR) += overlay-manager.o diff --git a/drivers/of/overlay-manager/overlay-manager.c b/drivers/of/overlay-manager/overlay-manager.c new file mode 100644 index 000000000000..a725d7e24d38 --- /dev/null +++ b/drivers/of/overlay-manager/overlay-manager.c @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2016 - Antoine Tenart + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include + +struct overlay_mgr_overlay { + struct list_head list; + char *name; +}; + +LIST_HEAD(overlay_mgr_overlays); +LIST_HEAD(overlay_mgr_formats); +DEFINE_SPINLOCK(overlay_mgr_lock); +DEFINE_SPINLOCK(overlay_mgr_format_lock); + +/* + * overlay_mgr_register_format() + * + * Adds a new format candidate to the list of supported formats. The registered + * formats are used to parse the headers stored on the dips. + */ +int overlay_mgr_register_format(struct overlay_mgr_format *candidate) +{ + struct overlay_mgr_format *format; + int err = 0; + + spin_lock(&overlay_mgr_format_lock); + + /* Check if the format is already registered */ + list_for_each_entry(format, &overlay_mgr_formats, list) { + if (!strcpy(format->name, candidate->name)) { + err = -EEXIST; + goto err; + } + } + + list_add_tail(&candidate->list, &overlay_mgr_formats); + +err: + spin_unlock(&overlay_mgr_format_lock); + return err; +} +EXPORT_SYMBOL_GPL(overlay_mgr_register_format); + +/* + * overlay_mgr_parse() + * + * Parse raw data with registered format parsers. Fills the candidate string if + * one parser understood the raw data format. + */ +int overlay_mgr_parse(struct device *dev, void *data, char ***candidates, + unsigned *n) +{ + struct list_head *pos, *tmp; + struct overlay_mgr_format *format; + + list_for_each_safe(pos, tmp, &overlay_mgr_formats) { + format = list_entry(pos, struct overlay_mgr_format, list); + + format->parse(dev, data, candidates, n); + if (n > 0) + return 0; + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(overlay_mgr_parse); + +static int overlay_mgr_check_overlay(struct device_node *node) +{ + struct property *p; + const char *str = NULL; + + p = of_find_property(node, "compatible", NULL); + if (!p) + return -EINVAL; + + do { + str = of_prop_next_string(p, str); + if (of_machine_is_compatible(str)) + return 0; + } while (str); + + return -EINVAL; +} + +/* + * _overlay_mgr_insert() + * + * Try to request and apply an overlay given a candidate name. + */ +static int _overlay_mgr_apply(struct device *dev, char *candidate) +{ + struct overlay_mgr_overlay *overlay; + struct device_node *node; + const struct firmware *firmware; + char *firmware_name; + int err = 0; + + spin_lock(&overlay_mgr_lock); + + list_for_each_entry(overlay, &overlay_mgr_overlays, list) { + if (!strcmp(overlay->name, candidate)) { + dev_err(dev, "overlay already loaded\n"); + err = -EEXIST; + goto err_lock; + } + } + + overlay = devm_kzalloc(dev, sizeof(*overlay), GFP_KERNEL); + if (!overlay) { + err = -ENOMEM; + goto err_lock; + } + + overlay->name = candidate; + + firmware_name = kasprintf(GFP_KERNEL, "overlay-%s.dtbo", candidate); + if (!firmware_name) { + err = -ENOMEM; + goto err_free; + } + + dev_info(dev, "requesting firmware '%s'\n", firmware_name); + + err = request_firmware_direct(&firmware, firmware_name, dev); + if (err) { + dev_info(dev, "failed to request firmware '%s'\n", + firmware_name); + goto err_free; + } + + of_fdt_unflatten_tree((unsigned long *)firmware->data, NULL, &node); + if (!node) { + dev_err(dev, "failed to unflatted tree\n"); + err = -EINVAL; + goto err_fw; + } + + of_node_set_flag(node, OF_DETACHED); + + err = of_resolve_phandles(node); + if (err) { + dev_err(dev, "failed to resolve phandles: %d\n", err); + goto err_fw; + } + + err = overlay_mgr_check_overlay(node); + if (err) { + dev_err(dev, "overlay checks failed: %d\n", err); + goto err_fw; + } + + err = of_overlay_create(node); + if (err < 0) { + dev_err(dev, "failed to create overlay: %d\n", err); + goto err_fw; + } + + list_add_tail(&overlay->list, &overlay_mgr_overlays); + + dev_info(dev, "loaded firmware '%s'\n", firmware_name); + + spin_unlock(&overlay_mgr_lock); + return 0; + +err_fw: + release_firmware(firmware); +err_free: + devm_kfree(dev, overlay); +err_lock: + spin_unlock(&overlay_mgr_lock); + return err; +} + +int overlay_mgr_apply(struct device *dev, char **candidates, unsigned n) +{ + int i, ret; + + for (i=0; i < n; i++) { + ret = _overlay_mgr_apply(dev, candidates[i]); + if (!ret) + return 0; + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(overlay_mgr_apply); diff --git a/include/linux/overlay-manager.h b/include/linux/overlay-manager.h new file mode 100644 index 000000000000..8adcc4f5ddf6 --- /dev/null +++ b/include/linux/overlay-manager.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2016 - Antoine Tenart + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef __OVERLAY_MGR_H__ +#define __OVERLAY_MGR_H__ + +#include +#include +#include + +#define OVERLAY_MGR_DIP_MAX_SZ SZ_128 + +struct overlay_mgr_format { + struct list_head list; + char *name; + int (*parse)(struct device *dev, void *data, char ***candidates, + unsigned *n); +}; + +int overlay_mgr_register_format(struct overlay_mgr_format *candidate); +int overlay_mgr_parse(struct device *dev, void *data, char ***candidates, + unsigned *n); +int overlay_mgr_apply(struct device *dev, char **candidates, unsigned n); + +#define dip_convert(field) \ + ( \ + (sizeof(field) == 1) ? field : \ + (sizeof(field) == 2) ? be16_to_cpu(field) : \ + (sizeof(field) == 4) ? be32_to_cpu(field) : \ + -1 \ + ) + +#endif /* __OVERLAY_MGR_H__ */