Commit b9132c32 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull CXL (Compute Express Link) updates from Dan Williams:
 "This development cycle extends the subsystem to discover CXL resources
  throughout a CXL/PCIe switch topology and respond to hot add/remove
  events anywhere in that topology.

  This is more foundational infrastructure in preparation for dynamic
  memory region provisioning support. Recall that CXL memory regions, as
  the new "Theory of Operation" section of
  Documentation/driver-api/cxl/memory-devices.rst describes, bring
  storage volume striping semantics to memory.

  The hot add/remove behavior is validated with extensions to the
  cxl_test unit test environment and this test in the cxl-cli test
  suite:

      https://github.com/pmem/ndctl/blob/djbw/for-74/cxl/test/cxl-topology.sh

  Summary:

   - Add a driver for 'struct cxl_memdev' objects responsible for
     CXL.mem operation as distinct from 'cxl_pci' mailbox operations.

     Its primary responsibility is enumerating an endpoint 'struct
     cxl_port' and all the 'struct cxl_port' instances between an
     endpoint and the CXL platform root.

   - Add a driver for 'struct cxl_port' objects responsible for
     enumerating and operating all Host-managed Device Memory (HDM)
     decoder resources between the platform-level CXL memory
     description, all intervening host bridges / switches, and the HDM
     resources in endpoints.

   - Update the cxl_pci driver to validate CXL.mem operation precursors
     to HDM decoder operation like ready-polling, and legacy CXL 1.1
     DVSEC based CXL.mem configuration.

   - Add basic lockdep coverage for usage of device_lock() on CXL
     subsystem objects similar to what exists for LIBNVDIMM. Include a
     compile-time switch for which subsystem to validate at run-time.

   - Update cxl_test to emulate a one level switch topology.

   - Document a "Theory of Operation" for the subsystem.

   - Add 'numa_node' and 'serial' attributes to cxl_memdev sysfs

   - Include miscellaneous fixes for spec / QEMU CXL emulation
     compatibility and static analysis reports"

* tag 'cxl-for-5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/cxl/cxl: (48 commits)
  cxl/core/port: Fix NULL but dereferenced coccicheck error
  cxl/port: Hold port reference until decoder release
  cxl/port: Fix endpoint refcount leak
  cxl/core: Fix cxl_device_lock() class detection
  cxl/core/port: Fix unregister_port() lock assertion
  cxl/regs: Fix size of CXL Capability Header Register
  cxl/core/port: Handle invalid decoders
  cxl/core/port: Fix / relax decoder target enumeration
  tools/testing/cxl: Add a physical_node link
  tools/testing/cxl: Enumerate mock decoders
  tools/testing/cxl: Mock one level of switches
  tools/testing/cxl: Fix root port to host bridge assignment
  tools/testing/cxl: Mock dvsec_ranges()
  cxl/core/port: Add endpoint decoders
  cxl/core: Move target_list out of base decoder attributes
  cxl/mem: Add the cxl_mem driver
  cxl/core/port: Add switch port enumeration
  cxl/memdev: Add numa_node attribute
  cxl/pci: Emit device serial number
  cxl/pci: Implement wait for media active
  ...
parents b14ffae3 05e81553
Loading
Loading
Loading
Loading
+36 −0
Original line number Diff line number Diff line
What:		/sys/bus/cxl/flush
Date:		Januarry, 2022
KernelVersion:	v5.18
Contact:	linux-cxl@vger.kernel.org
Description:
		(WO) If userspace manually unbinds a port the kernel schedules
		all descendant memdevs for unbind. Writing '1' to this attribute
		flushes that work.

What:		/sys/bus/cxl/devices/memX/firmware_version
Date:		December, 2020
KernelVersion:	v5.12
@@ -25,6 +34,24 @@ Description:
		identically named field in the Identify Memory Device Output
		Payload in the CXL-2.0 specification.

