1 // SPDX-License-Identifier: GPL-2.0
3 * PCI Error Disconnect Recover support
4 * Author: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
6 * Copyright (C) 2020 Intel Corp.
9 #define dev_fmt(fmt) "EDR: " fmt
11 #include <linux/pci.h>
12 #include <linux/pci-acpi.h>
17 #define EDR_PORT_DPC_ENABLE_DSM 0x0C
18 #define EDR_PORT_LOCATE_DSM 0x0D
19 #define EDR_OST_SUCCESS 0x80
20 #define EDR_OST_FAILED 0x81
23 * _DSM wrapper function to enable/disable DPC
24 * @pdev : PCI device structure
26 * returns 0 on success or errno on failure.
28 static int acpi_enable_dpc(struct pci_dev
*pdev
)
30 struct acpi_device
*adev
= ACPI_COMPANION(&pdev
->dev
);
31 union acpi_object
*obj
, argv4
, req
;
35 * Per PCI Firmware r3.3, sec 4.6.12, EDR_PORT_DPC_ENABLE_DSM is
36 * optional. Return success if it's not implemented.
38 if (!acpi_check_dsm(adev
->handle
, &pci_acpi_dsm_guid
, 6,
39 1ULL << EDR_PORT_DPC_ENABLE_DSM
))
42 req
.type
= ACPI_TYPE_INTEGER
;
43 req
.integer
.value
= 1;
45 argv4
.type
= ACPI_TYPE_PACKAGE
;
46 argv4
.package
.count
= 1;
47 argv4
.package
.elements
= &req
;
49 obj
= acpi_evaluate_dsm(adev
->handle
, &pci_acpi_dsm_guid
, 6,
50 EDR_PORT_DPC_ENABLE_DSM
, &argv4
);
54 if (obj
->type
!= ACPI_TYPE_INTEGER
) {
55 pci_err(pdev
, FW_BUG
"Enable DPC _DSM returned non integer\n");
59 if (obj
->integer
.value
!= 1) {
60 pci_err(pdev
, "Enable DPC _DSM failed to enable DPC\n");
70 * _DSM wrapper function to locate DPC port
71 * @pdev : Device which received EDR event
73 * Returns pci_dev or NULL. Caller is responsible for dropping a reference
74 * on the returned pci_dev with pci_dev_put().
76 static struct pci_dev
*acpi_dpc_port_get(struct pci_dev
*pdev
)
78 struct acpi_device
*adev
= ACPI_COMPANION(&pdev
->dev
);
79 union acpi_object
*obj
;
83 * If EDR_PORT_LOCATE_DSM is not implemented under the target of
84 * EDR, the target is the port that experienced the containment
85 * event (PCI Firmware r3.3, sec 4.6.13).
87 if (!acpi_check_dsm(adev
->handle
, &pci_acpi_dsm_guid
, 5,
88 1ULL << EDR_PORT_LOCATE_DSM
))
89 return pci_dev_get(pdev
);
91 obj
= acpi_evaluate_dsm(adev
->handle
, &pci_acpi_dsm_guid
, 5,
92 EDR_PORT_LOCATE_DSM
, NULL
);
94 return pci_dev_get(pdev
);
96 if (obj
->type
!= ACPI_TYPE_INTEGER
) {
98 pci_err(pdev
, FW_BUG
"Locate Port _DSM returned non integer\n");
103 * Bit 31 represents the success/failure of the operation. If bit
104 * 31 is set, the operation failed.
106 if (obj
->integer
.value
& BIT(31)) {
108 pci_err(pdev
, "Locate Port _DSM failed\n");
113 * Firmware returns DPC port BDF details in following format:
118 port
= obj
->integer
.value
;
122 return pci_get_domain_bus_and_slot(pci_domain_nr(pdev
->bus
),
123 PCI_BUS_NUM(port
), port
& 0xff);
127 * _OST wrapper function to let firmware know the status of EDR event
128 * @pdev : Device used to send _OST
129 * @edev : Device which experienced EDR event
130 * @status : Status of EDR event
132 static int acpi_send_edr_status(struct pci_dev
*pdev
, struct pci_dev
*edev
,
135 struct acpi_device
*adev
= ACPI_COMPANION(&pdev
->dev
);
138 pci_dbg(pdev
, "Status for %s: %#x\n", pci_name(edev
), status
);
140 ost_status
= PCI_DEVID(edev
->bus
->number
, edev
->devfn
) << 16;
141 ost_status
|= status
;
143 status
= acpi_evaluate_ost(adev
->handle
, ACPI_NOTIFY_DISCONNECT_RECOVER
,
145 if (ACPI_FAILURE(status
))
151 static void edr_handle_event(acpi_handle handle
, u32 event
, void *data
)
153 struct pci_dev
*pdev
= data
, *edev
;
154 pci_ers_result_t estate
= PCI_ERS_RESULT_DISCONNECT
;
157 if (event
!= ACPI_NOTIFY_DISCONNECT_RECOVER
)
161 * pdev is a Root Port or Downstream Port that is still present and
162 * has triggered a containment event, e.g., DPC, so its child
163 * devices have been disconnected (ACPI r6.5, sec 5.6.6).
165 pci_info(pdev
, "EDR event received\n");
168 * Locate the port that experienced the containment event. pdev
169 * may be that port or a parent of it (PCI Firmware r3.3, sec
172 edev
= acpi_dpc_port_get(pdev
);
174 pci_err(pdev
, "Firmware failed to locate DPC port\n");
178 pci_dbg(pdev
, "Reported EDR dev: %s\n", pci_name(edev
));
180 /* If port does not support DPC, just send the OST */
181 if (!edev
->dpc_cap
) {
182 pci_err(edev
, FW_BUG
"This device doesn't support DPC\n");
186 /* Check if there is a valid DPC trigger */
187 pci_read_config_word(edev
, edev
->dpc_cap
+ PCI_EXP_DPC_STATUS
, &status
);
188 if (!(status
& PCI_EXP_DPC_STATUS_TRIGGER
)) {
189 pci_err(edev
, "Invalid DPC trigger %#010x\n", status
);
193 dpc_process_error(edev
);
194 pci_aer_raw_clear_status(edev
);
197 * Irrespective of whether the DPC event is triggered by ERR_FATAL
198 * or ERR_NONFATAL, since the link is already down, use the FATAL
199 * error recovery path for both cases.
201 estate
= pcie_do_recovery(edev
, pci_channel_io_frozen
, dpc_reset_link
);
206 * If recovery is successful, send _OST(0xF, BDF << 16 | 0x80)
207 * to firmware. If not successful, send _OST(0xF, BDF << 16 | 0x81).
209 if (estate
== PCI_ERS_RESULT_RECOVERED
) {
210 pci_dbg(edev
, "DPC port successfully recovered\n");
211 pcie_clear_device_status(edev
);
212 acpi_send_edr_status(pdev
, edev
, EDR_OST_SUCCESS
);
214 pci_dbg(edev
, "DPC port recovery failed\n");
215 acpi_send_edr_status(pdev
, edev
, EDR_OST_FAILED
);
221 void pci_acpi_add_edr_notifier(struct pci_dev
*pdev
)
223 struct acpi_device
*adev
= ACPI_COMPANION(&pdev
->dev
);
227 pci_dbg(pdev
, "No valid ACPI node, skipping EDR init\n");
231 status
= acpi_install_notify_handler(adev
->handle
, ACPI_SYSTEM_NOTIFY
,
232 edr_handle_event
, pdev
);
233 if (ACPI_FAILURE(status
)) {
234 pci_err(pdev
, "Failed to install notify handler\n");
238 if (acpi_enable_dpc(pdev
))
239 acpi_remove_notify_handler(adev
->handle
, ACPI_SYSTEM_NOTIFY
,
242 pci_dbg(pdev
, "Notify handler installed\n");
245 void pci_acpi_remove_edr_notifier(struct pci_dev
*pdev
)
247 struct acpi_device
*adev
= ACPI_COMPANION(&pdev
->dev
);
252 acpi_remove_notify_handler(adev
->handle
, ACPI_SYSTEM_NOTIFY
,
254 pci_dbg(pdev
, "Notify handler removed\n");