summaryrefslogtreecommitdiff |
diff options
author | Petr Tesarik <ptesarik@suse.cz> | 2019-01-11 14:43:52 +0100 |
---|---|---|
committer | Petr Tesarik <ptesarik@suse.cz> | 2019-01-11 14:43:52 +0100 |
commit | b38de58973c1ff99c711ec0cad79ae8e650d1857 (patch) | |
tree | d26359f5ce0b7c49198f54cd009cc703f9ea7489 | |
parent | 55ae94187b23d2f82f8851a6e239c0f993058fc1 (diff) | |
parent | 802639a7196b8db740229b04977911e951bb375d (diff) |
Merge branch 'users/nsaenzjulienne/SLE15-SP1/for-next' into SLE15-SP1
Pull USB fixes from Nicolas Saenz
suse-commit: d0a916f8f86fdb3f4a12c6c3a470c35fbc4b9c7a
-rw-r--r-- | drivers/usb/host/xhci-pci.c | 8 | ||||
-rw-r--r-- | drivers/usb/host/xhci.c | 65 | ||||
-rw-r--r-- | drivers/usb/host/xhci.h | 5 | ||||
-rw-r--r-- | drivers/usb/typec/tcpm/tcpm.c | 35 |
4 files changed, 94 insertions, 19 deletions
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 1de006aebec5..a54ae83550e5 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -213,11 +213,15 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) xhci->quirks |= XHCI_BROKEN_STREAMS; } if (pdev->vendor == PCI_VENDOR_ID_RENESAS && - pdev->device == 0x0014) + pdev->device == 0x0014) { xhci->quirks |= XHCI_TRUST_TX_LENGTH; + xhci->quirks |= XHCI_ZERO_64B_REGS; + } if (pdev->vendor == PCI_VENDOR_ID_RENESAS && - pdev->device == 0x0015) + pdev->device == 0x0015) { xhci->quirks |= XHCI_RESET_ON_RESUME; + xhci->quirks |= XHCI_ZERO_64B_REGS; + } if (pdev->vendor == PCI_VENDOR_ID_VIA) xhci->quirks |= XHCI_RESET_ON_RESUME; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 57ddd36b02c7..d756554195b5 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -234,6 +234,68 @@ int xhci_reset(struct xhci_hcd *xhci) return ret; } +static void xhci_zero_64b_regs(struct xhci_hcd *xhci) +{ + struct device *dev = xhci_to_hcd(xhci)->self.sysdev; + int err, i; + u64 val; + + /* + * Some Renesas controllers get into a weird state if they are + * reset while programmed with 64bit addresses (they will preserve + * the top half of the address in internal, non visible + * registers). You end up with half the address coming from the + * kernel, and the other half coming from the firmware. Also, + * changing the programming leads to extra accesses even if the + * controller is supposed to be halted. The controller ends up with + * a fatal fault, and is then ripe for being properly reset. + * + * Special care is taken to only apply this if the device is behind + * an iommu. Doing anything when there is no iommu is definitely + * unsafe... + */ + if (!(xhci->quirks & XHCI_ZERO_64B_REGS) || !dev->iommu_group) + return; + + xhci_info(xhci, "Zeroing 64bit base registers, expecting fault\n"); + + /* Clear HSEIE so that faults do not get signaled */ + val = readl(&xhci->op_regs->command); + val &= ~CMD_HSEIE; + writel(val, &xhci->op_regs->command); + + /* Clear HSE (aka FATAL) */ + val = readl(&xhci->op_regs->status); + val |= STS_FATAL; + writel(val, &xhci->op_regs->status); + + /* Now zero the registers, and brace for impact */ + val = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr); + if (upper_32_bits(val)) + xhci_write_64(xhci, 0, &xhci->op_regs->dcbaa_ptr); + val = xhci_read_64(xhci, &xhci->op_regs->cmd_ring); + if (upper_32_bits(val)) + xhci_write_64(xhci, 0, &xhci->op_regs->cmd_ring); + + for (i = 0; i < HCS_MAX_INTRS(xhci->hcs_params1); i++) { + struct xhci_intr_reg __iomem *ir; + + ir = &xhci->run_regs->ir_set[i]; + val = xhci_read_64(xhci, &ir->erst_base); + if (upper_32_bits(val)) + xhci_write_64(xhci, 0, &ir->erst_base); + val= xhci_read_64(xhci, &ir->erst_dequeue); + if (upper_32_bits(val)) + xhci_write_64(xhci, 0, &ir->erst_dequeue); + } + + /* Wait for the fault to appear. It will be cleared on reset */ + err = xhci_handshake(&xhci->op_regs->status, + STS_FATAL, STS_FATAL, + XHCI_MAX_HALT_USEC); + if (!err) + xhci_info(xhci, "Fault detected\n"); +} #ifdef CONFIG_USB_PCI /* @@ -1085,6 +1147,7 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) xhci_dbg(xhci, "Stop HCD\n"); xhci_halt(xhci); + xhci_zero_64b_regs(xhci); xhci_reset(xhci); spin_unlock_irq(&xhci->lock); xhci_cleanup_msix(xhci); @@ -4938,6 +5001,8 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) if (retval) return retval; + xhci_zero_64b_regs(xhci); + xhci_dbg(xhci, "Resetting HCD\n"); /* Reset the internal HC memory state and registers. */ retval = xhci_reset(xhci); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index a222a9506cd6..515af74f4b73 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1837,6 +1837,7 @@ struct xhci_hcd { #define XHCI_ASMEDIA_MODIFY_FLOWCONTROL BIT_ULL(28) #define XHCI_RESET_PLL_ON_DISCONNECT BIT_ULL(29) #define XHCI_SUSPEND_DELAY BIT_ULL(30) +#define XHCI_ZERO_64B_REGS BIT_ULL(32) #define XHCI_SNPS_BROKEN_SUSPEND BIT_ULL(35) unsigned int num_active_eps; @@ -1857,6 +1858,8 @@ struct xhci_hcd { unsigned sw_lpm_support:1; /* support xHCI 1.0 spec USB2 hardware LPM */ unsigned hw_lpm_support:1; + /* Broken Suspend flag for SNPS Suspend resume issue */ + unsigned broken_suspend:1; /* cached usb2 extened protocol capabilites */ u32 *ext_caps; unsigned int num_ext_caps; @@ -1869,8 +1872,6 @@ struct xhci_hcd { /* platform-specific data -- must come last */ unsigned long priv[0] __aligned(sizeof(s64)); - /* Broken Suspend flag for SNPS Suspend resume issue */ - u8 broken_suspend; }; /* Platform specific overrides to generic XHCI hc_driver ops */ diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index f1a1e46fb40f..dc73ed7f2a66 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -2208,7 +2208,8 @@ static unsigned int tcpm_pd_select_pps_apdo(struct tcpm_port *port) unsigned int i, j, max_mw = 0, max_mv = 0; unsigned int min_src_mv, max_src_mv, src_ma, src_mw; unsigned int min_snk_mv, max_snk_mv; - u32 pdo; + unsigned int max_op_mv; + u32 pdo, src, snk; unsigned int src_pdo = 0, snk_pdo = 0; /* @@ -2258,16 +2259,18 @@ static unsigned int tcpm_pd_select_pps_apdo(struct tcpm_port *port) continue; } - if (max_src_mv <= max_snk_mv && - min_src_mv >= min_snk_mv) { + if (min_src_mv <= max_snk_mv && + max_src_mv >= min_snk_mv) { + max_op_mv = min(max_src_mv, max_snk_mv); + src_mw = (max_op_mv * src_ma) / 1000; /* Prefer higher voltages if available */ if ((src_mw == max_mw && - min_src_mv > max_mv) || + max_op_mv > max_mv) || src_mw > max_mw) { src_pdo = i; snk_pdo = j; max_mw = src_mw; - max_mv = max_src_mv; + max_mv = max_op_mv; } } } @@ -2280,16 +2283,18 @@ static unsigned int tcpm_pd_select_pps_apdo(struct tcpm_port *port) } if (src_pdo) { - pdo = port->source_caps[src_pdo]; - - port->pps_data.min_volt = pdo_pps_apdo_min_voltage(pdo); - port->pps_data.max_volt = pdo_pps_apdo_max_voltage(pdo); - port->pps_data.max_curr = - min_pps_apdo_current(pdo, port->snk_pdo[snk_pdo]); - port->pps_data.out_volt = - min(pdo_pps_apdo_max_voltage(pdo), port->pps_data.out_volt); - port->pps_data.op_curr = - min(port->pps_data.max_curr, port->pps_data.op_curr); + src = port->source_caps[src_pdo]; + snk = port->snk_pdo[snk_pdo]; + + port->pps_data.min_volt = max(pdo_pps_apdo_min_voltage(src), + pdo_pps_apdo_min_voltage(snk)); + port->pps_data.max_volt = min(pdo_pps_apdo_max_voltage(src), + pdo_pps_apdo_max_voltage(snk)); + port->pps_data.max_curr = min_pps_apdo_current(src, snk); + port->pps_data.out_volt = min(port->pps_data.max_volt, + port->pps_data.out_volt); + port->pps_data.op_curr = min(port->pps_data.max_curr, + port->pps_data.op_curr); } return src_pdo; |