Skip to content
Permalink
Browse files
cxl/port: Introduce a port driver
The CXL port driver is responsible for managing the decoder resources
contained within the port. It will also provide APIs that other drivers
will consume for managing these resources.

There are 4 types of ports in a system:
1. Platform port. This is a non-programmable entity. Such a port is
   named rootX. It is enumerated by cxl_acpi in an ACPI based system.
2. Hostbridge port. This ports register access is defined in a platform
   specific way (CHBS for ACPI platforms). It has n downstream ports,
   each of which are known as CXL 2.0 root ports. Once the platform
   specific mechanism to get the offset to the registers is obtained it
   operates just like other CXL components. The enumeration of this
   component is started by cxl_acpi and completed by cxl_port.
3. Switch port. A switch port is similar to a hostbridge port except
   register access is defined in the CXL specification in a platform
   agnostic way. The downstream ports for a switch are simply known as
   downstream ports. The enumeration of these are entirely contained
   within cxl_port.
4. Endpoint port. Endpoint ports are similar to switch ports with the
   exception that they have no downstream ports, only the underlying
   media on the device. The enumeration of these are started by cxl_pci,
   and completed by cxl_port.

Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
  • Loading branch information
bwidawsk authored and intel-lab-lkp committed Nov 20, 2021
1 parent 3f74c99 commit 8ff43502e84dd4fa1296a131cb0cc82146389db4
Show file tree
Hide file tree
Showing 7 changed files with 450 additions and 9 deletions.
@@ -28,6 +28,11 @@ CXL Memory Device
.. kernel-doc:: drivers/cxl/pci.c
:internal:

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

CXL Core
--------
.. kernel-doc:: drivers/cxl/cxl.h
@@ -77,4 +77,26 @@ config CXL_PMEM
provisioning the persistent memory capacity of CXL memory expanders.

If unsure say 'm'.

config CXL_MEM
tristate "CXL.mem: Memory Devices"
select CXL_PORT
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 was 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
tristate

endif
@@ -3,7 +3,9 @@ obj-$(CONFIG_CXL_BUS) += core/
obj-$(CONFIG_CXL_PCI) += cxl_pci.o
obj-$(CONFIG_CXL_ACPI) += cxl_acpi.o
obj-$(CONFIG_CXL_PMEM) += cxl_pmem.o
obj-$(CONFIG_CXL_PORT) += cxl_port.o

cxl_pci-y := pci.o
cxl_acpi-y := acpi.o
cxl_pmem-y := pmem.o
cxl_port-y := port.o
@@ -31,6 +31,8 @@ static DECLARE_RWSEM(root_port_sem);

static struct device *cxl_topology_host;

static bool is_cxl_decoder(struct device *dev);

int cxl_register_topology_host(struct device *host)
{
down_write(&topology_host_sem);
@@ -75,6 +77,45 @@ static void put_cxl_topology_host(struct device *dev)
up_read(&topology_host_sem);
}

static int decoder_match(struct device *dev, void *data)
{
struct resource *theirs = (struct resource *)data;
struct cxl_decoder *cxld;

if (!is_cxl_decoder(dev))
return 0;

cxld = to_cxl_decoder(dev);
if (theirs->start <= cxld->decoder_range.start &&
theirs->end >= cxld->decoder_range.end)
return 1;

return 0;
}

static struct cxl_decoder *cxl_find_root_decoder(resource_size_t base,
resource_size_t size)
{
struct cxl_decoder *cxld = NULL;
struct device *cxldd;
struct device *host;
struct resource res = (struct resource){
.start = base,
.end = base + size - 1,
};

host = get_cxl_topology_host();
if (!host)
return NULL;

cxldd = device_find_child(host, &res, decoder_match);
if (cxldd)
cxld = to_cxl_decoder(cxldd);

put_cxl_topology_host(host);
return cxld;
}

static ssize_t devtype_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
@@ -280,6 +321,11 @@ bool is_root_decoder(struct device *dev)
}
EXPORT_SYMBOL_NS_GPL(is_root_decoder, CXL);

static bool is_cxl_decoder(struct device *dev)
{
return dev->type->release == cxl_decoder_release;
}

