Skip to content

Commit abce329

Browse files
RajmohanManigregkh
authored andcommitted
xhci: Workaround to get D3 working in Intel xHCI
The xHCI in Intel CherryView / Braswell Platform requires a driver workaround to get xHCI D3 working. Without this workaround, xHCI might not enter D3. Workaround is to configure SSIC PORT as "unused" before D3 entry and "used" after D3 exit. This is done through a vendor specific register (PORT2_SSIC_CONFIG_REG2 at offset 0x883c), in xhci suspend / resume callbacks. Verified xHCI D3 works fine in CherryView / Braswell platform. Signed-off-by: Rajmohan Mani <[email protected]> Signed-off-by: Mathias Nyman <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent c3c5819 commit abce329

File tree

1 file changed

+37
-3
lines changed

1 file changed

+37
-3
lines changed

drivers/usb/host/xhci-pci.c

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@
2828
#include "xhci.h"
2929
#include "xhci-trace.h"
3030

31+
#define PORT2_SSIC_CONFIG_REG2 0x883c
32+
#define PROG_DONE (1 << 30)
33+
#define SSIC_PORT_UNUSED (1 << 31)
34+
3135
/* Device for a quirk */
3236
#define PCI_VENDOR_ID_FRESCO_LOGIC 0x1b73
3337
#define PCI_DEVICE_ID_FRESCO_LOGIC_PDK 0x1000
@@ -177,14 +181,44 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
177181
}
178182

179183
/*
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.
180189
* Make sure PME works on some Intel xHCI controllers by writing 1 to clear
181190
* the Internal PME flag bit in vendor specific PMCTRL register at offset 0x80a4
182191
*/
183-
static void xhci_pme_quirk(struct xhci_hcd *xhci)
192+
static void xhci_pme_quirk(struct usb_hcd *hcd, bool suspend)
184193
{
194+
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
195+
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
185196
u32 val;
186197
void __iomem *reg;
187198

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+
188222
reg = (void __iomem *) xhci->cap_regs + 0x80a4;
189223
val = readl(reg);
190224
writel(val | BIT(28), reg);
@@ -324,7 +358,7 @@ static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
324358
pdev->no_d3cold = true;
325359

326360
if (xhci->quirks & XHCI_PME_STUCK_QUIRK)
327-
xhci_pme_quirk(xhci);
361+
xhci_pme_quirk(hcd, true);
328362

329363
return xhci_suspend(xhci, do_wakeup);
330364
}
@@ -357,7 +391,7 @@ static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated)
357391
usb_enable_intel_xhci_ports(pdev);
358392

359393
if (xhci->quirks & XHCI_PME_STUCK_QUIRK)
360-
xhci_pme_quirk(xhci);
394+
xhci_pme_quirk(hcd, false);
361395

362396
retval = xhci_resume(xhci, hibernated);
363397
return retval;

0 commit comments

Comments
 (0)