From patchwork Fri Feb 21 15:37:53 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Grygorii Strashko X-Patchwork-Id: 3698141 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id E43099F1EE for ; Fri, 21 Feb 2014 15:38:45 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id DCD962012B for ; Fri, 21 Feb 2014 15:38:44 +0000 (UTC) Received: from casper.infradead.org (casper.infradead.org [85.118.1.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id B8A64201B6 for ; Fri, 21 Feb 2014 15:38:43 +0000 (UTC) Received: from merlin.infradead.org ([2001:4978:20e::2]) by casper.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1WGsBM-00041R-B3; Fri, 21 Feb 2014 15:38:32 +0000 Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1WGsBJ-0007Sp-Ph; Fri, 21 Feb 2014 15:38:29 +0000 Received: from bear.ext.ti.com ([192.94.94.41]) by merlin.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1WGsBH-0007Ry-3r for linux-arm-kernel@lists.infradead.org; Fri, 21 Feb 2014 15:38:28 +0000 Received: from dflxv15.itg.ti.com ([128.247.5.124]) by bear.ext.ti.com (8.13.7/8.13.7) with ESMTP id s1LFbt0o001999; Fri, 21 Feb 2014 09:37:55 -0600 Received: from DFRE70.ent.ti.com (dncmailx.itg.ti.com [10.167.188.34]) by dflxv15.itg.ti.com (8.14.3/8.13.8) with ESMTP id s1LFbso9006789; Fri, 21 Feb 2014 09:37:55 -0600 Received: from DFRE01.ent.ti.com ([fe80::b027:5293:c8d8:d82a]) by DFRE70.ent.ti.com ([fe80::645e:109b:13d3:dfa7%25]) with mapi id 14.03.0174.001; Fri, 21 Feb 2014 16:37:54 +0100 From: "Strashko, Grygorii" To: Jean-Jacques Hiblot , "grant.likely@linaro.org" , "gregkh@linuxfoundation.org" , "robh+dt@kernel.org" , "thierry.reding@gmail.com" Subject: RE: [PATCH v2] dt: platform driver: Fill the resources before probe and defer if needed Thread-Topic: [PATCH v2] dt: platform driver: Fill the resources before probe and defer if needed Thread-Index: AQHPLwgZ4qcczSJiVU+9DdUZkrGGP5q/xEtp Date: Fri, 21 Feb 2014 15:37:53 +0000 Message-ID: <902E09E6452B0E43903E4F2D568737AB0B9D2959@DFRE01.ent.ti.com> References: <20140220153042.DF053C4050F@trevor.secretlab.ca>, <1392988720-20976-1-git-send-email-jjhiblot@traphandler.com> In-Reply-To: <1392988720-20976-1-git-send-email-jjhiblot@traphandler.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-originating-ip: [128.247.5.52] x-exclaimer-md-config: f9c360f5-3d1e-4c3c-8703-f45bf52eff6b MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20140221_103827_271763_3D790103 X-CRM114-Status: GOOD ( 20.94 ) X-Spam-Score: -7.9 (-------) Cc: "gregory.clement@free-electrons.com" , "devicetree@vger.kernel.org" , "Shilimkar, Santosh" , "linux-kernel@vger.kernel.org" , "linux-arm-kernel@lists.infradead.org" X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-5.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Hi Jean-Jacques, Sorry for top posting. As I know, there have been several attempts to solve the same problem already:) [1] https://lkml.org/lkml/2013/9/18/216 [2] https://lkml.org/lkml/2013/11/22/520 [3] https://lkml.org/lkml/2014/1/8/240 There are some questions related to your approach: 1) How to distinguish between cases "IRQ domain not ready" and "wrong IRQ data in DT" or other IRQ parsing errors? Otherwise, Driver's probe will be deffered wrongly and forever, Thierry Reding has tried to solve this in [1]. 2) How will be handled driver reloading situation? The worst case (sparse IRQ enabled): - remove driver A - remove driver B (irq-controller) - load driver B <--- different set of Linux IRQ numbers can be assigned - load driver A <--- oops. IRQ resources table contains invalid data Best regards, Grygorii Strashko --- 1.9.0 ============================================= The goal of this patch is to allow drivers to be probed even if at the time of the DT parsing some of their ressources are not available yet. In the current situation, the resource of a platform device are filled from the DT at the time the device is created (of_device_alloc()). The drawbackof this is that a device sitting close to the top of the DT (ahb for example) but depending on ressources that are initialized later (IRQ domain dynamically created for example) will fail to probe because the ressources don't exist at this time. This patch fills the resource structure only before the device is probed and will defer the probe if the resource are not available yet. Signed-off-by: Jean-Jacques Hiblot Reviewed-by: Gregory CLEMENT --- Hi Grant, I reworked the patch as you proposed. To keep the overhead minimum, nirq and nreg are computed only the first time. In this implementation, only the missing IRQ ressources are re-tried for. It could easily be changed to re-parse all the IRQs though (replace if (!res->flags) with if ((!res->flags) || (res->flags & IORESOURCE_IRQ)). drivers/base/platform.c | 5 +++ drivers/of/platform.c | 100 +++++++++++++++++++++++++++++++++----------- include/linux/of_platform.h | 10 +++++ 3 files changed, 90 insertions(+), 25 deletions(-) diff --git a/drivers/base/platform.c b/drivers/base/platform.c index bc78848..cee9b8d 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -481,6 +481,10 @@ static int platform_drv_probe(struct device *_dev) struct platform_device *dev = to_platform_device(_dev); int ret; + ret = of_platform_device_prepare(dev); + if (ret) + goto error; + if (ACPI_HANDLE(_dev)) acpi_dev_pm_attach(_dev, true); @@ -488,6 +492,7 @@ static int platform_drv_probe(struct device *_dev) if (ret && ACPI_HANDLE(_dev)) acpi_dev_pm_detach(_dev, true); +error: if (drv->prevent_deferred_probe && ret == -EPROBE_DEFER) { dev_warn(_dev, "probe deferral not supported\n"); ret = -ENXIO; diff --git a/drivers/of/platform.c b/drivers/of/platform.c index 404d1da..a4e2602 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -141,36 +141,11 @@ struct platform_device *of_device_alloc(struct device_node *np, struct device *parent) { struct platform_device *dev; - int rc, i, num_reg = 0, num_irq; - struct resource *res, temp_res; dev = platform_device_alloc("", -1); if (!dev) return NULL; - /* count the io and irq resources */ - if (of_can_translate_address(np)) - while (of_address_to_resource(np, num_reg, &temp_res) == 0) - num_reg++; - num_irq = of_irq_count(np); - - /* Populate the resource table */ - if (num_irq || num_reg) { - res = kzalloc(sizeof(*res) * (num_irq + num_reg), GFP_KERNEL); - if (!res) { - platform_device_put(dev); - return NULL; - } - - dev->num_resources = num_reg + num_irq; - dev->resource = res; - for (i = 0; i < num_reg; i++, res++) { - rc = of_address_to_resource(np, i, res); - WARN_ON(rc); - } - WARN_ON(of_irq_to_resource_table(np, res, num_irq) != num_irq); - } - dev->dev.of_node = of_node_get(np); #if defined(CONFIG_MICROBLAZE) dev->dev.dma_mask = &dev->archdata.dma_mask; @@ -233,6 +208,81 @@ static struct platform_device *of_platform_device_create_pdata( return dev; } +static int of_reg_count(struct device_node *np) +{ + int nreg = 0; + if (of_can_translate_address(np)) { + struct resource temp_res; + while (of_address_to_resource(np, nreg, &temp_res) == 0) + nreg++; + } + return nreg; +} + +int of_platform_device_prepare(struct platform_device *dev) +{ + struct device_node *np; + int i, irq_index; + struct resource *res; + + /* + * This function applies only devices described in the DT. + * Other platform devices have their ressources already populated. + */ + np = dev->dev.of_node; + if (!np) + return 0; + + /* Populate the resource table */ + if (!dev->resource) { + int rc, nreg = 0, nirq; + /* count the io and irq resources */ + nreg = of_reg_count(np); + nirq = of_irq_count(np); + + if (!nirq && !nreg) + return 0; + + res = kzalloc(sizeof(*res) * (nirq + nreg), GFP_KERNEL); + if (!res) + return -ENOMEM; + + dev->resource = res; + dev->num_resources = nreg + nirq; + + for (i = 0; i < nreg; i++, res++) { + rc = of_address_to_resource(np, i, res); + if (WARN_ON(rc)) { + /* THIS IS BAD; don't try to defer probing */ + dev->num_resources = 0; + dev->resource = NULL; + kfree(res); + return rc; + } + } + + if (!rc && of_irq_to_resource_table(np, res, nirq) != nirq) { + /* IRQ controller is yet available. defer probing */ + return -EPROBE_DEFER; + } + + return 0; + } + + /* See which IRQ resources need to be redone */ + irq_index = 0; + for (i = 0, res = dev->resource; i < dev->num_resources; i++, res++) { + if (!res->flags) { + if (!of_irq_to_resource(np, irq_index, res)) + return -EPROBE_DEFER; + irq_index++; + } else if (res->flags & IORESOURCE_IRQ) + irq_index++; + } + return 0; +} +EXPORT_SYMBOL(of_platform_device_prepare); + /** * of_platform_device_create - Alloc, initialize and register an of_device * @np: pointer to node to create device for diff --git a/include/linux/of_platform.h b/include/linux/of_platform.h index 05cb4a9..4e487ff 100644 --- a/include/linux/of_platform.h +++ b/include/linux/of_platform.h @@ -53,6 +53,16 @@ struct of_dev_auxdata { extern const struct of_device_id of_default_bus_match_table[]; +/* Populate the resource for a platform device */ +#ifdef CONFIG_OF +int of_platform_device_prepare(struct platform_device *dev); +#else +static inline int of_platform_device_prepare( + struct platform_device *dev) +{ + return 0; +} +#endif /* Platform drivers register/unregister */ extern struct platform_device *of_device_alloc(struct device_node *np, const char *bus_id,