What:		/sys/bus/cxl/devices/memX/serial
Date:		January, 2022
KernelVersion:	v5.18
Contact:	linux-cxl@vger.kernel.org
Description:
		(RO) 64-bit serial number per the PCIe Device Serial Number
		capability. Mandatory for CXL devices, see CXL 2.0 8.1.12.2
		Memory Device PCIe Capabilities and Extended Capabilities.

What:		/sys/bus/cxl/devices/memX/numa_node
Date:		January, 2022
KernelVersion:	v5.18
Contact:	linux-cxl@vger.kernel.org
Description:
		(RO) If NUMA is enabled and the platform has affinitized the
		host PCI device for this memory device, emit the CPU node
		affinity for this device.

What:		/sys/bus/cxl/devices/*/devtype
Date:		June, 2021
KernelVersion:	v5.14
@@ -34,6 +61,15 @@ Description:
		the same value communicated in the DEVTYPE environment variable
		for uevents for devices on the "cxl" bus.

What:		/sys/bus/cxl/devices/*/modalias
Date:		December, 2021
KernelVersion:	v5.18
Contact:	linux-cxl@vger.kernel.org
Description:
		CXL device objects export the modalias attribute which mirrors
		the same value communicated in the MODALIAS environment variable
		for uevents for devices on the "cxl" bus.

What:		/sys/bus/cxl/devices/portX/uport
Date:		June, 2021
KernelVersion:	v5.14
+313 −2
Original line number Diff line number Diff line
@@ -14,6 +14,303 @@ that optionally define a device's contribution to an interleaved address
range across multiple devices underneath a host-bridge or interleaved
across host-bridges.

CXL Bus: Theory of Operation
============================
Similar to how a RAID driver takes disk objects and assembles them into a new
logical device, the CXL subsystem is tasked to take PCIe and ACPI objects and
assemble them into a CXL.mem decode topology. The need for runtime configuration
of the CXL.mem topology is also similar to RAID in that different environments
with the same hardware configuration may decide to assemble the topology in
contrasting ways. One may choose performance (RAID0) striping memory across
multiple Host Bridges and endpoints while another may opt for fault tolerance
and disable any striping in the CXL.mem topology.

Platform firmware enumerates a menu of interleave options at the "CXL root port"
(Linux term for the top of the CXL decode topology). From there, PCIe topology
dictates which endpoints can participate in which Host Bridge decode regimes.
Each PCIe Switch in the path between the root and an endpoint introduces a point
at which the interleave can be split. For example platform firmware may say at a
given range only decodes to 1 one Host Bridge, but that Host Bridge may in turn
interleave cycles across multiple Root Ports. An intervening Switch between a
port and an endpoint may interleave cycles across multiple Downstream Switch
Ports, etc.

