From patchwork Fri Aug 3 06:30:34 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Huang, Ying" X-Patchwork-Id: 1268621 Return-Path: X-Original-To: patchwork-linux-pm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by patchwork1.kernel.org (Postfix) with ESMTP id ECED13FD56 for ; Fri, 3 Aug 2012 06:31:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751992Ab2HCGaq (ORCPT ); Fri, 3 Aug 2012 02:30:46 -0400 Received: from mga01.intel.com ([192.55.52.88]:48832 "EHLO mga01.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751723Ab2HCGan (ORCPT ); Fri, 3 Aug 2012 02:30:43 -0400 Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by fmsmga101.fm.intel.com with ESMTP; 02 Aug 2012 23:30:42 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="4.71,315,1320652800"; d="scan'208";a="192770858" Received: from yhuang-dev.sh.intel.com ([10.239.13.28]) by fmsmga001.fm.intel.com with ESMTP; 02 Aug 2012 23:30:41 -0700 From: Huang Ying To: Bjorn Helgaas Cc: linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, linux-pm@vger.kernel.org, "Rafael J. Wysocki" , Huang Ying Subject: [BUGFIX 3/4] PCI/PM: Fix config reg access for D3cold and bridge suspending Date: Fri, 3 Aug 2012 14:30:34 +0800 Message-Id: <1343975435-25469-4-git-send-email-ying.huang@intel.com> X-Mailer: git-send-email 1.7.10.4 In-Reply-To: <1343975435-25469-1-git-send-email-ying.huang@intel.com> References: <1343975435-25469-1-git-send-email-ying.huang@intel.com> Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org This patch fixes the following bug: http://marc.info/?l=linux-pci&m=134338059022620&w=2 Where lspci does not work properly if a device and the corresponding parent bridge (such as PCIe port) is suspended. This is because the device configuration space registers will be not accessible if the corresponding parent bridge is suspended or the device is put into D3cold state. To solve the issue, the bridge/PCIe port connected to the device is put into active state before read/write configuration space registers. If the device is in D3cold state, it will be put into active state too. To avoid resume/suspend PCIe port for each configuration register read/write, a small delay is added before the PCIe port to go suspended. Reported-by: Bjorn Mork Signed-off-by: Huang Ying --- drivers/pci/pci-sysfs.c | 37 +++++++++++++++++++++++++++++++++++++ drivers/pci/pcie/portdrv_pci.c | 9 +++++++++ 2 files changed, 46 insertions(+) -- To unsubscribe from this list: send the line "unsubscribe linux-pm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -458,6 +458,35 @@ boot_vga_show(struct device *dev, struct } struct device_attribute vga_attr = __ATTR_RO(boot_vga); +static void +pci_config_pm_runtime_get(struct pci_dev *pdev) +{ + struct device *dev = &pdev->dev; + struct device *parent = dev->parent; + + if (parent) + pm_runtime_get_sync(parent); + pm_runtime_get_noresume(dev); + /* + * pdev->current_state is set to PCI_D3cold during suspending, + * so wait until suspending completes + */ + pm_runtime_barrier(dev); + if (pdev->current_state == PCI_D3cold) + pm_runtime_resume(dev); +} + +static void +pci_config_pm_runtime_put(struct pci_dev *pdev) +{ + struct device *dev = &pdev->dev; + struct device *parent = dev->parent; + + pm_runtime_put(dev); + if (parent) + pm_runtime_put(parent); +} + static ssize_t pci_read_config(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, @@ -484,6 +513,8 @@ pci_read_config(struct file *filp, struc size = count; } + pci_config_pm_runtime_get(dev); + if ((off & 1) && size) { u8 val; pci_user_read_config_byte(dev, off, &val); @@ -529,6 +560,8 @@ pci_read_config(struct file *filp, struc --size; } + pci_config_pm_runtime_put(dev); + return count; } @@ -549,6 +582,8 @@ pci_write_config(struct file* filp, stru count = size; } + pci_config_pm_runtime_get(dev); + if ((off & 1) && size) { pci_user_write_config_byte(dev, off, data[off - init_off]); off++; @@ -587,6 +622,8 @@ pci_write_config(struct file* filp, stru --size; } + pci_config_pm_runtime_put(dev); + return count; } --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -140,9 +140,17 @@ static int pcie_port_runtime_resume(stru { return 0; } + +static int pcie_port_runtime_idle(struct device *dev) +{ + /* Delay for a short while to prevent too frequent suspend/resume */ + pm_schedule_suspend(dev, 10); + return -EBUSY; +} #else #define pcie_port_runtime_suspend NULL #define pcie_port_runtime_resume NULL +#define pcie_port_runtime_idle NULL #endif static const struct dev_pm_ops pcie_portdrv_pm_ops = { @@ -155,6 +163,7 @@ static const struct dev_pm_ops pcie_port .resume_noirq = pcie_port_resume_noirq, .runtime_suspend = pcie_port_runtime_suspend, .runtime_resume = pcie_port_runtime_resume, + .runtime_idle = pcie_port_runtime_idle, }; #define PCIE_PORTDRV_PM_OPS (&pcie_portdrv_pm_ops)