From patchwork Fri Feb 23 10:23:47 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 10237405 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 8F3D360209 for ; Fri, 23 Feb 2018 10:47:02 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 7AA9D29513 for ; Fri, 23 Feb 2018 10:47:02 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 6F6D229515; Fri, 23 Feb 2018 10:47:02 +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=-1.9 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id B1C8C29513 for ; Fri, 23 Feb 2018 10:47:01 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:References: In-Reply-To:Message-Id:Date:Subject:To:From:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=BltXFTHr6wWmzuV4Q9UqEb2E3Nh8El2deCHUciYGqUk=; b=Sh4G3xkrSNG2eAF2xAJyHehMRj mnA50Erc+2k5JiestbCsYfQClXxbSVrYRRRGfgoePnKcHFKIIHefFJN+c/hQPD+4nxzEZ5UZlGCZY I6iB8JIPZXo5DDT4xG0VLUBCNbw3XFMbv/+m10B9e4rb9vVmVGI6a7oNTvr9seFpb9Z+NcHrNMj7B lpLysJ5/so49BHqiIr8/azrNEJiA6+WBVG80Jmop5DgGU6SqvE18uib0dzh8KRJvjXQGyXe97NhHx nvSox+HdEdPQ4lkR6wBl3zKc04TpPvByhG1s0hptjP+KO3uTWWQIMdAcA2t195Mk8aNzWzDBKE7qU qvouIuUg==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.89 #1 (Red Hat Linux)) id 1epAsK-0006xc-FA; Fri, 23 Feb 2018 10:46:48 +0000 Received: from merlin.infradead.org ([2001:8b0:10b:1231::1]) by bombadil.infradead.org with esmtps (Exim 4.89 #1 (Red Hat Linux)) id 1epAnO-0002ym-8E for linux-arm-kernel@bombadil.infradead.org; Fri, 23 Feb 2018 10:41:42 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=infradead.org; s=merlin.20170209; h=References:In-Reply-To:Message-Id:Date: Subject:Cc:To:From:Sender:Reply-To:MIME-Version:Content-Type: Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Id: List-Help:List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive; bh=cZWpDkD2kN3WHB175w1F2nQGLpuE0VK/FoyazSQXwcs=; b=Qfnm2IcFm57gN6Ajp3cdWQ0xW qifMhCYHOj1dbMK/YP38iR1CR04y4LxA9MB/EjPyErUJ+fBfH0x28R6aruEFja8rtriyIPouXygXP 7OQQBxgUsSX+jldvaitvE915uxFjCLCPjYibQiA47hBY0tjkxojdn8zHykZc2WYInB2jyRaD2Tnac S2aRbwjjXdX1+Gi99t5ThMXbWFA38dLs6RNbiTsDAoNfudkLfAZtE0cGhhXvFzckSogDUi+I2gucJ as3iTCywTcHPpDwjzhbvTIxhrTh9ApVEjojAdqIM5kWDUmf6X06oyp9hRaXnmXcw2u9BJVZM1795a 04kyhevbQ==; Received: from mail-pf0-x244.google.com ([2607:f8b0:400e:c00::244]) by merlin.infradead.org with esmtps (Exim 4.89 #1 (Red Hat Linux)) id 1epAWs-0007AN-QI for linux-arm-kernel@lists.infradead.org; Fri, 23 Feb 2018 10:24:39 +0000 Received: by mail-pf0-x244.google.com with SMTP id z24so3336915pfh.13 for ; Fri, 23 Feb 2018 02:24:28 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :in-reply-to:references; bh=cZWpDkD2kN3WHB175w1F2nQGLpuE0VK/FoyazSQXwcs=; b=NhCHa6PKhZ2kc4+3osFErmAABkZQYESeRqLGR67Mrgh3k5MNDqPxHpLYIaSUh6WD65 zkDud8lzjbNaYrCBDV2l/XoPvhlJedR4kKnutEiaxeeOoD+3SA6c/9rUyfhWHcUwZVI3 Cx3rIoQPqdSTx9Uz2McgpmthLy0q+m0ueoYBQ= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:in-reply-to:references; bh=cZWpDkD2kN3WHB175w1F2nQGLpuE0VK/FoyazSQXwcs=; b=UGaave/OnNhXo20btXJeoi2Gd7p0/uczkU+7wCP4wugien1grjmbq/OCVX/JlY/xYg bjRfHXD47zuXeLzh6kDsFPRzMl0PebFHbnnlIyYMqItAJcLOPpNoTDGHmFqpaev57MPQ LkH1eyWJMdQ8SRfN5xEEk5B1fZTISrA7F347PRhq3zUX4+wuASGPHunPwqsojaRNaHcz 7fOn2C+FFOB7Lx1L487KMopuiDJ6bIPp7iAfrYeWdrbEDeodUIOteaPAH9Qk83vKhmNB yeXAOq9cJV9RW/1sgQHQiJc/9uoKua1nLObqFkrZRHD2PYl55ELi3UTvpsIQTHcWQqzU gCcg== X-Gm-Message-State: APf1xPB9mXBeXBAWJFF33CPIrVQdF/333MDXUoYMg4cYs3Gtm68LHuRg MNMdLl2o50ulqIRShc+goKGtLg== X-Google-Smtp-Source: AH8x225OwCi3wZ64iO+qgkqie1BiQo/k43sIfO9Vcw5SSLy414a3yuX4XWh9wXh1gXFwjzV+5uQsbw== X-Received: by 10.98.72.10 with SMTP id v10mr1293274pfa.128.1519381466095; Fri, 23 Feb 2018 02:24:26 -0800 (PST) Received: from localhost ([122.167.232.138]) by smtp.gmail.com with ESMTPSA id a3sm4361730pfh.120.2018.02.23.02.24.25 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 23 Feb 2018 02:24:25 -0800 (PST) From: Viresh Kumar To: Greg Kroah-Hartman Subject: [PATCH V7 08/13] boot_constraint: Manage deferrable constraints Date: Fri, 23 Feb 2018 15:53:47 +0530 Message-Id: X-Mailer: git-send-email 2.15.0.194.g9af6a3dea062 In-Reply-To: References: In-Reply-To: References: X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20180223_052439_017560_CDFFA13C X-CRM114-Status: GOOD ( 33.41 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: nm@ti.com, Rajendra Nayak , Stephen Boyd , Viresh Kumar , s.hauer@pengutronix.de, linux-kernel@vger.kernel.org, xuwei5@hisilicon.com, olof@lixom.net, robdclark@gmail.com, robh+dt@kernel.org, fabio.estevam@nxp.com, Vincent Guittot , shawnguo@kernel.org, linux-arm-kernel@lists.infradead.org, l.stach@pengutronix.de 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 It is possible that some of the resources aren't available at the time constraints are getting set and the boot constraints core will return -EPROBE_DEFER for them. In order to retry adding the constraints at a later point of time (after the resource is added and before any of its users come up), this patch proposes two things: - Each constraint is represented by a virtual platform device, so that it is re-probed again until the time all the dependencies aren't met. The platform device is removed along with the constraint, with help of the free_resources() callback. - Enable early defer probing support by calling driver_enable_deferred_probe(), so that the core retries probing deferred devices every time any device is bound to a driver. This makes sure that the constraint is set before any of the users of the resources come up. This is tested on ARM64 Hikey board where probe was deferred for a device. Tested-by: Rajendra Nayak Signed-off-by: Viresh Kumar --- drivers/base/dd.c | 12 ++ drivers/bootconstraint/Makefile | 2 +- drivers/bootconstraint/deferrable_dev.c | 241 ++++++++++++++++++++++++++++++++ include/linux/boot_constraint.h | 27 ++++ 4 files changed, 281 insertions(+), 1 deletion(-) create mode 100644 drivers/bootconstraint/deferrable_dev.c diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 777267aa1710..482d971ceac6 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -227,6 +227,18 @@ void device_unblock_probing(void) driver_deferred_probe_trigger(); } +/** + * driver_enable_deferred_probe() - Enable probing of deferred devices + * + * We don't want to get in the way when the bulk of drivers are getting probed + * and so deferred probe is disabled in the beginning. Enable it now because we + * need it. + */ +void driver_enable_deferred_probe(void) +{ + driver_deferred_probe_enable = true; +} + /** * deferred_probe_initcall() - Enable probing of deferred devices * diff --git a/drivers/bootconstraint/Makefile b/drivers/bootconstraint/Makefile index b7ade1a7afb5..a765094623a3 100644 --- a/drivers/bootconstraint/Makefile +++ b/drivers/bootconstraint/Makefile @@ -1,3 +1,3 @@ # Makefile for device boot constraints -obj-y := clk.o core.o pm.o supply.o +obj-y := clk.o deferrable_dev.o core.o pm.o supply.o diff --git a/drivers/bootconstraint/deferrable_dev.c b/drivers/bootconstraint/deferrable_dev.c new file mode 100644 index 000000000000..34c493812869 --- /dev/null +++ b/drivers/bootconstraint/deferrable_dev.c @@ -0,0 +1,241 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Linaro. + * Viresh Kumar + */ + +#include +#include +#include +#include +#include +#include + +#include "core.h" + +static DEFINE_IDA(pdev_index); + +void driver_enable_deferred_probe(void); + +struct boot_constraint_pdata { + struct device *dev; + struct dev_boot_constraint constraint; + int probe_failed; + int index; +}; + +static void boot_constraint_remove(void *data) +{ + struct platform_device *pdev = data; + struct boot_constraint_pdata *pdata = dev_get_platdata(&pdev->dev); + + ida_simple_remove(&pdev_index, pdata->index); + kfree(pdata->constraint.data); + platform_device_unregister(pdev); +} + +/* + * A platform device is added for each and every constraint, to handle + * -EPROBE_DEFER properly. + */ +static int boot_constraint_probe(struct platform_device *pdev) +{ + struct boot_constraint_pdata *pdata = dev_get_platdata(&pdev->dev); + struct dev_boot_constraint_info info; + int ret; + + if (WARN_ON(!pdata)) + return -EINVAL; + + info.constraint = pdata->constraint; + info.free_resources = boot_constraint_remove; + info.free_resources_data = pdev; + + ret = dev_boot_constraint_add(pdata->dev, &info); + if (ret) { + if (ret == -EPROBE_DEFER) + driver_enable_deferred_probe(); + else + pdata->probe_failed = ret; + } + + return ret; +} + +static struct platform_driver boot_constraint_driver = { + .driver = { + .name = "boot-constraints-dev", + }, + .probe = boot_constraint_probe, +}; + +static int __init boot_constraint_init(void) +{ + return platform_driver_register(&boot_constraint_driver); +} +core_initcall(boot_constraint_init); + +static int boot_constraint_add_dev(struct device *dev, + struct dev_boot_constraint *constraint) +{ + struct boot_constraint_pdata pdata = { + .dev = dev, + .constraint.type = constraint->type, + }; + struct platform_device *pdev; + struct boot_constraint_pdata *pdev_pdata; + int size, ret; + + switch (constraint->type) { + case DEV_BOOT_CONSTRAINT_CLK: + size = sizeof(struct dev_boot_constraint_clk_info); + break; + case DEV_BOOT_CONSTRAINT_PM: + size = 0; + break; + case DEV_BOOT_CONSTRAINT_SUPPLY: + size = sizeof(struct dev_boot_constraint_supply_info); + break; + default: + dev_err(dev, "%s: Constraint type (%d) not supported\n", + __func__, constraint->type); + return -EINVAL; + } + + /* Will be freed from boot_constraint_remove() */ + pdata.constraint.data = kmemdup(constraint->data, size, GFP_KERNEL); + if (!pdata.constraint.data) + return -ENOMEM; + + ret = ida_simple_get(&pdev_index, 0, 256, GFP_KERNEL); + if (ret < 0) { + dev_err(dev, "failed to allocate index (%d)\n", ret); + goto free; + } + + pdata.index = ret; + + pdev = platform_device_register_data(NULL, "boot-constraints-dev", ret, + &pdata, sizeof(pdata)); + if (IS_ERR(pdev)) { + dev_err(dev, "%s: Failed to create pdev (%ld)\n", __func__, + PTR_ERR(pdev)); + ret = PTR_ERR(pdev); + goto ida_remove; + } + + /* Release resources if probe has failed */ + pdev_pdata = dev_get_platdata(&pdev->dev); + if (pdev_pdata->probe_failed) { + ret = pdev_pdata->probe_failed; + goto remove_pdev; + } + + return 0; + +remove_pdev: + platform_device_unregister(pdev); +ida_remove: + ida_simple_remove(&pdev_index, pdata.index); +free: + kfree(pdata.constraint.data); + + return ret; +} + +static int dev_boot_constraint_add_deferrable(struct device *dev, + struct dev_boot_constraint *constraints, int count) +{ + int ret, i; + + for (i = 0; i < count; i++) { + ret = boot_constraint_add_dev(dev, &constraints[i]); + if (ret) + return ret; + } + + return 0; +} + +/* This only creates platform devices for now */ +static void add_deferrable_of_single(struct device_node *np, + struct dev_boot_constraint *constraints, + int count) +{ + struct device *dev; + int ret; + + if (!of_device_is_available(np)) + return; + + ret = of_platform_bus_create(np, NULL, NULL, NULL, false); + if (ret) + return; + + dev = of_find_any_device_by_node(np); + if (!dev) { + pr_err("Boot Constraints: Failed to find dev: %pOF\n", np); + return; + } + + ret = dev_boot_constraint_add_deferrable(dev, constraints, count); + if (ret) + dev_err(dev, "Failed to add boot constraint (%d)\n", ret); +} + +/* Not all compatible device nodes may have boot constraints */ +static bool node_has_boot_constraints(struct device_node *np, + struct dev_boot_constraint_of *oconst) +{ + int i; + + if (!oconst->dev_names) + return true; + + for (i = 0; i < oconst->dev_names_count; i++) { + if (!strcmp(oconst->dev_names[i], kbasename(np->full_name))) + return true; + } + + return false; +} + +/** + * dev_boot_constraint_add_deferrable_of: Adds all constraints for a platform. + * + * @oconst: This is an array of 'struct dev_boot_constraint_of', where each + * entry of the array is used to add one or more boot constraints across one or + * more devices having the same compatibility in the device tree. + * @count: Size of the 'oconst' array. + * + * This helper routine provides an easy way to add all boot constraints for a + * machine or platform. Just like dev_boot_constraint_add(), this must be called + * before the devices (to which we want to add constraints) are probed by their + * drivers, otherwise the boot constraint will never get removed for those + * devices and may result in unwanted behavior of the hardware. The boot + * constraints are removed by the driver core automatically after the devices + * are probed (successfully or unsuccessfully). + * + * This adds the boot constraints in a deferrable way and the caller need not + * worry about the availability of the resources required by the constraint. + * This routine will return successfully and the constraint will be added by the + * boot constraint core as soon as the resource is available at a later point in + * time. + */ +void dev_boot_constraint_add_deferrable_of(struct dev_boot_constraint_of *oconst, + int count) +{ + struct device_node *np; + int i; + + for (i = 0; i < count; i++) { + for_each_compatible_node(np, NULL, oconst[i].compat) { + if (!node_has_boot_constraints(np, &oconst[i])) + continue; + + add_deferrable_of_single(np, oconst[i].constraints, + oconst[i].count); + } + } +} +EXPORT_SYMBOL_GPL(dev_boot_constraint_add_deferrable_of); diff --git a/include/linux/boot_constraint.h b/include/linux/boot_constraint.h index fbccb62f423d..91cbcc66b2c3 100644 --- a/include/linux/boot_constraint.h +++ b/include/linux/boot_constraint.h @@ -79,16 +79,43 @@ struct dev_boot_constraint_info { void *free_resources_data; }; +/** + * struct dev_boot_constraint_of - This is used to add one or more boot + * constraints across one or more devices having the same compatibility in the + * device tree. + * + * @compat: This must match the compatible string of the devices to which we + * want to apply constraints. + * @constraints: This points to one or more boot constraints. + * @count: This contains the number of boot constraints pointed by the + * 'constraints' field. + * @dev_names: This is used to limit the application of boot constraints to only + * a subset of devices with matching compatibility. + * @dev_names_count: This is the number of devices pointed by the 'dev_names' + * array. + */ +struct dev_boot_constraint_of { + const char *compat; + struct dev_boot_constraint *constraints; + unsigned int count; + + const char * const *dev_names; + unsigned int dev_names_count; +}; + #ifdef CONFIG_DEV_BOOT_CONSTRAINT int dev_boot_constraint_add(struct device *dev, struct dev_boot_constraint_info *info); void dev_boot_constraints_remove(struct device *dev); +void dev_boot_constraint_add_deferrable_of(struct dev_boot_constraint_of *oconst, + int count); #else static inline int dev_boot_constraint_add(struct device *dev, struct dev_boot_constraint_info *info) { return 0; } static inline void dev_boot_constraints_remove(struct device *dev) {} +static inline void dev_boot_constraint_add_deferrable_of(struct dev_boot_constraint_of *oconst, int count) {} #endif /* CONFIG_DEV_BOOT_CONSTRAINT */ #endif /* _LINUX_BOOT_CONSTRAINT_H */