Here is a sample listing of a CXL topology defined by 'cxl_test'. The 'cxl_test'
module generates an emulated CXL topology of 2 Host Bridges each with 2 Root
Ports. Each of those Root Ports are connected to 2-way switches with endpoints
connected to those downstream ports for a total of 8 endpoints::

    # cxl list -BEMPu -b cxl_test
    {
      "bus":"root3",
      "provider":"cxl_test",
      "ports:root3":[
        {
          "port":"port5",
          "host":"cxl_host_bridge.1",
          "ports:port5":[
            {
              "port":"port8",
              "host":"cxl_switch_uport.1",
              "endpoints:port8":[
                {
                  "endpoint":"endpoint9",
                  "host":"mem2",
                  "memdev":{
                    "memdev":"mem2",
                    "pmem_size":"256.00 MiB (268.44 MB)",
                    "ram_size":"256.00 MiB (268.44 MB)",
                    "serial":"0x1",
                    "numa_node":1,
                    "host":"cxl_mem.1"
                  }
                },
                {
                  "endpoint":"endpoint15",
                  "host":"mem6",
                  "memdev":{
                    "memdev":"mem6",
                    "pmem_size":"256.00 MiB (268.44 MB)",
                    "ram_size":"256.00 MiB (268.44 MB)",
                    "serial":"0x5",
                    "numa_node":1,
                    "host":"cxl_mem.5"
                  }
                }
              ]
            },
            {
              "port":"port12",
              "host":"cxl_switch_uport.3",
              "endpoints:port12":[
                {
                  "endpoint":"endpoint17",
                  "host":"mem8",
                  "memdev":{
                    "memdev":"mem8",
                    "pmem_size":"256.00 MiB (268.44 MB)",
                    "ram_size":"256.00 MiB (268.44 MB)",
                    "serial":"0x7",
                    "numa_node":1,
                    "host":"cxl_mem.7"
                  }
                },
                {
                  "endpoint":"endpoint13",
                  "host":"mem4",
                  "memdev":{
                    "memdev":"mem4",
                    "pmem_size":"256.00 MiB (268.44 MB)",
                    "ram_size":"256.00 MiB (268.44 MB)",
                    "serial":"0x3",
                    "numa_node":1,
                    "host":"cxl_mem.3"
                  }
                }
              ]
            }
          ]
        },
        {
          "port":"port4",
          "host":"cxl_host_bridge.0",
          "ports:port4":[
            {
              "port":"port6",
              "host":"cxl_switch_uport.0",
              "endpoints:port6":[
                {
                  "endpoint":"endpoint7",
                  "host":"mem1",
                  "memdev":{
                    "memdev":"mem1",
                    "pmem_size":"256.00 MiB (268.44 MB)",
                    "ram_size":"256.00 MiB (268.44 MB)",
                    "serial":"0",
                    "numa_node":0,
                    "host":"cxl_mem.0"
                  }
                },
                {
                  "endpoint":"endpoint14",
                  "host":"mem5",
                  "memdev":{
                    "memdev":"mem5",
                    "pmem_size":"256.00 MiB (268.44 MB)",
                    "ram_size":"256.00 MiB (268.44 MB)",
                    "serial":"0x4",
                    "numa_node":0,
                    "host":"cxl_mem.4"
                  }
                }
              ]
            },
            {
              "port":"port10",
              "host":"cxl_switch_uport.2",
              "endpoints:port10":[
                {
                  "endpoint":"endpoint16",
                  "host":"mem7",
                  "memdev":{
                    "memdev":"mem7",
                    "pmem_size":"256.00 MiB (268.44 MB)",
                    "ram_size":"256.00 MiB (268.44 MB)",
                    "serial":"0x6",
                    "numa_node":0,
                    "host":"cxl_mem.6"
                  }
                },
                {
                  "endpoint":"endpoint11",
                  "host":"mem3",
                  "memdev":{
                    "memdev":"mem3",
                    "pmem_size":"256.00 MiB (268.44 MB)",
                    "ram_size":"256.00 MiB (268.44 MB)",
                    "serial":"0x2",
                    "numa_node":0,
                    "host":"cxl_mem.2"
                  }
                }
              ]
            }
          ]
        }
      ]
    }

In that listing each "root", "port", and "endpoint" object correspond a kernel
'struct cxl_port' object. A 'cxl_port' is a device that can decode CXL.mem to
its descendants. So "root" claims non-PCIe enumerable platform decode ranges and
decodes them to "ports", "ports" decode to "endpoints", and "endpoints"
represent the decode from SPA (System Physical Address) to DPA (Device Physical
Address).

Continuing the RAID analogy, disks have both topology metadata and on device
metadata that determine RAID set assembly. CXL Port topology and CXL Port link
status is metadata for CXL.mem set assembly. The CXL Port topology is enumerated
by the arrival of a CXL.mem device. I.e. unless and until the PCIe core attaches
the cxl_pci driver to a CXL Memory Expander there is no role for CXL Port
objects. Conversely for hot-unplug / removal scenarios, there is no need for
the Linux PCI core to tear down switch-level CXL resources because the endpoint
->remove() event cleans up the port data that was established to support that
Memory Expander.

