From patchwork Fri Oct 27 07:26:06 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jeffy Chen X-Patchwork-Id: 831127 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-pci-owner@vger.kernel.org; receiver=) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yNb6R1GJmz9t2x for ; Fri, 27 Oct 2017 18:26:55 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752077AbdJ0H0p (ORCPT ); Fri, 27 Oct 2017 03:26:45 -0400 Received: from regular1.263xmail.com ([211.150.99.135]:52027 "EHLO regular1.263xmail.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751068AbdJ0H0g (ORCPT ); Fri, 27 Oct 2017 03:26:36 -0400 Received: from jeffy.chen?rock-chips.com (unknown [192.168.167.192]) by regular1.263xmail.com (Postfix) with ESMTP id C71501DEFB; Fri, 27 Oct 2017 15:26:31 +0800 (CST) X-263anti-spam: KSV:0; X-MAIL-GRAY: 0 X-MAIL-DELIVERY: 1 X-KSVirus-check: 0 X-ABS-CHECKED: 4 Received: from localhost (localhost [127.0.0.1]) by smtp.263.net (Postfix) with ESMTPA id 59CBE3CA; Fri, 27 Oct 2017 15:26:25 +0800 (CST) X-RL-SENDER: jeffy.chen@rock-chips.com X-FST-TO: linux-kernel@vger.kernel.org X-SENDER-IP: 103.29.142.67 X-LOGIN-NAME: jeffy.chen@rock-chips.com X-UNIQUE-TAG: <1d7e5b5644698a0d2767f13fc92b8a4f> X-ATTACHMENT-NUM: 0 X-SENDER: cjf@rock-chips.com X-DNS-TYPE: 0 Received: from localhost (unknown [103.29.142.67]) by smtp.263.net (Postfix) whith ESMTP id 5803OVVLPZ; Fri, 27 Oct 2017 15:26:31 +0800 (CST) From: Jeffy Chen To: linux-kernel@vger.kernel.org, bhelgaas@google.com Cc: linux-pm@vger.kernel.org, tony@atomide.com, shawn.lin@rock-chips.com, briannorris@chromium.org, rjw@rjwysocki.net, dianders@chromium.org, Jeffy Chen , devicetree@vger.kernel.org, linux-pci@vger.kernel.org, Rob Herring , Mark Rutland Subject: [RFC PATCH v10 1/7] dt-bindings: PCI: Add definition of PCIe WAKE# irq and PCI irq Date: Fri, 27 Oct 2017 15:26:06 +0800 Message-Id: <20171027072612.26565-2-jeffy.chen@rock-chips.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20171027072612.26565-1-jeffy.chen@rock-chips.com> References: <20171027072612.26565-1-jeffy.chen@rock-chips.com> Sender: linux-pci-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org We are going to handle PCIe WAKE# pin for PCI bus bridges and PCI devices in the pci core, so add definitions of the optional PCIe WAKE# pin for PCI bus bridges and PCI devices. Also add an definition of the optional PCI interrupt pin for PCI devices to distinguish it from the PCIe WAKE# pin. Signed-off-by: Jeffy Chen --- Changes in v10: None Changes in v9: Add section for PCI devices and rewrite the commit message. Changes in v8: Add optional "pci", and rewrite commit message. Changes in v7: None Changes in v6: None Changes in v5: Move to pci.txt Changes in v3: None Changes in v2: None Documentation/devicetree/bindings/pci/pci.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/pci/pci.txt b/Documentation/devicetree/bindings/pci/pci.txt index c77981c5dd18..d4406d4e15ad 100644 --- a/Documentation/devicetree/bindings/pci/pci.txt +++ b/Documentation/devicetree/bindings/pci/pci.txt @@ -24,3 +24,11 @@ driver implementation may support the following properties: unsupported link speed, for instance, trying to do training for unsupported link speed, etc. Must be '4' for gen4, '3' for gen3, '2' for gen2, and '1' for gen1. Any other values are invalid. +- interrupts: Interrupt specifier for each name in interrupt-names. +- interrupt-names: May contains "wakeup" for PCIe WAKE# interrupt. + +PCI devices have standardized Device Tree bindings: + +- interrupts: Interrupt specifier for each name in interrupt-names. +- interrupt-names: May contains "wakeup" for PCIe WAKE# interrupt and "pci" for + PCI interrupt. From patchwork Fri Oct 27 07:26:10 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jeffy Chen X-Patchwork-Id: 831128 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-pci-owner@vger.kernel.org; receiver=) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yNb700Cxsz9t34 for ; Fri, 27 Oct 2017 18:27:24 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751530AbdJ0H1V (ORCPT ); Fri, 27 Oct 2017 03:27:21 -0400 Received: from regular1.263xmail.com ([211.150.99.136]:42223 "EHLO regular1.263xmail.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751072AbdJ0H1T (ORCPT ); Fri, 27 Oct 2017 03:27:19 -0400 Received: from jeffy.chen?rock-chips.com (unknown [192.168.167.243]) by regular1.263xmail.com (Postfix) with ESMTP id EE4DB5D; Fri, 27 Oct 2017 15:27:16 +0800 (CST) X-263anti-spam: KSV:0; X-MAIL-GRAY: 0 X-MAIL-DELIVERY: 1 X-KSVirus-check: 0 X-ABS-CHECKED: 4 Received: from localhost (localhost [127.0.0.1]) by smtp.263.net (Postfix) with ESMTPA id 129A7399; Fri, 27 Oct 2017 15:27:04 +0800 (CST) X-RL-SENDER: jeffy.chen@rock-chips.com X-FST-TO: linux-kernel@vger.kernel.org X-SENDER-IP: 103.29.142.67 X-LOGIN-NAME: jeffy.chen@rock-chips.com X-UNIQUE-TAG: X-ATTACHMENT-NUM: 0 X-SENDER: cjf@rock-chips.com X-DNS-TYPE: 0 Received: from localhost (unknown [103.29.142.67]) by smtp.263.net (Postfix) whith ESMTP id 27511MBL0F1; Fri, 27 Oct 2017 15:27:14 +0800 (CST) From: Jeffy Chen To: linux-kernel@vger.kernel.org, bhelgaas@google.com Cc: linux-pm@vger.kernel.org, tony@atomide.com, shawn.lin@rock-chips.com, briannorris@chromium.org, rjw@rjwysocki.net, dianders@chromium.org, Jeffy Chen , linux-pci@vger.kernel.org Subject: [RFC PATCH v10 5/7] PCI: Make pci_platform_pm_ops's callbacks optional Date: Fri, 27 Oct 2017 15:26:10 +0800 Message-Id: <20171027072612.26565-6-jeffy.chen@rock-chips.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20171027072612.26565-1-jeffy.chen@rock-chips.com> References: <20171027072612.26565-1-jeffy.chen@rock-chips.com> Sender: linux-pci-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org Allow platforms not to provide some of the pci_platform_pm_ops's callbacks. Also change the return value from -ENOSYS to -ENODEV for: warning: drivers/pci/pci.c,594: ENOSYS means 'invalid syscall nr' and nothing else Signed-off-by: Jeffy Chen --- Changes in v10: None Changes in v9: None Changes in v8: None Changes in v7: None Changes in v6: None Changes in v5: None Changes in v3: None Changes in v2: None drivers/pci/pci.c | 38 +++++++++++++++++++++++++------------- drivers/pci/pci.h | 5 +---- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index f0d68066c726..e120b00a9017 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -572,46 +572,58 @@ static void pci_restore_bars(struct pci_dev *dev) static const struct pci_platform_pm_ops *pci_platform_pm; -int pci_set_platform_pm(const struct pci_platform_pm_ops *ops) +void pci_set_platform_pm(const struct pci_platform_pm_ops *ops) { - if (!ops->is_manageable || !ops->set_state || !ops->get_state || - !ops->choose_state || !ops->set_wakeup || !ops->need_resume) - return -EINVAL; pci_platform_pm = ops; - return 0; } static inline bool platform_pci_power_manageable(struct pci_dev *dev) { - return pci_platform_pm ? pci_platform_pm->is_manageable(dev) : false; + if (pci_platform_pm && pci_platform_pm->is_manageable) + return pci_platform_pm->is_manageable(dev); + + return false; } static inline int platform_pci_set_power_state(struct pci_dev *dev, pci_power_t t) { - return pci_platform_pm ? pci_platform_pm->set_state(dev, t) : -ENOSYS; + if (pci_platform_pm && pci_platform_pm->set_state) + return pci_platform_pm->set_state(dev, t); + + return -ENODEV; } static inline pci_power_t platform_pci_get_power_state(struct pci_dev *dev) { - return pci_platform_pm ? pci_platform_pm->get_state(dev) : PCI_UNKNOWN; + if (pci_platform_pm && pci_platform_pm->get_state) + return pci_platform_pm->get_state(dev); + + return PCI_UNKNOWN; } static inline pci_power_t platform_pci_choose_state(struct pci_dev *dev) { - return pci_platform_pm ? - pci_platform_pm->choose_state(dev) : PCI_POWER_ERROR; + if (pci_platform_pm && pci_platform_pm->choose_state) + return pci_platform_pm->choose_state(dev); + + return PCI_POWER_ERROR; } static inline int platform_pci_set_wakeup(struct pci_dev *dev, bool enable) { - return pci_platform_pm ? - pci_platform_pm->set_wakeup(dev, enable) : -ENODEV; + if (pci_platform_pm && pci_platform_pm->set_wakeup) + return pci_platform_pm->set_wakeup(dev, enable); + + return -ENODEV; } static inline bool platform_pci_need_resume(struct pci_dev *dev) { - return pci_platform_pm ? pci_platform_pm->need_resume(dev) : false; + if (pci_platform_pm && pci_platform_pm->need_resume) + return pci_platform_pm->need_resume(dev); + + return false; } /** diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index e816a13e6259..048668271014 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -52,9 +52,6 @@ int pci_probe_reset_function(struct pci_dev *dev); * @need_resume: returns 'true' if the given device (which is currently * suspended) needs to be resumed to be configured for system * wakeup. - * - * If given platform is generally capable of power managing PCI devices, all of - * these callbacks are mandatory. */ struct pci_platform_pm_ops { bool (*is_manageable)(struct pci_dev *dev); @@ -65,7 +62,7 @@ struct pci_platform_pm_ops { bool (*need_resume)(struct pci_dev *dev); }; -int pci_set_platform_pm(const struct pci_platform_pm_ops *ops); +void pci_set_platform_pm(const struct pci_platform_pm_ops *ops); void pci_update_current_state(struct pci_dev *dev, pci_power_t state); void pci_power_up(struct pci_dev *dev); void pci_disable_enabled_device(struct pci_dev *dev); From patchwork Fri Oct 27 07:26:11 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jeffy Chen X-Patchwork-Id: 831131 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-pci-owner@vger.kernel.org; receiver=) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yNb7t2KDXz9t2x for ; Fri, 27 Oct 2017 18:28:10 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752218AbdJ0H1b (ORCPT ); Fri, 27 Oct 2017 03:27:31 -0400 Received: from regular1.263xmail.com ([211.150.99.141]:48907 "EHLO regular1.263xmail.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751072AbdJ0H10 (ORCPT ); Fri, 27 Oct 2017 03:27:26 -0400 Received: from jeffy.chen?rock-chips.com (unknown [192.168.167.161]) by regular1.263xmail.com (Postfix) with ESMTP id B62D46C; Fri, 27 Oct 2017 15:27:22 +0800 (CST) X-263anti-spam: KSV:0; X-MAIL-GRAY: 0 X-MAIL-DELIVERY: 1 X-KSVirus-check: 0 X-ABS-CHECKED: 4 Received: from localhost (localhost [127.0.0.1]) by smtp.263.net (Postfix) with ESMTPA id E45ED393; Fri, 27 Oct 2017 15:27:15 +0800 (CST) X-RL-SENDER: jeffy.chen@rock-chips.com X-FST-TO: linux-kernel@vger.kernel.org X-SENDER-IP: 103.29.142.67 X-LOGIN-NAME: jeffy.chen@rock-chips.com X-UNIQUE-TAG: X-ATTACHMENT-NUM: 0 X-SENDER: cjf@rock-chips.com X-DNS-TYPE: 0 Received: from localhost (unknown [103.29.142.67]) by smtp.263.net (Postfix) whith ESMTP id 14555HINT57; Fri, 27 Oct 2017 15:27:21 +0800 (CST) From: Jeffy Chen To: linux-kernel@vger.kernel.org, bhelgaas@google.com Cc: linux-pm@vger.kernel.org, tony@atomide.com, shawn.lin@rock-chips.com, briannorris@chromium.org, rjw@rjwysocki.net, dianders@chromium.org, Jeffy Chen , linux-pci@vger.kernel.org, linux-acpi@vger.kernel.org, Len Brown Subject: [RFC PATCH v10 6/7] PCI / PM: Move acpi wakeup code to pci core Date: Fri, 27 Oct 2017 15:26:11 +0800 Message-Id: <20171027072612.26565-7-jeffy.chen@rock-chips.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20171027072612.26565-1-jeffy.chen@rock-chips.com> References: <20171027072612.26565-1-jeffy.chen@rock-chips.com> Sender: linux-pci-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org Move acpi wakeup code to pci core as pci_set_wakeup(), so that other platforms could reuse it. Also add .setup_dev() / .setup_host_bridge() / .cleanup() platform pm ops's callbacks to setup and cleanup pci devices and host bridge for wakeup. Signed-off-by: Jeffy Chen --- Changes in v10: None Changes in v9: None Changes in v8: None Changes in v7: None Changes in v6: None Changes in v5: None Changes in v3: None Changes in v2: None drivers/pci/pci-acpi.c | 121 +++++++++++++++++++++++------------------------ drivers/pci/pci-driver.c | 9 ++++ drivers/pci/pci.c | 84 ++++++++++++++++++++++++++++---- drivers/pci/pci.h | 28 +++++++++-- drivers/pci/probe.c | 12 ++++- drivers/pci/remove.c | 2 + include/linux/pci.h | 2 + 7 files changed, 180 insertions(+), 78 deletions(-) diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 4708eb9df71b..ee96e7afe1ac 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -569,31 +569,6 @@ static pci_power_t acpi_pci_get_power_state(struct pci_dev *dev) return state_conv[state]; } -static int acpi_pci_propagate_wakeup(struct pci_bus *bus, bool enable) -{ - while (bus->parent) { - if (acpi_pm_device_can_wakeup(&bus->self->dev)) - return acpi_pm_set_bridge_wakeup(&bus->self->dev, enable); - - bus = bus->parent; - } - - /* We have reached the root bus. */ - if (bus->bridge) { - if (acpi_pm_device_can_wakeup(bus->bridge)) - return acpi_pm_set_bridge_wakeup(bus->bridge, enable); - } - return 0; -} - -static int acpi_pci_wakeup(struct pci_dev *dev, bool enable) -{ - if (acpi_pm_device_can_wakeup(&dev->dev)) - return acpi_pm_set_device_wakeup(&dev->dev, enable); - - return acpi_pci_propagate_wakeup(dev->bus, enable); -} - static bool acpi_pci_need_resume(struct pci_dev *dev) { struct acpi_device *adev = ACPI_COMPANION(&dev->dev); @@ -610,14 +585,29 @@ static bool acpi_pci_need_resume(struct pci_dev *dev) return !!adev->power.flags.dsw_present; } -static const struct pci_platform_pm_ops acpi_pci_platform_pm = { - .is_manageable = acpi_pci_power_manageable, - .set_state = acpi_pci_set_power_state, - .get_state = acpi_pci_get_power_state, - .choose_state = acpi_pci_choose_state, - .set_wakeup = acpi_pci_wakeup, - .need_resume = acpi_pci_need_resume, -}; +static bool acpi_pci_can_wakeup(void *pmdata) +{ + struct device *dev = pmdata; + + if (!dev) + return false; + + return acpi_pm_device_can_wakeup(dev); +} + +static int acpi_pci_dev_wakeup(void *pmdata, bool enable) +{ + struct device *dev = pmdata; + + return acpi_pm_set_device_wakeup(dev, enable); +} + +static int acpi_pci_bridge_wakeup(void *pmdata, bool enable) +{ + struct device *dev = pmdata; + + return acpi_pm_set_bridge_wakeup(dev, enable); +} void acpi_pci_add_bus(struct pci_bus *bus) { @@ -658,20 +648,6 @@ void acpi_pci_remove_bus(struct pci_bus *bus) acpi_pci_slot_remove(bus); } -/* ACPI bus type */ -static struct acpi_device *acpi_pci_find_companion(struct device *dev) -{ - struct pci_dev *pci_dev = to_pci_dev(dev); - bool check_children; - u64 addr; - - check_children = pci_is_bridge(pci_dev); - /* Please ref to ACPI spec for the syntax of _ADR */ - addr = (PCI_SLOT(pci_dev->devfn) << 16) | PCI_FUNC(pci_dev->devfn); - return acpi_find_child_device(ACPI_COMPANION(dev->parent), addr, - check_children); -} - /** * pci_acpi_optimize_delay - optimize PCI D3 and D3cold delay from ACPI * @pdev: the PCI device whose delay is to be updated @@ -723,34 +699,55 @@ static void pci_acpi_optimize_delay(struct pci_dev *pdev, ACPI_FREE(obj); } -static void pci_acpi_setup(struct device *dev) +static void *acpi_pci_setup_dev(struct pci_dev *pci_dev) { - struct pci_dev *pci_dev = to_pci_dev(dev); - struct acpi_device *adev = ACPI_COMPANION(dev); + struct acpi_device *adev = ACPI_COMPANION(&pci_dev->dev); if (!adev) - return; + return NULL; pci_acpi_optimize_delay(pci_dev, adev->handle); pci_acpi_add_pm_notifier(adev, pci_dev); if (!adev->wakeup.flags.valid) - return; + return NULL; + + device_set_wakeup_capable(&pci_dev->dev, true); + acpi_pm_set_device_wakeup(&pci_dev->dev, false); - device_set_wakeup_capable(dev, true); - acpi_pci_wakeup(pci_dev, false); + return &pci_dev->dev; } -static void pci_acpi_cleanup(struct device *dev) +static void *acpi_pci_setup_host_bridge(struct pci_host_bridge *bridge) { - struct acpi_device *adev = ACPI_COMPANION(dev); + return &bridge->dev; +} - if (!adev) - return; +static const struct pci_platform_pm_ops acpi_pci_platform_pm = { + .is_manageable = acpi_pci_power_manageable, + .set_state = acpi_pci_set_power_state, + .get_state = acpi_pci_get_power_state, + .choose_state = acpi_pci_choose_state, + .need_resume = acpi_pci_need_resume, + .setup_dev = acpi_pci_setup_dev, + .setup_host_bridge = acpi_pci_setup_host_bridge, + .can_wakeup = acpi_pci_can_wakeup, + .dev_wakeup = acpi_pci_dev_wakeup, + .bridge_wakeup = acpi_pci_bridge_wakeup, +}; + +/* ACPI bus type */ +static struct acpi_device *acpi_pci_find_companion(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + bool check_children; + u64 addr; - pci_acpi_remove_pm_notifier(adev); - if (adev->wakeup.flags.valid) - device_set_wakeup_capable(dev, false); + check_children = pci_is_bridge(pci_dev); + /* Please ref to ACPI spec for the syntax of _ADR */ + addr = (PCI_SLOT(pci_dev->devfn) << 16) | PCI_FUNC(pci_dev->devfn); + return acpi_find_child_device(ACPI_COMPANION(dev->parent), addr, + check_children); } static bool pci_acpi_bus_match(struct device *dev) @@ -762,8 +759,6 @@ static struct acpi_bus_type acpi_pci_bus = { .name = "PCI", .match = pci_acpi_bus_match, .find_companion = acpi_pci_find_companion, - .setup = pci_acpi_setup, - .cleanup = pci_acpi_cleanup, }; diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 9be563067c0c..abb7caa8a6e5 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -421,10 +421,17 @@ static int pci_device_probe(struct device *dev) if (error < 0) return error; + pci_dev->pmdata = platform_pci_setup_dev(pci_dev); + if (IS_ERR(pci_dev->pmdata)) { + pcibios_free_irq(pci_dev); + return PTR_ERR(pci_dev->pmdata); + } + pci_dev_get(pci_dev); if (pci_device_can_probe(pci_dev)) { error = __pci_device_probe(drv, pci_dev); if (error) { + platform_pci_cleanup(pci_dev->pmdata); pcibios_free_irq(pci_dev); pci_dev_put(pci_dev); } @@ -438,6 +445,8 @@ static int pci_device_remove(struct device *dev) struct pci_dev *pci_dev = to_pci_dev(dev); struct pci_driver *drv = pci_dev->driver; + platform_pci_cleanup(pci_dev->pmdata); + if (drv) { if (drv->remove) { pm_runtime_get_sync(dev); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index e120b00a9017..6add5d3209bf 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -585,6 +585,52 @@ static inline bool platform_pci_power_manageable(struct pci_dev *dev) return false; } +void *platform_pci_setup_dev(struct pci_dev *dev) +{ + if (pci_platform_pm && pci_platform_pm->setup_dev) + return pci_platform_pm->setup_dev(dev); + + return NULL; +} + +void *platform_pci_setup_host_bridge(struct pci_host_bridge *bridge) +{ + if (pci_platform_pm && pci_platform_pm->setup_host_bridge) + return pci_platform_pm->setup_host_bridge(bridge); + + return NULL; +} + +void platform_pci_cleanup(void *pmdata) +{ + if (pci_platform_pm && pci_platform_pm->cleanup) + pci_platform_pm->cleanup(pmdata); +} + +static inline bool platform_pci_can_wakeup(void *pmdata) +{ + if (pci_platform_pm && pci_platform_pm->can_wakeup) + return pci_platform_pm->can_wakeup(pmdata); + + return false; +} + +static inline int platform_pci_dev_wakeup(void *pmdata, bool enable) +{ + if (pci_platform_pm && pci_platform_pm->dev_wakeup) + return pci_platform_pm->dev_wakeup(pmdata, enable); + + return -ENODEV; +} + +static inline int platform_pci_bridge_wakeup(void *pmdata, bool enable) +{ + if (pci_platform_pm && pci_platform_pm->bridge_wakeup) + return pci_platform_pm->bridge_wakeup(pmdata, enable); + + return -ENODEV; +} + static inline int platform_pci_set_power_state(struct pci_dev *dev, pci_power_t t) { @@ -610,14 +656,6 @@ static inline pci_power_t platform_pci_choose_state(struct pci_dev *dev) return PCI_POWER_ERROR; } -static inline int platform_pci_set_wakeup(struct pci_dev *dev, bool enable) -{ - if (pci_platform_pm && pci_platform_pm->set_wakeup) - return pci_platform_pm->set_wakeup(dev, enable); - - return -ENODEV; -} - static inline bool platform_pci_need_resume(struct pci_dev *dev) { if (pci_platform_pm && pci_platform_pm->need_resume) @@ -1903,6 +1941,32 @@ void pci_pme_active(struct pci_dev *dev, bool enable) } EXPORT_SYMBOL(pci_pme_active); +static int pci_set_wakeup(struct pci_dev *dev, bool enable) +{ + struct pci_bus *bus = dev->bus; + struct pci_host_bridge *bridge; + + /* Handle per device wakeup */ + if (platform_pci_can_wakeup(dev->pmdata)) + return platform_pci_dev_wakeup(dev->pmdata, enable); + + /* Find a parent which can handle wakeup */ + while (bus->parent) { + if (platform_pci_can_wakeup(bus->self->pmdata)) + return platform_pci_bridge_wakeup(bus->self->pmdata, + enable); + + bus = bus->parent; + } + + /* We have reached the root bus. */ + bridge = to_pci_host_bridge(bus->bridge); + if (platform_pci_can_wakeup(bridge->pmdata)) + return platform_pci_bridge_wakeup(bridge->pmdata, enable); + + return 0; +} + /** * pci_enable_wake - enable PCI device as wakeup event source * @dev: PCI device affected @@ -1950,13 +2014,13 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable) pci_pme_active(dev, true); else ret = 1; - error = platform_pci_set_wakeup(dev, true); + error = pci_set_wakeup(dev, true); if (ret) ret = error; if (!ret) dev->wakeup_prepared = true; } else { - platform_pci_set_wakeup(dev, false); + pci_set_wakeup(dev, false); pci_pme_active(dev, false); dev->wakeup_prepared = false; } diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 048668271014..dcefb9e759d7 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -47,21 +47,43 @@ int pci_probe_reset_function(struct pci_dev *dev); * platform; to be used during system-wide transitions from a * sleeping state to the working state and vice versa * - * @set_wakeup: enables/disables wakeup capability for the device - * * @need_resume: returns 'true' if the given device (which is currently * suspended) needs to be resumed to be configured for system * wakeup. + * + * @setup_dev: setup device's wakeup ability, alloc and return device's private + * pm data. + * + * @setup_host_bridge: setup host bridge's wakeup ability, alloc and return host + * bridge's private pm data. + * + * @cleanup: cleanup the private pm data and deinit wakeup ability. + * + * @can_wakeup: returns 'true' if given device is capable of waking up the + * system from a sleeping state. + * + * @dev_wakeup: enables/disables wakeup capability for the device. + * + * @bridge_wakeup: enables/disables wakeup capability for the bridge. */ struct pci_platform_pm_ops { bool (*is_manageable)(struct pci_dev *dev); int (*set_state)(struct pci_dev *dev, pci_power_t state); pci_power_t (*get_state)(struct pci_dev *dev); pci_power_t (*choose_state)(struct pci_dev *dev); - int (*set_wakeup)(struct pci_dev *dev, bool enable); bool (*need_resume)(struct pci_dev *dev); + void *(*setup_dev)(struct pci_dev *dev); + void *(*setup_host_bridge)(struct pci_host_bridge *bridge); + void (*cleanup)(void *pmdata); + bool (*can_wakeup)(void *pmdata); + int (*dev_wakeup)(void *pmdata, bool enable); + int (*bridge_wakeup)(void *pmdata, bool enable); }; +void *platform_pci_setup_dev(struct pci_dev *dev); +void *platform_pci_setup_host_bridge(struct pci_host_bridge *bridge); +void platform_pci_cleanup(void *pmdata); + void pci_set_platform_pm(const struct pci_platform_pm_ops *ops); void pci_update_current_state(struct pci_dev *dev, pci_power_t state); void pci_power_up(struct pci_dev *dev); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index cdc2f83c11c5..b12c552a5522 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -809,7 +809,13 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge) err = device_register(&bus->dev); if (err) - goto unregister; + goto unregister_bridge; + + bridge->pmdata = platform_pci_setup_host_bridge(bridge); + if (IS_ERR(bridge->pmdata)) { + err = PTR_ERR(bridge->pmdata); + goto unregister_bus; + } pcibios_add_bus(bus); @@ -853,7 +859,9 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge) return 0; -unregister: +unregister_bus: + device_unregister(&bus->dev); +unregister_bridge: put_device(&bridge->dev); device_unregister(&bridge->dev); diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index 73a03d382590..d7a8cf1dc69f 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -153,6 +153,8 @@ void pci_remove_root_bus(struct pci_bus *bus) if (!pci_is_root_bus(bus)) return; + platform_pci_cleanup(host_bridge->pmdata); + host_bridge = to_pci_host_bridge(bus->bridge); list_for_each_entry_safe(child, tmp, &bus->devices, bus_list) diff --git a/include/linux/pci.h b/include/linux/pci.h index 80eaa2dbe3e9..628faa58c790 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -293,6 +293,7 @@ struct pci_dev { struct pci_bus *subordinate; /* bus this device bridges to */ void *sysdata; /* hook for sys-specific extension */ + void *pmdata; /* data for platform pm */ struct proc_dir_entry *procent; /* device entry in /proc/bus/pci */ struct pci_slot *slot; /* Physical slot this device is in */ @@ -467,6 +468,7 @@ struct pci_host_bridge { struct pci_bus *bus; /* root bus */ struct pci_ops *ops; void *sysdata; + void *pmdata; /* data for platform pm */ int busnr; struct list_head windows; /* resource_entry */ u8 (*swizzle_irq)(struct pci_dev *, u8 *); /* platform IRQ swizzler */ From patchwork Fri Oct 27 07:26:12 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jeffy Chen X-Patchwork-Id: 831130 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-pci-owner@vger.kernel.org; receiver=) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yNb7p3yk8z9t2x for ; Fri, 27 Oct 2017 18:28:06 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752278AbdJ0H1v (ORCPT ); Fri, 27 Oct 2017 03:27:51 -0400 Received: from regular1.263xmail.com ([211.150.99.130]:45154 "EHLO regular1.263xmail.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752213AbdJ0H1b (ORCPT ); Fri, 27 Oct 2017 03:27:31 -0400 Received: from jeffy.chen?rock-chips.com (unknown [192.168.167.228]) by regular1.263xmail.com (Postfix) with ESMTP id AD229B23F; Fri, 27 Oct 2017 15:27:27 +0800 (CST) X-263anti-spam: KSV:0; X-MAIL-GRAY: 0 X-MAIL-DELIVERY: 1 X-KSVirus-check: 0 X-ABS-CHECKED: 4 Received: from localhost (localhost [127.0.0.1]) by smtp.263.net (Postfix) with ESMTPA id 7801C3C3; Fri, 27 Oct 2017 15:27:24 +0800 (CST) X-RL-SENDER: jeffy.chen@rock-chips.com X-FST-TO: linux-kernel@vger.kernel.org X-SENDER-IP: 103.29.142.67 X-LOGIN-NAME: jeffy.chen@rock-chips.com X-UNIQUE-TAG: <461e159ed8b17613307189c7f4772576> X-ATTACHMENT-NUM: 0 X-SENDER: cjf@rock-chips.com X-DNS-TYPE: 0 Received: from localhost (unknown [103.29.142.67]) by smtp.263.net (Postfix) whith ESMTP id 10507LW0K2A; Fri, 27 Oct 2017 15:27:27 +0800 (CST) From: Jeffy Chen To: linux-kernel@vger.kernel.org, bhelgaas@google.com Cc: linux-pm@vger.kernel.org, tony@atomide.com, shawn.lin@rock-chips.com, briannorris@chromium.org, rjw@rjwysocki.net, dianders@chromium.org, Jeffy Chen , linux-pci@vger.kernel.org Subject: [RFC PATCH v10 7/7] PCI / PM: Add support for the PCIe WAKE# signal for OF Date: Fri, 27 Oct 2017 15:26:12 +0800 Message-Id: <20171027072612.26565-8-jeffy.chen@rock-chips.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20171027072612.26565-1-jeffy.chen@rock-chips.com> References: <20171027072612.26565-1-jeffy.chen@rock-chips.com> Sender: linux-pci-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org Add pci-of.c to handle the PCIe WAKE# interrupt. Also use the dedicated wakeirq infrastructure to simplify it. Signed-off-by: Jeffy Chen --- Changes in v10: Use device_set_wakeup_capable() instead of device_set_wakeup_enable(), since dedicated wakeirq will be lost in device_set_wakeup_enable(false). Changes in v9: Fix check error in .cleanup(). Move dedicated wakeirq setup to setup() callback and use device_set_wakeup_enable() to enable/disable. Changes in v8: Add pci-of.c and use platform_pm_ops to handle the PCIe WAKE# signal. Changes in v7: Move PCIE_WAKE handling into pci core. Changes in v6: Fix device_init_wake error handling, and add some comments. Changes in v5: Rebase. Changes in v3: Fix error handling. Changes in v2: Use dev_pm_set_dedicated_wake_irq. drivers/pci/Makefile | 2 +- drivers/pci/pci-of.c | 127 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 drivers/pci/pci-of.c diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 66a21acad952..4f76dbdb024c 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -49,7 +49,7 @@ obj-$(CONFIG_PCI_ECAM) += ecam.o obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o -obj-$(CONFIG_OF) += of.o +obj-$(CONFIG_OF) += of.o pci-of.o ccflags-$(CONFIG_PCI_DEBUG) := -DDEBUG diff --git a/drivers/pci/pci-of.c b/drivers/pci/pci-of.c new file mode 100644 index 000000000000..28f3c4a0eec8 --- /dev/null +++ b/drivers/pci/pci-of.c @@ -0,0 +1,127 @@ +/* + * OF PCI PM support + * + * Copyright (c) 2017 Rockchip, Inc. + * + * Author: Jeffy Chen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include "pci.h" + +struct of_pci_pm_data { + struct device *dev; + unsigned int wakeup_irq; + atomic_t wakeup_cnt; +}; + +static void *of_pci_setup(struct device *dev) +{ + struct of_pci_pm_data *data; + int irq, ret; + + if (!dev->of_node) + return NULL; + + data = devm_kzalloc(dev, sizeof(struct of_pci_pm_data), GFP_KERNEL); + if (!data) + return ERR_PTR(-ENOMEM); + + irq = of_irq_get_byname(dev->of_node, "wakeup"); + if (irq < 0) { + if (irq == -EPROBE_DEFER) + return ERR_PTR(irq); + + return NULL; + } + + data->wakeup_irq = irq; + data->dev = dev; + + device_init_wakeup(dev, true); + ret = dev_pm_set_dedicated_wake_irq(dev, irq); + if (ret < 0) { + device_init_wakeup(dev, false); + return NULL; + } + device_set_wakeup_capable(dev, false); + + dev_info(dev, "Wakeup IRQ %d\n", irq); + return data; +} + +static void *of_pci_setup_dev(struct pci_dev *pci_dev) +{ + return of_pci_setup(&pci_dev->dev); +} + +static void *of_pci_setup_host_bridge(struct pci_host_bridge *bridge) +{ + return of_pci_setup(bridge->dev.parent); +} + +static void of_pci_cleanup(void *pmdata) +{ + struct of_pci_pm_data *data = pmdata; + + if (!IS_ERR_OR_NULL(data)) { + device_init_wakeup(data->dev, false); + dev_pm_clear_wake_irq(data->dev); + } +} + +static bool of_pci_can_wakeup(void *pmdata) +{ + struct of_pci_pm_data *data = pmdata; + + if (IS_ERR_OR_NULL(data)) + return false; + + return data->wakeup_irq > 0; +} + +static int of_pci_dev_wakeup(void *pmdata, bool enable) +{ + struct of_pci_pm_data *data = pmdata; + struct device *dev = data->dev; + + device_set_wakeup_capable(dev, enable); + return 0; +} + +static int of_pci_bridge_wakeup(void *pmdata, bool enable) +{ + struct of_pci_pm_data *data = pmdata; + + if (enable && atomic_inc_return(&data->wakeup_cnt) != 1) + return 0; + + if (!enable && atomic_dec_return(&data->wakeup_cnt) != 0) + return 0; + + return of_pci_dev_wakeup(pmdata, enable); +} + +static const struct pci_platform_pm_ops of_pci_platform_pm = { + .setup_dev = of_pci_setup_dev, + .setup_host_bridge = of_pci_setup_host_bridge, + .cleanup = of_pci_cleanup, + .can_wakeup = of_pci_can_wakeup, + .dev_wakeup = of_pci_dev_wakeup, + .bridge_wakeup = of_pci_bridge_wakeup, +}; + +static int __init of_pci_init(void) +{ + pci_set_platform_pm(&of_pci_platform_pm); + return 0; +} +arch_initcall(of_pci_init);