struct cxl_decoder *to_cxl_decoder(struct device *dev)
{
if (dev_WARN_ONCE(dev, dev->type->release != cxl_decoder_release,
@@ -327,6 +373,7 @@ struct cxl_port *to_cxl_port(struct device *dev)
return NULL;
return container_of(dev, struct cxl_port, dev);
}
EXPORT_SYMBOL_NS_GPL(to_cxl_port, CXL);

struct cxl_dport *cxl_get_root_dport(struct device *dev)
{
@@ -735,6 +782,24 @@ EXPORT_SYMBOL_NS_GPL(cxl_decoder_add, CXL);

static void cxld_unregister(void *dev)
{
struct cxl_decoder *plat_decoder, *cxld = to_cxl_decoder(dev);
resource_size_t base, size;

if (is_root_decoder(dev)) {
device_unregister(dev);
return;
}

base = cxld->decoder_range.start;
size = range_len(&cxld->decoder_range);

if (size) {
plat_decoder = cxl_find_root_decoder(base, size);
if (plat_decoder)
__release_region(&plat_decoder->platform_res, base,
size);
}

device_unregister(dev);
}

@@ -789,6 +854,8 @@ static int cxl_device_id(struct device *dev)
return CXL_DEVICE_NVDIMM_BRIDGE;
if (dev->type == &cxl_nvdimm_type)
return CXL_DEVICE_NVDIMM;
if (dev->type == &cxl_port_type)
return CXL_DEVICE_PORT;
return 0;
}

@@ -159,9 +159,8 @@ void cxl_probe_device_regs(struct device *dev, void __iomem *base,
}
EXPORT_SYMBOL_NS_GPL(cxl_probe_device_regs, CXL);

static void __iomem *devm_cxl_iomap_block(struct device *dev,
resource_size_t addr,
resource_size_t length)
void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr,
resource_size_t length)
{
void __iomem *ret_val;
struct resource *res;
@@ -180,6 +179,7 @@ static void __iomem *devm_cxl_iomap_block(struct device *dev,

return ret_val;
}
EXPORT_SYMBOL_NS_GPL(devm_cxl_iomap_block, CXL);

int cxl_map_component_regs(struct pci_dev *pdev,
struct cxl_component_regs *regs,
@@ -17,6 +17,9 @@
* (port-driver, region-driver, nvdimm object-drivers... etc).
*/

/* CXL 2.0 8.2.4 CXL Component Register Layout and Definition */
#define CXL_COMPONENT_REG_BLOCK_SIZE SZ_64K

/* CXL 2.0 8.2.5 CXL.cache and CXL.mem Registers*/
#define CXL_CM_OFFSET 0x1000
#define CXL_CM_CAP_HDR_OFFSET 0x0
@@ -36,11 +39,22 @@
#define CXL_HDM_DECODER_CAP_OFFSET 0x0
#define CXL_HDM_DECODER_COUNT_MASK GENMASK(3, 0)
#define CXL_HDM_DECODER_TARGET_COUNT_MASK GENMASK(7, 4)
#define CXL_HDM_DECODER0_BASE_LOW_OFFSET 0x10
#define CXL_HDM_DECODER0_BASE_HIGH_OFFSET 0x14
#define CXL_HDM_DECODER0_SIZE_LOW_OFFSET 0x18
#define CXL_HDM_DECODER0_SIZE_HIGH_OFFSET 0x1c
#define CXL_HDM_DECODER0_CTRL_OFFSET 0x20
#define CXL_HDM_DECODER_INTERLEAVE_11_8 BIT(8)
#define CXL_HDM_DECODER_INTERLEAVE_14_12 BIT(9)
#define CXL_HDM_DECODER_CTRL_OFFSET 0x4
#define CXL_HDM_DECODER_ENABLE BIT(1)
#define CXL_HDM_DECODER0_BASE_LOW_OFFSET(i) (0x20 * (i) + 0x10)
#define CXL_HDM_DECODER0_BASE_HIGH_OFFSET(i) (0x20 * (i) + 0x14)
#define CXL_HDM_DECODER0_SIZE_LOW_OFFSET(i) (0x20 * (i) + 0x18)
#define CXL_HDM_DECODER0_SIZE_HIGH_OFFSET(i) (0x20 * (i) + 0x1c)
#define CXL_HDM_DECODER0_CTRL_OFFSET(i) (0x20 * (i) + 0x20)
#define CXL_HDM_DECODER0_CTRL_IG_MASK GENMASK(3, 0)
#define CXL_HDM_DECODER0_CTRL_IW_MASK GENMASK(7, 4)
#define CXL_HDM_DECODER0_CTRL_COMMIT BIT(9)
#define CXL_HDM_DECODER0_CTRL_COMMITTED BIT(10)
#define CXL_HDM_DECODER0_CTRL_TYPE BIT(12)
#define CXL_HDM_DECODER0_TL_LOW(i) (0x20 * (i) + 0x24)
#define CXL_HDM_DECODER0_TL_HIGH(i) (0x20 * (i) + 0x28)

static inline int cxl_hdm_decoder_count(u32 cap_hdr)
{
@@ -148,6 +162,8 @@ int cxl_map_device_regs(struct pci_dev *pdev,
enum cxl_regloc_type;
int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
struct cxl_register_map *map);
void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr,
resource_size_t length);

#define CXL_RESOURCE_NONE ((resource_size_t) -1)
#define CXL_TARGET_STRLEN 20
@@ -165,7 +181,8 @@ void cxl_unregister_topology_host(struct device *host);
#define CXL_DECODER_F_TYPE2 BIT(2)
#define CXL_DECODER_F_TYPE3 BIT(3)
#define CXL_DECODER_F_LOCK BIT(4)
#define CXL_DECODER_F_MASK GENMASK(4, 0)
#define CXL_DECODER_F_ENABLE BIT(5)
#define CXL_DECODER_F_MASK GENMASK(5, 0)

enum cxl_decoder_type {
CXL_DECODER_ACCELERATOR = 2,
@@ -255,6 +272,8 @@ struct cxl_walk_context {
* @dports: cxl_dport instances referenced by decoders
* @decoder_ida: allocator for decoder ids
* @component_reg_phys: component register capability base address (optional)
* @rescan_work: worker object for bus rescans after port additions
* @data: opaque data with driver specific usage
*/
struct cxl_port {
struct device dev;
@@ -263,6 +282,8 @@ struct cxl_port {
struct list_head dports;
struct ida decoder_ida;
resource_size_t component_reg_phys;
struct work_struct rescan_work;
void *data;
};

/**
@@ -325,6 +346,7 @@ void cxl_driver_unregister(struct cxl_driver *cxl_drv);

#define CXL_DEVICE_NVDIMM_BRIDGE 1
#define CXL_DEVICE_NVDIMM 2
#define CXL_DEVICE_PORT 3

#define MODULE_ALIAS_CXL(type) MODULE_ALIAS("cxl:t" __stringify(type) "*")
#define CXL_MODALIAS_FMT "cxl:t%d"

0 comments on commit 8ff4350

Please sign in to comment.