The port metadata and potential decode schemes that a give memory device may
participate can be determined via a command like::

    # cxl list -BDMu -d root -m mem3
    {
      "bus":"root3",
      "provider":"cxl_test",
      "decoders:root3":[
        {
          "decoder":"decoder3.1",
          "resource":"0x8030000000",
          "size":"512.00 MiB (536.87 MB)",
          "volatile_capable":true,
          "nr_targets":2
        },
        {
          "decoder":"decoder3.3",
          "resource":"0x8060000000",
          "size":"512.00 MiB (536.87 MB)",
          "pmem_capable":true,
          "nr_targets":2
        },
        {
          "decoder":"decoder3.0",
          "resource":"0x8020000000",
          "size":"256.00 MiB (268.44 MB)",
          "volatile_capable":true,
          "nr_targets":1
        },
        {
          "decoder":"decoder3.2",
          "resource":"0x8050000000",
          "size":"256.00 MiB (268.44 MB)",
          "pmem_capable":true,
          "nr_targets":1
        }
      ],
      "memdevs:root3":[
        {
          "memdev":"mem3",
          "pmem_size":"256.00 MiB (268.44 MB)",
          "ram_size":"256.00 MiB (268.44 MB)",
          "serial":"0x2",
          "numa_node":0,
          "host":"cxl_mem.2"
        }
      ]
    }

...which queries the CXL topology to ask "given CXL Memory Expander with a kernel
device name of 'mem3' which platform level decode ranges may this device
participate". A given expander can participate in multiple CXL.mem interleave
sets simultaneously depending on how many decoder resource it has. In this
example mem3 can participate in one or more of a PMEM interleave that spans to
Host Bridges, a PMEM interleave that targets a single Host Bridge, a Volatile
memory interleave that spans 2 Host Bridges, and a Volatile memory interleave
that only targets a single Host Bridge.

Conversely the memory devices that can participate in a given platform level
decode scheme can be determined via a command like the following::

    # cxl list -MDu -d 3.2
    [
      {
        "memdevs":[
          {
            "memdev":"mem1",
            "pmem_size":"256.00 MiB (268.44 MB)",
            "ram_size":"256.00 MiB (268.44 MB)",
            "serial":"0",
            "numa_node":0,
            "host":"cxl_mem.0"
          },
          {
            "memdev":"mem5",
            "pmem_size":"256.00 MiB (268.44 MB)",
            "ram_size":"256.00 MiB (268.44 MB)",
            "serial":"0x4",
            "numa_node":0,
            "host":"cxl_mem.4"
          },
          {
            "memdev":"mem7",
            "pmem_size":"256.00 MiB (268.44 MB)",
            "ram_size":"256.00 MiB (268.44 MB)",
            "serial":"0x6",
            "numa_node":0,
            "host":"cxl_mem.6"
          },
          {
            "memdev":"mem3",
            "pmem_size":"256.00 MiB (268.44 MB)",
            "ram_size":"256.00 MiB (268.44 MB)",
            "serial":"0x2",
            "numa_node":0,
            "host":"cxl_mem.2"
          }
        ]
      },
      {
        "root decoders":[
          {
            "decoder":"decoder3.2",
            "resource":"0x8050000000",
            "size":"256.00 MiB (268.44 MB)",
            "pmem_capable":true,
            "nr_targets":1
          }
        ]
      }
    ]

...where the naming scheme for decoders is "decoder<port_id>.<instance_id>".

Driver Infrastructure
=====================

@@ -28,6 +325,14 @@ CXL Memory Device
.. kernel-doc:: drivers/cxl/pci.c
   :internal:

.. kernel-doc:: drivers/cxl/mem.c
   :doc: cxl mem

CXL Port
--------
.. kernel-doc:: drivers/cxl/port.c
   :doc: cxl port

