Commit 98d2d3a2 authored by Dan Williams's avatar Dan Williams
Browse files

cxl/core: Generalize dport enumeration in the core



The core houses infrastructure for decoder resources. A CXL port's
dports are more closely related to decoder infrastructure than topology
enumeration. Implement generic PCI based dport enumeration in the core,
i.e. arrange for existing root port enumeration from cxl_acpi to share
code with switch port enumeration which just amounts to a small
difference in a pci_walk_bus() invocation once the appropriate 'struct
pci_bus' has been retrieved.

Set the convention that decoder objects are registered after all dports
are enumerated. This enables userspace to know when the CXL core is
finished establishing 'dportX' links underneath the 'portX' object.

Reviewed-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
Link: https://lore.kernel.org/r/164368114191.354031.5270501846455462665.stgit@dwillia2-desk3.amr.corp.intel.com


Signed-off-by: default avatarDan Williams <dan.j.williams@intel.com>
parent af9cae9f
Loading
Loading
Loading
Loading
+8 −59
Original line number Original line Diff line number Diff line
@@ -130,48 +130,6 @@ static int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg,
	return 0;
	return 0;
}
}


__mock int match_add_root_ports(struct pci_dev *pdev, void *data)
{
	resource_size_t creg = CXL_RESOURCE_NONE;
	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;
	struct cxl_register_map map;
	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;

	/* The driver doesn't rely on component registers for Root Ports yet. */
	rc = cxl_find_regblock(pdev, CXL_REGLOC_RBI_COMPONENT, &map);
	if (!rc)
		dev_info(&pdev->dev, "No component register block found\n");

	creg = cxl_regmap_to_base(pdev, &map);

	port_num = FIELD_GET(PCI_EXP_LNKCAP_PN, lnkcap);
	rc = cxl_add_dport(port, &pdev->dev, port_num, creg);
	if (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 struct cxl_dport *find_dport_by_dev(struct cxl_port *port, struct device *dev)
static struct cxl_dport *find_dport_by_dev(struct cxl_port *port, struct device *dev)
{
{
	struct cxl_dport *dport;
	struct cxl_dport *dport;
@@ -210,7 +168,6 @@ static int add_host_bridge_uport(struct device *match, void *arg)
	struct device *host = root_port->dev.parent;
	struct device *host = root_port->dev.parent;
	struct acpi_device *bridge = to_cxl_host_bridge(host, match);
	struct acpi_device *bridge = to_cxl_host_bridge(host, match);
	struct acpi_pci_root *pci_root;
	struct acpi_pci_root *pci_root;
	struct cxl_walk_context ctx;
	int single_port_map[1], rc;
	int single_port_map[1], rc;
	struct cxl_decoder *cxld;
	struct cxl_decoder *cxld;
	struct cxl_dport *dport;
	struct cxl_dport *dport;
@@ -240,18 +197,10 @@ static int add_host_bridge_uport(struct device *match, void *arg)
		return PTR_ERR(port);
		return PTR_ERR(port);
	dev_dbg(host, "%s: add: %s\n", dev_name(match), dev_name(&port->dev));
	dev_dbg(host, "%s: add: %s\n", dev_name(match), dev_name(&port->dev));


	ctx = (struct cxl_walk_context){
	rc = devm_cxl_port_enumerate_dports(host, port);
		.dev = host,
	if (rc < 0)
		.root = pci_root->bus,
		return rc;
		.port = port,
	if (rc > 1)
	};
	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;
		return 0;


	/* TODO: Scan CHBCR for HDM Decoder resources */
	/* TODO: Scan CHBCR for HDM Decoder resources */
@@ -311,9 +260,9 @@ static int cxl_get_chbcr(union acpi_subtable_headers *header, void *arg,


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


	cxl_device_lock(&root_port->dev);
	cxl_device_lock(&root_port->dev);
	rc = cxl_add_dport(root_port, match, uid, ctx.chbcr);
	dport = devm_cxl_add_dport(host, root_port, match, uid, ctx.chbcr);
	cxl_device_unlock(&root_port->dev);
	cxl_device_unlock(&root_port->dev);
	if (rc) {
	if (IS_ERR(dport)) {
		dev_err(host, "failed to add downstream port: %s\n",
		dev_err(host, "failed to add downstream port: %s\n",
			dev_name(match));
			dev_name(match));
		return rc;
		return PTR_ERR(dport);
	}
	}
	dev_dbg(host, "add dport%llu: %s\n", uid, dev_name(match));
	dev_dbg(host, "add dport%llu: %s\n", uid, dev_name(match));
	return 0;
	return 0;
+1 −0
Original line number Original line Diff line number Diff line
@@ -7,3 +7,4 @@ cxl_core-y += pmem.o
cxl_core-y += regs.o
cxl_core-y += regs.o
cxl_core-y += memdev.o
cxl_core-y += memdev.o
cxl_core-y += mbox.o
cxl_core-y += mbox.o
cxl_core-y += pci.o

drivers/cxl/core/pci.c

0 → 100644
+101 −0
Original line number Original line Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2021 Intel Corporation. All rights reserved. */
#include <linux/device.h>
#include <linux/pci.h>
#include <cxlpci.h>
#include <cxl.h>
#include "core.h"

/**
 * DOC: cxl core pci
 *
 * Compute Express Link protocols are layered on top of PCIe. CXL core provides
 * a set of helpers for CXL interactions which occur via PCIe.
 */

struct cxl_walk_context {
	struct pci_bus *bus;
	struct device *host;
	struct cxl_port *port;
	int type;
	int error;
	int count;
};

static int match_add_dports(struct pci_dev *pdev, void *data)
{
	struct cxl_walk_context *ctx = data;
	struct cxl_port *port = ctx->port;
	int type = pci_pcie_type(pdev);
	struct cxl_register_map map;
	struct cxl_dport *dport;
	u32 lnkcap, port_num;
	int rc;

	if (pdev->bus != ctx->bus)
		return 0;
	if (!pci_is_pcie(pdev))
		return 0;
	if (type != ctx->type)
		return 0;
	if (pci_read_config_dword(pdev, pci_pcie_cap(pdev) + PCI_EXP_LNKCAP,
				  &lnkcap))
		return 0;

	rc = cxl_find_regblock(pdev, CXL_REGLOC_RBI_COMPONENT, &map);
	if (rc)
		dev_dbg(&port->dev, "failed to find component registers\n");

	port_num = FIELD_GET(PCI_EXP_LNKCAP_PN, lnkcap);
	cxl_device_lock(&port->dev);
	dport = devm_cxl_add_dport(ctx->host, port, &pdev->dev, port_num,
				   cxl_regmap_to_base(pdev, &map));
	cxl_device_unlock(&port->dev);
	if (IS_ERR(dport)) {
		ctx->error = PTR_ERR(dport);
		return PTR_ERR(dport);
	}
	ctx->count++;

	dev_dbg(&port->dev, "add dport%d: %s\n", port_num, dev_name(&pdev->dev));

	return 0;
}

/**
 * devm_cxl_port_enumerate_dports - enumerate downstream ports of the upstream port
 * @host: devm context
 * @port: cxl_port whose ->uport is the upstream of dports to be enumerated
 *
 * Returns a positive number of dports enumerated or a negative error
 * code.
 */
int devm_cxl_port_enumerate_dports(struct device *host, struct cxl_port *port)
{
	struct pci_bus *bus = cxl_port_to_pci_bus(port);
	struct cxl_walk_context ctx;
	int type;

	if (!bus)
		return -ENXIO;

	if (pci_is_root_bus(bus))
		type = PCI_EXP_TYPE_ROOT_PORT;
	else
		type = PCI_EXP_TYPE_DOWNSTREAM;

	ctx = (struct cxl_walk_context) {
		.host = host,
		.port = port,
		.bus = bus,
		.type = type,
	};
	pci_walk_bus(bus, match_add_dports, &ctx);

	if (ctx.count == 0)
		return -ENODEV;
	if (ctx.error)
		return ctx.error;
	return ctx.count;
}
EXPORT_SYMBOL_NS_GPL(devm_cxl_port_enumerate_dports, CXL);
+52 −39
Original line number Original line Diff line number Diff line
@@ -243,22 +243,10 @@ struct cxl_decoder *to_cxl_decoder(struct device *dev)
}
}
EXPORT_SYMBOL_NS_GPL(to_cxl_decoder, CXL);
EXPORT_SYMBOL_NS_GPL(to_cxl_decoder, CXL);


static void cxl_dport_release(struct cxl_dport *dport)
{
	list_del(&dport->list);
	put_device(dport->dport);
	kfree(dport);
}

static void cxl_port_release(struct device *dev)
static void cxl_port_release(struct device *dev)
{
{
	struct cxl_port *port = to_cxl_port(dev);
	struct cxl_port *port = to_cxl_port(dev);
	struct cxl_dport *dport, *_d;


	cxl_device_lock(dev);
	list_for_each_entry_safe(dport, _d, &port->dports, list)
		cxl_dport_release(dport);
	cxl_device_unlock(dev);
	ida_free(&cxl_port_ida, port->id);
	ida_free(&cxl_port_ida, port->id);
	kfree(port);
	kfree(port);
}
}
@@ -292,18 +280,7 @@ EXPORT_SYMBOL_NS_GPL(to_cxl_port, CXL);
static void unregister_port(void *_port)
static void unregister_port(void *_port)
{
{
	struct cxl_port *port = _port;
	struct cxl_port *port = _port;
	struct cxl_dport *dport;


	cxl_device_lock(&port->dev);
	list_for_each_entry(dport, &port->dports, list) {
		char link_name[CXL_TARGET_STRLEN];

		if (snprintf(link_name, CXL_TARGET_STRLEN, "dport%d",
			     dport->port_id) >= CXL_TARGET_STRLEN)
			continue;
		sysfs_remove_link(&port->dev.kobj, link_name);
	}
	cxl_device_unlock(&port->dev);
	device_unregister(&port->dev);
	device_unregister(&port->dev);
}
}


@@ -532,51 +509,87 @@ static int add_dport(struct cxl_port *port, struct cxl_dport *new)
	return dup ? -EEXIST : 0;
	return dup ? -EEXIST : 0;
}
}


static void cxl_dport_remove(void *data)
{
	struct cxl_dport *dport = data;
	struct cxl_port *port = dport->port;

	put_device(dport->dport);
	cxl_device_lock(&port->dev);
	list_del(&dport->list);
	cxl_device_unlock(&port->dev);
}

static void cxl_dport_unlink(void *data)
{
	struct cxl_dport *dport = data;
	struct cxl_port *port = dport->port;
	char link_name[CXL_TARGET_STRLEN];

	sprintf(link_name, "dport%d", dport->port_id);
	sysfs_remove_link(&port->dev.kobj, link_name);
}

/**
/**
 * cxl_add_dport - append downstream port data to a cxl_port
 * devm_cxl_add_dport - append downstream port data to a cxl_port
 * @host: devm context for allocations
 * @port: the cxl_port that references this dport
 * @port: the cxl_port that references this dport
 * @dport_dev: firmware or PCI device representing the dport
 * @dport_dev: firmware or PCI device representing the dport
 * @port_id: identifier for this dport in a decoder's target list
 * @port_id: identifier for this dport in a decoder's target list
 * @component_reg_phys: optional location of CXL component registers
 * @component_reg_phys: optional location of CXL component registers
 *
 *
 * Note that all allocations and links are undone by cxl_port deletion
 * Note that dports are appended to the devm release action's of the
 * and release.
 * either the port's host (for root ports), or the port itself (for
 * switch ports)
 */
 */
int cxl_add_dport(struct cxl_port *port, struct device *dport_dev, int port_id,
struct cxl_dport *devm_cxl_add_dport(struct device *host, struct cxl_port *port,
				     struct device *dport_dev, int port_id,
				     resource_size_t component_reg_phys)
				     resource_size_t component_reg_phys)
{
{
	char link_name[CXL_TARGET_STRLEN];
	char link_name[CXL_TARGET_STRLEN];
	struct cxl_dport *dport;
	struct cxl_dport *dport;
	int rc;
	int rc;


	if (!host->driver) {
		dev_WARN_ONCE(&port->dev, 1, "dport:%s bad devm context\n",
			      dev_name(dport_dev));
		return ERR_PTR(-ENXIO);
	}

	if (snprintf(link_name, CXL_TARGET_STRLEN, "dport%d", port_id) >=
	if (snprintf(link_name, CXL_TARGET_STRLEN, "dport%d", port_id) >=
	    CXL_TARGET_STRLEN)
	    CXL_TARGET_STRLEN)
		return -EINVAL;
		return ERR_PTR(-EINVAL);


	dport = kzalloc(sizeof(*dport), GFP_KERNEL);
	dport = devm_kzalloc(host, sizeof(*dport), GFP_KERNEL);
	if (!dport)
	if (!dport)
		return -ENOMEM;
		return ERR_PTR(-ENOMEM);


	INIT_LIST_HEAD(&dport->list);
	INIT_LIST_HEAD(&dport->list);
	dport->dport = get_device(dport_dev);
	dport->dport = dport_dev;
	dport->port_id = port_id;
	dport->port_id = port_id;
	dport->component_reg_phys = component_reg_phys;
	dport->component_reg_phys = component_reg_phys;
	dport->port = port;
	dport->port = port;


	rc = add_dport(port, dport);
	rc = add_dport(port, dport);
	if (rc)
	if (rc)
		goto err;
		return ERR_PTR(rc);

	get_device(dport_dev);
	rc = devm_add_action_or_reset(host, cxl_dport_remove, dport);
	if (rc)
		return ERR_PTR(rc);


	rc = sysfs_create_link(&port->dev.kobj, &dport_dev->kobj, link_name);
	rc = sysfs_create_link(&port->dev.kobj, &dport_dev->kobj, link_name);
	if (rc)
	if (rc)
		goto err;
		return ERR_PTR(rc);


	return 0;
	rc = devm_add_action_or_reset(host, cxl_dport_unlink, dport);
err:
	if (rc)
	cxl_dport_release(dport);
		return ERR_PTR(rc);
	return rc;

	return dport;
}
}
EXPORT_SYMBOL_NS_GPL(cxl_add_dport, CXL);
EXPORT_SYMBOL_NS_GPL(devm_cxl_add_dport, CXL);


static int decoder_populate_targets(struct cxl_decoder *cxld,
static int decoder_populate_targets(struct cxl_decoder *cxld,
				    struct cxl_port *port, int *target_map)
				    struct cxl_port *port, int *target_map)
+4 −12
Original line number Original line Diff line number Diff line
@@ -236,14 +236,6 @@ struct cxl_nvdimm {
	struct nvdimm *nvdimm;
	struct nvdimm *nvdimm;
};
};


