From patchwork Thu Feb 28 20:49:47 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kuppuswamy Sathyanarayanan X-Patchwork-Id: 10833717 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id AA60017EF for ; Thu, 28 Feb 2019 20:52:38 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 96D6F2F9FE for ; Thu, 28 Feb 2019 20:52:38 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 8A4962FB5C; Thu, 28 Feb 2019 20:52:38 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 1322E2FB59 for ; Thu, 28 Feb 2019 20:52:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2388277AbfB1UwV (ORCPT ); Thu, 28 Feb 2019 15:52:21 -0500 Received: from mga17.intel.com ([192.55.52.151]:16411 "EHLO mga17.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1733234AbfB1UwU (ORCPT ); Thu, 28 Feb 2019 15:52:20 -0500 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga003.jf.intel.com ([10.7.209.27]) by fmsmga107.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 28 Feb 2019 12:52:20 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.58,424,1544515200"; d="scan'208";a="130192189" Received: from skuppusw-desk.jf.intel.com ([10.54.74.33]) by orsmga003.jf.intel.com with ESMTP; 28 Feb 2019 12:52:19 -0800 From: sathyanarayanan.kuppuswamy@linux.intel.com To: bhelgaas@google.com, rjw@rjwysocki.net, lenb@kernel.org, robert.moore@intel.com, erik.schmauss@intel.com, ruscur@russell.cc Cc: linux-pci@vger.kernel.org, linux-acpi@vger.kernel.org, linux-kernel@vger.kernel.org, ashok.raj@intel.com, keith.busch@intel.com, Kuppuswamy Sathyanarayanan Subject: [PATCH v1 1/3] PCI/ACPI: Add _OSC based negotiation support for DPC Date: Thu, 28 Feb 2019 12:49:47 -0800 Message-Id: <54b368cc00409d00b57208c33808c49cc97fa6f1.1551382061.git.sathyanarayanan.kuppuswamy@linux.intel.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: References: MIME-Version: 1.0 Sender: linux-acpi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-acpi@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Kuppuswamy Sathyanarayanan As per PCI Firmware Specification, r3.2 ECN (https://members.pcisig.com/wg/PCI-SIG/document/12614), OS can use bit 7 of _OSC Control Field to negotiate control over Downstream Port Containment (DPC) configuration of PCIe port. After _OSC negotiation, firmware will Set this bit to grant OS control over PCIe DPC configuration and Clear it if this feature was requested and denied, or was not requested. Signed-off-by: Kuppuswamy Sathyanarayanan --- drivers/acpi/pci_root.c | 6 ++++++ drivers/pci/pcie/portdrv_core.c | 3 ++- drivers/pci/probe.c | 1 + include/linux/acpi.h | 3 ++- include/linux/pci.h | 1 + 5 files changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 707aafc7c2aa..a60261e50b08 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -154,6 +154,7 @@ static struct pci_osc_bit_struct pci_osc_control_bit[] = { { OSC_PCI_EXPRESS_AER_CONTROL, "AER" }, { OSC_PCI_EXPRESS_CAPABILITY_CONTROL, "PCIeCapability" }, { OSC_PCI_EXPRESS_LTR_CONTROL, "LTR" }, + { OSC_PCI_EXPRESS_DPC_CONTROL, "DPC" }, }; static void decode_osc_bits(struct acpi_pci_root *root, char *msg, u32 word, @@ -499,6 +500,9 @@ static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm, control |= OSC_PCI_EXPRESS_AER_CONTROL; } + if (IS_ENABLED(CONFIG_PCIE_DPC)) + control |= OSC_PCI_EXPRESS_DPC_CONTROL; + requested = control; status = acpi_pci_osc_control_set(handle, &control, OSC_PCI_EXPRESS_CAPABILITY_CONTROL); @@ -927,6 +931,8 @@ struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root, host_bridge->native_pme = 0; if (!(root->osc_control_set & OSC_PCI_EXPRESS_LTR_CONTROL)) host_bridge->native_ltr = 0; + if (!(root->osc_control_set & OSC_PCI_EXPRESS_DPC_CONTROL)) + host_bridge->native_dpc = 0; pci_scan_child_bus(bus); pci_set_host_bridge_release(host_bridge, acpi_pci_root_release_info, diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index f458ac9cb70c..9d510bbbaf1b 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -247,7 +247,8 @@ static int get_port_device_capability(struct pci_dev *dev) } if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC) && - pci_aer_available() && services & PCIE_PORT_SERVICE_AER) + pci_aer_available() && services & PCIE_PORT_SERVICE_AER && + (pcie_ports_native || host->native_dpc)) services |= PCIE_PORT_SERVICE_DPC; return services; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 257b9f6f2ebb..ca8dbd280deb 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -557,6 +557,7 @@ struct pci_host_bridge *pci_alloc_host_bridge(size_t priv) bridge->native_shpc_hotplug = 1; bridge->native_pme = 1; bridge->native_ltr = 1; + bridge->native_dpc = 1; return bridge; } diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 87715f20b69a..d42dae820410 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -517,7 +517,8 @@ extern bool osc_pc_lpi_support_confirmed; #define OSC_PCI_EXPRESS_AER_CONTROL 0x00000008 #define OSC_PCI_EXPRESS_CAPABILITY_CONTROL 0x00000010 #define OSC_PCI_EXPRESS_LTR_CONTROL 0x00000020 -#define OSC_PCI_CONTROL_MASKS 0x0000003f +#define OSC_PCI_EXPRESS_DPC_CONTROL 0x00000080 +#define OSC_PCI_CONTROL_MASKS 0x000000ff #define ACPI_GSB_ACCESS_ATTRIB_QUICK 0x00000002 #define ACPI_GSB_ACCESS_ATTRIB_SEND_RCV 0x00000004 diff --git a/include/linux/pci.h b/include/linux/pci.h index 65f1d8c2f082..f024508c3456 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -499,6 +499,7 @@ struct pci_host_bridge { unsigned int native_shpc_hotplug:1; /* OS may use SHPC hotplug */ unsigned int native_pme:1; /* OS may use PCIe PME */ unsigned int native_ltr:1; /* OS may use PCIe LTR */ + unsigned int native_dpc:1; /* OS may use PCIe DPC */ /* Resource alignment requirements */ resource_size_t (*align_resource)(struct pci_dev *dev, const struct resource *res, From patchwork Thu Feb 28 20:49:48 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kuppuswamy Sathyanarayanan X-Patchwork-Id: 10833725 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id BA96313B5 for ; Thu, 28 Feb 2019 20:52:45 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id AC10E2F9FE for ; Thu, 28 Feb 2019 20:52:45 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id A0B102FB5C; Thu, 28 Feb 2019 20:52:45 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 30C672F9FE for ; Thu, 28 Feb 2019 20:52:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2388377AbfB1Uwj (ORCPT ); Thu, 28 Feb 2019 15:52:39 -0500 Received: from mga17.intel.com ([192.55.52.151]:16411 "EHLO mga17.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2388186AbfB1UwV (ORCPT ); Thu, 28 Feb 2019 15:52:21 -0500 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga003.jf.intel.com ([10.7.209.27]) by fmsmga107.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 28 Feb 2019 12:52:20 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.58,424,1544515200"; d="scan'208";a="130192192" Received: from skuppusw-desk.jf.intel.com ([10.54.74.33]) by orsmga003.jf.intel.com with ESMTP; 28 Feb 2019 12:52:19 -0800 From: sathyanarayanan.kuppuswamy@linux.intel.com To: bhelgaas@google.com, rjw@rjwysocki.net, lenb@kernel.org, robert.moore@intel.com, erik.schmauss@intel.com, ruscur@russell.cc Cc: linux-pci@vger.kernel.org, linux-acpi@vger.kernel.org, linux-kernel@vger.kernel.org, ashok.raj@intel.com, keith.busch@intel.com, Kuppuswamy Sathyanarayanan Subject: [PATCH v1 2/3] ACPI: Add Error Disconnect Recover (EDR) ACPI notifier support Date: Thu, 28 Feb 2019 12:49:48 -0800 Message-Id: <5229ccc3bbb1792ba625b7c54f5ad6f101826454.1551382061.git.sathyanarayanan.kuppuswamy@linux.intel.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: References: MIME-Version: 1.0 Sender: linux-acpi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-acpi@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Kuppuswamy Sathyanarayanan As per ACPI spec r6.3, sec 6.3.5, Error Disconnect Recover notification can be used to notify OSPM of asynchronous removal of devices for error containment purposes. OSPM can use this notification to invalidate software states of disconnected child devices in the bus and also optionally can attempt recover the child devices. Signed-off-by: Kuppuswamy Sathyanarayanan --- drivers/acpi/acpica/utdecode.c | 3 ++- drivers/acpi/bus.c | 5 ++++- include/acpi/actypes.h | 3 ++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/acpica/utdecode.c b/drivers/acpi/acpica/utdecode.c index dad02b821e19..40d36c6661f1 100644 --- a/drivers/acpi/acpica/utdecode.c +++ b/drivers/acpi/acpica/utdecode.c @@ -431,7 +431,8 @@ static const char *acpi_gbl_generic_notify[ACPI_GENERIC_NOTIFY_MAX + 1] = { /* Reserved in ACPI 6.0 */ /* 0D */ "System Resource Affinity Update", /* 0E */ "Heterogeneous Memory Attributes Update" - /* ACPI 6.2 */ + /* 0F */ "Error Disconnect Recover", + /* ACPI 6.3 */ }; static const char *acpi_gbl_device_notify[5] = { diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 147f6c7ea59c..aacf82162604 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -397,7 +397,10 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data) case ACPI_NOTIFY_POWER_FAULT: acpi_handle_err(handle, "Device has suffered a power fault\n"); break; - + case ACPI_NOTIFY_ERROR_DISCONNECT: + acpi_handle_debug(handle, + "ACPI_NOTIFY_ERROR_DISCONNECT event\n"); + break; default: acpi_handle_debug(handle, "Unknown event type 0x%x\n", type); break; diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index 2590627dbfcc..40ad91b06f02 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -617,8 +617,9 @@ typedef u64 acpi_integer; #define ACPI_NOTIFY_SHUTDOWN_REQUEST (u8) 0x0C #define ACPI_NOTIFY_AFFINITY_UPDATE (u8) 0x0D #define ACPI_NOTIFY_MEMORY_UPDATE (u8) 0x0E +#define ACPI_NOTIFY_ERROR_DISCONNECT (u8) 0x0F -#define ACPI_GENERIC_NOTIFY_MAX 0x0E +#define ACPI_GENERIC_NOTIFY_MAX 0x0F #define ACPI_SPECIFIC_NOTIFY_MAX 0x84 /* From patchwork Thu Feb 28 20:49:49 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kuppuswamy Sathyanarayanan X-Patchwork-Id: 10833721 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id C69141390 for ; Thu, 28 Feb 2019 20:52:39 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id B9CF92F9FE for ; Thu, 28 Feb 2019 20:52:39 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id ADAC92FB52; Thu, 28 Feb 2019 20:52:39 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A2E372FB4E for ; Thu, 28 Feb 2019 20:52:37 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2388294AbfB1UwW (ORCPT ); Thu, 28 Feb 2019 15:52:22 -0500 Received: from mga17.intel.com ([192.55.52.151]:16411 "EHLO mga17.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2388264AbfB1UwV (ORCPT ); Thu, 28 Feb 2019 15:52:21 -0500 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga003.jf.intel.com ([10.7.209.27]) by fmsmga107.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 28 Feb 2019 12:52:20 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.58,424,1544515200"; d="scan'208";a="130192197" Received: from skuppusw-desk.jf.intel.com ([10.54.74.33]) by orsmga003.jf.intel.com with ESMTP; 28 Feb 2019 12:52:19 -0800 From: sathyanarayanan.kuppuswamy@linux.intel.com To: bhelgaas@google.com, rjw@rjwysocki.net, lenb@kernel.org, robert.moore@intel.com, erik.schmauss@intel.com, ruscur@russell.cc Cc: linux-pci@vger.kernel.org, linux-acpi@vger.kernel.org, linux-kernel@vger.kernel.org, ashok.raj@intel.com, keith.busch@intel.com, Kuppuswamy Sathyanarayanan Subject: [PATCH v1 3/3] PCI/DPC: Add Error Disconnect Recover (EDR) support Date: Thu, 28 Feb 2019 12:49:49 -0800 Message-Id: X-Mailer: git-send-email 2.20.1 In-Reply-To: References: MIME-Version: 1.0 Sender: linux-acpi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-acpi@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Kuppuswamy Sathyanarayanan As per PCI firmware specification v3.2 ECN (https://members.pcisig.com/wg/PCI-SIG/document/12614), when firmware owns Downstream Port Containment (DPC), its expected to use the "Error Disconnect Recover" (EDR) notification to alert OSPM of a DPC event and if OS supports EDR, its expected to handle the software state invalidation and port recovery in OS and let firmware know the recovery status via _OST ACPI call. Since EDR is a hybrid service, DPC service shall be enabled in OS even if AER is not natively enabled in OS. Signed-off-by: Kuppuswamy Sathyanarayanan --- drivers/pci/pcie/Kconfig | 10 + drivers/pci/pcie/dpc.c | 326 ++++++++++++++++++++++++++++++-- drivers/pci/pcie/portdrv_core.c | 9 +- 3 files changed, 329 insertions(+), 16 deletions(-) diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig index 44742b2e1126..c964a2288a14 100644 --- a/drivers/pci/pcie/Kconfig +++ b/drivers/pci/pcie/Kconfig @@ -143,3 +143,13 @@ config PCIE_PTM This is only useful if you have devices that support PTM, but it is safe to enable even if you don't. + +config PCIE_EDR + bool "PCI Express Error Disconnect Recover support" + default n + depends on PCIE_DPC && ACPI + help + This options adds Error Disconnect Recover support as specified + in PCI firmware specification v3.2 Downstream Port Containment + Related Enhancements ECN. Enable this if you want to support hybrid + DPC model which uses both firmware and OS to implement DPC. diff --git a/drivers/pci/pcie/dpc.c b/drivers/pci/pcie/dpc.c index e435d12e61a0..1b4098097fac 100644 --- a/drivers/pci/pcie/dpc.c +++ b/drivers/pci/pcie/dpc.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "portdrv.h" #include "../pci.h" @@ -20,8 +21,23 @@ struct dpc_dev { u16 cap_pos; bool rp_extensions; u8 rp_log_size; + bool native_dpc; + pci_ers_result_t error_state; +#ifdef CONFIG_ACPI + struct acpi_device *adev; +#endif }; +#ifdef CONFIG_ACPI + +#define EDR_PORT_ENABLE_DSM 0x0C +#define EDR_PORT_LOCATE_DSM 0x0D + +static const guid_t pci_acpi_dsm_guid = + GUID_INIT(0xe5c937d0, 0x3553, 0x4d7a, + 0x91, 0x17, 0xea, 0x4d, 0x19, 0xc3, 0x43, 0x4d); +#endif + static const char * const rp_pio_error_string[] = { "Configuration Request received UR Completion", /* Bit Position 0 */ "Configuration Request received CA Completion", /* Bit Position 1 */ @@ -67,6 +83,9 @@ void pci_save_dpc_state(struct pci_dev *dev) if (!dpc) return; + if (!dpc->native_dpc) + return; + save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_DPC); if (!save_state) return; @@ -88,6 +107,9 @@ void pci_restore_dpc_state(struct pci_dev *dev) if (!dpc) return; + if (!dpc->native_dpc) + return; + save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_DPC); if (!save_state) return; @@ -202,10 +224,9 @@ static void dpc_process_rp_pio_error(struct dpc_dev *dpc) pci_write_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_STATUS, status); } -static irqreturn_t dpc_handler(int irq, void *context) +static void dpc_process_error(struct dpc_dev *dpc) { struct aer_err_info info; - struct dpc_dev *dpc = context; struct pci_dev *pdev = dpc->dev->port; struct device *dev = &dpc->dev->device; u16 cap = dpc->cap_pos, status, source, reason, ext_reason; @@ -236,6 +257,13 @@ static irqreturn_t dpc_handler(int irq, void *context) /* We configure DPC so it only triggers on ERR_FATAL */ pcie_do_recovery(pdev, pci_channel_io_frozen, PCIE_PORT_SERVICE_DPC); +} + +static irqreturn_t dpc_handler(int irq, void *context) +{ + struct dpc_dev *dpc = context; + + dpc_process_error(dpc); return IRQ_HANDLED; } @@ -258,6 +286,230 @@ static irqreturn_t dpc_irq(int irq, void *context) return IRQ_HANDLED; } +void dpc_error_resume(struct pci_dev *dev) +{ + struct dpc_dev *dpc; + + dpc = to_dpc_dev(dev); + if (!dpc) + return; + + dpc->error_state = PCI_ERS_RESULT_RECOVERED; +} + +#ifdef CONFIG_ACPI + +/* + * _DSM wrapper function to enable/disable DPC port. + * @dpc : DPC device structure + * @enable: status of DPC port (0 or 1). + * + * returns 0 on success or errno on failure. + */ +static int acpi_enable_dpc_port(struct dpc_dev *dpc, bool enable) +{ + union acpi_object *obj; + int status; + union acpi_object argv4; + + /* Check whether EDR_PORT_ENABLE_DSM is supported in firmware */ + status = acpi_check_dsm(dpc->adev->handle, &pci_acpi_dsm_guid, 1, + 1 << EDR_PORT_ENABLE_DSM); + if (!status) + return -ENOTSUPP; + + argv4.type = ACPI_TYPE_INTEGER; + argv4.integer.value = enable; + + obj = acpi_evaluate_dsm(dpc->adev->handle, &pci_acpi_dsm_guid, 1, + EDR_PORT_ENABLE_DSM, &argv4); + if (!obj) + return -EIO; + + if (obj->type == ACPI_TYPE_INTEGER && obj->integer.value == enable) + status = 0; + else + status = -EIO; + + ACPI_FREE(obj); + + return status; +} + +/* + * _DSM wrapper function to locate DPC port. + * @dpc : DPC device structure + * + * returns pci_dev or NULL. + */ +static struct pci_dev *acpi_locate_dpc_port(struct dpc_dev *dpc) +{ + union acpi_object *obj; + int status; + u16 port; + + /* Check whether EDR_PORT_LOCATE_DSM is supported in firmware */ + status = acpi_check_dsm(dpc->adev->handle, &pci_acpi_dsm_guid, 1, + 1 << EDR_PORT_LOCATE_DSM); + if (!status) + return dpc->dev->port; + + + obj = acpi_evaluate_dsm(dpc->adev->handle, &pci_acpi_dsm_guid, 1, + EDR_PORT_LOCATE_DSM, NULL); + if (!obj) + return NULL; + + if (obj->type == ACPI_TYPE_INTEGER) { + /* + * Firmware returns DPC port BDF details in following format: + * 15:8 = bus + * 7:3 = device + * 2:0 = function + */ + port = obj->integer.value; + ACPI_FREE(obj); + } else { + ACPI_FREE(obj); + return NULL; + } + + return pci_get_domain_bus_and_slot(0, PCI_BUS_NUM(port), port & 0xff); +} + +/* + * _OST wrapper function to let firmware know the status of EDR event. + * @dpc : DPC device structure. + * @status: Status of EDR event. + * + */ +static int acpi_send_edr_status(struct dpc_dev *dpc, u16 status) +{ + u32 ost_status; + struct pci_dev *pdev = dpc->dev->port; + + dev_dbg(&pdev->dev, "Sending EDR status :%x\n", status); + + ost_status = PCI_DEVID(pdev->bus->number, pdev->devfn); + ost_status = (ost_status << 16) | status; + + if (!acpi_has_method(dpc->adev->handle, "_OST")) + return -ENOTSUPP; + + status = acpi_evaluate_ost(dpc->adev->handle, + ACPI_NOTIFY_ERROR_DISCONNECT, + ost_status, NULL); + if (ACPI_FAILURE(status)) + return -EINVAL; + + return 0; +} + +/* + * Helper function used for disconnecting the child devices when EDR event is + * received from firmware. + */ +static void dpc_disconnect_devices(struct pci_dev *dev) +{ + struct pci_dev *udev; + struct pci_bus *parent; + struct pci_dev *pdev, *temp; + + dev_dbg(&dev->dev, "Disconnecting the child devices\n"); + + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) + udev = dev; + else + udev = dev->bus->self; + + parent = udev->subordinate; + pci_walk_bus(parent, pci_dev_set_disconnected, NULL); + + pci_lock_rescan_remove(); + pci_dev_get(dev); + list_for_each_entry_safe_reverse(pdev, temp, &parent->devices, + bus_list) { + pci_stop_and_remove_bus_device(pdev); + } + pci_dev_put(dev); + pci_unlock_rescan_remove(); +} + +static void edr_handle_event(acpi_handle handle, u32 event, void *data) +{ + struct dpc_dev *dpc = (struct dpc_dev *) data; + struct pci_dev *pdev; + u16 status, cap; + + if (event != ACPI_NOTIFY_ERROR_DISCONNECT) + return; + + if (!data) { + pr_err("Invalid EDR event\n"); + return; + } + + dev_dbg(&dpc->dev->port->dev, "Valid EDR event received\n"); + + /* + * Check if _DSM(0xD) is available, and if present locate the + * port which issued EDR event. + */ + pdev = acpi_locate_dpc_port(dpc); + if (!pdev) { + dev_err(&dpc->dev->port->dev, "No valid port found\n"); + return; + } + + /* + * Get DPC priv data for given pdev + */ + dpc = to_dpc_dev(pdev); + dpc->error_state = PCI_ERS_RESULT_DISCONNECT; + pdev = dpc->dev->port; + cap = dpc->cap_pos; + + /* + * Check if the port supports DPC: + * + * if port does not support DPC, then let firmware handle + * the error recovery and OS is responsible for cleaning + * up the child devices. + * + * if port supports DPC, then fall back to default error + * recovery. + * + */ + if (cap) { + /* Check if there is a valid DPC trigger */ + pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status); + if (!(status & PCI_EXP_DPC_STATUS_TRIGGER)) { + dev_err(&pdev->dev, "Invalid DPC trigger\n"); + return; + } + dpc_process_error(dpc); + } + + if (dpc->error_state == PCI_ERS_RESULT_RECOVERED) { + /* + * Recovery is successful, so send + * _OST(0xF, BDF << 16 | 0x80, "") to firmware. + */ + status = 0x80; + } else { + /* + * Recovery is not successful, so disconnect child devices + * and send _OST(0xF, BDF << 16 | 0x81, "") to firmware. + */ + dpc_disconnect_devices(pdev); + status = 0x81; + } + + acpi_send_edr_status(dpc, status); +} + +#endif + #define FLAG(x, y) (((x) & (y)) ? '+' : '-') static int dpc_probe(struct pcie_device *dev) { @@ -266,9 +518,10 @@ static int dpc_probe(struct pcie_device *dev) struct device *device = &dev->device; int status; u16 ctl, cap; - - if (pcie_aer_get_firmware_first(pdev)) - return -ENOTSUPP; +#ifdef CONFIG_ACPI + struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); + acpi_status astatus; +#endif dpc = devm_kzalloc(device, sizeof(*dpc), GFP_KERNEL); if (!dpc) @@ -277,16 +530,53 @@ static int dpc_probe(struct pcie_device *dev) dpc->cap_pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_DPC); dpc->dev = dev; set_service_data(dev, dpc); + dpc->error_state = PCI_ERS_RESULT_NONE; - status = devm_request_threaded_irq(device, dev->irq, dpc_irq, - dpc_handler, IRQF_SHARED, - "pcie-dpc", dpc); - if (status) { - dev_warn(device, "request IRQ%d failed: %d\n", dev->irq, - status); - return status; + if (!pcie_aer_get_firmware_first(pdev)) + if (pci_aer_available() && dpc->cap_pos) + dpc->native_dpc = 1; + + /* + * If native support is not enabled and ACPI is not + * enabled then return error. + */ + if (!dpc->native_dpc && !IS_ENABLED(CONFIG_APCI)) + return -ENODEV; + + if (dpc->native_dpc) { + status = devm_request_threaded_irq(device, dev->irq, dpc_irq, + dpc_handler, IRQF_SHARED, + "pcie-dpc", dpc); + if (status) { + dev_warn(device, "request IRQ%d failed: %d\n", dev->irq, + status); + return status; + } } +#ifdef CONFIG_ACPI + if (!dpc->native_dpc) { + if (!adev) { + dev_err(device, "No valid acpi device found\n"); + return -ENODEV; + } + + dpc->adev = adev; + + /* Register ACPI notifier for EDR event */ + astatus = acpi_install_notify_handler(adev->handle, + ACPI_SYSTEM_NOTIFY, + edr_handle_event, + dpc); + + if (ACPI_FAILURE(astatus)) { + dev_err(device, "Install notifier failed\n"); + return -EBUSY; + } + + acpi_enable_dpc_port(dpc, true); + } +#endif pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CAP, &cap); pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, &ctl); @@ -300,8 +590,12 @@ static int dpc_probe(struct pcie_device *dev) } } - ctl = (ctl & 0xfff4) | PCI_EXP_DPC_CTL_EN_FATAL | PCI_EXP_DPC_CTL_INT_EN; - pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, ctl); + if (dpc->native_dpc) { + ctl = (ctl & 0xfff4) | PCI_EXP_DPC_CTL_EN_FATAL | + PCI_EXP_DPC_CTL_INT_EN; + pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, + ctl); + } dev_info(device, "DPC error containment capabilities: Int Msg #%d, RPExt%c PoisonedTLP%c SwTrigger%c RP PIO Log %d, DL_ActiveErr%c\n", cap & PCI_EXP_DPC_IRQ, FLAG(cap, PCI_EXP_DPC_CAP_RP_EXT), @@ -319,6 +613,9 @@ static void dpc_remove(struct pcie_device *dev) struct pci_dev *pdev = dev->port; u16 ctl; + if (!dpc->native_dpc) + return; + pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, &ctl); ctl &= ~(PCI_EXP_DPC_CTL_EN_FATAL | PCI_EXP_DPC_CTL_INT_EN); pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, ctl); @@ -331,6 +628,7 @@ static struct pcie_port_service_driver dpcdriver = { .probe = dpc_probe, .remove = dpc_remove, .reset_link = dpc_reset_link, + .error_resume = dpc_error_resume, }; int __init pcie_dpc_init(void) diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 9d510bbbaf1b..2e5c3b0bbd47 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -246,9 +246,14 @@ static int get_port_device_capability(struct pci_dev *dev) pcie_pme_interrupt_enable(dev, false); } + /* + * If EDR support is enabled in OS, then even if AER is not handled in + * OS, DPC service can be enabled. + */ if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC) && - pci_aer_available() && services & PCIE_PORT_SERVICE_AER && - (pcie_ports_native || host->native_dpc)) + ((IS_ENABLED(CONFIG_PCIE_EDR) && !host->native_dpc) || + (pci_aer_available() && services & PCIE_PORT_SERVICE_AER && + (pcie_ports_native || host->native_dpc)))) services |= PCIE_PORT_SERVICE_DPC; return services;