CXL Core
--------
.. kernel-doc:: drivers/cxl/cxl.h
@@ -36,10 +341,16 @@ CXL Core
.. kernel-doc:: drivers/cxl/cxl.h
   :internal:

.. kernel-doc:: drivers/cxl/core/bus.c
.. kernel-doc:: drivers/cxl/core/port.c
   :doc: cxl core

.. kernel-doc:: drivers/cxl/core/bus.c
.. kernel-doc:: drivers/cxl/core/port.c
   :identifiers:

.. kernel-doc:: drivers/cxl/core/pci.c
   :doc: cxl core pci

.. kernel-doc:: drivers/cxl/core/pci.c
   :identifiers:

.. kernel-doc:: drivers/cxl/core/pmem.c
+33 −11
Original line number Diff line number Diff line
@@ -13,25 +13,26 @@ menuconfig CXL_BUS

if CXL_BUS

config CXL_MEM
	tristate "CXL.mem: Memory Devices"
config CXL_PCI
	tristate "PCI manageability"
	default CXL_BUS
	help
	  The CXL.mem protocol allows a device to act as a provider of
	  "System RAM" and/or "Persistent Memory" that is fully coherent
	  as if the memory was attached to the typical CPU memory
	  controller.
	  The CXL specification defines a "CXL memory device" sub-class in the
	  PCI "memory controller" base class of devices. Device's identified by
	  this class code provide support for volatile and / or persistent
	  memory to be mapped into the system address map (Host-managed Device
	  Memory (HDM)).

	  Say 'y/m' to enable a driver that will attach to CXL.mem devices for
	  configuration and management primarily via the mailbox interface. See
	  Chapter 2.3 Type 3 CXL Device in the CXL 2.0 specification for more
	  details.
	  Say 'y/m' to enable a driver that will attach to CXL memory expander
	  devices enumerated by the memory device class code for configuration
	  and management primarily via the mailbox interface. See Chapter 2.3
	  Type 3 CXL Device in the CXL 2.0 specification for more details.

	  If unsure say 'm'.

config CXL_MEM_RAW_COMMANDS
	bool "RAW Command Interface for Memory Devices"
	depends on CXL_MEM
	depends on CXL_PCI
	help
	  Enable CXL RAW command interface.

@@ -76,4 +77,25 @@ config CXL_PMEM
	  provisioning the persistent memory capacity of CXL memory expanders.

	  If unsure say 'm'.

config CXL_MEM
	tristate "CXL: Memory Expansion"
	depends on CXL_PCI
	default CXL_BUS
	help
	  The CXL.mem protocol allows a device to act as a provider of "System
	  RAM" and/or "Persistent Memory" that is fully coherent as if the
	  memory were attached to the typical CPU memory controller. This is
	  known as HDM "Host-managed Device Memory".

	  Say 'y/m' to enable a driver that will attach to CXL.mem devices for
	  memory expansion and control of HDM. See Chapter 9.13 in the CXL 2.0
	  specification for a detailed description of HDM.

	  If unsure say 'm'.

config CXL_PORT
	default CXL_BUS
	tristate

endif
+5 −1
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_CXL_BUS) += core/
obj-$(CONFIG_CXL_MEM) += cxl_pci.o
obj-$(CONFIG_CXL_PCI) += cxl_pci.o
obj-$(CONFIG_CXL_MEM) += cxl_mem.o
obj-$(CONFIG_CXL_ACPI) += cxl_acpi.o
obj-$(CONFIG_CXL_PMEM) += cxl_pmem.o
obj-$(CONFIG_CXL_PORT) += cxl_port.o

cxl_mem-y := mem.o
cxl_pci-y := pci.o
cxl_acpi-y := acpi.o
cxl_pmem-y := pmem.o
cxl_port-y := port.o
+27 −125
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@
#include <linux/kernel.h>
#include <linux/acpi.h>
#include <linux/pci.h>
#include "cxlpci.h"
#include "cxl.h"

