Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

misc: alcor_pci: Use PCI core to manage ASPM instead of open-coding

"priv->ext_config_dev_aspm" was never set to a non-zero value. Therefore,
alcor_pci_aspm_ctrl(priv, 1) did nothing, and alcor_pci_aspm_ctrl(priv, 0)
always disabled ASPM in the device and the upstream bridge.

The driver disabled ASPM in alcor_pci_probe() and alcor_resume(), so it's
possible the device doesn't work well when ASPM is enabled.

Remove all the ASPM-related code and replace the alcor_pci_aspm_ctrl(0)
calls with pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S |
PCIE_LINK_STATE_L1), which asks the PCI core to disable ASPM.

Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Cc: Oleksij Rempel <o.rempel@pengutronix.de>
Reviewed-by: Oleksij Rempel <o.rempel@pengutronix.de>
Link: https://lore.kernel.org/r/20230307213816.886308-1-helgaas@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Bjorn Helgaas and committed by
Greg Kroah-Hartman
8efc5274 b085dbf6

+4 -147
+4 -140
drivers/misc/cardreader/alcor_pci.c
··· 95 95 } 96 96 EXPORT_SYMBOL_GPL(alcor_read32be); 97 97 98 - static int alcor_pci_find_cap_offset(struct alcor_pci_priv *priv, 99 - struct pci_dev *pci) 100 - { 101 - int where; 102 - u8 val8; 103 - u32 val32; 104 - 105 - where = ALCOR_CAP_START_OFFSET; 106 - pci_read_config_byte(pci, where, &val8); 107 - if (!val8) 108 - return 0; 109 - 110 - where = (int)val8; 111 - while (1) { 112 - pci_read_config_dword(pci, where, &val32); 113 - if (val32 == 0xffffffff) { 114 - dev_dbg(priv->dev, "find_cap_offset invalid value %x.\n", 115 - val32); 116 - return 0; 117 - } 118 - 119 - if ((val32 & 0xff) == 0x10) { 120 - dev_dbg(priv->dev, "pcie cap offset: %x\n", where); 121 - return where; 122 - } 123 - 124 - if ((val32 & 0xff00) == 0x00) { 125 - dev_dbg(priv->dev, "pci_find_cap_offset invalid value %x.\n", 126 - val32); 127 - break; 128 - } 129 - where = (int)((val32 >> 8) & 0xff); 130 - } 131 - 132 - return 0; 133 - } 134 - 135 - static void alcor_pci_init_check_aspm(struct alcor_pci_priv *priv) 136 - { 137 - struct pci_dev *pci; 138 - int where; 139 - u32 val32; 140 - 141 - priv->pdev_cap_off = alcor_pci_find_cap_offset(priv, priv->pdev); 142 - /* 143 - * A device might be attached to root complex directly and 144 - * priv->parent_pdev will be NULL. In this case we don't check its 145 - * capability and disable ASPM completely. 146 - */ 147 - if (priv->parent_pdev) 148 - priv->parent_cap_off = alcor_pci_find_cap_offset(priv, 149 - priv->parent_pdev); 150 - 151 - if ((priv->pdev_cap_off == 0) || (priv->parent_cap_off == 0)) { 152 - dev_dbg(priv->dev, "pci_cap_off: %x, parent_cap_off: %x\n", 153 - priv->pdev_cap_off, priv->parent_cap_off); 154 - return; 155 - } 156 - 157 - /* link capability */ 158 - pci = priv->pdev; 159 - where = priv->pdev_cap_off + ALCOR_PCIE_LINK_CAP_OFFSET; 160 - pci_read_config_dword(pci, where, &val32); 161 - priv->pdev_aspm_cap = (u8)(val32 >> 10) & 0x03; 162 - 163 - pci = priv->parent_pdev; 164 - where = priv->parent_cap_off + ALCOR_PCIE_LINK_CAP_OFFSET; 165 - pci_read_config_dword(pci, where, &val32); 166 - priv->parent_aspm_cap = (u8)(val32 >> 10) & 0x03; 167 - 168 - if (priv->pdev_aspm_cap != priv->parent_aspm_cap) { 169 - u8 aspm_cap; 170 - 171 - dev_dbg(priv->dev, "pdev_aspm_cap: %x, parent_aspm_cap: %x\n", 172 - priv->pdev_aspm_cap, priv->parent_aspm_cap); 173 - aspm_cap = priv->pdev_aspm_cap & priv->parent_aspm_cap; 174 - priv->pdev_aspm_cap = aspm_cap; 175 - priv->parent_aspm_cap = aspm_cap; 176 - } 177 - 178 - dev_dbg(priv->dev, "ext_config_dev_aspm: %x, pdev_aspm_cap: %x\n", 179 - priv->ext_config_dev_aspm, priv->pdev_aspm_cap); 180 - priv->ext_config_dev_aspm &= priv->pdev_aspm_cap; 181 - } 182 - 183 - static void alcor_pci_aspm_ctrl(struct alcor_pci_priv *priv, u8 aspm_enable) 184 - { 185 - struct pci_dev *pci; 186 - u8 aspm_ctrl, i; 187 - int where; 188 - u32 val32; 189 - 190 - if ((!priv->pdev_cap_off) || (!priv->parent_cap_off)) { 191 - dev_dbg(priv->dev, "pci_cap_off: %x, parent_cap_off: %x\n", 192 - priv->pdev_cap_off, priv->parent_cap_off); 193 - return; 194 - } 195 - 196 - if (!priv->pdev_aspm_cap) 197 - return; 198 - 199 - aspm_ctrl = 0; 200 - if (aspm_enable) { 201 - aspm_ctrl = priv->ext_config_dev_aspm; 202 - 203 - if (!aspm_ctrl) { 204 - dev_dbg(priv->dev, "aspm_ctrl == 0\n"); 205 - return; 206 - } 207 - } 208 - 209 - for (i = 0; i < 2; i++) { 210 - 211 - if (i) { 212 - pci = priv->parent_pdev; 213 - where = priv->parent_cap_off 214 - + ALCOR_PCIE_LINK_CTRL_OFFSET; 215 - } else { 216 - pci = priv->pdev; 217 - where = priv->pdev_cap_off 218 - + ALCOR_PCIE_LINK_CTRL_OFFSET; 219 - } 220 - 221 - pci_read_config_dword(pci, where, &val32); 222 - val32 &= (~0x03); 223 - val32 |= (aspm_ctrl & priv->pdev_aspm_cap); 224 - pci_write_config_byte(pci, where, (u8)val32); 225 - } 226 - 227 - } 228 - 229 98 static inline void alcor_mask_sd_irqs(struct alcor_pci_priv *priv) 230 99 { 231 100 alcor_write32(priv, 0, AU6601_REG_INT_ENABLE); ··· 177 308 178 309 pci_set_master(pdev); 179 310 pci_set_drvdata(pdev, priv); 180 - alcor_pci_init_check_aspm(priv); 181 311 182 312 for (i = 0; i < ARRAY_SIZE(alcor_pci_cells); i++) { 183 313 alcor_pci_cells[i].platform_data = priv; ··· 187 319 if (ret < 0) 188 320 goto error_clear_drvdata; 189 321 190 - alcor_pci_aspm_ctrl(priv, 0); 322 + pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1); 191 323 192 324 return 0; 193 325 ··· 207 339 208 340 priv = pci_get_drvdata(pdev); 209 341 210 - alcor_pci_aspm_ctrl(priv, 1); 211 - 212 342 mfd_remove_devices(&pdev->dev); 213 343 214 344 ida_free(&alcor_pci_idr, priv->id); ··· 219 353 #ifdef CONFIG_PM_SLEEP 220 354 static int alcor_suspend(struct device *dev) 221 355 { 222 - struct alcor_pci_priv *priv = dev_get_drvdata(dev); 223 - 224 - alcor_pci_aspm_ctrl(priv, 1); 225 356 return 0; 226 357 } 227 358 228 359 static int alcor_resume(struct device *dev) 229 360 { 230 - 231 361 struct alcor_pci_priv *priv = dev_get_drvdata(dev); 232 362 233 - alcor_pci_aspm_ctrl(priv, 0); 363 + pci_disable_link_state(priv->pdev, 364 + PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1); 365 + 234 366 return 0; 235 367 } 236 368 #endif /* CONFIG_PM_SLEEP */
-7
include/linux/alcor_pci.h
··· 268 268 unsigned long id; /* idr id */ 269 269 270 270 struct alcor_dev_cfg *cfg; 271 - 272 - /* PCI ASPM related vars */ 273 - int pdev_cap_off; 274 - u8 pdev_aspm_cap; 275 - int parent_cap_off; 276 - u8 parent_aspm_cap; 277 - u8 ext_config_dev_aspm; 278 271 }; 279 272 280 273 void alcor_write8(struct alcor_pci_priv *priv, u8 val, unsigned int addr);