From patchwork Thu Dec 14 15:33:10 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 121974 Delivered-To: patch@linaro.org Received: by 10.140.22.227 with SMTP id 90csp6960088qgn; Thu, 14 Dec 2017 07:34:09 -0800 (PST) X-Google-Smtp-Source: ACJfBovtcAMes6RrdTNVVcz2DsvDCjSYSHgeP7qL6pFgm6cBPqCvp+jHqwzpP+CT2m56m2ZuBKdK X-Received: by 10.99.60.83 with SMTP id i19mr5393589pgn.26.1513265649352; Thu, 14 Dec 2017 07:34:09 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1513265649; cv=none; d=google.com; s=arc-20160816; b=ocWT23C9kOeU/oJEkB3gXXIHMCw/1NQw8F80WrN1Z4eEZKcqIppwHP/9V/DUCHVoqV /Y2MY8GrT46rgkd9WunKJvSCUH4NsORaI81i/wTYjODR41qkOuooRhprS4DD58kDJiRb m1IPK5DAsBookcaKU0RBqwZObOqTutGvaGgvud+QF3U8I6qWUtZIHIzWqpqpSBEg4LYi cFsM8QVj/Fkn5ZTBU9VdybDLxYju/lu+gUOWXRKk67lhGKz6C2wvWQzOLYck4gNRf+x+ S31HwP57BYkGPS5jLW2qVhFlVzGBtwCKgdRz3ahaavsHaD6a5e2PfwOkNYU5hHpqnBvR RadA== 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=Q48q+6TjNvbTmyxVHj136e/nZ3VZyJzcyJOXTAoJhx4=; b=rCPMUy4YH5w/3McXliXLzC6hUvfGi2jDbwkxf3P+953N25MrHiP+zSn6ozS8o7aE0W y709sqsbL5XJeOPZRIoVylAjvEAephBHNRAYRO6ovhIp2bIabAnHhY8umfzqhgw/oR1a A0PHSBhFqTqZCRC2h4nvuY1l/VB7XKVJzoaU5MpkgFUOUM3ePmuU4iv8PmEWSeH4/cNo VkgCIpcgO4BgmC00p180IQ5d60NWgxEB+pjylCPI6TDkBdzAJ9Zj9VGYpI2sG2cXN59/ 9hb5wL+s1xa58dKR4ZFYLp/eK305sUlT9MiIOkyXnoZFvGMrJQLDw/Krr6RHYjoRYdG2 QVKQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=ESfY6KB+; 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 q12si3358439pls.475.2017.12.14.07.34.09; Thu, 14 Dec 2017 07:34:09 -0800 (PST) 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.s=google header.b=ESfY6KB+; 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 S1753416AbdLNPeD (ORCPT + 19 others); Thu, 14 Dec 2017 10:34:03 -0500 Received: from mail-pf0-f196.google.com ([209.85.192.196]:37438 "EHLO mail-pf0-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753264AbdLNPeA (ORCPT ); Thu, 14 Dec 2017 10:34:00 -0500 Received: by mail-pf0-f196.google.com with SMTP id n6so3851451pfa.4 for ; Thu, 14 Dec 2017 07:34:00 -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=Q48q+6TjNvbTmyxVHj136e/nZ3VZyJzcyJOXTAoJhx4=; b=ESfY6KB+6RPImGzzp1O3Jaum29FtT3uakojdtdfVxU28uTYlaJigF/N3cWBOO1VyBt K+U+Vnglw5c5klZ8S18AfAFBcU1NaUz/15salp+ltVHCfBVYYCpqa0CP426RvejqirHp SCCc7EvsIHAIfGr3fJAVmRWfLaaY7+k+o9h0Q= 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=Q48q+6TjNvbTmyxVHj136e/nZ3VZyJzcyJOXTAoJhx4=; b=UXNvztvugE17132xYVIzfi9z66YXbNwymk6X4pscglyVdkUytOUz6NsUUr6nQBAJKS 0BG+pDpVW+UlCSL3dZ1hunUyoDYH3DglgsppPG3mkWuZfSe+6M9I5QgrG316EjgIoJoh kfNpf2JTwrW3gSdY4t8M5LVF10Ys0WvAgNSoJ0qNUks4SJgt1SbYI0fhmwNrpCvxEwzA 4k84z9h/1co4BoU3joTNe8ZSDMZKbv7zJ7SBDyVqjykE1qz2MFr6Yiky1daSaMLBCn7R vUnhKoz46yIvI9+JqjuoCLp3C6zgLY95JRuuo5cZlZKk48yzEKbyJX7P8RJ0VkHGPshY n15g== X-Gm-Message-State: AKGB3mJLeGj6PehtoKYhfUjl9sRN3zd+QyDKItFlOuqs8R592bzFeotP ptkvWtljRREZd+PpatK/B+ZnQA== X-Received: by 10.98.11.71 with SMTP id t68mr9867381pfi.79.1513265639366; Thu, 14 Dec 2017 07:33:59 -0800 (PST) Received: from localhost ([122.172.99.7]) by smtp.gmail.com with ESMTPSA id t66sm8913566pfa.153.2017.12.14.07.33.58 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 14 Dec 2017 07:33:58 -0800 (PST) From: Viresh Kumar To: Greg Kroah-Hartman Cc: Viresh Kumar , Vincent Guittot , Stephen Boyd , Rajendra Nayak , linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, robdclark@gmail.com, s.hauer@pengutronix.de, l.stach@pengutronix.de, shawnguo@kernel.org, fabio.estevam@nxp.com, nm@ti.com, xuwei5@hisilicon.com, robh+dt@kernel.org Subject: [PATCH V5 03/13] drivers: Add boot constraints core Date: Thu, 14 Dec 2017 21:03:10 +0530 Message-Id: X-Mailer: git-send-email 2.15.0.194.g9af6a3dea062 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, PM domain, 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 incrementally updated by later patches. Only two routines are exposed by the boot constraints core for now: - dev_boot_constraint_add(): This shall be called by parts of the kernel (before the device is probed) to set the constraints. - dev_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 deferred probing. Tested-by: Rajendra Nayak Signed-off-by: Viresh Kumar --- drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/base/dd.c | 20 ++-- drivers/boot_constraint/Kconfig | 9 ++ drivers/boot_constraint/Makefile | 3 + drivers/boot_constraint/core.c | 219 +++++++++++++++++++++++++++++++++++++++ drivers/boot_constraint/core.h | 30 ++++++ include/linux/boot_constraint.h | 66 ++++++++++++ 8 files changed, 343 insertions(+), 7 deletions(-) create mode 100644 drivers/boot_constraint/Kconfig create mode 100644 drivers/boot_constraint/Makefile create mode 100644 drivers/boot_constraint/core.c create mode 100644 drivers/boot_constraint/core.h create mode 100644 include/linux/boot_constraint.h -- 2.15.0.194.g9af6a3dea062 diff --git a/drivers/Kconfig b/drivers/Kconfig index ef5fb8395d76..87ec868c74c5 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -5,6 +5,8 @@ source "drivers/amba/Kconfig" source "drivers/base/Kconfig" +source "drivers/boot_constraint/Kconfig" + source "drivers/bus/Kconfig" source "drivers/connector/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 7a2330077e47..7fd71b658aa7 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -73,6 +73,7 @@ obj-$(CONFIG_FB_INTEL) += video/fbdev/intelfb/ obj-$(CONFIG_PARPORT) += parport/ obj-$(CONFIG_NVM) += lightnvm/ obj-y += base/ block/ misc/ mfd/ nfc/ +obj-$(CONFIG_DEV_BOOT_CONSTRAINT) += boot_constraint/ obj-$(CONFIG_LIBNVDIMM) += nvdimm/ obj-$(CONFIG_DAX) += dax/ obj-$(CONFIG_DMA_SHARED_BUFFER) += dma-buf/ diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 533c82f55cea..dc89f98a2487 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -16,6 +16,7 @@ * Copyright (c) 2007-2009 Novell Inc. */ +#include #include #include #include @@ -419,15 +420,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) + dev_boot_constraints_remove(dev); + + if (ret) + goto probe_failed; if (test_remove) { test_remove = false; diff --git a/drivers/boot_constraint/Kconfig b/drivers/boot_constraint/Kconfig new file mode 100644 index 000000000000..9195f9a39fe2 --- /dev/null +++ b/drivers/boot_constraint/Kconfig @@ -0,0 +1,9 @@ +config DEV_BOOT_CONSTRAINT + 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. diff --git a/drivers/boot_constraint/Makefile b/drivers/boot_constraint/Makefile new file mode 100644 index 000000000000..0f2680177974 --- /dev/null +++ b/drivers/boot_constraint/Makefile @@ -0,0 +1,3 @@ +# Makefile for device boot constraints + +obj-y := core.o diff --git a/drivers/boot_constraint/core.c b/drivers/boot_constraint/core.c new file mode 100644 index 000000000000..45a0fbed1b11 --- /dev/null +++ b/drivers/boot_constraint/core.c @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This takes care of boot time device constraints, normally set by the + * Bootloader. + * + * Copyright (C) 2017 Linaro. + * Viresh Kumar + */ + +#include +#include +#include +#include + +#include "core.h" + +#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); + +/* 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 dev_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); +} + +/** + * dev_boot_constraint_add: Adds a boot constraint. + * + * @dev: Device for which the boot constraint is getting added. + * @info: Structure representing the boot constraint. + * + * This routine adds a single boot constraint for the device. This must be + * called before the device is probed by its driver, otherwise the boot + * constraint will never get removed and may result in unwanted behavior of the + * hardware. The boot constraint is removed by the driver core automatically + * after the device is probed (successfully or unsuccessfully). + * + * Return: 0 on success, and a negative error otherwise. + */ +int dev_boot_constraint_add(struct device *dev, + struct dev_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, info->constraint.type); + if (IS_ERR(constraint)) { + dev_err(dev, "Failed to add constraint type: %d (%ld)\n", + info->constraint.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.data); + if (ret) + goto free_constraint; + + dev_dbg(dev, "Added boot constraint-type (%d)\n", + info->constraint.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(dev_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); +} + +/** + * dev_boot_constraints_remove: Removes all boot constraints of a device. + * + * @dev: Device for which the boot constraints are getting removed. + * + * This routine removes all the boot constraints that were previously added for + * the device. This is called directly by the driver core and should not be + * called by platform specific code. + */ +void dev_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/boot_constraint/core.h b/drivers/boot_constraint/core.h new file mode 100644 index 000000000000..1e87125de531 --- /dev/null +++ b/drivers/boot_constraint/core.h @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2017 Linaro. + * Viresh Kumar + */ +#ifndef _CORE_H +#define _CORE_H + +#include +#include +#include + +struct constraint_dev { + struct device *dev; + struct list_head node; + struct list_head constraints; +}; + +struct constraint { + struct constraint_dev *cdev; + struct list_head node; + enum dev_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; +}; +#endif /* _CORE_H */ diff --git a/include/linux/boot_constraint.h b/include/linux/boot_constraint.h new file mode 100644 index 000000000000..2ce62b7b3cc6 --- /dev/null +++ b/include/linux/boot_constraint.h @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Boot constraints header. + * + * Copyright (C) 2017 Linaro. + * Viresh Kumar + */ +#ifndef _LINUX_BOOT_CONSTRAINT_H +#define _LINUX_BOOT_CONSTRAINT_H + +#include +#include + +struct device; + +/** + * enum dev_boot_constraint_type - This defines different boot constraint types. + * + */ +enum dev_boot_constraint_type { + DEV_BOOT_CONSTRAINT_NONE, +}; + +/** + * struct dev_boot_constraint - This represents a single boot constraint. + * + * @type: This is boot constraint type (like: clk, supply, etc.). + * @data: This points to constraint type specific data (like: + * dev_boot_constraint_clk_info). + */ +struct dev_boot_constraint { + enum dev_boot_constraint_type type; + void *data; +}; + +/** + * struct dev_boot_constraint_info - This is used to add a single boot + * constraint. + * + * @constraint: This represents a single boot constraint. + * @free_resources: This callback is called by the boot constraint core after + * the constraint is removed. This is an optional field. + * @free_resources_data: This is data to be passed to free_resources() callback. + * This is an optional field. + */ +struct dev_boot_constraint_info { + struct dev_boot_constraint constraint; + + /* This will be called just before the constraint is removed */ + void (*free_resources)(void *data); + void *free_resources_data; +}; + +#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); +#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) {} +#endif /* CONFIG_DEV_BOOT_CONSTRAINT */ + +#endif /* _LINUX_BOOT_CONSTRAINT_H */