/* Encode defined in CXL 2.0 8.2.5.12.7 HDM Decoder Control Register */
@@ -14,7 +15,7 @@

static unsigned long cfmws_to_decoder_flags(int restrictions)
{
	unsigned long flags = 0;
	unsigned long flags = CXL_DECODER_F_ENABLE;

	if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_TYPE2)
		flags |= CXL_DECODER_F_TYPE2;
@@ -101,16 +102,14 @@ static int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg,
	for (i = 0; i < CFMWS_INTERLEAVE_WAYS(cfmws); i++)
		target_map[i] = cfmws->interleave_targets[i];

	cxld = cxl_decoder_alloc(root_port, CFMWS_INTERLEAVE_WAYS(cfmws));
	cxld = cxl_root_decoder_alloc(root_port, CFMWS_INTERLEAVE_WAYS(cfmws));
	if (IS_ERR(cxld))
		return 0;

	cxld->flags = cfmws_to_decoder_flags(cfmws->restrictions);
	cxld->target_type = CXL_DECODER_EXPANDER;
	cxld->range = (struct range){
		.start = cfmws->base_hpa,
		.end = cfmws->base_hpa + cfmws->window_size - 1,
	};
	cxld->platform_res = (struct resource)DEFINE_RES_MEM(cfmws->base_hpa,
							     cfmws->window_size);
	cxld->interleave_ways = CFMWS_INTERLEAVE_WAYS(cfmws);
	cxld->interleave_granularity = CFMWS_INTERLEAVE_GRANULARITY(cfmws);

@@ -120,67 +119,17 @@ static int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg,
	else
		rc = cxl_decoder_autoremove(dev, cxld);
	if (rc) {
		dev_err(dev, "Failed to add decoder for %#llx-%#llx\n",
			cfmws->base_hpa,
			cfmws->base_hpa + cfmws->window_size - 1);
		dev_err(dev, "Failed to add decoder for %pr\n",
			&cxld->platform_res);
		return 0;
	}
	dev_dbg(dev, "add: %s node: %d range %#llx-%#llx\n",
		dev_name(&cxld->dev), phys_to_target_node(cxld->range.start),
		cfmws->base_hpa, cfmws->base_hpa + cfmws->window_size - 1);

	return 0;
}

__mock int match_add_root_ports(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) {
		ctx->error = rc;
		return rc;
	}
	ctx->count++;

	dev_dbg(dev, "add dport%d: %s\n", port_num, dev_name(&pdev->dev));
	dev_dbg(dev, "add: %s node: %d range %pr\n", dev_name(&cxld->dev),
		phys_to_target_node(cxld->platform_res.start),
		&cxld->platform_res);

	return 0;
}

static struct cxl_dport *find_dport_by_dev(struct cxl_port *port, struct device *dev)
{
	struct cxl_dport *dport;

	device_lock(&port->dev);
	list_for_each_entry(dport, &port->dports, list)
		if (dport->dport == dev) {
			device_unlock(&port->dev);
			return dport;
		}

	device_unlock(&port->dev);
	return NULL;
}

__mock struct acpi_device *to_cxl_host_bridge(struct device *host,
					      struct device *dev)
{
@@ -204,83 +153,35 @@ static int add_host_bridge_uport(struct device *match, void *arg)
	struct device *host = root_port->dev.parent;
	struct acpi_device *bridge = to_cxl_host_bridge(host, match);
	struct acpi_pci_root *pci_root;
	struct cxl_walk_context ctx;
	int single_port_map[1], rc;
	struct cxl_decoder *cxld;
	struct cxl_dport *dport;
	struct cxl_port *port;
	int rc;

	if (!bridge)
		return 0;

	dport = find_dport_by_dev(root_port, match);
	dport = cxl_find_dport_by_dev(root_port, match);
	if (!dport) {
		dev_dbg(host, "host bridge expected and not found\n");
		return 0;
	}