struct cxl_walk_context {
	struct device *dev;
	struct pci_bus *root;
	struct cxl_port *port;
	int error;
	int count;
};

/**
/**
 * struct cxl_port - logical collection of upstream port devices and
 * struct cxl_port - logical collection of upstream port devices and
 *		     downstream port devices to construct a CXL memory
 *		     downstream port devices to construct a CXL memory
@@ -295,17 +287,17 @@ static inline bool is_cxl_root(struct cxl_port *port)


bool is_cxl_port(struct device *dev);
bool is_cxl_port(struct device *dev);
struct cxl_port *to_cxl_port(struct device *dev);
struct cxl_port *to_cxl_port(struct device *dev);
struct pci_bus;
int devm_cxl_register_pci_bus(struct device *host, struct device *uport,
int devm_cxl_register_pci_bus(struct device *host, struct device *uport,
			      struct pci_bus *bus);
			      struct pci_bus *bus);
struct pci_bus *cxl_port_to_pci_bus(struct cxl_port *port);
struct pci_bus *cxl_port_to_pci_bus(struct cxl_port *port);
struct cxl_port *devm_cxl_add_port(struct device *host, struct device *uport,
struct cxl_port *devm_cxl_add_port(struct device *host, struct device *uport,
				   resource_size_t component_reg_phys,
				   resource_size_t component_reg_phys,
				   struct cxl_port *parent_port);
				   struct cxl_port *parent_port);

int cxl_add_dport(struct cxl_port *port, struct device *dport, int port_id,
		  resource_size_t component_reg_phys);
struct cxl_port *find_cxl_root(struct device *dev);
struct cxl_port *find_cxl_root(struct device *dev);

struct cxl_dport *devm_cxl_add_dport(struct device *host, struct cxl_port *port,
				     struct device *dport, int port_id,
				     resource_size_t component_reg_phys);
struct cxl_decoder *to_cxl_decoder(struct device *dev);
struct cxl_decoder *to_cxl_decoder(struct device *dev);
bool is_root_decoder(struct device *dev);
bool is_root_decoder(struct device *dev);
bool is_cxl_decoder(struct device *dev);
bool is_cxl_decoder(struct device *dev);
Loading