Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

cxl/acpi: Extract component registers of restricted hosts from RCRB

A downstream port must be connected to a component register block.
For restricted hosts the base address is determined from the RCRB. The
RCRB is provided by the host's CEDT CHBS entry. Rework CEDT parser to
get the RCRB and add code to extract the component register block from
it.

RCRB's BAR[0..1] point to the component block containing CXL subsystem
component registers. MEMBAR extraction follows the PCI base spec here,
esp. 64 bit extraction and memory range alignment (6.0, 7.5.1.2.1). The
RCRB base address is cached in the cxl_dport per-host bridge so that the
upstream port component registers can be retrieved later by an RCD
(RCIEP) associated with the host bridge.

Note: Right now the component register block is used for HDM decoder
capability only which is optional for RCDs. If unsupported by the RCD,
the HDM init will fail. It is future work to bypass it in this case.

Co-developed-by: Terry Bowman <terry.bowman@amd.com>
Signed-off-by: Terry Bowman <terry.bowman@amd.com>
Signed-off-by: Robert Richter <rrichter@amd.com>
Link: https://lore.kernel.org/r/Y4dsGZ24aJlxSfI1@rric.localdomain
[djbw: introduce devm_cxl_add_rch_dport()]
Link: https://lore.kernel.org/r/166993044524.1882361.2539922887413208807.stgit@dwillia2-xfh.jf.intel.com
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>

authored by

Robert Richter and committed by
Dan Williams
d5b1a271 1dedb6f3

+207 -11
+46 -5
drivers/cxl/acpi.c
··· 9 9 #include "cxlpci.h" 10 10 #include "cxl.h" 11 11 12 + #define CXL_RCRB_SIZE SZ_8K 13 + 12 14 static unsigned long cfmws_to_decoder_flags(int restrictions) 13 15 { 14 16 unsigned long flags = CXL_DECODER_F_ENABLE; ··· 217 215 return 0; 218 216 } 219 217 218 + if (dport->rch) { 219 + dev_info(bridge, "host supports CXL (restricted)\n"); 220 + return 0; 221 + } 222 + 220 223 rc = devm_cxl_register_pci_bus(host, bridge, pci_root->bus); 221 224 if (rc) 222 225 return rc; ··· 239 232 struct cxl_chbs_context { 240 233 struct device *dev; 241 234 unsigned long long uid; 235 + resource_size_t rcrb; 242 236 resource_size_t chbcr; 237 + u32 cxl_version; 243 238 }; 244 239 245 240 static int cxl_get_chbcr(union acpi_subtable_headers *header, void *arg, ··· 257 248 258 249 if (ctx->uid != chbs->uid) 259 250 return 0; 260 - ctx->chbcr = chbs->base; 251 + 252 + ctx->cxl_version = chbs->cxl_version; 253 + ctx->rcrb = CXL_RESOURCE_NONE; 254 + ctx->chbcr = CXL_RESOURCE_NONE; 255 + 256 + if (!chbs->base) 257 + return 0; 258 + 259 + if (chbs->cxl_version != ACPI_CEDT_CHBS_VERSION_CXL11) { 260 + ctx->chbcr = chbs->base; 261 + return 0; 262 + } 263 + 264 + if (chbs->length != CXL_RCRB_SIZE) 265 + return 0; 266 + 267 + ctx->rcrb = chbs->base; 268 + ctx->chbcr = cxl_rcrb_to_component(ctx->dev, chbs->base, 269 + CXL_RCRB_DOWNSTREAM); 261 270 262 271 return 0; 263 272 } ··· 304 277 dev_dbg(match, "UID found: %lld\n", uid); 305 278 306 279 ctx = (struct cxl_chbs_context) { 307 - .dev = host, 280 + .dev = match, 308 281 .uid = uid, 309 282 }; 310 283 acpi_table_parse_cedt(ACPI_CEDT_TYPE_CHBS, cxl_get_chbcr, &ctx); 311 284 312 - if (ctx.chbcr == 0) { 285 + if (!ctx.chbcr) { 286 + dev_warn(match, "No CHBS found for Host Bridge (UID %lld)\n", 287 + uid); 288 + return 0; 289 + } 290 + 291 + if (ctx.rcrb != CXL_RESOURCE_NONE) 292 + dev_dbg(match, "RCRB found for UID %lld: %pa\n", uid, &ctx.rcrb); 293 + 294 + if (ctx.chbcr == CXL_RESOURCE_NONE) { 313 295 dev_warn(match, "No CHBS found for Host Bridge (UID %lld)\n", uid); 314 296 return 0; 315 297 } 316 298 317 - dev_dbg(match, "CHBCR found: 0x%08llx\n", (u64)ctx.chbcr); 299 + dev_dbg(match, "CHBCR found: %pa\n", &ctx.chbcr); 318 300 319 301 pci_root = acpi_pci_find_root(hb->handle); 320 302 bridge = pci_root->bus->bridge; 321 - dport = devm_cxl_add_dport(root_port, bridge, uid, ctx.chbcr); 303 + if (ctx.cxl_version == ACPI_CEDT_CHBS_VERSION_CXL11) 304 + dport = devm_cxl_add_rch_dport(root_port, bridge, uid, 305 + ctx.chbcr, ctx.rcrb); 306 + else 307 + dport = devm_cxl_add_dport(root_port, bridge, uid, 308 + ctx.chbcr); 322 309 if (IS_ERR(dport)) 323 310 return PTR_ERR(dport); 324 311
+47 -6
drivers/cxl/core/port.c
··· 628 628 iter = to_cxl_port(iter->dev.parent); 629 629 if (iter->host_bridge) 630 630 port->host_bridge = iter->host_bridge; 631 + else if (parent_dport->rch) 632 + port->host_bridge = parent_dport->dport; 631 633 else 632 634 port->host_bridge = iter->uport; 633 635 dev_dbg(uport, "host-bridge: %s\n", dev_name(port->host_bridge)); ··· 901 899 sysfs_remove_link(&port->dev.kobj, link_name); 902 900 } 903 901 904 - static struct cxl_dport *__devm_cxl_add_dport(struct cxl_port *port, 905 - struct device *dport_dev, 906 - int port_id, 907 - resource_size_t component_reg_phys) 902 + static struct cxl_dport * 903 + __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev, 904 + int port_id, resource_size_t component_reg_phys, 905 + resource_size_t rcrb) 908 906 { 909 907 char link_name[CXL_TARGET_STRLEN]; 910 908 struct cxl_dport *dport; ··· 934 932 dport->port_id = port_id; 935 933 dport->component_reg_phys = component_reg_phys; 936 934 dport->port = port; 935 + if (rcrb != CXL_RESOURCE_NONE) 936 + dport->rch = true; 937 + dport->rcrb = rcrb; 937 938 938 939 cond_cxl_root_lock(port); 939 940 rc = add_dport(port, dport); ··· 961 956 } 962 957 963 958 /** 964 - * devm_cxl_add_dport - append downstream port data to a cxl_port 959 + * devm_cxl_add_dport - append VH downstream port data to a cxl_port 965 960 * @port: the cxl_port that references this dport 966 961 * @dport_dev: firmware or PCI device representing the dport 967 962 * @port_id: identifier for this dport in a decoder's target list ··· 978 973 struct cxl_dport *dport; 979 974 980 975 dport = __devm_cxl_add_dport(port, dport_dev, port_id, 981 - component_reg_phys); 976 + component_reg_phys, CXL_RESOURCE_NONE); 982 977 if (IS_ERR(dport)) { 983 978 dev_dbg(dport_dev, "failed to add dport to %s: %ld\n", 984 979 dev_name(&port->dev), PTR_ERR(dport)); ··· 990 985 return dport; 991 986 } 992 987 EXPORT_SYMBOL_NS_GPL(devm_cxl_add_dport, CXL); 988 + 989 + /** 990 + * devm_cxl_add_rch_dport - append RCH downstream port data to a cxl_port 991 + * @port: the cxl_port that references this dport 992 + * @dport_dev: firmware or PCI device representing the dport 993 + * @port_id: identifier for this dport in a decoder's target list 994 + * @component_reg_phys: optional location of CXL component registers 995 + * @rcrb: mandatory location of a Root Complex Register Block 996 + * 997 + * See CXL 3.0 9.11.8 CXL Devices Attached to an RCH 998 + */ 999 + struct cxl_dport *devm_cxl_add_rch_dport(struct cxl_port *port, 1000 + struct device *dport_dev, int port_id, 1001 + resource_size_t component_reg_phys, 1002 + resource_size_t rcrb) 1003 + { 1004 + struct cxl_dport *dport; 1005 + 1006 + if (rcrb == CXL_RESOURCE_NONE) { 1007 + dev_dbg(&port->dev, "failed to add RCH dport, missing RCRB\n"); 1008 + return ERR_PTR(-EINVAL); 1009 + } 1010 + 1011 + dport = __devm_cxl_add_dport(port, dport_dev, port_id, 1012 + component_reg_phys, rcrb); 1013 + if (IS_ERR(dport)) { 1014 + dev_dbg(dport_dev, "failed to add RCH dport to %s: %ld\n", 1015 + dev_name(&port->dev), PTR_ERR(dport)); 1016 + } else { 1017 + dev_dbg(dport_dev, "RCH dport added to %s\n", 1018 + dev_name(&port->dev)); 1019 + } 1020 + 1021 + return dport; 1022 + } 1023 + EXPORT_SYMBOL_NS_GPL(devm_cxl_add_rch_dport, CXL); 993 1024 994 1025 static int add_ep(struct cxl_ep *new) 995 1026 {
+65
drivers/cxl/core/regs.c
··· 307 307 return -ENODEV; 308 308 } 309 309 EXPORT_SYMBOL_NS_GPL(cxl_find_regblock, CXL); 310 + 311 + resource_size_t cxl_rcrb_to_component(struct device *dev, 312 + resource_size_t rcrb, 313 + enum cxl_rcrb which) 314 + { 315 + resource_size_t component_reg_phys; 316 + u32 bar0, bar1; 317 + void *addr; 318 + u16 cmd; 319 + u32 id; 320 + 321 + if (which == CXL_RCRB_UPSTREAM) 322 + rcrb += SZ_4K; 323 + 324 + /* 325 + * RCRB's BAR[0..1] point to component block containing CXL 326 + * subsystem component registers. MEMBAR extraction follows 327 + * the PCI Base spec here, esp. 64 bit extraction and memory 328 + * ranges alignment (6.0, 7.5.1.2.1). 329 + */ 330 + if (!request_mem_region(rcrb, SZ_4K, "CXL RCRB")) 331 + return CXL_RESOURCE_NONE; 332 + addr = ioremap(rcrb, SZ_4K); 333 + if (!addr) { 334 + dev_err(dev, "Failed to map region %pr\n", addr); 335 + release_mem_region(rcrb, SZ_4K); 336 + return CXL_RESOURCE_NONE; 337 + } 338 + 339 + id = readl(addr + PCI_VENDOR_ID); 340 + cmd = readw(addr + PCI_COMMAND); 341 + bar0 = readl(addr + PCI_BASE_ADDRESS_0); 342 + bar1 = readl(addr + PCI_BASE_ADDRESS_1); 343 + iounmap(addr); 344 + release_mem_region(rcrb, SZ_4K); 345 + 346 + /* 347 + * Sanity check, see CXL 3.0 Figure 9-8 CXL Device that Does Not 348 + * Remap Upstream Port and Component Registers 349 + */ 350 + if (id == U32_MAX) { 351 + if (which == CXL_RCRB_DOWNSTREAM) 352 + dev_err(dev, "Failed to access Downstream Port RCRB\n"); 353 + return CXL_RESOURCE_NONE; 354 + } 355 + if (!(cmd & PCI_COMMAND_MEMORY)) 356 + return CXL_RESOURCE_NONE; 357 + /* The RCRB is a Memory Window, and the MEM_TYPE_1M bit is obsolete */ 358 + if (bar0 & (PCI_BASE_ADDRESS_MEM_TYPE_1M | PCI_BASE_ADDRESS_SPACE_IO)) 359 + return CXL_RESOURCE_NONE; 360 + 361 + component_reg_phys = bar0 & PCI_BASE_ADDRESS_MEM_MASK; 362 + if (bar0 & PCI_BASE_ADDRESS_MEM_TYPE_64) 363 + component_reg_phys |= ((u64)bar1) << 32; 364 + 365 + if (!component_reg_phys) 366 + return CXL_RESOURCE_NONE; 367 + 368 + /* MEMBAR is block size (64k) aligned. */ 369 + if (!IS_ALIGNED(component_reg_phys, CXL_COMPONENT_REG_BLOCK_SIZE)) 370 + return CXL_RESOURCE_NONE; 371 + 372 + return component_reg_phys; 373 + } 374 + EXPORT_SYMBOL_NS_GPL(cxl_rcrb_to_component, CXL);
+16
drivers/cxl/cxl.h
··· 223 223 int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type, 224 224 struct cxl_register_map *map); 225 225 226 + enum cxl_rcrb { 227 + CXL_RCRB_DOWNSTREAM, 228 + CXL_RCRB_UPSTREAM, 229 + }; 230 + resource_size_t cxl_rcrb_to_component(struct device *dev, 231 + resource_size_t rcrb, 232 + enum cxl_rcrb which); 233 + 226 234 #define CXL_RESOURCE_NONE ((resource_size_t) -1) 227 235 #define CXL_TARGET_STRLEN 20 228 236 ··· 494 486 * @dport: PCI bridge or firmware device representing the downstream link 495 487 * @port_id: unique hardware identifier for dport in decoder target list 496 488 * @component_reg_phys: downstream port component registers 489 + * @rcrb: base address for the Root Complex Register Block 490 + * @rch: Indicate whether this dport was enumerated in RCH or VH mode 497 491 * @port: reference to cxl_port that contains this downstream port 498 492 */ 499 493 struct cxl_dport { 500 494 struct device *dport; 501 495 int port_id; 502 496 resource_size_t component_reg_phys; 497 + resource_size_t rcrb; 498 + bool rch; 503 499 struct cxl_port *port; 504 500 }; 505 501 ··· 573 561 struct cxl_dport *devm_cxl_add_dport(struct cxl_port *port, 574 562 struct device *dport, int port_id, 575 563 resource_size_t component_reg_phys); 564 + struct cxl_dport *devm_cxl_add_rch_dport(struct cxl_port *port, 565 + struct device *dport_dev, int port_id, 566 + resource_size_t component_reg_phys, 567 + resource_size_t rcrb); 576 568 577 569 struct cxl_decoder *to_cxl_decoder(struct device *dev); 578 570 struct cxl_root_decoder *to_cxl_root_decoder(struct device *dev);
+1
tools/testing/cxl/Kbuild
··· 10 10 ldflags-y += --wrap=devm_cxl_enumerate_decoders 11 11 ldflags-y += --wrap=cxl_await_media_ready 12 12 ldflags-y += --wrap=cxl_hdm_decode_init 13 + ldflags-y += --wrap=cxl_rcrb_to_component 13 14 14 15 DRIVERS := ../../../drivers 15 16 CXL_SRC := $(DRIVERS)/cxl
+10
tools/testing/cxl/test/cxl.c
··· 696 696 return 0; 697 697 } 698 698 699 + resource_size_t mock_cxl_rcrb_to_component(struct device *dev, 700 + resource_size_t rcrb, 701 + enum cxl_rcrb which) 702 + { 703 + dev_dbg(dev, "rcrb: %pa which: %d\n", &rcrb, which); 704 + 705 + return (resource_size_t) which + 1; 706 + } 707 + 699 708 static struct cxl_mock_ops cxl_mock_ops = { 700 709 .is_mock_adev = is_mock_adev, 701 710 .is_mock_bridge = is_mock_bridge, ··· 713 704 .is_mock_dev = is_mock_dev, 714 705 .acpi_table_parse_cedt = mock_acpi_table_parse_cedt, 715 706 .acpi_evaluate_integer = mock_acpi_evaluate_integer, 707 + .cxl_rcrb_to_component = mock_cxl_rcrb_to_component, 716 708 .acpi_pci_find_root = mock_acpi_pci_find_root, 717 709 .devm_cxl_port_enumerate_dports = mock_cxl_port_enumerate_dports, 718 710 .devm_cxl_setup_hdm = mock_cxl_setup_hdm,
+19
tools/testing/cxl/test/mock.c
··· 224 224 } 225 225 EXPORT_SYMBOL_NS_GPL(__wrap_cxl_hdm_decode_init, CXL); 226 226 227 + resource_size_t __wrap_cxl_rcrb_to_component(struct device *dev, 228 + resource_size_t rcrb, 229 + enum cxl_rcrb which) 230 + { 231 + int index; 232 + resource_size_t component_reg_phys; 233 + struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); 234 + 235 + if (ops && ops->is_mock_port(dev)) 236 + component_reg_phys = 237 + ops->cxl_rcrb_to_component(dev, rcrb, which); 238 + else 239 + component_reg_phys = cxl_rcrb_to_component(dev, rcrb, which); 240 + put_cxl_mock_ops(index); 241 + 242 + return component_reg_phys; 243 + } 244 + EXPORT_SYMBOL_NS_GPL(__wrap_cxl_rcrb_to_component, CXL); 245 + 227 246 MODULE_LICENSE("GPL v2"); 228 247 MODULE_IMPORT_NS(ACPI); 229 248 MODULE_IMPORT_NS(CXL);
+3
tools/testing/cxl/test/mock.h
··· 15 15 acpi_string pathname, 16 16 struct acpi_object_list *arguments, 17 17 unsigned long long *data); 18 + resource_size_t (*cxl_rcrb_to_component)(struct device *dev, 19 + resource_size_t rcrb, 20 + enum cxl_rcrb which); 18 21 struct acpi_pci_root *(*acpi_pci_find_root)(acpi_handle handle); 19 22 bool (*is_mock_bus)(struct pci_bus *bus); 20 23 bool (*is_mock_port)(struct device *dev);