	port = devm_cxl_add_port(host, match, dport->component_reg_phys,
				 root_port);
	if (IS_ERR(port))
		return PTR_ERR(port);
	dev_dbg(host, "%s: add: %s\n", dev_name(match), dev_name(&port->dev));

	/*
	 * Note that this lookup already succeeded in
	 * to_cxl_host_bridge(), so no need to check for failure here
	 */
	pci_root = acpi_pci_find_root(bridge->handle);
	ctx = (struct cxl_walk_context){
		.dev = host,
		.root = pci_root->bus,
		.port = port,
	};
	pci_walk_bus(pci_root->bus, match_add_root_ports, &ctx);

	if (ctx.count == 0)
		return -ENODEV;
	if (ctx.error)
		return ctx.error;
	if (ctx.count > 1)
		return 0;

	/* TODO: Scan CHBCR for HDM Decoder resources */

	/*
	 * Per the CXL specification (8.2.5.12 CXL HDM Decoder Capability
	 * Structure) single ported host-bridges need not publish a decoder
	 * capability when a passthrough decode can be assumed, i.e. all
	 * transactions that the uport sees are claimed and passed to the single
	 * dport. Disable the range until the first CXL region is enumerated /
	 * activated.
	 */
	cxld = cxl_decoder_alloc(port, 1);
	if (IS_ERR(cxld))
		return PTR_ERR(cxld);

	cxld->interleave_ways = 1;
	cxld->interleave_granularity = PAGE_SIZE;
	cxld->target_type = CXL_DECODER_EXPANDER;
	cxld->range = (struct range) {
		.start = 0,
		.end = -1,
	};

	device_lock(&port->dev);
	dport = list_first_entry(&port->dports, typeof(*dport), list);
	device_unlock(&port->dev);

	single_port_map[0] = dport->port_id;

	rc = cxl_decoder_add(cxld, single_port_map);
	rc = devm_cxl_register_pci_bus(host, match, pci_root->bus);
	if (rc)
		put_device(&cxld->dev);
	else
		rc = cxl_decoder_autoremove(host, cxld);

	if (rc == 0)
		dev_dbg(host, "add: %s\n", dev_name(&cxld->dev));
		return rc;

	port = devm_cxl_add_port(host, match, dport->component_reg_phys,
				 root_port);
	if (IS_ERR(port))
		return PTR_ERR(port);
	dev_dbg(host, "%s: add: %s\n", dev_name(match), dev_name(&port->dev));

	return 0;
}

struct cxl_chbs_context {
@@ -309,9 +210,9 @@ static int cxl_get_chbcr(union acpi_subtable_headers *header, void *arg,

static int add_host_bridge_dport(struct device *match, void *arg)
{
	int rc;
	acpi_status status;
	unsigned long long uid;
	struct cxl_dport *dport;
	struct cxl_chbs_context ctx;
	struct cxl_port *root_port = arg;
	struct device *host = root_port->dev.parent;
@@ -340,11 +241,11 @@ static int add_host_bridge_dport(struct device *match, void *arg)
		return 0;
	}

	rc = cxl_add_dport(root_port, match, uid, ctx.chbcr);
	if (rc) {
	dport = devm_cxl_add_dport(root_port, match, uid, ctx.chbcr);
	if (IS_ERR(dport)) {
		dev_err(host, "failed to add downstream port: %s\n",
			dev_name(match));
		return rc;
		return PTR_ERR(dport);
	}
	dev_dbg(host, "add dport%llu: %s\n", uid, dev_name(match));
	return 0;
@@ -413,7 +314,8 @@ static int cxl_acpi_probe(struct platform_device *pdev)
	if (rc < 0)
		return rc;

	return 0;
	/* In case PCI is scanned before ACPI re-trigger memdev attach */
	return cxl_bus_rescan();
}

static const struct acpi_device_id cxl_acpi_ids[] = {
Loading