Install ACPI 0x80 notify handler for CXL isolation, based on the result of the _OSC negotiation. The handler is once installed per CXL host bridge (HID of "ACPI0016") as part of isolation set up. A link for the ECN (expected in CXL 4.0 spec) that introduces the relevant parts of the _OSC method (CXL 3.2 9.18.2) can be found below. spec). This link is only accesible to CXL SSWG members, so here's a brief overview: The ECN introduces a field in the _OSC method to control how the OSPM is notified isolation has occurred. If the ERR_COR Signaling Supported bit in the isolation capability register (CXL 3.2 8.2.4.24.1) is NOT set, this portion of the _OSC can be ignored. If the OSPM is given control of isolation notification, the mechanism to be used when isolation occurs is an MSI/-X interrupt (pre-ECN behavior). If the platorm firmware reserves control, the OSPM will be notified through a ACPI 0x80 notify on the CXL host bridge ACPI device (ACPI HID: "ACPI0016"). Link: https://members.computeexpresslink.org/wg/software_systems/document/3118 Signed-off-by: Ben Cheatham <Benjamin.Cheatham@xxxxxxx> --- drivers/cxl/acpi.c | 51 +++++++++++++++++++++++++++++++++++++++++ drivers/cxl/core/port.c | 41 +++++++++++++++++++++++++-------- drivers/cxl/cxl.h | 3 +++ 3 files changed, 86 insertions(+), 9 deletions(-) diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c index b964f02fb56b..145a03f15255 100644 --- a/drivers/cxl/acpi.c +++ b/drivers/cxl/acpi.c @@ -390,10 +390,61 @@ static void decode_isolation_osc(struct cxl_port *hb, u32 iso_cap) hb->isolation_caps |= CXL_ISOLATION_INTERRUPTS; } +static void isolation_notify_handler(acpi_handle handle, u32 event, void *data) +{ + struct cxl_port *hb = data; + struct cxl_dport *dport; + unsigned long index; + + guard(device)(&hb->dev); + xa_for_each(&hb->dports, index, dport) { + if (dport->regs.isolation) + cxl_isolation_interrupt_handler(dport); + } +} + +static void cxl_remove_iso_handler(void *data) +{ + acpi_handle handle = data; + + acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, + isolation_notify_handler); +} + +static int cxl_register_iso_handler(struct cxl_port *hb) +{ + struct acpi_device *adev = ACPI_COMPANION(hb->uport_dev); + acpi_handle handle; + int rc; + + if (!adev) + return -EINVAL; + + handle = acpi_device_handle(adev); + + guard(device)(&hb->dev); + if (devm_is_action_added(&hb->dev, cxl_remove_iso_handler, handle)) + return 0; + + rc = acpi_install_notify_handler(handle, ACPI_DEVICE_NOTIFY, + isolation_notify_handler, hb); + if (rc != AE_OK) + return -ENXIO; + + rc = devm_add_action_or_reset(&hb->dev, cxl_remove_iso_handler, + handle); + if (rc) + return rc; + + dev_dbg(&hb->dev, "Installed CXL isolation notify handler\n"); + return 0; +} + static const struct cxl_root_ops acpi_root_ops = { .qos_class = cxl_acpi_qos_class, .setup_hostbridge_uport = cxl_acpi_setup_hostbridge_uport, .get_isolation_caps = decode_isolation_osc, + .register_hb_isolation_handler = cxl_register_iso_handler, }; static void del_cxl_resource(struct resource *res) diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c index e9eb7a8a5f72..ece667f3aaf5 100644 --- a/drivers/cxl/core/port.c +++ b/drivers/cxl/core/port.c @@ -1207,11 +1207,9 @@ struct isolation_intr_data { struct cxl_port *port; }; -static irqreturn_t cxl_isolation_thread(int irq, void *_data) +int cxl_isolation_interrupt_handler(struct cxl_dport *dport) { - struct isolation_intr_data *data = _data; - struct cxl_dport *dport = data->dport; - struct cxl_port *port = data->port; + struct cxl_port *port = dport->port; enum cxl_err_results res, acc; struct cxl_dev_state *cxlds; struct cxl_memdev *cxlmd; @@ -1221,10 +1219,6 @@ static irqreturn_t cxl_isolation_thread(int irq, void *_data) bool lnk_down; u32 status; - if (!dport || !port) - return IRQ_NONE; - - guard(device)(&port->dev); if (!dport->regs.isolation) goto panic; @@ -1255,8 +1249,9 @@ static irqreturn_t cxl_isolation_thread(int irq, void *_data) panic: panic("%s: downstream devices could not recover from CXL.mem link down\n", dev_name(dport->dport_dev)); - return IRQ_NONE; + return -ENXIO; } +EXPORT_SYMBOL_NS_GPL(cxl_isolation_interrupt_handler, "CXL"); static void cxl_dport_free_interrupts(void *data) { @@ -1274,6 +1269,24 @@ static void cxl_dport_free_interrupts(void *data) devm_free_irq(info->dev, info->irq, dport); } +static irqreturn_t cxl_isolation_thread(int irq, void *_data) +{ + struct isolation_intr_data *data = _data; + struct cxl_dport *dport = data->dport; + struct cxl_port *port = data->port; + int rc; + + if (!dport || !port) + return IRQ_NONE; + + guard(device)(&port->dev); + rc = cxl_isolation_interrupt_handler(dport); + if (rc) + return IRQ_NONE; + + return IRQ_HANDLED; +} + static int cxl_dport_setup_interrupts(struct device *host, struct cxl_dport *dport) { @@ -1282,6 +1295,16 @@ static int cxl_dport_setup_interrupts(struct device *host, u32 cap; int rc; + if (!(dport->port->isolation_caps & CXL_ISOLATION_INTERRUPTS)) { + struct cxl_root *root __free(put_cxl_root) = + find_cxl_root(dport->port); + if (!root || !root->ops || + !root->ops->register_hb_isolation_handler) + return -ENXIO; + + return root->ops->register_hb_isolation_handler(dport->port); + } + cap = readl(dport->regs.isolation + CXL_ISOLATION_CAPABILITY_OFFSET); if (!(cap & CXL_ISOLATION_CAP_INTR_SUPP)) return -ENXIO; diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h index aa36eba79181..68074c0d78d1 100644 --- a/drivers/cxl/cxl.h +++ b/drivers/cxl/cxl.h @@ -684,8 +684,11 @@ struct cxl_root_ops { struct device *bridge_dev); void (*get_isolation_caps)(struct cxl_port *hb, u32 iso_cap); + int (*register_hb_isolation_handler)(struct cxl_port *hb); }; +int cxl_isolation_interrupt_handler(struct cxl_dport *dport); + static inline struct cxl_dport * cxl_find_dport_by_dev(struct cxl_port *port, const struct device *dport_dev) { -- 2.34.1