From patchwork Mon Aug 24 08:11:33 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Shaohua Li X-Patchwork-Id: 43564 Received: from vger.kernel.org (vger.kernel.org [209.132.176.167]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id n7O8ACS4000436 for ; Mon, 24 Aug 2009 08:11:36 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751081AbZHXILd (ORCPT ); Mon, 24 Aug 2009 04:11:33 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1751410AbZHXILd (ORCPT ); Mon, 24 Aug 2009 04:11:33 -0400 Received: from mga01.intel.com ([192.55.52.88]:4982 "EHLO mga01.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751081AbZHXILc (ORCPT ); Mon, 24 Aug 2009 04:11:32 -0400 Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by fmsmga101.fm.intel.com with ESMTP; 24 Aug 2009 01:08:45 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="4.44,264,1249282800"; d="scan'208";a="719994866" Received: from sli10-conroe.sh.intel.com (HELO [10.239.13.175]) ([10.239.13.175]) by fmsmga001.fm.intel.com with ESMTP; 24 Aug 2009 01:14:40 -0700 Subject: [PATCH 1/2] handle wakeup event in PCI From: Shaohua Li To: "Rafael J. Wysocki" Cc: pm list , linux-pci Date: Mon, 24 Aug 2009 16:11:33 +0800 Message-Id: <1251101493.22288.53.camel@sli10-desk.sh.intel.com> Mime-Version: 1.0 X-Mailer: Evolution 2.26.1 Sender: linux-pci-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org Add an implementation how to detect wakeup event for PCI. PCI device can invoke PME, platform or PCIe native approach can collect the event and report to OS. OS should identify exactly which device invokes PME as several devices can share PME pin. In platform approach (ACPI in this case), some BIOS give exact device which invokes PME but others doesn't. In PCIe native approach, if PME source device is a pcie endpoint, the device is the exact PME source. If the device is root port or pcie-to-pci bridge, we need scan the hierarchy under the device. To identify PME source, the patch does: 1. if the source is a pci device, the device is the only source for PME 2. if the source is a bridge, scan the hierarchy under the bridge. Several devices under the bridge could be the sources. Signed-off-by: Shaohua Li --- drivers/pci/pci-driver.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++ drivers/pci/pci.h | 2 + 2 files changed, 79 insertions(+) -- To unsubscribe from this list: send the line "unsubscribe linux-pci" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Index: linux/drivers/pci/pci-driver.c =================================================================== --- linux.orig/drivers/pci/pci-driver.c 2009-08-24 10:50:12.000000000 +0800 +++ linux/drivers/pci/pci-driver.c 2009-08-24 15:42:49.000000000 +0800 @@ -17,6 +17,7 @@ #include #include #include +#include #include "pci.h" /* @@ -570,6 +571,82 @@ static void pci_pm_complete(struct devic drv->pm->complete(dev); } +/* + * Called when dev is suspected to invoke a wakeup event + * */ +static bool pci_pm_check_wakeup_event(struct pci_dev *pdev) +{ + int pme_pos = pdev->pm_cap; + u16 pmcsr; + bool spurious = false; + + if (pme_pos == 0) { + /* + * Some USB devices haven't PME, but have specific registers to + * control wakeup + */ + return false; + } + + /* clear PME status and disable PME to avoid interrupt flood */ + pci_read_config_word(pdev, pme_pos + PCI_PM_CTRL, &pmcsr); + if (!(pmcsr & PCI_PM_CTRL_PME_STATUS)) + return false; + /* I see spurious PME here, just ignore it for now */ + if (!(pmcsr & PCI_PM_CTRL_PME_ENABLE)) + spurious = true; + else + pmcsr &= ~PCI_PM_CTRL_PME_ENABLE; + pmcsr |= PCI_PM_CTRL_PME_STATUS; + pci_write_config_word(pdev, pme_pos + PCI_PM_CTRL, pmcsr); + + if (spurious) + return false; + return true; +} + +/* @target is suspected to invoke a wakeup event. Return true if it's true */ +bool pci_pm_handle_wakeup_event(struct pci_dev *target) +{ + bool ret; + struct pci_dev *tmp = NULL; + int domain_nr, bus_start, bus_end; + + /* + * @target could be a bridge or a device. + * if target is device, trust the device invokes PME. If target is a + * bridge, scan devices under the bridge and only trust device invokes + * PME which we can detect + **/ + ret = pci_pm_check_wakeup_event(target); + if (!target->subordinate || (target->is_pcie && + target->pcie_type != PCI_EXP_TYPE_ROOT_PORT && + target->pcie_type != PCI_EXP_TYPE_PCI_BRIDGE)) { + /* always trust the device invokes PME even we can't detect */ + pm_request_resume(&target->dev); + return true; + } + + if (ret) + pm_request_resume(&target->dev); + + /* scan devices under the bridge */ + domain_nr = pci_domain_nr(target->bus); + bus_start = target->subordinate->secondary; + bus_end = target->subordinate->subordinate; + while ((tmp = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, tmp)) != NULL) { + if (pci_domain_nr(tmp->bus) == domain_nr && + tmp->bus->number >= bus_start && + tmp->bus->number <= bus_end) { + if (pci_pm_check_wakeup_event(tmp)) { + ret = true; + pm_request_resume(&tmp->dev); + } + } + } + return ret; +} + #ifdef CONFIG_SUSPEND static int pci_pm_suspend(struct device *dev) Index: linux/drivers/pci/pci.h =================================================================== --- linux.orig/drivers/pci/pci.h 2009-08-24 11:04:22.000000000 +0800 +++ linux/drivers/pci/pci.h 2009-08-24 11:05:24.000000000 +0800 @@ -83,6 +83,8 @@ static inline void pci_vpd_release(struc dev->vpd->ops->release(dev); } +extern bool pci_pm_handle_wakeup_event(struct pci_dev *target); + /* PCI /proc functions */ #ifdef CONFIG_PROC_FS extern int pci_proc_attach_device(struct pci_dev *dev);