|
28 | 28 | #include "xhci.h" |
29 | 29 | #include "xhci-trace.h" |
30 | 30 |
|
| 31 | +#define PORT2_SSIC_CONFIG_REG2 0x883c |
| 32 | +#define PROG_DONE (1 << 30) |
| 33 | +#define SSIC_PORT_UNUSED (1 << 31) |
| 34 | + |
31 | 35 | /* Device for a quirk */ |
32 | 36 | #define PCI_VENDOR_ID_FRESCO_LOGIC 0x1b73 |
33 | 37 | #define PCI_DEVICE_ID_FRESCO_LOGIC_PDK 0x1000 |
@@ -177,14 +181,44 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) |
177 | 181 | } |
178 | 182 |
|
179 | 183 | /* |
| 184 | + * In some Intel xHCI controllers, in order to get D3 working, |
| 185 | + * through a vendor specific SSIC CONFIG register at offset 0x883c, |
| 186 | + * SSIC PORT need to be marked as "unused" before putting xHCI |
| 187 | + * into D3. After D3 exit, the SSIC port need to be marked as "used". |
| 188 | + * Without this change, xHCI might not enter D3 state. |
180 | 189 | * Make sure PME works on some Intel xHCI controllers by writing 1 to clear |
181 | 190 | * the Internal PME flag bit in vendor specific PMCTRL register at offset 0x80a4 |
182 | 191 | */ |
183 | | -static void xhci_pme_quirk(struct xhci_hcd *xhci) |
| 192 | +static void xhci_pme_quirk(struct usb_hcd *hcd, bool suspend) |
184 | 193 | { |
| 194 | + struct xhci_hcd *xhci = hcd_to_xhci(hcd); |
| 195 | + struct pci_dev *pdev = to_pci_dev(hcd->self.controller); |
185 | 196 | u32 val; |
186 | 197 | void __iomem *reg; |
187 | 198 |
|
| 199 | + if (pdev->vendor == PCI_VENDOR_ID_INTEL && |
| 200 | + pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI) { |
| 201 | + |
| 202 | + reg = (void __iomem *) xhci->cap_regs + PORT2_SSIC_CONFIG_REG2; |
| 203 | + |
| 204 | + /* Notify SSIC that SSIC profile programming is not done */ |
| 205 | + val = readl(reg) & ~PROG_DONE; |
| 206 | + writel(val, reg); |
| 207 | + |
| 208 | + /* Mark SSIC port as unused(suspend) or used(resume) */ |
| 209 | + val = readl(reg); |
| 210 | + if (suspend) |
| 211 | + val |= SSIC_PORT_UNUSED; |
| 212 | + else |
| 213 | + val &= ~SSIC_PORT_UNUSED; |
| 214 | + writel(val, reg); |
| 215 | + |
| 216 | + /* Notify SSIC that SSIC profile programming is done */ |
| 217 | + val = readl(reg) | PROG_DONE; |
| 218 | + writel(val, reg); |
| 219 | + readl(reg); |
| 220 | + } |
| 221 | + |
188 | 222 | reg = (void __iomem *) xhci->cap_regs + 0x80a4; |
189 | 223 | val = readl(reg); |
190 | 224 | writel(val | BIT(28), reg); |
@@ -324,7 +358,7 @@ static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) |
324 | 358 | pdev->no_d3cold = true; |
325 | 359 |
|
326 | 360 | if (xhci->quirks & XHCI_PME_STUCK_QUIRK) |
327 | | - xhci_pme_quirk(xhci); |
| 361 | + xhci_pme_quirk(hcd, true); |
328 | 362 |
|
329 | 363 | return xhci_suspend(xhci, do_wakeup); |
330 | 364 | } |
@@ -357,7 +391,7 @@ static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated) |
357 | 391 | usb_enable_intel_xhci_ports(pdev); |
358 | 392 |
|
359 | 393 | if (xhci->quirks & XHCI_PME_STUCK_QUIRK) |
360 | | - xhci_pme_quirk(xhci); |
| 394 | + xhci_pme_quirk(hcd, false); |
361 | 395 |
|
362 | 396 | retval = xhci_resume(xhci, hibernated); |
363 | 397 | return retval; |
|
0 commit comments