Merge remote-tracking branch 'origin/usb/dwc3' into usb/next
* origin/usb/dwc3: (10 commits) usb: dwc3: Add cache type configuration support usb: dwc3: Add chip-specific compatible string MLK-22675 usb: dwc3: host: disable park mode usb: dwc3: Add workaround for host mode VBUS glitch when boot usb: dwc3: Add avoiding vbus glitch happen during xhci reset ...
This commit is contained in:
commit
7866db2c15
|
@ -4,7 +4,15 @@ DWC3- USB3 CONTROLLER. Complies to the generic USB binding properties
|
|||
as described in 'usb/generic.txt'
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "snps,dwc3"
|
||||
- compatible: must be "snps,dwc3" and (if applicable) may contain a
|
||||
chip-specific compatible string in front of it to allow dwc3 driver be
|
||||
able to update cache type configuration accordingly, otherwise
|
||||
Layerscape SoC will encounter USB init failure when adding property
|
||||
dma-coherent on device tree.
|
||||
Example:
|
||||
* "fsl,layerscape-dwc3", "snps,dwc3"
|
||||
* "snps,dwc3"
|
||||
|
||||
- reg : Address and length of the register set for the device
|
||||
- interrupts: Interrupts used by the dwc3 controller.
|
||||
- clock-names: should contain "ref", "bus_early", "suspend"
|
||||
|
@ -109,6 +117,10 @@ Optional properties:
|
|||
more than one value, which means undefined length INCR burst type
|
||||
enabled. The values can be 1, 4, 8, 16, 32, 64, 128 and 256.
|
||||
|
||||
- snps,host-vbus-glitches: Power off all Root Hub ports immediately after
|
||||
setting host mode to avoid vbus (negative) glitch happen in later
|
||||
xhci reset. And the vbus will back to 5V automatically when reset done.
|
||||
|
||||
- in addition all properties from usb-xhci.txt from the current directory are
|
||||
supported as well
|
||||
|
||||
|
|
|
@ -894,6 +894,73 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc)
|
|||
dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, cfg);
|
||||
}
|
||||
|
||||
static void dwc3_set_power_down_clk_scale(struct dwc3 *dwc)
|
||||
{
|
||||
u32 reg, scale;
|
||||
|
||||
if (dwc->num_clks == 0)
|
||||
return;
|
||||
|
||||
/*
|
||||
* The power down scale field specifies how many suspend_clk
|
||||
* periods fit into a 16KHz clock period. When performing
|
||||
* the division, round up the remainder.
|
||||
*/
|
||||
scale = DIV_ROUND_UP(clk_get_rate(dwc->clks[2].clk), 16384);
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
|
||||
reg &= ~(DWC3_GCTL_PWRDNSCALE_MASK);
|
||||
reg |= DWC3_GCTL_PWRDNSCALE(scale);
|
||||
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
struct dwc3_cache_type {
|
||||
u8 transfer_type_datard;
|
||||
u8 transfer_type_descrd;
|
||||
u8 transfer_type_datawr;
|
||||
u8 transfer_type_descwr;
|
||||
};
|
||||
|
||||
static const struct dwc3_cache_type layerscape_dwc3_cache_type = {
|
||||
.transfer_type_datard = 2,
|
||||
.transfer_type_descrd = 2,
|
||||
.transfer_type_datawr = 2,
|
||||
.transfer_type_descwr = 2,
|
||||
};
|
||||
|
||||
/**
|
||||
* dwc3_set_cache_type - Configure cache type registers
|
||||
* @dwc: Pointer to our controller context structure
|
||||
*/
|
||||
static void dwc3_set_cache_type(struct dwc3 *dwc)
|
||||
{
|
||||
u32 tmp, reg;
|
||||
const struct dwc3_cache_type *cache_type =
|
||||
device_get_match_data(dwc->dev);
|
||||
|
||||
if (cache_type) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG0);
|
||||
tmp = reg;
|
||||
|
||||
reg &= ~DWC3_GSBUSCFG0_DATARD(~0);
|
||||
reg |= DWC3_GSBUSCFG0_DATARD(cache_type->transfer_type_datard);
|
||||
|
||||
reg &= ~DWC3_GSBUSCFG0_DESCRD(~0);
|
||||
reg |= DWC3_GSBUSCFG0_DESCRD(cache_type->transfer_type_descrd);
|
||||
|
||||
reg &= ~DWC3_GSBUSCFG0_DATAWR(~0);
|
||||
reg |= DWC3_GSBUSCFG0_DATAWR(cache_type->transfer_type_datawr);
|
||||
|
||||
reg &= ~DWC3_GSBUSCFG0_DESCWR(~0);
|
||||
reg |= DWC3_GSBUSCFG0_DESCWR(cache_type->transfer_type_descwr);
|
||||
|
||||
if (tmp != reg)
|
||||
dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, reg);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* dwc3_core_init - Low-level initialization of DWC3 Core
|
||||
* @dwc: Pointer to our controller context structure
|
||||
|
@ -918,6 +985,8 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
|||
dwc->maximum_speed = USB_SPEED_HIGH;
|
||||
}
|
||||
|
||||
dwc3_set_power_down_clk_scale(dwc);
|
||||
|
||||
ret = dwc3_phy_setup(dwc);
|
||||
if (ret)
|
||||
goto err0;
|
||||
|
@ -952,6 +1021,10 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
|||
|
||||
dwc3_set_incr_burst_type(dwc);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
dwc3_set_cache_type(dwc);
|
||||
#endif
|
||||
|
||||
usb_phy_set_suspend(dwc->usb2_phy, 0);
|
||||
usb_phy_set_suspend(dwc->usb3_phy, 0);
|
||||
ret = phy_power_on(dwc->usb2_generic_phy);
|
||||
|
@ -1009,6 +1082,21 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
|||
reg |= DWC3_GUCTL_HSTINAUTORETRY;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GUCTL, reg);
|
||||
|
||||
/*
|
||||
* Disable Park Mode for super speed:
|
||||
* Park mode is used in host mode when only a single async
|
||||
* endpoint is active, but which has a known issue cause
|
||||
* USB3.0 HC may die when read and write at the same time,
|
||||
* considering the advantages of this mode are minimal,
|
||||
* this issue only impacts super speed and exist on all IP
|
||||
* versions, disable it for SS, Synopsys will release a formal
|
||||
* STAR 9001415732, and disable it by default in next IP
|
||||
* release.
|
||||
*/
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUCTL1);
|
||||
reg |= DWC3_GUCTL1_PARKMODE_DISABLE_SS;
|
||||
dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1236,6 +1324,17 @@ static void dwc3_get_properties(struct dwc3 *dwc)
|
|||
|
||||
dwc->maximum_speed = usb_get_maximum_speed(dev);
|
||||
dwc->dr_mode = usb_get_dr_mode(dev);
|
||||
if (dwc->dr_mode == USB_DR_MODE_OTG) {
|
||||
dwc->otg_caps.otg_rev = 0x0300;
|
||||
dwc->otg_caps.hnp_support = true;
|
||||
dwc->otg_caps.srp_support = true;
|
||||
dwc->otg_caps.adp_support = true;
|
||||
|
||||
/* Update otg capabilities by DT properties */
|
||||
of_usb_update_otg_caps(dev->of_node,
|
||||
&dwc->otg_caps);
|
||||
}
|
||||
|
||||
dwc->hsphy_mode = of_usb_get_phy_mode(dev->of_node);
|
||||
|
||||
dwc->sysdev_is_parent = device_property_read_bool(dev,
|
||||
|
@ -1315,6 +1414,9 @@ static void dwc3_get_properties(struct dwc3 *dwc)
|
|||
dwc->dis_metastability_quirk = device_property_read_bool(dev,
|
||||
"snps,dis_metastability_quirk");
|
||||
|
||||
dwc->host_vbus_glitches = device_property_read_bool(dev,
|
||||
"snps,host-vbus-glitches");
|
||||
|
||||
dwc->lpm_nyet_threshold = lpm_nyet_threshold;
|
||||
dwc->tx_de_emphasis = tx_de_emphasis;
|
||||
|
||||
|
@ -1837,12 +1939,9 @@ static const struct dev_pm_ops dwc3_dev_pm_ops = {
|
|||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id of_dwc3_match[] = {
|
||||
{
|
||||
.compatible = "snps,dwc3"
|
||||
},
|
||||
{
|
||||
.compatible = "synopsys,dwc3"
|
||||
},
|
||||
{ .compatible = "fsl,layerscape-dwc3", .data = &layerscape_dwc3_cache_type, },
|
||||
{ .compatible = "snps,dwc3" },
|
||||
{ .compatible = "synopsys,dwc3" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_dwc3_match);
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/role.h>
|
||||
#include <linux/ulpi/interface.h>
|
||||
|
||||
#include <linux/phy/phy.h>
|
||||
|
@ -165,6 +166,21 @@
|
|||
/* Bit fields */
|
||||
|
||||
/* Global SoC Bus Configuration INCRx Register 0 */
|
||||
#ifdef CONFIG_OF
|
||||
#define DWC3_GSBUSCFG0_DATARD_SHIFT 28
|
||||
#define DWC3_GSBUSCFG0_DATARD(n) (((n) & 0xf) \
|
||||
<< DWC3_GSBUSCFG0_DATARD_SHIFT)
|
||||
#define DWC3_GSBUSCFG0_DESCRD_SHIFT 24
|
||||
#define DWC3_GSBUSCFG0_DESCRD(n) (((n) & 0xf) \
|
||||
<< DWC3_GSBUSCFG0_DESCRD_SHIFT)
|
||||
#define DWC3_GSBUSCFG0_DATAWR_SHIFT 20
|
||||
#define DWC3_GSBUSCFG0_DATAWR(n) (((n) & 0xf) \
|
||||
<< DWC3_GSBUSCFG0_DATAWR_SHIFT)
|
||||
#define DWC3_GSBUSCFG0_DESCWR_SHIFT 16
|
||||
#define DWC3_GSBUSCFG0_DESCWR(n) (((n) & 0xf) \
|
||||
<< DWC3_GSBUSCFG0_DESCWR_SHIFT)
|
||||
#endif
|
||||
|
||||
#define DWC3_GSBUSCFG0_INCR256BRSTENA (1 << 7) /* INCR256 burst */
|
||||
#define DWC3_GSBUSCFG0_INCR128BRSTENA (1 << 6) /* INCR128 burst */
|
||||
#define DWC3_GSBUSCFG0_INCR64BRSTENA (1 << 5) /* INCR64 burst */
|
||||
|
@ -223,6 +239,7 @@
|
|||
|
||||
/* Global Configuration Register */
|
||||
#define DWC3_GCTL_PWRDNSCALE(n) ((n) << 19)
|
||||
#define DWC3_GCTL_PWRDNSCALE_MASK DWC3_GCTL_PWRDNSCALE(0x1fff)
|
||||
#define DWC3_GCTL_U2RSTECN BIT(16)
|
||||
#define DWC3_GCTL_RAMCLKSEL(x) (((x) & DWC3_GCTL_CLK_MASK) << 6)
|
||||
#define DWC3_GCTL_CLK_BUS (0)
|
||||
|
@ -251,6 +268,7 @@
|
|||
/* Global User Control 1 Register */
|
||||
#define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28)
|
||||
#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24)
|
||||
#define DWC3_GUCTL1_PARKMODE_DISABLE_SS BIT(17)
|
||||
|
||||
/* Global Status Register */
|
||||
#define DWC3_GSTS_OTG_IP BIT(10)
|
||||
|
@ -941,6 +959,7 @@ struct dwc3_scratchpad_array {
|
|||
* @nr_scratch: number of scratch buffers
|
||||
* @u1u2: only used on revisions <1.83a for workaround
|
||||
* @maximum_speed: maximum speed requested (mainly for testing purposes)
|
||||
* @otg_caps: the OTG capabilities from hardware point
|
||||
* @revision: revision register contents
|
||||
* @version_type: VERSIONTYPE register contents, a sub release of a revision
|
||||
* @dr_mode: requested mode of operation
|
||||
|
@ -1029,6 +1048,8 @@ struct dwc3_scratchpad_array {
|
|||
* 2 - No de-emphasis
|
||||
* 3 - Reserved
|
||||
* @dis_metastability_quirk: set to disable metastability quirk.
|
||||
* @host_vbus_glitches: set to avoid vbus glitch during
|
||||
* xhci reset.
|
||||
* @imod_interval: set the interrupt moderation interval in 250ns
|
||||
* increments or 0 to disable.
|
||||
*/
|
||||
|
@ -1078,6 +1099,7 @@ struct dwc3 {
|
|||
void __iomem *regs;
|
||||
size_t regs_size;
|
||||
|
||||
struct usb_role_switch *role_switch;
|
||||
enum usb_dr_mode dr_mode;
|
||||
u32 current_dr_role;
|
||||
u32 desired_dr_role;
|
||||
|
@ -1094,6 +1116,7 @@ struct dwc3 {
|
|||
u32 nr_scratch;
|
||||
u32 u1u2;
|
||||
u32 maximum_speed;
|
||||
struct usb_otg_caps otg_caps;
|
||||
|
||||
/*
|
||||
* All 3.1 IP version constants are greater than the 3.0 IP
|
||||
|
@ -1218,6 +1241,7 @@ struct dwc3 {
|
|||
unsigned tx_de_emphasis:2;
|
||||
|
||||
unsigned dis_metastability_quirk:1;
|
||||
unsigned host_vbus_glitches:1;
|
||||
|
||||
u16 imod_interval;
|
||||
};
|
||||
|
|
|
@ -476,6 +476,43 @@ static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc)
|
|||
return edev;
|
||||
}
|
||||
|
||||
static int dwc3_usb_role_switch_set(struct device *dev, enum usb_role role)
|
||||
{
|
||||
u32 mode;
|
||||
|
||||
switch (role) {
|
||||
case USB_ROLE_HOST:
|
||||
mode = DWC3_GCTL_PRTCAP_HOST;
|
||||
break;
|
||||
case USB_ROLE_DEVICE:
|
||||
mode = DWC3_GCTL_PRTCAP_DEVICE;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
};
|
||||
|
||||
dwc3_set_mode(dev_get_drvdata(dev), mode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum usb_role dwc3_usb_role_switch_get(struct device *dev)
|
||||
{
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
enum usb_role role;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
role = dwc->current_dr_role;
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return role;
|
||||
}
|
||||
|
||||
static struct usb_role_switch_desc dwc3_role_switch = {
|
||||
.set = dwc3_usb_role_switch_set,
|
||||
.get = dwc3_usb_role_switch_get,
|
||||
};
|
||||
|
||||
int dwc3_drd_init(struct dwc3 *dwc)
|
||||
{
|
||||
int ret, irq;
|
||||
|
@ -484,7 +521,13 @@ int dwc3_drd_init(struct dwc3 *dwc)
|
|||
if (IS_ERR(dwc->edev))
|
||||
return PTR_ERR(dwc->edev);
|
||||
|
||||
if (dwc->edev) {
|
||||
if (device_property_read_bool(dwc->dev, "usb-role-switch")) {
|
||||
dwc3_role_switch.fwnode = dev_fwnode(dwc->dev);
|
||||
dwc->role_switch = usb_role_switch_register(dwc->dev,
|
||||
&dwc3_role_switch);
|
||||
if (IS_ERR(dwc->role_switch))
|
||||
return PTR_ERR(dwc->role_switch);
|
||||
} else if (dwc->edev) {
|
||||
dwc->edev_nb.notifier_call = dwc3_drd_notifier;
|
||||
ret = extcon_register_notifier(dwc->edev, EXTCON_USB_HOST,
|
||||
&dwc->edev_nb);
|
||||
|
@ -531,6 +574,9 @@ void dwc3_drd_exit(struct dwc3 *dwc)
|
|||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (dwc->role_switch)
|
||||
usb_role_switch_unregister(dwc->role_switch);
|
||||
|
||||
if (dwc->edev)
|
||||
extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST,
|
||||
&dwc->edev_nb);
|
||||
|
|
|
@ -270,7 +270,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
|
|||
{
|
||||
const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
u32 timeout = 1000;
|
||||
u32 timeout = 2000;
|
||||
u32 saved_config = 0;
|
||||
u32 reg;
|
||||
|
||||
|
@ -3343,6 +3343,10 @@ int dwc3_gadget_init(struct dwc3 *dwc)
|
|||
dwc->gadget.sg_supported = true;
|
||||
dwc->gadget.name = "dwc3-gadget";
|
||||
dwc->gadget.lpm_capable = true;
|
||||
dwc->gadget.is_otg = (dwc->dr_mode == USB_DR_MODE_OTG) &&
|
||||
(dwc->otg_caps.hnp_support ||
|
||||
dwc->otg_caps.srp_support ||
|
||||
dwc->otg_caps.adp_support);
|
||||
|
||||
/*
|
||||
* FIXME We might be setting max_speed to <SUPER, however versions
|
||||
|
|
|
@ -9,8 +9,49 @@
|
|||
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "../host/xhci.h"
|
||||
|
||||
#include "core.h"
|
||||
|
||||
|
||||
#define XHCI_HCSPARAMS1 0x4
|
||||
#define XHCI_PORTSC_BASE 0x400
|
||||
|
||||
/*
|
||||
* dwc3_power_off_all_roothub_ports - Power off all Root hub ports
|
||||
* @dwc3: Pointer to our controller context structure
|
||||
*/
|
||||
static void dwc3_power_off_all_roothub_ports(struct dwc3 *dwc)
|
||||
{
|
||||
int i, port_num;
|
||||
u32 reg, op_regs_base, offset;
|
||||
void __iomem *xhci_regs;
|
||||
|
||||
/* xhci regs is not mapped yet, do it temperary here */
|
||||
if (dwc->xhci_resources[0].start) {
|
||||
xhci_regs = ioremap(dwc->xhci_resources[0].start,
|
||||
DWC3_XHCI_REGS_END);
|
||||
if (IS_ERR(xhci_regs)) {
|
||||
dev_err(dwc->dev, "Failed to ioremap xhci_regs\n");
|
||||
return;
|
||||
}
|
||||
|
||||
op_regs_base = HC_LENGTH(readl(xhci_regs));
|
||||
reg = readl(xhci_regs + XHCI_HCSPARAMS1);
|
||||
port_num = HCS_MAX_PORTS(reg);
|
||||
|
||||
for (i = 1; i <= port_num; i++) {
|
||||
offset = op_regs_base + XHCI_PORTSC_BASE + 0x10*(i-1);
|
||||
reg = readl(xhci_regs + offset);
|
||||
reg &= ~PORT_POWER;
|
||||
writel(reg, xhci_regs + offset);
|
||||
}
|
||||
|
||||
iounmap(xhci_regs);
|
||||
} else
|
||||
dev_err(dwc->dev, "xhci base reg invalid\n");
|
||||
}
|
||||
|
||||
static int dwc3_host_get_irq(struct dwc3 *dwc)
|
||||
{
|
||||
struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
|
||||
|
@ -50,6 +91,13 @@ int dwc3_host_init(struct dwc3 *dwc)
|
|||
struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
|
||||
int prop_idx = 0;
|
||||
|
||||
/*
|
||||
* We have to power off all Root hub ports immediately after DWC3 set
|
||||
* to host mode to avoid VBUS glitch happen when xhci get reset later.
|
||||
*/
|
||||
if (dwc->host_vbus_glitches)
|
||||
dwc3_power_off_all_roothub_ports(dwc);
|
||||
|
||||
irq = dwc3_host_get_irq(dwc);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
|
Loading…
Reference in New Issue