xref: /linux/drivers/acpi/apei/einj-cxl.c (revision 6e7fd890f1d6ac83805409e9c346240de2705584)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * CXL Error INJection support. Used by CXL core to inject
4  * protocol errors into CXL ports.
5  *
6  * Copyright (C) 2023 Advanced Micro Devices, Inc.
7  *
8  * Author: Ben Cheatham <benjamin.cheatham@amd.com>
9  */
10 #include <linux/einj-cxl.h>
11 #include <linux/seq_file.h>
12 #include <linux/pci.h>
13 
14 #include "apei-internal.h"
15 
16 /* Defined in einj-core.c */
17 extern bool einj_initialized;
18 
19 static struct { u32 mask; const char *str; } const einj_cxl_error_type_string[] = {
20 	{ ACPI_EINJ_CXL_CACHE_CORRECTABLE, "CXL.cache Protocol Correctable" },
21 	{ ACPI_EINJ_CXL_CACHE_UNCORRECTABLE, "CXL.cache Protocol Uncorrectable non-fatal" },
22 	{ ACPI_EINJ_CXL_CACHE_FATAL, "CXL.cache Protocol Uncorrectable fatal" },
23 	{ ACPI_EINJ_CXL_MEM_CORRECTABLE, "CXL.mem Protocol Correctable" },
24 	{ ACPI_EINJ_CXL_MEM_UNCORRECTABLE, "CXL.mem Protocol Uncorrectable non-fatal" },
25 	{ ACPI_EINJ_CXL_MEM_FATAL, "CXL.mem Protocol Uncorrectable fatal" },
26 };
27 
28 int einj_cxl_available_error_type_show(struct seq_file *m, void *v)
29 {
30 	int cxl_err, rc;
31 	u32 available_error_type = 0;
32 
33 	rc = einj_get_available_error_type(&available_error_type);
34 	if (rc)
35 		return rc;
36 
37 	for (int pos = 0; pos < ARRAY_SIZE(einj_cxl_error_type_string); pos++) {
38 		cxl_err = ACPI_EINJ_CXL_CACHE_CORRECTABLE << pos;
39 
40 		if (available_error_type & cxl_err)
41 			seq_printf(m, "0x%08x\t%s\n",
42 				   einj_cxl_error_type_string[pos].mask,
43 				   einj_cxl_error_type_string[pos].str);
44 	}
45 
46 	return 0;
47 }
48 EXPORT_SYMBOL_NS_GPL(einj_cxl_available_error_type_show, CXL);
49 
50 static int cxl_dport_get_sbdf(struct pci_dev *dport_dev, u64 *sbdf)
51 {
52 	struct pci_bus *pbus;
53 	struct pci_host_bridge *bridge;
54 	u64 seg = 0, bus;
55 
56 	pbus = dport_dev->bus;
57 	bridge = pci_find_host_bridge(pbus);
58 
59 	if (!bridge)
60 		return -ENODEV;
61 
62 	if (bridge->domain_nr != PCI_DOMAIN_NR_NOT_SET)
63 		seg = bridge->domain_nr;
64 
65 	bus = pbus->number;
66 	*sbdf = (seg << 24) | (bus << 16) | dport_dev->devfn;
67 
68 	return 0;
69 }
70 
71 int einj_cxl_inject_rch_error(u64 rcrb, u64 type)
72 {
73 	int rc;
74 
75 	/* Only CXL error types can be specified */
76 	if (!einj_is_cxl_error_type(type))
77 		return -EINVAL;
78 
79 	rc = einj_validate_error_type(type);
80 	if (rc)
81 		return rc;
82 
83 	return einj_cxl_rch_error_inject(type, 0x2, rcrb, GENMASK_ULL(63, 0),
84 					 0, 0);
85 }
86 EXPORT_SYMBOL_NS_GPL(einj_cxl_inject_rch_error, CXL);
87 
88 int einj_cxl_inject_error(struct pci_dev *dport, u64 type)
89 {
90 	u64 param4 = 0;
91 	int rc;
92 
93 	/* Only CXL error types can be specified */
94 	if (!einj_is_cxl_error_type(type))
95 		return -EINVAL;
96 
97 	rc = einj_validate_error_type(type);
98 	if (rc)
99 		return rc;
100 
101 	rc = cxl_dport_get_sbdf(dport, &param4);
102 	if (rc)
103 		return rc;
104 
105 	return einj_error_inject(type, 0x4, 0, 0, 0, param4);
106 }
107 EXPORT_SYMBOL_NS_GPL(einj_cxl_inject_error, CXL);
108 
109 bool einj_cxl_is_initialized(void)
110 {
111 	return einj_initialized;
112 }
113 EXPORT_SYMBOL_NS_GPL(einj_cxl_is_initialized, CXL);
114