Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1030269AbcDMJgW (ORCPT ); Wed, 13 Apr 2016 05:36:22 -0400 Received: from mailout1.w1.samsung.com ([210.118.77.11]:23696 "EHLO mailout1.w1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S965448AbcDMJgS (ORCPT ); Wed, 13 Apr 2016 05:36:18 -0400 X-AuditID: cbfec7f5-f792a6d000001302-33-570e1309c150 From: Marek Szyprowski To: linux-samsung-soc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Cc: Marek Szyprowski , Russell King - ARM Linux , Ulf Hansson , Greg Kroah-Hartman , Krzysztof Kozlowski , Bartlomiej Zolnierkiewicz Subject: [PATCH v7 2/2] drivers: amba: properly handle devices with power domains Date: Wed, 13 Apr 2016 11:36:00 +0200 Message-id: <1460540160-18762-3-git-send-email-m.szyprowski@samsung.com> X-Mailer: git-send-email 1.9.2 In-reply-to: <1460540160-18762-1-git-send-email-m.szyprowski@samsung.com> References: <1460540160-18762-1-git-send-email-m.szyprowski@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFlrALMWRmVeSWpSXmKPExsVy+t/xy7qcwnzhBs1bzS02zljPatG8eD2b xesXhhabHl9jtbi8aw6bxYzz+5gsbl/mtVh75C67xfG14Q6cHi3NPWwed67tYfPYP3cNu8fm JfUefVtWMXp83iQXwBbFZZOSmpNZllqkb5fAldGz+wJbwVy1irs975gbGCfIdzFyckgImEgs ur6UFcIWk7hwbz1bFyMXh5DAUkaJo3u2QzlNTBL33jcwglSxCRhKdL3tYgOxRQSyJebv7WYH KWIWWMEk0f/wAVhCWCBY4sD+G8wgNouAqsTM9//BVvAKeEhcvbuSBWKdnMT/lyuYQGxOAU+J o3e2gcWFgGpe7drBPoGRdwEjwypG0dTS5ILipPRcI73ixNzi0rx0veT83E2MkHD7uoNx6TGr Q4wCHIxKPLwa63nDhVgTy4orcw8xSnAwK4nwfuHjCxfiTUmsrEotyo8vKs1JLT7EKM3BoiTO O3PX+xAhgfTEktTs1NSC1CKYLBMHp1QDI6vajK9+HFMYp5/OXcZctLTvU1idtP2MCWzPVabl f33J1jZNarP27ptTd56bGnFm1dbp2nOZzaIOVZ/blpN0yNty8tNHRioztr46+WzWraSYKXFi IvvWN0r/ff6b/1SX697110+oMNVvmtPQ15ybKa/Q+23rjgtF8+5Zcp3l0rIPv5rzOYtj4Uwl luKMREMt5qLiRAAnzXBdMwIAAA== Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 4737 Lines: 154 To read pid/cid registers, the probed device need to be properly turned on. When it is inside a power domain, the bus code should ensure that the given power domain is enabled before trying to access device's registers. However in some cases power domain (or clocks) might not be yet available. Returning EPROBE_DEFER is not a solution in such case, because callers don't handle this special error code. Instead such devices are added to the special list and their registration is retried in late_initcall, when hopefully all resources are available. Signed-off-by: Marek Szyprowski --- drivers/amba/bus.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 96 insertions(+), 10 deletions(-) diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c index f0099360039e..3329262007f9 100644 --- a/drivers/amba/bus.c +++ b/drivers/amba/bus.c @@ -336,16 +336,7 @@ static void amba_device_release(struct device *dev) kfree(d); } -/** - * amba_device_add - add a previously allocated AMBA device structure - * @dev: AMBA device allocated by amba_device_alloc - * @parent: resource parent for this devices resources - * - * Claim the resource, and read the device cell ID if not already - * initialized. Register the AMBA device with the Linux device - * manager. - */ -int amba_device_add(struct amba_device *dev, struct resource *parent) +static int amba_device_try_add(struct amba_device *dev, struct resource *parent) { u32 size; void __iomem *tmp; @@ -373,6 +364,12 @@ int amba_device_add(struct amba_device *dev, struct resource *parent) goto err_release; } + ret = dev_pm_domain_attach(&dev->dev, true); + if (ret == -EPROBE_DEFER) { + iounmap(tmp); + goto err_release; + } + ret = amba_get_enable_pclk(dev); if (ret == 0) { u32 pid, cid; @@ -398,6 +395,7 @@ int amba_device_add(struct amba_device *dev, struct resource *parent) } iounmap(tmp); + dev_pm_domain_detach(&dev->dev, true); if (ret) goto err_release; @@ -421,6 +419,94 @@ int amba_device_add(struct amba_device *dev, struct resource *parent) err_out: return ret; } + +/* + * Registration of AMBA device require reading its pid and cid registers. + * To do this, the device must be turned on (if it is a part of power domain) + * and have clocks enabled. However in some cases those resources might not be + * yet available. Returning EPROBE_DEFER is not a solution in such case, + * because callers don't handle this special error code. Instead such devices + * are added to the special list and their registration is retried from + * deferred probe notifier, when hopefully all resources are available. + */ +struct deferred_device { + struct amba_device *dev; + struct resource *parent; + struct list_head node; +}; + +static LIST_HEAD(deferred_devices); +static DEFINE_MUTEX(deferred_devices_lock); + +static int amba_deferred_probe_notifier(struct notifier_block *nb, + unsigned long action, void *data); +static struct notifier_block amba_deferred_probe_nb = { + .notifier_call = amba_deferred_probe_notifier, +}; + +static int amba_deferred_probe_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct deferred_device *ddev, *tmp; + + mutex_lock(&deferred_devices_lock); + + list_for_each_entry_safe(ddev, tmp, &deferred_devices, node) { + int ret = amba_device_try_add(ddev->dev, ddev->parent); + + if (ret == -EPROBE_DEFER) + continue; + + list_del_init(&ddev->node); + kfree(ddev); + } + + if (list_empty(&deferred_devices)) + deferred_probe_unregister_notifier(&amba_deferred_probe_nb); + + mutex_unlock(&deferred_devices_lock); + + return NOTIFY_DONE; +} + +/** + * amba_device_add - add a previously allocated AMBA device structure + * @dev: AMBA device allocated by amba_device_alloc + * @parent: resource parent for this devices resources + * + * Claim the resource, and read the device cell ID if not already + * initialized. Register the AMBA device with the Linux device + * manager. + */ +int amba_device_add(struct amba_device *dev, struct resource *parent) +{ + int ret = amba_device_try_add(dev, parent); + + if (ret == -EPROBE_DEFER) { + struct deferred_device *ddev; + + ddev = kmalloc(sizeof(*ddev), GFP_KERNEL); + if (!ddev) + return -ENOMEM; + + ddev->dev = dev; + ddev->parent = parent; + ret = 0; + + mutex_lock(&deferred_devices_lock); + + if (list_empty(&deferred_devices)) + ret = deferred_probe_register_notifier( + &amba_deferred_probe_nb); + if (ret == 0) + list_add_tail(&ddev->node, &deferred_devices); + else + kfree(ddev); + + mutex_unlock(&deferred_devices_lock); + } + return ret; +} EXPORT_SYMBOL_GPL(amba_device_add); static struct amba_device * -- 1.9.2