From patchwork Wed Jul 12 06:34:31 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 107443 Delivered-To: patch@linaro.org Received: by 10.140.101.44 with SMTP id t41csp432472qge; Tue, 11 Jul 2017 23:35:00 -0700 (PDT) X-Received: by 10.99.185.28 with SMTP id z28mr2355045pge.123.1499841300080; Tue, 11 Jul 2017 23:35:00 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1499841300; cv=none; d=google.com; s=arc-20160816; b=eRHkulOXDlGP/7HdkuZGn+11TxuoduJPlljZeUHTHtsqzcCAjg8XpVCYFkbtfKMp+m TGRtJK04onmjGcAsh2lmakK+4Ss8d64BaBfSaJ8YlegLn18x33RaQC8/fjclXmsf6hOM i36A2ZmlRhg0XwYpaesIIu2yHdDIUp8AlXddRKOzzusVQd/i1ik8ihrmfVIfvJGpZ7rx YPXF+pJnoNBVkCKPr/N4AxFpSs+tn1Ftmgj7rH1Gs13d5i3skJSNw7w0ygC2dTPXhZ7H lE40nUlN91IIW4egyheqMDskSj6fZegYmN2WU13LkcimUHqdRwDXiYGkHcgMiBdp5Hs4 qr2w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:dkim-signature :arc-authentication-results; bh=IToC8rz7lvMckfGIP9vtma0LUJG6whPNvJQ95dwPPt4=; b=ny+iDUhrumnYWFuTn5hw8AICznIGRcNZI09tpeCB5LxzYY3LQkTirbr2pCsEgUhSHv 122fIchQZVrfAInD57xnl6qAvhxs/KosoAzGY3wcEPxqK6znf6BzlcQzIa71DgrHcuTP uIlyjacxqzUYeIjZw/GW4lNaUPUWo0uPaVs46aN04KyHPI/VyHjzjMu0yI7MsonE17Lj m+lX6xv3nYASfoWZaNz5O29AdSSvF/Hs/uqUsj2qsHCNxY/cxBi25ibZD0nuW9nX9GWV w4fjc1K5spIwSb28wWP+F6Gm4Gs2WWsOKPyestw1PtBpo9UxQ/dzT8wwa7cuYqe0bAyg x2vA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.b=hUZKS3Hl; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id x13si1265814pgq.222.2017.07.11.23.34.59; Tue, 11 Jul 2017 23:35:00 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.b=hUZKS3Hl; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756667AbdGLGe5 (ORCPT + 25 others); Wed, 12 Jul 2017 02:34:57 -0400 Received: from mail-pg0-f43.google.com ([74.125.83.43]:34275 "EHLO mail-pg0-f43.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755899AbdGLGex (ORCPT ); Wed, 12 Jul 2017 02:34:53 -0400 Received: by mail-pg0-f43.google.com with SMTP id t186so8046892pgb.1 for ; Tue, 11 Jul 2017 23:34:47 -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=IToC8rz7lvMckfGIP9vtma0LUJG6whPNvJQ95dwPPt4=; b=hUZKS3Hlk+0x58NqL+v5TsjFvnPUWDp1yJSHK7+NyXdbHZQf8fbTm5Amp1ikGUMcrD n12UzWdy1gbimIn+JrZbJjD1zonW+dcXvG6htTH/UqfOzk+EOJAFcqMHiC12W7zYTdCa ALYgIvULiJk2EePUr3sx9NFckV5kw8Z2YDfF0= 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=IToC8rz7lvMckfGIP9vtma0LUJG6whPNvJQ95dwPPt4=; b=kQRaHRsXlo6ej0l5/GxBpfiU1QTwR+ejjBtEFya/ODqnbU+g+bwWJ/EvdozlTDhqOm 0wDzLvdMhkfaVAEWBz4LNIEm635T72bo5M2XXNYkpiS13guZAs0MrHtj3TsWEY+Oln18 mi+vZJzsw3pbB1QfV+MqM0jtQTc+bEaeJF2PeWh/lK5y4O8nt/2TJthX+GbSvgdcoB8S Tf7VLCWddJCPigr9At1YnvJsAtmgMuqQzzBd2UFIyi1ZCPda25d/Bmra3nF+9/VVj0Y/ mulYDQnt/X0S4+PaurJ4JdXHI3b1YzuN9AJmqJgkdeJF8/pQmyTZ22kOcPHwjAkvMP/y QgNg== X-Gm-Message-State: AIVw111ZFH3rRev3X4lffTMxWMWPEWYZsaMxSD1R4pPwfDMEvtZSCoU+ czQojAVzuYQCLr++ X-Received: by 10.84.229.13 with SMTP id b13mr2472433plk.1.1499841287252; Tue, 11 Jul 2017 23:34:47 -0700 (PDT) Received: from localhost ([122.167.138.108]) by smtp.gmail.com with ESMTPSA id i28sm2779556pfk.17.2017.07.11.23.34.46 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 11 Jul 2017 23:34:46 -0700 (PDT) From: Viresh Kumar To: Greg Kroah-Hartman Cc: Viresh Kumar , Vincent Guittot , Rob Herring , Mark Brown , Stephen Boyd , Rajendra Nayak , Shiraz Hashim , linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org Subject: [RFC v2 1/6] drivers: Add boot constraints core Date: Wed, 12 Jul 2017 12:04:31 +0530 Message-Id: X-Mailer: git-send-email 2.13.0.71.gd7076ec9c9cb In-Reply-To: References: In-Reply-To: References: Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Some devices are powered ON by the bootloader before the bootloader handovers control to Linux. It maybe important for those devices to keep working until the time a Linux device driver probes the device and reconfigure its resources. A typical example of that can be the LCD controller, which is used by the bootloaders to show image(s) while the platform is booting into Linux. The LCD controller can be using some resources, like clk, regulators, etc, that are shared between several devices. These shared resources should be configured to satisfy need of all the users. If another device's (X) driver gets probed before the LCD controller driver in this case, then it may end up reconfiguring these resources to ranges satisfying the current users (only device X) and that can make the LCD screen unstable. This patch introduces the concept of boot-constraints, which will be set by the bootloaders and the kernel will satisfy them until the time driver for such a device is probed (successfully or unsuccessfully). The list of boot constraint types is empty for now, and will be added by a later patch. Only two routines are exposed by the boot constraints core for now: - boot_constraint_add(): This will be called by parts of the kernel (before the device is probed) to set the constraints. - boot_constraints_remove(): This is called only by the driver core after a device is probed successfully or unsuccessfully. Special handling is done here for deffered probing. Signed-off-by: Viresh Kumar --- drivers/base/Kconfig | 10 ++ drivers/base/Makefile | 1 + drivers/base/boot_constraint.c | 219 ++++++++++++++++++++++++++++++++++++++++ drivers/base/dd.c | 20 ++-- include/linux/boot_constraint.h | 36 +++++++ 5 files changed, 279 insertions(+), 7 deletions(-) create mode 100644 drivers/base/boot_constraint.c create mode 100644 include/linux/boot_constraint.h -- 2.13.0.71.gd7076ec9c9cb diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index d718ae4b907a..0cf7cdd3081e 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -339,4 +339,14 @@ config CMA_ALIGNMENT endif +config BOOT_CONSTRAINTS + bool "Boot constraints for devices" + help + This enables boot constraints detection for devices. These constraints + are (normally) set by the Bootloader and must be satisfied by the + kernel until the relevant device driver is probed. Once the driver is + probed, the constraint is dropped. + + If unsure, say N. + endmenu diff --git a/drivers/base/Makefile b/drivers/base/Makefile index f2816f6ff76a..6094b3b75184 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -5,6 +5,7 @@ obj-y := component.o core.o bus.o dd.o syscore.o \ cpu.o firmware.o init.o map.o devres.o \ attribute_container.o transport_class.o \ topology.o container.o property.o cacheinfo.o +obj-$(CONFIG_BOOT_CONSTRAINTS) += boot_constraint.o obj-$(CONFIG_DEVTMPFS) += devtmpfs.o obj-$(CONFIG_DMA_CMA) += dma-contiguous.o obj-y += power/ diff --git a/drivers/base/boot_constraint.c b/drivers/base/boot_constraint.c new file mode 100644 index 000000000000..7dc7b6132765 --- /dev/null +++ b/drivers/base/boot_constraint.c @@ -0,0 +1,219 @@ +/* + * This takes care of boot time constraints, normally set by the Bootloader. + * + * 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 + +struct constraint { + struct constraint_dev *cdev; + struct list_head node; + enum boot_constraint_type type; + void (*free_resources)(void *data); + void *free_resources_data; + + int (*add)(struct constraint *constraint, void *data); + void (*remove)(struct constraint *constraint); + void *private; +}; + +struct constraint_dev { + struct device *dev; + struct list_head node; + struct list_head constraints; +}; + +#define for_each_constraint(_constraint, _temp, _cdev) \ + list_for_each_entry_safe(_constraint, _temp, &_cdev->constraints, node) + +/* Global list of all constraint devices currently registered */ +static LIST_HEAD(constraint_devices); +static DEFINE_MUTEX(constraint_devices_mutex); + +/* Forward declarations of constraints */ + + +/* Boot constraints core */ + +static struct constraint_dev *constraint_device_find(struct device *dev) +{ + struct constraint_dev *cdev; + + list_for_each_entry(cdev, &constraint_devices, node) { + if (cdev->dev == dev) + return cdev; + } + + return NULL; +} + +static struct constraint_dev *constraint_device_allocate(struct device *dev) +{ + struct constraint_dev *cdev; + + cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); + if (!cdev) + return ERR_PTR(-ENOMEM); + + cdev->dev = dev; + INIT_LIST_HEAD(&cdev->node); + INIT_LIST_HEAD(&cdev->constraints); + + list_add(&cdev->node, &constraint_devices); + + return cdev; +} + +static void constraint_device_free(struct constraint_dev *cdev) +{ + list_del(&cdev->node); + kfree(cdev); +} + +static struct constraint_dev *constraint_device_get(struct device *dev) +{ + struct constraint_dev *cdev; + + cdev = constraint_device_find(dev); + if (cdev) + return cdev; + + cdev = constraint_device_allocate(dev); + if (IS_ERR(cdev)) { + dev_err(dev, "Failed to add constraint dev (%ld)\n", + PTR_ERR(cdev)); + } + + return cdev; +} + +static void constraint_device_put(struct constraint_dev *cdev) +{ + if (!list_empty(&cdev->constraints)) + return; + + constraint_device_free(cdev); +} + +static struct constraint *constraint_allocate(struct constraint_dev *cdev, + enum boot_constraint_type type) +{ + struct constraint *constraint; + int (*add)(struct constraint *constraint, void *data); + void (*remove)(struct constraint *constraint); + + switch (type) { + default: + return ERR_PTR(-EINVAL); + } + + constraint = kzalloc(sizeof(*constraint), GFP_KERNEL); + if (!constraint) + return ERR_PTR(-ENOMEM); + + constraint->cdev = cdev; + constraint->type = type; + constraint->add = add; + constraint->remove = remove; + INIT_LIST_HEAD(&constraint->node); + + list_add(&constraint->node, &cdev->constraints); + + return constraint; +} + +static void constraint_free(struct constraint *constraint) +{ + list_del(&constraint->node); + kfree(constraint); +} + +int boot_constraint_add(struct device *dev, enum boot_constraint_type type, + struct boot_constraint_info *info) +{ + struct constraint_dev *cdev; + struct constraint *constraint; + int ret; + + mutex_lock(&constraint_devices_mutex); + + /* Find or add the cdev type first */ + cdev = constraint_device_get(dev); + if (IS_ERR(cdev)) { + ret = PTR_ERR(cdev); + goto unlock; + } + + constraint = constraint_allocate(cdev, type); + if (IS_ERR(constraint)) { + dev_err(dev, "Failed to add constraint type: %d (%ld)\n", type, + PTR_ERR(constraint)); + ret = PTR_ERR(constraint); + goto put_cdev; + } + + constraint->free_resources = info->free_resources; + constraint->free_resources_data = info->free_resources_data; + + /* Set constraint */ + ret = constraint->add(constraint, info->constraint_info); + if (ret) + goto free_constraint; + + dev_dbg(dev, "Added boot constraint-type (%d)\n", type); + + mutex_unlock(&constraint_devices_mutex); + + return 0; + +free_constraint: + constraint_free(constraint); +put_cdev: + constraint_device_put(cdev); +unlock: + mutex_unlock(&constraint_devices_mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(boot_constraint_add); + +static void constraint_remove(struct constraint *constraint) +{ + constraint->remove(constraint); + + if (constraint->free_resources) + constraint->free_resources(constraint->free_resources_data); + + constraint_free(constraint); +} + +void boot_constraints_remove(struct device *dev) +{ + struct constraint_dev *cdev; + struct constraint *constraint, *temp; + + mutex_lock(&constraint_devices_mutex); + + cdev = constraint_device_find(dev); + if (!cdev) + goto unlock; + + for_each_constraint(constraint, temp, cdev) + constraint_remove(constraint); + + constraint_device_put(cdev); +unlock: + mutex_unlock(&constraint_devices_mutex); +} diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 4882f06d12df..4eb9d183d647 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -17,6 +17,7 @@ * This file is released under the GPLv2 */ +#include #include #include #include @@ -381,15 +382,20 @@ static int really_probe(struct device *dev, struct device_driver *drv) */ devices_kset_move_last(dev); - if (dev->bus->probe) { + if (dev->bus->probe) ret = dev->bus->probe(dev); - if (ret) - goto probe_failed; - } else if (drv->probe) { + else if (drv->probe) ret = drv->probe(dev); - if (ret) - goto probe_failed; - } + + /* + * Remove boot constraints for both successful and unsuccessful probe(), + * except for the case where EPROBE_DEFER is returned by probe(). + */ + if (ret != -EPROBE_DEFER) + boot_constraints_remove(dev); + + if (ret) + goto probe_failed; if (test_remove) { test_remove = false; diff --git a/include/linux/boot_constraint.h b/include/linux/boot_constraint.h new file mode 100644 index 000000000000..99f268447f72 --- /dev/null +++ b/include/linux/boot_constraint.h @@ -0,0 +1,36 @@ +/* + * Boot constraints header. + * + * Copyright (C) 2017 Linaro. + * Viresh Kumar + * + * This file is released under the GPLv2 + */ + +#include +#include + +struct device; + +enum boot_constraint_type { + BOOT_CONSTRAINT_NONE, +}; + +struct boot_constraint_info { + /* This will be called just before the constraint is removed */ + void (*free_resources)(void *data); + void *free_resources_data; + void *constraint_info; +}; + +#ifdef CONFIG_BOOT_CONSTRAINTS +int boot_constraint_add(struct device *dev, enum boot_constraint_type type, + struct boot_constraint_info *info); +void boot_constraints_remove(struct device *dev); +#else +static inline int boot_constraint_add(struct device *dev, + enum boot_constraint_type type, + struct boot_constraint_info *info) +{ return -EINVAL; } +static inline void boot_constraints_remove(struct device *dev) {} +#endif