From: Arpit Kumar <arpit1.kumar@samsung.com>
To: Jonathan Cameron <jonathan.cameron@huawei.com>
Cc: qemu-devel@nongnu.org, gost.dev@samsung.com,
linux-cxl@vger.kernel.org, dave@stgolabs.net,
vishak.g@samsung.com, krish.reddy@samsung.com,
a.manzanares@samsung.com, alok.rathore@samsung.com,
arpit.sysdev@gmail.com, cpgs@samsung.com
Subject: Re: [PATCH v4 0/2] FM-API Physical Switch Command Set Support
Date: Fri, 30 Jan 2026 17:33:50 +0530 [thread overview]
Message-ID: <20260130120350.n7ngrvla5mfkwllo@test-PowerEdge-R740xd> (raw)
In-Reply-To: <20260127152350.00006447@huawei.com>
[-- Attachment #1: Type: text/plain, Size: 26565 bytes --]
On 27/01/26 03:23PM, Jonathan Cameron wrote:
>On Tue, 16 Sep 2025 13:37:34 +0530
>Arpit Kumar <arpit1.kumar@samsung.com> wrote:
>
>> This patch series refactor existing support for Identify Switch Device
>> and Get Physical Port State by utilizing physical ports (USP & DSP)
>> information stored during enumeration.
>
>Hi Arpit.
>
>So I came back to this series to try and tweak it into being in a state
>for upstreaming. I made some fairly heavy changes (to push the
>data down to the individual ports where to me it more logically sits)
>but it became apparent to me during this that there is a fundamental
>problem. This stuff isn't static because it needs to reflect port
>retraining and hotplug events etc. If we want to store cached
>data we are going to have to deal with updating it from a significant
>number of different places. The perst one you have covered in patch 2
>is an example but there are others.
>
>Now we definitely can do the generation in a neater way, but I've
>come to the conclusion that caching it really doesn't make sense.
>
>Maybe we can do that for the USP, but definitely not DSPs.
>
>Given I did a bunch of refactoring I might as well share it even though
>I'm currently thinking it might not be worth doing:
>
>From 40a8a36f22a6eb053c965231a698c8054d417049 Mon Sep 17 00:00:00 2001
>From: Arpit Kumar <arpit1.kumar@samsung.com>
>Date: Tue, 16 Sep 2025 13:37:35 +0530
>Subject: [PATCH] hw/cxl: Refactored Identify Switch Device & Get Physical Port
> State
>
>Store the physical port info for the USP on reset but refresh
>it on demand for the DSPs as they are hotplug capable.
>
>Enables common data to be used for both of:
>* Identify Switch Device (Opcode 5100h)
>* Get Physical Port State (Opcode 5101h) physical switch FM-API command set.
>
>Signed-off-by: Arpit Kumar <arpit1.kumar@samsung.com>
>Co-developed: Jonathan Cameron <Jonathan.Cameron@huawei.com>
>Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
>
>---
>Added a Co-dev as I have modified this a lot from Arpit's v4.
>v5:
>- Create a new header to avoid importing all of cxl_device.h into
>the upstream port emulation.
>- Move the call that caches state to the cxl_upstream_port reset
> to ensure downstream ports are in place before it is called.
> Also will make it available from whatever CCI. If we ever support
> multiple VCS switches this will need to move an appropriate structure
> representing whole switch information. With only one USP that is
> a reasonable place to put full switch info.
>- Downstream ports have more dynamic state (as devices can be
> hotplugged below them). As such, only fill in the data at time
> of request.
Hi Jonathan,
Thanks for the update! The changes look good to me. I do concede that
it requires more dynamic approach. This would help in future command
support like multiple VCS, MLD etc. I intially refrained from making changes
to individual ports but it does logically fits there and would
optimize the command support.
>---
> include/hw/cxl/cxl_port.h | 87 ++++++++
> include/hw/pci-bridge/cxl_downstream_port.h | 11 +
> include/hw/pci-bridge/cxl_upstream_port.h | 5 +
> hw/cxl/cxl-mailbox-utils.c | 210 ++++++++++----------
> hw/pci-bridge/cxl_downstream.c | 27 +++
> hw/pci-bridge/cxl_upstream.c | 5 +
> 6 files changed, 245 insertions(+), 100 deletions(-)
> create mode 100644 include/hw/cxl/cxl_port.h
> create mode 100644 include/hw/pci-bridge/cxl_downstream_port.h
>
>diff --git a/include/hw/cxl/cxl_port.h b/include/hw/cxl/cxl_port.h
>new file mode 100644
>index 000000000000..15960f8b5778
>--- /dev/null
>+++ b/include/hw/cxl/cxl_port.h
>@@ -0,0 +1,87 @@
>+/* SPDX-License-Identifier: GPL-2.0-or-later */
>+
>+#ifndef CXL_PORT_H
>+#define CXL_PORT_H
>+
>+#include "hw/pci/pci.h"
>+
>+/* Port related commands */
>+#define CXL_MAX_PHY_PORTS 256
>+
>+/* CXL r3.2 Table 7-19: Get Physical Port State Port Information Block Format */
>+#define CXL_PORT_CONFIG_STATE_DISABLED 0x0
>+#define CXL_PORT_CONFIG_STATE_BIND_IN_PROGRESS 0x1
>+#define CXL_PORT_CONFIG_STATE_UNBIND_IN_PROGRESS 0x2
>+#define CXL_PORT_CONFIG_STATE_DSP 0x3
>+#define CXL_PORT_CONFIG_STATE_USP 0x4
>+#define CXL_PORT_CONFIG_STATE_FABRIC_PORT 0x5
>+#define CXL_PORT_CONFIG_STATE_INVALID_PORT_ID 0xF
>+
>+#define CXL_PORT_CONNECTED_DEV_MODE_NOT_CXL_OR_DISCONN 0x00
>+#define CXL_PORT_CONNECTED_DEV_MODE_RCD 0x01
>+#define CXL_PORT_CONNECTED_DEV_MODE_68B_VH 0x02
>+#define CXL_PORT_CONNECTED_DEV_MODE_256B 0x03
>+#define CXL_PORT_CONNECTED_DEV_MODE_LO_256B 0x04
>+#define CXL_PORT_CONNECTED_DEV_MODE_PBR 0x05
>+
>+#define CXL_PORT_CONNECTED_DEV_TYPE_NONE 0x00
>+#define CXL_PORT_CONNECTED_DEV_TYPE_PCIE 0x01
>+#define CXL_PORT_CONNECTED_DEV_TYPE_1 0x02
>+#define CXL_PORT_CONNECTED_DEV_TYPE_2_OR_HBR_SWITCH 0x03
>+#define CXL_PORT_CONNECTED_DEV_TYPE_3_SLD 0x04
>+#define CXL_PORT_CONNECTED_DEV_TYPE_3_MLD 0x05
>+#define CXL_PORT_CONNECTED_DEV_PBR_COMPONENT 0x06
>+
>+#define CXL_PORT_SUPPORTS_RCD BIT(0)
>+#define CXL_PORT_SUPPORTS_68B_VH BIT(1)
>+#define CXL_PORT_SUPPORTS_256B BIT(2)
>+#define CXL_PORT_SUPPORTS_LO_256B BIT(3)
>+#define CXL_PORT_SUPPORTS_PBR BIT(4)
>+
>+#define CXL_PORT_LTSSM_DETECT 0x00
>+#define CXL_PORT_LTSSM_POLLING 0x01
>+#define CXL_PORT_LTSSM_CONFIGURATION 0x02
>+#define CXL_PORT_LTSSM_RECOVERY 0x03
>+#define CXL_PORT_LTSSM_L0 0x04
>+#define CXL_PORT_LTSSM_L0S 0x05
>+#define CXL_PORT_LTSSM_L1 0x06
>+#define CXL_PORT_LTSSM_L2 0x07
>+#define CXL_PORT_LTSSM_DISABLED 0x08
>+#define CXL_PORT_LTSSM_LOOPBACK 0x09
>+#define CXL_PORT_LTSSM_HOT_RESET 0x0A
>+
>+#define CXL_PORT_LINK_STATE_FLAG_LANE_REVERSED BIT(0)
>+#define CXL_PORT_LINK_STATE_FLAG_PERST_ASSERTED BIT(1)
>+#define CXL_PORT_LINK_STATE_FLAG_PRSNT BIT(2)
>+#define CXL_PORT_LINK_STATE_FLAG_POWER_OFF BIT(3)
>+
>+typedef struct CXLPhyPortInfo {
>+ uint8_t port_id;
>+ uint8_t current_port_config_state;
>+ uint8_t connected_device_mode;
>+ uint8_t rsv1;
>+ uint8_t connected_device_type;
>+ uint8_t supported_cxl_modes;
>+ uint8_t max_link_width;
>+ uint8_t negotiated_link_width;
>+ uint8_t supported_link_speeds_vector;
>+ uint8_t max_link_speed;
>+ uint8_t current_link_speed;
>+ uint8_t ltssm_state;
>+ uint8_t first_negotiated_lane_num;
>+ uint16_t link_state_flags;
>+ uint8_t supported_ld_count;
>+} QEMU_PACKED CXLPhyPortInfo;
>+
>+void cxl_init_physical_port_info(PCIDevice *port_dev, CXLPhyPortInfo *info,
>+ uint8_t pn,
>+ uint8_t current_port_config_state,
>+ uint8_t connected_device_type,
>+ uint8_t supported_ld_count);
>+
>+/* Common data for CXL USP and DSP */
>+typedef struct CXLSwitchPortData {
>+ CXLPhyPortInfo info;
>+} CXLSwitchPortData;
>+
>+#endif /* CXL_PORT_H */
>diff --git a/include/hw/pci-bridge/cxl_downstream_port.h b/include/hw/pci-bridge/cxl_downstream_port.h
>new file mode 100644
>index 000000000000..c3f058365283
>--- /dev/null
>+++ b/include/hw/pci-bridge/cxl_downstream_port.h
>@@ -0,0 +1,11 @@
>+#ifndef CXL_DOWNSTREAM_PORT_H
>+#define CXL_DOWNSTREAM_PORT_H
>+#include "hw/pci/pcie.h"
>+#include "hw/pci/pcie_port.h"
>+#include "hw/cxl/cxl.h"
>+#include "include/hw/cxl/cxl_port.h"
>+
>+typedef struct CXLDownstreamPort CXLDownstreamPort;
>+CXLPhyPortInfo *cxl_dsp_get_physical_port_info(CXLDownstreamPort *dsp);
>+
>+#endif
>diff --git a/include/hw/pci-bridge/cxl_upstream_port.h b/include/hw/pci-bridge/cxl_upstream_port.h
>index e3d6a27acc86..fe367fb66f0c 100644
>--- a/include/hw/pci-bridge/cxl_upstream_port.h
>+++ b/include/hw/pci-bridge/cxl_upstream_port.h
>@@ -4,6 +4,7 @@
> #include "hw/pci/pcie.h"
> #include "hw/pci/pcie_port.h"
> #include "hw/cxl/cxl.h"
>+#include "include/hw/cxl/cxl_port.h"
>
> typedef struct CXLUpstreamPort {
> /*< private >*/
>@@ -19,6 +20,10 @@ typedef struct CXLUpstreamPort {
>
> DOECap doe_cdat;
> uint64_t sn;
>+
>+ CXLSwitchPortData swport_data;
> } CXLUpstreamPort;
>
>+void cxl_set_physical_port_info(CXLUpstreamPort *cxl_usp);
>+
> #endif /* CXL_SUP_H */
>diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c
>index 2f449980cdc0..f7aa56b0d385 100644
>--- a/hw/cxl/cxl-mailbox-utils.c
>+++ b/hw/cxl/cxl-mailbox-utils.c
>@@ -13,9 +13,11 @@
> #include "hw/pci/msi.h"
> #include "hw/pci/msix.h"
> #include "hw/cxl/cxl.h"
>+#include "hw/cxl/cxl_port.h"
> #include "hw/cxl/cxl_events.h"
> #include "hw/cxl/cxl_mailbox.h"
> #include "hw/pci/pci.h"
>+#include "hw/pci-bridge/cxl_downstream_port.h"
> #include "hw/pci-bridge/cxl_upstream_port.h"
> #include "qemu/cutils.h"
> #include "qemu/host-utils.h"
>@@ -488,13 +490,21 @@ static CXLRetCode cmd_set_response_msg_limit(const struct cxl_cmd *cmd,
> return CXL_MBOX_SUCCESS;
> }
>
>-static void cxl_set_dsp_active_bm(PCIBus *b, PCIDevice *d,
>- void *private)
>+static uint8_t cxl_num_switch_ports(CXLUpstreamPort *usp)
> {
>- uint8_t *bm = private;
>- if (object_dynamic_cast(OBJECT(d), TYPE_CXL_DSP)) {
>- uint8_t port = PCIE_PORT(d)->port;
>- bm[port / 8] |= 1 << (port % 8);
>+ PCIBus *bus = &PCI_BRIDGE(usp)->sec_bus;
>+
>+ return pcie_count_ds_ports(bus) + 1;
>+}
>+
>+static void cxl_mark_dsp_active(PCIBus *bus, PCIDevice *dev, void *opaque)
>+{
>+ uint8_t *bitmask = opaque;
>+
>+ if (object_dynamic_cast(OBJECT(dev), TYPE_CXL_DSP)) {
>+ uint8_t pn = PCIE_PORT(dev)->port;
>+
>+ bitmask[pn / 8] |= (1 << (pn % 8));
> }
> }
>
>@@ -506,9 +516,9 @@ static CXLRetCode cmd_identify_switch_device(const struct cxl_cmd *cmd,
> size_t *len_out,
> CXLCCI *cci)
> {
>- PCIEPort *usp = PCIE_PORT(cci->d);
>- PCIBus *bus = &PCI_BRIDGE(cci->d)->sec_bus;
>- int num_phys_ports = pcie_count_ds_ports(bus);
>+ CXLUpstreamPort *pp = CXL_USP(cci->d);
>+ uint8_t num_phys_ports = cxl_num_switch_ports(pp);
>+ uint8_t pn;
>
> struct cxl_fmapi_ident_switch_dev_resp_pl {
> uint8_t ingress_port_id;
>@@ -525,11 +535,11 @@ static CXLRetCode cmd_identify_switch_device(const struct cxl_cmd *cmd,
>
> out = (struct cxl_fmapi_ident_switch_dev_resp_pl *)payload_out;
> *out = (struct cxl_fmapi_ident_switch_dev_resp_pl) {
>- .num_physical_ports = num_phys_ports + 1, /* 1 USP */
>+ .num_physical_ports = num_phys_ports,
> .num_vcss = 1, /* Not yet support multiple VCS - potentially tricky */
> .active_vcs_bitmask[0] = 0x1,
>- .total_vppbs = num_phys_ports + 1,
>- .bound_vppbs = num_phys_ports + 1,
>+ .total_vppbs = num_phys_ports,
>+ .bound_vppbs = num_phys_ports,
> .num_hdm_decoders_per_usp = 4,
> };
>
>@@ -541,16 +551,46 @@ static CXLRetCode cmd_identify_switch_device(const struct cxl_cmd *cmd,
> out->ingress_port_id = 0;
> }
>
>- pci_for_each_device_under_bus(bus, cxl_set_dsp_active_bm,
>+ pn = PCIE_PORT(pp)->port;
>+ out->active_port_bitmask[pn / 8] |= (1 << pn % 8);
>+ pci_for_each_device_under_bus(&PCI_BRIDGE(pp)->sec_bus,
>+ cxl_mark_dsp_active,
> out->active_port_bitmask);
>- out->active_port_bitmask[usp->port / 8] |= (1 << usp->port % 8);
>
> *len_out = sizeof(*out);
>
> return CXL_MBOX_SUCCESS;
> }
>
>-/* CXL r3.1 Section 7.6.7.1.2: Get Physical Port State (Opcode 5101h) */
>+static CXLDownstreamPort *cxl_find_dsp_on_bus(PCIBus *bus, uint8_t pn)
>+{
>+
>+ PCIDevice *port_dev = pcie_find_port_by_pn(bus, pn);
>+
>+ if (object_dynamic_cast(OBJECT(port_dev), TYPE_CXL_DSP)) {
>+ return CXL_DSP(port_dev);
>+ }
>+
>+ return NULL;
>+}
>+
>+static CXLPhyPortInfo *cxl_get_phyport_info(CXLUpstreamPort *usp, uint8_t pn)
>+{
>+ CXLDownstreamPort *dsp;
>+
>+ if (usp->swport_data.info.port_id == pn) {
>+ return &usp->swport_data.info;
>+ }
>+
>+ dsp = cxl_find_dsp_on_bus(&PCI_BRIDGE(usp)->sec_bus, pn);
>+ if (dsp) {
>+ return cxl_dsp_get_physical_port_info(dsp);
>+ }
>+
>+ return NULL;
>+}
>+
>+/* CXL r3.2 Section 7.6.7.1.2: Get Physical Port State (Opcode 5101h) */
> static CXLRetCode cmd_get_physical_port_state(const struct cxl_cmd *cmd,
> uint8_t *payload_in,
> size_t len_in,
>@@ -558,44 +598,22 @@ static CXLRetCode cmd_get_physical_port_state(const struct cxl_cmd *cmd,
> size_t *len_out,
> CXLCCI *cci)
> {
>- /* CXL r3.1 Table 7-17: Get Physical Port State Request Payload */
>+ CXLUpstreamPort *pp = CXL_USP(cci->d);
>+ size_t pl_size;
>+ int i;
>+
>+ /* CXL r3.2 Table 7-17: Get Physical Port State Request Payload */
> struct cxl_fmapi_get_phys_port_state_req_pl {
> uint8_t num_ports;
> uint8_t ports[];
> } QEMU_PACKED *in;
>
>- /*
>- * CXL r3.1 Table 7-19: Get Physical Port State Port Information Block
>- * Format
>- */
>- struct cxl_fmapi_port_state_info_block {
>- uint8_t port_id;
>- uint8_t config_state;
>- uint8_t connected_device_cxl_version;
>- uint8_t rsv1;
>- uint8_t connected_device_type;
>- uint8_t port_cxl_version_bitmask;
>- uint8_t max_link_width;
>- uint8_t negotiated_link_width;
>- uint8_t supported_link_speeds_vector;
>- uint8_t max_link_speed;
>- uint8_t current_link_speed;
>- uint8_t ltssm_state;
>- uint8_t first_lane_num;
>- uint16_t link_state;
>- uint8_t supported_ld_count;
>- } QEMU_PACKED;
>-
>- /* CXL r3.1 Table 7-18: Get Physical Port State Response Payload */
>+ /* CXL r3.2 Table 7-18: Get Physical Port State Response Payload */
> struct cxl_fmapi_get_phys_port_state_resp_pl {
> uint8_t num_ports;
> uint8_t rsv1[3];
>- struct cxl_fmapi_port_state_info_block ports[];
>+ CXLPhyPortInfo ports[];
> } QEMU_PACKED *out;
>- PCIBus *bus = &PCI_BRIDGE(cci->d)->sec_bus;
>- PCIEPort *usp = PCIE_PORT(cci->d);
>- size_t pl_size;
>- int i;
>
> in = (struct cxl_fmapi_get_phys_port_state_req_pl *)payload_in;
> out = (struct cxl_fmapi_get_phys_port_state_resp_pl *)payload_out;
>@@ -608,69 +626,22 @@ static CXLRetCode cmd_get_physical_port_state(const struct cxl_cmd *cmd,
> return CXL_MBOX_INVALID_INPUT;
> }
>
>- /* For success there should be a match for each requested */
>- out->num_ports = in->num_ports;
>+ if (in->num_ports > cxl_num_switch_ports(pp)) {
>+ return CXL_MBOX_INVALID_INPUT;
>+ }
>
> for (i = 0; i < in->num_ports; i++) {
>- struct cxl_fmapi_port_state_info_block *port;
>- /* First try to match on downstream port */
>- PCIDevice *port_dev;
>- uint16_t lnkcap, lnkcap2, lnksta;
>-
>- port = &out->ports[i];
>-
>- port_dev = pcie_find_port_by_pn(bus, in->ports[i]);
>- if (port_dev) { /* DSP */
>- PCIDevice *ds_dev = pci_bridge_get_sec_bus(PCI_BRIDGE(port_dev))
>- ->devices[0];
>- port->config_state = 3;
>- if (ds_dev) {
>- if (object_dynamic_cast(OBJECT(ds_dev), TYPE_CXL_TYPE3)) {
>- port->connected_device_type = 5; /* Assume MLD for now */
>- } else {
>- port->connected_device_type = 1;
>- }
>- } else {
>- port->connected_device_type = 0;
>- }
>- port->supported_ld_count = 3;
>- } else if (usp->port == in->ports[i]) { /* USP */
>- port_dev = PCI_DEVICE(usp);
>- port->config_state = 4;
>- port->connected_device_type = 0;
>- } else {
>+ int pn = in->ports[i];
>+ CXLPhyPortInfo *info = cxl_get_phyport_info(pp, pn);
>+
>+ if (!info) {
> return CXL_MBOX_INVALID_INPUT;
> }
>
>- port->port_id = in->ports[i];
>- /* Information on status of this port in lnksta, lnkcap */
>- if (!port_dev->exp.exp_cap) {
>- return CXL_MBOX_INTERNAL_ERROR;
>- }
>- lnksta = port_dev->config_read(port_dev,
>- port_dev->exp.exp_cap + PCI_EXP_LNKSTA,
>- sizeof(lnksta));
>- lnkcap = port_dev->config_read(port_dev,
>- port_dev->exp.exp_cap + PCI_EXP_LNKCAP,
>- sizeof(lnkcap));
>- lnkcap2 = port_dev->config_read(port_dev,
>- port_dev->exp.exp_cap + PCI_EXP_LNKCAP2,
>- sizeof(lnkcap2));
>-
>- port->max_link_width = (lnkcap & PCI_EXP_LNKCAP_MLW) >> 4;
>- port->negotiated_link_width = (lnksta & PCI_EXP_LNKSTA_NLW) >> 4;
>- /* No definition for SLS field in linux/pci_regs.h */
>- port->supported_link_speeds_vector = (lnkcap2 & 0xFE) >> 1;
>- port->max_link_speed = lnkcap & PCI_EXP_LNKCAP_SLS;
>- port->current_link_speed = lnksta & PCI_EXP_LNKSTA_CLS;
>- /* TODO: Track down if we can get the rest of the info */
>- port->ltssm_state = 0x7;
>- port->first_lane_num = 0;
>- port->link_state = 0;
>- port->port_cxl_version_bitmask = 0x2;
>- port->connected_device_cxl_version = 0x2;
>+ memcpy(&out->ports[i], info, sizeof(*info));
> }
>
>+ out->num_ports = in->num_ports;
> pl_size = sizeof(*out) + sizeof(*out->ports) * in->num_ports;
> *len_out = pl_size;
>
>@@ -4633,6 +4604,45 @@ void cxl_add_cci_commands(CXLCCI *cci, const struct cxl_cmd (*cxl_cmd_set)[256],
> cxl_rebuild_cel(cci);
> }
>
>+void cxl_init_physical_port_info(PCIDevice *port_dev, CXLPhyPortInfo *info,
>+ uint8_t pn,
>+ uint8_t current_port_config_state,
>+ uint8_t connected_device_type,
>+ uint8_t supported_ld_count)
>+{
>+ uint16_t lnkcap, lnkcap2, lnksta;
>+
>+ if (!port_dev->exp.exp_cap) {
>+ return;
>+ }
>+ lnksta = port_dev->config_read(port_dev,
>+ port_dev->exp.exp_cap + PCI_EXP_LNKSTA,
>+ sizeof(lnksta));
>+ lnkcap = port_dev->config_read(port_dev,
>+ port_dev->exp.exp_cap + PCI_EXP_LNKCAP,
>+ sizeof(lnkcap));
>+ lnkcap2 = port_dev->config_read(port_dev,
>+ port_dev->exp.exp_cap + PCI_EXP_LNKCAP2,
>+ sizeof(lnkcap2));
>+
>+ *info = (CXLPhyPortInfo) {
>+ .port_id = pn,
>+ .current_port_config_state = current_port_config_state,
>+ .connected_device_mode = CXL_PORT_CONNECTED_DEV_MODE_256B,
>+ .connected_device_type = connected_device_type,
>+ .supported_cxl_modes = CXL_PORT_SUPPORTS_256B,
>+ .max_link_width = (lnkcap & PCI_EXP_LNKCAP_MLW) >> 4,
>+ .negotiated_link_width = (lnksta & PCI_EXP_LNKSTA_NLW) >> 4,
>+ .supported_link_speeds_vector = (lnkcap2 & 0xFE) >> 1,
>+ .max_link_speed = lnkcap & PCI_EXP_LNKCAP_SLS,
>+ .current_link_speed = lnksta & PCI_EXP_LNKSTA_CLS,
>+ .ltssm_state = CXL_PORT_LTSSM_L2,
>+ .first_negotiated_lane_num = 0,
>+ .link_state_flags = 0,
>+ .supported_ld_count = supported_ld_count,
>+ };
>+}
>+
> void cxl_initialize_mailbox_swcci(CXLCCI *cci, DeviceState *intf,
> DeviceState *d, size_t payload_max)
> {
>diff --git a/hw/pci-bridge/cxl_downstream.c b/hw/pci-bridge/cxl_downstream.c
>index 320818a8f1ce..8611f54ebd0c 100644
>--- a/hw/pci-bridge/cxl_downstream.c
>+++ b/hw/pci-bridge/cxl_downstream.c
>@@ -13,6 +13,7 @@
> #include "hw/pci/msi.h"
> #include "hw/pci/pcie.h"
> #include "hw/pci/pcie_port.h"
>+#include "hw/pci-bridge/cxl_downstream_port.h"
> #include "hw/core/qdev-properties.h"
> #include "hw/core/qdev-properties-system.h"
> #include "hw/cxl/cxl.h"
>@@ -24,6 +25,7 @@ typedef struct CXLDownstreamPort {
>
> /*< public >*/
> CXLComponentState cxl_cstate;
>+ CXLSwitchPortData swport_data;
> } CXLDownstreamPort;
>
> #define CXL_DOWNSTREAM_PORT_MSI_OFFSET 0x70
>@@ -81,6 +83,31 @@ static void cxl_dsp_config_write(PCIDevice *d, uint32_t address,
> cxl_dsp_dvsec_write_config(d, address, val, len);
> }
>
>+CXLPhyPortInfo *cxl_dsp_get_physical_port_info(CXLDownstreamPort *dsp)
>+{
>+ CXLPhyPortInfo *info = &dsp->swport_data.info;
>+ uint8_t connected_device_type;
>+ PCIDevice *port_dev = PCI_DEVICE(dsp);
>+ PCIDevice *ds_dev = pci_bridge_get_sec_bus(PCI_BRIDGE(dsp))
>+ ->devices[0];
>+
>+ if (ds_dev) {
>+ if (object_dynamic_cast(OBJECT(ds_dev), TYPE_CXL_TYPE3)) {
>+ connected_device_type = CXL_PORT_CONNECTED_DEV_TYPE_3_SLD;
>+ } else {
>+ connected_device_type = CXL_PORT_CONNECTED_DEV_TYPE_PCIE;
>+ }
>+ } else {
>+ connected_device_type = CXL_PORT_CONNECTED_DEV_TYPE_NONE;
>+ }
>+
>+ cxl_init_physical_port_info(port_dev, info, PCIE_PORT(dsp)->port,
>+ CXL_PORT_CONFIG_STATE_DSP,
>+ connected_device_type, 3);
>+
>+ return info;
>+}
>+
> static void cxl_dsp_reset(DeviceState *qdev)
> {
> PCIDevice *d = PCI_DEVICE(qdev);
>diff --git a/hw/pci-bridge/cxl_upstream.c b/hw/pci-bridge/cxl_upstream.c
>index fb8d19539c9f..64f31110ff2c 100644
>--- a/hw/pci-bridge/cxl_upstream.c
>+++ b/hw/pci-bridge/cxl_upstream.c
>@@ -103,6 +103,11 @@ static void cxl_usp_reset(DeviceState *qdev)
> pcie_cap_deverr_reset(d);
> pcie_cap_fill_link_ep_usp(d, usp->width, usp->speed, usp->flitmode);
> latch_registers(usp);
>+
>+ /* Assume that the info about the upstream link only changes on reset. */
>+ cxl_init_physical_port_info(PCI_DEVICE(usp), &usp->swport_data.info,
>+ PCIE_PORT(usp)->port, CXL_PORT_CONFIG_STATE_USP,
>+ CXL_PORT_CONNECTED_DEV_TYPE_NONE, 0);
> }
>
> static void build_dvsecs(CXLUpstreamPort *usp)
>--
>2.48.1
>
>
>
>>
>> Additionally, it introduces new support for Physical Port Control
>> of Physical Switch Command Set as per CXL spec r3.2 Section 7.6.7.1.3.
>> It primarily constitutes two logic:
>> -Assert-Deassert PERST: Assert PERST involves physical port to be in
>> hold reset phase for minimum 100ms. No other physical port control
>> request are entertained until Deassert PERST command for the given
>> port is issued.
>> -Reset PPB: cold reset of physical port (completing enter->hold->exit
>> phases).
>>
>> Tested using libcxl-mi interface[1]:
>> All active ports and all opcodes per active port is tested. Also, tested
>> against possible edge cases manually since the interface currently dosen't
>> support run time input.
>>
>> Typical Qemu topology
>> (1 USP + 3 DSP's in a switch with 2 CXLType3 devices connected to the 2 DSP's):
>> FM="-object memory-backend-file,id=cxl-mem1,mem-path=$TMP_DIR/t3_cxl1.raw,size=256M \
>> -object memory-backend-file,id=cxl-lsa1,mem-path=$TMP_DIR/t3_lsa1.raw,size=1M \
>> -object memory-backend-file,id=cxl-mem2,mem-path=$TMP_DIR/t3_cxl2.raw,size=512M \
>> -object memory-backend-file,id=cxl-lsa2,mem-path=$TMP_DIR/t3_lsa2.raw,size=512M \
>> -device pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1,hdm_for_passthrough=true \
>> -device cxl-rp,port=0,bus=cxl.1,id=cxl_rp_port0,chassis=0,slot=2 \
>> -device cxl-upstream,port=2,sn=1234,bus=cxl_rp_port0,id=us0,addr=0.0,multifunction=on, \
>> -device cxl-switch-mailbox-cci,bus=cxl_rp_port0,addr=0.1,target=us0 \
>> -device cxl-downstream,port=0,bus=us0,id=swport0,chassis=0,slot=4 \
>> -device cxl-downstream,port=1,bus=us0,id=swport1,chassis=0,slot=5 \
>> -device cxl-downstream,port=3,bus=us0,id=swport2,chassis=0,slot=6 \
>> -device cxl-type3,bus=swport0,memdev=cxl-mem1,id=cxl-pmem1,lsa=cxl-lsa1,sn=3 \
>> -device cxl-type3,bus=swport2,memdev=cxl-mem2,id=cxl-pmem2,lsa=cxl-lsa2,sn=4 \
>> -machine cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=4G,cxl-fmw.0.interleave-granularity=1k \
>> -device i2c_mctp_cxl,bus=aspeed.i2c.bus.0,address=4,target=us0 \
>> -device i2c_mctp_cxl,bus=aspeed.i2c.bus.0,address=5,target=cxl-pmem1 \
>> -device i2c_mctp_cxl,bus=aspeed.i2c.bus.0,address=6,target=cxl-pmem2 \
>> -device virtio-rng-pci,bus=swport1"
>>
>> Tested multiple Qemu topologies:
>> -without any devices connected to downstream ports.
>> -with virtio-rng-pci devices connected to downstream ports.
>> -with CXLType3 devices connected to downstream ports.
>> -with different unique values of ports (both upstream and downstream).
>>
>> Changes from v3 (https://lore.kernel.org/qemu-devel/20250909160316.00000190@huawei.com/T/):
>> -Namespaced the defines with cleaner prefix for Get Physical Port State
>> Port Information Block members.
>> -switch CCI implementation instead of switch FM interface as per
>> Jonathan's review comments, hence moved perst members initializations
>> from: cxl_initialize_usp_mctpcci() -> cxl_initialize_mailbox_swcci().
>>
>> [1] https://github.com/computexpresslink/libcxlmi/commit/35fe68bd9a31469f832a87694d7b18d2d50be5b8
>>
>> The patches are generated against the Johnathan's tree
>> https://gitlab.com/jic23/qemu.git and branch cxl-2025-07-03.
>>
>> Signed-off-by: Arpit Kumar <arpit1.kumar@samsung.com>
>>
>> Arpit Kumar (2):
>> hw/cxl: Refactored Identify Switch Device & Get Physical Port State
>> hw/cxl: Add Physical Port Control (Opcode 5102h)
>>
>> hw/cxl/cxl-mailbox-utils.c | 368 +++++++++++++++-------
>> include/hw/cxl/cxl_device.h | 76 +++++
>> include/hw/cxl/cxl_mailbox.h | 1 +
>> include/hw/pci-bridge/cxl_upstream_port.h | 9 +
>> 4 files changed, 348 insertions(+), 106 deletions(-)
>>
>
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
next prev parent reply other threads:[~2026-01-30 12:04 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <CGME20250916080757epcas5p2db403c9648f7818b22413d90acb4fdd4@epcas5p2.samsung.com>
2025-09-16 8:07 ` [PATCH v4 0/2] FM-API Physical Switch Command Set Support Arpit Kumar
2025-09-16 8:07 ` [PATCH v4 1/2] hw/cxl: Refactored Identify Switch Device & Get Physical Port State Arpit Kumar
2025-09-17 15:55 ` Jonathan Cameron
2025-09-19 11:03 ` Arpit Kumar
2025-09-30 14:26 ` Jonathan Cameron
2025-09-16 8:07 ` [PATCH v4 2/2] hw/cxl: Add Physical Port Control (Opcode 5102h) Arpit Kumar
2025-09-17 16:05 ` Jonathan Cameron
2025-09-19 11:16 ` Arpit Kumar
2025-09-17 16:29 ` Jonathan Cameron
2025-09-19 11:21 ` Arpit Kumar
2026-01-27 15:23 ` [PATCH v4 0/2] FM-API Physical Switch Command Set Support Jonathan Cameron
2026-01-30 12:03 ` Arpit Kumar [this message]
2026-01-30 12:31 ` Jonathan Cameron
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260130120350.n7ngrvla5mfkwllo@test-PowerEdge-R740xd \
--to=arpit1.kumar@samsung.com \
--cc=a.manzanares@samsung.com \
--cc=alok.rathore@samsung.com \
--cc=arpit.sysdev@gmail.com \
--cc=cpgs@samsung.com \
--cc=dave@stgolabs.net \
--cc=gost.dev@samsung.com \
--cc=jonathan.cameron@huawei.com \
--cc=krish.reddy@samsung.com \
--cc=linux-cxl@vger.kernel.org \
--cc=qemu-devel@nongnu.org \
--cc=vishak.g@samsung.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox