From: Saif Abrar<[email protected]>
Config-read the link-status register in the PCI-E macro,
Depending on the link-active bit, set the link-active status
in the HOTPLUG_STATUS and LINK_MANAGEMENT registers
Also, clear the Presence-status active low bit in HOTPLUG_STATUS reg
after config-reading the slot-status in the PCI-E macro.
Signed-off-by: Saif Abrar<[email protected]>
Reviewed-by: Michael S. Tsirkin<[email protected]>
Reviewed-by: Caleb Schlossin<[email protected]>
Reviewed-by: Aditya Gupta<[email protected]>
---
hw/pci-host/pnv_phb4.c | 74 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 73 insertions(+), 1 deletion(-)
diff --git a/hw/pci-host/pnv_phb4.c b/hw/pci-host/pnv_phb4.c
index b13165f84d..87446aedc8 100644
--- a/hw/pci-host/pnv_phb4.c
+++ b/hw/pci-host/pnv_phb4.c
@@ -508,6 +508,34 @@ uint32_t get_exp_offset(PCIDevice *pdev)
return rpc->exp_offset;
}
+/*
+ * Config-read the link-status register in the PCI-E macro,
+ * convert to LE and check the link-active bit.
+ */
+static uint32_t is_link_active(PnvPHB4 *phb)
+{
+ PCIHostState *pci = PCI_HOST_BRIDGE(phb->phb_base);
+ PCIDevice *pdev = pci_find_device(pci->bus, 0, 0);
+ uint32_t exp_offset = get_exp_offset(pdev);
+
+ return (be16_to_cpu(pnv_phb4_rc_config_read(phb,
+ exp_offset + PCI_EXP_LNKSTA, 4)) &
+ PCI_EXP_LNKSTA_DLLLA);
+}
+
+/*
+ * Apply sticky-mask 's' to the reset-value 'v' and write to the address 'a'.
+ * RC-config space values and masks are LE.
+ * Method pnv_phb4_rc_config_read() returns BE, hence convert to LE.
+ * Compute new value in LE domain.
+ * New value computation using sticky-mask is in LE.
+ * Convert the computed value from LE to BE before writing back.
+ */
+#define RC_CONFIG_STICKY_RESET(a, v, s) \
+ (pci_set_word(conf + (a), be16_to_cpu( \
+ (be16_to_cpu(pci_get_word(conf + (a))) & (s)) | \
+ ((v) & ~(s)))))
+
void pnv_phb4_cfg_core_reset(PCIDevice *d)
{
uint8_t *conf = d->config;
@@ -680,6 +708,11 @@ void pnv_phb4_reg_write(void *opaque, hwaddr off, uint64_t
val,
val = 0;
break;
+ case PHB_PCIE_HOTPLUG_STATUS:
+ /* For normal operations, Simspeed diagnostic bit is always zero */
+ val &= PHB_PCIE_HPSTAT_SIMDIAG;
+ break;
+
/* Read only registers */
case PHB_CPU_LOADSTORE_STATUS:
case PHB_ETU_ERR_SUMMARY:
@@ -827,6 +860,10 @@ void pnv_phb4_reg_write(void *opaque, hwaddr off, uint64_t
val,
uint64_t pnv_phb4_reg_read(void *opaque, hwaddr off, unsigned size)
{
PnvPHB4 *phb = PNV_PHB4(opaque);
+ /* Get the PCI-E capability offset from the root-port */
+ PCIHostState *pci = PCI_HOST_BRIDGE(phb->phb_base);
+ PCIDevice *pdev = pci_find_device(pci->bus, 0, 0);
+ uint32_t exp_base = get_exp_offset(pdev);
uint64_t val;
if ((off & 0xfffc) == PHB_CONFIG_DATA) {
@@ -895,8 +932,38 @@ uint64_t pnv_phb4_reg_read(void *opaque, hwaddr off,
unsigned size)
val |= PHB_PCIE_DLP_INBAND_PRESENCE | PHB_PCIE_DLP_TL_LINKACT;
return val;
+ /*
+ * Read PCI-E registers and set status for:
+ * - Card present (active low bit 10)
+ * - Link active (bit 12)
+ */
case PHB_PCIE_HOTPLUG_STATUS:
- /* Clear write-only bit */
+ /*
+ * Presence-status bit hpi_present_n is active-low, with reset value 1.
+ * Start by setting this bit to 1, indicating the card is not present.
+ * Then check the PCI-E register and clear the bit if card is present.
+ */
+ val |= PHB_PCIE_HPSTAT_PRESENCE;
+
+ /*
+ * Config-read the PCI-E macro register for slot-status.
+ * Method for config-read converts to BE value.
+ * To check actual bit in the PCI-E register,
+ * convert the value back to host format.
+ * Clear the Presence-status active low bit.
+ */
+ if (be16_to_cpu(pnv_phb4_rc_config_read(phb, exp_base + PCI_EXP_SLTSTA,
+ 4))
+ & PCI_EXP_SLTSTA_PDS) {
+ val &= ~PHB_PCIE_HPSTAT_PRESENCE;
+ }
+
+ /* Check if link is active and set the bit */
+ if (is_link_active(phb)) {
+ val |= PHB_PCIE_HPSTAT_LINKACTIVE;
+ }
+
+ /* Clear write-only resample-bit */
val &= ~PHB_PCIE_HPSTAT_RESAMPLE;
return val;
@@ -904,6 +971,11 @@ uint64_t pnv_phb4_reg_read(void *opaque, hwaddr off, unsigned size)
case PHB_PCIE_LMR:
/* These write-only bits always read as 0 */
val &= ~(PHB_PCIE_LMR_CHANGELW | PHB_PCIE_LMR_RETRAINLINK);
+
+ /* Check if link is active and set the bit */
+ if (is_link_active(phb)) {
+ val |= PHB_PCIE_LMR_LINKACTIVE;
+ }
return val;
/* Silent simple reads */