On 4/01/24 06:10, Kai-Heng Feng wrote: > On Wed, Jan 3, 2024 at 6:53 PM Ulf Hansson <ulf.hansson@xxxxxxxxxx> wrote: >> >> On Thu, 21 Dec 2023 at 04:23, Kai-Heng Feng <kai.heng.feng@xxxxxxxxxxxxx> wrote: >>> >>> Spamming `lspci -vv` can still observe the replay timer timeout error >>> even after commit 015c9cbcf0ad ("mmc: sdhci-pci-gli: GL9750: Mask the >>> replay timer timeout of AER"), albeit with a lower reproduce rate. >>> >>> Such AER interrupt can still prevent the system from suspending, so let >>> root port mask and unmask replay timer timeout during suspend and >>> resume, respectively. >>> >>> Cc: Victor Shih <victor.shih@xxxxxxxxxxxxxxxxxxx> >>> Cc: Ben Chuang <benchuanggli@xxxxxxxxx> >>> Signed-off-by: Kai-Heng Feng <kai.heng.feng@xxxxxxxxxxxxx> >>> --- >>> v2: >>> - Change subject to reflect it works on GL9750 & GL9755 >>> - Fix when aer_cap is missing >>> >>> drivers/mmc/host/sdhci-pci-core.c | 2 +- >>> drivers/mmc/host/sdhci-pci-gli.c | 55 +++++++++++++++++++++++++++++-- >>> drivers/mmc/host/sdhci-pci.h | 1 + >>> 3 files changed, 55 insertions(+), 3 deletions(-) >>> >>> diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c >>> index 025b31aa712c..59ae4da72974 100644 >>> --- a/drivers/mmc/host/sdhci-pci-core.c >>> +++ b/drivers/mmc/host/sdhci-pci-core.c >>> @@ -68,7 +68,7 @@ static int sdhci_pci_init_wakeup(struct sdhci_pci_chip *chip) >>> return 0; >>> } >>> >>> -static int sdhci_pci_suspend_host(struct sdhci_pci_chip *chip) >>> +int sdhci_pci_suspend_host(struct sdhci_pci_chip *chip) >>> { >>> int i, ret; >>> >>> diff --git a/drivers/mmc/host/sdhci-pci-gli.c b/drivers/mmc/host/sdhci-pci-gli.c >>> index 77911a57b12c..54943e9df835 100644 >>> --- a/drivers/mmc/host/sdhci-pci-gli.c >>> +++ b/drivers/mmc/host/sdhci-pci-gli.c >>> @@ -1429,6 +1429,55 @@ static int sdhci_pci_gli_resume(struct sdhci_pci_chip *chip) >>> return sdhci_pci_resume_host(chip); >>> } >>> >>> +#ifdef CONFIG_PCIEAER >>> +static void mask_replay_timer_timeout(struct pci_dev *pdev) >>> +{ >>> + struct pci_dev *parent = pci_upstream_bridge(pdev); >>> + u32 val; >>> + >>> + if (!parent || !parent->aer_cap) >> >> Wouldn't it be more correct to use pci_aer_available(), rather than >> just checking the aer_cap? > > pci_aer_available() is more of a global check, so checking aer_cap is > still required for the device. It is not obvious whether aer_cap is meant to be used outside PCI internal code. Maybe reading the offset directly is more appropriate? aer_pos = pci_find_ext_capability(root, PCI_EXT_CAP_ID_ERR); > >> >> If pci_aer_available() can be used, we wouldn't even need the stubs as >> the is already stubs for pci_aer_available(). > > A helper that checks both aer_cap and pci_aer_available() can be > added for such purpose, but there aren't many users of that. > > Kai-Heng > >> >>> + return; >>> + >>> + pci_read_config_dword(parent, parent->aer_cap + PCI_ERR_COR_MASK, &val); >>> + val |= PCI_ERR_COR_REP_TIMER; >>> + pci_write_config_dword(parent, parent->aer_cap + PCI_ERR_COR_MASK, val); >>> +} >>> + >>> +static void unmask_replay_timer_timeout(struct pci_dev *pdev) >>> +{ >>> + struct pci_dev *parent = pci_upstream_bridge(pdev); >>> + u32 val; >>> + >>> + if (!parent || !parent->aer_cap) >>> + return; >>> + >>> + pci_read_config_dword(pdev, parent->aer_cap + PCI_ERR_COR_MASK, &val); >>> + val &= ~PCI_ERR_COR_REP_TIMER; >>> + pci_write_config_dword(pdev, parent->aer_cap + PCI_ERR_COR_MASK, val); >>> +} >>> +#else >>> +static inline void mask_replay_timer_timeout(struct pci_dev *pdev) { } >>> +static inline void unmask_replay_timer_timeout(struct pci_dev *pdev) { } >>> +#endif >>> + >>> +static int sdhci_pci_gl975x_suspend(struct sdhci_pci_chip *chip) >>> +{ >>> + mask_replay_timer_timeout(chip->pdev); >>> + >>> + return sdhci_pci_suspend_host(chip); >>> +} >>> + >>> +static int sdhci_pci_gl975x_resume(struct sdhci_pci_chip *chip) >>> +{ >>> + int ret; >>> + >>> + ret = sdhci_pci_gli_resume(chip); >>> + >>> + unmask_replay_timer_timeout(chip->pdev); >>> + >>> + return ret; >>> +} >>> + >>> static int gl9763e_resume(struct sdhci_pci_chip *chip) >>> { >>> struct sdhci_pci_slot *slot = chip->slots[0]; >>> @@ -1547,7 +1596,8 @@ const struct sdhci_pci_fixes sdhci_gl9755 = { >>> .probe_slot = gli_probe_slot_gl9755, >>> .ops = &sdhci_gl9755_ops, >>> #ifdef CONFIG_PM_SLEEP >>> - .resume = sdhci_pci_gli_resume, >>> + .suspend = sdhci_pci_gl975x_suspend, >>> + .resume = sdhci_pci_gl975x_resume, >>> #endif >>> }; >>> >>> @@ -1570,7 +1620,8 @@ const struct sdhci_pci_fixes sdhci_gl9750 = { >>> .probe_slot = gli_probe_slot_gl9750, >>> .ops = &sdhci_gl9750_ops, >>> #ifdef CONFIG_PM_SLEEP >>> - .resume = sdhci_pci_gli_resume, >>> + .suspend = sdhci_pci_gl975x_suspend, >>> + .resume = sdhci_pci_gl975x_resume, >>> #endif >>> }; >>> >>> diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h >>> index 153704f812ed..19253dce687d 100644 >>> --- a/drivers/mmc/host/sdhci-pci.h >>> +++ b/drivers/mmc/host/sdhci-pci.h >>> @@ -190,6 +190,7 @@ static inline void *sdhci_pci_priv(struct sdhci_pci_slot *slot) >>> } >>> >>> #ifdef CONFIG_PM_SLEEP >>> +int sdhci_pci_suspend_host(struct sdhci_pci_chip *chip); >>> int sdhci_pci_resume_host(struct sdhci_pci_chip *chip); >>> #endif >>> int sdhci_pci_enable_dma(struct sdhci_host *host); >> >> Kind regards >> Uffe