forked from Minki/linux
usb: patches for v3.15
another substantial pull request with new features all over the place. dwc3 got a bit closer towards hibernation support with after a few patches re-factoring code to be reused for hibernation. Also in dwc3 two new workarounds for known silicon bugs have been implemented, some randconfig build errors have been fixed, and it was taught about the new generic phy layer. MUSB on AM335x now supports isochronous transfers thanks to George Cherian's work. The atmel_usba driver got two crash fixes: one when no endpoint was specified in DeviceTree data and another when stopping the UDC in DEBUG builds. Function FS got a much needed fix to ffs_epfile_io() which was copying too much data to userspace in some cases. The printer gadget got a fix for a possible deadlock and plugged a memory leak. Ethernet drivers now use NAPI for RX which gives improved throughput. Other than that, the usual miscelaneous fixes, cleanups, and the like. Signed-of-by: Felipe Balbi <balbi@ti.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJTGmOyAAoJEIaOsuA1yqREVsQP/AjfOAuQJ7awaUVsJqBLGADW qmxc1rd+wKv7e9s4Jtu8z8PaZklAd4E3M8M378BsJIiMSbHZhb/KUFQPVgkp5Rnz Ps9k7k4P3+yoovWruR1YRk+/mx5ra//MdJ+FNMe6wa3Y57X7NRAfzSSQOOnmkJ5+ 3aSz2IRKXBmSqpcMZVTgn1j/YATfZxpCmrTPSOdgyIjeWs+8s1SjaEFsgCaVb4vH V1B3HsMPRCtrR0BV0f1FA1xp1pXdAU3UeRecuVibP5DDIKhMqB7N8BWTi2nu8c+1 8wZ4S3BNJwBPmHU/XOkjLWnoZ6gZVIx2DEeCdh0hXF2lsbJYELCPptYEApDPyOvF zli6cCdPcM+bnWzmQyBXmu7uPVBOz3lv1HAkodOwhof1K556baZfXF+OpZ21+/oj l3I9Ebr86soVmxMzY1FyMN0F+klNCPTzmx4GS0GHJDCpMLvfX5rRVR0EXggIPGMC Lug/G8ySP0s+R1NTx+I6zJUV3BXkjp2KQmfjiMSzWzOAMSdaucRu4S71mgCvx1uN 5T4tWAAtl90O/6V+d3Lx4PMOUiBXCv3ZboDKNdRXrX3/omd2JkOKqj4J8hxJ1F0w l6jb8IIXiO6xElC6fBQ7Dq54kD7cLzEnFBn5I4Fg5AjPatbHDEjerArL8I+Loe/u E+V2mp0qzoUxtqi5aMND =AAie -----END PGP SIGNATURE----- Merge tag 'usb-for-v3.15' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next Felipe writes: usb: patches for v3.15 another substantial pull request with new features all over the place. dwc3 got a bit closer towards hibernation support with after a few patches re-factoring code to be reused for hibernation. Also in dwc3 two new workarounds for known silicon bugs have been implemented, some randconfig build errors have been fixed, and it was taught about the new generic phy layer. MUSB on AM335x now supports isochronous transfers thanks to George Cherian's work. The atmel_usba driver got two crash fixes: one when no endpoint was specified in DeviceTree data and another when stopping the UDC in DEBUG builds. Function FS got a much needed fix to ffs_epfile_io() which was copying too much data to userspace in some cases. The printer gadget got a fix for a possible deadlock and plugged a memory leak. Ethernet drivers now use NAPI for RX which gives improved throughput. Other than that, the usual miscelaneous fixes, cleanups, and the like. Signed-of-by: Felipe Balbi <balbi@ti.com>
This commit is contained in:
commit
7338a06593
86
Documentation/devicetree/bindings/phy/ti-phy.txt
Normal file
86
Documentation/devicetree/bindings/phy/ti-phy.txt
Normal file
@ -0,0 +1,86 @@
|
||||
TI PHY: DT DOCUMENTATION FOR PHYs in TI PLATFORMs
|
||||
|
||||
OMAP CONTROL PHY
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be one of
|
||||
"ti,control-phy-otghs" - if it has otghs_control mailbox register as on OMAP4.
|
||||
"ti,control-phy-usb2" - if it has Power down bit in control_dev_conf register
|
||||
e.g. USB2_PHY on OMAP5.
|
||||
"ti,control-phy-pipe3" - if it has DPLL and individual Rx & Tx power control
|
||||
e.g. USB3 PHY and SATA PHY on OMAP5.
|
||||
"ti,control-phy-usb2-dra7" - if it has power down register like USB2 PHY on
|
||||
DRA7 platform.
|
||||
"ti,control-phy-usb2-am437" - if it has power down register like USB2 PHY on
|
||||
AM437 platform.
|
||||
- reg : Address and length of the register set for the device. It contains
|
||||
the address of "otghs_control" for control-phy-otghs or "power" register
|
||||
for other types.
|
||||
- reg-names: should be "otghs_control" control-phy-otghs and "power" for
|
||||
other types.
|
||||
|
||||
omap_control_usb: omap-control-usb@4a002300 {
|
||||
compatible = "ti,control-phy-otghs";
|
||||
reg = <0x4a00233c 0x4>;
|
||||
reg-names = "otghs_control";
|
||||
};
|
||||
|
||||
OMAP USB2 PHY
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "ti,omap-usb2"
|
||||
- reg : Address and length of the register set for the device.
|
||||
- #phy-cells: determine the number of cells that should be given in the
|
||||
phandle while referencing this phy.
|
||||
|
||||
Optional properties:
|
||||
- ctrl-module : phandle of the control module used by PHY driver to power on
|
||||
the PHY.
|
||||
|
||||
This is usually a subnode of ocp2scp to which it is connected.
|
||||
|
||||
usb2phy@4a0ad080 {
|
||||
compatible = "ti,omap-usb2";
|
||||
reg = <0x4a0ad080 0x58>;
|
||||
ctrl-module = <&omap_control_usb>;
|
||||
#phy-cells = <0>;
|
||||
};
|
||||
|
||||
TI PIPE3 PHY
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "ti,phy-usb3" or "ti,phy-pipe3-sata".
|
||||
"ti,omap-usb3" is deprecated.
|
||||
- reg : Address and length of the register set for the device.
|
||||
- reg-names: The names of the register addresses corresponding to the registers
|
||||
filled in "reg".
|
||||
- #phy-cells: determine the number of cells that should be given in the
|
||||
phandle while referencing this phy.
|
||||
- clocks: a list of phandles and clock-specifier pairs, one for each entry in
|
||||
clock-names.
|
||||
- clock-names: should include:
|
||||
* "wkupclk" - wakeup clock.
|
||||
* "sysclk" - system clock.
|
||||
* "refclk" - reference clock.
|
||||
|
||||
Optional properties:
|
||||
- ctrl-module : phandle of the control module used by PHY driver to power on
|
||||
the PHY.
|
||||
|
||||
This is usually a subnode of ocp2scp to which it is connected.
|
||||
|
||||
usb3phy@4a084400 {
|
||||
compatible = "ti,phy-usb3";
|
||||
reg = <0x4a084400 0x80>,
|
||||
<0x4a084800 0x64>,
|
||||
<0x4a084c00 0x40>;
|
||||
reg-names = "phy_rx", "phy_tx", "pll_ctrl";
|
||||
ctrl-module = <&omap_control_usb>;
|
||||
#phy-cells = <0>;
|
||||
clocks = <&usb_phy_cm_clk32k>,
|
||||
<&sys_clkin>,
|
||||
<&usb_otg_ss_refclk960m>;
|
||||
clock-names = "wkupclk",
|
||||
"sysclk",
|
||||
"refclk";
|
||||
};
|
@ -6,11 +6,13 @@ Required properties:
|
||||
- compatible: must be "snps,dwc3"
|
||||
- reg : Address and length of the register set for the device
|
||||
- interrupts: Interrupts used by the dwc3 controller.
|
||||
|
||||
Optional properties:
|
||||
- usb-phy : array of phandle for the PHY device. The first element
|
||||
in the array is expected to be a handle to the USB2/HS PHY and
|
||||
the second element is expected to be a handle to the USB3/SS PHY
|
||||
|
||||
Optional properties:
|
||||
- phys: from the *Generic PHY* bindings
|
||||
- phy-names: from the *Generic PHY* bindings
|
||||
- tx-fifo-resize: determines if the FIFO *has* to be reallocated.
|
||||
|
||||
This is usually a subnode to DWC3 glue to which it is connected.
|
||||
|
@ -1,13 +1,19 @@
|
||||
* Freescale MXS USB Phy Device
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "fsl,imx23-usbphy"
|
||||
- compatible: should contain:
|
||||
* "fsl,imx23-usbphy" for imx23 and imx28
|
||||
* "fsl,imx6q-usbphy" for imx6dq and imx6dl
|
||||
* "fsl,imx6sl-usbphy" for imx6sl
|
||||
"fsl,imx23-usbphy" is still a fallback for other strings
|
||||
- reg: Should contain registers location and length
|
||||
- interrupts: Should contain phy interrupt
|
||||
- fsl,anatop: phandle for anatop register, it is only for imx6 SoC series
|
||||
|
||||
Example:
|
||||
usbphy1: usbphy@020c9000 {
|
||||
compatible = "fsl,imx6q-usbphy", "fsl,imx23-usbphy";
|
||||
reg = <0x020c9000 0x1000>;
|
||||
interrupts = <0 44 0x04>;
|
||||
fsl,anatop = <&anatop>;
|
||||
};
|
||||
|
@ -76,27 +76,3 @@ omap_dwc3 {
|
||||
ranges;
|
||||
};
|
||||
|
||||
OMAP CONTROL USB
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be one of
|
||||
"ti,control-phy-otghs" - if it has otghs_control mailbox register as on OMAP4.
|
||||
"ti,control-phy-usb2" - if it has Power down bit in control_dev_conf register
|
||||
e.g. USB2_PHY on OMAP5.
|
||||
"ti,control-phy-pipe3" - if it has DPLL and individual Rx & Tx power control
|
||||
e.g. USB3 PHY and SATA PHY on OMAP5.
|
||||
"ti,control-phy-dra7usb2" - if it has power down register like USB2 PHY on
|
||||
DRA7 platform.
|
||||
"ti,control-phy-am437usb2" - if it has power down register like USB2 PHY on
|
||||
AM437 platform.
|
||||
- reg : Address and length of the register set for the device. It contains
|
||||
the address of "otghs_control" for control-phy-otghs or "power" register
|
||||
for other types.
|
||||
- reg-names: should be "otghs_control" control-phy-otghs and "power" for
|
||||
other types.
|
||||
|
||||
omap_control_usb: omap-control-usb@4a002300 {
|
||||
compatible = "ti,control-phy-otghs";
|
||||
reg = <0x4a00233c 0x4>;
|
||||
reg-names = "otghs_control";
|
||||
};
|
||||
|
@ -1,48 +0,0 @@
|
||||
USB PHY
|
||||
|
||||
OMAP USB2 PHY
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "ti,omap-usb2"
|
||||
- reg : Address and length of the register set for the device.
|
||||
- #phy-cells: determine the number of cells that should be given in the
|
||||
phandle while referencing this phy.
|
||||
|
||||
Optional properties:
|
||||
- ctrl-module : phandle of the control module used by PHY driver to power on
|
||||
the PHY.
|
||||
|
||||
This is usually a subnode of ocp2scp to which it is connected.
|
||||
|
||||
usb2phy@4a0ad080 {
|
||||
compatible = "ti,omap-usb2";
|
||||
reg = <0x4a0ad080 0x58>;
|
||||
ctrl-module = <&omap_control_usb>;
|
||||
#phy-cells = <0>;
|
||||
};
|
||||
|
||||
OMAP USB3 PHY
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "ti,omap-usb3"
|
||||
- reg : Address and length of the register set for the device.
|
||||
- reg-names: The names of the register addresses corresponding to the registers
|
||||
filled in "reg".
|
||||
- #phy-cells: determine the number of cells that should be given in the
|
||||
phandle while referencing this phy.
|
||||
|
||||
Optional properties:
|
||||
- ctrl-module : phandle of the control module used by PHY driver to power on
|
||||
the PHY.
|
||||
|
||||
This is usually a subnode of ocp2scp to which it is connected.
|
||||
|
||||
usb3phy@4a084400 {
|
||||
compatible = "ti,omap-usb3";
|
||||
reg = <0x4a084400 0x80>,
|
||||
<0x4a084800 0x64>,
|
||||
<0x4a084c00 0x40>;
|
||||
reg-names = "phy_rx", "phy_tx", "pll_ctrl";
|
||||
ctrl-module = <&omap_control_usb>;
|
||||
#phy-cells = <0>;
|
||||
};
|
@ -61,9 +61,10 @@ void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
|
||||
* dwc3_core_soft_reset - Issues core soft reset and PHY reset
|
||||
* @dwc: pointer to our context structure
|
||||
*/
|
||||
static void dwc3_core_soft_reset(struct dwc3 *dwc)
|
||||
static int dwc3_core_soft_reset(struct dwc3 *dwc)
|
||||
{
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
/* Before Resetting PHY, put Core in Reset */
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
|
||||
@ -82,6 +83,15 @@ static void dwc3_core_soft_reset(struct dwc3 *dwc)
|
||||
|
||||
usb_phy_init(dwc->usb2_phy);
|
||||
usb_phy_init(dwc->usb3_phy);
|
||||
ret = phy_init(dwc->usb2_generic_phy);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = phy_init(dwc->usb3_generic_phy);
|
||||
if (ret < 0) {
|
||||
phy_exit(dwc->usb2_generic_phy);
|
||||
return ret;
|
||||
}
|
||||
mdelay(100);
|
||||
|
||||
/* Clear USB3 PHY reset */
|
||||
@ -100,6 +110,8 @@ static void dwc3_core_soft_reset(struct dwc3 *dwc)
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
|
||||
reg &= ~DWC3_GCTL_CORESOFTRESET;
|
||||
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -242,6 +254,90 @@ static void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
|
||||
}
|
||||
}
|
||||
|
||||
static int dwc3_alloc_scratch_buffers(struct dwc3 *dwc)
|
||||
{
|
||||
if (!dwc->has_hibernation)
|
||||
return 0;
|
||||
|
||||
if (!dwc->nr_scratch)
|
||||
return 0;
|
||||
|
||||
dwc->scratchbuf = kmalloc_array(dwc->nr_scratch,
|
||||
DWC3_SCRATCHBUF_SIZE, GFP_KERNEL);
|
||||
if (!dwc->scratchbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_setup_scratch_buffers(struct dwc3 *dwc)
|
||||
{
|
||||
dma_addr_t scratch_addr;
|
||||
u32 param;
|
||||
int ret;
|
||||
|
||||
if (!dwc->has_hibernation)
|
||||
return 0;
|
||||
|
||||
if (!dwc->nr_scratch)
|
||||
return 0;
|
||||
|
||||
/* should never fall here */
|
||||
if (!WARN_ON(dwc->scratchbuf))
|
||||
return 0;
|
||||
|
||||
scratch_addr = dma_map_single(dwc->dev, dwc->scratchbuf,
|
||||
dwc->nr_scratch * DWC3_SCRATCHBUF_SIZE,
|
||||
DMA_BIDIRECTIONAL);
|
||||
if (dma_mapping_error(dwc->dev, scratch_addr)) {
|
||||
dev_err(dwc->dev, "failed to map scratch buffer\n");
|
||||
ret = -EFAULT;
|
||||
goto err0;
|
||||
}
|
||||
|
||||
dwc->scratch_addr = scratch_addr;
|
||||
|
||||
param = lower_32_bits(scratch_addr);
|
||||
|
||||
ret = dwc3_send_gadget_generic_command(dwc,
|
||||
DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO, param);
|
||||
if (ret < 0)
|
||||
goto err1;
|
||||
|
||||
param = upper_32_bits(scratch_addr);
|
||||
|
||||
ret = dwc3_send_gadget_generic_command(dwc,
|
||||
DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI, param);
|
||||
if (ret < 0)
|
||||
goto err1;
|
||||
|
||||
return 0;
|
||||
|
||||
err1:
|
||||
dma_unmap_single(dwc->dev, dwc->scratch_addr, dwc->nr_scratch *
|
||||
DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL);
|
||||
|
||||
err0:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void dwc3_free_scratch_buffers(struct dwc3 *dwc)
|
||||
{
|
||||
if (!dwc->has_hibernation)
|
||||
return;
|
||||
|
||||
if (!dwc->nr_scratch)
|
||||
return;
|
||||
|
||||
/* should never fall here */
|
||||
if (!WARN_ON(dwc->scratchbuf))
|
||||
return;
|
||||
|
||||
dma_unmap_single(dwc->dev, dwc->scratch_addr, dwc->nr_scratch *
|
||||
DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL);
|
||||
kfree(dwc->scratchbuf);
|
||||
}
|
||||
|
||||
static void dwc3_core_num_eps(struct dwc3 *dwc)
|
||||
{
|
||||
struct dwc3_hwparams *parms = &dwc->hwparams;
|
||||
@ -277,6 +373,7 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
|
||||
static int dwc3_core_init(struct dwc3 *dwc)
|
||||
{
|
||||
unsigned long timeout;
|
||||
u32 hwparams4 = dwc->hwparams.hwparams4;
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
@ -306,7 +403,9 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
||||
cpu_relax();
|
||||
} while (true);
|
||||
|
||||
dwc3_core_soft_reset(dwc);
|
||||
ret = dwc3_core_soft_reset(dwc);
|
||||
if (ret)
|
||||
goto err0;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
|
||||
reg &= ~DWC3_GCTL_SCALEDOWN_MASK;
|
||||
@ -314,7 +413,29 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
||||
|
||||
switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1)) {
|
||||
case DWC3_GHWPARAMS1_EN_PWROPT_CLK:
|
||||
reg &= ~DWC3_GCTL_DSBLCLKGTNG;
|
||||
/**
|
||||
* WORKAROUND: DWC3 revisions between 2.10a and 2.50a have an
|
||||
* issue which would cause xHCI compliance tests to fail.
|
||||
*
|
||||
* Because of that we cannot enable clock gating on such
|
||||
* configurations.
|
||||
*
|
||||
* Refers to:
|
||||
*
|
||||
* STAR#9000588375: Clock Gating, SOF Issues when ref_clk-Based
|
||||
* SOF/ITP Mode Used
|
||||
*/
|
||||
if ((dwc->dr_mode == USB_DR_MODE_HOST ||
|
||||
dwc->dr_mode == USB_DR_MODE_OTG) &&
|
||||
(dwc->revision >= DWC3_REVISION_210A &&
|
||||
dwc->revision <= DWC3_REVISION_250A))
|
||||
reg |= DWC3_GCTL_DSBLCLKGTNG | DWC3_GCTL_SOFITPSYNC;
|
||||
else
|
||||
reg &= ~DWC3_GCTL_DSBLCLKGTNG;
|
||||
break;
|
||||
case DWC3_GHWPARAMS1_EN_PWROPT_HIB:
|
||||
/* enable hibernation here */
|
||||
dwc->nr_scratch = DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(hwparams4);
|
||||
break;
|
||||
default:
|
||||
dev_dbg(dwc->dev, "No power optimization available\n");
|
||||
@ -333,16 +454,36 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
|
||||
|
||||
ret = dwc3_alloc_scratch_buffers(dwc);
|
||||
if (ret)
|
||||
goto err1;
|
||||
|
||||
ret = dwc3_setup_scratch_buffers(dwc);
|
||||
if (ret)
|
||||
goto err2;
|
||||
|
||||
return 0;
|
||||
|
||||
err2:
|
||||
dwc3_free_scratch_buffers(dwc);
|
||||
|
||||
err1:
|
||||
usb_phy_shutdown(dwc->usb2_phy);
|
||||
usb_phy_shutdown(dwc->usb3_phy);
|
||||
phy_exit(dwc->usb2_generic_phy);
|
||||
phy_exit(dwc->usb3_generic_phy);
|
||||
|
||||
err0:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void dwc3_core_exit(struct dwc3 *dwc)
|
||||
{
|
||||
dwc3_free_scratch_buffers(dwc);
|
||||
usb_phy_shutdown(dwc->usb2_phy);
|
||||
usb_phy_shutdown(dwc->usb3_phy);
|
||||
phy_exit(dwc->usb2_generic_phy);
|
||||
phy_exit(dwc->usb3_generic_phy);
|
||||
}
|
||||
|
||||
#define DWC3_ALIGN_MASK (16 - 1)
|
||||
@ -411,32 +552,52 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
|
||||
if (IS_ERR(dwc->usb2_phy)) {
|
||||
ret = PTR_ERR(dwc->usb2_phy);
|
||||
|
||||
/*
|
||||
* if -ENXIO is returned, it means PHY layer wasn't
|
||||
* enabled, so it makes no sense to return -EPROBE_DEFER
|
||||
* in that case, since no PHY driver will ever probe.
|
||||
*/
|
||||
if (ret == -ENXIO)
|
||||
if (ret == -ENXIO || ret == -ENODEV) {
|
||||
dwc->usb2_phy = NULL;
|
||||
} else if (ret == -EPROBE_DEFER) {
|
||||
return ret;
|
||||
|
||||
dev_err(dev, "no usb2 phy configured\n");
|
||||
return -EPROBE_DEFER;
|
||||
} else {
|
||||
dev_err(dev, "no usb2 phy configured\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_ERR(dwc->usb3_phy)) {
|
||||
ret = PTR_ERR(dwc->usb3_phy);
|
||||
|
||||
/*
|
||||
* if -ENXIO is returned, it means PHY layer wasn't
|
||||
* enabled, so it makes no sense to return -EPROBE_DEFER
|
||||
* in that case, since no PHY driver will ever probe.
|
||||
*/
|
||||
if (ret == -ENXIO)
|
||||
if (ret == -ENXIO || ret == -ENODEV) {
|
||||
dwc->usb3_phy = NULL;
|
||||
} else if (ret == -EPROBE_DEFER) {
|
||||
return ret;
|
||||
} else {
|
||||
dev_err(dev, "no usb3 phy configured\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
dev_err(dev, "no usb3 phy configured\n");
|
||||
return -EPROBE_DEFER;
|
||||
dwc->usb2_generic_phy = devm_phy_get(dev, "usb2-phy");
|
||||
if (IS_ERR(dwc->usb2_generic_phy)) {
|
||||
ret = PTR_ERR(dwc->usb2_generic_phy);
|
||||
if (ret == -ENOSYS || ret == -ENODEV) {
|
||||
dwc->usb2_generic_phy = NULL;
|
||||
} else if (ret == -EPROBE_DEFER) {
|
||||
return ret;
|
||||
} else {
|
||||
dev_err(dev, "no usb2 phy configured\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
dwc->usb3_generic_phy = devm_phy_get(dev, "usb3-phy");
|
||||
if (IS_ERR(dwc->usb3_generic_phy)) {
|
||||
ret = PTR_ERR(dwc->usb3_generic_phy);
|
||||
if (ret == -ENOSYS || ret == -ENODEV) {
|
||||
dwc->usb3_generic_phy = NULL;
|
||||
} else if (ret == -EPROBE_DEFER) {
|
||||
return ret;
|
||||
} else {
|
||||
dev_err(dev, "no usb3 phy configured\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
dwc->xhci_resources[0].start = res->start;
|
||||
@ -479,6 +640,14 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
goto err0;
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_USB_DWC3_HOST))
|
||||
dwc->dr_mode = USB_DR_MODE_HOST;
|
||||
else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET))
|
||||
dwc->dr_mode = USB_DR_MODE_PERIPHERAL;
|
||||
|
||||
if (dwc->dr_mode == USB_DR_MODE_UNKNOWN)
|
||||
dwc->dr_mode = USB_DR_MODE_OTG;
|
||||
|
||||
ret = dwc3_core_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize core\n");
|
||||
@ -487,21 +656,20 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
|
||||
usb_phy_set_suspend(dwc->usb2_phy, 0);
|
||||
usb_phy_set_suspend(dwc->usb3_phy, 0);
|
||||
ret = phy_power_on(dwc->usb2_generic_phy);
|
||||
if (ret < 0)
|
||||
goto err1;
|
||||
|
||||
ret = phy_power_on(dwc->usb3_generic_phy);
|
||||
if (ret < 0)
|
||||
goto err_usb2phy_power;
|
||||
|
||||
ret = dwc3_event_buffers_setup(dwc);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to setup event buffers\n");
|
||||
goto err1;
|
||||
goto err_usb3phy_power;
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_USB_DWC3_HOST))
|
||||
dwc->dr_mode = USB_DR_MODE_HOST;
|
||||
else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET))
|
||||
dwc->dr_mode = USB_DR_MODE_PERIPHERAL;
|
||||
|
||||
if (dwc->dr_mode == USB_DR_MODE_UNKNOWN)
|
||||
dwc->dr_mode = USB_DR_MODE_OTG;
|
||||
|
||||
switch (dwc->dr_mode) {
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
|
||||
@ -568,6 +736,12 @@ err3:
|
||||
err2:
|
||||
dwc3_event_buffers_cleanup(dwc);
|
||||
|
||||
err_usb3phy_power:
|
||||
phy_power_off(dwc->usb3_generic_phy);
|
||||
|
||||
err_usb2phy_power:
|
||||
phy_power_off(dwc->usb2_generic_phy);
|
||||
|
||||
err1:
|
||||
usb_phy_set_suspend(dwc->usb2_phy, 1);
|
||||
usb_phy_set_suspend(dwc->usb3_phy, 1);
|
||||
@ -585,6 +759,8 @@ static int dwc3_remove(struct platform_device *pdev)
|
||||
|
||||
usb_phy_set_suspend(dwc->usb2_phy, 1);
|
||||
usb_phy_set_suspend(dwc->usb3_phy, 1);
|
||||
phy_power_off(dwc->usb2_generic_phy);
|
||||
phy_power_off(dwc->usb3_generic_phy);
|
||||
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
@ -682,6 +858,8 @@ static int dwc3_suspend(struct device *dev)
|
||||
|
||||
usb_phy_shutdown(dwc->usb3_phy);
|
||||
usb_phy_shutdown(dwc->usb2_phy);
|
||||
phy_exit(dwc->usb2_generic_phy);
|
||||
phy_exit(dwc->usb3_generic_phy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -690,9 +868,17 @@ static int dwc3_resume(struct device *dev)
|
||||
{
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
usb_phy_init(dwc->usb3_phy);
|
||||
usb_phy_init(dwc->usb2_phy);
|
||||
ret = phy_init(dwc->usb2_generic_phy);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = phy_init(dwc->usb3_generic_phy);
|
||||
if (ret < 0)
|
||||
goto err_usb2phy_init;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
|
||||
@ -716,6 +902,11 @@ static int dwc3_resume(struct device *dev)
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_usb2phy_init:
|
||||
phy_exit(dwc->usb2_generic_phy);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops dwc3_dev_pm_ops = {
|
||||
|
@ -31,11 +31,14 @@
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/otg.h>
|
||||
|
||||
#include <linux/phy/phy.h>
|
||||
|
||||
/* Global constants */
|
||||
#define DWC3_EP0_BOUNCE_SIZE 512
|
||||
#define DWC3_ENDPOINTS_NUM 32
|
||||
#define DWC3_XHCI_RESOURCES_NUM 2
|
||||
|
||||
#define DWC3_SCRATCHBUF_SIZE 4096 /* each buffer is assumed to be 4KiB */
|
||||
#define DWC3_EVENT_SIZE 4 /* bytes */
|
||||
#define DWC3_EVENT_MAX_NUM 64 /* 2 events/endpoint */
|
||||
#define DWC3_EVENT_BUFFERS_SIZE (DWC3_EVENT_SIZE * DWC3_EVENT_MAX_NUM)
|
||||
@ -157,6 +160,7 @@
|
||||
#define DWC3_GCTL_PRTCAP_OTG 3
|
||||
|
||||
#define DWC3_GCTL_CORESOFTRESET (1 << 11)
|
||||
#define DWC3_GCTL_SOFITPSYNC (1 << 10)
|
||||
#define DWC3_GCTL_SCALEDOWN(n) ((n) << 4)
|
||||
#define DWC3_GCTL_SCALEDOWN_MASK DWC3_GCTL_SCALEDOWN(3)
|
||||
#define DWC3_GCTL_DISSCRAMBLE (1 << 3)
|
||||
@ -318,7 +322,7 @@
|
||||
/* Device Endpoint Command Register */
|
||||
#define DWC3_DEPCMD_PARAM_SHIFT 16
|
||||
#define DWC3_DEPCMD_PARAM(x) ((x) << DWC3_DEPCMD_PARAM_SHIFT)
|
||||
#define DWC3_DEPCMD_GET_RSC_IDX(x) (((x) >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f)
|
||||
#define DWC3_DEPCMD_GET_RSC_IDX(x) (((x) >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f)
|
||||
#define DWC3_DEPCMD_STATUS(x) (((x) >> 15) & 1)
|
||||
#define DWC3_DEPCMD_HIPRI_FORCERM (1 << 11)
|
||||
#define DWC3_DEPCMD_CMDACT (1 << 10)
|
||||
@ -393,6 +397,7 @@ struct dwc3_event_buffer {
|
||||
* @busy_slot: first slot which is owned by HW
|
||||
* @desc: usb_endpoint_descriptor pointer
|
||||
* @dwc: pointer to DWC controller
|
||||
* @saved_state: ep state saved during hibernation
|
||||
* @flags: endpoint flags (wedged, stalled, ...)
|
||||
* @current_trb: index of current used trb
|
||||
* @number: endpoint number (1 - 15)
|
||||
@ -415,6 +420,7 @@ struct dwc3_ep {
|
||||
const struct usb_ss_ep_comp_descriptor *comp_desc;
|
||||
struct dwc3 *dwc;
|
||||
|
||||
u32 saved_state;
|
||||
unsigned flags;
|
||||
#define DWC3_EP_ENABLED (1 << 0)
|
||||
#define DWC3_EP_STALL (1 << 1)
|
||||
@ -598,6 +604,7 @@ struct dwc3_scratchpad_array {
|
||||
* @ep0_trb: dma address of ep0_trb
|
||||
* @ep0_usb_req: dummy req used while handling STD USB requests
|
||||
* @ep0_bounce_addr: dma address of ep0_bounce
|
||||
* @scratch_addr: dma address of scratchbuf
|
||||
* @lock: for synchronizing
|
||||
* @dev: pointer to our struct device
|
||||
* @xhci: pointer to our xHCI child
|
||||
@ -606,6 +613,7 @@ struct dwc3_scratchpad_array {
|
||||
* @gadget_driver: pointer to the gadget driver
|
||||
* @regs: base address for our registers
|
||||
* @regs_size: address space size
|
||||
* @nr_scratch: number of scratch buffers
|
||||
* @num_event_buffers: calculated number of event buffers
|
||||
* @u1u2: only used on revisions <1.83a for workaround
|
||||
* @maximum_speed: maximum speed requested (mainly for testing purposes)
|
||||
@ -613,16 +621,10 @@ struct dwc3_scratchpad_array {
|
||||
* @dr_mode: requested mode of operation
|
||||
* @usb2_phy: pointer to USB2 PHY
|
||||
* @usb3_phy: pointer to USB3 PHY
|
||||
* @usb2_generic_phy: pointer to USB2 PHY
|
||||
* @usb3_generic_phy: pointer to USB3 PHY
|
||||
* @dcfg: saved contents of DCFG register
|
||||
* @gctl: saved contents of GCTL register
|
||||
* @is_selfpowered: true when we are selfpowered
|
||||
* @three_stage_setup: set if we perform a three phase setup
|
||||
* @ep0_bounced: true when we used bounce buffer
|
||||
* @ep0_expect_in: true when we expect a DATA IN transfer
|
||||
* @start_config_issued: true when StartConfig command has been issued
|
||||
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
|
||||
* @needs_fifo_resize: not all users might want fifo resizing, flag it
|
||||
* @resize_fifos: tells us it's ok to reconfigure our TxFIFO sizes.
|
||||
* @isoch_delay: wValue from Set Isochronous Delay request;
|
||||
* @u2sel: parameter from Set SEL request.
|
||||
* @u2pel: parameter from Set SEL request.
|
||||
@ -637,15 +639,31 @@ struct dwc3_scratchpad_array {
|
||||
* @mem: points to start of memory which is used for this struct.
|
||||
* @hwparams: copy of hwparams registers
|
||||
* @root: debugfs root folder pointer
|
||||
* @regset: debugfs pointer to regdump file
|
||||
* @test_mode: true when we're entering a USB test mode
|
||||
* @test_mode_nr: test feature selector
|
||||
* @delayed_status: true when gadget driver asks for delayed status
|
||||
* @ep0_bounced: true when we used bounce buffer
|
||||
* @ep0_expect_in: true when we expect a DATA IN transfer
|
||||
* @has_hibernation: true when dwc3 was configured with Hibernation
|
||||
* @is_selfpowered: true when we are selfpowered
|
||||
* @needs_fifo_resize: not all users might want fifo resizing, flag it
|
||||
* @pullups_connected: true when Run/Stop bit is set
|
||||
* @resize_fifos: tells us it's ok to reconfigure our TxFIFO sizes.
|
||||
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
|
||||
* @start_config_issued: true when StartConfig command has been issued
|
||||
* @three_stage_setup: set if we perform a three phase setup
|
||||
*/
|
||||
struct dwc3 {
|
||||
struct usb_ctrlrequest *ctrl_req;
|
||||
struct dwc3_trb *ep0_trb;
|
||||
void *ep0_bounce;
|
||||
void *scratchbuf;
|
||||
u8 *setup_buf;
|
||||
dma_addr_t ctrl_req_addr;
|
||||
dma_addr_t ep0_trb_addr;
|
||||
dma_addr_t ep0_bounce_addr;
|
||||
dma_addr_t scratch_addr;
|
||||
struct dwc3_request ep0_usb_req;
|
||||
|
||||
/* device lock */
|
||||
@ -665,6 +683,9 @@ struct dwc3 {
|
||||
struct usb_phy *usb2_phy;
|
||||
struct usb_phy *usb3_phy;
|
||||
|
||||
struct phy *usb2_generic_phy;
|
||||
struct phy *usb3_generic_phy;
|
||||
|
||||
void __iomem *regs;
|
||||
size_t regs_size;
|
||||
|
||||
@ -674,6 +695,7 @@ struct dwc3 {
|
||||
u32 dcfg;
|
||||
u32 gctl;
|
||||
|
||||
u32 nr_scratch;
|
||||
u32 num_event_buffers;
|
||||
u32 u1u2;
|
||||
u32 maximum_speed;
|
||||
@ -695,17 +717,9 @@ struct dwc3 {
|
||||
#define DWC3_REVISION_230A 0x5533230a
|
||||
#define DWC3_REVISION_240A 0x5533240a
|
||||
#define DWC3_REVISION_250A 0x5533250a
|
||||
|
||||
unsigned is_selfpowered:1;
|
||||
unsigned three_stage_setup:1;
|
||||
unsigned ep0_bounced:1;
|
||||
unsigned ep0_expect_in:1;
|
||||
unsigned start_config_issued:1;
|
||||
unsigned setup_packet_pending:1;
|
||||
unsigned delayed_status:1;
|
||||
unsigned needs_fifo_resize:1;
|
||||
unsigned resize_fifos:1;
|
||||
unsigned pullups_connected:1;
|
||||
#define DWC3_REVISION_260A 0x5533260a
|
||||
#define DWC3_REVISION_270A 0x5533270a
|
||||
#define DWC3_REVISION_280A 0x5533280a
|
||||
|
||||
enum dwc3_ep0_next ep0_next_event;
|
||||
enum dwc3_ep0_state ep0state;
|
||||
@ -730,6 +744,18 @@ struct dwc3 {
|
||||
|
||||
u8 test_mode;
|
||||
u8 test_mode_nr;
|
||||
|
||||
unsigned delayed_status:1;
|
||||
unsigned ep0_bounced:1;
|
||||
unsigned ep0_expect_in:1;
|
||||
unsigned has_hibernation:1;
|
||||
unsigned is_selfpowered:1;
|
||||
unsigned needs_fifo_resize:1;
|
||||
unsigned pullups_connected:1;
|
||||
unsigned resize_fifos:1;
|
||||
unsigned setup_packet_pending:1;
|
||||
unsigned start_config_issued:1;
|
||||
unsigned three_stage_setup:1;
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
@ -815,15 +841,15 @@ struct dwc3_event_depevt {
|
||||
* 12 - VndrDevTstRcved
|
||||
* @reserved15_12: Reserved, not used
|
||||
* @event_info: Information about this event
|
||||
* @reserved31_24: Reserved, not used
|
||||
* @reserved31_25: Reserved, not used
|
||||
*/
|
||||
struct dwc3_event_devt {
|
||||
u32 one_bit:1;
|
||||
u32 device_event:7;
|
||||
u32 type:4;
|
||||
u32 reserved15_12:4;
|
||||
u32 event_info:8;
|
||||
u32 reserved31_24:8;
|
||||
u32 event_info:9;
|
||||
u32 reserved31_25:7;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
@ -856,6 +882,19 @@ union dwc3_event {
|
||||
struct dwc3_event_gevt gevt;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dwc3_gadget_ep_cmd_params - representation of endpoint command
|
||||
* parameters
|
||||
* @param2: third parameter
|
||||
* @param1: second parameter
|
||||
* @param0: first parameter
|
||||
*/
|
||||
struct dwc3_gadget_ep_cmd_params {
|
||||
u32 param2;
|
||||
u32 param1;
|
||||
u32 param0;
|
||||
};
|
||||
|
||||
/*
|
||||
* DWC3 Features to be used as Driver Data
|
||||
*/
|
||||
@ -881,11 +920,31 @@ static inline void dwc3_host_exit(struct dwc3 *dwc)
|
||||
#if IS_ENABLED(CONFIG_USB_DWC3_GADGET) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
|
||||
int dwc3_gadget_init(struct dwc3 *dwc);
|
||||
void dwc3_gadget_exit(struct dwc3 *dwc);
|
||||
int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode);
|
||||
int dwc3_gadget_get_link_state(struct dwc3 *dwc);
|
||||
int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state);
|
||||
int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
|
||||
unsigned cmd, struct dwc3_gadget_ep_cmd_params *params);
|
||||
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param);
|
||||
#else
|
||||
static inline int dwc3_gadget_init(struct dwc3 *dwc)
|
||||
{ return 0; }
|
||||
static inline void dwc3_gadget_exit(struct dwc3 *dwc)
|
||||
{ }
|
||||
static inline int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode)
|
||||
{ return 0; }
|
||||
static inline int dwc3_gadget_get_link_state(struct dwc3 *dwc)
|
||||
{ return 0; }
|
||||
static inline int dwc3_gadget_set_link_state(struct dwc3 *dwc,
|
||||
enum dwc3_link_state state)
|
||||
{ return 0; }
|
||||
|
||||
static inline int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
|
||||
unsigned cmd, struct dwc3_gadget_ep_cmd_params *params)
|
||||
{ return 0; }
|
||||
static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
|
||||
int cmd, u32 param)
|
||||
{ return 0; }
|
||||
#endif
|
||||
|
||||
/* power management interface */
|
||||
|
@ -424,11 +424,6 @@ static int dwc3_omap_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "missing memory base resource\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
@ -67,6 +67,22 @@ int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_gadget_get_link_state - Gets current state of USB Link
|
||||
* @dwc: pointer to our context structure
|
||||
*
|
||||
* Caller should take care of locking. This function will
|
||||
* return the link state on success (>= 0) or -ETIMEDOUT.
|
||||
*/
|
||||
int dwc3_gadget_get_link_state(struct dwc3 *dwc)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
||||
|
||||
return DWC3_DSTS_USBLNKST(reg);
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_gadget_set_link_state - Sets USB Link to a particular State
|
||||
* @dwc: pointer to our context structure
|
||||
@ -417,7 +433,7 @@ static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep)
|
||||
static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
|
||||
const struct usb_endpoint_descriptor *desc,
|
||||
const struct usb_ss_ep_comp_descriptor *comp_desc,
|
||||
bool ignore)
|
||||
bool ignore, bool restore)
|
||||
{
|
||||
struct dwc3_gadget_ep_cmd_params params;
|
||||
|
||||
@ -436,6 +452,11 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
|
||||
if (ignore)
|
||||
params.param0 |= DWC3_DEPCFG_IGN_SEQ_NUM;
|
||||
|
||||
if (restore) {
|
||||
params.param0 |= DWC3_DEPCFG_ACTION_RESTORE;
|
||||
params.param2 |= dep->saved_state;
|
||||
}
|
||||
|
||||
params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN
|
||||
| DWC3_DEPCFG_XFER_NOT_READY_EN;
|
||||
|
||||
@ -494,7 +515,7 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep)
|
||||
static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
|
||||
const struct usb_endpoint_descriptor *desc,
|
||||
const struct usb_ss_ep_comp_descriptor *comp_desc,
|
||||
bool ignore)
|
||||
bool ignore, bool restore)
|
||||
{
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
u32 reg;
|
||||
@ -508,7 +529,8 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc, ignore);
|
||||
ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc, ignore,
|
||||
restore);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -548,13 +570,13 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum);
|
||||
static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force);
|
||||
static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
|
||||
{
|
||||
struct dwc3_request *req;
|
||||
|
||||
if (!list_empty(&dep->req_queued)) {
|
||||
dwc3_stop_active_transfer(dwc, dep->number);
|
||||
dwc3_stop_active_transfer(dwc, dep->number, true);
|
||||
|
||||
/* - giveback all requests to gadget driver */
|
||||
while (!list_empty(&dep->req_queued)) {
|
||||
@ -659,7 +681,7 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc, false);
|
||||
ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc, false, false);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return ret;
|
||||
@ -771,9 +793,6 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
|
||||
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
|
||||
else
|
||||
trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS;
|
||||
|
||||
if (!req->request.no_interrupt && !chain)
|
||||
trb->ctrl |= DWC3_TRB_CTRL_IOC;
|
||||
break;
|
||||
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
@ -788,6 +807,9 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
|
||||
BUG();
|
||||
}
|
||||
|
||||
if (!req->request.no_interrupt && !chain)
|
||||
trb->ctrl |= DWC3_TRB_CTRL_IOC;
|
||||
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
||||
trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
|
||||
trb->ctrl |= DWC3_TRB_CTRL_CSP;
|
||||
@ -1077,7 +1099,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
||||
*/
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
||||
if (list_empty(&dep->req_queued)) {
|
||||
dwc3_stop_active_transfer(dwc, dep->number);
|
||||
dwc3_stop_active_transfer(dwc, dep->number, true);
|
||||
dep->flags = DWC3_EP_ENABLED;
|
||||
}
|
||||
return 0;
|
||||
@ -1107,6 +1129,23 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* 4. Stream Capable Bulk Endpoints. We need to start the transfer
|
||||
* right away, otherwise host will not know we have streams to be
|
||||
* handled.
|
||||
*/
|
||||
if (dep->stream_capable) {
|
||||
int ret;
|
||||
|
||||
ret = __dwc3_gadget_kick_transfer(dep, 0, true);
|
||||
if (ret && ret != -EBUSY) {
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
|
||||
dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
|
||||
dep->name);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1163,7 +1202,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
|
||||
}
|
||||
if (r == req) {
|
||||
/* wait until it is processed */
|
||||
dwc3_stop_active_transfer(dwc, dep->number);
|
||||
dwc3_stop_active_transfer(dwc, dep->number, true);
|
||||
goto out1;
|
||||
}
|
||||
dev_err(dwc->dev, "request %p was not queued to %s\n",
|
||||
@ -1194,8 +1233,7 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value)
|
||||
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
|
||||
DWC3_DEPCMD_SETSTALL, ¶ms);
|
||||
if (ret)
|
||||
dev_err(dwc->dev, "failed to %s STALL on %s\n",
|
||||
value ? "set" : "clear",
|
||||
dev_err(dwc->dev, "failed to set STALL on %s\n",
|
||||
dep->name);
|
||||
else
|
||||
dep->flags |= DWC3_EP_STALL;
|
||||
@ -1203,8 +1241,7 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value)
|
||||
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
|
||||
DWC3_DEPCMD_CLEARSTALL, ¶ms);
|
||||
if (ret)
|
||||
dev_err(dwc->dev, "failed to %s STALL on %s\n",
|
||||
value ? "set" : "clear",
|
||||
dev_err(dwc->dev, "failed to clear STALL on %s\n",
|
||||
dep->name);
|
||||
else
|
||||
dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
|
||||
@ -1387,7 +1424,7 @@ static int dwc3_gadget_set_selfpowered(struct usb_gadget *g,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
|
||||
static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
|
||||
{
|
||||
u32 reg;
|
||||
u32 timeout = 500;
|
||||
@ -1402,9 +1439,17 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
|
||||
if (dwc->revision >= DWC3_REVISION_194A)
|
||||
reg &= ~DWC3_DCTL_KEEP_CONNECT;
|
||||
reg |= DWC3_DCTL_RUN_STOP;
|
||||
|
||||
if (dwc->has_hibernation)
|
||||
reg |= DWC3_DCTL_KEEP_CONNECT;
|
||||
|
||||
dwc->pullups_connected = true;
|
||||
} else {
|
||||
reg &= ~DWC3_DCTL_RUN_STOP;
|
||||
|
||||
if (dwc->has_hibernation && !suspend)
|
||||
reg &= ~DWC3_DCTL_KEEP_CONNECT;
|
||||
|
||||
dwc->pullups_connected = false;
|
||||
}
|
||||
|
||||
@ -1442,7 +1487,7 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
|
||||
is_on = !!is_on;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
ret = dwc3_gadget_run_stop(dwc, is_on);
|
||||
ret = dwc3_gadget_run_stop(dwc, is_on, false);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return ret;
|
||||
@ -1549,14 +1594,16 @@ static int dwc3_gadget_start(struct usb_gadget *g,
|
||||
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
|
||||
|
||||
dep = dwc->eps[0];
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false,
|
||||
false);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
|
||||
goto err2;
|
||||
}
|
||||
|
||||
dep = dwc->eps[1];
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false,
|
||||
false);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
|
||||
goto err3;
|
||||
@ -1849,15 +1896,12 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
||||
*/
|
||||
dep->flags = DWC3_EP_PENDING_REQUEST;
|
||||
} else {
|
||||
dwc3_stop_active_transfer(dwc, dep->number);
|
||||
dwc3_stop_active_transfer(dwc, dep->number, true);
|
||||
dep->flags = DWC3_EP_ENABLED;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((event->status & DEPEVT_STATUS_IOC) &&
|
||||
(trb->ctrl & DWC3_TRB_CTRL_IOC))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -1999,7 +2043,25 @@ static void dwc3_disconnect_gadget(struct dwc3 *dwc)
|
||||
}
|
||||
}
|
||||
|
||||
static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum)
|
||||
static void dwc3_suspend_gadget(struct dwc3 *dwc)
|
||||
{
|
||||
if (dwc->gadget_driver && dwc->gadget_driver->suspend) {
|
||||
spin_unlock(&dwc->lock);
|
||||
dwc->gadget_driver->suspend(&dwc->gadget);
|
||||
spin_lock(&dwc->lock);
|
||||
}
|
||||
}
|
||||
|
||||
static void dwc3_resume_gadget(struct dwc3 *dwc)
|
||||
{
|
||||
if (dwc->gadget_driver && dwc->gadget_driver->resume) {
|
||||
spin_unlock(&dwc->lock);
|
||||
dwc->gadget_driver->resume(&dwc->gadget);
|
||||
spin_lock(&dwc->lock);
|
||||
}
|
||||
}
|
||||
|
||||
static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force)
|
||||
{
|
||||
struct dwc3_ep *dep;
|
||||
struct dwc3_gadget_ep_cmd_params params;
|
||||
@ -2031,7 +2093,8 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum)
|
||||
*/
|
||||
|
||||
cmd = DWC3_DEPCMD_ENDTRANSFER;
|
||||
cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC;
|
||||
cmd |= force ? DWC3_DEPCMD_HIPRI_FORCERM : 0;
|
||||
cmd |= DWC3_DEPCMD_CMDIOC;
|
||||
cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms);
|
||||
@ -2259,18 +2322,24 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
|
||||
*/
|
||||
reg |= DWC3_DCTL_HIRD_THRES(12);
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
} else {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg &= ~DWC3_DCTL_HIRD_THRES_MASK;
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
}
|
||||
|
||||
dep = dwc->eps[0];
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true);
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true,
|
||||
false);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
|
||||
return;
|
||||
}
|
||||
|
||||
dep = dwc->eps[1];
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true);
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true,
|
||||
false);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
|
||||
return;
|
||||
@ -2378,9 +2447,50 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
|
||||
|
||||
dwc->link_state = next;
|
||||
|
||||
switch (next) {
|
||||
case DWC3_LINK_STATE_U1:
|
||||
if (dwc->speed == USB_SPEED_SUPER)
|
||||
dwc3_suspend_gadget(dwc);
|
||||
break;
|
||||
case DWC3_LINK_STATE_U2:
|
||||
case DWC3_LINK_STATE_U3:
|
||||
dwc3_suspend_gadget(dwc);
|
||||
break;
|
||||
case DWC3_LINK_STATE_RESUME:
|
||||
dwc3_resume_gadget(dwc);
|
||||
break;
|
||||
default:
|
||||
/* do nothing */
|
||||
break;
|
||||
}
|
||||
|
||||
dev_vdbg(dwc->dev, "%s link %d\n", __func__, dwc->link_state);
|
||||
}
|
||||
|
||||
static void dwc3_gadget_hibernation_interrupt(struct dwc3 *dwc,
|
||||
unsigned int evtinfo)
|
||||
{
|
||||
unsigned int is_ss = evtinfo & BIT(4);
|
||||
|
||||
/**
|
||||
* WORKAROUND: DWC3 revison 2.20a with hibernation support
|
||||
* have a known issue which can cause USB CV TD.9.23 to fail
|
||||
* randomly.
|
||||
*
|
||||
* Because of this issue, core could generate bogus hibernation
|
||||
* events which SW needs to ignore.
|
||||
*
|
||||
* Refers to:
|
||||
*
|
||||
* STAR#9000546576: Device Mode Hibernation: Issue in USB 2.0
|
||||
* Device Fallback from SuperSpeed
|
||||
*/
|
||||
if (is_ss ^ (dwc->speed == USB_SPEED_SUPER))
|
||||
return;
|
||||
|
||||
/* enter hibernation here */
|
||||
}
|
||||
|
||||
static void dwc3_gadget_interrupt(struct dwc3 *dwc,
|
||||
const struct dwc3_event_devt *event)
|
||||
{
|
||||
@ -2397,6 +2507,13 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
|
||||
case DWC3_DEVICE_EVENT_WAKEUP:
|
||||
dwc3_gadget_wakeup_interrupt(dwc);
|
||||
break;
|
||||
case DWC3_DEVICE_EVENT_HIBER_REQ:
|
||||
if (dev_WARN_ONCE(dwc->dev, !dwc->has_hibernation,
|
||||
"unexpected hibernation event\n"))
|
||||
break;
|
||||
|
||||
dwc3_gadget_hibernation_interrupt(dwc, event->event_info);
|
||||
break;
|
||||
case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
|
||||
dwc3_gadget_linksts_change_interrupt(dwc, event->event_info);
|
||||
break;
|
||||
@ -2661,8 +2778,10 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
|
||||
|
||||
int dwc3_gadget_prepare(struct dwc3 *dwc)
|
||||
{
|
||||
if (dwc->pullups_connected)
|
||||
if (dwc->pullups_connected) {
|
||||
dwc3_gadget_disable_irq(dwc);
|
||||
dwc3_gadget_run_stop(dwc, true, true);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2671,7 +2790,7 @@ void dwc3_gadget_complete(struct dwc3 *dwc)
|
||||
{
|
||||
if (dwc->pullups_connected) {
|
||||
dwc3_gadget_enable_irq(dwc);
|
||||
dwc3_gadget_run_stop(dwc, true);
|
||||
dwc3_gadget_run_stop(dwc, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2694,12 +2813,14 @@ int dwc3_gadget_resume(struct dwc3 *dwc)
|
||||
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
|
||||
|
||||
dep = dwc->eps[0];
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false,
|
||||
false);
|
||||
if (ret)
|
||||
goto err0;
|
||||
|
||||
dep = dwc->eps[1];
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false,
|
||||
false);
|
||||
if (ret)
|
||||
goto err1;
|
||||
|
||||
|
@ -56,12 +56,6 @@ struct dwc3;
|
||||
/* DEPXFERCFG parameter 0 */
|
||||
#define DWC3_DEPXFERCFG_NUM_XFER_RES(n) ((n) & 0xffff)
|
||||
|
||||
struct dwc3_gadget_ep_cmd_params {
|
||||
u32 param2;
|
||||
u32 param1;
|
||||
u32 param0;
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#define to_dwc3_request(r) (container_of(r, struct dwc3_request, request))
|
||||
@ -85,9 +79,6 @@ static inline void dwc3_gadget_move_request_queued(struct dwc3_request *req)
|
||||
void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
|
||||
int status);
|
||||
|
||||
int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode);
|
||||
int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state);
|
||||
|
||||
void dwc3_ep0_interrupt(struct dwc3 *dwc,
|
||||
const struct dwc3_event_depevt *event);
|
||||
void dwc3_ep0_out_start(struct dwc3 *dwc);
|
||||
@ -95,9 +86,6 @@ int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value);
|
||||
int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
|
||||
gfp_t gfp_flags);
|
||||
int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value);
|
||||
int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
|
||||
unsigned cmd, struct dwc3_gadget_ep_cmd_params *params);
|
||||
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param);
|
||||
|
||||
/**
|
||||
* dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW
|
||||
|
@ -301,7 +301,6 @@ config USB_PXA27X
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
config USB_S3C_HSOTG
|
||||
depends on ARM
|
||||
tristate "Designware/S3C HS/OtG USB Device controller"
|
||||
help
|
||||
The Designware USB2.0 high-speed gadget controller
|
||||
|
@ -1758,15 +1758,15 @@ static int at91udc_probe(struct platform_device *pdev)
|
||||
|
||||
/* newer chips have more FIFO memory than rm9200 */
|
||||
if (cpu_is_at91sam9260() || cpu_is_at91sam9g20()) {
|
||||
usb_ep_set_maxpacket_limit(&udc->ep[0].ep, 64);
|
||||
usb_ep_set_maxpacket_limit(&udc->ep[3].ep, 64);
|
||||
usb_ep_set_maxpacket_limit(&udc->ep[4].ep, 512);
|
||||
usb_ep_set_maxpacket_limit(&udc->ep[5].ep, 512);
|
||||
udc->ep[0].maxpacket = 64;
|
||||
udc->ep[3].maxpacket = 64;
|
||||
udc->ep[4].maxpacket = 512;
|
||||
udc->ep[5].maxpacket = 512;
|
||||
} else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) {
|
||||
usb_ep_set_maxpacket_limit(&udc->ep[3].ep, 64);
|
||||
udc->ep[3].maxpacket = 64;
|
||||
} else if (cpu_is_at91sam9263()) {
|
||||
usb_ep_set_maxpacket_limit(&udc->ep[0].ep, 64);
|
||||
usb_ep_set_maxpacket_limit(&udc->ep[3].ep, 64);
|
||||
udc->ep[0].maxpacket = 64;
|
||||
udc->ep[3].maxpacket = 64;
|
||||
}
|
||||
|
||||
udc->udp_baseaddr = ioremap(res->start, resource_size(res));
|
||||
|
@ -1661,7 +1661,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
|
||||
if (dma_status) {
|
||||
int i;
|
||||
|
||||
for (i = 1; i < USBA_NR_ENDPOINTS; i++)
|
||||
for (i = 1; i < USBA_NR_DMAS; i++)
|
||||
if (dma_status & (1 << i))
|
||||
usba_dma_irq(udc, &udc->usba_ep[i]);
|
||||
}
|
||||
@ -1670,7 +1670,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
|
||||
if (ep_status) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < USBA_NR_ENDPOINTS; i++)
|
||||
for (i = 0; i < udc->num_ep; i++)
|
||||
if (ep_status & (1 << i)) {
|
||||
if (ep_is_control(&udc->usba_ep[i]))
|
||||
usba_control_irq(udc, &udc->usba_ep[i]);
|
||||
@ -1827,12 +1827,12 @@ static int atmel_usba_stop(struct usb_gadget *gadget,
|
||||
toggle_bias(0);
|
||||
usba_writel(udc, CTRL, USBA_DISABLE_MASK);
|
||||
|
||||
udc->driver = NULL;
|
||||
|
||||
clk_disable_unprepare(udc->hclk);
|
||||
clk_disable_unprepare(udc->pclk);
|
||||
|
||||
DBG(DBG_GADGET, "unregistered driver `%s'\n", driver->driver.name);
|
||||
DBG(DBG_GADGET, "unregistered driver `%s'\n", udc->driver->driver.name);
|
||||
|
||||
udc->driver = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1914,6 +1914,12 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
dev_err(&pdev->dev, "of_probe: no endpoint specified\n");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
return eps;
|
||||
err:
|
||||
return ERR_PTR(ret);
|
||||
|
@ -210,7 +210,7 @@
|
||||
#define USBA_FIFO_BASE(x) ((x) << 16)
|
||||
|
||||
/* Synth parameters */
|
||||
#define USBA_NR_ENDPOINTS 7
|
||||
#define USBA_NR_DMAS 7
|
||||
|
||||
#define EP0_FIFO_SIZE 64
|
||||
#define EP0_EPT_SIZE USBA_EPT_SIZE_64
|
||||
|
@ -28,6 +28,10 @@
|
||||
#include <linux/usb/composite.h>
|
||||
#include <linux/usb/functionfs.h>
|
||||
|
||||
#include <linux/aio.h>
|
||||
#include <linux/mmu_context.h>
|
||||
#include <linux/poll.h>
|
||||
|
||||
#include "u_fs.h"
|
||||
#include "configfs.h"
|
||||
|
||||
@ -99,6 +103,14 @@ static struct ffs_function *ffs_func_from_usb(struct usb_function *f)
|
||||
}
|
||||
|
||||
|
||||
static inline enum ffs_setup_state
|
||||
ffs_setup_state_clear_cancelled(struct ffs_data *ffs)
|
||||
{
|
||||
return (enum ffs_setup_state)
|
||||
cmpxchg(&ffs->setup_state, FFS_SETUP_CANCELLED, FFS_NO_SETUP);
|
||||
}
|
||||
|
||||
|
||||
static void ffs_func_eps_disable(struct ffs_function *func);
|
||||
static int __must_check ffs_func_eps_enable(struct ffs_function *func);
|
||||
|
||||
@ -122,8 +134,8 @@ struct ffs_ep {
|
||||
struct usb_ep *ep; /* P: ffs->eps_lock */
|
||||
struct usb_request *req; /* P: epfile->mutex */
|
||||
|
||||
/* [0]: full speed, [1]: high speed */
|
||||
struct usb_endpoint_descriptor *descs[2];
|
||||
/* [0]: full speed, [1]: high speed, [2]: super speed */
|
||||
struct usb_endpoint_descriptor *descs[3];
|
||||
|
||||
u8 num;
|
||||
|
||||
@ -148,6 +160,25 @@ struct ffs_epfile {
|
||||
unsigned char _pad;
|
||||
};
|
||||
|
||||
/* ffs_io_data structure ***************************************************/
|
||||
|
||||
struct ffs_io_data {
|
||||
bool aio;
|
||||
bool read;
|
||||
|
||||
struct kiocb *kiocb;
|
||||
const struct iovec *iovec;
|
||||
unsigned long nr_segs;
|
||||
char __user *buf;
|
||||
size_t len;
|
||||
|
||||
struct mm_struct *mm;
|
||||
struct work_struct work;
|
||||
|
||||
struct usb_ep *ep;
|
||||
struct usb_request *req;
|
||||
};
|
||||
|
||||
static int __must_check ffs_epfiles_create(struct ffs_data *ffs);
|
||||
static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count);
|
||||
|
||||
@ -161,8 +192,10 @@ ffs_sb_create_file(struct super_block *sb, const char *name, void *data,
|
||||
DEFINE_MUTEX(ffs_lock);
|
||||
EXPORT_SYMBOL(ffs_lock);
|
||||
|
||||
static struct ffs_dev *ffs_find_dev(const char *name);
|
||||
static struct ffs_dev *_ffs_find_dev(const char *name);
|
||||
static struct ffs_dev *_ffs_alloc_dev(void);
|
||||
static int _ffs_name_dev(struct ffs_dev *dev, const char *name);
|
||||
static void _ffs_free_dev(struct ffs_dev *dev);
|
||||
static void *ffs_acquire_dev(const char *dev_name);
|
||||
static void ffs_release_dev(struct ffs_data *ffs_data);
|
||||
static int ffs_ready(struct ffs_data *ffs);
|
||||
@ -218,7 +251,7 @@ static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len)
|
||||
}
|
||||
|
||||
ffs->setup_state = FFS_NO_SETUP;
|
||||
return ffs->ep0req_status;
|
||||
return req->status ? req->status : req->actual;
|
||||
}
|
||||
|
||||
static int __ffs_ep0_stall(struct ffs_data *ffs)
|
||||
@ -244,7 +277,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
|
||||
ENTER();
|
||||
|
||||
/* Fast check if setup was canceled */
|
||||
if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED)
|
||||
if (ffs_setup_state_clear_cancelled(ffs) == FFS_SETUP_CANCELLED)
|
||||
return -EIDRM;
|
||||
|
||||
/* Acquire mutex */
|
||||
@ -310,8 +343,8 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
|
||||
* rather then _irqsave
|
||||
*/
|
||||
spin_lock_irq(&ffs->ev.waitq.lock);
|
||||
switch (FFS_SETUP_STATE(ffs)) {
|
||||
case FFS_SETUP_CANCELED:
|
||||
switch (ffs_setup_state_clear_cancelled(ffs)) {
|
||||
case FFS_SETUP_CANCELLED:
|
||||
ret = -EIDRM;
|
||||
goto done_spin;
|
||||
|
||||
@ -346,7 +379,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
|
||||
/*
|
||||
* We are guaranteed to be still in FFS_ACTIVE state
|
||||
* but the state of setup could have changed from
|
||||
* FFS_SETUP_PENDING to FFS_SETUP_CANCELED so we need
|
||||
* FFS_SETUP_PENDING to FFS_SETUP_CANCELLED so we need
|
||||
* to check for that. If that happened we copied data
|
||||
* from user space in vain but it's unlikely.
|
||||
*
|
||||
@ -355,7 +388,8 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
|
||||
* transition can be performed and it's protected by
|
||||
* mutex.
|
||||
*/
|
||||
if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) {
|
||||
if (ffs_setup_state_clear_cancelled(ffs) ==
|
||||
FFS_SETUP_CANCELLED) {
|
||||
ret = -EIDRM;
|
||||
done_spin:
|
||||
spin_unlock_irq(&ffs->ev.waitq.lock);
|
||||
@ -421,7 +455,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
|
||||
ENTER();
|
||||
|
||||
/* Fast check if setup was canceled */
|
||||
if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED)
|
||||
if (ffs_setup_state_clear_cancelled(ffs) == FFS_SETUP_CANCELLED)
|
||||
return -EIDRM;
|
||||
|
||||
/* Acquire mutex */
|
||||
@ -441,8 +475,8 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
|
||||
*/
|
||||
spin_lock_irq(&ffs->ev.waitq.lock);
|
||||
|
||||
switch (FFS_SETUP_STATE(ffs)) {
|
||||
case FFS_SETUP_CANCELED:
|
||||
switch (ffs_setup_state_clear_cancelled(ffs)) {
|
||||
case FFS_SETUP_CANCELLED:
|
||||
ret = -EIDRM;
|
||||
break;
|
||||
|
||||
@ -489,7 +523,8 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
|
||||
spin_lock_irq(&ffs->ev.waitq.lock);
|
||||
|
||||
/* See ffs_ep0_write() */
|
||||
if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) {
|
||||
if (ffs_setup_state_clear_cancelled(ffs) ==
|
||||
FFS_SETUP_CANCELLED) {
|
||||
ret = -EIDRM;
|
||||
break;
|
||||
}
|
||||
@ -558,6 +593,45 @@ static long ffs_ep0_ioctl(struct file *file, unsigned code, unsigned long value)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int ffs_ep0_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct ffs_data *ffs = file->private_data;
|
||||
unsigned int mask = POLLWRNORM;
|
||||
int ret;
|
||||
|
||||
poll_wait(file, &ffs->ev.waitq, wait);
|
||||
|
||||
ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
|
||||
if (unlikely(ret < 0))
|
||||
return mask;
|
||||
|
||||
switch (ffs->state) {
|
||||
case FFS_READ_DESCRIPTORS:
|
||||
case FFS_READ_STRINGS:
|
||||
mask |= POLLOUT;
|
||||
break;
|
||||
|
||||
case FFS_ACTIVE:
|
||||
switch (ffs->setup_state) {
|
||||
case FFS_NO_SETUP:
|
||||
if (ffs->ev.count)
|
||||
mask |= POLLIN;
|
||||
break;
|
||||
|
||||
case FFS_SETUP_PENDING:
|
||||
case FFS_SETUP_CANCELLED:
|
||||
mask |= (POLLIN | POLLOUT);
|
||||
break;
|
||||
}
|
||||
case FFS_CLOSING:
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&ffs->mutex);
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
static const struct file_operations ffs_ep0_operations = {
|
||||
.llseek = no_llseek,
|
||||
|
||||
@ -566,6 +640,7 @@ static const struct file_operations ffs_ep0_operations = {
|
||||
.read = ffs_ep0_read,
|
||||
.release = ffs_ep0_release,
|
||||
.unlocked_ioctl = ffs_ep0_ioctl,
|
||||
.poll = ffs_ep0_poll,
|
||||
};
|
||||
|
||||
|
||||
@ -581,8 +656,52 @@ static void ffs_epfile_io_complete(struct usb_ep *_ep, struct usb_request *req)
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t ffs_epfile_io(struct file *file,
|
||||
char __user *buf, size_t len, int read)
|
||||
static void ffs_user_copy_worker(struct work_struct *work)
|
||||
{
|
||||
struct ffs_io_data *io_data = container_of(work, struct ffs_io_data,
|
||||
work);
|
||||
int ret = io_data->req->status ? io_data->req->status :
|
||||
io_data->req->actual;
|
||||
|
||||
if (io_data->read && ret > 0) {
|
||||
int i;
|
||||
size_t pos = 0;
|
||||
use_mm(io_data->mm);
|
||||
for (i = 0; i < io_data->nr_segs; i++) {
|
||||
if (unlikely(copy_to_user(io_data->iovec[i].iov_base,
|
||||
&io_data->buf[pos],
|
||||
io_data->iovec[i].iov_len))) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
pos += io_data->iovec[i].iov_len;
|
||||
}
|
||||
unuse_mm(io_data->mm);
|
||||
}
|
||||
|
||||
aio_complete(io_data->kiocb, ret, ret);
|
||||
|
||||
usb_ep_free_request(io_data->ep, io_data->req);
|
||||
|
||||
io_data->kiocb->private = NULL;
|
||||
if (io_data->read)
|
||||
kfree(io_data->iovec);
|
||||
kfree(io_data->buf);
|
||||
kfree(io_data);
|
||||
}
|
||||
|
||||
static void ffs_epfile_async_io_complete(struct usb_ep *_ep,
|
||||
struct usb_request *req)
|
||||
{
|
||||
struct ffs_io_data *io_data = req->context;
|
||||
|
||||
ENTER();
|
||||
|
||||
INIT_WORK(&io_data->work, ffs_user_copy_worker);
|
||||
schedule_work(&io_data->work);
|
||||
}
|
||||
|
||||
static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
|
||||
{
|
||||
struct ffs_epfile *epfile = file->private_data;
|
||||
struct ffs_ep *ep;
|
||||
@ -612,7 +731,7 @@ static ssize_t ffs_epfile_io(struct file *file,
|
||||
}
|
||||
|
||||
/* Do we halt? */
|
||||
halt = !read == !epfile->in;
|
||||
halt = (!io_data->read == !epfile->in);
|
||||
if (halt && epfile->isoc) {
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
@ -630,15 +749,32 @@ static ssize_t ffs_epfile_io(struct file *file,
|
||||
* Controller may require buffer size to be aligned to
|
||||
* maxpacketsize of an out endpoint.
|
||||
*/
|
||||
data_len = read ? usb_ep_align_maybe(gadget, ep->ep, len) : len;
|
||||
data_len = io_data->read ?
|
||||
usb_ep_align_maybe(gadget, ep->ep, io_data->len) :
|
||||
io_data->len;
|
||||
|
||||
data = kmalloc(data_len, GFP_KERNEL);
|
||||
if (unlikely(!data))
|
||||
return -ENOMEM;
|
||||
|
||||
if (!read && unlikely(copy_from_user(data, buf, len))) {
|
||||
ret = -EFAULT;
|
||||
goto error;
|
||||
if (io_data->aio && !io_data->read) {
|
||||
int i;
|
||||
size_t pos = 0;
|
||||
for (i = 0; i < io_data->nr_segs; i++) {
|
||||
if (unlikely(copy_from_user(&data[pos],
|
||||
io_data->iovec[i].iov_base,
|
||||
io_data->iovec[i].iov_len))) {
|
||||
ret = -EFAULT;
|
||||
goto error;
|
||||
}
|
||||
pos += io_data->iovec[i].iov_len;
|
||||
}
|
||||
} else {
|
||||
if (!io_data->read &&
|
||||
unlikely(__copy_from_user(data, io_data->buf,
|
||||
io_data->len))) {
|
||||
ret = -EFAULT;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -661,40 +797,74 @@ static ssize_t ffs_epfile_io(struct file *file,
|
||||
ret = -EBADMSG;
|
||||
} else {
|
||||
/* Fire the request */
|
||||
DECLARE_COMPLETION_ONSTACK(done);
|
||||
struct usb_request *req;
|
||||
|
||||
struct usb_request *req = ep->req;
|
||||
req->context = &done;
|
||||
req->complete = ffs_epfile_io_complete;
|
||||
req->buf = data;
|
||||
req->length = data_len;
|
||||
if (io_data->aio) {
|
||||
req = usb_ep_alloc_request(ep->ep, GFP_KERNEL);
|
||||
if (unlikely(!req))
|
||||
goto error;
|
||||
|
||||
ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
|
||||
req->buf = data;
|
||||
req->length = io_data->len;
|
||||
|
||||
spin_unlock_irq(&epfile->ffs->eps_lock);
|
||||
io_data->buf = data;
|
||||
io_data->ep = ep->ep;
|
||||
io_data->req = req;
|
||||
|
||||
if (unlikely(ret < 0)) {
|
||||
/* nop */
|
||||
} else if (unlikely(wait_for_completion_interruptible(&done))) {
|
||||
ret = -EINTR;
|
||||
usb_ep_dequeue(ep->ep, req);
|
||||
req->context = io_data;
|
||||
req->complete = ffs_epfile_async_io_complete;
|
||||
|
||||
ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
|
||||
if (unlikely(ret)) {
|
||||
usb_ep_free_request(ep->ep, req);
|
||||
goto error;
|
||||
}
|
||||
ret = -EIOCBQUEUED;
|
||||
|
||||
spin_unlock_irq(&epfile->ffs->eps_lock);
|
||||
} else {
|
||||
/*
|
||||
* XXX We may end up silently droping data here.
|
||||
* Since data_len (i.e. req->length) may be bigger
|
||||
* than len (after being rounded up to maxpacketsize),
|
||||
* we may end up with more data then user space has
|
||||
* space for.
|
||||
*/
|
||||
ret = ep->status;
|
||||
if (read && ret > 0 &&
|
||||
unlikely(copy_to_user(buf, data,
|
||||
min_t(size_t, ret, len))))
|
||||
ret = -EFAULT;
|
||||
DECLARE_COMPLETION_ONSTACK(done);
|
||||
|
||||
req = ep->req;
|
||||
req->buf = data;
|
||||
req->length = io_data->len;
|
||||
|
||||
req->context = &done;
|
||||
req->complete = ffs_epfile_io_complete;
|
||||
|
||||
ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
|
||||
|
||||
spin_unlock_irq(&epfile->ffs->eps_lock);
|
||||
|
||||
if (unlikely(ret < 0)) {
|
||||
/* nop */
|
||||
} else if (unlikely(
|
||||
wait_for_completion_interruptible(&done))) {
|
||||
ret = -EINTR;
|
||||
usb_ep_dequeue(ep->ep, req);
|
||||
} else {
|
||||
/*
|
||||
* XXX We may end up silently droping data
|
||||
* here. Since data_len (i.e. req->length) may
|
||||
* be bigger than len (after being rounded up
|
||||
* to maxpacketsize), we may end up with more
|
||||
* data then user space has space for.
|
||||
*/
|
||||
ret = ep->status;
|
||||
if (io_data->read && ret > 0) {
|
||||
ret = min_t(size_t, ret, io_data->len);
|
||||
|
||||
if (unlikely(copy_to_user(io_data->buf,
|
||||
data, ret)))
|
||||
ret = -EFAULT;
|
||||
}
|
||||
}
|
||||
kfree(data);
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&epfile->mutex);
|
||||
return ret;
|
||||
error:
|
||||
kfree(data);
|
||||
return ret;
|
||||
@ -704,17 +874,31 @@ static ssize_t
|
||||
ffs_epfile_write(struct file *file, const char __user *buf, size_t len,
|
||||
loff_t *ptr)
|
||||
{
|
||||
struct ffs_io_data io_data;
|
||||
|
||||
ENTER();
|
||||
|
||||
return ffs_epfile_io(file, (char __user *)buf, len, 0);
|
||||
io_data.aio = false;
|
||||
io_data.read = false;
|
||||
io_data.buf = (char * __user)buf;
|
||||
io_data.len = len;
|
||||
|
||||
return ffs_epfile_io(file, &io_data);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
ffs_epfile_read(struct file *file, char __user *buf, size_t len, loff_t *ptr)
|
||||
{
|
||||
struct ffs_io_data io_data;
|
||||
|
||||
ENTER();
|
||||
|
||||
return ffs_epfile_io(file, buf, len, 1);
|
||||
io_data.aio = false;
|
||||
io_data.read = true;
|
||||
io_data.buf = buf;
|
||||
io_data.len = len;
|
||||
|
||||
return ffs_epfile_io(file, &io_data);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -733,6 +917,89 @@ ffs_epfile_open(struct inode *inode, struct file *file)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ffs_aio_cancel(struct kiocb *kiocb)
|
||||
{
|
||||
struct ffs_io_data *io_data = kiocb->private;
|
||||
struct ffs_epfile *epfile = kiocb->ki_filp->private_data;
|
||||
int value;
|
||||
|
||||
ENTER();
|
||||
|
||||
spin_lock_irq(&epfile->ffs->eps_lock);
|
||||
|
||||
if (likely(io_data && io_data->ep && io_data->req))
|
||||
value = usb_ep_dequeue(io_data->ep, io_data->req);
|
||||
else
|
||||
value = -EINVAL;
|
||||
|
||||
spin_unlock_irq(&epfile->ffs->eps_lock);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static ssize_t ffs_epfile_aio_write(struct kiocb *kiocb,
|
||||
const struct iovec *iovec,
|
||||
unsigned long nr_segs, loff_t loff)
|
||||
{
|
||||
struct ffs_io_data *io_data;
|
||||
|
||||
ENTER();
|
||||
|
||||
io_data = kmalloc(sizeof(*io_data), GFP_KERNEL);
|
||||
if (unlikely(!io_data))
|
||||
return -ENOMEM;
|
||||
|
||||
io_data->aio = true;
|
||||
io_data->read = false;
|
||||
io_data->kiocb = kiocb;
|
||||
io_data->iovec = iovec;
|
||||
io_data->nr_segs = nr_segs;
|
||||
io_data->len = kiocb->ki_nbytes;
|
||||
io_data->mm = current->mm;
|
||||
|
||||
kiocb->private = io_data;
|
||||
|
||||
kiocb_set_cancel_fn(kiocb, ffs_aio_cancel);
|
||||
|
||||
return ffs_epfile_io(kiocb->ki_filp, io_data);
|
||||
}
|
||||
|
||||
static ssize_t ffs_epfile_aio_read(struct kiocb *kiocb,
|
||||
const struct iovec *iovec,
|
||||
unsigned long nr_segs, loff_t loff)
|
||||
{
|
||||
struct ffs_io_data *io_data;
|
||||
struct iovec *iovec_copy;
|
||||
|
||||
ENTER();
|
||||
|
||||
iovec_copy = kmalloc_array(nr_segs, sizeof(*iovec_copy), GFP_KERNEL);
|
||||
if (unlikely(!iovec_copy))
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(iovec_copy, iovec, sizeof(struct iovec)*nr_segs);
|
||||
|
||||
io_data = kmalloc(sizeof(*io_data), GFP_KERNEL);
|
||||
if (unlikely(!io_data)) {
|
||||
kfree(iovec_copy);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
io_data->aio = true;
|
||||
io_data->read = true;
|
||||
io_data->kiocb = kiocb;
|
||||
io_data->iovec = iovec_copy;
|
||||
io_data->nr_segs = nr_segs;
|
||||
io_data->len = kiocb->ki_nbytes;
|
||||
io_data->mm = current->mm;
|
||||
|
||||
kiocb->private = io_data;
|
||||
|
||||
kiocb_set_cancel_fn(kiocb, ffs_aio_cancel);
|
||||
|
||||
return ffs_epfile_io(kiocb->ki_filp, io_data);
|
||||
}
|
||||
|
||||
static int
|
||||
ffs_epfile_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
@ -789,6 +1056,8 @@ static const struct file_operations ffs_epfile_operations = {
|
||||
.open = ffs_epfile_open,
|
||||
.write = ffs_epfile_write,
|
||||
.read = ffs_epfile_read,
|
||||
.aio_write = ffs_epfile_aio_write,
|
||||
.aio_read = ffs_epfile_aio_read,
|
||||
.release = ffs_epfile_release,
|
||||
.unlocked_ioctl = ffs_epfile_ioctl,
|
||||
};
|
||||
@ -1172,7 +1441,7 @@ static void ffs_data_clear(struct ffs_data *ffs)
|
||||
if (ffs->epfiles)
|
||||
ffs_epfiles_destroy(ffs->epfiles, ffs->eps_count);
|
||||
|
||||
kfree(ffs->raw_descs);
|
||||
kfree(ffs->raw_descs_data);
|
||||
kfree(ffs->raw_strings);
|
||||
kfree(ffs->stringtabs);
|
||||
}
|
||||
@ -1184,14 +1453,15 @@ static void ffs_data_reset(struct ffs_data *ffs)
|
||||
ffs_data_clear(ffs);
|
||||
|
||||
ffs->epfiles = NULL;
|
||||
ffs->raw_descs_data = NULL;
|
||||
ffs->raw_descs = NULL;
|
||||
ffs->raw_strings = NULL;
|
||||
ffs->stringtabs = NULL;
|
||||
|
||||
ffs->raw_descs_length = 0;
|
||||
ffs->raw_fs_descs_length = 0;
|
||||
ffs->fs_descs_count = 0;
|
||||
ffs->hs_descs_count = 0;
|
||||
ffs->ss_descs_count = 0;
|
||||
|
||||
ffs->strings_count = 0;
|
||||
ffs->interfaces_count = 0;
|
||||
@ -1334,7 +1604,24 @@ static int ffs_func_eps_enable(struct ffs_function *func)
|
||||
spin_lock_irqsave(&func->ffs->eps_lock, flags);
|
||||
do {
|
||||
struct usb_endpoint_descriptor *ds;
|
||||
ds = ep->descs[ep->descs[1] ? 1 : 0];
|
||||
int desc_idx;
|
||||
|
||||
if (ffs->gadget->speed == USB_SPEED_SUPER)
|
||||
desc_idx = 2;
|
||||
else if (ffs->gadget->speed == USB_SPEED_HIGH)
|
||||
desc_idx = 1;
|
||||
else
|
||||
desc_idx = 0;
|
||||
|
||||
/* fall-back to lower speed if desc missing for current speed */
|
||||
do {
|
||||
ds = ep->descs[desc_idx];
|
||||
} while (!ds && --desc_idx >= 0);
|
||||
|
||||
if (!ds) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
ep->ep->driver_data = ep;
|
||||
ep->ep->desc = ds;
|
||||
@ -1469,6 +1756,12 @@ static int __must_check ffs_do_desc(char *data, unsigned len,
|
||||
}
|
||||
break;
|
||||
|
||||
case USB_DT_SS_ENDPOINT_COMP:
|
||||
pr_vdebug("EP SS companion descriptor\n");
|
||||
if (length != sizeof(struct usb_ss_ep_comp_descriptor))
|
||||
goto inv_length;
|
||||
break;
|
||||
|
||||
case USB_DT_OTHER_SPEED_CONFIG:
|
||||
case USB_DT_INTERFACE_POWER:
|
||||
case USB_DT_DEBUG:
|
||||
@ -1579,60 +1872,76 @@ static int __ffs_data_do_entity(enum ffs_entity_type type,
|
||||
static int __ffs_data_got_descs(struct ffs_data *ffs,
|
||||
char *const _data, size_t len)
|
||||
{
|
||||
unsigned fs_count, hs_count;
|
||||
int fs_len, ret = -EINVAL;
|
||||
char *data = _data;
|
||||
char *data = _data, *raw_descs;
|
||||
unsigned counts[3], flags;
|
||||
int ret = -EINVAL, i;
|
||||
|
||||
ENTER();
|
||||
|
||||
if (unlikely(get_unaligned_le32(data) != FUNCTIONFS_DESCRIPTORS_MAGIC ||
|
||||
get_unaligned_le32(data + 4) != len))
|
||||
if (get_unaligned_le32(data + 4) != len)
|
||||
goto error;
|
||||
fs_count = get_unaligned_le32(data + 8);
|
||||
hs_count = get_unaligned_le32(data + 12);
|
||||
|
||||
if (!fs_count && !hs_count)
|
||||
goto einval;
|
||||
|
||||
data += 16;
|
||||
len -= 16;
|
||||
|
||||
if (likely(fs_count)) {
|
||||
fs_len = ffs_do_descs(fs_count, data, len,
|
||||
__ffs_data_do_entity, ffs);
|
||||
if (unlikely(fs_len < 0)) {
|
||||
ret = fs_len;
|
||||
switch (get_unaligned_le32(data)) {
|
||||
case FUNCTIONFS_DESCRIPTORS_MAGIC:
|
||||
flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC;
|
||||
data += 8;
|
||||
len -= 8;
|
||||
break;
|
||||
case FUNCTIONFS_DESCRIPTORS_MAGIC_V2:
|
||||
flags = get_unaligned_le32(data + 8);
|
||||
if (flags & ~(FUNCTIONFS_HAS_FS_DESC |
|
||||
FUNCTIONFS_HAS_HS_DESC |
|
||||
FUNCTIONFS_HAS_SS_DESC)) {
|
||||
ret = -ENOSYS;
|
||||
goto error;
|
||||
}
|
||||
|
||||
data += fs_len;
|
||||
len -= fs_len;
|
||||
} else {
|
||||
fs_len = 0;
|
||||
data += 12;
|
||||
len -= 12;
|
||||
break;
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (likely(hs_count)) {
|
||||
ret = ffs_do_descs(hs_count, data, len,
|
||||
__ffs_data_do_entity, ffs);
|
||||
if (unlikely(ret < 0))
|
||||
/* Read fs_count, hs_count and ss_count (if present) */
|
||||
for (i = 0; i < 3; ++i) {
|
||||
if (!(flags & (1 << i))) {
|
||||
counts[i] = 0;
|
||||
} else if (len < 4) {
|
||||
goto error;
|
||||
} else {
|
||||
ret = 0;
|
||||
} else {
|
||||
counts[i] = get_unaligned_le32(data);
|
||||
data += 4;
|
||||
len -= 4;
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(len != ret))
|
||||
goto einval;
|
||||
/* Read descriptors */
|
||||
raw_descs = data;
|
||||
for (i = 0; i < 3; ++i) {
|
||||
if (!counts[i])
|
||||
continue;
|
||||
ret = ffs_do_descs(counts[i], data, len,
|
||||
__ffs_data_do_entity, ffs);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
data += ret;
|
||||
len -= ret;
|
||||
}
|
||||
|
||||
ffs->raw_fs_descs_length = fs_len;
|
||||
ffs->raw_descs_length = fs_len + ret;
|
||||
ffs->raw_descs = _data;
|
||||
ffs->fs_descs_count = fs_count;
|
||||
ffs->hs_descs_count = hs_count;
|
||||
if (raw_descs == data || len) {
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
ffs->raw_descs_data = _data;
|
||||
ffs->raw_descs = raw_descs;
|
||||
ffs->raw_descs_length = data - raw_descs;
|
||||
ffs->fs_descs_count = counts[0];
|
||||
ffs->hs_descs_count = counts[1];
|
||||
ffs->ss_descs_count = counts[2];
|
||||
|
||||
return 0;
|
||||
|
||||
einval:
|
||||
ret = -EINVAL;
|
||||
error:
|
||||
kfree(_data);
|
||||
return ret;
|
||||
@ -1789,7 +2098,7 @@ static void __ffs_event_add(struct ffs_data *ffs,
|
||||
* the source does nothing.
|
||||
*/
|
||||
if (ffs->setup_state == FFS_SETUP_PENDING)
|
||||
ffs->setup_state = FFS_SETUP_CANCELED;
|
||||
ffs->setup_state = FFS_SETUP_CANCELLED;
|
||||
|
||||
switch (type) {
|
||||
case FUNCTIONFS_RESUME:
|
||||
@ -1850,21 +2159,28 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
|
||||
struct usb_endpoint_descriptor *ds = (void *)desc;
|
||||
struct ffs_function *func = priv;
|
||||
struct ffs_ep *ffs_ep;
|
||||
|
||||
/*
|
||||
* If hs_descriptors is not NULL then we are reading hs
|
||||
* descriptors now
|
||||
*/
|
||||
const int isHS = func->function.hs_descriptors != NULL;
|
||||
unsigned idx;
|
||||
unsigned ep_desc_id, idx;
|
||||
static const char *speed_names[] = { "full", "high", "super" };
|
||||
|
||||
if (type != FFS_DESCRIPTOR)
|
||||
return 0;
|
||||
|
||||
if (isHS)
|
||||
/*
|
||||
* If ss_descriptors is not NULL, we are reading super speed
|
||||
* descriptors; if hs_descriptors is not NULL, we are reading high
|
||||
* speed descriptors; otherwise, we are reading full speed
|
||||
* descriptors.
|
||||
*/
|
||||
if (func->function.ss_descriptors) {
|
||||
ep_desc_id = 2;
|
||||
func->function.ss_descriptors[(long)valuep] = desc;
|
||||
} else if (func->function.hs_descriptors) {
|
||||
ep_desc_id = 1;
|
||||
func->function.hs_descriptors[(long)valuep] = desc;
|
||||
else
|
||||
} else {
|
||||
ep_desc_id = 0;
|
||||
func->function.fs_descriptors[(long)valuep] = desc;
|
||||
}
|
||||
|
||||
if (!desc || desc->bDescriptorType != USB_DT_ENDPOINT)
|
||||
return 0;
|
||||
@ -1872,13 +2188,13 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
|
||||
idx = (ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) - 1;
|
||||
ffs_ep = func->eps + idx;
|
||||
|
||||
if (unlikely(ffs_ep->descs[isHS])) {
|
||||
pr_vdebug("two %sspeed descriptors for EP %d\n",
|
||||
isHS ? "high" : "full",
|
||||
if (unlikely(ffs_ep->descs[ep_desc_id])) {
|
||||
pr_err("two %sspeed descriptors for EP %d\n",
|
||||
speed_names[ep_desc_id],
|
||||
ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
|
||||
return -EINVAL;
|
||||
}
|
||||
ffs_ep->descs[isHS] = ds;
|
||||
ffs_ep->descs[ep_desc_id] = ds;
|
||||
|
||||
ffs_dump_mem(": Original ep desc", ds, ds->bLength);
|
||||
if (ffs_ep->ep) {
|
||||
@ -2022,8 +2338,10 @@ static int _ffs_func_bind(struct usb_configuration *c,
|
||||
const int full = !!func->ffs->fs_descs_count;
|
||||
const int high = gadget_is_dualspeed(func->gadget) &&
|
||||
func->ffs->hs_descs_count;
|
||||
const int super = gadget_is_superspeed(func->gadget) &&
|
||||
func->ffs->ss_descs_count;
|
||||
|
||||
int ret;
|
||||
int fs_len, hs_len, ret;
|
||||
|
||||
/* Make it a single chunk, less management later on */
|
||||
vla_group(d);
|
||||
@ -2032,15 +2350,16 @@ static int _ffs_func_bind(struct usb_configuration *c,
|
||||
full ? ffs->fs_descs_count + 1 : 0);
|
||||
vla_item_with_sz(d, struct usb_descriptor_header *, hs_descs,
|
||||
high ? ffs->hs_descs_count + 1 : 0);
|
||||
vla_item_with_sz(d, struct usb_descriptor_header *, ss_descs,
|
||||
super ? ffs->ss_descs_count + 1 : 0);
|
||||
vla_item_with_sz(d, short, inums, ffs->interfaces_count);
|
||||
vla_item_with_sz(d, char, raw_descs,
|
||||
high ? ffs->raw_descs_length : ffs->raw_fs_descs_length);
|
||||
vla_item_with_sz(d, char, raw_descs, ffs->raw_descs_length);
|
||||
char *vlabuf;
|
||||
|
||||
ENTER();
|
||||
|
||||
/* Only high speed but not supported by gadget? */
|
||||
if (unlikely(!(full | high)))
|
||||
/* Has descriptors only for speeds gadget does not support */
|
||||
if (unlikely(!(full | high | super)))
|
||||
return -ENOTSUPP;
|
||||
|
||||
/* Allocate a single chunk, less management later on */
|
||||
@ -2050,8 +2369,10 @@ static int _ffs_func_bind(struct usb_configuration *c,
|
||||
|
||||
/* Zero */
|
||||
memset(vla_ptr(vlabuf, d, eps), 0, d_eps__sz);
|
||||
memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs + 16,
|
||||
d_raw_descs__sz);
|
||||
/* Copy descriptors */
|
||||
memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs,
|
||||
ffs->raw_descs_length);
|
||||
|
||||
memset(vla_ptr(vlabuf, d, inums), 0xff, d_inums__sz);
|
||||
for (ret = ffs->eps_count; ret; --ret) {
|
||||
struct ffs_ep *ptr;
|
||||
@ -2073,22 +2394,38 @@ static int _ffs_func_bind(struct usb_configuration *c,
|
||||
*/
|
||||
if (likely(full)) {
|
||||
func->function.fs_descriptors = vla_ptr(vlabuf, d, fs_descs);
|
||||
ret = ffs_do_descs(ffs->fs_descs_count,
|
||||
vla_ptr(vlabuf, d, raw_descs),
|
||||
d_raw_descs__sz,
|
||||
__ffs_func_bind_do_descs, func);
|
||||
if (unlikely(ret < 0))
|
||||
fs_len = ffs_do_descs(ffs->fs_descs_count,
|
||||
vla_ptr(vlabuf, d, raw_descs),
|
||||
d_raw_descs__sz,
|
||||
__ffs_func_bind_do_descs, func);
|
||||
if (unlikely(fs_len < 0)) {
|
||||
ret = fs_len;
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
ret = 0;
|
||||
fs_len = 0;
|
||||
}
|
||||
|
||||
if (likely(high)) {
|
||||
func->function.hs_descriptors = vla_ptr(vlabuf, d, hs_descs);
|
||||
ret = ffs_do_descs(ffs->hs_descs_count,
|
||||
vla_ptr(vlabuf, d, raw_descs) + ret,
|
||||
d_raw_descs__sz - ret,
|
||||
__ffs_func_bind_do_descs, func);
|
||||
hs_len = ffs_do_descs(ffs->hs_descs_count,
|
||||
vla_ptr(vlabuf, d, raw_descs) + fs_len,
|
||||
d_raw_descs__sz - fs_len,
|
||||
__ffs_func_bind_do_descs, func);
|
||||
if (unlikely(hs_len < 0)) {
|
||||
ret = hs_len;
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
hs_len = 0;
|
||||
}
|
||||
|
||||
if (likely(super)) {
|
||||
func->function.ss_descriptors = vla_ptr(vlabuf, d, ss_descs);
|
||||
ret = ffs_do_descs(ffs->ss_descs_count,
|
||||
vla_ptr(vlabuf, d, raw_descs) + fs_len + hs_len,
|
||||
d_raw_descs__sz - fs_len - hs_len,
|
||||
__ffs_func_bind_do_descs, func);
|
||||
if (unlikely(ret < 0))
|
||||
goto error;
|
||||
}
|
||||
@ -2099,7 +2436,8 @@ static int _ffs_func_bind(struct usb_configuration *c,
|
||||
* now.
|
||||
*/
|
||||
ret = ffs_do_descs(ffs->fs_descs_count +
|
||||
(high ? ffs->hs_descs_count : 0),
|
||||
(high ? ffs->hs_descs_count : 0) +
|
||||
(super ? ffs->ss_descs_count : 0),
|
||||
vla_ptr(vlabuf, d, raw_descs), d_raw_descs__sz,
|
||||
__ffs_func_bind_do_nums, func);
|
||||
if (unlikely(ret < 0))
|
||||
@ -2258,7 +2596,7 @@ static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf)
|
||||
|
||||
static LIST_HEAD(ffs_devices);
|
||||
|
||||
static struct ffs_dev *_ffs_find_dev(const char *name)
|
||||
static struct ffs_dev *_ffs_do_find_dev(const char *name)
|
||||
{
|
||||
struct ffs_dev *dev;
|
||||
|
||||
@ -2275,7 +2613,7 @@ static struct ffs_dev *_ffs_find_dev(const char *name)
|
||||
/*
|
||||
* ffs_lock must be taken by the caller of this function
|
||||
*/
|
||||
static struct ffs_dev *ffs_get_single_dev(void)
|
||||
static struct ffs_dev *_ffs_get_single_dev(void)
|
||||
{
|
||||
struct ffs_dev *dev;
|
||||
|
||||
@ -2291,15 +2629,15 @@ static struct ffs_dev *ffs_get_single_dev(void)
|
||||
/*
|
||||
* ffs_lock must be taken by the caller of this function
|
||||
*/
|
||||
static struct ffs_dev *ffs_find_dev(const char *name)
|
||||
static struct ffs_dev *_ffs_find_dev(const char *name)
|
||||
{
|
||||
struct ffs_dev *dev;
|
||||
|
||||
dev = ffs_get_single_dev();
|
||||
dev = _ffs_get_single_dev();
|
||||
if (dev)
|
||||
return dev;
|
||||
|
||||
return _ffs_find_dev(name);
|
||||
return _ffs_do_find_dev(name);
|
||||
}
|
||||
|
||||
/* Configfs support *********************************************************/
|
||||
@ -2335,7 +2673,7 @@ static void ffs_free_inst(struct usb_function_instance *f)
|
||||
|
||||
opts = to_f_fs_opts(f);
|
||||
ffs_dev_lock();
|
||||
ffs_free_dev(opts->dev);
|
||||
_ffs_free_dev(opts->dev);
|
||||
ffs_dev_unlock();
|
||||
kfree(opts);
|
||||
}
|
||||
@ -2390,7 +2728,7 @@ static struct usb_function_instance *ffs_alloc_inst(void)
|
||||
opts->func_inst.set_inst_name = ffs_set_inst_name;
|
||||
opts->func_inst.free_func_inst = ffs_free_inst;
|
||||
ffs_dev_lock();
|
||||
dev = ffs_alloc_dev();
|
||||
dev = _ffs_alloc_dev();
|
||||
ffs_dev_unlock();
|
||||
if (IS_ERR(dev)) {
|
||||
kfree(opts);
|
||||
@ -2446,6 +2784,7 @@ static void ffs_func_unbind(struct usb_configuration *c,
|
||||
*/
|
||||
func->function.fs_descriptors = NULL;
|
||||
func->function.hs_descriptors = NULL;
|
||||
func->function.ss_descriptors = NULL;
|
||||
func->interfaces_nums = NULL;
|
||||
|
||||
ffs_event_add(ffs, FUNCTIONFS_UNBIND);
|
||||
@ -2478,12 +2817,12 @@ static struct usb_function *ffs_alloc(struct usb_function_instance *fi)
|
||||
/*
|
||||
* ffs_lock must be taken by the caller of this function
|
||||
*/
|
||||
struct ffs_dev *ffs_alloc_dev(void)
|
||||
static struct ffs_dev *_ffs_alloc_dev(void)
|
||||
{
|
||||
struct ffs_dev *dev;
|
||||
int ret;
|
||||
|
||||
if (ffs_get_single_dev())
|
||||
if (_ffs_get_single_dev())
|
||||
return ERR_PTR(-EBUSY);
|
||||
|
||||
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
||||
@ -2511,10 +2850,10 @@ static int _ffs_name_dev(struct ffs_dev *dev, const char *name)
|
||||
{
|
||||
struct ffs_dev *existing;
|
||||
|
||||
existing = _ffs_find_dev(name);
|
||||
existing = _ffs_do_find_dev(name);
|
||||
if (existing)
|
||||
return -EBUSY;
|
||||
|
||||
|
||||
dev->name = name;
|
||||
|
||||
return 0;
|
||||
@ -2555,7 +2894,7 @@ EXPORT_SYMBOL(ffs_single_dev);
|
||||
/*
|
||||
* ffs_lock must be taken by the caller of this function
|
||||
*/
|
||||
void ffs_free_dev(struct ffs_dev *dev)
|
||||
static void _ffs_free_dev(struct ffs_dev *dev)
|
||||
{
|
||||
list_del(&dev->entry);
|
||||
if (dev->name_allocated)
|
||||
@ -2572,7 +2911,7 @@ static void *ffs_acquire_dev(const char *dev_name)
|
||||
ENTER();
|
||||
ffs_dev_lock();
|
||||
|
||||
ffs_dev = ffs_find_dev(dev_name);
|
||||
ffs_dev = _ffs_find_dev(dev_name);
|
||||
if (!ffs_dev)
|
||||
ffs_dev = ERR_PTR(-ENODEV);
|
||||
else if (ffs_dev->mounted)
|
||||
@ -2595,11 +2934,12 @@ static void ffs_release_dev(struct ffs_data *ffs_data)
|
||||
ffs_dev_lock();
|
||||
|
||||
ffs_dev = ffs_data->private_data;
|
||||
if (ffs_dev)
|
||||
if (ffs_dev) {
|
||||
ffs_dev->mounted = false;
|
||||
|
||||
if (ffs_dev->ffs_release_dev_callback)
|
||||
ffs_dev->ffs_release_dev_callback(ffs_dev);
|
||||
|
||||
if (ffs_dev->ffs_release_dev_callback)
|
||||
ffs_dev->ffs_release_dev_callback(ffs_dev);
|
||||
}
|
||||
|
||||
ffs_dev_unlock();
|
||||
}
|
||||
|
@ -225,14 +225,8 @@ static void gr_dfs_create(struct gr_udc *dev)
|
||||
const char *name = "gr_udc_state";
|
||||
|
||||
dev->dfs_root = debugfs_create_dir(dev_name(dev->dev), NULL);
|
||||
if (IS_ERR(dev->dfs_root)) {
|
||||
dev_err(dev->dev, "Failed to create debugfs directory\n");
|
||||
return;
|
||||
}
|
||||
dev->dfs_state = debugfs_create_file(name, 0444, dev->dfs_root,
|
||||
dev, &gr_dfs_fops);
|
||||
if (IS_ERR(dev->dfs_state))
|
||||
dev_err(dev->dev, "Failed to create debugfs file %s\n", name);
|
||||
dev->dfs_state = debugfs_create_file(name, 0444, dev->dfs_root, dev,
|
||||
&gr_dfs_fops);
|
||||
}
|
||||
|
||||
static void gr_dfs_delete(struct gr_udc *dev)
|
||||
|
@ -427,12 +427,17 @@ setup_rx_reqs(struct printer_dev *dev)
|
||||
req->length = USB_BUFSIZE;
|
||||
req->complete = rx_complete;
|
||||
|
||||
/* here, we unlock, and only unlock, to avoid deadlock. */
|
||||
spin_unlock(&dev->lock);
|
||||
error = usb_ep_queue(dev->out_ep, req, GFP_ATOMIC);
|
||||
spin_lock(&dev->lock);
|
||||
if (error) {
|
||||
DBG(dev, "rx submit --> %d\n", error);
|
||||
list_add(&req->list, &dev->rx_reqs);
|
||||
break;
|
||||
} else {
|
||||
}
|
||||
/* if the req is empty, then add it into dev->rx_reqs_active. */
|
||||
else if (list_empty(&req->list)) {
|
||||
list_add(&req->list, &dev->rx_reqs_active);
|
||||
}
|
||||
}
|
||||
@ -1133,6 +1138,7 @@ static int __init printer_bind_config(struct usb_configuration *c)
|
||||
NULL, "g_printer");
|
||||
if (IS_ERR(dev->pdev)) {
|
||||
ERROR(dev, "Failed to create device: g_printer\n");
|
||||
status = PTR_ERR(dev->pdev);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -617,7 +617,7 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
|
||||
to_write = DIV_ROUND_UP(to_write, 4);
|
||||
data = hs_req->req.buf + buf_pos;
|
||||
|
||||
writesl(hsotg->regs + EPFIFO(hs_ep->index), data, to_write);
|
||||
iowrite32_rep(hsotg->regs + EPFIFO(hs_ep->index), data, to_write);
|
||||
|
||||
return (to_write >= can_write) ? -ENOSPC : 0;
|
||||
}
|
||||
@ -720,8 +720,8 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg,
|
||||
ureq->length, ureq->actual);
|
||||
if (0)
|
||||
dev_dbg(hsotg->dev,
|
||||
"REQ buf %p len %d dma 0x%08x noi=%d zp=%d snok=%d\n",
|
||||
ureq->buf, length, ureq->dma,
|
||||
"REQ buf %p len %d dma 0x%pad noi=%d zp=%d snok=%d\n",
|
||||
ureq->buf, length, &ureq->dma,
|
||||
ureq->no_interrupt, ureq->zero, ureq->short_not_ok);
|
||||
|
||||
maxreq = get_ep_limit(hs_ep);
|
||||
@ -789,8 +789,8 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg,
|
||||
dma_reg = dir_in ? DIEPDMA(index) : DOEPDMA(index);
|
||||
writel(ureq->dma, hsotg->regs + dma_reg);
|
||||
|
||||
dev_dbg(hsotg->dev, "%s: 0x%08x => 0x%08x\n",
|
||||
__func__, ureq->dma, dma_reg);
|
||||
dev_dbg(hsotg->dev, "%s: 0x%pad => 0x%08x\n",
|
||||
__func__, &ureq->dma, dma_reg);
|
||||
}
|
||||
|
||||
ctrl |= DxEPCTL_EPEna; /* ensure ep enabled */
|
||||
@ -1185,6 +1185,41 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg,
|
||||
static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg);
|
||||
static void s3c_hsotg_disconnect(struct s3c_hsotg *hsotg);
|
||||
|
||||
/**
|
||||
* s3c_hsotg_stall_ep0 - stall ep0
|
||||
* @hsotg: The device state
|
||||
*
|
||||
* Set stall for ep0 as response for setup request.
|
||||
*/
|
||||
static void s3c_hsotg_stall_ep0(struct s3c_hsotg *hsotg) {
|
||||
struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
|
||||
u32 reg;
|
||||
u32 ctrl;
|
||||
|
||||
dev_dbg(hsotg->dev, "ep0 stall (dir=%d)\n", ep0->dir_in);
|
||||
reg = (ep0->dir_in) ? DIEPCTL0 : DOEPCTL0;
|
||||
|
||||
/*
|
||||
* DxEPCTL_Stall will be cleared by EP once it has
|
||||
* taken effect, so no need to clear later.
|
||||
*/
|
||||
|
||||
ctrl = readl(hsotg->regs + reg);
|
||||
ctrl |= DxEPCTL_Stall;
|
||||
ctrl |= DxEPCTL_CNAK;
|
||||
writel(ctrl, hsotg->regs + reg);
|
||||
|
||||
dev_dbg(hsotg->dev,
|
||||
"written DxEPCTL=0x%08x to %08x (DxEPCTL=0x%08x)\n",
|
||||
ctrl, reg, readl(hsotg->regs + reg));
|
||||
|
||||
/*
|
||||
* complete won't be called, so we enqueue
|
||||
* setup request here
|
||||
*/
|
||||
s3c_hsotg_enqueue_setup(hsotg);
|
||||
}
|
||||
|
||||
/**
|
||||
* s3c_hsotg_process_control - process a control request
|
||||
* @hsotg: The device state
|
||||
@ -1262,38 +1297,8 @@ static void s3c_hsotg_process_control(struct s3c_hsotg *hsotg,
|
||||
* so respond with a STALL for the status stage to indicate failure.
|
||||
*/
|
||||
|
||||
if (ret < 0) {
|
||||
u32 reg;
|
||||
u32 ctrl;
|
||||
|
||||
dev_dbg(hsotg->dev, "ep0 stall (dir=%d)\n", ep0->dir_in);
|
||||
reg = (ep0->dir_in) ? DIEPCTL0 : DOEPCTL0;
|
||||
|
||||
/*
|
||||
* DxEPCTL_Stall will be cleared by EP once it has
|
||||
* taken effect, so no need to clear later.
|
||||
*/
|
||||
|
||||
ctrl = readl(hsotg->regs + reg);
|
||||
ctrl |= DxEPCTL_Stall;
|
||||
ctrl |= DxEPCTL_CNAK;
|
||||
writel(ctrl, hsotg->regs + reg);
|
||||
|
||||
dev_dbg(hsotg->dev,
|
||||
"written DxEPCTL=0x%08x to %08x (DxEPCTL=0x%08x)\n",
|
||||
ctrl, reg, readl(hsotg->regs + reg));
|
||||
|
||||
/*
|
||||
* don't believe we need to anything more to get the EP
|
||||
* to reply with a STALL packet
|
||||
*/
|
||||
|
||||
/*
|
||||
* complete won't be called, so we enqueue
|
||||
* setup request here
|
||||
*/
|
||||
s3c_hsotg_enqueue_setup(hsotg);
|
||||
}
|
||||
if (ret < 0)
|
||||
s3c_hsotg_stall_ep0(hsotg);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1488,7 +1493,7 @@ static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size)
|
||||
* note, we might over-write the buffer end by 3 bytes depending on
|
||||
* alignment of the data.
|
||||
*/
|
||||
readsl(fifo, hs_req->req.buf + read_ptr, to_read);
|
||||
ioread32_rep(fifo, hs_req->req.buf + read_ptr, to_read);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2832,6 +2837,15 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value)
|
||||
|
||||
dev_info(hs->dev, "%s(ep %p %s, %d)\n", __func__, ep, ep->name, value);
|
||||
|
||||
if (index == 0) {
|
||||
if (value)
|
||||
s3c_hsotg_stall_ep0(hs);
|
||||
else
|
||||
dev_warn(hs->dev,
|
||||
"%s: can't clear halt on ep0\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* write both IN and OUT control registers */
|
||||
|
||||
epreg = DIEPCTL(index);
|
||||
@ -3760,10 +3774,55 @@ static int s3c_hsotg_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 1
|
||||
#define s3c_hsotg_suspend NULL
|
||||
#define s3c_hsotg_resume NULL
|
||||
#endif
|
||||
static int s3c_hsotg_suspend(struct platform_device *pdev, pm_message_t state)
|
||||
{
|
||||
struct s3c_hsotg *hsotg = platform_get_drvdata(pdev);
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
if (hsotg->driver)
|
||||
dev_info(hsotg->dev, "suspending usb gadget %s\n",
|
||||
hsotg->driver->driver.name);
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
s3c_hsotg_disconnect(hsotg);
|
||||
s3c_hsotg_phy_disable(hsotg);
|
||||
hsotg->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
|
||||
if (hsotg->driver) {
|
||||
int ep;
|
||||
for (ep = 0; ep < hsotg->num_of_eps; ep++)
|
||||
s3c_hsotg_ep_disable(&hsotg->eps[ep].ep);
|
||||
|
||||
ret = regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies),
|
||||
hsotg->supplies);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int s3c_hsotg_resume(struct platform_device *pdev)
|
||||
{
|
||||
struct s3c_hsotg *hsotg = platform_get_drvdata(pdev);
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
if (hsotg->driver) {
|
||||
dev_info(hsotg->dev, "resuming usb gadget %s\n",
|
||||
hsotg->driver->driver.name);
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies),
|
||||
hsotg->supplies);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
hsotg->last_rst = jiffies;
|
||||
s3c_hsotg_phy_enable(hsotg);
|
||||
s3c_hsotg_core_init(hsotg);
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id s3c_hsotg_of_ids[] = {
|
||||
|
@ -1344,7 +1344,6 @@ static int s3c_hsudc_probe(struct platform_device *pdev)
|
||||
|
||||
return 0;
|
||||
err_add_udc:
|
||||
err_add_device:
|
||||
clk_disable(hsudc->uclk);
|
||||
err_res:
|
||||
if (!IS_ERR_OR_NULL(hsudc->transceiver))
|
||||
|
@ -48,6 +48,8 @@
|
||||
|
||||
#define UETH__VERSION "29-May-2008"
|
||||
|
||||
#define GETHER_NAPI_WEIGHT 32
|
||||
|
||||
struct eth_dev {
|
||||
/* lock is held while accessing port_usb
|
||||
*/
|
||||
@ -72,6 +74,7 @@ struct eth_dev {
|
||||
struct sk_buff_head *list);
|
||||
|
||||
struct work_struct work;
|
||||
struct napi_struct rx_napi;
|
||||
|
||||
unsigned long todo;
|
||||
#define WORK_RX_MEMORY 0
|
||||
@ -253,18 +256,16 @@ enomem:
|
||||
DBG(dev, "rx submit --> %d\n", retval);
|
||||
if (skb)
|
||||
dev_kfree_skb_any(skb);
|
||||
spin_lock_irqsave(&dev->req_lock, flags);
|
||||
list_add(&req->list, &dev->rx_reqs);
|
||||
spin_unlock_irqrestore(&dev->req_lock, flags);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void rx_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct sk_buff *skb = req->context, *skb2;
|
||||
struct sk_buff *skb = req->context;
|
||||
struct eth_dev *dev = ep->driver_data;
|
||||
int status = req->status;
|
||||
bool rx_queue = 0;
|
||||
|
||||
switch (status) {
|
||||
|
||||
@ -288,30 +289,8 @@ static void rx_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
} else {
|
||||
skb_queue_tail(&dev->rx_frames, skb);
|
||||
}
|
||||
skb = NULL;
|
||||
|
||||
skb2 = skb_dequeue(&dev->rx_frames);
|
||||
while (skb2) {
|
||||
if (status < 0
|
||||
|| ETH_HLEN > skb2->len
|
||||
|| skb2->len > VLAN_ETH_FRAME_LEN) {
|
||||
dev->net->stats.rx_errors++;
|
||||
dev->net->stats.rx_length_errors++;
|
||||
DBG(dev, "rx length %d\n", skb2->len);
|
||||
dev_kfree_skb_any(skb2);
|
||||
goto next_frame;
|
||||
}
|
||||
skb2->protocol = eth_type_trans(skb2, dev->net);
|
||||
dev->net->stats.rx_packets++;
|
||||
dev->net->stats.rx_bytes += skb2->len;
|
||||
|
||||
/* no buffer copies needed, unless hardware can't
|
||||
* use skb buffers.
|
||||
*/
|
||||
status = netif_rx(skb2);
|
||||
next_frame:
|
||||
skb2 = skb_dequeue(&dev->rx_frames);
|
||||
}
|
||||
if (!status)
|
||||
rx_queue = 1;
|
||||
break;
|
||||
|
||||
/* software-driven interface shutdown */
|
||||
@ -334,22 +313,20 @@ quiesce:
|
||||
/* FALLTHROUGH */
|
||||
|
||||
default:
|
||||
rx_queue = 1;
|
||||
dev_kfree_skb_any(skb);
|
||||
dev->net->stats.rx_errors++;
|
||||
DBG(dev, "rx status %d\n", status);
|
||||
break;
|
||||
}
|
||||
|
||||
if (skb)
|
||||
dev_kfree_skb_any(skb);
|
||||
if (!netif_running(dev->net)) {
|
||||
clean:
|
||||
spin_lock(&dev->req_lock);
|
||||
list_add(&req->list, &dev->rx_reqs);
|
||||
spin_unlock(&dev->req_lock);
|
||||
req = NULL;
|
||||
}
|
||||
if (req)
|
||||
rx_submit(dev, req, GFP_ATOMIC);
|
||||
|
||||
if (rx_queue && likely(napi_schedule_prep(&dev->rx_napi)))
|
||||
__napi_schedule(&dev->rx_napi);
|
||||
}
|
||||
|
||||
static int prealloc(struct list_head *list, struct usb_ep *ep, unsigned n)
|
||||
@ -414,16 +391,24 @@ static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags)
|
||||
{
|
||||
struct usb_request *req;
|
||||
unsigned long flags;
|
||||
int rx_counts = 0;
|
||||
|
||||
/* fill unused rxq slots with some skb */
|
||||
spin_lock_irqsave(&dev->req_lock, flags);
|
||||
while (!list_empty(&dev->rx_reqs)) {
|
||||
|
||||
if (++rx_counts > qlen(dev->gadget, dev->qmult))
|
||||
break;
|
||||
|
||||
req = container_of(dev->rx_reqs.next,
|
||||
struct usb_request, list);
|
||||
list_del_init(&req->list);
|
||||
spin_unlock_irqrestore(&dev->req_lock, flags);
|
||||
|
||||
if (rx_submit(dev, req, gfp_flags) < 0) {
|
||||
spin_lock_irqsave(&dev->req_lock, flags);
|
||||
list_add(&req->list, &dev->rx_reqs);
|
||||
spin_unlock_irqrestore(&dev->req_lock, flags);
|
||||
defer_kevent(dev, WORK_RX_MEMORY);
|
||||
return;
|
||||
}
|
||||
@ -433,6 +418,41 @@ static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags)
|
||||
spin_unlock_irqrestore(&dev->req_lock, flags);
|
||||
}
|
||||
|
||||
static int gether_poll(struct napi_struct *napi, int budget)
|
||||
{
|
||||
struct eth_dev *dev = container_of(napi, struct eth_dev, rx_napi);
|
||||
struct sk_buff *skb;
|
||||
unsigned int work_done = 0;
|
||||
int status = 0;
|
||||
|
||||
while ((skb = skb_dequeue(&dev->rx_frames))) {
|
||||
if (status < 0
|
||||
|| ETH_HLEN > skb->len
|
||||
|| skb->len > VLAN_ETH_FRAME_LEN) {
|
||||
dev->net->stats.rx_errors++;
|
||||
dev->net->stats.rx_length_errors++;
|
||||
DBG(dev, "rx length %d\n", skb->len);
|
||||
dev_kfree_skb_any(skb);
|
||||
continue;
|
||||
}
|
||||
skb->protocol = eth_type_trans(skb, dev->net);
|
||||
dev->net->stats.rx_packets++;
|
||||
dev->net->stats.rx_bytes += skb->len;
|
||||
|
||||
status = netif_rx_ni(skb);
|
||||
}
|
||||
|
||||
if (netif_running(dev->net)) {
|
||||
rx_fill(dev, GFP_KERNEL);
|
||||
work_done++;
|
||||
}
|
||||
|
||||
if (work_done < budget)
|
||||
napi_complete(&dev->rx_napi);
|
||||
|
||||
return work_done;
|
||||
}
|
||||
|
||||
static void eth_work(struct work_struct *work)
|
||||
{
|
||||
struct eth_dev *dev = container_of(work, struct eth_dev, work);
|
||||
@ -625,6 +645,7 @@ static void eth_start(struct eth_dev *dev, gfp_t gfp_flags)
|
||||
/* and open the tx floodgates */
|
||||
atomic_set(&dev->tx_qlen, 0);
|
||||
netif_wake_queue(dev->net);
|
||||
napi_enable(&dev->rx_napi);
|
||||
}
|
||||
|
||||
static int eth_open(struct net_device *net)
|
||||
@ -651,6 +672,7 @@ static int eth_stop(struct net_device *net)
|
||||
unsigned long flags;
|
||||
|
||||
VDBG(dev, "%s\n", __func__);
|
||||
napi_disable(&dev->rx_napi);
|
||||
netif_stop_queue(net);
|
||||
|
||||
DBG(dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n",
|
||||
@ -768,6 +790,7 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g,
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
dev = netdev_priv(net);
|
||||
netif_napi_add(net, &dev->rx_napi, gether_poll, GETHER_NAPI_WEIGHT);
|
||||
spin_lock_init(&dev->lock);
|
||||
spin_lock_init(&dev->req_lock);
|
||||
INIT_WORK(&dev->work, eth_work);
|
||||
@ -830,6 +853,7 @@ struct net_device *gether_setup_name_default(const char *netname)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
dev = netdev_priv(net);
|
||||
netif_napi_add(net, &dev->rx_napi, gether_poll, GETHER_NAPI_WEIGHT);
|
||||
spin_lock_init(&dev->lock);
|
||||
spin_lock_init(&dev->req_lock);
|
||||
INIT_WORK(&dev->work, eth_work);
|
||||
@ -1113,6 +1137,7 @@ void gether_disconnect(struct gether *link)
|
||||
{
|
||||
struct eth_dev *dev = link->ioport;
|
||||
struct usb_request *req;
|
||||
struct sk_buff *skb;
|
||||
|
||||
WARN_ON(!dev);
|
||||
if (!dev)
|
||||
@ -1139,6 +1164,12 @@ void gether_disconnect(struct gether *link)
|
||||
spin_lock(&dev->req_lock);
|
||||
}
|
||||
spin_unlock(&dev->req_lock);
|
||||
|
||||
spin_lock(&dev->rx_frames.lock);
|
||||
while ((skb = __skb_dequeue(&dev->rx_frames)))
|
||||
dev_kfree_skb_any(skb);
|
||||
spin_unlock(&dev->rx_frames.lock);
|
||||
|
||||
link->in_ep->driver_data = NULL;
|
||||
link->in_ep->desc = NULL;
|
||||
|
||||
|
@ -65,10 +65,8 @@ static inline void ffs_dev_unlock(void)
|
||||
mutex_unlock(&ffs_lock);
|
||||
}
|
||||
|
||||
struct ffs_dev *ffs_alloc_dev(void);
|
||||
int ffs_name_dev(struct ffs_dev *dev, const char *name);
|
||||
int ffs_single_dev(struct ffs_dev *dev);
|
||||
void ffs_free_dev(struct ffs_dev *dev);
|
||||
|
||||
struct ffs_epfile;
|
||||
struct ffs_function;
|
||||
@ -125,7 +123,7 @@ enum ffs_setup_state {
|
||||
* setup. If this state is set read/write on ep0 return
|
||||
* -EIDRM. This state is only set when adding event.
|
||||
*/
|
||||
FFS_SETUP_CANCELED
|
||||
FFS_SETUP_CANCELLED
|
||||
};
|
||||
|
||||
struct ffs_data {
|
||||
@ -156,7 +154,6 @@ struct ffs_data {
|
||||
*/
|
||||
struct usb_request *ep0req; /* P: mutex */
|
||||
struct completion ep0req_completion; /* P: mutex */
|
||||
int ep0req_status; /* P: mutex */
|
||||
|
||||
/* reference counter */
|
||||
atomic_t ref;
|
||||
@ -168,19 +165,18 @@ struct ffs_data {
|
||||
|
||||
/*
|
||||
* Possible transitions:
|
||||
* + FFS_NO_SETUP -> FFS_SETUP_PENDING -- P: ev.waitq.lock
|
||||
* + FFS_NO_SETUP -> FFS_SETUP_PENDING -- P: ev.waitq.lock
|
||||
* happens only in ep0 read which is P: mutex
|
||||
* + FFS_SETUP_PENDING -> FFS_NO_SETUP -- P: ev.waitq.lock
|
||||
* + FFS_SETUP_PENDING -> FFS_NO_SETUP -- P: ev.waitq.lock
|
||||
* happens only in ep0 i/o which is P: mutex
|
||||
* + FFS_SETUP_PENDING -> FFS_SETUP_CANCELED -- P: ev.waitq.lock
|
||||
* + FFS_SETUP_CANCELED -> FFS_NO_SETUP -- cmpxchg
|
||||
* + FFS_SETUP_PENDING -> FFS_SETUP_CANCELLED -- P: ev.waitq.lock
|
||||
* + FFS_SETUP_CANCELLED -> FFS_NO_SETUP -- cmpxchg
|
||||
*
|
||||
* This field should never be accessed directly and instead
|
||||
* ffs_setup_state_clear_cancelled function should be used.
|
||||
*/
|
||||
enum ffs_setup_state setup_state;
|
||||
|
||||
#define FFS_SETUP_STATE(ffs) \
|
||||
((enum ffs_setup_state)cmpxchg(&(ffs)->setup_state, \
|
||||
FFS_SETUP_CANCELED, FFS_NO_SETUP))
|
||||
|
||||
/* Events & such. */
|
||||
struct {
|
||||
u8 types[4];
|
||||
@ -210,16 +206,16 @@ struct ffs_data {
|
||||
|
||||
/* filled by __ffs_data_got_descs() */
|
||||
/*
|
||||
* Real descriptors are 16 bytes after raw_descs (so you need
|
||||
* to skip 16 bytes (ie. ffs->raw_descs + 16) to get to the
|
||||
* first full speed descriptor). raw_descs_length and
|
||||
* raw_fs_descs_length do not have those 16 bytes added.
|
||||
* raw_descs is what you kfree, real_descs points inside of raw_descs,
|
||||
* where full speed, high speed and super speed descriptors start.
|
||||
* real_descs_length is the length of all those descriptors.
|
||||
*/
|
||||
const void *raw_descs_data;
|
||||
const void *raw_descs;
|
||||
unsigned raw_descs_length;
|
||||
unsigned raw_fs_descs_length;
|
||||
unsigned fs_descs_count;
|
||||
unsigned hs_descs_count;
|
||||
unsigned ss_descs_count;
|
||||
|
||||
unsigned short strings_count;
|
||||
unsigned short interfaces_count;
|
||||
|
@ -43,6 +43,7 @@ config USB_MUSB_HOST
|
||||
config USB_MUSB_GADGET
|
||||
bool "Gadget only mode"
|
||||
depends on USB_GADGET=y || USB_GADGET=USB_MUSB_HDRC
|
||||
depends on HAS_DMA
|
||||
help
|
||||
Select this when you want to use MUSB in gadget mode only,
|
||||
thereby the host feature will be regressed.
|
||||
@ -50,6 +51,7 @@ config USB_MUSB_GADGET
|
||||
config USB_MUSB_DUAL_ROLE
|
||||
bool "Dual Role mode"
|
||||
depends on ((USB=y || USB=USB_MUSB_HDRC) && (USB_GADGET=y || USB_GADGET=USB_MUSB_HDRC))
|
||||
depends on HAS_DMA
|
||||
help
|
||||
This is the default mode of working of MUSB controller where
|
||||
both host and gadget features are enabled.
|
||||
|
@ -438,7 +438,6 @@ void musb_hnp_stop(struct musb *musb)
|
||||
static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
|
||||
u8 devctl)
|
||||
{
|
||||
struct usb_otg *otg = musb->xceiv->otg;
|
||||
irqreturn_t handled = IRQ_NONE;
|
||||
|
||||
dev_dbg(musb->controller, "<== DevCtl=%02x, int_usb=0x%x\n", devctl,
|
||||
@ -656,7 +655,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
|
||||
break;
|
||||
case OTG_STATE_B_PERIPHERAL:
|
||||
musb_g_suspend(musb);
|
||||
musb->is_active = otg->gadget->b_hnp_enable;
|
||||
musb->is_active = musb->g.b_hnp_enable;
|
||||
if (musb->is_active) {
|
||||
musb->xceiv->state = OTG_STATE_B_WAIT_ACON;
|
||||
dev_dbg(musb->controller, "HNP: Setting timer for b_ase0_brst\n");
|
||||
@ -672,7 +671,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
|
||||
break;
|
||||
case OTG_STATE_A_HOST:
|
||||
musb->xceiv->state = OTG_STATE_A_SUSPEND;
|
||||
musb->is_active = otg->host->b_hnp_enable;
|
||||
musb->is_active = musb->hcd->self.b_hnp_enable;
|
||||
break;
|
||||
case OTG_STATE_B_HOST:
|
||||
/* Transition to B_PERIPHERAL, see 6.8.2.6 p 44 */
|
||||
|
@ -39,6 +39,7 @@ struct cppi41_dma_channel {
|
||||
u32 transferred;
|
||||
u32 packet_sz;
|
||||
struct list_head tx_check;
|
||||
struct work_struct dma_completion;
|
||||
};
|
||||
|
||||
#define MUSB_DMA_NUM_CHANNELS 15
|
||||
@ -112,6 +113,18 @@ static bool musb_is_tx_fifo_empty(struct musb_hw_ep *hw_ep)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool is_isoc(struct musb_hw_ep *hw_ep, bool in)
|
||||
{
|
||||
if (in && hw_ep->in_qh) {
|
||||
if (hw_ep->in_qh->type == USB_ENDPOINT_XFER_ISOC)
|
||||
return true;
|
||||
} else if (hw_ep->out_qh) {
|
||||
if (hw_ep->out_qh->type == USB_ENDPOINT_XFER_ISOC)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void cppi41_dma_callback(void *private_data);
|
||||
|
||||
static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel)
|
||||
@ -119,7 +132,8 @@ static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel)
|
||||
struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep;
|
||||
struct musb *musb = hw_ep->musb;
|
||||
|
||||
if (!cppi41_channel->prog_len) {
|
||||
if (!cppi41_channel->prog_len ||
|
||||
(cppi41_channel->channel.status == MUSB_DMA_STATUS_FREE)) {
|
||||
|
||||
/* done, complete */
|
||||
cppi41_channel->channel.actual_len =
|
||||
@ -165,6 +179,32 @@ static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel)
|
||||
}
|
||||
}
|
||||
|
||||
static void cppi_trans_done_work(struct work_struct *work)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct cppi41_dma_channel *cppi41_channel =
|
||||
container_of(work, struct cppi41_dma_channel, dma_completion);
|
||||
struct cppi41_dma_controller *controller = cppi41_channel->controller;
|
||||
struct musb *musb = controller->musb;
|
||||
struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep;
|
||||
bool empty;
|
||||
|
||||
if (!cppi41_channel->is_tx && is_isoc(hw_ep, 1)) {
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
cppi41_trans_done(cppi41_channel);
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
} else {
|
||||
empty = musb_is_tx_fifo_empty(hw_ep);
|
||||
if (empty) {
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
cppi41_trans_done(cppi41_channel);
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
} else {
|
||||
schedule_work(&cppi41_channel->dma_completion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static enum hrtimer_restart cppi41_recheck_tx_req(struct hrtimer *timer)
|
||||
{
|
||||
struct cppi41_dma_controller *controller;
|
||||
@ -228,6 +268,14 @@ static void cppi41_dma_callback(void *private_data)
|
||||
transferred < cppi41_channel->packet_sz)
|
||||
cppi41_channel->prog_len = 0;
|
||||
|
||||
if (!cppi41_channel->is_tx) {
|
||||
if (is_isoc(hw_ep, 1))
|
||||
schedule_work(&cppi41_channel->dma_completion);
|
||||
else
|
||||
cppi41_trans_done(cppi41_channel);
|
||||
goto out;
|
||||
}
|
||||
|
||||
empty = musb_is_tx_fifo_empty(hw_ep);
|
||||
if (empty) {
|
||||
cppi41_trans_done(cppi41_channel);
|
||||
@ -264,6 +312,10 @@ static void cppi41_dma_callback(void *private_data)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (is_isoc(hw_ep, 0)) {
|
||||
schedule_work(&cppi41_channel->dma_completion);
|
||||
goto out;
|
||||
}
|
||||
list_add_tail(&cppi41_channel->tx_check,
|
||||
&controller->early_tx_list);
|
||||
if (!hrtimer_active(&controller->early_tx)) {
|
||||
@ -448,12 +500,25 @@ static int cppi41_dma_channel_program(struct dma_channel *channel,
|
||||
dma_addr_t dma_addr, u32 len)
|
||||
{
|
||||
int ret;
|
||||
struct cppi41_dma_channel *cppi41_channel = channel->private_data;
|
||||
int hb_mult = 0;
|
||||
|
||||
BUG_ON(channel->status == MUSB_DMA_STATUS_UNKNOWN ||
|
||||
channel->status == MUSB_DMA_STATUS_BUSY);
|
||||
|
||||
if (is_host_active(cppi41_channel->controller->musb)) {
|
||||
if (cppi41_channel->is_tx)
|
||||
hb_mult = cppi41_channel->hw_ep->out_qh->hb_mult;
|
||||
else
|
||||
hb_mult = cppi41_channel->hw_ep->in_qh->hb_mult;
|
||||
}
|
||||
|
||||
channel->status = MUSB_DMA_STATUS_BUSY;
|
||||
channel->actual_len = 0;
|
||||
|
||||
if (hb_mult)
|
||||
packet_sz = hb_mult * (packet_sz & 0x7FF);
|
||||
|
||||
ret = cppi41_configure_channel(channel, packet_sz, mode, dma_addr, len);
|
||||
if (!ret)
|
||||
channel->status = MUSB_DMA_STATUS_FREE;
|
||||
@ -607,6 +672,8 @@ static int cppi41_dma_controller_start(struct cppi41_dma_controller *controller)
|
||||
cppi41_channel->port_num = port;
|
||||
cppi41_channel->is_tx = is_tx;
|
||||
INIT_LIST_HEAD(&cppi41_channel->tx_check);
|
||||
INIT_WORK(&cppi41_channel->dma_completion,
|
||||
cppi_trans_done_work);
|
||||
|
||||
musb_dma = &cppi41_channel->channel;
|
||||
musb_dma->private_data = cppi41_channel;
|
||||
|
@ -45,6 +45,8 @@
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/usb/of.h>
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#include "musb_core.h"
|
||||
|
||||
static const struct of_device_id musb_dsps_of_match[];
|
||||
@ -136,6 +138,26 @@ struct dsps_glue {
|
||||
unsigned long last_timer; /* last timer data for each instance */
|
||||
|
||||
struct dsps_context context;
|
||||
struct debugfs_regset32 regset;
|
||||
struct dentry *dbgfs_root;
|
||||
};
|
||||
|
||||
static const struct debugfs_reg32 dsps_musb_regs[] = {
|
||||
{ "revision", 0x00 },
|
||||
{ "control", 0x14 },
|
||||
{ "status", 0x18 },
|
||||
{ "eoi", 0x24 },
|
||||
{ "intr0_stat", 0x30 },
|
||||
{ "intr1_stat", 0x34 },
|
||||
{ "intr0_set", 0x38 },
|
||||
{ "intr1_set", 0x3c },
|
||||
{ "txmode", 0x70 },
|
||||
{ "rxmode", 0x74 },
|
||||
{ "autoreq", 0xd0 },
|
||||
{ "srpfixtime", 0xd4 },
|
||||
{ "tdown", 0xd8 },
|
||||
{ "phy_utmi", 0xe0 },
|
||||
{ "mode", 0xe8 },
|
||||
};
|
||||
|
||||
static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout)
|
||||
@ -368,6 +390,30 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dsps_musb_dbg_init(struct musb *musb, struct dsps_glue *glue)
|
||||
{
|
||||
struct dentry *root;
|
||||
struct dentry *file;
|
||||
char buf[128];
|
||||
|
||||
sprintf(buf, "%s.dsps", dev_name(musb->controller));
|
||||
root = debugfs_create_dir(buf, NULL);
|
||||
if (!root)
|
||||
return -ENOMEM;
|
||||
glue->dbgfs_root = root;
|
||||
|
||||
glue->regset.regs = dsps_musb_regs;
|
||||
glue->regset.nregs = ARRAY_SIZE(dsps_musb_regs);
|
||||
glue->regset.base = musb->ctrl_base;
|
||||
|
||||
file = debugfs_create_regset32("regdump", S_IRUGO, root, &glue->regset);
|
||||
if (!file) {
|
||||
debugfs_remove_recursive(root);
|
||||
return -ENOMEM;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dsps_musb_init(struct musb *musb)
|
||||
{
|
||||
struct device *dev = musb->controller;
|
||||
@ -377,6 +423,7 @@ static int dsps_musb_init(struct musb *musb)
|
||||
void __iomem *reg_base;
|
||||
struct resource *r;
|
||||
u32 rev, val;
|
||||
int ret;
|
||||
|
||||
r = platform_get_resource_byname(parent, IORESOURCE_MEM, "control");
|
||||
if (!r)
|
||||
@ -410,6 +457,10 @@ static int dsps_musb_init(struct musb *musb)
|
||||
val &= ~(1 << wrp->otg_disable);
|
||||
dsps_writel(musb->ctrl_base, wrp->phy_utmi, val);
|
||||
|
||||
ret = dsps_musb_dbg_init(musb, glue);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -616,7 +667,7 @@ static int dsps_probe(struct platform_device *pdev)
|
||||
wrp = match->data;
|
||||
|
||||
/* allocate glue */
|
||||
glue = kzalloc(sizeof(*glue), GFP_KERNEL);
|
||||
glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
|
||||
if (!glue) {
|
||||
dev_err(&pdev->dev, "unable to allocate glue memory\n");
|
||||
return -ENOMEM;
|
||||
@ -644,7 +695,6 @@ err3:
|
||||
pm_runtime_put(&pdev->dev);
|
||||
err2:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
kfree(glue);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -657,7 +707,9 @@ static int dsps_remove(struct platform_device *pdev)
|
||||
/* disable usbss clocks */
|
||||
pm_runtime_put(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
kfree(glue);
|
||||
|
||||
debugfs_remove_recursive(glue->dbgfs_root);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1694,7 +1694,8 @@ void musb_host_rx(struct musb *musb, u8 epnum)
|
||||
| MUSB_RXCSR_RXPKTRDY);
|
||||
musb_writew(hw_ep->regs, MUSB_RXCSR, val);
|
||||
|
||||
#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA)
|
||||
#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) || \
|
||||
defined(CONFIG_USB_TI_CPPI41_DMA)
|
||||
if (usb_pipeisoc(pipe)) {
|
||||
struct usb_iso_packet_descriptor *d;
|
||||
|
||||
@ -1707,10 +1708,30 @@ void musb_host_rx(struct musb *musb, u8 epnum)
|
||||
if (d->status != -EILSEQ && d->status != -EOVERFLOW)
|
||||
d->status = 0;
|
||||
|
||||
if (++qh->iso_idx >= urb->number_of_packets)
|
||||
if (++qh->iso_idx >= urb->number_of_packets) {
|
||||
done = true;
|
||||
else
|
||||
} else {
|
||||
#if defined(CONFIG_USB_TI_CPPI41_DMA)
|
||||
struct dma_controller *c;
|
||||
dma_addr_t *buf;
|
||||
u32 length, ret;
|
||||
|
||||
c = musb->dma_controller;
|
||||
buf = (void *)
|
||||
urb->iso_frame_desc[qh->iso_idx].offset
|
||||
+ (u32)urb->transfer_dma;
|
||||
|
||||
length =
|
||||
urb->iso_frame_desc[qh->iso_idx].length;
|
||||
|
||||
val |= MUSB_RXCSR_DMAENAB;
|
||||
musb_writew(hw_ep->regs, MUSB_RXCSR, val);
|
||||
|
||||
ret = c->channel_program(dma, qh->maxpacket,
|
||||
0, (u32) buf, length);
|
||||
#endif
|
||||
done = false;
|
||||
}
|
||||
|
||||
} else {
|
||||
/* done if urb buffer is full or short packet is recd */
|
||||
@ -1750,7 +1771,8 @@ void musb_host_rx(struct musb *musb, u8 epnum)
|
||||
}
|
||||
|
||||
/* we are expecting IN packets */
|
||||
#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA)
|
||||
#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) || \
|
||||
defined(CONFIG_USB_TI_CPPI41_DMA)
|
||||
if (dma) {
|
||||
struct dma_controller *c;
|
||||
u16 rx_count;
|
||||
|
@ -317,10 +317,12 @@ int otg_statemachine(struct otg_fsm *fsm)
|
||||
otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL);
|
||||
break;
|
||||
case OTG_STATE_A_HOST:
|
||||
if ((!fsm->a_bus_req || fsm->a_suspend_req_inf) &&
|
||||
if (fsm->id || fsm->a_bus_drop)
|
||||
otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL);
|
||||
else if ((!fsm->a_bus_req || fsm->a_suspend_req_inf) &&
|
||||
fsm->otg->host->b_hnp_enable)
|
||||
otg_set_state(fsm, OTG_STATE_A_SUSPEND);
|
||||
else if (fsm->id || !fsm->b_conn || fsm->a_bus_drop)
|
||||
else if (!fsm->b_conn)
|
||||
otg_set_state(fsm, OTG_STATE_A_WAIT_BCON);
|
||||
else if (!fsm->a_vbus_vld)
|
||||
otg_set_state(fsm, OTG_STATE_A_VBUS_ERR);
|
||||
@ -346,8 +348,7 @@ int otg_statemachine(struct otg_fsm *fsm)
|
||||
otg_set_state(fsm, OTG_STATE_A_VBUS_ERR);
|
||||
break;
|
||||
case OTG_STATE_A_WAIT_VFALL:
|
||||
if (fsm->a_wait_vfall_tmout || fsm->id || fsm->a_bus_req ||
|
||||
(!fsm->a_sess_vld && !fsm->b_conn))
|
||||
if (fsm->a_wait_vfall_tmout)
|
||||
otg_set_state(fsm, OTG_STATE_A_IDLE);
|
||||
break;
|
||||
case OTG_STATE_A_VBUS_ERR:
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012 Freescale Semiconductor, Inc.
|
||||
* Copyright 2012-2013 Freescale Semiconductor, Inc.
|
||||
* Copyright (C) 2012 Marek Vasut <marex@denx.de>
|
||||
* on behalf of DENX Software Engineering GmbH
|
||||
*
|
||||
@ -20,6 +20,9 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
|
||||
#define DRIVER_NAME "mxs_phy"
|
||||
|
||||
@ -28,18 +31,134 @@
|
||||
#define HW_USBPHY_CTRL_SET 0x34
|
||||
#define HW_USBPHY_CTRL_CLR 0x38
|
||||
|
||||
#define HW_USBPHY_DEBUG_SET 0x54
|
||||
#define HW_USBPHY_DEBUG_CLR 0x58
|
||||
|
||||
#define HW_USBPHY_IP 0x90
|
||||
#define HW_USBPHY_IP_SET 0x94
|
||||
#define HW_USBPHY_IP_CLR 0x98
|
||||
|
||||
#define BM_USBPHY_CTRL_SFTRST BIT(31)
|
||||
#define BM_USBPHY_CTRL_CLKGATE BIT(30)
|
||||
#define BM_USBPHY_CTRL_ENAUTOSET_USBCLKS BIT(26)
|
||||
#define BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE BIT(25)
|
||||
#define BM_USBPHY_CTRL_ENVBUSCHG_WKUP BIT(23)
|
||||
#define BM_USBPHY_CTRL_ENIDCHG_WKUP BIT(22)
|
||||
#define BM_USBPHY_CTRL_ENDPDMCHG_WKUP BIT(21)
|
||||
#define BM_USBPHY_CTRL_ENAUTOCLR_PHY_PWD BIT(20)
|
||||
#define BM_USBPHY_CTRL_ENAUTOCLR_CLKGATE BIT(19)
|
||||
#define BM_USBPHY_CTRL_ENAUTO_PWRON_PLL BIT(18)
|
||||
#define BM_USBPHY_CTRL_ENUTMILEVEL3 BIT(15)
|
||||
#define BM_USBPHY_CTRL_ENUTMILEVEL2 BIT(14)
|
||||
#define BM_USBPHY_CTRL_ENHOSTDISCONDETECT BIT(1)
|
||||
|
||||
#define BM_USBPHY_IP_FIX (BIT(17) | BIT(18))
|
||||
|
||||
#define BM_USBPHY_DEBUG_CLKGATE BIT(30)
|
||||
|
||||
/* Anatop Registers */
|
||||
#define ANADIG_ANA_MISC0 0x150
|
||||
#define ANADIG_ANA_MISC0_SET 0x154
|
||||
#define ANADIG_ANA_MISC0_CLR 0x158
|
||||
|
||||
#define ANADIG_USB1_VBUS_DET_STAT 0x1c0
|
||||
#define ANADIG_USB2_VBUS_DET_STAT 0x220
|
||||
|
||||
#define ANADIG_USB1_LOOPBACK_SET 0x1e4
|
||||
#define ANADIG_USB1_LOOPBACK_CLR 0x1e8
|
||||
#define ANADIG_USB2_LOOPBACK_SET 0x244
|
||||
#define ANADIG_USB2_LOOPBACK_CLR 0x248
|
||||
|
||||
#define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG BIT(12)
|
||||
#define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG_SL BIT(11)
|
||||
|
||||
#define BM_ANADIG_USB1_VBUS_DET_STAT_VBUS_VALID BIT(3)
|
||||
#define BM_ANADIG_USB2_VBUS_DET_STAT_VBUS_VALID BIT(3)
|
||||
|
||||
#define BM_ANADIG_USB1_LOOPBACK_UTMI_DIG_TST1 BIT(2)
|
||||
#define BM_ANADIG_USB1_LOOPBACK_TSTI_TX_EN BIT(5)
|
||||
#define BM_ANADIG_USB2_LOOPBACK_UTMI_DIG_TST1 BIT(2)
|
||||
#define BM_ANADIG_USB2_LOOPBACK_TSTI_TX_EN BIT(5)
|
||||
|
||||
#define to_mxs_phy(p) container_of((p), struct mxs_phy, phy)
|
||||
|
||||
/* Do disconnection between PHY and controller without vbus */
|
||||
#define MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS BIT(0)
|
||||
|
||||
/*
|
||||
* The PHY will be in messy if there is a wakeup after putting
|
||||
* bus to suspend (set portsc.suspendM) but before setting PHY to low
|
||||
* power mode (set portsc.phcd).
|
||||
*/
|
||||
#define MXS_PHY_ABNORMAL_IN_SUSPEND BIT(1)
|
||||
|
||||
/*
|
||||
* The SOF sends too fast after resuming, it will cause disconnection
|
||||
* between host and high speed device.
|
||||
*/
|
||||
#define MXS_PHY_SENDING_SOF_TOO_FAST BIT(2)
|
||||
|
||||
/*
|
||||
* IC has bug fixes logic, they include
|
||||
* MXS_PHY_ABNORMAL_IN_SUSPEND and MXS_PHY_SENDING_SOF_TOO_FAST
|
||||
* which are described at above flags, the RTL will handle it
|
||||
* according to different versions.
|
||||
*/
|
||||
#define MXS_PHY_NEED_IP_FIX BIT(3)
|
||||
|
||||
struct mxs_phy_data {
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
static const struct mxs_phy_data imx23_phy_data = {
|
||||
.flags = MXS_PHY_ABNORMAL_IN_SUSPEND | MXS_PHY_SENDING_SOF_TOO_FAST,
|
||||
};
|
||||
|
||||
static const struct mxs_phy_data imx6q_phy_data = {
|
||||
.flags = MXS_PHY_SENDING_SOF_TOO_FAST |
|
||||
MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
|
||||
MXS_PHY_NEED_IP_FIX,
|
||||
};
|
||||
|
||||
static const struct mxs_phy_data imx6sl_phy_data = {
|
||||
.flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
|
||||
MXS_PHY_NEED_IP_FIX,
|
||||
};
|
||||
|
||||
static const struct of_device_id mxs_phy_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx6sl-usbphy", .data = &imx6sl_phy_data, },
|
||||
{ .compatible = "fsl,imx6q-usbphy", .data = &imx6q_phy_data, },
|
||||
{ .compatible = "fsl,imx23-usbphy", .data = &imx23_phy_data, },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mxs_phy_dt_ids);
|
||||
|
||||
struct mxs_phy {
|
||||
struct usb_phy phy;
|
||||
struct clk *clk;
|
||||
const struct mxs_phy_data *data;
|
||||
struct regmap *regmap_anatop;
|
||||
int port_id;
|
||||
};
|
||||
|
||||
#define to_mxs_phy(p) container_of((p), struct mxs_phy, phy)
|
||||
static inline bool is_imx6q_phy(struct mxs_phy *mxs_phy)
|
||||
{
|
||||
return mxs_phy->data == &imx6q_phy_data;
|
||||
}
|
||||
|
||||
static inline bool is_imx6sl_phy(struct mxs_phy *mxs_phy)
|
||||
{
|
||||
return mxs_phy->data == &imx6sl_phy_data;
|
||||
}
|
||||
|
||||
/*
|
||||
* PHY needs some 32K cycles to switch from 32K clock to
|
||||
* bus (such as AHB/AXI, etc) clock.
|
||||
*/
|
||||
static void mxs_phy_clock_switch_delay(void)
|
||||
{
|
||||
usleep_range(300, 400);
|
||||
}
|
||||
|
||||
static int mxs_phy_hw_init(struct mxs_phy *mxs_phy)
|
||||
{
|
||||
@ -53,19 +172,105 @@ static int mxs_phy_hw_init(struct mxs_phy *mxs_phy)
|
||||
/* Power up the PHY */
|
||||
writel(0, base + HW_USBPHY_PWD);
|
||||
|
||||
/* enable FS/LS device */
|
||||
writel(BM_USBPHY_CTRL_ENUTMILEVEL2 |
|
||||
BM_USBPHY_CTRL_ENUTMILEVEL3,
|
||||
/*
|
||||
* USB PHY Ctrl Setting
|
||||
* - Auto clock/power on
|
||||
* - Enable full/low speed support
|
||||
*/
|
||||
writel(BM_USBPHY_CTRL_ENAUTOSET_USBCLKS |
|
||||
BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE |
|
||||
BM_USBPHY_CTRL_ENAUTOCLR_PHY_PWD |
|
||||
BM_USBPHY_CTRL_ENAUTOCLR_CLKGATE |
|
||||
BM_USBPHY_CTRL_ENAUTO_PWRON_PLL |
|
||||
BM_USBPHY_CTRL_ENUTMILEVEL2 |
|
||||
BM_USBPHY_CTRL_ENUTMILEVEL3,
|
||||
base + HW_USBPHY_CTRL_SET);
|
||||
|
||||
if (mxs_phy->data->flags & MXS_PHY_NEED_IP_FIX)
|
||||
writel(BM_USBPHY_IP_FIX, base + HW_USBPHY_IP_SET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return true if the vbus is there */
|
||||
static bool mxs_phy_get_vbus_status(struct mxs_phy *mxs_phy)
|
||||
{
|
||||
unsigned int vbus_value;
|
||||
|
||||
if (mxs_phy->port_id == 0)
|
||||
regmap_read(mxs_phy->regmap_anatop,
|
||||
ANADIG_USB1_VBUS_DET_STAT,
|
||||
&vbus_value);
|
||||
else if (mxs_phy->port_id == 1)
|
||||
regmap_read(mxs_phy->regmap_anatop,
|
||||
ANADIG_USB2_VBUS_DET_STAT,
|
||||
&vbus_value);
|
||||
|
||||
if (vbus_value & BM_ANADIG_USB1_VBUS_DET_STAT_VBUS_VALID)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static void __mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool disconnect)
|
||||
{
|
||||
void __iomem *base = mxs_phy->phy.io_priv;
|
||||
u32 reg;
|
||||
|
||||
if (disconnect)
|
||||
writel_relaxed(BM_USBPHY_DEBUG_CLKGATE,
|
||||
base + HW_USBPHY_DEBUG_CLR);
|
||||
|
||||
if (mxs_phy->port_id == 0) {
|
||||
reg = disconnect ? ANADIG_USB1_LOOPBACK_SET
|
||||
: ANADIG_USB1_LOOPBACK_CLR;
|
||||
regmap_write(mxs_phy->regmap_anatop, reg,
|
||||
BM_ANADIG_USB1_LOOPBACK_UTMI_DIG_TST1 |
|
||||
BM_ANADIG_USB1_LOOPBACK_TSTI_TX_EN);
|
||||
} else if (mxs_phy->port_id == 1) {
|
||||
reg = disconnect ? ANADIG_USB2_LOOPBACK_SET
|
||||
: ANADIG_USB2_LOOPBACK_CLR;
|
||||
regmap_write(mxs_phy->regmap_anatop, reg,
|
||||
BM_ANADIG_USB2_LOOPBACK_UTMI_DIG_TST1 |
|
||||
BM_ANADIG_USB2_LOOPBACK_TSTI_TX_EN);
|
||||
}
|
||||
|
||||
if (!disconnect)
|
||||
writel_relaxed(BM_USBPHY_DEBUG_CLKGATE,
|
||||
base + HW_USBPHY_DEBUG_SET);
|
||||
|
||||
/* Delay some time, and let Linestate be SE0 for controller */
|
||||
if (disconnect)
|
||||
usleep_range(500, 1000);
|
||||
}
|
||||
|
||||
static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on)
|
||||
{
|
||||
bool vbus_is_on = false;
|
||||
|
||||
/* If the SoCs don't need to disconnect line without vbus, quit */
|
||||
if (!(mxs_phy->data->flags & MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS))
|
||||
return;
|
||||
|
||||
/* If the SoCs don't have anatop, quit */
|
||||
if (!mxs_phy->regmap_anatop)
|
||||
return;
|
||||
|
||||
vbus_is_on = mxs_phy_get_vbus_status(mxs_phy);
|
||||
|
||||
if (on && !vbus_is_on)
|
||||
__mxs_phy_disconnect_line(mxs_phy, true);
|
||||
else
|
||||
__mxs_phy_disconnect_line(mxs_phy, false);
|
||||
|
||||
}
|
||||
|
||||
static int mxs_phy_init(struct usb_phy *phy)
|
||||
{
|
||||
int ret;
|
||||
struct mxs_phy *mxs_phy = to_mxs_phy(phy);
|
||||
|
||||
mxs_phy_clock_switch_delay();
|
||||
ret = clk_prepare_enable(mxs_phy->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -94,6 +299,7 @@ static int mxs_phy_suspend(struct usb_phy *x, int suspend)
|
||||
x->io_priv + HW_USBPHY_CTRL_SET);
|
||||
clk_disable_unprepare(mxs_phy->clk);
|
||||
} else {
|
||||
mxs_phy_clock_switch_delay();
|
||||
ret = clk_prepare_enable(mxs_phy->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -105,11 +311,28 @@ static int mxs_phy_suspend(struct usb_phy *x, int suspend)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxs_phy_set_wakeup(struct usb_phy *x, bool enabled)
|
||||
{
|
||||
struct mxs_phy *mxs_phy = to_mxs_phy(x);
|
||||
u32 value = BM_USBPHY_CTRL_ENVBUSCHG_WKUP |
|
||||
BM_USBPHY_CTRL_ENDPDMCHG_WKUP |
|
||||
BM_USBPHY_CTRL_ENIDCHG_WKUP;
|
||||
if (enabled) {
|
||||
mxs_phy_disconnect_line(mxs_phy, true);
|
||||
writel_relaxed(value, x->io_priv + HW_USBPHY_CTRL_SET);
|
||||
} else {
|
||||
writel_relaxed(value, x->io_priv + HW_USBPHY_CTRL_CLR);
|
||||
mxs_phy_disconnect_line(mxs_phy, false);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxs_phy_on_connect(struct usb_phy *phy,
|
||||
enum usb_device_speed speed)
|
||||
{
|
||||
dev_dbg(phy->dev, "%s speed device has connected\n",
|
||||
(speed == USB_SPEED_HIGH) ? "high" : "non-high");
|
||||
dev_dbg(phy->dev, "%s device has connected\n",
|
||||
(speed == USB_SPEED_HIGH) ? "HS" : "FS/LS");
|
||||
|
||||
if (speed == USB_SPEED_HIGH)
|
||||
writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
|
||||
@ -121,8 +344,8 @@ static int mxs_phy_on_connect(struct usb_phy *phy,
|
||||
static int mxs_phy_on_disconnect(struct usb_phy *phy,
|
||||
enum usb_device_speed speed)
|
||||
{
|
||||
dev_dbg(phy->dev, "%s speed device has disconnected\n",
|
||||
(speed == USB_SPEED_HIGH) ? "high" : "non-high");
|
||||
dev_dbg(phy->dev, "%s device has disconnected\n",
|
||||
(speed == USB_SPEED_HIGH) ? "HS" : "FS/LS");
|
||||
|
||||
if (speed == USB_SPEED_HIGH)
|
||||
writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
|
||||
@ -138,6 +361,9 @@ static int mxs_phy_probe(struct platform_device *pdev)
|
||||
struct clk *clk;
|
||||
struct mxs_phy *mxs_phy;
|
||||
int ret;
|
||||
const struct of_device_id *of_id =
|
||||
of_match_device(mxs_phy_dt_ids, &pdev->dev);
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(&pdev->dev, res);
|
||||
@ -157,6 +383,22 @@ static int mxs_phy_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Some SoCs don't have anatop registers */
|
||||
if (of_get_property(np, "fsl,anatop", NULL)) {
|
||||
mxs_phy->regmap_anatop = syscon_regmap_lookup_by_phandle
|
||||
(np, "fsl,anatop");
|
||||
if (IS_ERR(mxs_phy->regmap_anatop)) {
|
||||
dev_dbg(&pdev->dev,
|
||||
"failed to find regmap for anatop\n");
|
||||
return PTR_ERR(mxs_phy->regmap_anatop);
|
||||
}
|
||||
}
|
||||
|
||||
ret = of_alias_get_id(np, "usbphy");
|
||||
if (ret < 0)
|
||||
dev_dbg(&pdev->dev, "failed to get alias id, errno %d\n", ret);
|
||||
mxs_phy->port_id = ret;
|
||||
|
||||
mxs_phy->phy.io_priv = base;
|
||||
mxs_phy->phy.dev = &pdev->dev;
|
||||
mxs_phy->phy.label = DRIVER_NAME;
|
||||
@ -166,11 +408,15 @@ static int mxs_phy_probe(struct platform_device *pdev)
|
||||
mxs_phy->phy.notify_connect = mxs_phy_on_connect;
|
||||
mxs_phy->phy.notify_disconnect = mxs_phy_on_disconnect;
|
||||
mxs_phy->phy.type = USB_PHY_TYPE_USB2;
|
||||
mxs_phy->phy.set_wakeup = mxs_phy_set_wakeup;
|
||||
|
||||
mxs_phy->clk = clk;
|
||||
mxs_phy->data = of_id->data;
|
||||
|
||||
platform_set_drvdata(pdev, mxs_phy);
|
||||
|
||||
device_set_wakeup_capable(&pdev->dev, true);
|
||||
|
||||
ret = usb_add_phy_dev(&mxs_phy->phy);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -187,11 +433,46 @@ static int mxs_phy_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id mxs_phy_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx23-usbphy", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mxs_phy_dt_ids);
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static void mxs_phy_enable_ldo_in_suspend(struct mxs_phy *mxs_phy, bool on)
|
||||
{
|
||||
unsigned int reg = on ? ANADIG_ANA_MISC0_SET : ANADIG_ANA_MISC0_CLR;
|
||||
|
||||
/* If the SoCs don't have anatop, quit */
|
||||
if (!mxs_phy->regmap_anatop)
|
||||
return;
|
||||
|
||||
if (is_imx6q_phy(mxs_phy))
|
||||
regmap_write(mxs_phy->regmap_anatop, reg,
|
||||
BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG);
|
||||
else if (is_imx6sl_phy(mxs_phy))
|
||||
regmap_write(mxs_phy->regmap_anatop,
|
||||
reg, BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG_SL);
|
||||
}
|
||||
|
||||
static int mxs_phy_system_suspend(struct device *dev)
|
||||
{
|
||||
struct mxs_phy *mxs_phy = dev_get_drvdata(dev);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
mxs_phy_enable_ldo_in_suspend(mxs_phy, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxs_phy_system_resume(struct device *dev)
|
||||
{
|
||||
struct mxs_phy *mxs_phy = dev_get_drvdata(dev);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
mxs_phy_enable_ldo_in_suspend(mxs_phy, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(mxs_phy_pm, mxs_phy_system_suspend,
|
||||
mxs_phy_system_resume);
|
||||
|
||||
static struct platform_driver mxs_phy_driver = {
|
||||
.probe = mxs_phy_probe,
|
||||
@ -200,6 +481,7 @@ static struct platform_driver mxs_phy_driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = mxs_phy_dt_ids,
|
||||
.pm = &mxs_phy_pm,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -177,15 +177,15 @@ static int rcar_gen2_usb_phy_probe(struct platform_device *pdev)
|
||||
struct clk *clk;
|
||||
int retval;
|
||||
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
pdata = dev_get_platdata(dev);
|
||||
if (!pdata) {
|
||||
dev_err(dev, "No platform data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
clk = devm_clk_get(&pdev->dev, "usbhs");
|
||||
clk = devm_clk_get(dev, "usbhs");
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev, "Can't get the clock\n");
|
||||
dev_err(dev, "Can't get the clock\n");
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
|
||||
|
@ -111,6 +111,13 @@ struct usb_phy {
|
||||
int (*set_suspend)(struct usb_phy *x,
|
||||
int suspend);
|
||||
|
||||
/*
|
||||
* Set wakeup enable for PHY, in that case, the PHY can be
|
||||
* woken up from suspend status due to external events,
|
||||
* like vbus change, dp/dm change and id.
|
||||
*/
|
||||
int (*set_wakeup)(struct usb_phy *x, bool enabled);
|
||||
|
||||
/* notify phy connect status change */
|
||||
int (*notify_connect)(struct usb_phy *x,
|
||||
enum usb_device_speed speed);
|
||||
@ -264,6 +271,15 @@ usb_phy_set_suspend(struct usb_phy *x, int suspend)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
usb_phy_set_wakeup(struct usb_phy *x, bool enabled)
|
||||
{
|
||||
if (x && x->set_wakeup)
|
||||
return x->set_wakeup(x, enabled);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
usb_phy_notify_connect(struct usb_phy *x, enum usb_device_speed speed)
|
||||
{
|
||||
|
@ -10,9 +10,15 @@
|
||||
|
||||
enum {
|
||||
FUNCTIONFS_DESCRIPTORS_MAGIC = 1,
|
||||
FUNCTIONFS_STRINGS_MAGIC = 2
|
||||
FUNCTIONFS_STRINGS_MAGIC = 2,
|
||||
FUNCTIONFS_DESCRIPTORS_MAGIC_V2 = 3,
|
||||
};
|
||||
|
||||
enum functionfs_flags {
|
||||
FUNCTIONFS_HAS_FS_DESC = 1,
|
||||
FUNCTIONFS_HAS_HS_DESC = 2,
|
||||
FUNCTIONFS_HAS_SS_DESC = 4,
|
||||
};
|
||||
|
||||
#ifndef __KERNEL__
|
||||
|
||||
@ -28,30 +34,40 @@ struct usb_endpoint_descriptor_no_audio {
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
/*
|
||||
* All numbers must be in little endian order.
|
||||
*/
|
||||
|
||||
struct usb_functionfs_descs_head {
|
||||
__le32 magic;
|
||||
__le32 length;
|
||||
__le32 fs_count;
|
||||
__le32 hs_count;
|
||||
} __attribute__((packed));
|
||||
|
||||
/*
|
||||
* Descriptors format:
|
||||
*
|
||||
* | off | name | type | description |
|
||||
* |-----+-----------+--------------+--------------------------------------|
|
||||
* | 0 | magic | LE32 | FUNCTIONFS_{FS,HS}_DESCRIPTORS_MAGIC |
|
||||
* | 0 | magic | LE32 | FUNCTIONFS_DESCRIPTORS_MAGIC_V2 |
|
||||
* | 4 | length | LE32 | length of the whole data chunk |
|
||||
* | 8 | flags | LE32 | combination of functionfs_flags |
|
||||
* | | fs_count | LE32 | number of full-speed descriptors |
|
||||
* | | hs_count | LE32 | number of high-speed descriptors |
|
||||
* | | ss_count | LE32 | number of super-speed descriptors |
|
||||
* | | fs_descrs | Descriptor[] | list of full-speed descriptors |
|
||||
* | | hs_descrs | Descriptor[] | list of high-speed descriptors |
|
||||
* | | ss_descrs | Descriptor[] | list of super-speed descriptors |
|
||||
*
|
||||
* Depending on which flags are set, various fields may be missing in the
|
||||
* structure. Any flags that are not recognised cause the whole block to be
|
||||
* rejected with -ENOSYS.
|
||||
*
|
||||
* Legacy descriptors format:
|
||||
*
|
||||
* | off | name | type | description |
|
||||
* |-----+-----------+--------------+--------------------------------------|
|
||||
* | 0 | magic | LE32 | FUNCTIONFS_DESCRIPTORS_MAGIC |
|
||||
* | 4 | length | LE32 | length of the whole data chunk |
|
||||
* | 8 | fs_count | LE32 | number of full-speed descriptors |
|
||||
* | 12 | hs_count | LE32 | number of high-speed descriptors |
|
||||
* | 16 | fs_descrs | Descriptor[] | list of full-speed descriptors |
|
||||
* | | hs_descrs | Descriptor[] | list of high-speed descriptors |
|
||||
*
|
||||
* descs are just valid USB descriptors and have the following format:
|
||||
* All numbers must be in little endian order.
|
||||
*
|
||||
* Descriptor[] is an array of valid USB descriptors which have the following
|
||||
* format:
|
||||
*
|
||||
* | off | name | type | description |
|
||||
* |-----+-----------------+------+--------------------------|
|
||||
|
Loading…
Reference in New Issue
Block a user