| From 661c399a651c11aaf83c45cbfe0b4a1fb7bc3179 Mon Sep 17 00:00:00 2001 |
| From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> |
| Date: Tue, 5 Oct 2021 20:09:51 +0200 |
| Subject: PCI: aardvark: Fix checking for link up via LTSSM state |
| MIME-Version: 1.0 |
| Content-Type: text/plain; charset=UTF-8 |
| Content-Transfer-Encoding: 8bit |
| |
| From: Pali Rohár <pali@kernel.org> |
| |
| commit 661c399a651c11aaf83c45cbfe0b4a1fb7bc3179 upstream. |
| |
| Current implementation of advk_pcie_link_up() is wrong as it marks also |
| link disabled or hot reset states as link up. |
| |
| Fix it by marking link up only to those states which are defined in PCIe |
| Base specification 3.0, Table 4-14: Link Status Mapped to the LTSSM. |
| |
| To simplify implementation, Define macros for every LTSSM state which |
| aardvark hardware can return in CFG_REG register. |
| |
| Fix also checking for link training according to the same Table 4-14. |
| Define a new function advk_pcie_link_training() for this purpose. |
| |
| Link: https://lore.kernel.org/r/20211005180952.6812-13-kabel@kernel.org |
| Fixes: 8c39d710363c ("PCI: aardvark: Add Aardvark PCI host controller driver") |
| Signed-off-by: Pali Rohár <pali@kernel.org> |
| Signed-off-by: Marek Behún <kabel@kernel.org> |
| Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> |
| Reviewed-by: Marek Behún <kabel@kernel.org> |
| Cc: stable@vger.kernel.org |
| Cc: Remi Pommarel <repk@triplefau.lt> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/pci/controller/pci-aardvark.c | 76 +++++++++++++++++++++++++++++++--- |
| 1 file changed, 70 insertions(+), 6 deletions(-) |
| |
| --- a/drivers/pci/controller/pci-aardvark.c |
| +++ b/drivers/pci/controller/pci-aardvark.c |
| @@ -126,8 +126,50 @@ |
| #define CFG_REG (LMI_BASE_ADDR + 0x0) |
| #define LTSSM_SHIFT 24 |
| #define LTSSM_MASK 0x3f |
| -#define LTSSM_L0 0x10 |
| #define RC_BAR_CONFIG 0x300 |
| + |
| +/* LTSSM values in CFG_REG */ |
| +enum { |
| + LTSSM_DETECT_QUIET = 0x0, |
| + LTSSM_DETECT_ACTIVE = 0x1, |
| + LTSSM_POLLING_ACTIVE = 0x2, |
| + LTSSM_POLLING_COMPLIANCE = 0x3, |
| + LTSSM_POLLING_CONFIGURATION = 0x4, |
| + LTSSM_CONFIG_LINKWIDTH_START = 0x5, |
| + LTSSM_CONFIG_LINKWIDTH_ACCEPT = 0x6, |
| + LTSSM_CONFIG_LANENUM_ACCEPT = 0x7, |
| + LTSSM_CONFIG_LANENUM_WAIT = 0x8, |
| + LTSSM_CONFIG_COMPLETE = 0x9, |
| + LTSSM_CONFIG_IDLE = 0xa, |
| + LTSSM_RECOVERY_RCVR_LOCK = 0xb, |
| + LTSSM_RECOVERY_SPEED = 0xc, |
| + LTSSM_RECOVERY_RCVR_CFG = 0xd, |
| + LTSSM_RECOVERY_IDLE = 0xe, |
| + LTSSM_L0 = 0x10, |
| + LTSSM_RX_L0S_ENTRY = 0x11, |
| + LTSSM_RX_L0S_IDLE = 0x12, |
| + LTSSM_RX_L0S_FTS = 0x13, |
| + LTSSM_TX_L0S_ENTRY = 0x14, |
| + LTSSM_TX_L0S_IDLE = 0x15, |
| + LTSSM_TX_L0S_FTS = 0x16, |
| + LTSSM_L1_ENTRY = 0x17, |
| + LTSSM_L1_IDLE = 0x18, |
| + LTSSM_L2_IDLE = 0x19, |
| + LTSSM_L2_TRANSMIT_WAKE = 0x1a, |
| + LTSSM_DISABLED = 0x20, |
| + LTSSM_LOOPBACK_ENTRY_MASTER = 0x21, |
| + LTSSM_LOOPBACK_ACTIVE_MASTER = 0x22, |
| + LTSSM_LOOPBACK_EXIT_MASTER = 0x23, |
| + LTSSM_LOOPBACK_ENTRY_SLAVE = 0x24, |
| + LTSSM_LOOPBACK_ACTIVE_SLAVE = 0x25, |
| + LTSSM_LOOPBACK_EXIT_SLAVE = 0x26, |
| + LTSSM_HOT_RESET = 0x27, |
| + LTSSM_RECOVERY_EQUALIZATION_PHASE0 = 0x28, |
| + LTSSM_RECOVERY_EQUALIZATION_PHASE1 = 0x29, |
| + LTSSM_RECOVERY_EQUALIZATION_PHASE2 = 0x2a, |
| + LTSSM_RECOVERY_EQUALIZATION_PHASE3 = 0x2b, |
| +}; |
| + |
| #define VENDOR_ID_REG (LMI_BASE_ADDR + 0x44) |
| |
| /* PCIe core controller registers */ |
| @@ -219,13 +261,35 @@ static inline u32 advk_readl(struct advk |
| return readl(pcie->base + reg); |
| } |
| |
| -static int advk_pcie_link_up(struct advk_pcie *pcie) |
| +static u8 advk_pcie_ltssm_state(struct advk_pcie *pcie) |
| { |
| - u32 val, ltssm_state; |
| + u32 val; |
| + u8 ltssm_state; |
| |
| val = advk_readl(pcie, CFG_REG); |
| ltssm_state = (val >> LTSSM_SHIFT) & LTSSM_MASK; |
| - return ltssm_state >= LTSSM_L0; |
| + return ltssm_state; |
| +} |
| + |
| +static inline bool advk_pcie_link_up(struct advk_pcie *pcie) |
| +{ |
| + /* check if LTSSM is in normal operation - some L* state */ |
| + u8 ltssm_state = advk_pcie_ltssm_state(pcie); |
| + return ltssm_state >= LTSSM_L0 && ltssm_state < LTSSM_DISABLED; |
| +} |
| + |
| +static inline bool advk_pcie_link_training(struct advk_pcie *pcie) |
| +{ |
| + /* |
| + * According to PCIe Base specification 3.0, Table 4-14: Link |
| + * Status Mapped to the LTSSM is Link Training mapped to LTSSM |
| + * Configuration and Recovery states. |
| + */ |
| + u8 ltssm_state = advk_pcie_ltssm_state(pcie); |
| + return ((ltssm_state >= LTSSM_CONFIG_LINKWIDTH_START && |
| + ltssm_state < LTSSM_L0) || |
| + (ltssm_state >= LTSSM_RECOVERY_EQUALIZATION_PHASE0 && |
| + ltssm_state <= LTSSM_RECOVERY_EQUALIZATION_PHASE3)); |
| } |
| |
| static int advk_pcie_wait_for_link(struct advk_pcie *pcie) |
| @@ -252,7 +316,7 @@ static void advk_pcie_wait_for_retrain(s |
| size_t retries; |
| |
| for (retries = 0; retries < RETRAIN_WAIT_MAX_RETRIES; ++retries) { |
| - if (!advk_pcie_link_up(pcie)) |
| + if (advk_pcie_link_training(pcie)) |
| break; |
| udelay(RETRAIN_WAIT_USLEEP_US); |
| } |
| @@ -516,7 +580,7 @@ advk_pci_bridge_emul_pcie_conf_read(stru |
| /* u32 contains both PCI_EXP_LNKCTL and PCI_EXP_LNKSTA */ |
| u32 val = advk_readl(pcie, PCIE_CORE_PCIEXP_CAP + reg) & |
| ~(PCI_EXP_LNKSTA_LT << 16); |
| - if (!advk_pcie_link_up(pcie)) |
| + if (advk_pcie_link_training(pcie)) |
| val |= (PCI_EXP_LNKSTA_LT << 16); |
| *value = val; |
| return PCI_BRIDGE_EMUL_HANDLED; |