Commit d25f0025 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'cxl-for-6.5' of git://git.kernel.org/pub/scm/linux/kernel/git/cxl/cxl

Pull CXL updates from Dan Williams:
 "The highlights in terms of new functionality are support for the
  standard CXL Performance Monitor definition that appeared in CXL 3.0,
  support for device sanitization (wiping all data from a device),
  secure-erase (re-keying encryption of user data), and support for
  firmware update. The firmware update support is notable as it reuses
  the simple sysfs_upload interface to just cat(1) a blob to a sysfs
  file and pipe that to the device.

  Additionally there are a substantial number of cleanups and
  reorganizations to get ready for RCH error handling (RCH == Restricted
  CXL Host == current shipping hardware generation / pre CXL-2.0
  topologies) and type-2 (accelerator / vendor specific) devices.

  For vendor specific devices they implement a subset of what the
  generic type-3 (generic memory expander) driver expects. As a result
  the rework decouples optional infrastructure from the core driver
  context.

  For RCH topologies, where the specification working group did not want
  to confuse pre-CXL-aware operating systems, many of the standard
  registers are hidden which makes support standard bus features like
  AER (PCIe Advanced Error Reporting) difficult. The rework arranges for
  the driver to help the PCI-AER core. Bjorn is on board with this
  direction but a late regression disocvery means the completion of this
  functionality needs to cook a bit longer, so it is code
  reorganizations only for now.

  Summary:

   - Add infrastructure for supporting background commands along with
     support for device sanitization and firmware update

   - Introduce a CXL performance monitoring unit driver based on the
     common definition in the specification.

   - Land some preparatory cleanup and refactoring for the anticipated
     arrival of CXL type-2 (accelerator devices) and CXL RCH (CXL-v1.1
     topology) error handling.

   - Rework CPU cache management with respect to region configuration
     (device hotplug or other dynamic changes to memory interleaving)

   - Fix region reconfiguration vs CXL decoder ordering rules"

* tag 'cxl-for-6.5' of git://git.kernel.org/pub/scm/linux/kernel/git/cxl/cxl: (51 commits)
  cxl: Fix one kernel-doc comment
  cxl/pci: Use correct flag for sanitize polling
  docs: perf: Minimal introduction the the CXL PMU device and driver
  perf: CXL Performance Monitoring Unit driver
  tools/testing/cxl: add firmware update emulation to CXL memdevs
  tools/testing/cxl: Use named effects for the Command Effect Log
  tools/testing/cxl: Fix command effects for inject/clear poison
  cxl: add a firmware update mechanism using the sysfs firmware loader
  cxl/test: Add Secure Erase opcode support
  cxl/mem: Support Secure Erase
  cxl/test: Add Sanitize opcode support
  cxl/mem: Wire up Sanitization support
  cxl/mbox: Add sanitization handling machinery
  cxl/mem: Introduce security state sysfs file
  cxl/mbox: Allow for IRQ_NONE case in the isr
  Revert "cxl/port: Enable the HDM decoder capability for switch ports"
  cxl/memdev: Formalize endpoint port linkage
  cxl/pci: Unconditionally unmask 256B Flit errors
  cxl/region: Manage decoder target_type at decoder-attach time
  cxl/hdm: Default CXL_DEVTYPE_DEVMEM decoders to CXL_DECODER_DEVMEM
  ...
