| // SPDX-License-Identifier: GPL-2.0-only |
| /* Copyright(c) 2021 Intel Corporation. All rights reserved. */ |
| |
| #include <linux/platform_device.h> |
| #include <linux/device.h> |
| #include <linux/acpi.h> |
| #include <linux/pci.h> |
| #include <cxl.h> |
| #include "test/mock.h" |
| |
| struct acpi_device *to_cxl_host_bridge(struct device *host, struct device *dev) |
| { |
| int index; |
| struct acpi_device *adev, *found = NULL; |
| struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); |
| |
| if (ops && ops->is_mock_bridge(dev)) { |
| found = ACPI_COMPANION(dev); |
| goto out; |
| } |
| |
| if (dev->bus == &platform_bus_type) |
| goto out; |
| |
| adev = to_acpi_device(dev); |
| if (!acpi_pci_find_root(adev->handle)) |
| goto out; |
| |
| if (strcmp(acpi_device_hid(adev), "ACPI0016") == 0) { |
| found = adev; |
| dev_dbg(host, "found host bridge %s\n", dev_name(&adev->dev)); |
| } |
| out: |
| put_cxl_mock_ops(index); |
| return found; |
| } |
| |
| static int match_add_root_port(struct pci_dev *pdev, void *data) |
| { |
| struct cxl_walk_context *ctx = data; |
| struct pci_bus *root_bus = ctx->root; |
| struct cxl_port *port = ctx->port; |
| int type = pci_pcie_type(pdev); |
| struct device *dev = ctx->dev; |
| u32 lnkcap, port_num; |
| int rc; |
| |
| if (pdev->bus != root_bus) |
| return 0; |
| if (!pci_is_pcie(pdev)) |
| return 0; |
| if (type != PCI_EXP_TYPE_ROOT_PORT) |
| return 0; |
| if (pci_read_config_dword(pdev, pci_pcie_cap(pdev) + PCI_EXP_LNKCAP, |
| &lnkcap) != PCIBIOS_SUCCESSFUL) |
| return 0; |
| |
| /* TODO walk DVSEC to find component register base */ |
| port_num = FIELD_GET(PCI_EXP_LNKCAP_PN, lnkcap); |
| rc = cxl_add_dport(port, &pdev->dev, port_num, CXL_RESOURCE_NONE); |
| if (rc) { |
| dev_err(dev, "failed to add dport: %s (%d)\n", |
| dev_name(&pdev->dev), rc); |
| ctx->error = rc; |
| return rc; |
| } |
| ctx->count++; |
| |
| dev_dbg(dev, "add dport%d: %s\n", port_num, dev_name(&pdev->dev)); |
| |
| return 0; |
| } |
| |
| static int mock_add_root_port(struct platform_device *pdev, void *data) |
| { |
| struct cxl_walk_context *ctx = data; |
| struct cxl_port *port = ctx->port; |
| struct device *dev = ctx->dev; |
| int rc; |
| |
| rc = cxl_add_dport(port, &pdev->dev, pdev->id, CXL_RESOURCE_NONE); |
| if (rc) { |
| dev_err(dev, "failed to add dport: %s (%d)\n", |
| dev_name(&pdev->dev), rc); |
| ctx->error = rc; |
| return rc; |
| } |
| ctx->count++; |
| |
| dev_dbg(dev, "add dport%d: %s\n", pdev->id, dev_name(&pdev->dev)); |
| |
| return 0; |
| } |
| |
| int match_add_root_ports(struct pci_dev *dev, void *data) |
| { |
| int index, rc; |
| struct cxl_mock_ops *ops = get_cxl_mock_ops(&index); |
| struct platform_device *pdev = (struct platform_device *) dev; |
| |
| if (ops && ops->is_mock_port(pdev)) |
| rc = mock_add_root_port(pdev, data); |
| else |
| rc = match_add_root_port(dev, data); |
| |
| put_cxl_mock_ops(index); |
| |
| return rc; |
| } |