forked from Minki/linux
USB: changes for v5.2 merge window
With a total of 50 non-merge commits, this is not a large pull request. Most of the changes are, again, in dwc2 (37%) and dwc3 (32%) with the rest of it scattered among other UDCs, function drivers and device-tree bindings. No really big feature this time around apart from support to Amlogic being added to both dwc3 and dwc2 drivers. -----BEGIN PGP SIGNATURE----- iQJRBAABCAA7FiEElLzh7wn96CXwjh2IzL64meEamQYFAlzMBPsdHGZlbGlwZS5i YWxiaUBsaW51eC5pbnRlbC5jb20ACgkQzL64meEamQZZ5RAAtYw++SU5+K8n9M5m IH+dooCgKc1ZGTmqEI7PRLQBllSMqTjOIByGd9sdsqzSp+rCToYNF0St6t2e+5oU oh+El2HNYJU6OSApiQaUya9lgDFWw0O0hXrWQ131LArVfrPNSrk7TQXxL5s7wGPG M33fa48YQ+xkPQ8w2/FQWddJgBmB9qScXrCNueheI9JZuU//Wvjpfrq+lzxI50wA 7qpqVFw/fJwLXltwEogeuzHwVd+Y/nTL4EpG5iluRqzoH72VwKlCqAwRPvyi0bnY i8gZQo2K8DBQeJqr6VMfDfWowMbINs9rL1un0mR1ci1O2PSoraluZcvzrY7s7gG3 F4rf3K/6dsnsE3PIYvAfzjug8g4luuVya0V4j1X2U7lGshLY0xHe+RSbG5QPWm/p TlXHN16Cs3t/C/dc1TxyuoKzysABBPC1rQT/0GhHpKz308UFW3z8UVAK54WiaFV7 X0bpC2AI7nc7igi4YMho4PoOAUqPgvX7G7ODxQbPLSvhWi3+K4Ih1684gHr0Gopv cHj1TLh17RaQ8o35AADHEC7iDyU+tmeIVzMm+VWzrgwE3NM1ebjd7hEUoS+iUAUt MwOaCHAaCQWRUTrIjQr75OO/j+WaJwkISJODLmJGZ1Jrj7o15WUUVOEqkqLOqE1c upNsdzzva5W19t/m56Qb1AowvIw= =k1p8 -----END PGP SIGNATURE----- Merge tag 'usb-for-v5.2' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next Felipe writes: USB: changes for v5.2 merge window With a total of 50 non-merge commits, this is not a large pull request. Most of the changes are, again, in dwc2 (37%) and dwc3 (32%) with the rest of it scattered among other UDCs, function drivers and device-tree bindings. No really big feature this time around apart from support to Amlogic being added to both dwc3 and dwc2 drivers. * tag 'usb-for-v5.2' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb: (50 commits) usb: dwc3: Rename DWC3_DCTL_LPM_ERRATA usb: dwc3: Fix default lpm_nyet_threshold value usb: dwc3: debug: Print GET_STATUS(device) tracepoint usb: dwc3: Do core validation early on probe usb: dwc3: gadget: Set lpm_capable usb: gadget: atmel: tie wake lock to running clock usb: gadget: atmel: support USB suspend usb: gadget: atmel_usba_udc: simplify setting of interrupt-enabled mask dwc2: gadget: Fix completed transfer size calculation in DDMA usb: dwc2: Set lpm mode parameters depend on HW configuration usb: dwc2: Fix channel disable flow usb: dwc2: Set actual frame number for completed ISOC transfer usb: gadget: do not use __constant_cpu_to_le16 usb: dwc2: gadget: Increase descriptors count for ISOC's usb: introduce usb_ep_type_string() function usb: dwc3: move synchronize_irq() out of the spinlock protected block usb: dwc3: Free resource immediately after use usb: dwc3: of-simple: Convert to bulk clk API usb: dwc2: Delayed status support usb: gadget: udc: lpc32xx: rework interrupt handling ...
This commit is contained in:
commit
3515468a87
@ -40,3 +40,91 @@ Example device nodes:
|
||||
phy-names = "usb2-phy", "usb3-phy";
|
||||
};
|
||||
};
|
||||
|
||||
Amlogic Meson G12A DWC3 USB SoC Controller Glue
|
||||
|
||||
The Amlogic G12A embeds a DWC3 USB IP Core configured for USB2 and USB3
|
||||
in host-only mode, and a DWC2 IP Core configured for USB2 peripheral mode
|
||||
only.
|
||||
|
||||
A glue connects the DWC3 core to USB2 PHYs and optionnaly to an USB3 PHY.
|
||||
|
||||
One of the USB2 PHY can be re-routed in peripheral mode to a DWC2 USB IP.
|
||||
|
||||
The DWC3 Glue controls the PHY routing and power, an interrupt line is
|
||||
connected to the Glue to serve as OTG ID change detection.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "amlogic,meson-g12a-usb-ctrl"
|
||||
- clocks: a handle for the "USB" clock
|
||||
- resets: a handle for the shared "USB" reset line
|
||||
- reg: The base address and length of the registers
|
||||
- interrupts: the interrupt specifier for the OTG detection
|
||||
- phys: handle to used PHYs on the system
|
||||
- a <0> phandle can be used if a PHY is not used
|
||||
- phy-names: names of the used PHYs on the system :
|
||||
- "usb2-phy0" for USB2 PHY0 if USBHOST_A port is used
|
||||
- "usb2-phy1" for USB2 PHY1 if USBOTG_B port is used
|
||||
- "usb3-phy0" for USB3 PHY if USB3_0 is used
|
||||
- dr_mode: should be "host", "peripheral", or "otg" depending on
|
||||
the usage and configuration of the OTG Capable port.
|
||||
- "host" and "peripheral" means a fixed Host or Device only connection
|
||||
- "otg" means the port can be used as both Host or Device and
|
||||
be switched automatically using the OTG ID pin.
|
||||
|
||||
Optional properties:
|
||||
- vbus-supply: should be a phandle to the regulator controlling the VBUS
|
||||
power supply when used in OTG switchable mode
|
||||
|
||||
Required child nodes:
|
||||
|
||||
A child node must exist to represent the core DWC3 IP block. The name of
|
||||
the node is not important. The content of the node is defined in dwc3.txt.
|
||||
|
||||
A child node must exist to represent the core DWC2 IP block. The name of
|
||||
the node is not important. The content of the node is defined in dwc2.txt.
|
||||
|
||||
PHY documentation is provided in the following places:
|
||||
- Documentation/devicetree/bindings/phy/meson-g12a-usb2-phy.txt
|
||||
- Documentation/devicetree/bindings/phy/meson-g12a-usb3-pcie-phy.txt
|
||||
|
||||
Example device nodes:
|
||||
usb: usb@ffe09000 {
|
||||
compatible = "amlogic,meson-g12a-usb-ctrl";
|
||||
reg = <0x0 0xffe09000 0x0 0xa0>;
|
||||
interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
|
||||
clocks = <&clkc CLKID_USB>;
|
||||
resets = <&reset RESET_USB>;
|
||||
|
||||
dr_mode = "otg";
|
||||
|
||||
phys = <&usb2_phy0>, <&usb2_phy1>,
|
||||
<&usb3_pcie_phy PHY_TYPE_USB3>;
|
||||
phy-names = "usb2-phy0", "usb2-phy1", "usb3-phy0";
|
||||
|
||||
dwc2: usb@ff400000 {
|
||||
compatible = "amlogic,meson-g12a-usb", "snps,dwc2";
|
||||
reg = <0x0 0xff400000 0x0 0x40000>;
|
||||
interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clkc CLKID_USB1_DDR_BRIDGE>;
|
||||
clock-names = "ddr";
|
||||
phys = <&usb2_phy1>;
|
||||
dr_mode = "peripheral";
|
||||
g-rx-fifo-size = <192>;
|
||||
g-np-tx-fifo-size = <128>;
|
||||
g-tx-fifo-size = <128 128 16 16 16>;
|
||||
};
|
||||
|
||||
dwc3: usb@ff500000 {
|
||||
compatible = "snps,dwc3";
|
||||
reg = <0x0 0xff500000 0x0 0x100000>;
|
||||
interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
|
||||
dr_mode = "host";
|
||||
snps,dis_u2_susphy_quirk;
|
||||
snps,quirk-frame-length-adjustment;
|
||||
};
|
||||
};
|
||||
|
@ -14,6 +14,7 @@ Required properties:
|
||||
- "amlogic,meson8-usb": The DWC2 USB controller instance in Amlogic Meson8 SoCs;
|
||||
- "amlogic,meson8b-usb": The DWC2 USB controller instance in Amlogic Meson8b SoCs;
|
||||
- "amlogic,meson-gxbb-usb": The DWC2 USB controller instance in Amlogic S905 SoCs;
|
||||
- "amlogic,meson-g12a-usb": The DWC2 USB controller instance in Amlogic G12A SoCs;
|
||||
- "amcc,dwc-otg": The DWC2 USB controller instance in AMCC Canyonlands 460EX SoCs;
|
||||
- snps,dwc2: A generic DWC2 USB controller with default parameters.
|
||||
- "st,stm32f4x9-fsotg": The DWC2 USB FS/HS controller instance in STM32F4x9 SoCs
|
||||
@ -31,12 +32,18 @@ Refer to clk/clock-bindings.txt for generic clock consumer properties
|
||||
Optional properties:
|
||||
- phys: phy provider specifier
|
||||
- phy-names: shall be "usb2-phy"
|
||||
- vbus-supply: reference to the VBUS regulator. Depending on the current mode
|
||||
this is enabled (in "host" mode") or disabled (in "peripheral" mode). The
|
||||
regulator is updated if the controller is configured in "otg" mode and the
|
||||
status changes between "host" and "peripheral".
|
||||
Refer to phy/phy-bindings.txt for generic phy consumer properties
|
||||
- dr_mode: shall be one of "host", "peripheral" and "otg"
|
||||
Refer to usb/generic.txt
|
||||
- g-rx-fifo-size: size of rx fifo size in gadget mode.
|
||||
- g-np-tx-fifo-size: size of non-periodic tx fifo size in gadget mode.
|
||||
- g-tx-fifo-size: size of periodic tx fifo per endpoint (except ep0) in gadget mode.
|
||||
- snps,reset-phy-on-wake: If present indicates that we need to reset the PHY when
|
||||
we detect a wakeup. This is due to a hardware errata.
|
||||
|
||||
Deprecated properties:
|
||||
- g-use-dma: gadget DMA mode is automatically detected
|
||||
|
@ -616,6 +616,7 @@
|
||||
dr_mode = "host";
|
||||
phys = <&usbphy2>;
|
||||
phy-names = "usb2-phy";
|
||||
snps,reset-phy-on-wake;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
@ -904,6 +905,8 @@
|
||||
clocks = <&cru SCLK_OTGPHY0>;
|
||||
clock-names = "phyclk";
|
||||
#clock-cells = <0>;
|
||||
resets = <&cru SRST_USBOTG_PHY>;
|
||||
reset-names = "phy-reset";
|
||||
};
|
||||
|
||||
usbphy1: usb-phy@334 {
|
||||
@ -912,6 +915,8 @@
|
||||
clocks = <&cru SCLK_OTGPHY1>;
|
||||
clock-names = "phyclk";
|
||||
#clock-cells = <0>;
|
||||
resets = <&cru SRST_USBHOST0_PHY>;
|
||||
reset-names = "phy-reset";
|
||||
};
|
||||
|
||||
usbphy2: usb-phy@348 {
|
||||
@ -920,6 +925,8 @@
|
||||
clocks = <&cru SCLK_OTGPHY2>;
|
||||
clock-names = "phyclk";
|
||||
#clock-cells = <0>;
|
||||
resets = <&cru SRST_USBHOST1_PHY>;
|
||||
reset-names = "phy-reset";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -3174,13 +3174,14 @@ static int usb_disable_remote_wakeup(struct usb_device *udev)
|
||||
}
|
||||
|
||||
/* Count of wakeup-enabled devices at or below udev */
|
||||
static unsigned wakeup_enabled_descendants(struct usb_device *udev)
|
||||
unsigned usb_wakeup_enabled_descendants(struct usb_device *udev)
|
||||
{
|
||||
struct usb_hub *hub = usb_hub_to_struct_hub(udev);
|
||||
|
||||
return udev->do_remote_wakeup +
|
||||
(hub ? hub->wakeup_enabled_descendants : 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_wakeup_enabled_descendants);
|
||||
|
||||
/*
|
||||
* usb_port_suspend - suspend a usb device's upstream port
|
||||
@ -3282,7 +3283,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
|
||||
* Therefore we will turn on the suspend feature if udev or any of its
|
||||
* descendants is enabled for remote wakeup.
|
||||
*/
|
||||
else if (PMSG_IS_AUTO(msg) || wakeup_enabled_descendants(udev) > 0)
|
||||
else if (PMSG_IS_AUTO(msg) || usb_wakeup_enabled_descendants(udev) > 0)
|
||||
status = set_port_feature(hub->hdev, port1,
|
||||
USB_PORT_FEAT_SUSPEND);
|
||||
else {
|
||||
@ -3686,7 +3687,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
|
||||
}
|
||||
if (udev)
|
||||
hub->wakeup_enabled_descendants +=
|
||||
wakeup_enabled_descendants(udev);
|
||||
usb_wakeup_enabled_descendants(udev);
|
||||
}
|
||||
|
||||
if (hdev->do_remote_wakeup && hub->quirk_check_port_auto_suspend) {
|
||||
|
@ -1020,6 +1020,205 @@ int dwc2_hsotg_wait_bit_clear(struct dwc2_hsotg *hsotg, u32 offset, u32 mask,
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initializes the FSLSPClkSel field of the HCFG register depending on the
|
||||
* PHY type
|
||||
*/
|
||||
void dwc2_init_fs_ls_pclk_sel(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 hcfg, val;
|
||||
|
||||
if ((hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI &&
|
||||
hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED &&
|
||||
hsotg->params.ulpi_fs_ls) ||
|
||||
hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS) {
|
||||
/* Full speed PHY */
|
||||
val = HCFG_FSLSPCLKSEL_48_MHZ;
|
||||
} else {
|
||||
/* High speed PHY running at full speed or high speed */
|
||||
val = HCFG_FSLSPCLKSEL_30_60_MHZ;
|
||||
}
|
||||
|
||||
dev_dbg(hsotg->dev, "Initializing HCFG.FSLSPClkSel to %08x\n", val);
|
||||
hcfg = dwc2_readl(hsotg, HCFG);
|
||||
hcfg &= ~HCFG_FSLSPCLKSEL_MASK;
|
||||
hcfg |= val << HCFG_FSLSPCLKSEL_SHIFT;
|
||||
dwc2_writel(hsotg, hcfg, HCFG);
|
||||
}
|
||||
|
||||
static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
|
||||
{
|
||||
u32 usbcfg, ggpio, i2cctl;
|
||||
int retval = 0;
|
||||
|
||||
/*
|
||||
* core_init() is now called on every switch so only call the
|
||||
* following for the first time through
|
||||
*/
|
||||
if (select_phy) {
|
||||
dev_dbg(hsotg->dev, "FS PHY selected\n");
|
||||
|
||||
usbcfg = dwc2_readl(hsotg, GUSBCFG);
|
||||
if (!(usbcfg & GUSBCFG_PHYSEL)) {
|
||||
usbcfg |= GUSBCFG_PHYSEL;
|
||||
dwc2_writel(hsotg, usbcfg, GUSBCFG);
|
||||
|
||||
/* Reset after a PHY select */
|
||||
retval = dwc2_core_reset(hsotg, false);
|
||||
|
||||
if (retval) {
|
||||
dev_err(hsotg->dev,
|
||||
"%s: Reset failed, aborting", __func__);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
if (hsotg->params.activate_stm_fs_transceiver) {
|
||||
ggpio = dwc2_readl(hsotg, GGPIO);
|
||||
if (!(ggpio & GGPIO_STM32_OTG_GCCFG_PWRDWN)) {
|
||||
dev_dbg(hsotg->dev, "Activating transceiver\n");
|
||||
/*
|
||||
* STM32F4x9 uses the GGPIO register as general
|
||||
* core configuration register.
|
||||
*/
|
||||
ggpio |= GGPIO_STM32_OTG_GCCFG_PWRDWN;
|
||||
dwc2_writel(hsotg, ggpio, GGPIO);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Program DCFG.DevSpd or HCFG.FSLSPclkSel to 48Mhz in FS. Also
|
||||
* do this on HNP Dev/Host mode switches (done in dev_init and
|
||||
* host_init).
|
||||
*/
|
||||
if (dwc2_is_host_mode(hsotg))
|
||||
dwc2_init_fs_ls_pclk_sel(hsotg);
|
||||
|
||||
if (hsotg->params.i2c_enable) {
|
||||
dev_dbg(hsotg->dev, "FS PHY enabling I2C\n");
|
||||
|
||||
/* Program GUSBCFG.OtgUtmiFsSel to I2C */
|
||||
usbcfg = dwc2_readl(hsotg, GUSBCFG);
|
||||
usbcfg |= GUSBCFG_OTG_UTMI_FS_SEL;
|
||||
dwc2_writel(hsotg, usbcfg, GUSBCFG);
|
||||
|
||||
/* Program GI2CCTL.I2CEn */
|
||||
i2cctl = dwc2_readl(hsotg, GI2CCTL);
|
||||
i2cctl &= ~GI2CCTL_I2CDEVADDR_MASK;
|
||||
i2cctl |= 1 << GI2CCTL_I2CDEVADDR_SHIFT;
|
||||
i2cctl &= ~GI2CCTL_I2CEN;
|
||||
dwc2_writel(hsotg, i2cctl, GI2CCTL);
|
||||
i2cctl |= GI2CCTL_I2CEN;
|
||||
dwc2_writel(hsotg, i2cctl, GI2CCTL);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
|
||||
{
|
||||
u32 usbcfg, usbcfg_old;
|
||||
int retval = 0;
|
||||
|
||||
if (!select_phy)
|
||||
return 0;
|
||||
|
||||
usbcfg = dwc2_readl(hsotg, GUSBCFG);
|
||||
usbcfg_old = usbcfg;
|
||||
|
||||
/*
|
||||
* HS PHY parameters. These parameters are preserved during soft reset
|
||||
* so only program the first time. Do a soft reset immediately after
|
||||
* setting phyif.
|
||||
*/
|
||||
switch (hsotg->params.phy_type) {
|
||||
case DWC2_PHY_TYPE_PARAM_ULPI:
|
||||
/* ULPI interface */
|
||||
dev_dbg(hsotg->dev, "HS ULPI PHY selected\n");
|
||||
usbcfg |= GUSBCFG_ULPI_UTMI_SEL;
|
||||
usbcfg &= ~(GUSBCFG_PHYIF16 | GUSBCFG_DDRSEL);
|
||||
if (hsotg->params.phy_ulpi_ddr)
|
||||
usbcfg |= GUSBCFG_DDRSEL;
|
||||
|
||||
/* Set external VBUS indicator as needed. */
|
||||
if (hsotg->params.oc_disable)
|
||||
usbcfg |= (GUSBCFG_ULPI_INT_VBUS_IND |
|
||||
GUSBCFG_INDICATORPASSTHROUGH);
|
||||
break;
|
||||
case DWC2_PHY_TYPE_PARAM_UTMI:
|
||||
/* UTMI+ interface */
|
||||
dev_dbg(hsotg->dev, "HS UTMI+ PHY selected\n");
|
||||
usbcfg &= ~(GUSBCFG_ULPI_UTMI_SEL | GUSBCFG_PHYIF16);
|
||||
if (hsotg->params.phy_utmi_width == 16)
|
||||
usbcfg |= GUSBCFG_PHYIF16;
|
||||
|
||||
/* Set turnaround time */
|
||||
if (dwc2_is_device_mode(hsotg)) {
|
||||
usbcfg &= ~GUSBCFG_USBTRDTIM_MASK;
|
||||
if (hsotg->params.phy_utmi_width == 16)
|
||||
usbcfg |= 5 << GUSBCFG_USBTRDTIM_SHIFT;
|
||||
else
|
||||
usbcfg |= 9 << GUSBCFG_USBTRDTIM_SHIFT;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
dev_err(hsotg->dev, "FS PHY selected at HS!\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (usbcfg != usbcfg_old) {
|
||||
dwc2_writel(hsotg, usbcfg, GUSBCFG);
|
||||
|
||||
/* Reset after setting the PHY parameters */
|
||||
retval = dwc2_core_reset(hsotg, false);
|
||||
if (retval) {
|
||||
dev_err(hsotg->dev,
|
||||
"%s: Reset failed, aborting", __func__);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
int dwc2_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
|
||||
{
|
||||
u32 usbcfg;
|
||||
int retval = 0;
|
||||
|
||||
if ((hsotg->params.speed == DWC2_SPEED_PARAM_FULL ||
|
||||
hsotg->params.speed == DWC2_SPEED_PARAM_LOW) &&
|
||||
hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS) {
|
||||
/* If FS/LS mode with FS/LS PHY */
|
||||
retval = dwc2_fs_phy_init(hsotg, select_phy);
|
||||
if (retval)
|
||||
return retval;
|
||||
} else {
|
||||
/* High speed PHY */
|
||||
retval = dwc2_hs_phy_init(hsotg, select_phy);
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI &&
|
||||
hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED &&
|
||||
hsotg->params.ulpi_fs_ls) {
|
||||
dev_dbg(hsotg->dev, "Setting ULPI FSLS\n");
|
||||
usbcfg = dwc2_readl(hsotg, GUSBCFG);
|
||||
usbcfg |= GUSBCFG_ULPI_FS_LS;
|
||||
usbcfg |= GUSBCFG_ULPI_CLK_SUSP_M;
|
||||
dwc2_writel(hsotg, usbcfg, GUSBCFG);
|
||||
} else {
|
||||
usbcfg = dwc2_readl(hsotg, GUSBCFG);
|
||||
usbcfg &= ~GUSBCFG_ULPI_FS_LS;
|
||||
usbcfg &= ~GUSBCFG_ULPI_CLK_SUSP_M;
|
||||
dwc2_writel(hsotg, usbcfg, GUSBCFG);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
MODULE_DESCRIPTION("DESIGNWARE HS OTG Core");
|
||||
MODULE_AUTHOR("Synopsys, Inc.");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
|
@ -859,6 +859,8 @@ struct dwc2_hregs_backup {
|
||||
* @gadget_enabled: Peripheral mode sub-driver initialization indicator.
|
||||
* @ll_hw_enabled: Status of low-level hardware resources.
|
||||
* @hibernated: True if core is hibernated
|
||||
* @reset_phy_on_wake: Quirk saying that we should assert PHY reset on a
|
||||
* remote wakeup.
|
||||
* @frame_number: Frame number read from the core. For both device
|
||||
* and host modes. The value ranges are from 0
|
||||
* to HFNUM_MAX_FRNUM.
|
||||
@ -869,7 +871,6 @@ struct dwc2_hregs_backup {
|
||||
* removed once all SoCs support usb transceiver.
|
||||
* @supplies: Definition of USB power supplies
|
||||
* @vbus_supply: Regulator supplying vbus.
|
||||
* @phyif: PHY interface width
|
||||
* @lock: Spinlock that protects all the driver data structures
|
||||
* @priv: Stores a pointer to the struct usb_hcd
|
||||
* @queuing_high_bandwidth: True if multiple packets of a high-bandwidth
|
||||
@ -972,6 +973,7 @@ struct dwc2_hregs_backup {
|
||||
* @status_buf_dma: DMA address for status_buf
|
||||
* @start_work: Delayed work for handling host A-cable connection
|
||||
* @reset_work: Delayed work for handling a port reset
|
||||
* @phy_reset_work: Work structure for doing a PHY reset
|
||||
* @otg_port: OTG port number
|
||||
* @frame_list: Frame list
|
||||
* @frame_list_dma: Frame list DMA address
|
||||
@ -991,6 +993,7 @@ struct dwc2_hregs_backup {
|
||||
* @ctrl_buff: Buffer for EP0 control requests.
|
||||
* @ctrl_req: Request for EP0 control packets.
|
||||
* @ep0_state: EP0 control transfers state
|
||||
* @delayed_status: true when gadget driver asks for delayed status
|
||||
* @test_mode: USB test mode requested by the host
|
||||
* @remote_wakeup_allowed: True if device is allowed to wake-up host by
|
||||
* remote-wakeup signalling
|
||||
@ -1045,6 +1048,7 @@ struct dwc2_hsotg {
|
||||
unsigned int gadget_enabled:1;
|
||||
unsigned int ll_hw_enabled:1;
|
||||
unsigned int hibernated:1;
|
||||
unsigned int reset_phy_on_wake:1;
|
||||
u16 frame_number;
|
||||
|
||||
struct phy *phy;
|
||||
@ -1052,7 +1056,6 @@ struct dwc2_hsotg {
|
||||
struct dwc2_hsotg_plat *plat;
|
||||
struct regulator_bulk_data supplies[DWC2_NUM_SUPPLIES];
|
||||
struct regulator *vbus_supply;
|
||||
u32 phyif;
|
||||
|
||||
spinlock_t lock;
|
||||
void *priv;
|
||||
@ -1147,6 +1150,7 @@ struct dwc2_hsotg {
|
||||
|
||||
struct delayed_work start_work;
|
||||
struct delayed_work reset_work;
|
||||
struct work_struct phy_reset_work;
|
||||
u8 otg_port;
|
||||
u32 *frame_list;
|
||||
dma_addr_t frame_list_dma;
|
||||
@ -1172,6 +1176,7 @@ struct dwc2_hsotg {
|
||||
void *ep0_buff;
|
||||
void *ctrl_buff;
|
||||
enum dwc2_ep0_state ep0_state;
|
||||
unsigned delayed_status : 1;
|
||||
u8 test_mode;
|
||||
|
||||
dma_addr_t setup_desc_dma[2];
|
||||
@ -1283,6 +1288,8 @@ int dwc2_exit_partial_power_down(struct dwc2_hsotg *hsotg, bool restore);
|
||||
int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg, int is_host);
|
||||
int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup,
|
||||
int reset, int is_host);
|
||||
void dwc2_init_fs_ls_pclk_sel(struct dwc2_hsotg *hsotg);
|
||||
int dwc2_phy_init(struct dwc2_hsotg *hsotg, bool select_phy);
|
||||
|
||||
void dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host);
|
||||
void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg);
|
||||
@ -1431,6 +1438,8 @@ int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg);
|
||||
int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg);
|
||||
int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg,
|
||||
int rem_wakeup, int reset);
|
||||
static inline void dwc2_host_schedule_phy_reset(struct dwc2_hsotg *hsotg)
|
||||
{ schedule_work(&hsotg->phy_reset_work); }
|
||||
#else
|
||||
static inline int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg)
|
||||
{ return 0; }
|
||||
@ -1454,6 +1463,7 @@ static inline int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg)
|
||||
static inline int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg,
|
||||
int rem_wakeup, int reset)
|
||||
{ return 0; }
|
||||
static inline void dwc2_host_schedule_phy_reset(struct dwc2_hsotg *hsotg) {}
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -435,6 +435,18 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
|
||||
/* Restart the Phy Clock */
|
||||
pcgcctl &= ~PCGCTL_STOPPCLK;
|
||||
dwc2_writel(hsotg, pcgcctl, PCGCTL);
|
||||
|
||||
/*
|
||||
* If we've got this quirk then the PHY is stuck upon
|
||||
* wakeup. Assert reset. This will propagate out and
|
||||
* eventually we'll re-enumerate the device. Not great
|
||||
* but the best we can do. We can't call phy_reset()
|
||||
* at interrupt time but there's no hurry, so we'll
|
||||
* schedule it for later.
|
||||
*/
|
||||
if (hsotg->reset_phy_on_wake)
|
||||
dwc2_host_schedule_phy_reset(hsotg);
|
||||
|
||||
mod_timer(&hsotg->wkp_timer,
|
||||
jiffies + msecs_to_jiffies(71));
|
||||
} else {
|
||||
|
@ -27,6 +27,8 @@
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/phy.h>
|
||||
#include <linux/usb/composite.h>
|
||||
|
||||
|
||||
#include "core.h"
|
||||
#include "hw.h"
|
||||
@ -714,13 +716,11 @@ static unsigned int dwc2_gadget_get_chain_limit(struct dwc2_hsotg_ep *hs_ep)
|
||||
unsigned int maxsize;
|
||||
|
||||
if (is_isoc)
|
||||
maxsize = hs_ep->dir_in ? DEV_DMA_ISOC_TX_NBYTES_LIMIT :
|
||||
DEV_DMA_ISOC_RX_NBYTES_LIMIT;
|
||||
maxsize = (hs_ep->dir_in ? DEV_DMA_ISOC_TX_NBYTES_LIMIT :
|
||||
DEV_DMA_ISOC_RX_NBYTES_LIMIT) *
|
||||
MAX_DMA_DESC_NUM_HS_ISOC;
|
||||
else
|
||||
maxsize = DEV_DMA_NBYTES_LIMIT;
|
||||
|
||||
/* Above size of one descriptor was chosen, multiple it */
|
||||
maxsize *= MAX_DMA_DESC_NUM_GENERIC;
|
||||
maxsize = DEV_DMA_NBYTES_LIMIT * MAX_DMA_DESC_NUM_GENERIC;
|
||||
|
||||
return maxsize;
|
||||
}
|
||||
@ -932,7 +932,7 @@ static int dwc2_gadget_fill_isoc_desc(struct dwc2_hsotg_ep *hs_ep,
|
||||
|
||||
/* Update index of last configured entry in the chain */
|
||||
hs_ep->next_desc++;
|
||||
if (hs_ep->next_desc >= MAX_DMA_DESC_NUM_GENERIC)
|
||||
if (hs_ep->next_desc >= MAX_DMA_DESC_NUM_HS_ISOC)
|
||||
hs_ep->next_desc = 0;
|
||||
|
||||
return 0;
|
||||
@ -964,7 +964,7 @@ static void dwc2_gadget_start_isoc_ddma(struct dwc2_hsotg_ep *hs_ep)
|
||||
}
|
||||
|
||||
/* Initialize descriptor chain by Host Busy status */
|
||||
for (i = 0; i < MAX_DMA_DESC_NUM_GENERIC; i++) {
|
||||
for (i = 0; i < MAX_DMA_DESC_NUM_HS_ISOC; i++) {
|
||||
desc = &hs_ep->desc_list[i];
|
||||
desc->status = 0;
|
||||
desc->status |= (DEV_DMA_BUFF_STS_HBUSY
|
||||
@ -1446,6 +1446,11 @@ static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Change EP direction if status phase request is after data out */
|
||||
if (!hs_ep->index && !req->length && !hs_ep->dir_in &&
|
||||
hs->ep0_state == DWC2_EP0_DATA_OUT)
|
||||
hs_ep->dir_in = 1;
|
||||
|
||||
if (first) {
|
||||
if (!hs_ep->isochronous) {
|
||||
dwc2_hsotg_start_req(hs, hs_ep, hs_req, false);
|
||||
@ -1938,6 +1943,10 @@ static void dwc2_hsotg_process_control(struct dwc2_hsotg *hsotg,
|
||||
dev_dbg(hsotg->dev, "driver->setup() ret %d\n", ret);
|
||||
}
|
||||
|
||||
hsotg->delayed_status = false;
|
||||
if (ret == USB_GADGET_DELAYED_STATUS)
|
||||
hsotg->delayed_status = true;
|
||||
|
||||
/*
|
||||
* the request is either unhandlable, or is not formatted correctly
|
||||
* so respond with a STALL for the status stage to indicate failure.
|
||||
@ -2157,12 +2166,17 @@ static void dwc2_gadget_complete_isoc_request_ddma(struct dwc2_hsotg_ep *hs_ep)
|
||||
*/
|
||||
if (!hs_ep->dir_in && ureq->length & 0x3)
|
||||
ureq->actual += 4 - (ureq->length & 0x3);
|
||||
|
||||
/* Set actual frame number for completed transfers */
|
||||
ureq->frame_number =
|
||||
(desc_sts & DEV_DMA_ISOC_FRNUM_MASK) >>
|
||||
DEV_DMA_ISOC_FRNUM_SHIFT;
|
||||
}
|
||||
|
||||
dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
|
||||
|
||||
hs_ep->compl_desc++;
|
||||
if (hs_ep->compl_desc > (MAX_DMA_DESC_NUM_GENERIC - 1))
|
||||
if (hs_ep->compl_desc > (MAX_DMA_DESC_NUM_HS_ISOC - 1))
|
||||
hs_ep->compl_desc = 0;
|
||||
desc_sts = hs_ep->desc_list[hs_ep->compl_desc].status;
|
||||
}
|
||||
@ -2311,6 +2325,7 @@ static unsigned int dwc2_gadget_get_xfersize_ddma(struct dwc2_hsotg_ep *hs_ep)
|
||||
if (status & DEV_DMA_STS_MASK)
|
||||
dev_err(hsotg->dev, "descriptor %d closed with %x\n",
|
||||
i, status & DEV_DMA_STS_MASK);
|
||||
desc++;
|
||||
}
|
||||
|
||||
return bytes_rem;
|
||||
@ -2387,8 +2402,8 @@ static void dwc2_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, int epnum)
|
||||
if (!using_desc_dma(hsotg) && epnum == 0 &&
|
||||
hsotg->ep0_state == DWC2_EP0_DATA_OUT) {
|
||||
/* Move to STATUS IN */
|
||||
dwc2_hsotg_ep0_zlp(hsotg, true);
|
||||
return;
|
||||
if (!hsotg->delayed_status)
|
||||
dwc2_hsotg_ep0_zlp(hsotg, true);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -3053,8 +3068,20 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
|
||||
/* Safety check EP0 state when STSPHSERCVD asserted */
|
||||
if (hsotg->ep0_state == DWC2_EP0_DATA_OUT) {
|
||||
/* Move to STATUS IN for DDMA */
|
||||
if (using_desc_dma(hsotg))
|
||||
dwc2_hsotg_ep0_zlp(hsotg, true);
|
||||
if (using_desc_dma(hsotg)) {
|
||||
if (!hsotg->delayed_status)
|
||||
dwc2_hsotg_ep0_zlp(hsotg, true);
|
||||
else
|
||||
/* In case of 3 stage Control Write with delayed
|
||||
* status, when Status IN transfer started
|
||||
* before STSPHSERCVD asserted, NAKSTS bit not
|
||||
* cleared by CNAK in dwc2_hsotg_start_req()
|
||||
* function. Clear now NAKSTS to allow complete
|
||||
* transfer.
|
||||
*/
|
||||
dwc2_set_bit(hsotg, DIEPCTL(0),
|
||||
DXEPCTL_CNAK);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -3314,21 +3341,14 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
|
||||
|
||||
/* keep other bits untouched (so e.g. forced modes are not lost) */
|
||||
usbcfg = dwc2_readl(hsotg, GUSBCFG);
|
||||
usbcfg &= ~(GUSBCFG_TOUTCAL_MASK | GUSBCFG_PHYIF16 | GUSBCFG_SRPCAP |
|
||||
GUSBCFG_HNPCAP | GUSBCFG_USBTRDTIM_MASK);
|
||||
usbcfg &= ~GUSBCFG_TOUTCAL_MASK;
|
||||
usbcfg |= GUSBCFG_TOUTCAL(7);
|
||||
|
||||
if (hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS &&
|
||||
(hsotg->params.speed == DWC2_SPEED_PARAM_FULL ||
|
||||
hsotg->params.speed == DWC2_SPEED_PARAM_LOW)) {
|
||||
/* FS/LS Dedicated Transceiver Interface */
|
||||
usbcfg |= GUSBCFG_PHYSEL;
|
||||
} else {
|
||||
/* set the PLL on, remove the HNP/SRP and set the PHY */
|
||||
val = (hsotg->phyif == GUSBCFG_PHYIF8) ? 9 : 5;
|
||||
usbcfg |= hsotg->phyif | GUSBCFG_TOUTCAL(7) |
|
||||
(val << GUSBCFG_USBTRDTIM_SHIFT);
|
||||
}
|
||||
dwc2_writel(hsotg, usbcfg, GUSBCFG);
|
||||
/* remove the HNP/SRP and set the PHY */
|
||||
usbcfg &= ~(GUSBCFG_SRPCAP | GUSBCFG_HNPCAP);
|
||||
dwc2_writel(hsotg, usbcfg, GUSBCFG);
|
||||
|
||||
dwc2_phy_init(hsotg, true);
|
||||
|
||||
dwc2_hsotg_init_fifo(hsotg);
|
||||
|
||||
@ -3899,6 +3919,7 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep,
|
||||
unsigned int i, val, size;
|
||||
int ret = 0;
|
||||
unsigned char ep_type;
|
||||
int desc_num;
|
||||
|
||||
dev_dbg(hsotg->dev,
|
||||
"%s: ep %s: a 0x%02x, attr 0x%02x, mps 0x%04x, intr %d\n",
|
||||
@ -3945,11 +3966,15 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep,
|
||||
dev_dbg(hsotg->dev, "%s: read DxEPCTL=0x%08x from 0x%08x\n",
|
||||
__func__, epctrl, epctrl_reg);
|
||||
|
||||
if (using_desc_dma(hsotg) && ep_type == USB_ENDPOINT_XFER_ISOC)
|
||||
desc_num = MAX_DMA_DESC_NUM_HS_ISOC;
|
||||
else
|
||||
desc_num = MAX_DMA_DESC_NUM_GENERIC;
|
||||
|
||||
/* Allocate DMA descriptor chain for non-ctrl endpoints */
|
||||
if (using_desc_dma(hsotg) && !hs_ep->desc_list) {
|
||||
hs_ep->desc_list = dmam_alloc_coherent(hsotg->dev,
|
||||
MAX_DMA_DESC_NUM_GENERIC *
|
||||
sizeof(struct dwc2_dma_desc),
|
||||
desc_num * sizeof(struct dwc2_dma_desc),
|
||||
&hs_ep->desc_list_dma, GFP_ATOMIC);
|
||||
if (!hs_ep->desc_list) {
|
||||
ret = -ENOMEM;
|
||||
@ -4092,7 +4117,7 @@ error1:
|
||||
|
||||
error2:
|
||||
if (ret && using_desc_dma(hsotg) && hs_ep->desc_list) {
|
||||
dmam_free_coherent(hsotg->dev, MAX_DMA_DESC_NUM_GENERIC *
|
||||
dmam_free_coherent(hsotg->dev, desc_num *
|
||||
sizeof(struct dwc2_dma_desc),
|
||||
hs_ep->desc_list, hs_ep->desc_list_dma);
|
||||
hs_ep->desc_list = NULL;
|
||||
@ -4328,8 +4353,6 @@ static const struct usb_ep_ops dwc2_hsotg_ep_ops = {
|
||||
*/
|
||||
static void dwc2_hsotg_init(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 trdtim;
|
||||
u32 usbcfg;
|
||||
/* unmask subset of endpoint interrupts */
|
||||
|
||||
dwc2_writel(hsotg, DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK |
|
||||
@ -4353,17 +4376,6 @@ static void dwc2_hsotg_init(struct dwc2_hsotg *hsotg)
|
||||
|
||||
dwc2_hsotg_init_fifo(hsotg);
|
||||
|
||||
/* keep other bits untouched (so e.g. forced modes are not lost) */
|
||||
usbcfg = dwc2_readl(hsotg, GUSBCFG);
|
||||
usbcfg &= ~(GUSBCFG_TOUTCAL_MASK | GUSBCFG_PHYIF16 | GUSBCFG_SRPCAP |
|
||||
GUSBCFG_HNPCAP | GUSBCFG_USBTRDTIM_MASK);
|
||||
|
||||
/* set the PLL on, remove the HNP/SRP and set the PHY */
|
||||
trdtim = (hsotg->phyif == GUSBCFG_PHYIF8) ? 9 : 5;
|
||||
usbcfg |= hsotg->phyif | GUSBCFG_TOUTCAL(7) |
|
||||
(trdtim << GUSBCFG_USBTRDTIM_SHIFT);
|
||||
dwc2_writel(hsotg, usbcfg, GUSBCFG);
|
||||
|
||||
if (using_dma(hsotg))
|
||||
dwc2_set_bit(hsotg, GAHBCFG, GAHBCFG_DMA_EN);
|
||||
}
|
||||
@ -5073,6 +5085,7 @@ void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg)
|
||||
val |= hsotg->params.lpm_clock_gating ? GLPMCFG_ENBLSLPM : 0;
|
||||
val |= hsotg->params.hird_threshold << GLPMCFG_HIRD_THRES_SHIFT;
|
||||
val |= hsotg->params.besl ? GLPMCFG_ENBESL : 0;
|
||||
val |= GLPMCFG_LPM_REJECT_CTRL_CONTROL;
|
||||
val |= GLPMCFG_LPM_ACCEPT_CTRL_ISOC;
|
||||
dwc2_writel(hsotg, val, GLPMCFG);
|
||||
dev_dbg(hsotg->dev, "GLPMCFG=0x%08x\n", dwc2_readl(hsotg, GLPMCFG));
|
||||
|
@ -97,196 +97,6 @@ static void dwc2_enable_common_interrupts(struct dwc2_hsotg *hsotg)
|
||||
dwc2_writel(hsotg, intmsk, GINTMSK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initializes the FSLSPClkSel field of the HCFG register depending on the
|
||||
* PHY type
|
||||
*/
|
||||
static void dwc2_init_fs_ls_pclk_sel(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 hcfg, val;
|
||||
|
||||
if ((hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI &&
|
||||
hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED &&
|
||||
hsotg->params.ulpi_fs_ls) ||
|
||||
hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS) {
|
||||
/* Full speed PHY */
|
||||
val = HCFG_FSLSPCLKSEL_48_MHZ;
|
||||
} else {
|
||||
/* High speed PHY running at full speed or high speed */
|
||||
val = HCFG_FSLSPCLKSEL_30_60_MHZ;
|
||||
}
|
||||
|
||||
dev_dbg(hsotg->dev, "Initializing HCFG.FSLSPClkSel to %08x\n", val);
|
||||
hcfg = dwc2_readl(hsotg, HCFG);
|
||||
hcfg &= ~HCFG_FSLSPCLKSEL_MASK;
|
||||
hcfg |= val << HCFG_FSLSPCLKSEL_SHIFT;
|
||||
dwc2_writel(hsotg, hcfg, HCFG);
|
||||
}
|
||||
|
||||
static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
|
||||
{
|
||||
u32 usbcfg, ggpio, i2cctl;
|
||||
int retval = 0;
|
||||
|
||||
/*
|
||||
* core_init() is now called on every switch so only call the
|
||||
* following for the first time through
|
||||
*/
|
||||
if (select_phy) {
|
||||
dev_dbg(hsotg->dev, "FS PHY selected\n");
|
||||
|
||||
usbcfg = dwc2_readl(hsotg, GUSBCFG);
|
||||
if (!(usbcfg & GUSBCFG_PHYSEL)) {
|
||||
usbcfg |= GUSBCFG_PHYSEL;
|
||||
dwc2_writel(hsotg, usbcfg, GUSBCFG);
|
||||
|
||||
/* Reset after a PHY select */
|
||||
retval = dwc2_core_reset(hsotg, false);
|
||||
|
||||
if (retval) {
|
||||
dev_err(hsotg->dev,
|
||||
"%s: Reset failed, aborting", __func__);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
if (hsotg->params.activate_stm_fs_transceiver) {
|
||||
ggpio = dwc2_readl(hsotg, GGPIO);
|
||||
if (!(ggpio & GGPIO_STM32_OTG_GCCFG_PWRDWN)) {
|
||||
dev_dbg(hsotg->dev, "Activating transceiver\n");
|
||||
/*
|
||||
* STM32F4x9 uses the GGPIO register as general
|
||||
* core configuration register.
|
||||
*/
|
||||
ggpio |= GGPIO_STM32_OTG_GCCFG_PWRDWN;
|
||||
dwc2_writel(hsotg, ggpio, GGPIO);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Program DCFG.DevSpd or HCFG.FSLSPclkSel to 48Mhz in FS. Also
|
||||
* do this on HNP Dev/Host mode switches (done in dev_init and
|
||||
* host_init).
|
||||
*/
|
||||
if (dwc2_is_host_mode(hsotg))
|
||||
dwc2_init_fs_ls_pclk_sel(hsotg);
|
||||
|
||||
if (hsotg->params.i2c_enable) {
|
||||
dev_dbg(hsotg->dev, "FS PHY enabling I2C\n");
|
||||
|
||||
/* Program GUSBCFG.OtgUtmiFsSel to I2C */
|
||||
usbcfg = dwc2_readl(hsotg, GUSBCFG);
|
||||
usbcfg |= GUSBCFG_OTG_UTMI_FS_SEL;
|
||||
dwc2_writel(hsotg, usbcfg, GUSBCFG);
|
||||
|
||||
/* Program GI2CCTL.I2CEn */
|
||||
i2cctl = dwc2_readl(hsotg, GI2CCTL);
|
||||
i2cctl &= ~GI2CCTL_I2CDEVADDR_MASK;
|
||||
i2cctl |= 1 << GI2CCTL_I2CDEVADDR_SHIFT;
|
||||
i2cctl &= ~GI2CCTL_I2CEN;
|
||||
dwc2_writel(hsotg, i2cctl, GI2CCTL);
|
||||
i2cctl |= GI2CCTL_I2CEN;
|
||||
dwc2_writel(hsotg, i2cctl, GI2CCTL);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
|
||||
{
|
||||
u32 usbcfg, usbcfg_old;
|
||||
int retval = 0;
|
||||
|
||||
if (!select_phy)
|
||||
return 0;
|
||||
|
||||
usbcfg = dwc2_readl(hsotg, GUSBCFG);
|
||||
usbcfg_old = usbcfg;
|
||||
|
||||
/*
|
||||
* HS PHY parameters. These parameters are preserved during soft reset
|
||||
* so only program the first time. Do a soft reset immediately after
|
||||
* setting phyif.
|
||||
*/
|
||||
switch (hsotg->params.phy_type) {
|
||||
case DWC2_PHY_TYPE_PARAM_ULPI:
|
||||
/* ULPI interface */
|
||||
dev_dbg(hsotg->dev, "HS ULPI PHY selected\n");
|
||||
usbcfg |= GUSBCFG_ULPI_UTMI_SEL;
|
||||
usbcfg &= ~(GUSBCFG_PHYIF16 | GUSBCFG_DDRSEL);
|
||||
if (hsotg->params.phy_ulpi_ddr)
|
||||
usbcfg |= GUSBCFG_DDRSEL;
|
||||
|
||||
/* Set external VBUS indicator as needed. */
|
||||
if (hsotg->params.oc_disable)
|
||||
usbcfg |= (GUSBCFG_ULPI_INT_VBUS_IND |
|
||||
GUSBCFG_INDICATORPASSTHROUGH);
|
||||
break;
|
||||
case DWC2_PHY_TYPE_PARAM_UTMI:
|
||||
/* UTMI+ interface */
|
||||
dev_dbg(hsotg->dev, "HS UTMI+ PHY selected\n");
|
||||
usbcfg &= ~(GUSBCFG_ULPI_UTMI_SEL | GUSBCFG_PHYIF16);
|
||||
if (hsotg->params.phy_utmi_width == 16)
|
||||
usbcfg |= GUSBCFG_PHYIF16;
|
||||
break;
|
||||
default:
|
||||
dev_err(hsotg->dev, "FS PHY selected at HS!\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (usbcfg != usbcfg_old) {
|
||||
dwc2_writel(hsotg, usbcfg, GUSBCFG);
|
||||
|
||||
/* Reset after setting the PHY parameters */
|
||||
retval = dwc2_core_reset(hsotg, false);
|
||||
if (retval) {
|
||||
dev_err(hsotg->dev,
|
||||
"%s: Reset failed, aborting", __func__);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int dwc2_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
|
||||
{
|
||||
u32 usbcfg;
|
||||
int retval = 0;
|
||||
|
||||
if ((hsotg->params.speed == DWC2_SPEED_PARAM_FULL ||
|
||||
hsotg->params.speed == DWC2_SPEED_PARAM_LOW) &&
|
||||
hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS) {
|
||||
/* If FS/LS mode with FS/LS PHY */
|
||||
retval = dwc2_fs_phy_init(hsotg, select_phy);
|
||||
if (retval)
|
||||
return retval;
|
||||
} else {
|
||||
/* High speed PHY */
|
||||
retval = dwc2_hs_phy_init(hsotg, select_phy);
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI &&
|
||||
hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED &&
|
||||
hsotg->params.ulpi_fs_ls) {
|
||||
dev_dbg(hsotg->dev, "Setting ULPI FSLS\n");
|
||||
usbcfg = dwc2_readl(hsotg, GUSBCFG);
|
||||
usbcfg |= GUSBCFG_ULPI_FS_LS;
|
||||
usbcfg |= GUSBCFG_ULPI_CLK_SUSP_M;
|
||||
dwc2_writel(hsotg, usbcfg, GUSBCFG);
|
||||
} else {
|
||||
usbcfg = dwc2_readl(hsotg, GUSBCFG);
|
||||
usbcfg &= ~GUSBCFG_ULPI_FS_LS;
|
||||
usbcfg &= ~GUSBCFG_ULPI_CLK_SUSP_M;
|
||||
dwc2_writel(hsotg, usbcfg, GUSBCFG);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int dwc2_gahbcfg_init(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 ahbcfg = dwc2_readl(hsotg, GAHBCFG);
|
||||
@ -2437,25 +2247,31 @@ static void dwc2_core_host_init(struct dwc2_hsotg *hsotg)
|
||||
num_channels = hsotg->params.host_channels;
|
||||
for (i = 0; i < num_channels; i++) {
|
||||
hcchar = dwc2_readl(hsotg, HCCHAR(i));
|
||||
hcchar &= ~HCCHAR_CHENA;
|
||||
hcchar |= HCCHAR_CHDIS;
|
||||
hcchar &= ~HCCHAR_EPDIR;
|
||||
dwc2_writel(hsotg, hcchar, HCCHAR(i));
|
||||
if (hcchar & HCCHAR_CHENA) {
|
||||
hcchar &= ~HCCHAR_CHENA;
|
||||
hcchar |= HCCHAR_CHDIS;
|
||||
hcchar &= ~HCCHAR_EPDIR;
|
||||
dwc2_writel(hsotg, hcchar, HCCHAR(i));
|
||||
}
|
||||
}
|
||||
|
||||
/* Halt all channels to put them into a known state */
|
||||
for (i = 0; i < num_channels; i++) {
|
||||
hcchar = dwc2_readl(hsotg, HCCHAR(i));
|
||||
hcchar |= HCCHAR_CHENA | HCCHAR_CHDIS;
|
||||
hcchar &= ~HCCHAR_EPDIR;
|
||||
dwc2_writel(hsotg, hcchar, HCCHAR(i));
|
||||
dev_dbg(hsotg->dev, "%s: Halt channel %d\n",
|
||||
__func__, i);
|
||||
if (hcchar & HCCHAR_CHENA) {
|
||||
hcchar |= HCCHAR_CHENA | HCCHAR_CHDIS;
|
||||
hcchar &= ~HCCHAR_EPDIR;
|
||||
dwc2_writel(hsotg, hcchar, HCCHAR(i));
|
||||
dev_dbg(hsotg->dev, "%s: Halt channel %d\n",
|
||||
__func__, i);
|
||||
|
||||
if (dwc2_hsotg_wait_bit_clear(hsotg, HCCHAR(i),
|
||||
HCCHAR_CHENA, 1000)) {
|
||||
dev_warn(hsotg->dev, "Unable to clear enable on channel %d\n",
|
||||
i);
|
||||
if (dwc2_hsotg_wait_bit_clear(hsotg, HCCHAR(i),
|
||||
HCCHAR_CHENA,
|
||||
1000)) {
|
||||
dev_warn(hsotg->dev,
|
||||
"Unable to clear enable on channel %d\n",
|
||||
i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4376,6 +4192,17 @@ static void dwc2_hcd_reset_func(struct work_struct *work)
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
}
|
||||
|
||||
static void dwc2_hcd_phy_reset_func(struct work_struct *work)
|
||||
{
|
||||
struct dwc2_hsotg *hsotg = container_of(work, struct dwc2_hsotg,
|
||||
phy_reset_work);
|
||||
int ret;
|
||||
|
||||
ret = phy_reset(hsotg->phy);
|
||||
if (ret)
|
||||
dev_warn(hsotg->dev, "PHY reset failed\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* =========================================================================
|
||||
* Linux HC Driver Functions
|
||||
@ -4471,6 +4298,7 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd)
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
u32 hprt0;
|
||||
u32 pcgctl;
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
|
||||
@ -4486,7 +4314,7 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd)
|
||||
if (hsotg->op_state == OTG_STATE_B_PERIPHERAL)
|
||||
goto unlock;
|
||||
|
||||
if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_PARTIAL)
|
||||
if (hsotg->params.power_down > DWC2_POWER_DOWN_PARAM_PARTIAL)
|
||||
goto skip_power_saving;
|
||||
|
||||
/*
|
||||
@ -4495,21 +4323,35 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd)
|
||||
*/
|
||||
if (!hsotg->bus_suspended) {
|
||||
hprt0 = dwc2_read_hprt0(hsotg);
|
||||
hprt0 |= HPRT0_SUSP;
|
||||
hprt0 &= ~HPRT0_PWR;
|
||||
dwc2_writel(hsotg, hprt0, HPRT0);
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
dwc2_vbus_supply_exit(hsotg);
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
if (hprt0 & HPRT0_CONNSTS) {
|
||||
hprt0 |= HPRT0_SUSP;
|
||||
if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL)
|
||||
hprt0 &= ~HPRT0_PWR;
|
||||
dwc2_writel(hsotg, hprt0, HPRT0);
|
||||
}
|
||||
if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) {
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
dwc2_vbus_supply_exit(hsotg);
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
} else {
|
||||
pcgctl = readl(hsotg->regs + PCGCTL);
|
||||
pcgctl |= PCGCTL_STOPPCLK;
|
||||
writel(pcgctl, hsotg->regs + PCGCTL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Enter partial_power_down */
|
||||
ret = dwc2_enter_partial_power_down(hsotg);
|
||||
if (ret) {
|
||||
if (ret != -ENOTSUPP)
|
||||
dev_err(hsotg->dev,
|
||||
"enter partial_power_down failed\n");
|
||||
goto skip_power_saving;
|
||||
if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) {
|
||||
/* Enter partial_power_down */
|
||||
ret = dwc2_enter_partial_power_down(hsotg);
|
||||
if (ret) {
|
||||
if (ret != -ENOTSUPP)
|
||||
dev_err(hsotg->dev,
|
||||
"enter partial_power_down failed\n");
|
||||
goto skip_power_saving;
|
||||
}
|
||||
|
||||
/* After entering partial_power_down, hardware is no more accessible */
|
||||
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
||||
}
|
||||
|
||||
/* Ask phy to be suspended */
|
||||
@ -4519,9 +4361,6 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd)
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
}
|
||||
|
||||
/* After entering partial_power_down, hardware is no more accessible */
|
||||
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
||||
|
||||
skip_power_saving:
|
||||
hsotg->lx_state = DWC2_L2;
|
||||
unlock:
|
||||
@ -4534,6 +4373,7 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd)
|
||||
{
|
||||
struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
|
||||
unsigned long flags;
|
||||
u32 pcgctl;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
@ -4544,17 +4384,11 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd)
|
||||
if (hsotg->lx_state != DWC2_L2)
|
||||
goto unlock;
|
||||
|
||||
if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_PARTIAL) {
|
||||
if (hsotg->params.power_down > DWC2_POWER_DOWN_PARAM_PARTIAL) {
|
||||
hsotg->lx_state = DWC2_L0;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set HW accessible bit before powering on the controller
|
||||
* since an interrupt may rise.
|
||||
*/
|
||||
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
||||
|
||||
/*
|
||||
* Enable power if not already done.
|
||||
* This must not be spinlocked since duration
|
||||
@ -4566,10 +4400,23 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd)
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
}
|
||||
|
||||
/* Exit partial_power_down */
|
||||
ret = dwc2_exit_partial_power_down(hsotg, true);
|
||||
if (ret && (ret != -ENOTSUPP))
|
||||
dev_err(hsotg->dev, "exit partial_power_down failed\n");
|
||||
if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) {
|
||||
/*
|
||||
* Set HW accessible bit before powering on the controller
|
||||
* since an interrupt may rise.
|
||||
*/
|
||||
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
||||
|
||||
|
||||
/* Exit partial_power_down */
|
||||
ret = dwc2_exit_partial_power_down(hsotg, true);
|
||||
if (ret && (ret != -ENOTSUPP))
|
||||
dev_err(hsotg->dev, "exit partial_power_down failed\n");
|
||||
} else {
|
||||
pcgctl = readl(hsotg->regs + PCGCTL);
|
||||
pcgctl &= ~PCGCTL_STOPPCLK;
|
||||
writel(pcgctl, hsotg->regs + PCGCTL);
|
||||
}
|
||||
|
||||
hsotg->lx_state = DWC2_L0;
|
||||
|
||||
@ -4581,10 +4428,12 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd)
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
dwc2_port_resume(hsotg);
|
||||
} else {
|
||||
dwc2_vbus_supply_init(hsotg);
|
||||
if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) {
|
||||
dwc2_vbus_supply_init(hsotg);
|
||||
|
||||
/* Wait for controller to correctly update D+/D- level */
|
||||
usleep_range(3000, 5000);
|
||||
/* Wait for controller to correctly update D+/D- level */
|
||||
usleep_range(3000, 5000);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear Port Enable and Port Status changes.
|
||||
@ -5130,6 +4979,8 @@ static void dwc2_hcd_free(struct dwc2_hsotg *hsotg)
|
||||
destroy_workqueue(hsotg->wq_otg);
|
||||
}
|
||||
|
||||
cancel_work_sync(&hsotg->phy_reset_work);
|
||||
|
||||
del_timer(&hsotg->wkp_timer);
|
||||
}
|
||||
|
||||
@ -5271,11 +5122,10 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
|
||||
hsotg->hc_ptr_array[i] = channel;
|
||||
}
|
||||
|
||||
/* Initialize hsotg start work */
|
||||
/* Initialize work */
|
||||
INIT_DELAYED_WORK(&hsotg->start_work, dwc2_hcd_start_func);
|
||||
|
||||
/* Initialize port reset work */
|
||||
INIT_DELAYED_WORK(&hsotg->reset_work, dwc2_hcd_reset_func);
|
||||
INIT_WORK(&hsotg->phy_reset_work, dwc2_hcd_phy_reset_func);
|
||||
|
||||
/*
|
||||
* Allocate space for storing data on status transactions. Normally no
|
||||
|
@ -310,12 +310,12 @@
|
||||
#define GHWCFG4_NUM_DEV_MODE_CTRL_EP_SHIFT 16
|
||||
#define GHWCFG4_UTMI_PHY_DATA_WIDTH_MASK (0x3 << 14)
|
||||
#define GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT 14
|
||||
#define GHWCFG4_ACG_SUPPORTED BIT(12)
|
||||
#define GHWCFG4_IPG_ISOC_SUPPORTED BIT(11)
|
||||
#define GHWCFG4_SERVICE_INTERVAL_SUPPORTED BIT(10)
|
||||
#define GHWCFG4_UTMI_PHY_DATA_WIDTH_8 0
|
||||
#define GHWCFG4_UTMI_PHY_DATA_WIDTH_16 1
|
||||
#define GHWCFG4_UTMI_PHY_DATA_WIDTH_8_OR_16 2
|
||||
#define GHWCFG4_ACG_SUPPORTED BIT(12)
|
||||
#define GHWCFG4_IPG_ISOC_SUPPORTED BIT(11)
|
||||
#define GHWCFG4_SERVICE_INTERVAL_SUPPORTED BIT(10)
|
||||
#define GHWCFG4_XHIBER BIT(7)
|
||||
#define GHWCFG4_HIBER BIT(6)
|
||||
#define GHWCFG4_MIN_AHB_FREQ BIT(5)
|
||||
@ -333,7 +333,7 @@
|
||||
#define GLPMCFG_SNDLPM BIT(24)
|
||||
#define GLPMCFG_RETRY_CNT_MASK (0x7 << 21)
|
||||
#define GLPMCFG_RETRY_CNT_SHIFT 21
|
||||
#define GLPMCFG_LPM_ACCEPT_CTRL_CONTROL BIT(21)
|
||||
#define GLPMCFG_LPM_REJECT_CTRL_CONTROL BIT(21)
|
||||
#define GLPMCFG_LPM_ACCEPT_CTRL_ISOC BIT(22)
|
||||
#define GLPMCFG_LPM_CHNL_INDX_MASK (0xf << 17)
|
||||
#define GLPMCFG_LPM_CHNL_INDX_SHIFT 17
|
||||
|
@ -121,6 +121,16 @@ static void dwc2_set_amlogic_params(struct dwc2_hsotg *hsotg)
|
||||
p->power_down = DWC2_POWER_DOWN_PARAM_NONE;
|
||||
}
|
||||
|
||||
static void dwc2_set_amlogic_g12a_params(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_core_params *p = &hsotg->params;
|
||||
|
||||
p->lpm = false;
|
||||
p->lpm_clock_gating = false;
|
||||
p->besl = false;
|
||||
p->hird_threshold_en = false;
|
||||
}
|
||||
|
||||
static void dwc2_set_amcc_params(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_core_params *p = &hsotg->params;
|
||||
@ -167,6 +177,8 @@ const struct of_device_id dwc2_of_match_table[] = {
|
||||
.data = dwc2_set_amlogic_params },
|
||||
{ .compatible = "amlogic,meson-gxbb-usb",
|
||||
.data = dwc2_set_amlogic_params },
|
||||
{ .compatible = "amlogic,meson-g12a-usb",
|
||||
.data = dwc2_set_amlogic_g12a_params },
|
||||
{ .compatible = "amcc,dwc-otg", .data = dwc2_set_amcc_params },
|
||||
{ .compatible = "st,stm32f4x9-fsotg",
|
||||
.data = dwc2_set_stm32f4x9_fsotg_params },
|
||||
@ -273,6 +285,23 @@ static void dwc2_set_param_power_down(struct dwc2_hsotg *hsotg)
|
||||
hsotg->params.power_down = val;
|
||||
}
|
||||
|
||||
static void dwc2_set_param_lpm(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_core_params *p = &hsotg->params;
|
||||
|
||||
p->lpm = hsotg->hw_params.lpm_mode;
|
||||
if (p->lpm) {
|
||||
p->lpm_clock_gating = true;
|
||||
p->besl = true;
|
||||
p->hird_threshold_en = true;
|
||||
p->hird_threshold = 4;
|
||||
} else {
|
||||
p->lpm_clock_gating = false;
|
||||
p->besl = false;
|
||||
p->hird_threshold_en = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_set_default_params() - Set all core parameters to their
|
||||
* auto-detected default values.
|
||||
@ -291,6 +320,7 @@ static void dwc2_set_default_params(struct dwc2_hsotg *hsotg)
|
||||
dwc2_set_param_speed(hsotg);
|
||||
dwc2_set_param_phy_utmi_width(hsotg);
|
||||
dwc2_set_param_power_down(hsotg);
|
||||
dwc2_set_param_lpm(hsotg);
|
||||
p->phy_ulpi_ddr = false;
|
||||
p->phy_ulpi_ext_vbus = false;
|
||||
|
||||
@ -303,11 +333,6 @@ static void dwc2_set_default_params(struct dwc2_hsotg *hsotg)
|
||||
p->reload_ctl = (hw->snpsid >= DWC2_CORE_REV_2_92a);
|
||||
p->uframe_sched = true;
|
||||
p->external_id_pin_ctl = false;
|
||||
p->lpm = true;
|
||||
p->lpm_clock_gating = true;
|
||||
p->besl = true;
|
||||
p->hird_threshold_en = true;
|
||||
p->hird_threshold = 4;
|
||||
p->ipg_isoc_en = false;
|
||||
p->service_interval = false;
|
||||
p->max_packet_count = hw->max_packet_count;
|
||||
|
@ -230,9 +230,6 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg)
|
||||
|
||||
reset_control_deassert(hsotg->reset_ecc);
|
||||
|
||||
/* Set default UTMI width */
|
||||
hsotg->phyif = GUSBCFG_PHYIF16;
|
||||
|
||||
/*
|
||||
* Attempt to find a generic PHY, then look for an old style
|
||||
* USB PHY and then fall back to pdata
|
||||
@ -280,7 +277,7 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg)
|
||||
* width is 8-bit and set the phyif appropriately.
|
||||
*/
|
||||
if (phy_get_bus_width(hsotg->phy) == 8)
|
||||
hsotg->phyif = GUSBCFG_PHYIF8;
|
||||
hsotg->params.phy_utmi_width = 8;
|
||||
}
|
||||
|
||||
/* Clock */
|
||||
@ -481,6 +478,15 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
||||
hsotg->gadget_enabled = 1;
|
||||
}
|
||||
|
||||
hsotg->reset_phy_on_wake =
|
||||
of_property_read_bool(dev->dev.of_node,
|
||||
"snps,reset-phy-on-wake");
|
||||
if (hsotg->reset_phy_on_wake && !hsotg->phy) {
|
||||
dev_warn(hsotg->dev,
|
||||
"Quirk reset-phy-on-wake only supports generic PHYs\n");
|
||||
hsotg->reset_phy_on_wake = false;
|
||||
}
|
||||
|
||||
if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) {
|
||||
retval = dwc2_hcd_init(hsotg);
|
||||
if (retval) {
|
||||
|
@ -96,6 +96,16 @@ config USB_DWC3_KEYSTONE
|
||||
Support of USB2/3 functionality in TI Keystone2 and AM654 platforms.
|
||||
Say 'Y' or 'M' here if you have one such device
|
||||
|
||||
config USB_DWC3_MESON_G12A
|
||||
tristate "Amlogic Meson G12A Platforms"
|
||||
depends on OF && COMMON_CLK
|
||||
depends on ARCH_MESON || COMPILE_TEST
|
||||
default USB_DWC3
|
||||
select USB_ROLE_SWITCH
|
||||
help
|
||||
Support USB2/3 functionality in Amlogic G12A platforms.
|
||||
Say 'Y' or 'M' if you have one such device.
|
||||
|
||||
config USB_DWC3_OF_SIMPLE
|
||||
tristate "Generic OF Simple Glue Layer"
|
||||
depends on OF && COMMON_CLK
|
||||
|
@ -47,6 +47,7 @@ obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o
|
||||
obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o
|
||||
obj-$(CONFIG_USB_DWC3_HAPS) += dwc3-haps.o
|
||||
obj-$(CONFIG_USB_DWC3_KEYSTONE) += dwc3-keystone.o
|
||||
obj-$(CONFIG_USB_DWC3_MESON_G12A) += dwc3-meson-g12a.o
|
||||
obj-$(CONFIG_USB_DWC3_OF_SIMPLE) += dwc3-of-simple.o
|
||||
obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o
|
||||
obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o
|
||||
|
@ -828,6 +828,7 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc)
|
||||
ret = device_property_read_u32_array(dev,
|
||||
"snps,incr-burst-type-adjustment", vals, ntype);
|
||||
if (ret) {
|
||||
kfree(vals);
|
||||
dev_err(dev, "Error to get property\n");
|
||||
return;
|
||||
}
|
||||
@ -846,6 +847,8 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc)
|
||||
incrx_mode = INCRX_BURST_MODE;
|
||||
}
|
||||
|
||||
kfree(vals);
|
||||
|
||||
/* Enable Undefined Length INCR Burst and Enable INCRx Burst */
|
||||
cfg &= ~DWC3_GSBUSCFG0_INCRBRST_MASK;
|
||||
if (incrx_mode)
|
||||
@ -893,12 +896,6 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
if (!dwc3_core_is_valid(dwc)) {
|
||||
dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n");
|
||||
ret = -ENODEV;
|
||||
goto err0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write Linux Version Code to our GUID register so it's easy to figure
|
||||
* out which kernel version a bug was found.
|
||||
@ -1218,7 +1215,7 @@ static void dwc3_get_properties(struct dwc3 *dwc)
|
||||
u8 tx_max_burst_prd;
|
||||
|
||||
/* default to highest possible threshold */
|
||||
lpm_nyet_threshold = 0xff;
|
||||
lpm_nyet_threshold = 0xf;
|
||||
|
||||
/* default to -3.5dB de-emphasis */
|
||||
tx_de_emphasis = 1;
|
||||
@ -1426,6 +1423,11 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
dwc->regs = regs;
|
||||
dwc->regs_size = resource_size(&dwc_res);
|
||||
|
||||
if (!dwc3_core_is_valid(dwc)) {
|
||||
dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dwc3_get_properties(dwc);
|
||||
|
||||
dwc->reset = devm_reset_control_get_optional_shared(dev, NULL);
|
||||
@ -1600,6 +1602,7 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
dwc3_gadget_suspend(dwc);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
synchronize_irq(dwc->irq_gadget);
|
||||
dwc3_core_exit(dwc);
|
||||
break;
|
||||
case DWC3_GCTL_PRTCAP_HOST:
|
||||
@ -1632,6 +1635,7 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
dwc3_gadget_suspend(dwc);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
synchronize_irq(dwc->irq_gadget);
|
||||
}
|
||||
|
||||
dwc3_otg_exit(dwc);
|
||||
|
@ -406,8 +406,7 @@
|
||||
#define DWC3_DCTL_TRGTULST_SS_INACT (DWC3_DCTL_TRGTULST(6))
|
||||
|
||||
/* These apply for core versions 1.94a and later */
|
||||
#define DWC3_DCTL_LPM_ERRATA_MASK DWC3_DCTL_LPM_ERRATA(0xf)
|
||||
#define DWC3_DCTL_LPM_ERRATA(n) ((n) << 20)
|
||||
#define DWC3_DCTL_NYET_THRES(n) (((n) & 0xf) << 20)
|
||||
|
||||
#define DWC3_DCTL_KEEP_CONNECT BIT(19)
|
||||
#define DWC3_DCTL_L1_HIBER_EN BIT(18)
|
||||
|
@ -250,6 +250,9 @@ static inline void dwc3_decode_get_status(__u8 t, __u16 i, __u16 l, char *str,
|
||||
size_t size)
|
||||
{
|
||||
switch (t & USB_RECIP_MASK) {
|
||||
case USB_RECIP_DEVICE:
|
||||
snprintf(str, size, "Get Device Status(Length = %d)", l);
|
||||
break;
|
||||
case USB_RECIP_INTERFACE:
|
||||
snprintf(str, size, "Get Interface Status(Intf = %d, Length = %d)",
|
||||
i, l);
|
||||
|
604
drivers/usb/dwc3/dwc3-meson-g12a.c
Normal file
604
drivers/usb/dwc3/dwc3-meson-g12a.c
Normal file
@ -0,0 +1,604 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* USB Glue for Amlogic G12A SoCs
|
||||
*
|
||||
* Copyright (c) 2019 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
* The USB is organized with a glue around the DWC3 Controller IP as :
|
||||
* - Control registers for each USB2 Ports
|
||||
* - Control registers for the USB PHY layer
|
||||
* - SuperSpeed PHY can be enabled only if port is used
|
||||
*
|
||||
* TOFIX:
|
||||
* - Add dynamic OTG switching with ID change interrupt
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/role.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
/* USB2 Ports Control Registers */
|
||||
|
||||
#define U2P_REG_SIZE 0x20
|
||||
|
||||
#define U2P_R0 0x0
|
||||
#define U2P_R0_HOST_DEVICE BIT(0)
|
||||
#define U2P_R0_POWER_OK BIT(1)
|
||||
#define U2P_R0_HAST_MODE BIT(2)
|
||||
#define U2P_R0_POWER_ON_RESET BIT(3)
|
||||
#define U2P_R0_ID_PULLUP BIT(4)
|
||||
#define U2P_R0_DRV_VBUS BIT(5)
|
||||
|
||||
#define U2P_R1 0x4
|
||||
#define U2P_R1_PHY_READY BIT(0)
|
||||
#define U2P_R1_ID_DIG BIT(1)
|
||||
#define U2P_R1_OTG_SESSION_VALID BIT(2)
|
||||
#define U2P_R1_VBUS_VALID BIT(3)
|
||||
|
||||
/* USB Glue Control Registers */
|
||||
|
||||
#define USB_R0 0x80
|
||||
#define USB_R0_P30_LANE0_TX2RX_LOOPBACK BIT(17)
|
||||
#define USB_R0_P30_LANE0_EXT_PCLK_REQ BIT(18)
|
||||
#define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK GENMASK(28, 19)
|
||||
#define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK GENMASK(30, 29)
|
||||
#define USB_R0_U2D_ACT BIT(31)
|
||||
|
||||
#define USB_R1 0x84
|
||||
#define USB_R1_U3H_BIGENDIAN_GS BIT(0)
|
||||
#define USB_R1_U3H_PME_ENABLE BIT(1)
|
||||
#define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK GENMASK(4, 2)
|
||||
#define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK GENMASK(9, 7)
|
||||
#define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK GENMASK(13, 12)
|
||||
#define USB_R1_U3H_HOST_U3_PORT_DISABLE BIT(16)
|
||||
#define USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT BIT(17)
|
||||
#define USB_R1_U3H_HOST_MSI_ENABLE BIT(18)
|
||||
#define USB_R1_U3H_FLADJ_30MHZ_REG_MASK GENMASK(24, 19)
|
||||
#define USB_R1_P30_PCS_TX_SWING_FULL_MASK GENMASK(31, 25)
|
||||
|
||||
#define USB_R2 0x88
|
||||
#define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK GENMASK(25, 20)
|
||||
#define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK GENMASK(31, 26)
|
||||
|
||||
#define USB_R3 0x8c
|
||||
#define USB_R3_P30_SSC_ENABLE BIT(0)
|
||||
#define USB_R3_P30_SSC_RANGE_MASK GENMASK(3, 1)
|
||||
#define USB_R3_P30_SSC_REF_CLK_SEL_MASK GENMASK(12, 4)
|
||||
#define USB_R3_P30_REF_SSP_EN BIT(13)
|
||||
|
||||
#define USB_R4 0x90
|
||||
#define USB_R4_P21_PORT_RESET_0 BIT(0)
|
||||
#define USB_R4_P21_SLEEP_M0 BIT(1)
|
||||
#define USB_R4_MEM_PD_MASK GENMASK(3, 2)
|
||||
#define USB_R4_P21_ONLY BIT(4)
|
||||
|
||||
#define USB_R5 0x94
|
||||
#define USB_R5_ID_DIG_SYNC BIT(0)
|
||||
#define USB_R5_ID_DIG_REG BIT(1)
|
||||
#define USB_R5_ID_DIG_CFG_MASK GENMASK(3, 2)
|
||||
#define USB_R5_ID_DIG_EN_0 BIT(4)
|
||||
#define USB_R5_ID_DIG_EN_1 BIT(5)
|
||||
#define USB_R5_ID_DIG_CURR BIT(6)
|
||||
#define USB_R5_ID_DIG_IRQ BIT(7)
|
||||
#define USB_R5_ID_DIG_TH_MASK GENMASK(15, 8)
|
||||
#define USB_R5_ID_DIG_CNT_MASK GENMASK(23, 16)
|
||||
|
||||
enum {
|
||||
USB2_HOST_PHY = 0,
|
||||
USB2_OTG_PHY,
|
||||
USB3_HOST_PHY,
|
||||
PHY_COUNT,
|
||||
};
|
||||
|
||||
static const char *phy_names[PHY_COUNT] = {
|
||||
"usb2-phy0", "usb2-phy1", "usb3-phy0",
|
||||
};
|
||||
|
||||
struct dwc3_meson_g12a {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct clk *clk;
|
||||
struct reset_control *reset;
|
||||
struct phy *phys[PHY_COUNT];
|
||||
enum usb_dr_mode otg_mode;
|
||||
enum phy_mode otg_phy_mode;
|
||||
unsigned int usb2_ports;
|
||||
unsigned int usb3_ports;
|
||||
struct regulator *vbus;
|
||||
struct usb_role_switch_desc switch_desc;
|
||||
struct usb_role_switch *role_switch;
|
||||
};
|
||||
|
||||
static void dwc3_meson_g12a_usb2_set_mode(struct dwc3_meson_g12a *priv,
|
||||
int i, enum phy_mode mode)
|
||||
{
|
||||
if (mode == PHY_MODE_USB_HOST)
|
||||
regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
|
||||
U2P_R0_HOST_DEVICE,
|
||||
U2P_R0_HOST_DEVICE);
|
||||
else
|
||||
regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
|
||||
U2P_R0_HOST_DEVICE, 0);
|
||||
}
|
||||
|
||||
static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (priv->otg_mode == USB_DR_MODE_PERIPHERAL)
|
||||
priv->otg_phy_mode = PHY_MODE_USB_DEVICE;
|
||||
else
|
||||
priv->otg_phy_mode = PHY_MODE_USB_HOST;
|
||||
|
||||
for (i = 0 ; i < USB3_HOST_PHY ; ++i) {
|
||||
if (!priv->phys[i])
|
||||
continue;
|
||||
|
||||
regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
|
||||
U2P_R0_POWER_ON_RESET,
|
||||
U2P_R0_POWER_ON_RESET);
|
||||
|
||||
if (i == USB2_OTG_PHY) {
|
||||
regmap_update_bits(priv->regmap,
|
||||
U2P_R0 + (U2P_REG_SIZE * i),
|
||||
U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS,
|
||||
U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS);
|
||||
|
||||
dwc3_meson_g12a_usb2_set_mode(priv, i,
|
||||
priv->otg_phy_mode);
|
||||
} else
|
||||
dwc3_meson_g12a_usb2_set_mode(priv, i,
|
||||
PHY_MODE_USB_HOST);
|
||||
|
||||
regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
|
||||
U2P_R0_POWER_ON_RESET, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dwc3_meson_g12a_usb3_init(struct dwc3_meson_g12a *priv)
|
||||
{
|
||||
regmap_update_bits(priv->regmap, USB_R3,
|
||||
USB_R3_P30_SSC_RANGE_MASK |
|
||||
USB_R3_P30_REF_SSP_EN,
|
||||
USB_R3_P30_SSC_ENABLE |
|
||||
FIELD_PREP(USB_R3_P30_SSC_RANGE_MASK, 2) |
|
||||
USB_R3_P30_REF_SSP_EN);
|
||||
udelay(2);
|
||||
|
||||
regmap_update_bits(priv->regmap, USB_R2,
|
||||
USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK,
|
||||
FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK, 0x15));
|
||||
|
||||
regmap_update_bits(priv->regmap, USB_R2,
|
||||
USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK,
|
||||
FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK, 0x20));
|
||||
|
||||
udelay(2);
|
||||
|
||||
regmap_update_bits(priv->regmap, USB_R1,
|
||||
USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT,
|
||||
USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT);
|
||||
|
||||
regmap_update_bits(priv->regmap, USB_R1,
|
||||
USB_R1_P30_PCS_TX_SWING_FULL_MASK,
|
||||
FIELD_PREP(USB_R1_P30_PCS_TX_SWING_FULL_MASK, 127));
|
||||
}
|
||||
|
||||
static void dwc3_meson_g12a_usb_otg_apply_mode(struct dwc3_meson_g12a *priv)
|
||||
{
|
||||
if (priv->otg_phy_mode == PHY_MODE_USB_DEVICE) {
|
||||
regmap_update_bits(priv->regmap, USB_R0,
|
||||
USB_R0_U2D_ACT, USB_R0_U2D_ACT);
|
||||
regmap_update_bits(priv->regmap, USB_R0,
|
||||
USB_R0_U2D_SS_SCALEDOWN_MODE_MASK, 0);
|
||||
regmap_update_bits(priv->regmap, USB_R4,
|
||||
USB_R4_P21_SLEEP_M0, USB_R4_P21_SLEEP_M0);
|
||||
} else {
|
||||
regmap_update_bits(priv->regmap, USB_R0,
|
||||
USB_R0_U2D_ACT, 0);
|
||||
regmap_update_bits(priv->regmap, USB_R4,
|
||||
USB_R4_P21_SLEEP_M0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = dwc3_meson_g12a_usb2_init(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
regmap_update_bits(priv->regmap, USB_R1,
|
||||
USB_R1_U3H_FLADJ_30MHZ_REG_MASK,
|
||||
FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20));
|
||||
|
||||
regmap_update_bits(priv->regmap, USB_R5,
|
||||
USB_R5_ID_DIG_EN_0,
|
||||
USB_R5_ID_DIG_EN_0);
|
||||
regmap_update_bits(priv->regmap, USB_R5,
|
||||
USB_R5_ID_DIG_EN_1,
|
||||
USB_R5_ID_DIG_EN_1);
|
||||
regmap_update_bits(priv->regmap, USB_R5,
|
||||
USB_R5_ID_DIG_TH_MASK,
|
||||
FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff));
|
||||
|
||||
/* If we have an actual SuperSpeed port, initialize it */
|
||||
if (priv->usb3_ports)
|
||||
dwc3_meson_g12a_usb3_init(priv);
|
||||
|
||||
dwc3_meson_g12a_usb_otg_apply_mode(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct regmap_config phy_meson_g12a_usb3_regmap_conf = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.max_register = USB_R5,
|
||||
};
|
||||
|
||||
static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0 ; i < PHY_COUNT ; ++i) {
|
||||
priv->phys[i] = devm_phy_optional_get(priv->dev, phy_names[i]);
|
||||
if (!priv->phys[i])
|
||||
continue;
|
||||
|
||||
if (IS_ERR(priv->phys[i]))
|
||||
return PTR_ERR(priv->phys[i]);
|
||||
|
||||
if (i == USB3_HOST_PHY)
|
||||
priv->usb3_ports++;
|
||||
else
|
||||
priv->usb2_ports++;
|
||||
}
|
||||
|
||||
dev_info(priv->dev, "USB2 ports: %d\n", priv->usb2_ports);
|
||||
dev_info(priv->dev, "USB3 ports: %d\n", priv->usb3_ports);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum phy_mode dwc3_meson_g12a_get_id(struct dwc3_meson_g12a *priv)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
regmap_read(priv->regmap, USB_R5, ®);
|
||||
|
||||
if (reg & (USB_R5_ID_DIG_SYNC | USB_R5_ID_DIG_REG))
|
||||
return PHY_MODE_USB_DEVICE;
|
||||
|
||||
return PHY_MODE_USB_HOST;
|
||||
}
|
||||
|
||||
static int dwc3_meson_g12a_otg_mode_set(struct dwc3_meson_g12a *priv,
|
||||
enum phy_mode mode)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!priv->phys[USB2_OTG_PHY])
|
||||
return -EINVAL;
|
||||
|
||||
if (mode == PHY_MODE_USB_HOST)
|
||||
dev_info(priv->dev, "switching to Host Mode\n");
|
||||
else
|
||||
dev_info(priv->dev, "switching to Device Mode\n");
|
||||
|
||||
if (priv->vbus) {
|
||||
if (mode == PHY_MODE_USB_DEVICE)
|
||||
ret = regulator_disable(priv->vbus);
|
||||
else
|
||||
ret = regulator_enable(priv->vbus);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->otg_phy_mode = mode;
|
||||
|
||||
dwc3_meson_g12a_usb2_set_mode(priv, USB2_OTG_PHY, mode);
|
||||
|
||||
dwc3_meson_g12a_usb_otg_apply_mode(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_meson_g12a_role_set(struct device *dev, enum usb_role role)
|
||||
{
|
||||
struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
|
||||
enum phy_mode mode;
|
||||
|
||||
if (role == USB_ROLE_NONE)
|
||||
return 0;
|
||||
|
||||
mode = (role == USB_ROLE_HOST) ? PHY_MODE_USB_HOST
|
||||
: PHY_MODE_USB_DEVICE;
|
||||
|
||||
if (mode == priv->otg_phy_mode)
|
||||
return 0;
|
||||
|
||||
return dwc3_meson_g12a_otg_mode_set(priv, mode);
|
||||
}
|
||||
|
||||
static enum usb_role dwc3_meson_g12a_role_get(struct device *dev)
|
||||
{
|
||||
struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
|
||||
|
||||
return priv->otg_phy_mode == PHY_MODE_USB_HOST ?
|
||||
USB_ROLE_HOST : USB_ROLE_DEVICE;
|
||||
}
|
||||
|
||||
static struct device *dwc3_meson_g12_find_child(struct device *dev,
|
||||
const char *compatible)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
struct device_node *np;
|
||||
|
||||
np = of_get_compatible_child(dev->of_node, compatible);
|
||||
if (!np)
|
||||
return NULL;
|
||||
|
||||
pdev = of_find_device_by_node(np);
|
||||
of_node_put(np);
|
||||
if (!pdev)
|
||||
return NULL;
|
||||
|
||||
return &pdev->dev;
|
||||
}
|
||||
|
||||
static int dwc3_meson_g12a_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3_meson_g12a *priv;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
void __iomem *base;
|
||||
struct resource *res;
|
||||
enum phy_mode otg_id;
|
||||
int ret, i;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
priv->regmap = devm_regmap_init_mmio(dev, base,
|
||||
&phy_meson_g12a_usb3_regmap_conf);
|
||||
if (IS_ERR(priv->regmap))
|
||||
return PTR_ERR(priv->regmap);
|
||||
|
||||
priv->vbus = devm_regulator_get_optional(dev, "vbus");
|
||||
if (IS_ERR(priv->vbus)) {
|
||||
if (PTR_ERR(priv->vbus) == -EPROBE_DEFER)
|
||||
return PTR_ERR(priv->vbus);
|
||||
priv->vbus = NULL;
|
||||
}
|
||||
|
||||
priv->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(priv->clk))
|
||||
return PTR_ERR(priv->clk);
|
||||
|
||||
ret = clk_prepare_enable(priv->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
devm_add_action_or_reset(dev,
|
||||
(void(*)(void *))clk_disable_unprepare,
|
||||
priv->clk);
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
priv->dev = dev;
|
||||
|
||||
priv->reset = devm_reset_control_get(dev, NULL);
|
||||
if (IS_ERR(priv->reset)) {
|
||||
ret = PTR_ERR(priv->reset);
|
||||
dev_err(dev, "failed to get device reset, err=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = reset_control_reset(priv->reset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = dwc3_meson_g12a_get_phys(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (priv->vbus) {
|
||||
ret = regulator_enable(priv->vbus);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Get dr_mode */
|
||||
priv->otg_mode = usb_get_dr_mode(dev);
|
||||
|
||||
dwc3_meson_g12a_usb_init(priv);
|
||||
|
||||
/* Init PHYs */
|
||||
for (i = 0 ; i < PHY_COUNT ; ++i) {
|
||||
ret = phy_init(priv->phys[i]);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Set PHY Power */
|
||||
for (i = 0 ; i < PHY_COUNT ; ++i) {
|
||||
ret = phy_power_on(priv->phys[i]);
|
||||
if (ret)
|
||||
goto err_phys_exit;
|
||||
}
|
||||
|
||||
ret = of_platform_populate(np, NULL, NULL, dev);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(priv->clk);
|
||||
goto err_phys_power;
|
||||
}
|
||||
|
||||
/* Setup OTG mode corresponding to the ID pin */
|
||||
if (priv->otg_mode == USB_DR_MODE_OTG) {
|
||||
/* TOFIX Handle ID mode toggling via IRQ */
|
||||
otg_id = dwc3_meson_g12a_get_id(priv);
|
||||
if (otg_id != priv->otg_phy_mode) {
|
||||
if (dwc3_meson_g12a_otg_mode_set(priv, otg_id))
|
||||
dev_warn(dev, "Failed to switch OTG mode\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup role switcher */
|
||||
priv->switch_desc.usb2_port = dwc3_meson_g12_find_child(dev,
|
||||
"snps,dwc3");
|
||||
priv->switch_desc.udc = dwc3_meson_g12_find_child(dev, "snps,dwc2");
|
||||
priv->switch_desc.allow_userspace_control = true;
|
||||
priv->switch_desc.set = dwc3_meson_g12a_role_set;
|
||||
priv->switch_desc.get = dwc3_meson_g12a_role_get;
|
||||
|
||||
priv->role_switch = usb_role_switch_register(dev, &priv->switch_desc);
|
||||
if (IS_ERR(priv->role_switch))
|
||||
dev_warn(dev, "Unable to register Role Switch\n");
|
||||
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_get_sync(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_phys_power:
|
||||
for (i = 0 ; i < PHY_COUNT ; ++i)
|
||||
phy_power_off(priv->phys[i]);
|
||||
|
||||
err_phys_exit:
|
||||
for (i = 0 ; i < PHY_COUNT ; ++i)
|
||||
phy_exit(priv->phys[i]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dwc3_meson_g12a_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3_meson_g12a *priv = platform_get_drvdata(pdev);
|
||||
struct device *dev = &pdev->dev;
|
||||
int i;
|
||||
|
||||
usb_role_switch_unregister(priv->role_switch);
|
||||
|
||||
of_platform_depopulate(dev);
|
||||
|
||||
for (i = 0 ; i < PHY_COUNT ; ++i) {
|
||||
phy_power_off(priv->phys[i]);
|
||||
phy_exit(priv->phys[i]);
|
||||
}
|
||||
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_put_noidle(dev);
|
||||
pm_runtime_set_suspended(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused dwc3_meson_g12a_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable(priv->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused dwc3_meson_g12a_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
|
||||
|
||||
return clk_enable(priv->clk);
|
||||
}
|
||||
|
||||
static int __maybe_unused dwc3_meson_g12a_suspend(struct device *dev)
|
||||
{
|
||||
struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
|
||||
int i;
|
||||
|
||||
for (i = 0 ; i < PHY_COUNT ; ++i) {
|
||||
phy_power_off(priv->phys[i]);
|
||||
phy_exit(priv->phys[i]);
|
||||
}
|
||||
|
||||
reset_control_assert(priv->reset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev)
|
||||
{
|
||||
struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
|
||||
int i, ret;
|
||||
|
||||
reset_control_deassert(priv->reset);
|
||||
|
||||
dwc3_meson_g12a_usb_init(priv);
|
||||
|
||||
/* Init PHYs */
|
||||
for (i = 0 ; i < PHY_COUNT ; ++i) {
|
||||
ret = phy_init(priv->phys[i]);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Set PHY Power */
|
||||
for (i = 0 ; i < PHY_COUNT ; ++i) {
|
||||
ret = phy_power_on(priv->phys[i]);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops dwc3_meson_g12a_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(dwc3_meson_g12a_suspend, dwc3_meson_g12a_resume)
|
||||
SET_RUNTIME_PM_OPS(dwc3_meson_g12a_runtime_suspend,
|
||||
dwc3_meson_g12a_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct of_device_id dwc3_meson_g12a_match[] = {
|
||||
{ .compatible = "amlogic,meson-g12a-usb-ctrl" },
|
||||
{ /* Sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dwc3_meson_g12a_match);
|
||||
|
||||
static struct platform_driver dwc3_meson_g12a_driver = {
|
||||
.probe = dwc3_meson_g12a_probe,
|
||||
.remove = dwc3_meson_g12a_remove,
|
||||
.driver = {
|
||||
.name = "dwc3-meson-g12a",
|
||||
.of_match_table = dwc3_meson_g12a_match,
|
||||
.pm = &dwc3_meson_g12a_dev_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(dwc3_meson_g12a_driver);
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("Amlogic Meson G12A USB Glue Layer");
|
||||
MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
|
@ -24,59 +24,13 @@
|
||||
|
||||
struct dwc3_of_simple {
|
||||
struct device *dev;
|
||||
struct clk **clks;
|
||||
struct clk_bulk_data *clks;
|
||||
int num_clocks;
|
||||
struct reset_control *resets;
|
||||
bool pulse_resets;
|
||||
bool need_reset;
|
||||
};
|
||||
|
||||
static int dwc3_of_simple_clk_init(struct dwc3_of_simple *simple, int count)
|
||||
{
|
||||
struct device *dev = simple->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
int i;
|
||||
|
||||
simple->num_clocks = count;
|
||||
|
||||
if (!count)
|
||||
return 0;
|
||||
|
||||
simple->clks = devm_kcalloc(dev, simple->num_clocks,
|
||||
sizeof(struct clk *), GFP_KERNEL);
|
||||
if (!simple->clks)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < simple->num_clocks; i++) {
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
|
||||
clk = of_clk_get(np, i);
|
||||
if (IS_ERR(clk)) {
|
||||
while (--i >= 0) {
|
||||
clk_disable_unprepare(simple->clks[i]);
|
||||
clk_put(simple->clks[i]);
|
||||
}
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret < 0) {
|
||||
while (--i >= 0) {
|
||||
clk_disable_unprepare(simple->clks[i]);
|
||||
clk_put(simple->clks[i]);
|
||||
}
|
||||
clk_put(clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
simple->clks[i] = clk;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_of_simple_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3_of_simple *simple;
|
||||
@ -84,7 +38,6 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
|
||||
struct device_node *np = dev->of_node;
|
||||
|
||||
int ret;
|
||||
int i;
|
||||
bool shared_resets = false;
|
||||
|
||||
simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL);
|
||||
@ -124,20 +77,18 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
|
||||
goto err_resetc_put;
|
||||
}
|
||||
|
||||
ret = dwc3_of_simple_clk_init(simple, of_count_phandle_with_args(np,
|
||||
"clocks", "#clock-cells"));
|
||||
ret = clk_bulk_get_all(simple->dev, &simple->clks);
|
||||
if (ret < 0)
|
||||
goto err_resetc_assert;
|
||||
|
||||
simple->num_clocks = ret;
|
||||
ret = clk_bulk_prepare_enable(simple->num_clocks, simple->clks);
|
||||
if (ret)
|
||||
goto err_resetc_assert;
|
||||
|
||||
ret = of_platform_populate(np, NULL, NULL, dev);
|
||||
if (ret) {
|
||||
for (i = 0; i < simple->num_clocks; i++) {
|
||||
clk_disable_unprepare(simple->clks[i]);
|
||||
clk_put(simple->clks[i]);
|
||||
}
|
||||
|
||||
goto err_resetc_assert;
|
||||
}
|
||||
if (ret)
|
||||
goto err_clk_put;
|
||||
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
@ -145,6 +96,10 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
|
||||
|
||||
return 0;
|
||||
|
||||
err_clk_put:
|
||||
clk_bulk_disable_unprepare(simple->num_clocks, simple->clks);
|
||||
clk_bulk_put_all(simple->num_clocks, simple->clks);
|
||||
|
||||
err_resetc_assert:
|
||||
if (!simple->pulse_resets)
|
||||
reset_control_assert(simple->resets);
|
||||
@ -158,14 +113,11 @@ static int dwc3_of_simple_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3_of_simple *simple = platform_get_drvdata(pdev);
|
||||
struct device *dev = &pdev->dev;
|
||||
int i;
|
||||
|
||||
of_platform_depopulate(dev);
|
||||
|
||||
for (i = 0; i < simple->num_clocks; i++) {
|
||||
clk_disable_unprepare(simple->clks[i]);
|
||||
clk_put(simple->clks[i]);
|
||||
}
|
||||
clk_bulk_disable_unprepare(simple->num_clocks, simple->clks);
|
||||
clk_bulk_put_all(simple->num_clocks, simple->clks);
|
||||
simple->num_clocks = 0;
|
||||
|
||||
if (!simple->pulse_resets)
|
||||
@ -183,10 +135,8 @@ static int dwc3_of_simple_remove(struct platform_device *pdev)
|
||||
static int __maybe_unused dwc3_of_simple_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct dwc3_of_simple *simple = dev_get_drvdata(dev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < simple->num_clocks; i++)
|
||||
clk_disable(simple->clks[i]);
|
||||
clk_bulk_disable(simple->num_clocks, simple->clks);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -194,19 +144,8 @@ static int __maybe_unused dwc3_of_simple_runtime_suspend(struct device *dev)
|
||||
static int __maybe_unused dwc3_of_simple_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct dwc3_of_simple *simple = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < simple->num_clocks; i++) {
|
||||
ret = clk_enable(simple->clks[i]);
|
||||
if (ret < 0) {
|
||||
while (--i >= 0)
|
||||
clk_disable(simple->clks[i]);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return clk_bulk_enable(simple->num_clocks, simple->clks);
|
||||
}
|
||||
|
||||
static int __maybe_unused dwc3_of_simple_suspend(struct device *dev)
|
||||
|
@ -2863,7 +2863,7 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
|
||||
"LPM Erratum not available on dwc3 revisions < 2.40a\n");
|
||||
|
||||
if (dwc->has_lpm_erratum && dwc->revision >= DWC3_REVISION_240A)
|
||||
reg |= DWC3_DCTL_LPM_ERRATA(dwc->lpm_nyet_threshold);
|
||||
reg |= DWC3_DCTL_NYET_THRES(dwc->lpm_nyet_threshold);
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
} else {
|
||||
@ -3301,6 +3301,7 @@ int dwc3_gadget_init(struct dwc3 *dwc)
|
||||
dwc->gadget.sg_supported = true;
|
||||
dwc->gadget.name = "dwc3-gadget";
|
||||
dwc->gadget.is_otg = dwc->dr_mode == USB_DR_MODE_OTG;
|
||||
dwc->gadget.lpm_capable = true;
|
||||
|
||||
/*
|
||||
* FIXME We might be setting max_speed to <SUPER, however versions
|
||||
@ -3384,8 +3385,6 @@ int dwc3_gadget_suspend(struct dwc3 *dwc)
|
||||
dwc3_disconnect_gadget(dwc);
|
||||
__dwc3_gadget_stop(dwc);
|
||||
|
||||
synchronize_irq(dwc->irq_gadget);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1133,7 +1133,8 @@ error_lock:
|
||||
error_mutex:
|
||||
mutex_unlock(&epfile->mutex);
|
||||
error:
|
||||
ffs_free_buffer(io_data);
|
||||
if (ret != -EIOCBQUEUED) /* don't free if there is iocb queued */
|
||||
ffs_free_buffer(io_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "u_ether.h"
|
||||
#include "u_ether_configfs.h"
|
||||
#include "u_ncm.h"
|
||||
#include "configfs.h"
|
||||
|
||||
/*
|
||||
* This function is a "CDC Network Control Model" (CDC NCM) Ethernet link.
|
||||
@ -35,9 +36,7 @@
|
||||
|
||||
/* to trigger crc/non-crc ndp signature */
|
||||
|
||||
#define NCM_NDP_HDR_CRC_MASK 0x01000000
|
||||
#define NCM_NDP_HDR_CRC 0x01000000
|
||||
#define NCM_NDP_HDR_NOCRC 0x00000000
|
||||
|
||||
enum ncm_notify_state {
|
||||
NCM_NOTIFY_NONE, /* don't notify */
|
||||
@ -526,6 +525,7 @@ static inline void ncm_reset_values(struct f_ncm *ncm)
|
||||
{
|
||||
ncm->parser_opts = &ndp16_opts;
|
||||
ncm->is_crc = false;
|
||||
ncm->ndp_sign = ncm->parser_opts->ndp_sign;
|
||||
ncm->port.cdc_filter = DEFAULT_FILTER;
|
||||
|
||||
/* doesn't make sense for ncm, fixed size used */
|
||||
@ -805,25 +805,20 @@ static int ncm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
|
||||
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
|
||||
| USB_CDC_SET_CRC_MODE:
|
||||
{
|
||||
int ndp_hdr_crc = 0;
|
||||
|
||||
if (w_length != 0 || w_index != ncm->ctrl_id)
|
||||
goto invalid;
|
||||
switch (w_value) {
|
||||
case 0x0000:
|
||||
ncm->is_crc = false;
|
||||
ndp_hdr_crc = NCM_NDP_HDR_NOCRC;
|
||||
DBG(cdev, "non-CRC mode selected\n");
|
||||
break;
|
||||
case 0x0001:
|
||||
ncm->is_crc = true;
|
||||
ndp_hdr_crc = NCM_NDP_HDR_CRC;
|
||||
DBG(cdev, "CRC mode selected\n");
|
||||
break;
|
||||
default:
|
||||
goto invalid;
|
||||
}
|
||||
ncm->ndp_sign = ncm->parser_opts->ndp_sign | ndp_hdr_crc;
|
||||
value = 0;
|
||||
break;
|
||||
}
|
||||
@ -840,6 +835,8 @@ invalid:
|
||||
ctrl->bRequestType, ctrl->bRequest,
|
||||
w_value, w_index, w_length);
|
||||
}
|
||||
ncm->ndp_sign = ncm->parser_opts->ndp_sign |
|
||||
(ncm->is_crc ? NCM_NDP_HDR_CRC : 0);
|
||||
|
||||
/* respond with data transfer or status phase? */
|
||||
if (value >= 0) {
|
||||
@ -1395,6 +1392,16 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
return -EINVAL;
|
||||
|
||||
ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst);
|
||||
|
||||
if (cdev->use_os_string) {
|
||||
f->os_desc_table = kzalloc(sizeof(*f->os_desc_table),
|
||||
GFP_KERNEL);
|
||||
if (!f->os_desc_table)
|
||||
return -ENOMEM;
|
||||
f->os_desc_n = 1;
|
||||
f->os_desc_table[0].os_desc = &ncm_opts->ncm_os_desc;
|
||||
}
|
||||
|
||||
/*
|
||||
* in drivers/usb/gadget/configfs.c:configfs_composite_bind()
|
||||
* configurations are bound in sequence with list_for_each_entry,
|
||||
@ -1408,13 +1415,15 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
status = gether_register_netdev(ncm_opts->net);
|
||||
mutex_unlock(&ncm_opts->lock);
|
||||
if (status)
|
||||
return status;
|
||||
goto fail;
|
||||
ncm_opts->bound = true;
|
||||
}
|
||||
us = usb_gstrings_attach(cdev, ncm_strings,
|
||||
ARRAY_SIZE(ncm_string_defs));
|
||||
if (IS_ERR(us))
|
||||
return PTR_ERR(us);
|
||||
if (IS_ERR(us)) {
|
||||
status = PTR_ERR(us);
|
||||
goto fail;
|
||||
}
|
||||
ncm_control_intf.iInterface = us[STRING_CTRL_IDX].id;
|
||||
ncm_data_nop_intf.iInterface = us[STRING_DATA_IDX].id;
|
||||
ncm_data_intf.iInterface = us[STRING_DATA_IDX].id;
|
||||
@ -1431,6 +1440,10 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
ncm_control_intf.bInterfaceNumber = status;
|
||||
ncm_union_desc.bMasterInterface0 = status;
|
||||
|
||||
if (cdev->use_os_string)
|
||||
f->os_desc_table[0].if_id =
|
||||
ncm_iad_desc.bFirstInterface;
|
||||
|
||||
status = usb_interface_id(c, f);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
@ -1510,6 +1523,9 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
kfree(f->os_desc_table);
|
||||
f->os_desc_n = 0;
|
||||
|
||||
if (ncm->notify_req) {
|
||||
kfree(ncm->notify_req->buf);
|
||||
usb_ep_free_request(ncm->notify, ncm->notify_req);
|
||||
@ -1564,16 +1580,22 @@ static void ncm_free_inst(struct usb_function_instance *f)
|
||||
gether_cleanup(netdev_priv(opts->net));
|
||||
else
|
||||
free_netdev(opts->net);
|
||||
kfree(opts->ncm_interf_group);
|
||||
kfree(opts);
|
||||
}
|
||||
|
||||
static struct usb_function_instance *ncm_alloc_inst(void)
|
||||
{
|
||||
struct f_ncm_opts *opts;
|
||||
struct usb_os_desc *descs[1];
|
||||
char *names[1];
|
||||
struct config_group *ncm_interf_group;
|
||||
|
||||
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
|
||||
if (!opts)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
opts->ncm_os_desc.ext_compat_id = opts->ncm_ext_compat_id;
|
||||
|
||||
mutex_init(&opts->lock);
|
||||
opts->func_inst.free_func_inst = ncm_free_inst;
|
||||
opts->net = gether_setup_default();
|
||||
@ -1582,8 +1604,20 @@ static struct usb_function_instance *ncm_alloc_inst(void)
|
||||
kfree(opts);
|
||||
return ERR_CAST(net);
|
||||
}
|
||||
INIT_LIST_HEAD(&opts->ncm_os_desc.ext_prop);
|
||||
|
||||
descs[0] = &opts->ncm_os_desc;
|
||||
names[0] = "ncm";
|
||||
|
||||
config_group_init_type_name(&opts->func_inst.group, "", &ncm_func_type);
|
||||
ncm_interf_group =
|
||||
usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs,
|
||||
names, THIS_MODULE);
|
||||
if (IS_ERR(ncm_interf_group)) {
|
||||
ncm_free_inst(&opts->func_inst);
|
||||
return ERR_CAST(ncm_interf_group);
|
||||
}
|
||||
opts->ncm_interf_group = ncm_interf_group;
|
||||
|
||||
return &opts->func_inst;
|
||||
}
|
||||
@ -1609,6 +1643,9 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
|
||||
hrtimer_cancel(&ncm->task_timer);
|
||||
|
||||
kfree(f->os_desc_table);
|
||||
f->os_desc_n = 0;
|
||||
|
||||
ncm_string_defs[0].id = 0;
|
||||
usb_free_all_descriptors(f);
|
||||
|
||||
|
@ -54,8 +54,8 @@ static struct uac1_ac_header_descriptor_1 ac_header_desc = {
|
||||
.bLength = UAC_DT_AC_HEADER_LENGTH,
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubtype = UAC_HEADER,
|
||||
.bcdADC = __constant_cpu_to_le16(0x0100),
|
||||
.wTotalLength = __constant_cpu_to_le16(UAC_DT_TOTAL_LENGTH),
|
||||
.bcdADC = cpu_to_le16(0x0100),
|
||||
.wTotalLength = cpu_to_le16(UAC_DT_TOTAL_LENGTH),
|
||||
.bInCollection = F_AUDIO_NUM_INTERFACES,
|
||||
.baInterfaceNr = {
|
||||
/* Interface number of the first AudioStream interface */
|
||||
@ -183,7 +183,7 @@ static struct uac_iso_endpoint_descriptor as_iso_out_desc = {
|
||||
.bDescriptorSubtype = UAC_EP_GENERAL,
|
||||
.bmAttributes = 1,
|
||||
.bLockDelayUnits = 1,
|
||||
.wLockDelay = __constant_cpu_to_le16(1),
|
||||
.wLockDelay = cpu_to_le16(1),
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *f_audio_desc[] = {
|
||||
|
@ -20,6 +20,9 @@ struct f_ncm_opts {
|
||||
struct net_device *net;
|
||||
bool bound;
|
||||
|
||||
struct config_group *ncm_interf_group;
|
||||
struct usb_os_desc ncm_os_desc;
|
||||
char ncm_ext_compat_id[16];
|
||||
/*
|
||||
* Read/write access to configfs attributes is handled by configfs.
|
||||
*
|
||||
|
@ -358,8 +358,20 @@ static inline u32 usba_int_enb_get(struct usba_udc *udc)
|
||||
return udc->int_enb_cache;
|
||||
}
|
||||
|
||||
static inline void usba_int_enb_set(struct usba_udc *udc, u32 val)
|
||||
static inline void usba_int_enb_set(struct usba_udc *udc, u32 mask)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = udc->int_enb_cache | mask;
|
||||
usba_writel(udc, INT_ENB, val);
|
||||
udc->int_enb_cache = val;
|
||||
}
|
||||
|
||||
static inline void usba_int_enb_clear(struct usba_udc *udc, u32 mask)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = udc->int_enb_cache & ~mask;
|
||||
usba_writel(udc, INT_ENB, val);
|
||||
udc->int_enb_cache = val;
|
||||
}
|
||||
@ -629,14 +641,12 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
|
||||
if (ep->can_dma) {
|
||||
u32 ctrl;
|
||||
|
||||
usba_int_enb_set(udc, usba_int_enb_get(udc) |
|
||||
USBA_BF(EPT_INT, 1 << ep->index) |
|
||||
usba_int_enb_set(udc, USBA_BF(EPT_INT, 1 << ep->index) |
|
||||
USBA_BF(DMA_INT, 1 << ep->index));
|
||||
ctrl = USBA_AUTO_VALID | USBA_INTDIS_DMA;
|
||||
usba_ep_writel(ep, CTL_ENB, ctrl);
|
||||
} else {
|
||||
usba_int_enb_set(udc, usba_int_enb_get(udc) |
|
||||
USBA_BF(EPT_INT, 1 << ep->index));
|
||||
usba_int_enb_set(udc, USBA_BF(EPT_INT, 1 << ep->index));
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
@ -680,8 +690,7 @@ static int usba_ep_disable(struct usb_ep *_ep)
|
||||
usba_dma_readl(ep, STATUS);
|
||||
}
|
||||
usba_ep_writel(ep, CTL_DIS, USBA_EPT_ENABLE);
|
||||
usba_int_enb_set(udc, usba_int_enb_get(udc) &
|
||||
~USBA_BF(EPT_INT, 1 << ep->index));
|
||||
usba_int_enb_clear(udc, USBA_BF(EPT_INT, 1 << ep->index));
|
||||
|
||||
request_complete_list(ep, &req_list, -ESHUTDOWN);
|
||||
|
||||
@ -1694,6 +1703,9 @@ static void usba_dma_irq(struct usba_udc *udc, struct usba_ep *ep)
|
||||
}
|
||||
}
|
||||
|
||||
static int start_clock(struct usba_udc *udc);
|
||||
static void stop_clock(struct usba_udc *udc);
|
||||
|
||||
static irqreturn_t usba_udc_irq(int irq, void *devid)
|
||||
{
|
||||
struct usba_udc *udc = devid;
|
||||
@ -1708,10 +1720,13 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
|
||||
DBG(DBG_INT, "irq, status=%#08x\n", status);
|
||||
|
||||
if (status & USBA_DET_SUSPEND) {
|
||||
usba_writel(udc, INT_CLR, USBA_DET_SUSPEND|USBA_WAKE_UP);
|
||||
usba_int_enb_set(udc, USBA_WAKE_UP);
|
||||
usba_int_enb_clear(udc, USBA_DET_SUSPEND);
|
||||
udc->suspended = true;
|
||||
toggle_bias(udc, 0);
|
||||
usba_writel(udc, INT_CLR, USBA_DET_SUSPEND);
|
||||
usba_int_enb_set(udc, int_enb | USBA_WAKE_UP);
|
||||
udc->bias_pulse_needed = true;
|
||||
stop_clock(udc);
|
||||
DBG(DBG_BUS, "Suspend detected\n");
|
||||
if (udc->gadget.speed != USB_SPEED_UNKNOWN
|
||||
&& udc->driver && udc->driver->suspend) {
|
||||
@ -1722,14 +1737,17 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
|
||||
}
|
||||
|
||||
if (status & USBA_WAKE_UP) {
|
||||
start_clock(udc);
|
||||
toggle_bias(udc, 1);
|
||||
usba_writel(udc, INT_CLR, USBA_WAKE_UP);
|
||||
usba_int_enb_set(udc, int_enb & ~USBA_WAKE_UP);
|
||||
DBG(DBG_BUS, "Wake Up CPU detected\n");
|
||||
}
|
||||
|
||||
if (status & USBA_END_OF_RESUME) {
|
||||
udc->suspended = false;
|
||||
usba_writel(udc, INT_CLR, USBA_END_OF_RESUME);
|
||||
usba_int_enb_clear(udc, USBA_WAKE_UP);
|
||||
usba_int_enb_set(udc, USBA_DET_SUSPEND);
|
||||
generate_bias_pulse(udc);
|
||||
DBG(DBG_BUS, "Resume detected\n");
|
||||
if (udc->gadget.speed != USB_SPEED_UNKNOWN
|
||||
@ -1744,6 +1762,8 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
|
||||
if (dma_status) {
|
||||
int i;
|
||||
|
||||
usba_int_enb_set(udc, USBA_DET_SUSPEND);
|
||||
|
||||
for (i = 1; i <= USBA_NR_DMAS; i++)
|
||||
if (dma_status & (1 << i))
|
||||
usba_dma_irq(udc, &udc->usba_ep[i]);
|
||||
@ -1753,6 +1773,8 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
|
||||
if (ep_status) {
|
||||
int i;
|
||||
|
||||
usba_int_enb_set(udc, USBA_DET_SUSPEND);
|
||||
|
||||
for (i = 0; i < udc->num_ep; i++)
|
||||
if (ep_status & (1 << i)) {
|
||||
if (ep_is_control(&udc->usba_ep[i]))
|
||||
@ -1766,7 +1788,9 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
|
||||
struct usba_ep *ep0, *ep;
|
||||
int i, n;
|
||||
|
||||
usba_writel(udc, INT_CLR, USBA_END_OF_RESET);
|
||||
usba_writel(udc, INT_CLR,
|
||||
USBA_END_OF_RESET|USBA_END_OF_RESUME
|
||||
|USBA_DET_SUSPEND|USBA_WAKE_UP);
|
||||
generate_bias_pulse(udc);
|
||||
reset_all_endpoints(udc);
|
||||
|
||||
@ -1793,7 +1817,12 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
|
||||
| USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE)));
|
||||
usba_ep_writel(ep0, CTL_ENB,
|
||||
USBA_EPT_ENABLE | USBA_RX_SETUP);
|
||||
usba_int_enb_set(udc, int_enb | USBA_BF(EPT_INT, 1) |
|
||||
|
||||
/* If we get reset while suspended... */
|
||||
udc->suspended = false;
|
||||
usba_int_enb_clear(udc, USBA_WAKE_UP);
|
||||
|
||||
usba_int_enb_set(udc, USBA_BF(EPT_INT, 1) |
|
||||
USBA_DET_SUSPEND | USBA_END_OF_RESUME);
|
||||
|
||||
/*
|
||||
@ -1827,6 +1856,8 @@ static int start_clock(struct usba_udc *udc)
|
||||
if (udc->clocked)
|
||||
return 0;
|
||||
|
||||
pm_stay_awake(&udc->pdev->dev);
|
||||
|
||||
ret = clk_prepare_enable(udc->pclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -1849,6 +1880,8 @@ static void stop_clock(struct usba_udc *udc)
|
||||
clk_disable_unprepare(udc->pclk);
|
||||
|
||||
udc->clocked = false;
|
||||
|
||||
pm_relax(&udc->pdev->dev);
|
||||
}
|
||||
|
||||
static int usba_start(struct usba_udc *udc)
|
||||
@ -1860,9 +1893,19 @@ static int usba_start(struct usba_udc *udc)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (udc->suspended)
|
||||
return 0;
|
||||
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
toggle_bias(udc, 1);
|
||||
usba_writel(udc, CTRL, USBA_ENABLE_MASK);
|
||||
/* Clear all requested and pending interrupts... */
|
||||
usba_writel(udc, INT_ENB, 0);
|
||||
udc->int_enb_cache = 0;
|
||||
usba_writel(udc, INT_CLR,
|
||||
USBA_END_OF_RESET|USBA_END_OF_RESUME
|
||||
|USBA_DET_SUSPEND|USBA_WAKE_UP);
|
||||
/* ...and enable just 'reset' IRQ to get us started */
|
||||
usba_int_enb_set(udc, USBA_END_OF_RESET);
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
||||
@ -1873,6 +1916,9 @@ static void usba_stop(struct usba_udc *udc)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (udc->suspended)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
udc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
reset_all_endpoints(udc);
|
||||
@ -1900,6 +1946,7 @@ static irqreturn_t usba_vbus_irq_thread(int irq, void *devid)
|
||||
if (vbus) {
|
||||
usba_start(udc);
|
||||
} else {
|
||||
udc->suspended = false;
|
||||
usba_stop(udc);
|
||||
|
||||
if (udc->driver->disconnect)
|
||||
@ -1963,6 +2010,7 @@ static int atmel_usba_stop(struct usb_gadget *gadget)
|
||||
if (fifo_mode == 0)
|
||||
udc->configured_ep = 1;
|
||||
|
||||
udc->suspended = false;
|
||||
usba_stop(udc);
|
||||
|
||||
udc->driver = NULL;
|
||||
@ -2276,6 +2324,7 @@ static int usba_udc_suspend(struct device *dev)
|
||||
mutex_lock(&udc->vbus_mutex);
|
||||
|
||||
if (!device_may_wakeup(dev)) {
|
||||
udc->suspended = false;
|
||||
usba_stop(udc);
|
||||
goto out;
|
||||
}
|
||||
@ -2285,10 +2334,13 @@ static int usba_udc_suspend(struct device *dev)
|
||||
* to request vbus irq, assuming always on.
|
||||
*/
|
||||
if (udc->vbus_pin) {
|
||||
/* FIXME: right to stop here...??? */
|
||||
usba_stop(udc);
|
||||
enable_irq_wake(gpiod_to_irq(udc->vbus_pin));
|
||||
}
|
||||
|
||||
enable_irq_wake(udc->irq);
|
||||
|
||||
out:
|
||||
mutex_unlock(&udc->vbus_mutex);
|
||||
return 0;
|
||||
@ -2302,8 +2354,12 @@ static int usba_udc_resume(struct device *dev)
|
||||
if (!udc->driver)
|
||||
return 0;
|
||||
|
||||
if (device_may_wakeup(dev) && udc->vbus_pin)
|
||||
disable_irq_wake(gpiod_to_irq(udc->vbus_pin));
|
||||
if (device_may_wakeup(dev)) {
|
||||
if (udc->vbus_pin)
|
||||
disable_irq_wake(gpiod_to_irq(udc->vbus_pin));
|
||||
|
||||
disable_irq_wake(udc->irq);
|
||||
}
|
||||
|
||||
/* If Vbus is present, enable the controller and wait for reset */
|
||||
mutex_lock(&udc->vbus_mutex);
|
||||
|
@ -331,6 +331,7 @@ struct usba_udc {
|
||||
struct usba_ep *usba_ep;
|
||||
bool bias_pulse_needed;
|
||||
bool clocked;
|
||||
bool suspended;
|
||||
|
||||
u16 devstatus;
|
||||
|
||||
|
@ -965,8 +965,18 @@ static int dummy_udc_start(struct usb_gadget *g,
|
||||
struct dummy_hcd *dum_hcd = gadget_to_dummy_hcd(g);
|
||||
struct dummy *dum = dum_hcd->dum;
|
||||
|
||||
if (driver->max_speed == USB_SPEED_UNKNOWN)
|
||||
switch (g->speed) {
|
||||
/* All the speeds we support */
|
||||
case USB_SPEED_LOW:
|
||||
case USB_SPEED_FULL:
|
||||
case USB_SPEED_HIGH:
|
||||
case USB_SPEED_SUPER:
|
||||
break;
|
||||
default:
|
||||
dev_err(dummy_dev(dum_hcd), "Unsupported driver max speed %d\n",
|
||||
driver->max_speed);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* SLAVE side init ... the layer above hardware, which
|
||||
@ -1770,9 +1780,10 @@ static void dummy_timer(struct timer_list *t)
|
||||
/* Bus speed is 500000 bytes/ms, so use a little less */
|
||||
total = 490000;
|
||||
break;
|
||||
default:
|
||||
default: /* Can't happen */
|
||||
dev_err(dummy_dev(dum_hcd), "bogus device speed\n");
|
||||
return;
|
||||
total = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* FIXME if HZ != 1000 this will probably misbehave ... */
|
||||
@ -1814,7 +1825,7 @@ restart:
|
||||
|
||||
/* Used up this frame's bandwidth? */
|
||||
if (total <= 0)
|
||||
break;
|
||||
continue;
|
||||
|
||||
/* find the gadget's ep for this request (if configured) */
|
||||
address = usb_pipeendpoint (urb->pipe);
|
||||
|
@ -115,6 +115,11 @@ struct lpc32xx_ep {
|
||||
bool wedge;
|
||||
};
|
||||
|
||||
enum atx_type {
|
||||
ISP1301,
|
||||
STOTG04,
|
||||
};
|
||||
|
||||
/*
|
||||
* Common UDC structure
|
||||
*/
|
||||
@ -129,8 +134,6 @@ struct lpc32xx_udc {
|
||||
|
||||
/* Board and device specific */
|
||||
struct lpc32xx_usbd_cfg *board;
|
||||
u32 io_p_start;
|
||||
u32 io_p_size;
|
||||
void __iomem *udp_baseaddr;
|
||||
int udp_irq[4];
|
||||
struct clk *usb_slv_clk;
|
||||
@ -151,10 +154,10 @@ struct lpc32xx_udc {
|
||||
u8 last_vbus;
|
||||
int pullup;
|
||||
int poweron;
|
||||
enum atx_type atx;
|
||||
|
||||
/* Work queues related to I2C support */
|
||||
struct work_struct pullup_job;
|
||||
struct work_struct vbus_job;
|
||||
struct work_struct power_job;
|
||||
|
||||
/* USB device peripheral - various */
|
||||
@ -553,6 +556,15 @@ static inline void remove_debug_file(struct lpc32xx_udc *udc) {}
|
||||
/* Primary initialization sequence for the ISP1301 transceiver */
|
||||
static void isp1301_udc_configure(struct lpc32xx_udc *udc)
|
||||
{
|
||||
u8 value;
|
||||
s32 vendor, product;
|
||||
|
||||
vendor = i2c_smbus_read_word_data(udc->isp1301_i2c_client, 0x00);
|
||||
product = i2c_smbus_read_word_data(udc->isp1301_i2c_client, 0x02);
|
||||
|
||||
if (vendor == 0x0483 && product == 0xa0c4)
|
||||
udc->atx = STOTG04;
|
||||
|
||||
/* LPC32XX only supports DAT_SE0 USB mode */
|
||||
/* This sequence is important */
|
||||
|
||||
@ -572,8 +584,12 @@ static void isp1301_udc_configure(struct lpc32xx_udc *udc)
|
||||
*/
|
||||
i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
|
||||
(ISP1301_I2C_MODE_CONTROL_2 | ISP1301_I2C_REG_CLEAR_ADDR), ~0);
|
||||
|
||||
value = MC2_BI_DI;
|
||||
if (udc->atx != STOTG04)
|
||||
value |= MC2_SPD_SUSP_CTRL;
|
||||
i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
|
||||
ISP1301_I2C_MODE_CONTROL_2, (MC2_BI_DI | MC2_SPD_SUSP_CTRL));
|
||||
ISP1301_I2C_MODE_CONTROL_2, value);
|
||||
|
||||
/* Driver VBUS_DRV high or low depending on board setup */
|
||||
if (udc->board->vbus_drv_pol != 0)
|
||||
@ -601,24 +617,19 @@ static void isp1301_udc_configure(struct lpc32xx_udc *udc)
|
||||
(ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR),
|
||||
OTG1_VBUS_DISCHRG);
|
||||
|
||||
/* Clear and enable VBUS high edge interrupt */
|
||||
i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
|
||||
ISP1301_I2C_INTERRUPT_LATCH | ISP1301_I2C_REG_CLEAR_ADDR, ~0);
|
||||
|
||||
i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
|
||||
ISP1301_I2C_INTERRUPT_FALLING | ISP1301_I2C_REG_CLEAR_ADDR, ~0);
|
||||
i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
|
||||
ISP1301_I2C_INTERRUPT_FALLING, INT_VBUS_VLD);
|
||||
i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
|
||||
ISP1301_I2C_INTERRUPT_RISING | ISP1301_I2C_REG_CLEAR_ADDR, ~0);
|
||||
i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
|
||||
ISP1301_I2C_INTERRUPT_RISING, INT_VBUS_VLD);
|
||||
|
||||
dev_info(udc->dev, "ISP1301 Vendor ID : 0x%04x\n",
|
||||
i2c_smbus_read_word_data(udc->isp1301_i2c_client, 0x00));
|
||||
dev_info(udc->dev, "ISP1301 Product ID : 0x%04x\n",
|
||||
i2c_smbus_read_word_data(udc->isp1301_i2c_client, 0x02));
|
||||
dev_info(udc->dev, "ISP1301 Vendor ID : 0x%04x\n", vendor);
|
||||
dev_info(udc->dev, "ISP1301 Product ID : 0x%04x\n", product);
|
||||
dev_info(udc->dev, "ISP1301 Version ID : 0x%04x\n",
|
||||
i2c_smbus_read_word_data(udc->isp1301_i2c_client, 0x14));
|
||||
|
||||
}
|
||||
|
||||
/* Enables or disables the USB device pullup via the ISP1301 transceiver */
|
||||
@ -661,6 +672,10 @@ static void isp1301_pullup_enable(struct lpc32xx_udc *udc, int en_pullup,
|
||||
/* Powers up or down the ISP1301 transceiver */
|
||||
static void isp1301_set_powerstate(struct lpc32xx_udc *udc, int enable)
|
||||
{
|
||||
/* There is no "global power down" register for stotg04 */
|
||||
if (udc->atx == STOTG04)
|
||||
return;
|
||||
|
||||
if (enable != 0)
|
||||
/* Power up ISP1301 - this ISP1301 will automatically wakeup
|
||||
when VBUS is detected */
|
||||
@ -2830,11 +2845,9 @@ static irqreturn_t lpc32xx_usb_devdma_irq(int irq, void *_udc)
|
||||
* VBUS detection, pullup handler, and Gadget cable state notification
|
||||
*
|
||||
*/
|
||||
static void vbus_work(struct work_struct *work)
|
||||
static void vbus_work(struct lpc32xx_udc *udc)
|
||||
{
|
||||
u8 value;
|
||||
struct lpc32xx_udc *udc = container_of(work, struct lpc32xx_udc,
|
||||
vbus_job);
|
||||
|
||||
if (udc->enabled != 0) {
|
||||
/* Discharge VBUS real quick */
|
||||
@ -2870,18 +2883,13 @@ static void vbus_work(struct work_struct *work)
|
||||
lpc32xx_vbus_session(&udc->gadget, udc->vbus);
|
||||
}
|
||||
}
|
||||
|
||||
/* Re-enable after completion */
|
||||
enable_irq(udc->udp_irq[IRQ_USB_ATX]);
|
||||
}
|
||||
|
||||
static irqreturn_t lpc32xx_usb_vbus_irq(int irq, void *_udc)
|
||||
{
|
||||
struct lpc32xx_udc *udc = _udc;
|
||||
|
||||
/* Defer handling of VBUS IRQ to work queue */
|
||||
disable_irq_nosync(udc->udp_irq[IRQ_USB_ATX]);
|
||||
schedule_work(&udc->vbus_job);
|
||||
vbus_work(udc);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
@ -2890,7 +2898,6 @@ static int lpc32xx_start(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct lpc32xx_udc *udc = to_udc(gadget);
|
||||
int i;
|
||||
|
||||
if (!driver || driver->max_speed < USB_SPEED_FULL || !driver->setup) {
|
||||
dev_err(udc->dev, "bad parameter.\n");
|
||||
@ -2910,22 +2917,25 @@ static int lpc32xx_start(struct usb_gadget *gadget,
|
||||
|
||||
/* Force VBUS process once to check for cable insertion */
|
||||
udc->last_vbus = udc->vbus = 0;
|
||||
schedule_work(&udc->vbus_job);
|
||||
vbus_work(udc);
|
||||
|
||||
/* Do not re-enable ATX IRQ (3) */
|
||||
for (i = IRQ_USB_LP; i < IRQ_USB_ATX; i++)
|
||||
enable_irq(udc->udp_irq[i]);
|
||||
/* enable interrupts */
|
||||
i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
|
||||
ISP1301_I2C_INTERRUPT_FALLING, INT_SESS_VLD | INT_VBUS_VLD);
|
||||
i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
|
||||
ISP1301_I2C_INTERRUPT_RISING, INT_SESS_VLD | INT_VBUS_VLD);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lpc32xx_stop(struct usb_gadget *gadget)
|
||||
{
|
||||
int i;
|
||||
struct lpc32xx_udc *udc = to_udc(gadget);
|
||||
|
||||
for (i = IRQ_USB_LP; i <= IRQ_USB_ATX; i++)
|
||||
disable_irq(udc->udp_irq[i]);
|
||||
i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
|
||||
ISP1301_I2C_INTERRUPT_FALLING | ISP1301_I2C_REG_CLEAR_ADDR, ~0);
|
||||
i2c_smbus_write_byte_data(udc->isp1301_i2c_client,
|
||||
ISP1301_I2C_INTERRUPT_RISING | ISP1301_I2C_REG_CLEAR_ADDR, ~0);
|
||||
|
||||
if (udc->clocked) {
|
||||
spin_lock(&udc->lock);
|
||||
@ -2999,7 +3009,7 @@ static int lpc32xx_udc_probe(struct platform_device *pdev)
|
||||
dma_addr_t dma_handle;
|
||||
struct device_node *isp1301_node;
|
||||
|
||||
udc = kmemdup(&controller_template, sizeof(*udc), GFP_KERNEL);
|
||||
udc = devm_kmemdup(dev, &controller_template, sizeof(*udc), GFP_KERNEL);
|
||||
if (!udc)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -3022,8 +3032,7 @@ static int lpc32xx_udc_probe(struct platform_device *pdev)
|
||||
|
||||
udc->isp1301_i2c_client = isp1301_get_client(isp1301_node);
|
||||
if (!udc->isp1301_i2c_client) {
|
||||
retval = -EPROBE_DEFER;
|
||||
goto phy_fail;
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
dev_info(udc->dev, "ISP1301 I2C device at address 0x%x\n",
|
||||
@ -3032,7 +3041,7 @@ static int lpc32xx_udc_probe(struct platform_device *pdev)
|
||||
pdev->dev.dma_mask = &lpc32xx_usbd_dmamask;
|
||||
retval = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
|
||||
if (retval)
|
||||
goto resource_fail;
|
||||
return retval;
|
||||
|
||||
udc->board = &lpc32xx_usbddata;
|
||||
|
||||
@ -3045,10 +3054,8 @@ static int lpc32xx_udc_probe(struct platform_device *pdev)
|
||||
* IORESOURCE_IRQ, USB transceiver interrupt number
|
||||
*/
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
retval = -ENXIO;
|
||||
goto resource_fail;
|
||||
}
|
||||
if (!res)
|
||||
return -ENXIO;
|
||||
|
||||
spin_lock_init(&udc->lock);
|
||||
|
||||
@ -3058,45 +3065,33 @@ static int lpc32xx_udc_probe(struct platform_device *pdev)
|
||||
if (udc->udp_irq[i] < 0) {
|
||||
dev_err(udc->dev,
|
||||
"irq resource %d not available!\n", i);
|
||||
retval = udc->udp_irq[i];
|
||||
goto irq_fail;
|
||||
return udc->udp_irq[i];
|
||||
}
|
||||
}
|
||||
|
||||
udc->io_p_start = res->start;
|
||||
udc->io_p_size = resource_size(res);
|
||||
if (!request_mem_region(udc->io_p_start, udc->io_p_size, driver_name)) {
|
||||
dev_err(udc->dev, "someone's using UDC memory\n");
|
||||
retval = -EBUSY;
|
||||
goto request_mem_region_fail;
|
||||
}
|
||||
|
||||
udc->udp_baseaddr = ioremap(udc->io_p_start, udc->io_p_size);
|
||||
udc->udp_baseaddr = devm_ioremap_resource(dev, res);
|
||||
if (!udc->udp_baseaddr) {
|
||||
retval = -ENOMEM;
|
||||
dev_err(udc->dev, "IO map failure\n");
|
||||
goto io_map_fail;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Get USB device clock */
|
||||
udc->usb_slv_clk = clk_get(&pdev->dev, NULL);
|
||||
udc->usb_slv_clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(udc->usb_slv_clk)) {
|
||||
dev_err(udc->dev, "failed to acquire USB device clock\n");
|
||||
retval = PTR_ERR(udc->usb_slv_clk);
|
||||
goto usb_clk_get_fail;
|
||||
return PTR_ERR(udc->usb_slv_clk);
|
||||
}
|
||||
|
||||
/* Enable USB device clock */
|
||||
retval = clk_prepare_enable(udc->usb_slv_clk);
|
||||
if (retval < 0) {
|
||||
dev_err(udc->dev, "failed to start USB device clock\n");
|
||||
goto usb_clk_enable_fail;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Setup deferred workqueue data */
|
||||
udc->poweron = udc->pullup = 0;
|
||||
INIT_WORK(&udc->pullup_job, pullup_work);
|
||||
INIT_WORK(&udc->vbus_job, vbus_work);
|
||||
#ifdef CONFIG_PM
|
||||
INIT_WORK(&udc->power_job, power_work);
|
||||
#endif
|
||||
@ -3134,47 +3129,44 @@ static int lpc32xx_udc_probe(struct platform_device *pdev)
|
||||
|
||||
/* Request IRQs - low and high priority USB device IRQs are routed to
|
||||
* the same handler, while the DMA interrupt is routed elsewhere */
|
||||
retval = request_irq(udc->udp_irq[IRQ_USB_LP], lpc32xx_usb_lp_irq,
|
||||
0, "udc_lp", udc);
|
||||
retval = devm_request_irq(dev, udc->udp_irq[IRQ_USB_LP],
|
||||
lpc32xx_usb_lp_irq, 0, "udc_lp", udc);
|
||||
if (retval < 0) {
|
||||
dev_err(udc->dev, "LP request irq %d failed\n",
|
||||
udc->udp_irq[IRQ_USB_LP]);
|
||||
goto irq_lp_fail;
|
||||
goto irq_req_fail;
|
||||
}
|
||||
retval = request_irq(udc->udp_irq[IRQ_USB_HP], lpc32xx_usb_hp_irq,
|
||||
0, "udc_hp", udc);
|
||||
retval = devm_request_irq(dev, udc->udp_irq[IRQ_USB_HP],
|
||||
lpc32xx_usb_hp_irq, 0, "udc_hp", udc);
|
||||
if (retval < 0) {
|
||||
dev_err(udc->dev, "HP request irq %d failed\n",
|
||||
udc->udp_irq[IRQ_USB_HP]);
|
||||
goto irq_hp_fail;
|
||||
goto irq_req_fail;
|
||||
}
|
||||
|
||||
retval = request_irq(udc->udp_irq[IRQ_USB_DEVDMA],
|
||||
lpc32xx_usb_devdma_irq, 0, "udc_dma", udc);
|
||||
retval = devm_request_irq(dev, udc->udp_irq[IRQ_USB_DEVDMA],
|
||||
lpc32xx_usb_devdma_irq, 0, "udc_dma", udc);
|
||||
if (retval < 0) {
|
||||
dev_err(udc->dev, "DEV request irq %d failed\n",
|
||||
udc->udp_irq[IRQ_USB_DEVDMA]);
|
||||
goto irq_dev_fail;
|
||||
goto irq_req_fail;
|
||||
}
|
||||
|
||||
/* The transceiver interrupt is used for VBUS detection and will
|
||||
kick off the VBUS handler function */
|
||||
retval = request_irq(udc->udp_irq[IRQ_USB_ATX], lpc32xx_usb_vbus_irq,
|
||||
0, "udc_otg", udc);
|
||||
retval = devm_request_threaded_irq(dev, udc->udp_irq[IRQ_USB_ATX], NULL,
|
||||
lpc32xx_usb_vbus_irq, IRQF_ONESHOT,
|
||||
"udc_otg", udc);
|
||||
if (retval < 0) {
|
||||
dev_err(udc->dev, "VBUS request irq %d failed\n",
|
||||
udc->udp_irq[IRQ_USB_ATX]);
|
||||
goto irq_xcvr_fail;
|
||||
goto irq_req_fail;
|
||||
}
|
||||
|
||||
/* Initialize wait queue */
|
||||
init_waitqueue_head(&udc->ep_disable_wait_queue);
|
||||
atomic_set(&udc->enabled_ep_cnt, 0);
|
||||
|
||||
/* Keep all IRQs disabled until GadgetFS starts up */
|
||||
for (i = IRQ_USB_LP; i <= IRQ_USB_ATX; i++)
|
||||
disable_irq(udc->udp_irq[i]);
|
||||
|
||||
retval = usb_add_gadget_udc(dev, &udc->gadget);
|
||||
if (retval < 0)
|
||||
goto add_gadget_fail;
|
||||
@ -3190,32 +3182,15 @@ static int lpc32xx_udc_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
|
||||
add_gadget_fail:
|
||||
free_irq(udc->udp_irq[IRQ_USB_ATX], udc);
|
||||
irq_xcvr_fail:
|
||||
free_irq(udc->udp_irq[IRQ_USB_DEVDMA], udc);
|
||||
irq_dev_fail:
|
||||
free_irq(udc->udp_irq[IRQ_USB_HP], udc);
|
||||
irq_hp_fail:
|
||||
free_irq(udc->udp_irq[IRQ_USB_LP], udc);
|
||||
irq_lp_fail:
|
||||
irq_req_fail:
|
||||
dma_pool_destroy(udc->dd_cache);
|
||||
dma_alloc_fail:
|
||||
dma_free_coherent(&pdev->dev, UDCA_BUFF_SIZE,
|
||||
udc->udca_v_base, udc->udca_p_base);
|
||||
i2c_fail:
|
||||
clk_disable_unprepare(udc->usb_slv_clk);
|
||||
usb_clk_enable_fail:
|
||||
clk_put(udc->usb_slv_clk);
|
||||
usb_clk_get_fail:
|
||||
iounmap(udc->udp_baseaddr);
|
||||
io_map_fail:
|
||||
release_mem_region(udc->io_p_start, udc->io_p_size);
|
||||
dev_err(udc->dev, "%s probe failed, %d\n", driver_name, retval);
|
||||
request_mem_region_fail:
|
||||
irq_fail:
|
||||
resource_fail:
|
||||
phy_fail:
|
||||
kfree(udc);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
@ -3231,24 +3206,14 @@ static int lpc32xx_udc_remove(struct platform_device *pdev)
|
||||
udc_disable(udc);
|
||||
pullup(udc, 0);
|
||||
|
||||
free_irq(udc->udp_irq[IRQ_USB_ATX], udc);
|
||||
|
||||
device_init_wakeup(&pdev->dev, 0);
|
||||
remove_debug_file(udc);
|
||||
|
||||
dma_pool_destroy(udc->dd_cache);
|
||||
dma_free_coherent(&pdev->dev, UDCA_BUFF_SIZE,
|
||||
udc->udca_v_base, udc->udca_p_base);
|
||||
free_irq(udc->udp_irq[IRQ_USB_DEVDMA], udc);
|
||||
free_irq(udc->udp_irq[IRQ_USB_HP], udc);
|
||||
free_irq(udc->udp_irq[IRQ_USB_LP], udc);
|
||||
|
||||
clk_disable_unprepare(udc->usb_slv_clk);
|
||||
clk_put(udc->usb_slv_clk);
|
||||
|
||||
iounmap(udc->udp_baseaddr);
|
||||
release_mem_region(udc->io_p_start, udc->io_p_size);
|
||||
kfree(udc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -573,8 +573,7 @@ net2272_read_fifo(struct net2272_ep *ep, struct net2272_request *req)
|
||||
|
||||
/* completion */
|
||||
if (unlikely(cleanup || is_short ||
|
||||
((req->req.actual == req->req.length)
|
||||
&& !req->req.zero))) {
|
||||
req->req.actual == req->req.length)) {
|
||||
|
||||
if (cleanup) {
|
||||
net2272_out_flush(ep);
|
||||
|
@ -789,8 +789,7 @@ static int read_fifo(struct net2280_ep *ep, struct net2280_request *req)
|
||||
(void) readl(&ep->regs->ep_rsp);
|
||||
}
|
||||
|
||||
return is_short || ((req->req.actual == req->req.length) &&
|
||||
!req->req.zero);
|
||||
return is_short || req->req.actual == req->req.length;
|
||||
}
|
||||
|
||||
/* fill out dma descriptor to match a given request */
|
||||
@ -1058,7 +1057,7 @@ net2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
|
||||
/* PIO ... stuff the fifo, or unblock it. */
|
||||
if (ep->is_in)
|
||||
write_fifo(ep, _req);
|
||||
else if (list_empty(&ep->queue)) {
|
||||
else {
|
||||
u32 s;
|
||||
|
||||
/* OUT FIFO might have packet(s) buffered */
|
||||
|
@ -21,7 +21,7 @@ config AB8500_USB
|
||||
in host mode, low speed.
|
||||
|
||||
config FSL_USB2_OTG
|
||||
bool "Freescale USB OTG Transceiver Driver"
|
||||
tristate "Freescale USB OTG Transceiver Driver"
|
||||
depends on USB_EHCI_FSL && USB_FSL_USB2 && USB_OTG_FSM=y && PM
|
||||
depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y'
|
||||
select USB_PHY
|
||||
|
@ -653,11 +653,16 @@ extern wait_queue_head_t usb_kill_urb_queue;
|
||||
#define usb_endpoint_out(ep_dir) (!((ep_dir) & USB_DIR_IN))
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
extern unsigned usb_wakeup_enabled_descendants(struct usb_device *udev);
|
||||
extern void usb_root_hub_lost_power(struct usb_device *rhdev);
|
||||
extern int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg);
|
||||
extern int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg);
|
||||
extern void usb_hcd_resume_root_hub(struct usb_hcd *hcd);
|
||||
#else
|
||||
static inline unsigned usb_wakeup_enabled_descendants(struct usb_device *udev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void usb_hcd_resume_root_hub(struct usb_hcd *hcd)
|
||||
{
|
||||
return;
|
||||
|
Loading…
Reference in New Issue
Block a user