parents 0a1c979c fe77cc2e
......@@ -58,6 +58,54 @@ Description:
affinity for this device.
What: /sys/bus/cxl/devices/memX/security/state
Date: June, 2023
KernelVersion: v6.5
Contact: linux-cxl@vger.kernel.org
Description:
(RO) Reading this file will display the CXL security state for
that device. Such states can be: 'disabled', 'sanitize', when
a sanitization is currently underway; or those available only
for persistent memory: 'locked', 'unlocked' or 'frozen'. This
sysfs entry is select/poll capable from userspace to notify
upon completion of a sanitize operation.
What: /sys/bus/cxl/devices/memX/security/sanitize
Date: June, 2023
KernelVersion: v6.5
Contact: linux-cxl@vger.kernel.org
Description:
(WO) Write a boolean 'true' string value to this attribute to
sanitize the device to securely re-purpose or decommission it.
This is done by ensuring that all user data and meta-data,
whether it resides in persistent capacity, volatile capacity,
or the LSA, is made permanently unavailable by whatever means
is appropriate for the media type. This functionality requires
the device to be not be actively decoding any HPA ranges.
What /sys/bus/cxl/devices/memX/security/erase
Date: June, 2023
KernelVersion: v6.5
Contact: linux-cxl@vger.kernel.org
Description:
(WO) Write a boolean 'true' string value to this attribute to
secure erase user data by changing the media encryption keys for
all user data areas of the device.
What: /sys/bus/cxl/devices/memX/firmware/
Date: April, 2023
KernelVersion: v6.5
Contact: linux-cxl@vger.kernel.org
Description:
(RW) Firmware uploader mechanism. The different files under
this directory can be used to upload and activate new
firmware for CXL devices. The interfaces under this are
documented in sysfs-class-firmware.
What: /sys/bus/cxl/devices/*/devtype
Date: June, 2021
KernelVersion: v5.14
......
.. SPDX-License-Identifier: GPL-2.0
======================================
CXL Performance Monitoring Unit (CPMU)
======================================
The CXL rev 3.0 specification provides a definition of CXL Performance
Monitoring Unit in section 13.2: Performance Monitoring.
CXL components (e.g. Root Port, Switch Upstream Port, End Point) may have
any number of CPMU instances. CPMU capabilities are fully discoverable from
the devices. The specification provides event definitions for all CXL protocol
message types and a set of additional events for things commonly counted on
CXL devices (e.g. DRAM events).
CPMU driver
===========
The CPMU driver registers a perf PMU with the name pmu_mem<X>.<Y> on the CXL bus
representing the Yth CPMU for memX.
/sys/bus/cxl/device/pmu_mem<X>.<Y>
The associated PMU is registered as
/sys/bus/event_sources/devices/cxl_pmu_mem<X>.<Y>
In common with other CXL bus devices, the id has no specific meaning and the
relationship to specific CXL device should be established via the device parent
of the device on the CXL bus.
PMU driver provides description of available events and filter options in sysfs.
The "format" directory describes all formats of the config (event vendor id,
group id and mask) config1 (threshold, filter enables) and config2 (filter
parameters) fields of the perf_event_attr structure. The "events" directory
describes all documented events show in perf list.
The events shown in perf list are the most fine grained events with a single
bit of the event mask set. More general events may be enable by setting
multiple mask bits in config. For example, all Device to Host Read Requests
may be captured on a single counter by setting the bits for all of
* d2h_req_rdcurr
* d2h_req_rdown
* d2h_req_rdshared
* d2h_req_rdany
* d2h_req_rdownnodata
Example of usage::
$#perf list
cxl_pmu_mem0.0/clock_ticks/ [Kernel PMU event]
cxl_pmu_mem0.0/d2h_req_rdshared/ [Kernel PMU event]
cxl_pmu_mem0.0/h2d_req_snpcur/ [Kernel PMU event]
cxl_pmu_mem0.0/h2d_req_snpdata/ [Kernel PMU event]
cxl_pmu_mem0.0/h2d_req_snpinv/ [Kernel PMU event]
-----------------------------------------------------------
$# perf stat -a -e cxl_pmu_mem0.0/clock_ticks/ -e cxl_pmu_mem0.0/d2h_req_rdshared/
Vendor specific events may also be available and if so can be used via
$# perf stat -a -e cxl_pmu_mem0.0/vid=VID,gid=GID,mask=MASK/
The driver does not support sampling so "perf record" is unsupported.
It only supports system-wide counting so attaching to a task is
unsupported.
......@@ -21,3 +21,4 @@ Performance monitor support
alibaba_pmu
nvidia-pmu
meson-ddr-pmu
cxl
......@@ -5203,6 +5203,13 @@ S: Maintained
F: drivers/cxl/
F: include/uapi/linux/cxl_mem.h
COMPUTE EXPRESS LINK PMU (CPMU)
M: Jonathan Cameron <jonathan.cameron@huawei.com>
L: linux-cxl@vger.kernel.org
S: Maintained
F: Documentation/admin-guide/perf/cxl.rst
F: drivers/perf/cxl_pmu.c
CONEXANT ACCESSRUNNER USB DRIVER
L: accessrunner-general@lists.sourceforge.net
S: Orphan
......
......@@ -82,6 +82,7 @@ config CXL_PMEM
config CXL_MEM
tristate "CXL: Memory Expansion"
depends on CXL_PCI
select FW_UPLOAD
default CXL_BUS
help
The CXL.mem protocol allows a device to act as a provider of "System
......@@ -139,4 +140,17 @@ config CXL_REGION_INVALIDATION_TEST
If unsure, or if this kernel is meant for production environments,
say N.
config CXL_PMU
tristate "CXL Performance Monitoring Unit"
default CXL_BUS
depends on PERF_EVENTS
help
Support performance monitoring as defined in CXL rev 3.0
section 13.2: Performance Monitoring. CXL components may have
one or more CXL Performance Monitoring Units (CPMUs).
Say 'y/m' to enable a driver that will attach to performance
monitoring units and provide standard perf based interfaces.
If unsure say 'm'.
endif
......@@ -258,7 +258,7 @@ static int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg,
cxld = &cxlrd->cxlsd.cxld;
cxld->flags = cfmws_to_decoder_flags(cfmws->restrictions);
cxld->target_type = CXL_DECODER_EXPANDER;
cxld->target_type = CXL_DECODER_HOSTONLYMEM;
cxld->hpa_range = (struct range) {
.start = res->start,
.end = res->end,
......@@ -327,66 +327,21 @@ __mock struct acpi_device *to_cxl_host_bridge(struct device *host,
return NULL;
}
/*
* A host bridge is a dport to a CFMWS decode and it is a uport to the
* dport (PCIe Root Ports) in the host bridge.
*/
static int add_host_bridge_uport(struct device *match, void *arg)
{
struct cxl_port *root_port = arg;
struct device *host = root_port->dev.parent;
struct acpi_device *hb = to_cxl_host_bridge(host, match);
struct acpi_pci_root *pci_root;
struct cxl_dport *dport;
struct cxl_port *port;
struct device *bridge;
int rc;
if (!hb)
return 0;
pci_root = acpi_pci_find_root(hb->handle);
bridge = pci_root->bus->bridge;
dport = cxl_find_dport_by_dev(root_port, bridge);
if (!dport) {
dev_dbg(host, "host bridge expected and not found\n");
return 0;
}
if (dport->rch) {
dev_info(bridge, "host supports CXL (restricted)\n");
return 0;
}
rc = devm_cxl_register_pci_bus(host, bridge, pci_root->bus);
if (rc)
return rc;
port = devm_cxl_add_port(host, bridge, dport->component_reg_phys,
dport);
if (IS_ERR(port))
return PTR_ERR(port);
dev_info(bridge, "host supports CXL\n");
return 0;
}
/* Note, @dev is used by mock_acpi_table_parse_cedt() */
struct cxl_chbs_context {
struct device *dev;
unsigned long long uid;
resource_size_t rcrb;
resource_size_t chbcr;
resource_size_t base;
u32 cxl_version;
};
static int cxl_get_chbcr(union acpi_subtable_headers *header, void *arg,
const unsigned long end)
static int cxl_get_chbs_iter(union acpi_subtable_headers *header, void *arg,
const unsigned long end)
{
struct cxl_chbs_context *ctx = arg;
struct acpi_cedt_chbs *chbs;
if (ctx->chbcr)
if (ctx->base != CXL_RESOURCE_NONE)
return 0;
chbs = (struct acpi_cedt_chbs *) header;
......@@ -395,23 +350,39 @@ static int cxl_get_chbcr(union acpi_subtable_headers *header, void *arg,
return 0;
ctx->cxl_version = chbs->cxl_version;
ctx->rcrb = CXL_RESOURCE_NONE;
ctx->chbcr = CXL_RESOURCE_NONE;
if (!chbs->base)
return 0;
if (chbs->cxl_version != ACPI_CEDT_CHBS_VERSION_CXL11) {
ctx->chbcr = chbs->base;
if (chbs->cxl_version == ACPI_CEDT_CHBS_VERSION_CXL11 &&
chbs->length != CXL_RCRB_SIZE)
return 0;
ctx->base = chbs->base;
return 0;
}
static int cxl_get_chbs(struct device *dev, struct acpi_device *hb,
struct cxl_chbs_context *ctx)
{
unsigned long long uid;
int rc;
rc = acpi_evaluate_integer(hb->handle, METHOD_NAME__UID, NULL, &uid);
if (rc != AE_OK) {
dev_err(dev, "unable to retrieve _UID\n");
return -ENOENT;
}
if (chbs->length != CXL_RCRB_SIZE)
return 0;
dev_dbg(dev, "UID found: %lld\n", uid);
*ctx = (struct cxl_chbs_context) {
.dev = dev,
.uid = uid,
.base = CXL_RESOURCE_NONE,
.cxl_version = UINT_MAX,
};
ctx->rcrb = chbs->base;
ctx->chbcr = cxl_rcrb_to_component(ctx->dev, chbs->base,
CXL_RCRB_DOWNSTREAM);
acpi_table_parse_cedt(ACPI_CEDT_TYPE_CHBS, cxl_get_chbs_iter, ctx);
return 0;
}
......@@ -420,7 +391,6 @@ static int add_host_bridge_dport(struct device *match, void *arg)
{
acpi_status rc;
struct device *bridge;
unsigned long long uid;
struct cxl_dport *dport;
struct cxl_chbs_context ctx;
struct acpi_pci_root *pci_root;
......@@ -431,51 +401,107 @@ static int add_host_bridge_dport(struct device *match, void *arg)
if (!hb)
return 0;
rc = acpi_evaluate_integer(hb->handle, METHOD_NAME__UID, NULL, &uid);
if (rc != AE_OK) {
dev_err(match, "unable to retrieve _UID\n");
return -ENODEV;
}
dev_dbg(match, "UID found: %lld\n", uid);
ctx = (struct cxl_chbs_context) {
.dev = match,
.uid = uid,
};
acpi_table_parse_cedt(ACPI_CEDT_TYPE_CHBS, cxl_get_chbcr, &ctx);
rc = cxl_get_chbs(match, hb, &ctx);
if (rc)
return rc;
if (!ctx.chbcr) {
if (ctx.cxl_version == UINT_MAX) {
dev_warn(match, "No CHBS found for Host Bridge (UID %lld)\n",
uid);
ctx.uid);
return 0;
}
if (ctx.rcrb != CXL_RESOURCE_NONE)
dev_dbg(match, "RCRB found for UID %lld: %pa\n", uid, &ctx.rcrb);
if (ctx.chbcr == CXL_RESOURCE_NONE) {
dev_warn(match, "CHBCR invalid for Host Bridge (UID %lld)\n",
uid);
if (ctx.base == CXL_RESOURCE_NONE) {
dev_warn(match, "CHBS invalid for Host Bridge (UID %lld)\n",
ctx.uid);
return 0;
}
dev_dbg(match, "CHBCR found: %pa\n", &ctx.chbcr);
pci_root = acpi_pci_find_root(hb->handle);
bridge = pci_root->bus->bridge;
if (ctx.cxl_version == ACPI_CEDT_CHBS_VERSION_CXL11)
dport = devm_cxl_add_rch_dport(root_port, bridge, uid,
ctx.chbcr, ctx.rcrb);
else
dport = devm_cxl_add_dport(root_port, bridge, uid,
ctx.chbcr);
/*
* In RCH mode, bind the component regs base to the dport. In
* VH mode it will be bound to the CXL host bridge's port
* object later in add_host_bridge_uport().
*/
if (ctx.cxl_version == ACPI_CEDT_CHBS_VERSION_CXL11) {
dev_dbg(match, "RCRB found for UID %lld: %pa\n", ctx.uid,
&ctx.base);
dport = devm_cxl_add_rch_dport(root_port, bridge, ctx.uid,
ctx.base);
} else {
dport = devm_cxl_add_dport(root_port, bridge, ctx.uid,
CXL_RESOURCE_NONE);
}
if (IS_ERR(dport))
return PTR_ERR(dport);
return 0;
}
/*
* A host bridge is a dport to a CFMWS decode and it is a uport to the
* dport (PCIe Root Ports) in the host bridge.
*/
static int add_host_bridge_uport(struct device *match, void *arg)
{
struct cxl_port *root_port = arg;
struct device *host = root_port->dev.parent;
struct acpi_device *hb = to_cxl_host_bridge(host, match);
struct acpi_pci_root *pci_root;
struct cxl_dport *dport;
struct cxl_port *port;
struct device *bridge;
struct cxl_chbs_context ctx;
resource_size_t component_reg_phys;
int rc;
if (!hb)
return 0;
pci_root = acpi_pci_find_root(hb->handle);
bridge = pci_root->bus->bridge;
dport = cxl_find_dport_by_dev(root_port, bridge);
if (!dport) {
dev_dbg(host, "host bridge expected and not found\n");
return 0;
}
if (dport->rch) {
dev_info(bridge, "host supports CXL (restricted)\n");
return 0;
}
rc = cxl_get_chbs(match, hb, &ctx);
if (rc)
return rc;
if (ctx.cxl_version == ACPI_CEDT_CHBS_VERSION_CXL11) {
dev_warn(bridge,
"CXL CHBS version mismatch, skip port registration\n");
return 0;
}
component_reg_phys = ctx.base;
if (component_reg_phys != CXL_RESOURCE_NONE)
dev_dbg(match, "CHBCR found for UID %lld: %pa\n",
ctx.uid, &component_reg_phys);
rc = devm_cxl_register_pci_bus(host, bridge, pci_root->bus);
if (rc)
return rc;
port = devm_cxl_add_port(host, bridge, component_reg_phys, dport);
if (IS_ERR(port))
return PTR_ERR(port);
dev_info(bridge, "host supports CXL\n");
return 0;
}
static int add_root_nvdimm_bridge(struct device *match, void *data)
{
struct cxl_decoder *cxld;
......
......@@ -12,5 +12,6 @@ cxl_core-y += memdev.o
cxl_core-y += mbox.o
cxl_core-y += pci.o
cxl_core-y += hdm.o
cxl_core-y += pmu.o
cxl_core-$(CONFIG_TRACING) += trace.o
cxl_core-$(CONFIG_CXL_REGION) += region.o
......@@ -6,6 +6,7 @@
extern const struct device_type cxl_nvdimm_bridge_type;
extern const struct device_type cxl_nvdimm_type;
extern const struct device_type cxl_pmu_type;
extern struct attribute_group cxl_base_attribute_group;
......@@ -63,6 +64,16 @@ int cxl_dpa_alloc(struct cxl_endpoint_decoder *cxled, unsigned long long size);
int cxl_dpa_free(struct cxl_endpoint_decoder *cxled);
resource_size_t cxl_dpa_size(struct cxl_endpoint_decoder *cxled);
resource_size_t cxl_dpa_resource_start(struct cxl_endpoint_decoder *cxled);
enum cxl_rcrb {
CXL_RCRB_DOWNSTREAM,
CXL_RCRB_UPSTREAM,
};
struct cxl_rcrb_info;
resource_size_t __rcrb_to_component(struct device *dev,
struct cxl_rcrb_info *ri,
enum cxl_rcrb which);
extern struct rw_semaphore cxl_dpa_rwsem;
int cxl_memdev_init(void);
......
......@@ -85,6 +85,7 @@ static int map_hdm_decoder_regs(struct cxl_port *port, void __iomem *crb,
struct cxl_component_regs *regs)
{
struct cxl_register_map map = {
.dev = &port->dev,
.resource = port->component_reg_phys,
.base = crb,
.max_size = CXL_COMPONENT_REG_BLOCK_SIZE,
......@@ -97,8 +98,7 @@ static int map_hdm_decoder_regs(struct cxl_port *port, void __iomem *crb,
return -ENODEV;
}
return cxl_map_component_regs(&port->dev, regs, &map,
BIT(CXL_CM_CAP_CAP_ID_HDM));
return cxl_map_component_regs(&map, regs, BIT(CXL_CM_CAP_CAP_ID_HDM));
}
static bool should_emulate_decoders(struct cxl_endpoint_dvsec_info *info)
......@@ -570,8 +570,9 @@ static void cxld_set_interleave(struct cxl_decoder *cxld, u32 *ctrl)
static void cxld_set_type(struct cxl_decoder *cxld, u32 *ctrl)
{
u32p_replace_bits(ctrl, !!(cxld->target_type == 3),
CXL_HDM_DECODER0_CTRL_TYPE);
u32p_replace_bits(ctrl,
!!(cxld->target_type == CXL_DECODER_HOSTONLYMEM),
CXL_HDM_DECODER0_CTRL_HOSTONLY);
}
static int cxlsd_set_targets(struct cxl_switch_decoder *cxlsd, u64 *tgt)
......@@ -764,7 +765,7 @@ static int cxl_setup_hdm_decoder_from_dvsec(
if (!len)
return -ENOENT;
cxld->target_type = CXL_DECODER_EXPANDER;
cxld->target_type = CXL_DECODER_HOSTONLYMEM;
cxld->commit = NULL;
cxld->reset = NULL;
cxld->hpa_range = info->dvsec_range[which];
......@@ -793,8 +794,8 @@ static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
int *target_map, void __iomem *hdm, int which,
u64 *dpa_base, struct cxl_endpoint_dvsec_info *info)
{
struct cxl_endpoint_decoder *cxled = NULL;
u64 size, base, skip, dpa_size, lo, hi;
struct cxl_endpoint_decoder *cxled;
bool committed;
u32 remainder;
int i, rc;
......@@ -827,6 +828,8 @@ static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
return -ENXIO;
}
if (info)
cxled = to_cxl_endpoint_decoder(&cxld->dev);
cxld->hpa_range = (struct range) {
.start = base,
.end = base + size - 1,
......@@ -837,10 +840,10 @@ static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
cxld->flags |= CXL_DECODER_F_ENABLE;
if (ctrl & CXL_HDM_DECODER0_CTRL_LOCK)
cxld->flags |= CXL_DECODER_F_LOCK;
if (FIELD_GET(CXL_HDM_DECODER0_CTRL_TYPE, ctrl))
cxld->target_type = CXL_DECODER_EXPANDER;
if (FIELD_GET(CXL_HDM_DECODER0_CTRL_HOSTONLY, ctrl))
cxld->target_type = CXL_DECODER_HOSTONLYMEM;
else
cxld->target_type = CXL_DECODER_ACCELERATOR;
cxld->target_type = CXL_DECODER_DEVMEM;
if (cxld->id != port->commit_end + 1) {
dev_warn(&port->dev,
"decoder%d.%d: Committed out of order\n",
......@@ -856,12 +859,28 @@ static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
}
port->commit_end = cxld->id;
} else {
/* unless / until type-2 drivers arrive, assume type-3 */
if (FIELD_GET(CXL_HDM_DECODER0_CTRL_TYPE, ctrl) == 0) {
ctrl |= CXL_HDM_DECODER0_CTRL_TYPE;
if (cxled) {
struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
struct cxl_dev_state *cxlds = cxlmd->cxlds;
/*
* Default by devtype until a device arrives that needs
* more precision.
*/
if (cxlds->type == CXL_DEVTYPE_CLASSMEM)
cxld->target_type = CXL_DECODER_HOSTONLYMEM;
else
cxld->target_type = CXL_DECODER_DEVMEM;
} else {
/* To be overridden by region type at commit time */
cxld->target_type = CXL_DECODER_HOSTONLYMEM;
}
if (!FIELD_GET(CXL_HDM_DECODER0_CTRL_HOSTONLY, ctrl) &&
cxld->target_type == CXL_DECODER_HOSTONLYMEM) {
ctrl |= CXL_HDM_DECODER0_CTRL_HOSTONLY;
writel(ctrl, hdm + CXL_HDM_DECODER0_CTRL_OFFSET(which));
}
cxld->target_type = CXL_DECODER_EXPANDER;
}
rc = eiw_to_ways(FIELD_GET(CXL_HDM_DECODER0_CTRL_IW_MASK, ctrl),
&cxld->interleave_ways);
......@@ -880,7 +899,7 @@ static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
port->id, cxld->id, cxld->hpa_range.start, cxld->hpa_range.end,
cxld->interleave_ways, cxld->interleave_granularity);
if (!info) {
if (!cxled) {
lo = readl(hdm + CXL_HDM_DECODER0_TL_LOW(which));
hi = readl(hdm + CXL_HDM_DECODER0_TL_HIGH(which));
target_list.value = (hi << 32) + lo;
......@@ -903,7 +922,6 @@ static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
lo = readl(hdm + CXL_HDM_DECODER0_SKIP_LOW(which));
hi = readl(hdm + CXL_HDM_DECODER0_SKIP_HIGH(which));
skip = (hi << 32) + lo;
cxled = to_cxl_endpoint_decoder(&cxld->dev);
rc = devm_cxl_dpa_reserve(cxled, *dpa_base + skip, dpa_size, skip);
if (rc) {
dev_err(&port->dev,
......
This diff is collapsed.
This diff is collapsed.
......@@ -67,7 +67,7 @@ static int match_add_dports(struct pci_dev *pdev, void *data)
/**
* devm_cxl_port_enumerate_dports - enumerate downstream ports of the upstream port
* @port: cxl_port whose ->uport is the upstream of dports to be enumerated
* @port: cxl_port whose ->uport_dev is the upstream of dports to be enumerated
*
* Returns a positive number of dports enumerated or a negative error
* code.
......@@ -308,36 +308,17 @@ static void disable_hdm(void *_cxlhdm)
hdm + CXL_HDM_DECODER_CTRL_OFFSET);
}
int devm_cxl_enable_hdm(struct cxl_port *port, struct cxl_hdm *cxlhdm)
static int devm_cxl_enable_hdm(struct device *host, struct cxl_hdm *cxlhdm)
{
void __iomem *hdm;
void __iomem *hdm = cxlhdm->regs.hdm_decoder;
u32 global_ctrl;
/*
* If the hdm capability was not mapped there is nothing to enable and
* the caller is responsible for what happens next. For example,
* emulate a passthrough decoder.
*/
if (IS_ERR(cxlhdm))
return 0;
hdm = cxlhdm->regs.hdm_decoder;
global_ctrl = readl(hdm + CXL_HDM_DECODER_CTRL_OFFSET);
/*
* If the HDM decoder capability was enabled on entry, skip
* registering disable_hdm() since this decode capability may be
* owned by platform firmware.
*/
if (global_ctrl & CXL_HDM_DECODER_ENABLE)
return 0;
writel(global_ctrl | CXL_HDM_DECODER_ENABLE,
hdm + CXL_HDM_DECODER_CTRL_OFFSET);
return devm_add_action_or_reset(&port->dev, disable_hdm, cxlhdm);
return devm_add_action_or_reset(host, disable_hdm, cxlhdm);
}
EXPORT_SYMBOL_NS_GPL(devm_cxl_enable_hdm, CXL);
int cxl_dvsec_rr_decode(struct device *dev, int d,
struct cxl_endpoint_dvsec_info *info)
......@@ -511,7 +492,7 @@ int cxl_hdm_decode_init(struct cxl_dev_state *cxlds, struct cxl_hdm *cxlhdm,
if (info->mem_enabled)
return 0;
rc = devm_cxl_enable_hdm(port, cxlhdm);
rc = devm_cxl_enable_hdm(&port->dev, cxlhdm);
if (rc)
return rc;
......@@ -622,7 +603,7 @@ static int cxl_cdat_read_table(struct device *dev,
*/
void read_cdat_data(struct cxl_port *port)
{
struct cxl_memdev *cxlmd = to_cxl_memdev(port->uport);
struct cxl_memdev *cxlmd = to_cxl_memdev(port->uport_dev);
struct device *host = cxlmd->dev.parent;
struct device *dev = &port->dev;
struct pci_doe_mb *cdat_doe;
......
......@@ -64,7 +64,7 @@ static int match_nvdimm_bridge(struct device *dev, void *data)
struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct cxl_memdev *cxlmd)
{
struct cxl_port *port = find_cxl_root(dev_get_drvdata(&cxlmd->dev));
struct cxl_port *port = find_cxl_root(cxlmd->endpoint);
struct device *dev;
if (!port)
......
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2023 Huawei. All rights reserved. */
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/idr.h>
#include <cxlmem.h>
#include <pmu.h>
#include <cxl.h>
#include "core.h"
static void cxl_pmu_release(struct device *dev)
{
struct cxl_pmu *pmu = to_cxl_pmu(dev);
kfree(pmu);
}
const struct device_type cxl_pmu_type = {
.name = "cxl_pmu",
.release = cxl_pmu_release,
};
static void remove_dev(void *dev)
{
device_del(dev);
}
int devm_cxl_pmu_add(struct device *parent, struct cxl_pmu_regs *regs,
int assoc_id, int index, enum cxl_pmu_type type)
{
struct cxl_pmu *pmu;
struct device *dev;
int rc;
pmu = kzalloc(sizeof(*pmu), GFP_KERNEL);
if (!pmu)
return -ENOMEM;
pmu->assoc_id = assoc_id;
pmu->index = index;
pmu->type = type;
pmu->base = regs->pmu;
dev = &pmu->dev;
device_initialize(dev);
device_set_pm_not_required(dev);
dev->parent = parent;
dev->bus = &cxl_bus_type;
dev->type = &cxl_pmu_type;
switch (pmu->type) {
case CXL_PMU_MEMDEV:
rc = dev_set_name(dev, "pmu_mem%d.%d", assoc_id, index);
break;
}
if (rc)
goto err;
rc = device_add(dev);
if (rc)
goto err;
return devm_add_action_or_reset(parent, remove_dev, dev);
err:
put_device(&pmu->dev);
return rc;
}
EXPORT_SYMBOL_NS_GPL(devm_cxl_pmu_add, CXL);
This diff is collapsed.
This diff is collapsed.
......@@ -6,6 +6,7 @@
#include <linux/pci.h>
#include <cxlmem.h>
#include <cxlpci.h>
#include <pmu.h>
#include "core.h"
......@@ -199,11 +200,13 @@ void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr,
return ret_val;
}
int cxl_map_component_regs(struct device *dev, struct cxl_component_regs *regs,
struct cxl_register_map *map, unsigned long map_mask)
int cxl_map_component_regs(const struct cxl_register_map *map,
struct cxl_component_regs *regs,
unsigned long map_mask)
{
struct device *dev = map->dev;
struct mapinfo {
struct cxl_reg_map *rmap;
const struct cxl_reg_map *rmap;
void __iomem **addr;
} mapinfo[] = {
{ &map->component_map.hdm_decoder, &regs->hdm_decoder },
......@@ -231,13 +234,13 @@ int cxl_map_component_regs(struct device *dev, struct cxl_component_regs *regs,
}
EXPORT_SYMBOL_NS_GPL(cxl_map_component_regs, CXL);
int cxl_map_device_regs(struct device *dev,
struct cxl_device_regs *regs,
struct cxl_register_map *map)
int cxl_map_device_regs(const struct cxl_register_map *map,
struct cxl_device_regs *regs)
{
struct device *dev = map->dev;
resource_size_t phys_addr = map->resource;
struct mapinfo {
struct cxl_reg_map *rmap;
const struct cxl_reg_map *rmap;
void __iomem **addr;
} mapinfo[] = {
{ &map->device_map.status, &regs->status, },
......@@ -286,23 +289,30 @@ static bool cxl_decode_regblock(struct pci_dev *pdev, u32 reg_lo, u32 reg_hi,
}
/**
* cxl_find_regblock() - Locate register blocks by type
* cxl_find_regblock_instance() - Locate a register block by type / index
* @pdev: The CXL PCI device to enumerate.
* @type: Register Block Indicator id
* @map: Enumeration output, clobbered on error
* @index: Index into which particular instance of a regblock wanted in the
* order found in register locator DVSEC.
*
* Return: 0 if register block enumerated, negative error code otherwise
*
* A CXL DVSEC may point to one or more register blocks, search for them
* by @type.
* by @type and @index.
*/
int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
struct cxl_register_map *map)
int cxl_find_regblock_instance(struct pci_dev *pdev, enum cxl_regloc_type type,
struct cxl_register_map *map, int index)
{
u32 regloc_size, regblocks;
int instance = 0;
int regloc, i;
map->resource = CXL_RESOURCE_NONE;
*map = (struct cxl_register_map) {
.dev = &pdev->dev,
.resource = CXL_RESOURCE_NONE,
};
regloc = pci_find_dvsec_capability(pdev, PCI_DVSEC_VENDOR_ID_CXL,
CXL_DVSEC_REG_LOCATOR);
if (!regloc)
......@@ -323,20 +333,148 @@ int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
if (!cxl_decode_regblock(pdev, reg_lo, reg_hi, map))
continue;
if (map->reg_type == type)
return 0;
if (map->reg_type == type) {
if (index == instance)
return 0;
instance++;
}
}
map->resource = CXL_RESOURCE_NONE;
return -ENODEV;
}
EXPORT_SYMBOL_NS_GPL(cxl_find_regblock_instance, CXL);
/**
* cxl_find_regblock() - Locate register blocks by type
* @pdev: The CXL PCI device to enumerate.
* @type: Register Block Indicator id
* @map: Enumeration output, clobbered on error
*
* Return: 0 if register block enumerated, negative error code otherwise
*
* A CXL DVSEC may point to one or more register blocks, search for them
* by @type.
*/
int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
struct cxl_register_map *map)
{
return cxl_find_regblock_instance(pdev, type, map, 0);
}
EXPORT_SYMBOL_NS_GPL(cxl_find_regblock, CXL);
resource_size_t cxl_rcrb_to_component(struct device *dev,
resource_size_t rcrb,
enum cxl_rcrb which)
/**
* cxl_count_regblock() - Count instances of a given regblock type.
* @pdev: The CXL PCI device to enumerate.
* @type: Register Block Indicator id
*
* Some regblocks may be repeated. Count how many instances.
*
* Return: count of matching regblocks.
*/
int cxl_count_regblock(struct pci_dev *pdev, enum cxl_regloc_type type)
{
struct cxl_register_map map;
int rc, count = 0;
while (1) {
rc = cxl_find_regblock_instance(pdev, type, &map, count);
if (rc)
return count;
count++;
}
}
EXPORT_SYMBOL_NS_GPL(cxl_count_regblock, CXL);
int cxl_map_pmu_regs(struct pci_dev *pdev, struct cxl_pmu_regs *regs,
struct cxl_register_map *map)
{
struct device *dev = &pdev->dev;
resource_size_t phys_addr;
phys_addr = map->resource;
regs->pmu = devm_cxl_iomap_block(dev, phys_addr, CXL_PMU_REGMAP_SIZE);
if (!regs->pmu)
return -ENOMEM;
return 0;
}
EXPORT_SYMBOL_NS_GPL(cxl_map_pmu_regs, CXL);
static int cxl_map_regblock(struct cxl_register_map *map)
{
struct device *dev = map->dev;
map->base = ioremap(map->resource, map->max_size);
if (!map->base) {
dev_err(dev, "failed to map registers\n");
return -ENOMEM;
}
dev_dbg(dev, "Mapped CXL Memory Device resource %pa\n", &map->resource);
return 0;
}
static void cxl_unmap_regblock(struct cxl_register_map *map)
{
iounmap(map->base);
map->base = NULL;
}
static int cxl_probe_regs(struct cxl_register_map *map)
{
struct cxl_component_reg_map *comp_map;
struct cxl_device_reg_map *dev_map;
struct device *dev = map->dev;
void __iomem *base = map->base;
switch (map->reg_type) {
case CXL_REGLOC_RBI_COMPONENT:
comp_map = &map->component_map;
cxl_probe_component_regs(dev, base, comp_map);
dev_dbg(dev, "Set up component registers\n");
break;
case CXL_REGLOC_RBI_MEMDEV:
dev_map = &map->device_map;
cxl_probe_device_regs(dev, base, dev_map);
if (!dev_map->status.valid || !dev_map->mbox.valid ||
!dev_map->memdev.valid) {
dev_err(dev, "registers not found: %s%s%s\n",
!dev_map->status.valid ? "status " : "",
!dev_map->mbox.valid ? "mbox " : "",
!dev_map->memdev.valid ? "memdev " : "");
return -ENXIO;
}
dev_dbg(dev, "Probing device registers...\n");
break;
default:
break;
}
return 0;
}
int cxl_setup_regs(struct cxl_register_map *map)
{
int rc;
rc = cxl_map_regblock(map);
if (rc)
return rc;
rc = cxl_probe_regs(map);
cxl_unmap_regblock(map);
return rc;
}
EXPORT_SYMBOL_NS_GPL(cxl_setup_regs, CXL);
resource_size_t __rcrb_to_component(struct device *dev, struct cxl_rcrb_info *ri,
enum cxl_rcrb which)
{
resource_size_t component_reg_phys;
resource_size_t rcrb = ri->base;
void __iomem *addr;
u32 bar0, bar1;
u16 cmd;
......@@ -395,4 +533,12 @@ resource_size_t cxl_rcrb_to_component(struct device *dev,
return component_reg_phys;
}
EXPORT_SYMBOL_NS_GPL(cxl_rcrb_to_component, CXL);
resource_size_t cxl_rcd_component_reg_phys(struct device *dev,
struct cxl_dport *dport)
{
if (!dport->rch)
return CXL_RESOURCE_NONE;
return __rcrb_to_component(dev, &dport->rcrb, CXL_RCRB_UPSTREAM);
}
EXPORT_SYMBOL_NS_GPL(cxl_rcd_component_reg_phys, CXL);
This diff is collapsed.
This diff is collapsed.
......@@ -67,6 +67,7 @@ enum cxl_regloc_type {
CXL_REGLOC_RBI_COMPONENT,
CXL_REGLOC_RBI_VIRT,
CXL_REGLOC_RBI_MEMDEV,
CXL_REGLOC_RBI_PMU,
CXL_REGLOC_RBI_TYPES
};
......
......@@ -51,7 +51,6 @@ static int devm_cxl_add_endpoint(struct device *host, struct cxl_memdev *cxlmd,
struct cxl_port *parent_port = parent_dport->port;
struct cxl_dev_state *cxlds = cxlmd->cxlds;
struct cxl_port *endpoint, *iter, *down;
resource_size_t component_reg_phys;
int rc;
/*
......@@ -66,17 +65,8 @@ static int devm_cxl_add_endpoint(struct device *host, struct cxl_memdev *cxlmd,
ep->next = down;
}
/*
* The component registers for an RCD might come from the
* host-bridge RCRB if they are not already mapped via the
* typical register locator mechanism.
*/
if (parent_dport->rch && cxlds->component_reg_phys == CXL_RESOURCE_NONE)
component_reg_phys = cxl_rcrb_to_component(
&cxlmd->dev, parent_dport->rcrb, CXL_RCRB_UPSTREAM);
else
component_reg_phys = cxlds->component_reg_phys;
endpoint = devm_cxl_add_port(host, &cxlmd->dev, component_reg_phys,
endpoint = devm_cxl_add_port(host, &cxlmd->dev,
cxlds->component_reg_phys,
parent_dport);
if (IS_ERR(endpoint))
return PTR_ERR(endpoint);
......@@ -117,6 +107,7 @@ DEFINE_DEBUGFS_ATTRIBUTE(cxl_poison_clear_fops, NULL,
static int cxl_mem_probe(struct device *dev)
{
struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
struct cxl_dev_state *cxlds = cxlmd->cxlds;
struct device *endpoint_parent;
struct cxl_port *parent_port;
......@@ -141,10 +132,10 @@ static int cxl_mem_probe(struct device *dev)
dentry = cxl_debugfs_create_dir(dev_name(dev));
debugfs_create_devm_seqfile(dev, "dpamem", dentry, cxl_mem_dpa_show);
if (test_bit(CXL_POISON_ENABLED_INJECT, cxlds->poison.enabled_cmds))
if (test_bit(CXL_POISON_ENABLED_INJECT, mds->poison.enabled_cmds))
debugfs_create_file("inject_poison", 0200, dentry, cxlmd,
&cxl_poison_inject_fops);
if (test_bit(CXL_POISON_ENABLED_CLEAR, cxlds->poison.enabled_cmds))
if (test_bit(CXL_POISON_ENABLED_CLEAR, mds->poison.enabled_cmds))
debugfs_create_file("clear_poison", 0200, dentry, cxlmd,
&cxl_poison_clear_fops);
......@@ -163,7 +154,7 @@ static int cxl_mem_probe(struct device *dev)
}
if (dport->rch)
endpoint_parent = parent_port->uport;
endpoint_parent = parent_port->uport_dev;
else
endpoint_parent = &parent_port->dev;
......@@ -227,9 +218,12 @@ static umode_t cxl_mem_visible(struct kobject *kobj, struct attribute *a, int n)
{
if (a == &dev_attr_trigger_poison_list.attr) {
struct device *dev = kobj_to_dev(kobj);
struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
struct cxl_memdev_state *mds =
to_cxl_memdev_state(cxlmd->cxlds);
if (!test_bit(CXL_POISON_ENABLED_LIST,
to_cxl_memdev(dev)->cxlds->poison.enabled_cmds))
mds->poison.enabled_cmds))
return 0;
}
return a->mode;
......
This diff is collapsed.
......@@ -15,9 +15,9 @@ extern const struct nvdimm_security_ops *cxl_security_ops;
static __read_mostly DECLARE_BITMAP(exclusive_cmds, CXL_MEM_COMMAND_ID_MAX);
static void clear_exclusive(void *cxlds)
static void clear_exclusive(void *mds)
{
clear_exclusive_cxl_commands(cxlds, exclusive_cmds);
clear_exclusive_cxl_commands(mds, exclusive_cmds);
}
static void unregister_nvdimm(void *nvdimm)
......@@ -65,13 +65,13 @@ static int cxl_nvdimm_probe(struct device *dev)
struct cxl_nvdimm *cxl_nvd = to_cxl_nvdimm(dev);
struct cxl_memdev *cxlmd = cxl_nvd->cxlmd;
struct cxl_nvdimm_bridge *cxl_nvb = cxlmd->cxl_nvb;
struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
unsigned long flags = 0, cmd_mask = 0;
struct cxl_dev_state *cxlds = cxlmd->cxlds;
struct nvdimm *nvdimm;
int rc;
set_exclusive_cxl_commands(cxlds, exclusive_cmds);
rc = devm_add_action_or_reset(dev, clear_exclusive, cxlds);
set_exclusive_cxl_commands(mds, exclusive_cmds);
rc = devm_add_action_or_reset(dev, clear_exclusive, mds);
if (rc)
return rc;
......@@ -100,22 +100,23 @@ static struct cxl_driver cxl_nvdimm_driver = {
},
};
static int cxl_pmem_get_config_size(struct cxl_dev_state *cxlds,
static int cxl_pmem_get_config_size(struct cxl_memdev_state *mds,
struct nd_cmd_get_config_size *cmd,
unsigned int buf_len)
{
if (sizeof(*cmd) > buf_len)
return -EINVAL;
*cmd = (struct nd_cmd_get_config_size) {
.config_size = cxlds->lsa_size,
.max_xfer = cxlds->payload_size - sizeof(struct cxl_mbox_set_lsa),
*cmd = (struct nd_cmd_get_config_size){
.config_size = mds->lsa_size,
.max_xfer =
mds->payload_size - sizeof(struct cxl_mbox_set_lsa),
};
return 0;
}
static int cxl_pmem_get_config_data(struct cxl_dev_state *cxlds,
static int cxl_pmem_get_config_data(struct cxl_memdev_state *mds,
struct nd_cmd_get_config_data_hdr *cmd,
unsigned int buf_len)
{
......@@ -140,13 +141,13 @@ static int cxl_pmem_get_config_data(struct cxl_dev_state *cxlds,
.payload_out = cmd->out_buf,
};
rc = cxl_internal_send_cmd(cxlds, &mbox_cmd);
rc = cxl_internal_send_cmd(mds, &mbox_cmd);
cmd->status = 0;
return rc;
}
static int cxl_pmem_set_config_data(struct cxl_dev_state *cxlds,
static int cxl_pmem_set_config_data(struct cxl_memdev_state *mds,
struct nd_cmd_set_config_hdr *cmd,
unsigned int buf_len)
{
......@@ -176,7 +177,7 @@ static int cxl_pmem_set_config_data(struct cxl_dev_state *cxlds,
.size_in = struct_size(set_lsa, data, cmd->in_length),
};
rc = cxl_internal_send_cmd(cxlds, &mbox_cmd);
rc = cxl_internal_send_cmd(mds, &mbox_cmd);
/*
* Set "firmware" status (4-packed bytes at the end of the input
......@@ -194,18 +195,18 @@ static int cxl_pmem_nvdimm_ctl(struct nvdimm *nvdimm, unsigned int cmd,
struct cxl_nvdimm *cxl_nvd = nvdimm_provider_data(nvdimm);
unsigned long cmd_mask = nvdimm_cmd_mask(nvdimm);
struct cxl_memdev *cxlmd = cxl_nvd->cxlmd;
struct cxl_dev_state *cxlds = cxlmd->cxlds;
struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
if (!test_bit(cmd, &cmd_mask))
return -ENOTTY;
switch (cmd) {
case ND_CMD_GET_CONFIG_SIZE:
return cxl_pmem_get_config_size(cxlds, buf, buf_len);
return cxl_pmem_get_config_size(mds, buf, buf_len);
case ND_CMD_GET_CONFIG_DATA:
return cxl_pmem_get_config_data(cxlds, buf, buf_len);
return cxl_pmem_get_config_data(mds, buf, buf_len);
case ND_CMD_SET_CONFIG_DATA:
return cxl_pmem_set_config_data(cxlds, buf, buf_len);
return cxl_pmem_set_config_data(mds, buf, buf_len);
default:
return -ENOTTY;
}
......
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright(c) 2023 Huawei
* CXL Specification rev 3.0 Setion 8.2.7 (CPMU Register Interface)
*/
#ifndef CXL_PMU_H
#define CXL_PMU_H
#include <linux/device.h>
enum cxl_pmu_type {
CXL_PMU_MEMDEV,
};
#define CXL_PMU_REGMAP_SIZE 0xe00 /* Table 8-32 CXL 3.0 specification */
struct cxl_pmu {
struct device dev;
void __iomem *base;
int assoc_id;
int index;
enum cxl_pmu_type type;
};
#define to_cxl_pmu(dev) container_of(dev, struct cxl_pmu, dev)
struct cxl_pmu_regs;
int devm_cxl_pmu_add(struct device *parent, struct cxl_pmu_regs *regs,
int assoc_id, int idx, enum cxl_pmu_type type);
#endif
......@@ -60,17 +60,13 @@ static int discover_region(struct device *dev, void *root)
static int cxl_switch_port_probe(struct cxl_port *port)
{
struct cxl_hdm *cxlhdm;
int rc, nr_dports;
nr_dports = devm_cxl_port_enumerate_dports(port);
if (nr_dports < 0)
return nr_dports;
int rc;
cxlhdm = devm_cxl_setup_hdm(port, NULL);
rc = devm_cxl_enable_hdm(port, cxlhdm);
if (rc)
rc = devm_cxl_port_enumerate_dports(port);
if (rc < 0)
return rc;
cxlhdm = devm_cxl_setup_hdm(port, NULL);
if (!IS_ERR(cxlhdm))
return devm_cxl_enumerate_decoders(cxlhdm, NULL);
......@@ -79,7 +75,7 @@ static int cxl_switch_port_probe(struct cxl_port *port)
return PTR_ERR(cxlhdm);
}
if (nr_dports == 1) {
if (rc == 1) {
dev_dbg(&port->dev, "Fallback to passthrough decoder\n");
return devm_cxl_add_passthrough_decoder(port);
}
......@@ -91,7 +87,7 @@ static int cxl_switch_port_probe(struct cxl_port *port)
static int cxl_endpoint_port_probe(struct cxl_port *port)
{
struct cxl_endpoint_dvsec_info info = { .port = port };
struct cxl_memdev *cxlmd = to_cxl_memdev(port->uport);
struct cxl_memdev *cxlmd = to_cxl_memdev(port->uport_dev);
struct cxl_dev_state *cxlds = cxlmd->cxlds;
struct cxl_hdm *cxlhdm;
struct cxl_port *root;
......@@ -102,8 +98,11 @@ static int cxl_endpoint_port_probe(struct cxl_port *port)
return rc;
cxlhdm = devm_cxl_setup_hdm(port, &info);
if (IS_ERR(cxlhdm))
if (IS_ERR(cxlhdm)) {
if (PTR_ERR(cxlhdm) == -ENODEV)
dev_err(&port->dev, "HDM decoder registers not found\n");
return PTR_ERR(cxlhdm);
}
/* Cache the data early to ensure is_visible() works */
read_cdat_data(port);
......
......@@ -14,7 +14,7 @@ static unsigned long cxl_pmem_get_security_flags(struct nvdimm *nvdimm,
{
struct cxl_nvdimm *cxl_nvd = nvdimm_provider_data(nvdimm);
struct cxl_memdev *cxlmd = cxl_nvd->cxlmd;
struct cxl_dev_state *cxlds = cxlmd->cxlds;
struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
unsigned long security_flags = 0;
struct cxl_get_security_output {
__le32 flags;
......@@ -29,11 +29,14 @@ static unsigned long cxl_pmem_get_security_flags(struct nvdimm *nvdimm,
.payload_out = &out,
};
rc = cxl_internal_send_cmd(cxlds, &mbox_cmd);
rc = cxl_internal_send_cmd(mds, &mbox_cmd);
if (rc < 0)
return 0;
sec_out = le32_to_cpu(out.flags);
/* cache security state */
mds->security.state = sec_out;
if (ptype == NVDIMM_MASTER) {
if (sec_out & CXL_PMEM_SEC_STATE_MASTER_PASS_SET)
set_bit(NVDIMM_SECURITY_UNLOCKED, &security_flags);
......@@ -67,7 +70,7 @@ static int cxl_pmem_security_change_key(struct nvdimm *nvdimm,
{
struct cxl_nvdimm *cxl_nvd = nvdimm_provider_data(nvdimm);
struct cxl_memdev *cxlmd = cxl_nvd->cxlmd;
struct cxl_dev_state *cxlds = cxlmd->cxlds;
struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
struct cxl_mbox_cmd mbox_cmd;
struct cxl_set_pass set_pass;
......@@ -84,7 +87,7 @@ static int cxl_pmem_security_change_key(struct nvdimm *nvdimm,
.payload_in = &set_pass,
};
return cxl_internal_send_cmd(cxlds, &mbox_cmd);
return cxl_internal_send_cmd(mds, &mbox_cmd);
}
static int __cxl_pmem_security_disable(struct nvdimm *nvdimm,
......@@ -93,7 +96,7 @@ static int __cxl_pmem_security_disable(struct nvdimm *nvdimm,
{
struct cxl_nvdimm *cxl_nvd = nvdimm_provider_data(nvdimm);
struct cxl_memdev *cxlmd = cxl_nvd->cxlmd;
struct cxl_dev_state *cxlds = cxlmd->cxlds;
struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
struct cxl_disable_pass dis_pass;
struct cxl_mbox_cmd mbox_cmd;
......@@ -109,7 +112,7 @@ static int __cxl_pmem_security_disable(struct nvdimm *nvdimm,
.payload_in = &dis_pass,
};
return cxl_internal_send_cmd(cxlds, &mbox_cmd);
return cxl_internal_send_cmd(mds, &mbox_cmd);
}
static int cxl_pmem_security_disable(struct nvdimm *nvdimm,
......@@ -128,12 +131,12 @@ static int cxl_pmem_security_freeze(struct nvdimm *nvdimm)
{
struct cxl_nvdimm *cxl_nvd = nvdimm_provider_data(nvdimm);
struct cxl_memdev *cxlmd = cxl_nvd->cxlmd;
struct cxl_dev_state *cxlds = cxlmd->cxlds;
struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
struct cxl_mbox_cmd mbox_cmd = {
.opcode = CXL_MBOX_OP_FREEZE_SECURITY,
};
return cxl_internal_send_cmd(cxlds, &mbox_cmd);
return cxl_internal_send_cmd(mds, &mbox_cmd);
}
static int cxl_pmem_security_unlock(struct nvdimm *nvdimm,
......@@ -141,7 +144,7 @@ static int cxl_pmem_security_unlock(struct nvdimm *nvdimm,
{
struct cxl_nvdimm *cxl_nvd = nvdimm_provider_data(nvdimm);
struct cxl_memdev *cxlmd = cxl_nvd->cxlmd;
struct cxl_dev_state *cxlds = cxlmd->cxlds;
struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
u8 pass[NVDIMM_PASSPHRASE_LEN];
struct cxl_mbox_cmd mbox_cmd;
int rc;
......@@ -153,7 +156,7 @@ static int cxl_pmem_security_unlock(struct nvdimm *nvdimm,
.payload_in = pass,
};
rc = cxl_internal_send_cmd(cxlds, &mbox_cmd);
rc = cxl_internal_send_cmd(mds, &mbox_cmd);
if (rc < 0)
return rc;
......@@ -166,7 +169,7 @@ static int cxl_pmem_security_passphrase_erase(struct nvdimm *nvdimm,
{
struct cxl_nvdimm *cxl_nvd = nvdimm_provider_data(nvdimm);
struct cxl_memdev *cxlmd = cxl_nvd->cxlmd;
struct cxl_dev_state *cxlds = cxlmd->cxlds;
struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
struct cxl_mbox_cmd mbox_cmd;
struct cxl_pass_erase erase;
int rc;
......@@ -182,7 +185,7 @@ static int cxl_pmem_security_passphrase_erase(struct nvdimm *nvdimm,
.payload_in = &erase,
};
rc = cxl_internal_send_cmd(cxlds, &mbox_cmd);
rc = cxl_internal_send_cmd(mds, &mbox_cmd);
if (rc < 0)
return rc;
......
......@@ -221,4 +221,17 @@ source "drivers/perf/arm_cspmu/Kconfig"
source "drivers/perf/amlogic/Kconfig"
config CXL_PMU
tristate "CXL Performance Monitoring Unit"
depends on CXL_BUS
help
Support performance monitoring as defined in CXL rev 3.0
section 13.2: Performance Monitoring. CXL components may have
one or more CXL Performance Monitoring Units (CPMUs).
Say 'y/m' to enable a driver that will attach to performance
monitoring units and provide standard perf based interfaces.
If unsure say 'm'.
endmenu
......@@ -25,3 +25,4 @@ obj-$(CONFIG_APPLE_M1_CPU_PMU) += apple_m1_cpu_pmu.o
obj-$(CONFIG_ALIBABA_UNCORE_DRW_PMU) += alibaba_uncore_drw_pmu.o
obj-$(CONFIG_ARM_CORESIGHT_PMU_ARCH_SYSTEM_PMU) += arm_cspmu/
obj-$(CONFIG_MESON_DDR_PMU) += amlogic/
obj-$(CONFIG_CXL_PMU) += cxl_pmu.o
This diff is collapsed.
......@@ -305,6 +305,7 @@ struct pmu {
struct module *module;
struct device *dev;
struct device *parent;
const struct attribute_group **attr_groups;
const struct attribute_group **attr_update;
const char *name;
......
This diff is collapsed.
......@@ -11391,6 +11391,7 @@ static int pmu_dev_alloc(struct pmu *pmu)
dev_set_drvdata(pmu->dev, pmu);
pmu->dev->bus = &pmu_bus;
pmu->dev->parent = pmu->parent;
pmu->dev->release = pmu_dev_release;
ret = dev_set_name(pmu->dev, "%s", pmu->name);
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment