From patchwork Tue Aug 1 09:23:48 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 9873987 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 EF93F603B4 for ; Tue, 1 Aug 2017 09:39:37 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E09E52869F for ; Tue, 1 Aug 2017 09:39:37 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id D423C2869A; Tue, 1 Aug 2017 09:39:37 +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,RCVD_IN_DNSWL_NONE autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [65.50.211.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 2DFB8206E2 for ; Tue, 1 Aug 2017 09:39:36 +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=dArjY2NIMzSN65d1f6T7me1GnHqHg5tsagNBxJGtp74=; b=cwpZa1zcXISHLjelHGTuk8i3DM eNqD/0mUj4b6GUXMixO3EG9xa8/tAn/sGC7zWRH1oK+FqeChlVaXprfsTdR3bdkzanVF6HiI2GrIf P6w7zEIdlGv5gWq62Sh7Avj30eJ7ApOlUhKJkAADDXuWAiOBIKdbSVOEYrDgkk9xGGwVq36X6RyvL S2xF868hKAzR8IiV7XWKyT2Rr/ns6ENoS0FfS0Cp8LnvUyZ8UVKhvPJk65P8PryNHvg+iTNmFqyox 5+mZaJJN5Gv8rFgig/hEYl/p0OXmdgBxMSsMug2V3JHqh8EF74Brq1kYKuBD9cLSZdFS5GcoUc3zN OV9CoVxQ==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.87 #1 (Red Hat Linux)) id 1dcTda-0000mQ-95; Tue, 01 Aug 2017 09:38:50 +0000 Received: from mail-pf0-x234.google.com ([2607:f8b0:400e:c00::234]) by bombadil.infradead.org with esmtps (Exim 4.87 #1 (Red Hat Linux)) id 1dcTUZ-00024e-0X for linux-arm-kernel@lists.infradead.org; Tue, 01 Aug 2017 09:29:44 +0000 Received: by mail-pf0-x234.google.com with SMTP id z129so5529949pfb.3 for ; Tue, 01 Aug 2017 02:29:10 -0700 (PDT) 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=X3NxMmF0TAnr17DvQAQ3pYgH0rtWaQ06KjoMdYLurRQ=; b=d7ZpyqAurY/DJ5X+exA6qKhiVZf16bX7a2sF++s59ETMwQ42nd+gj7tQtRiya3Fnwq /5LDUMVhWYQt9QnWSKlHZog7n82UXR8188PzsS+IhYnl9glf+N/Skr6g27JwUci2cxVy tBj3Pp+GQiwkDjEr3pviqSAeuVoJgKV3IzaxA= 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=X3NxMmF0TAnr17DvQAQ3pYgH0rtWaQ06KjoMdYLurRQ=; b=pFfcf5TFijNPOGQFFtn3pIaKGgJ/X+o7lj0bhQi0uPqZvN9p2/q+TrSWjMNrfgQMP4 s37DXzM4ZlFlH9GooNp0Cn7UYgONWCxrwnAYgJKlesFfmh6mTIeBoCvx5FRHVvgq0bEm tjabAZuGWlw368ajwOJgDOthGACHVGqhjpMCbOK4Sq7W7XDG+0BmU56kKCr55hZZKfHj rsRQti9T9fCcqq5CJb+R8RbXdqTZ08nxXeeRug5LeWz4h5gcJ9oRzk00lpMJHP8lR7mI p8DaklmqatcwIkmxDEC9cmGQxYtqPONnecZm3HBg5iPt0K8EEE4YI7pgtw3Ac7hIZ1G4 PL1w== X-Gm-Message-State: AIVw111NxN6X6J5IwVLGHVcAFgH9jv64UPP+y/v/rbJGcaz0AtkTlwz/ NXzK3EvgPfRB/bIEYH0anA== X-Received: by 10.84.168.131 with SMTP id f3mr20079288plb.220.1501579750109; Tue, 01 Aug 2017 02:29:10 -0700 (PDT) Received: from localhost ([122.172.27.66]) by smtp.gmail.com with ESMTPSA id b4sm51097210pgc.9.2017.08.01.02.29.09 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 01 Aug 2017 02:29:09 -0700 (PDT) From: Viresh Kumar To: Greg Kroah-Hartman Subject: [PATCH V3 7/8] drivers: boot_constraint: Manage deferrable constraints Date: Tue, 1 Aug 2017 14:53:48 +0530 Message-Id: <79e1a28ab90d0bb88c47c6179f57542e9d197443.1501578037.git.viresh.kumar@linaro.org> X-Mailer: git-send-email 2.13.0.71.gd7076ec9c9cb In-Reply-To: References: In-Reply-To: References: X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20170801_022931_455462_0D340DE8 X-CRM114-Status: GOOD ( 25.27 ) 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: Vincent Guittot , Viresh Kumar , Stephen Boyd , linux-kernel@vger.kernel.org, robdclark@gmail.com, Mark Brown , Rajendra Nayak , Shiraz Hashim , 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 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/base.h | 1 + drivers/base/boot_constraints/Makefile | 2 +- drivers/base/boot_constraints/core.c | 2 +- drivers/base/boot_constraints/core.h | 2 + drivers/base/boot_constraints/deferrable_dev.c | 192 +++++++++++++++++++++++++ drivers/base/dd.c | 12 ++ include/linux/boot_constraint.h | 10 ++ 7 files changed, 219 insertions(+), 2 deletions(-) create mode 100644 drivers/base/boot_constraints/deferrable_dev.c diff --git a/drivers/base/base.h b/drivers/base/base.h index 539432a14b5c..9c117f0dc44c 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -131,6 +131,7 @@ extern char *make_class_name(const char *name, struct kobject *kobj); extern int devres_release_all(struct device *dev); extern void device_block_probing(void); extern void device_unblock_probing(void); +extern void driver_enable_deferred_probe(void); /* /sys/devices directory */ extern struct kset *devices_kset; diff --git a/drivers/base/boot_constraints/Makefile b/drivers/base/boot_constraints/Makefile index b7ade1a7afb5..a765094623a3 100644 --- a/drivers/base/boot_constraints/Makefile +++ b/drivers/base/boot_constraints/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/base/boot_constraints/core.c b/drivers/base/boot_constraints/core.c index c0e3a85ff85a..8e2f9b8fe80f 100644 --- a/drivers/base/boot_constraints/core.c +++ b/drivers/base/boot_constraints/core.c @@ -24,7 +24,7 @@ static LIST_HEAD(constraint_devices); static DEFINE_MUTEX(constraint_devices_mutex); -static bool boot_constraints_disabled; +bool boot_constraints_disabled; static int __init constraints_disable(char *str) { diff --git a/drivers/base/boot_constraints/core.h b/drivers/base/boot_constraints/core.h index ee84e237c66a..41354f7206bf 100644 --- a/drivers/base/boot_constraints/core.h +++ b/drivers/base/boot_constraints/core.h @@ -32,6 +32,8 @@ struct constraint { void *private; }; +extern bool boot_constraints_disabled; + void constraint_add_debugfs(struct constraint *constraint, const char *suffix); void constraint_remove_debugfs(struct constraint *constraint); diff --git a/drivers/base/boot_constraints/deferrable_dev.c b/drivers/base/boot_constraints/deferrable_dev.c new file mode 100644 index 000000000000..5169882f2af1 --- /dev/null +++ b/drivers/base/boot_constraints/deferrable_dev.c @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2017 Linaro. + * Viresh Kumar + * + * This file is released under the GPLv2. + */ + +#define pr_fmt(fmt) "Boot Constraints: " fmt + +#include +#include +#include +#include +#include +#include + +#include "../base.h" +#include "core.h" + +static DEFINE_IDA(pdev_index); + +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; +} + +int dev_boot_constraint_add_deferrable(struct device *dev, + struct dev_boot_constraint *constraints, int count) +{ + int ret, i; + + if (boot_constraints_disabled) + return -ENODEV; + + for (i = 0; i < count; i++) { + ret = _boot_constraint_add_dev(dev, &constraints[i]); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(dev_boot_constraint_add_deferrable); + +/* This only creates platform devices for now */ +int dev_boot_constraint_add_of_deferrable(const char *compatible, + struct dev_boot_constraint *constraints, int count) +{ + struct platform_device *pdev; + struct device_node *np; + + if (boot_constraints_disabled) + return -ENODEV; + + np = of_find_compatible_node(NULL, NULL, compatible); + if (!np) + return -ENODEV; + + pdev = of_find_device_by_node(np); + if (!pdev) + pdev = of_platform_device_create(np, NULL, NULL); + + of_node_put(np); + + if (!pdev) + return -ENODEV; + + return dev_boot_constraint_add_deferrable(&pdev->dev, constraints, + count); +} +EXPORT_SYMBOL_GPL(dev_boot_constraint_add_of_deferrable); diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 2262a4a4c0e4..62a8a22f8b04 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -204,6 +204,18 @@ void device_unblock_probing(void) } /** + * 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 * * We don't want to get in the way when the bulk of drivers are getting probed. diff --git a/include/linux/boot_constraint.h b/include/linux/boot_constraint.h index edc9abe7913a..f66c2f6d14dc 100644 --- a/include/linux/boot_constraint.h +++ b/include/linux/boot_constraint.h @@ -47,12 +47,22 @@ struct dev_boot_constraint_info { int dev_boot_constraint_add(struct device *dev, struct dev_boot_constraint_info *info); void dev_boot_constraints_remove(struct device *dev); +int dev_boot_constraint_add_deferrable(struct device *dev, + struct dev_boot_constraint *constraints, int count); +int dev_boot_constraint_add_of_deferrable(const char *compatible, + struct dev_boot_constraint *constraints, int count); #else static inline int dev_boot_constraint_add(struct device *dev, struct dev_boot_constraint_info *info); { return -EINVAL; } static inline void dev_boot_constraints_remove(struct device *dev) {} +static inline int dev_boot_constraint_add_deferrable(struct device *dev, + struct dev_boot_constraint *constraints, int count) +{ return -EINVAL; } +static inline int dev_boot_constraint_add_of_deferrable(const char *compatible, + struct dev_boot_constraint *constraints, int count) +{ return -EINVAL; } #endif /* CONFIG_DEV_BOOT_CONSTRAINTS */ #endif /* _LINUX_BOOT_CONSTRAINT_H */