@samitouri / QOSamiQemu / commits / 6e1410fdcf

hw/cxl: Add Physical Port Control FMAPI Command (Opcode 5102h)

Added assert-deassert PERST implementation for physical ports (both USP and DSP's). Assert PERST involves bg operation for holding 100ms. Reset PPB implementation for physical ports. Signed-off-by: Arpit Kumar <arpit1.kumar@samsung.com> Co-developed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Message-Id: <20260204173223.44122-4-Jonathan.Cameron@huawei.com>

Arpit Kumar committed Feb 4, 2026 at 17:32 UTC 6e1410fdcf7040b2c32a7845b423ab604384aded
6 files changed +174
hw/cxl/cxl-mailbox-utils.c
+130
@@ -17,6 +17,7 @@
17 #include "hw/cxl/cxl_mailbox.h"
18 #include "hw/cxl/cxl_port.h"
19 #include "hw/pci/pci.h"
20 +#include "hw/pci-bridge/cxl_downstream_port.h"
21 #include "hw/pci-bridge/cxl_upstream_port.h"
22 #include "qemu/cutils.h"
23 #include "qemu/host-utils.h"
@@ -119,6 +120,7 @@ enum {
120 PHYSICAL_SWITCH = 0x51,
121 #define IDENTIFY_SWITCH_DEVICE 0x0
122 #define GET_PHYSICAL_PORT_STATE 0x1
123 + #define PHYSICAL_PORT_CONTROL 0x2
124 TUNNEL = 0x53,
125 #define MANAGEMENT_COMMAND 0x0
126 FMAPI_DCD_MGMT = 0x56,
@@ -616,6 +618,7 @@ static CXLRetCode cmd_get_physical_port_state(const struct cxl_cmd *cmd,
618 struct cxl_fmapi_port_state_info_block *port;
619 /* First try to match on downstream port */
620 PCIDevice *port_dev;
621 + CXLPhyPortPerst *perst;
622 uint16_t lnkcap, lnkcap2, lnksta;
623
624 port = &out->ports[i];
@@ -663,6 +666,7 @@ static CXLRetCode cmd_get_physical_port_state(const struct cxl_cmd *cmd,
666 port->supported_cxl_mode_bitmask = CXL_PORT_SUPPORTS_68B_VH |
667 CXL_PORT_SUPPORTS_256B;
668 port->supported_ld_count = 3;
669 + perst = cxl_dsp_get_perst(CXL_DSP(port_dev));
670 } else if (usp->port == in->ports[i]) { /* USP */
671 port_dev = PCI_DEVICE(usp);
672 port->config_state = CXL_PORT_CONFIG_STATE_USP;
@@ -670,6 +674,7 @@ static CXLRetCode cmd_get_physical_port_state(const struct cxl_cmd *cmd,
674 port->connected_device_mode = 0; /* Reserved for USP */
675 port->supported_cxl_mode_bitmask = CXL_PORT_SUPPORTS_68B_VH |
676 (CXL_USP(usp)->flitmode ? CXL_PORT_SUPPORTS_256B : 0);
677 + perst = &CXL_USP(usp)->perst;
678 } else {
679 return CXL_MBOX_INVALID_INPUT;
680 }
@@ -698,6 +703,7 @@ static CXLRetCode cmd_get_physical_port_state(const struct cxl_cmd *cmd,
703 /* TODO: Track down if we can get the rest of the info */
704 port->ltssm_state = 0x7;
705 port->first_lane_num = 0;
706 + port->link_state = perst ? CXL_PORT_LINK_STATE_FLAG_PERST_ASSERTED : 0;
707 }
708
709 pl_size = sizeof(*out) + sizeof(*out->ports) * in->num_ports;
@@ -706,6 +712,115 @@ static CXLRetCode cmd_get_physical_port_state(const struct cxl_cmd *cmd,
712 return CXL_MBOX_SUCCESS;
713 }
714
715 +static void *bg_assertcb(void *opaque)
716 +{
717 + CXLPhyPortPerst *perst = opaque;
718 +
719 + /* holding reset phase for 100ms */
720 + while (perst->asrt_time--) {
721 + usleep(1000);
722 + }
723 + perst->issued_assert_perst = true;
724 + return NULL;
725 +}
726 +
727 +static CXLRetCode cxl_deassert_perst(Object *obj, CXLPhyPortPerst *perst)
728 +{
729 + if (!perst->issued_assert_perst) {
730 + return CXL_MBOX_INTERNAL_ERROR;
731 + }
732 +
733 + QEMU_LOCK_GUARD(&perst->lock);
734 + resettable_release_reset(obj, RESET_TYPE_COLD);
735 + perst->issued_assert_perst = false;
736 + perst->asrt_time = ASSERT_WAIT_TIME_MS;
737 +
738 + return CXL_MBOX_SUCCESS;
739 +}
740 +
741 +static CXLRetCode cxl_assert_perst(Object *obj, CXLPhyPortPerst *perst)
742 +{
743 + if (cxl_perst_asserted(perst)) {
744 + return CXL_MBOX_INTERNAL_ERROR;
745 + }
746 +
747 + QEMU_LOCK_GUARD(&perst->lock);
748 + resettable_assert_reset(obj, RESET_TYPE_COLD);
749 + qemu_thread_create(&perst->asrt_thread, "assert_thread", bg_assertcb,
750 + perst, QEMU_THREAD_DETACHED);
751 +
752 + return CXL_MBOX_SUCCESS;
753 +}
754 +
755 +static CXLDownstreamPort *cxl_find_dsp_on_bus(PCIBus *bus, uint8_t pn)
756 +{
757 +
758 + PCIDevice *port_dev = pcie_find_port_by_pn(bus, pn);
759 +
760 + if (object_dynamic_cast(OBJECT(port_dev), TYPE_CXL_DSP)) {
761 + return CXL_DSP(port_dev);
762 + }
763 +
764 + return NULL;
765 +}
766 +
767 +/* CXL r3.2 Section 7.6.7.1.3: Get Physical Port Control (Opcode 5102h) */
768 +static CXLRetCode cmd_physical_port_control(const struct cxl_cmd *cmd,
769 + uint8_t *payload_in,
770 + size_t len_in,
771 + uint8_t *payload_out,
772 + size_t *len_out,
773 + CXLCCI *cci)
774 +{
775 + CXLUpstreamPort *pp = CXL_USP(cci->d);
776 + CXLPhyPortPerst *perst;
777 + PCIDevice *dev;
778 +
779 + struct cxl_fmapi_get_physical_port_control_req_pl {
780 + uint8_t ppb_id;
781 + uint8_t ports_op;
782 + } QEMU_PACKED *in = (void *)payload_in;
783 +
784 + if (len_in < sizeof(*in)) {
785 + return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
786 + }
787 +
788 + if (PCIE_PORT(pp)->port == in->ppb_id) {
789 + dev = PCI_DEVICE(pp);
790 + perst = &pp->perst;
791 + } else {
792 + CXLDownstreamPort *dsp =
793 + cxl_find_dsp_on_bus(&PCI_BRIDGE(pp)->sec_bus, in->ppb_id);
794 +
795 + if (!dsp) {
796 + return CXL_MBOX_INVALID_INPUT;
797 + }
798 + dev = PCI_DEVICE(dsp);
799 + perst = cxl_dsp_get_perst(dsp);
800 + }
801 +
802 + switch (in->ports_op) {
803 + case 0:
804 + return cxl_assert_perst(OBJECT(&dev->qdev), perst);
805 + case 1:
806 + return cxl_deassert_perst(OBJECT(&dev->qdev), perst);
807 + case 2: {
808 + if (!perst) {
809 + return CXL_MBOX_INVALID_INPUT;
810 + }
811 +
812 + if (perst->issued_assert_perst ||
813 + perst->asrt_time < ASSERT_WAIT_TIME_MS) {
814 + return CXL_MBOX_INTERNAL_ERROR;
815 + }
816 + device_cold_reset(&dev->qdev);
817 + return CXL_MBOX_SUCCESS;
818 + }
819 + default:
820 + return CXL_MBOX_INVALID_INPUT;
821 + }
822 +}
823 +
824 /* CXL r3.1 Section 8.2.9.1.2: Background Operation Status (Opcode 0002h) */
825 static CXLRetCode cmd_infostat_bg_op_sts(const struct cxl_cmd *cmd,
826 uint8_t *payload_in,
@@ -4412,6 +4527,8 @@ static const struct cxl_cmd cxl_cmd_set_sw[256][256] = {
4527 cmd_identify_switch_device, 0, 0 },
4528 [PHYSICAL_SWITCH][GET_PHYSICAL_PORT_STATE] = { "SWITCH_PHYSICAL_PORT_STATS",
4529 cmd_get_physical_port_state, ~0, 0 },
4530 + [PHYSICAL_SWITCH][PHYSICAL_PORT_CONTROL] = { "SWITCH_PHYSICAL_PORT_CONTROL",
4531 + cmd_physical_port_control, 2, 0 },
4532 [TUNNEL][MANAGEMENT_COMMAND] = { "TUNNEL_MANAGEMENT_COMMAND",
4533 cmd_tunnel_management_cmd, ~0, 0 },
4534 };
@@ -4618,6 +4735,19 @@ static void cxl_rebuild_cel(CXLCCI *cci)
4735 }
4736 }
4737
4738 +void cxl_init_physical_port_control(CXLPhyPortPerst *perst)
4739 +{
4740 + qemu_mutex_init(&perst->lock);
4741 + perst->issued_assert_perst = false;
4742 + /*
4743 + * Assert PERST involves physical port to be in
4744 + * hold reset phase for minimum 100ms. No other
4745 + * physical port control requests are entertained
4746 + * until Deassert PERST command.
4747 + */
4748 + perst->asrt_time = ASSERT_WAIT_TIME_MS;
4749 +}
4750 +
4751 void cxl_init_cci(CXLCCI *cci, size_t payload_max)
4752 {
4753 cci->payload_max = payload_max;
hw/pci-bridge/cxl_downstream.c
+9
@@ -13,9 +13,11 @@
13 #include "hw/pci/msi.h"
14 #include "hw/pci/pcie.h"
15 #include "hw/pci/pcie_port.h"
16 +#include "hw/pci-bridge/cxl_downstream_port.h"
17 #include "hw/core/qdev-properties.h"
18 #include "hw/core/qdev-properties-system.h"
19 #include "hw/cxl/cxl.h"
20 +#include "hw/cxl/cxl_port.h"
21 #include "qapi/error.h"
22
23 typedef struct CXLDownstreamPort {
@@ -24,6 +26,7 @@ typedef struct CXLDownstreamPort {
26
27 /*< public >*/
28 CXLComponentState cxl_cstate;
29 + CXLPhyPortPerst perst;
30 } CXLDownstreamPort;
31
32 #define CXL_DOWNSTREAM_PORT_MSI_OFFSET 0x70
@@ -81,6 +84,11 @@ static void cxl_dsp_config_write(PCIDevice *d, uint32_t address,
84 cxl_dsp_dvsec_write_config(d, address, val, len);
85 }
86
87 +CXLPhyPortPerst *cxl_dsp_get_perst(CXLDownstreamPort *dsp)
88 +{
89 + return &dsp->perst;
90 +}
91 +
92 static void cxl_dsp_reset(DeviceState *qdev)
93 {
94 PCIDevice *d = PCI_DEVICE(qdev);
@@ -92,6 +100,7 @@ static void cxl_dsp_reset(DeviceState *qdev)
100 pci_bridge_reset(qdev);
101
102 latch_registers(dsp);
103 + cxl_init_physical_port_control(&dsp->perst);
104 }
105
106 static void build_dvsecs(PCIDevice *d, CXLComponentState *cxl)
hw/pci-bridge/cxl_upstream.c
+1
@@ -103,6 +103,7 @@ static void cxl_usp_reset(DeviceState *qdev)
103 pcie_cap_deverr_reset(d);
104 pcie_cap_fill_link_ep_usp(d, usp->width, usp->speed, usp->flitmode);
105 latch_registers(usp);
106 + cxl_init_physical_port_control(&usp->perst);
107 }
108
109 static void build_dvsecs(CXLUpstreamPort *usp)
include/hw/cxl/cxl_port.h
+20
@@ -3,6 +3,8 @@
3 #ifndef CXL_PORT_H
4 #define CXL_PORT_H
5
6 +#include "qemu/thread.h"
7 +
8 /* CXL r3.2 Table 7-19: Get Physical Port State Port Information Block Format */
9 #define CXL_PORT_CONFIG_STATE_DISABLED 0x0
10 #define CXL_PORT_CONFIG_STATE_BIND_IN_PROGRESS 0x1
@@ -50,4 +52,22 @@
52 #define CXL_PORT_LINK_STATE_FLAG_PRSNT BIT(2)
53 #define CXL_PORT_LINK_STATE_FLAG_POWER_OFF BIT(3)
54
55 +#define CXL_MAX_PHY_PORTS 256
56 +#define ASSERT_WAIT_TIME_MS 100 /* Assert - Deassert PERST */
57 +
58 +/* Assert - Deassert PERST */
59 +typedef struct CXLPhyPortPerst {
60 + bool issued_assert_perst;
61 + QemuMutex lock; /* protecting assert-deassert reset request */
62 + uint64_t asrt_time;
63 + QemuThread asrt_thread; /* thread for 100ms delay */
64 +} CXLPhyPortPerst;
65 +
66 +void cxl_init_physical_port_control(CXLPhyPortPerst *perst);
67 +
68 +static inline bool cxl_perst_asserted(CXLPhyPortPerst *perst)
69 +{
70 + return perst->issued_assert_perst || perst->asrt_time < ASSERT_WAIT_TIME_MS;
71 +}
72 +
73 #endif /* CXL_PORT_H */
include/hw/pci-bridge/cxl_downstream_port.h new
+12
@@ -0,0 +1,12 @@
1 +/*
2 + * SPDX-License-Identifier: GPL-2.0-or-later
3 + */
4 +
5 +#ifndef CXL_DOWNSTREAM_PORT_H
6 +#define CXL_DOWNSTREAM_PORT_H
7 +#include "include/hw/cxl/cxl_port.h"
8 +
9 +typedef struct CXLDownstreamPort CXLDownstreamPort;
10 +CXLPhyPortPerst *cxl_dsp_get_perst(CXLDownstreamPort *dsp);
11 +
12 +#endif
include/hw/pci-bridge/cxl_upstream_port.h
+2
@@ -4,6 +4,7 @@
4 #include "hw/pci/pcie.h"
5 #include "hw/pci/pcie_port.h"
6 #include "hw/cxl/cxl.h"
7 +#include "hw/cxl/cxl_port.h"
8
9 typedef struct CXLUpstreamPort {
10 /*< private >*/
@@ -12,6 +13,7 @@ typedef struct CXLUpstreamPort {
13 /*< public >*/
14 CXLComponentState cxl_cstate;
15 CXLCCI swcci;
16 + CXLPhyPortPerst perst;
17
18 PCIExpLinkSpeed speed;
19 PCIExpLinkWidth width;