Add the PCIe Device 3 Extended Capability (PCIe 6.4 section 7.7.9), with which an endpoint exposes its UIO requester and completer support and the related control bits.
The capability tracks the link's flit state: the requester-enable bits stay clear while the link is not in flit mode, and the Segment Captured status bit follows the flit state. Signed-off-by: Shrihari E S <[email protected]> Signed-off-by: Dongjoo Seo <[email protected]> --- hw/pci/meson.build | 2 +- hw/pci/pci.c | 2 + hw/pci/pcie.c | 29 ++++++++++ hw/pci/pcie_dev3.c | 111 +++++++++++++++++++++++++++++++++++++ include/hw/pci/pcie.h | 5 ++ include/hw/pci/pcie_dev3.h | 26 +++++++++ include/hw/pci/pcie_regs.h | 26 +++++++++ 7 files changed, 200 insertions(+), 1 deletion(-) create mode 100644 hw/pci/pcie_dev3.c create mode 100644 include/hw/pci/pcie_dev3.h diff --git a/hw/pci/meson.build b/hw/pci/meson.build index d55882a0c9..0a7f3b162a 100644 --- a/hw/pci/meson.build +++ b/hw/pci/meson.build @@ -14,7 +14,7 @@ pci_ss.add(files( # The functions in these modules can be used by devices too. Since we # allow plugging PCIe devices into PCI buses, include them even if # CONFIG_PCI_EXPRESS=n. -pci_ss.add(files('pcie.c', 'pcie_aer.c', 'pcie_svc.c')) +pci_ss.add(files('pcie.c', 'pcie_aer.c', 'pcie_svc.c', 'pcie_dev3.c')) pci_ss.add(files('pcie_doe.c')) system_ss.add(when: 'CONFIG_PCI_EXPRESS', if_true: files('pcie_port.c', 'pcie_host.c')) system_ss.add_all(when: 'CONFIG_PCI', if_true: pci_ss) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 50472cab88..cc5e40ac5c 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -571,6 +571,7 @@ static void pci_do_device_reset(PCIDevice *dev) pcie_sriov_pf_reset(dev); pcie_cap_flit_reset(dev); pcie_svc_cap_reset(dev); + pcie_dev3_cap_reset(dev); } /* @@ -1819,6 +1820,7 @@ void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val_in, int pcie_sriov_config_write(d, addr, val_in, l); pcie_cap_flit_write_config(d, addr, l); pcie_svc_cap_write_config(d, addr, val_in, l); + pcie_dev3_cap_write_config(d, addr, val_in, l); } /***********************************************************/ diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index b05f70022a..9f16cc8d1b 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -190,6 +190,32 @@ void pcie_cap_fill_link_ep_usp(PCIDevice *dev, PCIExpLinkWidth width, pcie_cap_fill_lnk(dev, exp_cap, width, speed, flitmode); } +bool pcie_link_flit_active(PCIDevice *dev) +{ + uint8_t *exp_cap; + uint16_t lnksta2; + uint16_t lnkctl; + uint16_t flags; + + if (!pci_is_express(dev) || !dev->exp.exp_cap) { + return false; + } + + exp_cap = dev->config + dev->exp.exp_cap; + flags = pci_get_word(exp_cap + PCI_EXP_FLAGS); + if (!(flags & PCI_EXP_FLAGS_FLIT)) { + return false; + } + lnkctl = pci_get_word(exp_cap + PCI_EXP_LNKCTL); + lnksta2 = pci_get_word(exp_cap + PCI_EXP_LNKSTA2); + + if (lnkctl & PCI_EXP_LNKCTL_FLIT_DIS) { + return false; + } + + return lnksta2 & PCI_EXP_LNKSTA2_FLIT; +} + /* * Derive the read-only Flit Status bit of the Link Status 2 * register from the Flit Disable bit of the Link Control register. @@ -226,6 +252,9 @@ void pcie_cap_flit_write_config(PCIDevice *dev, uint32_t addr, int len) } pcie_cap_flit_update_sta(dev); + if (dev->exp.dev3_cap) { + pcie_dev3_cap_flit_update(dev); + } } /* diff --git a/hw/pci/pcie_dev3.c b/hw/pci/pcie_dev3.c new file mode 100644 index 0000000000..8ca7c74b8c --- /dev/null +++ b/hw/pci/pcie_dev3.c @@ -0,0 +1,111 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * PCIe Device3 Extended Capability + * + * Copyright (c) 2026 Samsung Electronics Co., Ltd. + */ + +#include "qemu/osdep.h" +#include "hw/pci/pci_device.h" +#include "hw/pci/pcie.h" +#include "hw/pci/pcie_port.h" +#include "hw/pci/pcie_dev3.h" + +int pcie_dev3_cap_init(PCIDevice *dev, uint32_t offset, + bool uio_req, bool uio_comp, Error **errp) +{ + uint32_t devcap3 = 0; + uint32_t wmask = 0; + + if (!pci_is_express(dev)) { + error_setg(errp, "Device 3 ECAP requires PCIe"); + return -EINVAL; + } + + pcie_add_capability(dev, PCI_EXT_CAP_ID_DEV3, 1, offset, PCI_DEV3_SIZEOF); + dev->exp.dev3_cap = offset; + dev->exp.dev3.uio_req = uio_req; + dev->exp.dev3.uio_comp = uio_comp; + + /* + * In this implementation, 14-bit tag support is only advertised + * when UIO is enabled. A real device may support 14-bit tags + * independently of UIO. + */ + if (uio_comp) { + devcap3 |= PCI_DEV3_CAP_TAG_COMP | PCI_DEV3_CAP_UIO_COMP; + } + if (uio_req) { + devcap3 |= PCI_DEV3_CAP_TAG_REQ | PCI_DEV3_CAP_UIO_REQ; + wmask |= PCI_DEV3_CTL_TAG_REQ_EN | + PCI_DEV3_CTL_UIO_REQ_EN | + PCI_DEV3_CTL_UIO_256B_DIS; + } + + pci_set_long(dev->config + offset + PCI_DEV3_CAP_OFFSET, devcap3); + pci_set_long(dev->wmask + offset + PCI_DEV3_CTL_OFFSET, wmask); + + return 0; +} + +/* + * Track the link's flit state in the Device 3 capability: the + * requester-enable bits of the Device Control 3 register are cleared + * while the link is not in flit mode, and the Segment Captured + * status bit follows the link's flit state. + */ +void pcie_dev3_cap_flit_update(PCIDevice *dev) +{ + uint32_t off = dev->exp.dev3_cap; + uint32_t devctl3; + uint32_t devsta3; + bool flit; + + if (!off) { + return; + } + + flit = pcie_link_flit_active(dev); + if (!dev->exp.dev3.uio_req || !flit) { + devctl3 = pci_get_long(dev->config + off + PCI_DEV3_CTL_OFFSET); + devctl3 &= ~(PCI_DEV3_CTL_TAG_REQ_EN | + PCI_DEV3_CTL_UIO_REQ_EN | + PCI_DEV3_CTL_UIO_256B_DIS); + pci_set_long(dev->config + off + PCI_DEV3_CTL_OFFSET, devctl3); + } + + devsta3 = pci_get_long(dev->config + off + PCI_DEV3_STA_OFFSET); + if (flit) { + devsta3 |= PCI_DEV3_STA_SEGMENT_CAP; + } else { + devsta3 &= ~PCI_DEV3_STA_SEGMENT_CAP; + } + pci_set_long(dev->config + off + PCI_DEV3_STA_OFFSET, devsta3); +} + +void pcie_dev3_cap_reset(PCIDevice *dev) +{ + uint32_t off = dev->exp.dev3_cap; + + if (!off) { + return; + } + + pci_set_long(dev->config + off + PCI_DEV3_CTL_OFFSET, 0); + pci_set_long(dev->config + off + PCI_DEV3_STA_OFFSET, 0); + pcie_dev3_cap_flit_update(dev); +} + +void pcie_dev3_cap_write_config(PCIDevice *dev, + uint32_t addr, uint32_t val, int len) +{ + if (!dev->exp.dev3_cap) { + return; + } + + if (ranges_overlap(addr, len, + dev->exp.dev3_cap + PCI_DEV3_CTL_OFFSET, 4)) { + pcie_dev3_cap_flit_update(dev); + } +} diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h index 3d092e8871..95f670c830 100644 --- a/include/hw/pci/pcie.h +++ b/include/hw/pci/pcie.h @@ -26,6 +26,7 @@ #include "hw/pci/pcie_aer.h" #include "hw/pci/pcie_sriov.h" #include "hw/pci/pcie_svc.h" +#include "hw/pci/pcie_dev3.h" #include "hw/core/hotplug.h" typedef struct PCIEPort PCIEPort; @@ -86,6 +87,9 @@ struct PCIExpressDevice { /* Streamlined Virtual Channel (SVC) introduced from PCIe 6.1 */ uint32_t svc_cap; + /* PCIe Device 3 Capability */ + uint32_t dev3_cap; + struct PCIEDev3Cap dev3; }; #define COMPAT_PROP_PCP "power_controller_present" @@ -149,6 +153,7 @@ void pcie_cap_fill_link_ep_usp(PCIDevice *dev, PCIExpLinkWidth width, PCIExpLinkSpeed speed, bool flitmode); void pcie_cap_flit_write_config(PCIDevice *dev, uint32_t addr, int len); void pcie_cap_flit_reset(PCIDevice *dev); +bool pcie_link_flit_active(PCIDevice *dev); void pcie_cap_slot_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); diff --git a/include/hw/pci/pcie_dev3.h b/include/hw/pci/pcie_dev3.h new file mode 100644 index 0000000000..4cfa07fe32 --- /dev/null +++ b/include/hw/pci/pcie_dev3.h @@ -0,0 +1,26 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * PCIe Device3 Extended Capability + * + * Copyright (c) 2026 Samsung Electronics Co., Ltd. + */ + +#ifndef HW_PCIE_DEV3_H +#define HW_PCIE_DEV3_H + +#include "hw/pci/pci.h" + +typedef struct PCIEDev3Cap { + bool uio_req; + bool uio_comp; +} PCIEDev3Cap; + +int pcie_dev3_cap_init(PCIDevice *dev, uint32_t offset, + bool uio_req, bool uio_comp, Error **errp); +void pcie_dev3_cap_reset(PCIDevice *dev); +void pcie_dev3_cap_write_config(PCIDevice *dev, + uint32_t addr, uint32_t val, int len); +void pcie_dev3_cap_flit_update(PCIDevice *dev); + +#endif /* HW_PCIE_DEV3_H */ diff --git a/include/hw/pci/pcie_regs.h b/include/hw/pci/pcie_regs.h index 044b3caae4..484178adb3 100644 --- a/include/hw/pci/pcie_regs.h +++ b/include/hw/pci/pcie_regs.h @@ -256,4 +256,30 @@ typedef enum PCIExpLinkWidth { #define PCI_SVC_RES_STATUS(n) (PCI_SVC_RES_STATUS_BASE + ((n) * 0x0c)) #define PCI_SVC_VC_NEGO BIT(1) +/* PCIe Device 3 capability structure. PCIe 6.4 Specification Section 7.7.9 */ +#ifndef PCI_EXT_CAP_ID_DEV3 +#define PCI_EXT_CAP_ID_DEV3 0x2f +#endif + +#define PCI_DEV3_CAP_OFFSET 0x04 +#define PCI_DEV3_CTL_OFFSET 0x08 +#define PCI_DEV3_STA_OFFSET 0x0c +#define PCI_DEV3_SIZEOF 0x10 + +/* Section 7.7.9.2 Device Capabilities 3 Register */ +#define PCI_DMWR_REQ_SUPPORTED BIT(0) +#define PCI_DEV3_CAP_TAG_COMP BIT(1) +#define PCI_DEV3_CAP_TAG_REQ BIT(2) +#define PCI_L0P_SUPPORTED BIT(3) +#define PCI_DEV3_CAP_UIO_COMP BIT(10) +#define PCI_DEV3_CAP_UIO_REQ BIT(11) + +/* Section 7.7.9.3 Device Control 3 Register */ +#define PCI_DEV3_CTL_TAG_REQ_EN BIT(2) +#define PCI_DEV3_CTL_UIO_REQ_EN BIT(7) +#define PCI_DEV3_CTL_UIO_256B_DIS BIT(8) + +/* Section 7.7.9.4 Device Status 3 Register */ +#define PCI_DEV3_STA_SEGMENT_CAP BIT(3) + #endif /* QEMU_PCIE_REGS_H */ -- 2.34.1
