1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright(c) 2021 Intel Corporation. All rights reserved. */
3 #include <linux/platform_device.h>
4 #include <linux/module.h>
5 #include <linux/device.h>
6 #include <linux/kernel.h>
7 #include <linux/acpi.h>
11 static struct acpi_table_header
*acpi_cedt
;
13 /* Encode defined in CXL 2.0 8.2.5.12.7 HDM Decoder Control Register */
14 #define CFMWS_INTERLEAVE_WAYS(x) (1 << (x)->interleave_ways)
15 #define CFMWS_INTERLEAVE_GRANULARITY(x) ((x)->granularity + 8)
17 static unsigned long cfmws_to_decoder_flags(int restrictions
)
19 unsigned long flags
= 0;
21 if (restrictions
& ACPI_CEDT_CFMWS_RESTRICT_TYPE2
)
22 flags
|= CXL_DECODER_F_TYPE2
;
23 if (restrictions
& ACPI_CEDT_CFMWS_RESTRICT_TYPE3
)
24 flags
|= CXL_DECODER_F_TYPE3
;
25 if (restrictions
& ACPI_CEDT_CFMWS_RESTRICT_VOLATILE
)
26 flags
|= CXL_DECODER_F_RAM
;
27 if (restrictions
& ACPI_CEDT_CFMWS_RESTRICT_PMEM
)
28 flags
|= CXL_DECODER_F_PMEM
;
29 if (restrictions
& ACPI_CEDT_CFMWS_RESTRICT_FIXED
)
30 flags
|= CXL_DECODER_F_LOCK
;
35 static int cxl_acpi_cfmws_verify(struct device
*dev
,
36 struct acpi_cedt_cfmws
*cfmws
)
40 if (cfmws
->interleave_arithmetic
!= ACPI_CEDT_CFMWS_ARITHMETIC_MODULO
) {
41 dev_err(dev
, "CFMWS Unsupported Interleave Arithmetic\n");
45 if (!IS_ALIGNED(cfmws
->base_hpa
, SZ_256M
)) {
46 dev_err(dev
, "CFMWS Base HPA not 256MB aligned\n");
50 if (!IS_ALIGNED(cfmws
->window_size
, SZ_256M
)) {
51 dev_err(dev
, "CFMWS Window Size not 256MB aligned\n");
55 expected_len
= struct_size((cfmws
), interleave_targets
,
56 CFMWS_INTERLEAVE_WAYS(cfmws
));
58 if (cfmws
->header
.length
< expected_len
) {
59 dev_err(dev
, "CFMWS length %d less than expected %d\n",
60 cfmws
->header
.length
, expected_len
);
64 if (cfmws
->header
.length
> expected_len
)
65 dev_dbg(dev
, "CFMWS length %d greater than expected %d\n",
66 cfmws
->header
.length
, expected_len
);
71 static void cxl_add_cfmws_decoders(struct device
*dev
,
72 struct cxl_port
*root_port
)
74 struct acpi_cedt_cfmws
*cfmws
;
75 struct cxl_decoder
*cxld
;
76 acpi_size len
, cur
= 0;
81 len
= acpi_cedt
->length
- sizeof(*acpi_cedt
);
82 cedt_subtable
= acpi_cedt
+ 1;
85 struct acpi_cedt_header
*c
= cedt_subtable
+ cur
;
87 if (c
->type
!= ACPI_CEDT_TYPE_CFMWS
) {
92 cfmws
= cedt_subtable
+ cur
;
94 if (cfmws
->header
.length
< sizeof(*cfmws
)) {
96 "CFMWS entry skipped:invalid length:%u\n",
97 cfmws
->header
.length
);
102 rc
= cxl_acpi_cfmws_verify(dev
, cfmws
);
104 dev_err(dev
, "CFMWS range %#llx-%#llx not registered\n",
105 cfmws
->base_hpa
, cfmws
->base_hpa
+
106 cfmws
->window_size
- 1);
111 flags
= cfmws_to_decoder_flags(cfmws
->restrictions
);
112 cxld
= devm_cxl_add_decoder(dev
, root_port
,
113 CFMWS_INTERLEAVE_WAYS(cfmws
),
114 cfmws
->base_hpa
, cfmws
->window_size
,
115 CFMWS_INTERLEAVE_WAYS(cfmws
),
116 CFMWS_INTERLEAVE_GRANULARITY(cfmws
),
117 CXL_DECODER_EXPANDER
,
121 dev_err(dev
, "Failed to add decoder for %#llx-%#llx\n",
122 cfmws
->base_hpa
, cfmws
->base_hpa
+
123 cfmws
->window_size
- 1);
125 dev_dbg(dev
, "add: %s range %#llx-%#llx\n",
126 dev_name(&cxld
->dev
), cfmws
->base_hpa
,
127 cfmws
->base_hpa
+ cfmws
->window_size
- 1);
133 static struct acpi_cedt_chbs
*cxl_acpi_match_chbs(struct device
*dev
, u32 uid
)
135 struct acpi_cedt_chbs
*chbs
, *chbs_match
= NULL
;
136 acpi_size len
, cur
= 0;
139 len
= acpi_cedt
->length
- sizeof(*acpi_cedt
);
140 cedt_subtable
= acpi_cedt
+ 1;
143 struct acpi_cedt_header
*c
= cedt_subtable
+ cur
;
145 if (c
->type
!= ACPI_CEDT_TYPE_CHBS
) {
150 chbs
= cedt_subtable
+ cur
;
152 if (chbs
->header
.length
< sizeof(*chbs
)) {
154 "CHBS entry skipped: invalid length:%u\n",
155 chbs
->header
.length
);
160 if (chbs
->uid
!= uid
) {
167 "CHBS entry skipped: duplicate UID:%u\n",
177 return chbs_match
? chbs_match
: ERR_PTR(-ENODEV
);
180 static resource_size_t
get_chbcr(struct acpi_cedt_chbs
*chbs
)
182 return IS_ERR(chbs
) ? CXL_RESOURCE_NONE
: chbs
->base
;
185 struct cxl_walk_context
{
187 struct pci_bus
*root
;
188 struct cxl_port
*port
;
193 static int match_add_root_ports(struct pci_dev
*pdev
, void *data
)
195 struct cxl_walk_context
*ctx
= data
;
196 struct pci_bus
*root_bus
= ctx
->root
;
197 struct cxl_port
*port
= ctx
->port
;
198 int type
= pci_pcie_type(pdev
);
199 struct device
*dev
= ctx
->dev
;
200 u32 lnkcap
, port_num
;
203 if (pdev
->bus
!= root_bus
)
205 if (!pci_is_pcie(pdev
))
207 if (type
!= PCI_EXP_TYPE_ROOT_PORT
)
209 if (pci_read_config_dword(pdev
, pci_pcie_cap(pdev
) + PCI_EXP_LNKCAP
,
210 &lnkcap
) != PCIBIOS_SUCCESSFUL
)
213 /* TODO walk DVSEC to find component register base */
214 port_num
= FIELD_GET(PCI_EXP_LNKCAP_PN
, lnkcap
);
215 rc
= cxl_add_dport(port
, &pdev
->dev
, port_num
, CXL_RESOURCE_NONE
);
222 dev_dbg(dev
, "add dport%d: %s\n", port_num
, dev_name(&pdev
->dev
));
227 static struct cxl_dport
*find_dport_by_dev(struct cxl_port
*port
, struct device
*dev
)
229 struct cxl_dport
*dport
;
231 device_lock(&port
->dev
);
232 list_for_each_entry(dport
, &port
->dports
, list
)
233 if (dport
->dport
== dev
) {
234 device_unlock(&port
->dev
);
238 device_unlock(&port
->dev
);
242 static struct acpi_device
*to_cxl_host_bridge(struct device
*dev
)
244 struct acpi_device
*adev
= to_acpi_device(dev
);
246 if (!acpi_pci_find_root(adev
->handle
))
249 if (strcmp(acpi_device_hid(adev
), "ACPI0016") == 0)
255 * A host bridge is a dport to a CFMWS decode and it is a uport to the
256 * dport (PCIe Root Ports) in the host bridge.
258 static int add_host_bridge_uport(struct device
*match
, void *arg
)
260 struct acpi_device
*bridge
= to_cxl_host_bridge(match
);
261 struct cxl_port
*root_port
= arg
;
262 struct device
*host
= root_port
->dev
.parent
;
263 struct acpi_pci_root
*pci_root
;
264 struct cxl_walk_context ctx
;
265 struct cxl_decoder
*cxld
;
266 struct cxl_dport
*dport
;
267 struct cxl_port
*port
;
272 dport
= find_dport_by_dev(root_port
, match
);
274 dev_dbg(host
, "host bridge expected and not found\n");
278 port
= devm_cxl_add_port(host
, match
, dport
->component_reg_phys
,
281 return PTR_ERR(port
);
282 dev_dbg(host
, "%s: add: %s\n", dev_name(match
), dev_name(&port
->dev
));
285 * Note that this lookup already succeeded in
286 * to_cxl_host_bridge(), so no need to check for failure here
288 pci_root
= acpi_pci_find_root(bridge
->handle
);
289 ctx
= (struct cxl_walk_context
){
291 .root
= pci_root
->bus
,
294 pci_walk_bus(pci_root
->bus
, match_add_root_ports
, &ctx
);
301 /* TODO: Scan CHBCR for HDM Decoder resources */
304 * In the single-port host-bridge case there are no HDM decoders
305 * in the CHBCR and a 1:1 passthrough decode is implied.
307 if (ctx
.count
== 1) {
308 cxld
= devm_cxl_add_passthrough_decoder(host
, port
);
310 return PTR_ERR(cxld
);
312 dev_dbg(host
, "add: %s\n", dev_name(&cxld
->dev
));
318 static int add_host_bridge_dport(struct device
*match
, void *arg
)
322 unsigned long long uid
;
323 struct acpi_cedt_chbs
*chbs
;
324 struct cxl_port
*root_port
= arg
;
325 struct device
*host
= root_port
->dev
.parent
;
326 struct acpi_device
*bridge
= to_cxl_host_bridge(match
);
331 status
= acpi_evaluate_integer(bridge
->handle
, METHOD_NAME__UID
, NULL
,
333 if (status
!= AE_OK
) {
334 dev_err(host
, "unable to retrieve _UID of %s\n",
339 chbs
= cxl_acpi_match_chbs(host
, uid
);
341 dev_dbg(host
, "No CHBS found for Host Bridge: %s\n",
344 rc
= cxl_add_dport(root_port
, match
, uid
, get_chbcr(chbs
));
346 dev_err(host
, "failed to add downstream port: %s\n",
350 dev_dbg(host
, "add dport%llu: %s\n", uid
, dev_name(match
));
354 static int add_root_nvdimm_bridge(struct device
*match
, void *data
)
356 struct cxl_decoder
*cxld
;
357 struct cxl_port
*root_port
= data
;
358 struct cxl_nvdimm_bridge
*cxl_nvb
;
359 struct device
*host
= root_port
->dev
.parent
;
361 if (!is_root_decoder(match
))
364 cxld
= to_cxl_decoder(match
);
365 if (!(cxld
->flags
& CXL_DECODER_F_PMEM
))
368 cxl_nvb
= devm_cxl_add_nvdimm_bridge(host
, root_port
);
369 if (IS_ERR(cxl_nvb
)) {
370 dev_dbg(host
, "failed to register pmem\n");
371 return PTR_ERR(cxl_nvb
);
373 dev_dbg(host
, "%s: add: %s\n", dev_name(&root_port
->dev
),
374 dev_name(&cxl_nvb
->dev
));
378 static int cxl_acpi_probe(struct platform_device
*pdev
)
382 struct cxl_port
*root_port
;
383 struct device
*host
= &pdev
->dev
;
384 struct acpi_device
*adev
= ACPI_COMPANION(host
);
386 root_port
= devm_cxl_add_port(host
, host
, CXL_RESOURCE_NONE
, NULL
);
387 if (IS_ERR(root_port
))
388 return PTR_ERR(root_port
);
389 dev_dbg(host
, "add: %s\n", dev_name(&root_port
->dev
));
391 status
= acpi_get_table(ACPI_SIG_CEDT
, 0, &acpi_cedt
);
392 if (ACPI_FAILURE(status
))
395 rc
= bus_for_each_dev(adev
->dev
.bus
, NULL
, root_port
,
396 add_host_bridge_dport
);
400 cxl_add_cfmws_decoders(host
, root_port
);
403 * Root level scanned with host-bridge as dports, now scan host-bridges
404 * for their role as CXL uports to their CXL-capable PCIe Root Ports.
406 rc
= bus_for_each_dev(adev
->dev
.bus
, NULL
, root_port
,
407 add_host_bridge_uport
);
411 if (IS_ENABLED(CONFIG_CXL_PMEM
))
412 rc
= device_for_each_child(&root_port
->dev
, root_port
,
413 add_root_nvdimm_bridge
);
416 acpi_put_table(acpi_cedt
);
422 static const struct acpi_device_id cxl_acpi_ids
[] = {
426 MODULE_DEVICE_TABLE(acpi
, cxl_acpi_ids
);
428 static struct platform_driver cxl_acpi_driver
= {
429 .probe
= cxl_acpi_probe
,
431 .name
= KBUILD_MODNAME
,
432 .acpi_match_table
= cxl_acpi_ids
,
436 module_platform_driver(cxl_acpi_driver
);
437 MODULE_LICENSE("GPL v2");
438 MODULE_IMPORT_NS(CXL
);