usb: patches for v4.9 merge window

This time around we have 92 non-merge commits. Most
 of the changes are in drivers/usb/gadget (40.3%)
 with drivers/usb/gadget/function being the most
 active directory (27.2%).
 
 As for UDC drivers, only dwc3 (26.5%) and dwc2
 (12.7%) have really been active.
 
 The most important changes for dwc3 are better
 support for scatterlist and, again, throughput
 improvements. While on dwc2 got some minor stability
 fixes related to soft reset and FIFO usage.
 
 Felipe Tonello has done some good work fixing up our
 f_midi gadget and Tal Shorer has implemented a nice
 API change for our ULPI bus.
 
 Apart from these, we have our usual set of
 non-critical fixes, spelling fixes, build warning
 fixes, etc.
 -----BEGIN PGP SIGNATURE-----
 
 iQI6BAABCAAkBQJX2TpXHRxmZWxpcGUuYmFsYmlAbGludXguaW50ZWwuY29tAAoJ
 EMy+uJnhGpkGxX0QAIOavB96wkAP4msMzCMIKyKX8NBVWEYzLy7Ou6IrPKiGOR28
 CjDi1C5qW7838H4neA6Gfw896rfTiAODhoiOY/RTXI7p2hTUUXHQuJ81Bad75gHD
 744BUMPy37YJnvgHTasYn0GxAvP73YmV+omRxo76poetYZ9eH8dGECvC9q6m+jRU
 XaubWEq1JMvzHvlyO7BIrndGY4ByRbBoG0XPiZF07e5YDkKWQmv56tgAAN7fEkeh
 8HIg8lG2xvgf+w6cDbrQ2c8fp055OvrOq40R2pSXwQgYYKXPJ+vFiNzriQ6Rfxai
 gIYrB+mrKZcY6mi6OhoulGfNxT65VqMqnUfwVbbwlJQbDe5EkV6o/1WYdaBvdO2s
 qTT9A5alabFzbQ8ZtjzsIHtV62LwmZlMWk7gxZlcvLFNjf/P2CMqqnJi30/JlrsE
 iqhwIGRDhMq4QZZbiiEiJEaEn6vh2zseRdmCy3uMFearXKBP/I2177QOTDG7ZMKf
 fZR4ROlv6c5tIpBCOsTV0+7c/fnnnOTHU4+vJiUzU0krkPzaLcL8iMT1tn+uGchX
 4d2XLuT6AbVxQR4N8YF4FwRzB/PbEb+ZWWGu1mOVSd9/dsA43K50zNdc061dgz8K
 q8lau6bmtfUXdbeWa3WMEaAZIuSBmFarJY0tPZV6W7cXUAgKitThRD6fp4E0
 =vTFa
 -----END PGP SIGNATURE-----

Merge tag 'usb-for-v4.9' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next

Felipe writes:

usb: patches for v4.9 merge window

This time around we have 92 non-merge commits. Most
of the changes are in drivers/usb/gadget (40.3%)
with drivers/usb/gadget/function being the most
active directory (27.2%).

As for UDC drivers, only dwc3 (26.5%) and dwc2
(12.7%) have really been active.

The most important changes for dwc3 are better
support for scatterlist and, again, throughput
improvements. While on dwc2 got some minor stability
fixes related to soft reset and FIFO usage.

Felipe Tonello has done some good work fixing up our
f_midi gadget and Tal Shorer has implemented a nice
API change for our ULPI bus.

Apart from these, we have our usual set of
non-critical fixes, spelling fixes, build warning
fixes, etc.
This commit is contained in:
Greg Kroah-Hartman 2016-09-14 20:37:50 +02:00
commit 8152263748
51 changed files with 1198 additions and 499 deletions

View file

@ -12,6 +12,16 @@ Required properties:
- interrupts: Should contain phy interrupt - interrupts: Should contain phy interrupt
- fsl,anatop: phandle for anatop register, it is only for imx6 SoC series - fsl,anatop: phandle for anatop register, it is only for imx6 SoC series
Optional properties:
- fsl,tx-cal-45-dn-ohms: Integer [30-55]. Resistance (in ohms) of switchable
high-speed trimming resistor connected in parallel with the 45 ohm resistor
that terminates the DN output signal. Default: 45
- fsl,tx-cal-45-dp-ohms: Integer [30-55]. Resistance (in ohms) of switchable
high-speed trimming resistor connected in parallel with the 45 ohm resistor
that terminates the DP output signal. Default: 45
- fsl,tx-d-cal: Integer [79-119]. Current trimming value (as a percentage) of
the 17.78mA TX reference current. Default: 100
Example: Example:
usbphy1: usbphy@020c9000 { usbphy1: usbphy@020c9000 {
compatible = "fsl,imx6q-usbphy", "fsl,imx23-usbphy"; compatible = "fsl,imx6q-usbphy", "fsl,imx23-usbphy";

View file

@ -26,7 +26,10 @@ Refer to phy/phy-bindings.txt for generic phy consumer properties
- g-use-dma: enable dma usage in gadget driver. - g-use-dma: enable dma usage in gadget driver.
- g-rx-fifo-size: size of rx fifo size in gadget mode. - g-rx-fifo-size: size of rx fifo size in gadget mode.
- g-np-tx-fifo-size: size of non-periodic tx fifo size in gadget mode. - g-np-tx-fifo-size: size of non-periodic tx fifo size in gadget mode.
- g-tx-fifo-size: size of periodic tx fifo per endpoint (except ep0) in gadget mode.
Deprecated properties:
- g-tx-fifo-size: size of periodic tx fifo per endpoint (except ep0)
in gadget mode.
Example: Example:

View file

@ -0,0 +1,28 @@
Cavium SuperSpeed DWC3 USB SoC controller
Required properties:
- compatible: Should contain "cavium,octeon-7130-usb-uctl"
Required child node:
A child node must exist to represent the core DWC3 IP block. The name of
the node is not important. The content of the node is defined in dwc3.txt.
Example device node:
uctl@1180069000000 {
compatible = "cavium,octeon-7130-usb-uctl";
reg = <0x00011800 0x69000000 0x00000000 0x00000100>;
ranges;
#address-cells = <0x00000002>;
#size-cells = <0x00000002>;
refclk-frequency = <0x05f5e100>;
refclk-type-ss = "dlmc_ref_clk0";
refclk-type-hs = "dlmc_ref_clk0";
power = <0x00000002 0x00000002 0x00000001>;
xhci@1690000000000 {
compatible = "cavium,octeon-7130-xhci", "synopsys,dwc3";
reg = <0x00016900 0x00000000 0x00000010 0x00000000>;
interrupt-parent = <0x00000010>;
interrupts = <0x00000009 0x00000004>;
};
};

View file

@ -13,7 +13,8 @@ Optional properties:
in the array is expected to be a handle to the USB2/HS PHY and 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 the second element is expected to be a handle to the USB3/SS PHY
- phys: from the *Generic PHY* bindings - phys: from the *Generic PHY* bindings
- phy-names: from the *Generic PHY* bindings - phy-names: from the *Generic PHY* bindings; supported names are "usb2-phy"
or "usb3-phy".
- snps,usb3_lpm_capable: determines if platform is USB3 LPM capable - snps,usb3_lpm_capable: determines if platform is USB3 LPM capable
- snps,disable_scramble_quirk: true when SW should disable data scrambling. - snps,disable_scramble_quirk: true when SW should disable data scrambling.
Only really useful for FPGA builds. Only really useful for FPGA builds.
@ -39,6 +40,11 @@ Optional properties:
disabling the suspend signal to the PHY. disabling the suspend signal to the PHY.
- snps,dis_rxdet_inp3_quirk: when set core will disable receiver detection - snps,dis_rxdet_inp3_quirk: when set core will disable receiver detection
in PHY P3 power state. in PHY P3 power state.
- snps,dis-u2-freeclk-exists-quirk: when set, clear the u2_freeclk_exists
in GUSB2PHYCFG, specify that USB2 PHY doesn't provide
a free-running PHY clock.
- snps,dis-del-phy-power-chg-quirk: when set core will change PHY power
from P0 to P1/P2/P3 without delay.
- snps,is-utmi-l1-suspend: true when DWC3 asserts output signal - snps,is-utmi-l1-suspend: true when DWC3 asserts output signal
utmi_l1_suspend_n, false when asserts utmi_sleep_n utmi_l1_suspend_n, false when asserts utmi_sleep_n
- snps,hird-threshold: HIRD threshold - snps,hird-threshold: HIRD threshold

View file

@ -11,6 +11,11 @@ Optional properties:
"peripheral" and "otg". In case this attribute isn't "peripheral" and "otg". In case this attribute isn't
passed via DT, USB DRD controllers should default to passed via DT, USB DRD controllers should default to
OTG. OTG.
- phy_type: tells USB controllers that we want to configure the core to support
a UTMI+ PHY with an 8- or 16-bit interface if UTMI+ is
selected. Valid arguments are "utmi" and "utmi_wide".
In case this isn't passed via DT, USB controllers should
default to HW capability.
- otg-rev: tells usb driver the release number of the OTG and EH supplement - otg-rev: tells usb driver the release number of the OTG and EH supplement
with which the device and its descriptors are compliant, with which the device and its descriptors are compliant,
in binary-coded decimal (i.e. 2.0 is 0200H). This in binary-coded decimal (i.e. 2.0 is 0200H). This
@ -34,6 +39,7 @@ dwc3@4a030000 {
usb-phy = <&usb2_phy>, <&usb3,phy>; usb-phy = <&usb2_phy>, <&usb3,phy>;
maximum-speed = "super-speed"; maximum-speed = "super-speed";
dr_mode = "otg"; dr_mode = "otg";
phy_type = "utmi_wide";
otg-rev = <0x0200>; otg-rev = <0x0200>;
adp-disable; adp-disable;
}; };

View file

@ -9,6 +9,7 @@ Required properties:
- "renesas,usbhs-r8a7793" for r8a7793 (R-Car M2-N) compatible device - "renesas,usbhs-r8a7793" for r8a7793 (R-Car M2-N) compatible device
- "renesas,usbhs-r8a7794" for r8a7794 (R-Car E2) compatible device - "renesas,usbhs-r8a7794" for r8a7794 (R-Car E2) compatible device
- "renesas,usbhs-r8a7795" for r8a7795 (R-Car H3) compatible device - "renesas,usbhs-r8a7795" for r8a7795 (R-Car H3) compatible device
- "renesas,usbhs-r8a7796" for r8a7796 (R-Car M3-W) compatible device
- "renesas,rcar-gen2-usbhs" for R-Car Gen2 compatible device - "renesas,rcar-gen2-usbhs" for R-Car Gen2 compatible device
- "renesas,rcar-gen3-usbhs" for R-Car Gen3 compatible device - "renesas,rcar-gen3-usbhs" for R-Car Gen3 compatible device

View file

@ -0,0 +1,59 @@
Rockchip SuperSpeed DWC3 USB SoC controller
Required properties:
- compatible: should contain "rockchip,rk3399-dwc3" for rk3399 SoC
- clocks: A list of phandle + clock-specifier pairs for the
clocks listed in clock-names
- clock-names: Should contain the following:
"ref_clk" Controller reference clk, have to be 24 MHz
"suspend_clk" Controller suspend clk, have to be 24 MHz or 32 KHz
"bus_clk" Master/Core clock, have to be >= 62.5 MHz for SS
operation and >= 30MHz for HS operation
"grf_clk" Controller grf clk
Required child node:
A child node must exist to represent the core DWC3 IP block. The name of
the node is not important. The content of the node is defined in dwc3.txt.
Phy documentation is provided in the following places:
Documentation/devicetree/bindings/phy/rockchip,dwc3-usb-phy.txt
Example device nodes:
usbdrd3_0: usb@fe800000 {
compatible = "rockchip,rk3399-dwc3";
clocks = <&cru SCLK_USB3OTG0_REF>, <&cru SCLK_USB3OTG0_SUSPEND>,
<&cru ACLK_USB3OTG0>, <&cru ACLK_USB3_GRF>;
clock-names = "ref_clk", "suspend_clk",
"bus_clk", "grf_clk";
#address-cells = <2>;
#size-cells = <2>;
ranges;
status = "disabled";
usbdrd_dwc3_0: dwc3@fe800000 {
compatible = "snps,dwc3";
reg = <0x0 0xfe800000 0x0 0x100000>;
interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>;
dr_mode = "otg";
status = "disabled";
};
};
usbdrd3_1: usb@fe900000 {
compatible = "rockchip,rk3399-dwc3";
clocks = <&cru SCLK_USB3OTG1_REF>, <&cru SCLK_USB3OTG1_SUSPEND>,
<&cru ACLK_USB3OTG1>, <&cru ACLK_USB3_GRF>;
clock-names = "ref_clk", "suspend_clk",
"bus_clk", "grf_clk";
#address-cells = <2>;
#size-cells = <2>;
ranges;
status = "disabled";
usbdrd_dwc3_1: dwc3@fe900000 {
compatible = "snps,dwc3";
reg = <0x0 0xfe900000 0x0 0x100000>;
interrupts = <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH>;
dr_mode = "otg";
status = "disabled";
};
};

View file

@ -21,13 +21,13 @@
int ulpi_read(struct ulpi *ulpi, u8 addr) int ulpi_read(struct ulpi *ulpi, u8 addr)
{ {
return ulpi->ops->read(ulpi->ops, addr); return ulpi->ops->read(ulpi->dev.parent, addr);
} }
EXPORT_SYMBOL_GPL(ulpi_read); EXPORT_SYMBOL_GPL(ulpi_read);
int ulpi_write(struct ulpi *ulpi, u8 addr, u8 val) int ulpi_write(struct ulpi *ulpi, u8 addr, u8 val)
{ {
return ulpi->ops->write(ulpi->ops, addr, val); return ulpi->ops->write(ulpi->dev.parent, addr, val);
} }
EXPORT_SYMBOL_GPL(ulpi_write); EXPORT_SYMBOL_GPL(ulpi_write);
@ -157,6 +157,8 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi)
{ {
int ret; int ret;
ulpi->dev.parent = dev; /* needed early for ops */
/* Test the interface */ /* Test the interface */
ret = ulpi_write(ulpi, ULPI_SCRATCH, 0xaa); ret = ulpi_write(ulpi, ULPI_SCRATCH, 0xaa);
if (ret < 0) if (ret < 0)
@ -175,7 +177,6 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi)
ulpi->id.product = ulpi_read(ulpi, ULPI_PRODUCT_ID_LOW); ulpi->id.product = ulpi_read(ulpi, ULPI_PRODUCT_ID_LOW);
ulpi->id.product |= ulpi_read(ulpi, ULPI_PRODUCT_ID_HIGH) << 8; ulpi->id.product |= ulpi_read(ulpi, ULPI_PRODUCT_ID_HIGH) << 8;
ulpi->dev.parent = dev;
ulpi->dev.bus = &ulpi_bus; ulpi->dev.bus = &ulpi_bus;
ulpi->dev.type = &ulpi_dev_type; ulpi->dev.type = &ulpi_dev_type;
dev_set_name(&ulpi->dev, "%s.ulpi", dev_name(dev)); dev_set_name(&ulpi->dev, "%s.ulpi", dev_name(dev));
@ -202,7 +203,8 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi)
* Allocates and registers a ULPI device and an interface for it. Called from * Allocates and registers a ULPI device and an interface for it. Called from
* the USB controller that provides the ULPI interface. * the USB controller that provides the ULPI interface.
*/ */
struct ulpi *ulpi_register_interface(struct device *dev, struct ulpi_ops *ops) struct ulpi *ulpi_register_interface(struct device *dev,
const struct ulpi_ops *ops)
{ {
struct ulpi *ulpi; struct ulpi *ulpi;
int ret; int ret;
@ -212,7 +214,6 @@ struct ulpi *ulpi_register_interface(struct device *dev, struct ulpi_ops *ops)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
ulpi->ops = ops; ulpi->ops = ops;
ops->dev = dev;
ret = ulpi_register(dev, ulpi); ret = ulpi_register(dev, ulpi);
if (ret) { if (ret) {

View file

@ -238,6 +238,77 @@ int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg)
return ret; return ret;
} }
/**
* dwc2_wait_for_mode() - Waits for the controller mode.
* @hsotg: Programming view of the DWC_otg controller.
* @host_mode: If true, waits for host mode, otherwise device mode.
*/
static void dwc2_wait_for_mode(struct dwc2_hsotg *hsotg,
bool host_mode)
{
ktime_t start;
ktime_t end;
unsigned int timeout = 110;
dev_vdbg(hsotg->dev, "Waiting for %s mode\n",
host_mode ? "host" : "device");
start = ktime_get();
while (1) {
s64 ms;
if (dwc2_is_host_mode(hsotg) == host_mode) {
dev_vdbg(hsotg->dev, "%s mode set\n",
host_mode ? "Host" : "Device");
break;
}
end = ktime_get();
ms = ktime_to_ms(ktime_sub(end, start));
if (ms >= (s64)timeout) {
dev_warn(hsotg->dev, "%s: Couldn't set %s mode\n",
__func__, host_mode ? "host" : "device");
break;
}
usleep_range(1000, 2000);
}
}
/**
* dwc2_iddig_filter_enabled() - Returns true if the IDDIG debounce
* filter is enabled.
*/
static bool dwc2_iddig_filter_enabled(struct dwc2_hsotg *hsotg)
{
u32 gsnpsid;
u32 ghwcfg4;
if (!dwc2_hw_is_otg(hsotg))
return false;
/* Check if core configuration includes the IDDIG filter. */
ghwcfg4 = dwc2_readl(hsotg->regs + GHWCFG4);
if (!(ghwcfg4 & GHWCFG4_IDDIG_FILT_EN))
return false;
/*
* Check if the IDDIG debounce filter is bypassed. Available
* in core version >= 3.10a.
*/
gsnpsid = dwc2_readl(hsotg->regs + GSNPSID);
if (gsnpsid >= DWC2_CORE_REV_3_10a) {
u32 gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
if (gotgctl & GOTGCTL_DBNCE_FLTR_BYPASS)
return false;
}
return true;
}
/* /*
* Do core a soft reset of the core. Be careful with this because it * Do core a soft reset of the core. Be careful with this because it
* resets all the internal state machines of the core. * resets all the internal state machines of the core.
@ -246,9 +317,30 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg)
{ {
u32 greset; u32 greset;
int count = 0; int count = 0;
bool wait_for_host_mode = false;
dev_vdbg(hsotg->dev, "%s()\n", __func__); dev_vdbg(hsotg->dev, "%s()\n", __func__);
/*
* If the current mode is host, either due to the force mode
* bit being set (which persists after core reset) or the
* connector id pin, a core soft reset will temporarily reset
* the mode to device. A delay from the IDDIG debounce filter
* will occur before going back to host mode.
*
* Determine whether we will go back into host mode after a
* reset and account for this delay after the reset.
*/
if (dwc2_iddig_filter_enabled(hsotg)) {
u32 gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
u32 gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
if (!(gotgctl & GOTGCTL_CONID_B) ||
(gusbcfg & GUSBCFG_FORCEHOSTMODE)) {
wait_for_host_mode = true;
}
}
/* Core Soft Reset */ /* Core Soft Reset */
greset = dwc2_readl(hsotg->regs + GRSTCTL); greset = dwc2_readl(hsotg->regs + GRSTCTL);
greset |= GRSTCTL_CSFTRST; greset |= GRSTCTL_CSFTRST;
@ -277,6 +369,9 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg)
} }
} while (!(greset & GRSTCTL_AHBIDLE)); } while (!(greset & GRSTCTL_AHBIDLE));
if (wait_for_host_mode)
dwc2_wait_for_mode(hsotg, true);
return 0; return 0;
} }
@ -300,9 +395,9 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg)
* Checks are done in this function to determine whether doing a force * Checks are done in this function to determine whether doing a force
* would be valid or not. * would be valid or not.
* *
* If a force is done, it requires a 25ms delay to take effect. * If a force is done, it requires a IDDIG debounce filter delay if
* * the filter is configured and enabled. We poll the current mode of
* Returns true if the mode was forced. * the controller to account for this delay.
*/ */
static bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host) static bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
{ {
@ -337,12 +432,18 @@ static bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
gusbcfg |= set; gusbcfg |= set;
dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG); dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
msleep(25); dwc2_wait_for_mode(hsotg, host);
return true; return true;
} }
/* /**
* Clears the force mode bits. * dwc2_clear_force_mode() - Clears the force mode bits.
*
* After clearing the bits, wait up to 100 ms to account for any
* potential IDDIG filter delay. We can't know if we expect this delay
* or not because the value of the connector ID status is affected by
* the force mode. We only need to call this once during probe if
* dr_mode == OTG.
*/ */
static void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg) static void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg)
{ {
@ -353,11 +454,8 @@ static void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg)
gusbcfg &= ~GUSBCFG_FORCEDEVMODE; gusbcfg &= ~GUSBCFG_FORCEDEVMODE;
dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG); dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
/* if (dwc2_iddig_filter_enabled(hsotg))
* NOTE: This long sleep is _very_ important, otherwise the core will usleep_range(100000, 110000);
* not stay in host mode after a connector ID change!
*/
msleep(25);
} }
/* /*
@ -380,12 +478,6 @@ void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg)
__func__, hsotg->dr_mode); __func__, hsotg->dr_mode);
break; break;
} }
/*
* NOTE: This is required for some rockchip soc based
* platforms.
*/
msleep(50);
} }
/* /*

View file

@ -259,13 +259,6 @@ enum dwc2_lx_state {
DWC2_L3, /* Off state */ DWC2_L3, /* Off state */
}; };
/*
* Gadget periodic tx fifo sizes as used by legacy driver
* EP0 is not included
*/
#define DWC2_G_P_LEGACY_TX_FIFO_SIZE {256, 256, 256, 256, 768, 768, 768, \
768, 0, 0, 0, 0, 0, 0, 0}
/* Gadget ep0 states */ /* Gadget ep0 states */
enum dwc2_ep0_state { enum dwc2_ep0_state {
DWC2_EP0_SETUP, DWC2_EP0_SETUP,
@ -890,6 +883,7 @@ struct dwc2_hsotg {
#define DWC2_CORE_REV_2_92a 0x4f54292a #define DWC2_CORE_REV_2_92a 0x4f54292a
#define DWC2_CORE_REV_2_94a 0x4f54294a #define DWC2_CORE_REV_2_94a 0x4f54294a
#define DWC2_CORE_REV_3_00a 0x4f54300a #define DWC2_CORE_REV_3_00a 0x4f54300a
#define DWC2_CORE_REV_3_10a 0x4f54310a
#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) #if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
union dwc2_hcd_internal_flags { union dwc2_hcd_internal_flags {

View file

@ -186,9 +186,10 @@ static void dwc2_hsotg_ctrl_epint(struct dwc2_hsotg *hsotg,
*/ */
static void dwc2_hsotg_init_fifo(struct dwc2_hsotg *hsotg) static void dwc2_hsotg_init_fifo(struct dwc2_hsotg *hsotg)
{ {
unsigned int ep; unsigned int fifo;
unsigned int addr; unsigned int addr;
int timeout; int timeout;
u32 dptxfsizn;
u32 val; u32 val;
/* Reset fifo map if not correctly cleared during previous session */ /* Reset fifo map if not correctly cleared during previous session */
@ -216,16 +217,16 @@ static void dwc2_hsotg_init_fifo(struct dwc2_hsotg *hsotg)
* them to endpoints dynamically according to maxpacket size value of * them to endpoints dynamically according to maxpacket size value of
* given endpoint. * given endpoint.
*/ */
for (ep = 1; ep < MAX_EPS_CHANNELS; ep++) { for (fifo = 1; fifo < MAX_EPS_CHANNELS; fifo++) {
if (!hsotg->g_tx_fifo_sz[ep]) dptxfsizn = dwc2_readl(hsotg->regs + DPTXFSIZN(fifo));
continue;
val = addr;
val |= hsotg->g_tx_fifo_sz[ep] << FIFOSIZE_DEPTH_SHIFT;
WARN_ONCE(addr + hsotg->g_tx_fifo_sz[ep] > hsotg->fifo_mem,
"insufficient fifo memory");
addr += hsotg->g_tx_fifo_sz[ep];
dwc2_writel(val, hsotg->regs + DPTXFSIZN(ep)); val = (dptxfsizn & FIFOSIZE_DEPTH_MASK) | addr;
addr += dptxfsizn >> FIFOSIZE_DEPTH_SHIFT;
if (addr > hsotg->fifo_mem)
break;
dwc2_writel(val, hsotg->regs + DPTXFSIZN(fifo));
} }
/* /*
@ -388,7 +389,8 @@ static int dwc2_hsotg_write_fifo(struct dwc2_hsotg *hsotg,
return -ENOSPC; return -ENOSPC;
} }
} else if (hsotg->dedicated_fifos && hs_ep->index != 0) { } else if (hsotg->dedicated_fifos && hs_ep->index != 0) {
can_write = dwc2_readl(hsotg->regs + DTXFSTS(hs_ep->index)); can_write = dwc2_readl(hsotg->regs +
DTXFSTS(hs_ep->fifo_index));
can_write &= 0xffff; can_write &= 0xffff;
can_write *= 4; can_write *= 4;
@ -2432,7 +2434,7 @@ static void kill_all_requests(struct dwc2_hsotg *hsotg,
if (!hsotg->dedicated_fifos) if (!hsotg->dedicated_fifos)
return; return;
size = (dwc2_readl(hsotg->regs + DTXFSTS(ep->index)) & 0xffff) * 4; size = (dwc2_readl(hsotg->regs + DTXFSTS(ep->fifo_index)) & 0xffff) * 4;
if (size < ep->fifo_size) if (size < ep->fifo_size)
dwc2_hsotg_txfifo_flush(hsotg, ep->fifo_index); dwc2_hsotg_txfifo_flush(hsotg, ep->fifo_index);
} }
@ -3041,22 +3043,11 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep,
break; break;
} }
/* If fifo is already allocated for this ep */
if (hs_ep->fifo_index) {
size = hs_ep->ep.maxpacket * hs_ep->mc;
/* If bigger fifo is required deallocate current one */
if (size > hs_ep->fifo_size) {
hsotg->fifo_map &= ~(1 << hs_ep->fifo_index);
hs_ep->fifo_index = 0;
hs_ep->fifo_size = 0;
}
}
/* /*
* if the hardware has dedicated fifos, we must give each IN EP * if the hardware has dedicated fifos, we must give each IN EP
* a unique tx-fifo even if it is non-periodic. * a unique tx-fifo even if it is non-periodic.
*/ */
if (dir_in && hsotg->dedicated_fifos && !hs_ep->fifo_index) { if (dir_in && hsotg->dedicated_fifos) {
u32 fifo_index = 0; u32 fifo_index = 0;
u32 fifo_size = UINT_MAX; u32 fifo_size = UINT_MAX;
size = hs_ep->ep.maxpacket*hs_ep->mc; size = hs_ep->ep.maxpacket*hs_ep->mc;
@ -3129,10 +3120,6 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep)
spin_lock_irqsave(&hsotg->lock, flags); spin_lock_irqsave(&hsotg->lock, flags);
hsotg->fifo_map &= ~(1<<hs_ep->fifo_index);
hs_ep->fifo_index = 0;
hs_ep->fifo_size = 0;
ctrl = dwc2_readl(hsotg->regs + epctrl_reg); ctrl = dwc2_readl(hsotg->regs + epctrl_reg);
ctrl &= ~DXEPCTL_EPENA; ctrl &= ~DXEPCTL_EPENA;
ctrl &= ~DXEPCTL_USBACTEP; ctrl &= ~DXEPCTL_USBACTEP;
@ -3147,6 +3134,10 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep)
/* terminate all requests with shutdown */ /* terminate all requests with shutdown */
kill_all_requests(hsotg, hs_ep, -ESHUTDOWN); kill_all_requests(hsotg, hs_ep, -ESHUTDOWN);
hsotg->fifo_map &= ~(1 << hs_ep->fifo_index);
hs_ep->fifo_index = 0;
hs_ep->fifo_size = 0;
spin_unlock_irqrestore(&hsotg->lock, flags); spin_unlock_irqrestore(&hsotg->lock, flags);
return 0; return 0;
} }
@ -3475,8 +3466,11 @@ static int dwc2_hsotg_udc_start(struct usb_gadget *gadget,
otg_set_peripheral(hsotg->uphy->otg, &hsotg->gadget); otg_set_peripheral(hsotg->uphy->otg, &hsotg->gadget);
spin_lock_irqsave(&hsotg->lock, flags); spin_lock_irqsave(&hsotg->lock, flags);
dwc2_hsotg_init(hsotg); if (dwc2_hw_is_device(hsotg)) {
dwc2_hsotg_core_init_disconnected(hsotg, false); dwc2_hsotg_init(hsotg);
dwc2_hsotg_core_init_disconnected(hsotg, false);
}
hsotg->enabled = 0; hsotg->enabled = 0;
spin_unlock_irqrestore(&hsotg->lock, flags); spin_unlock_irqrestore(&hsotg->lock, flags);
@ -3813,36 +3807,10 @@ static void dwc2_hsotg_dump(struct dwc2_hsotg *hsotg)
static void dwc2_hsotg_of_probe(struct dwc2_hsotg *hsotg) static void dwc2_hsotg_of_probe(struct dwc2_hsotg *hsotg)
{ {
struct device_node *np = hsotg->dev->of_node; struct device_node *np = hsotg->dev->of_node;
u32 len = 0;
u32 i = 0;
/* Enable dma if requested in device tree */ /* Enable dma if requested in device tree */
hsotg->g_using_dma = of_property_read_bool(np, "g-use-dma"); hsotg->g_using_dma = of_property_read_bool(np, "g-use-dma");
/*
* Register TX periodic fifo size per endpoint.
* EP0 is excluded since it has no fifo configuration.
*/
if (!of_find_property(np, "g-tx-fifo-size", &len))
goto rx_fifo;
len /= sizeof(u32);
/* Read tx fifo sizes other than ep0 */
if (of_property_read_u32_array(np, "g-tx-fifo-size",
&hsotg->g_tx_fifo_sz[1], len))
goto rx_fifo;
/* Add ep0 */
len++;
/* Make remaining TX fifos unavailable */
if (len < MAX_EPS_CHANNELS) {
for (i = len; i < MAX_EPS_CHANNELS; i++)
hsotg->g_tx_fifo_sz[i] = 0;
}
rx_fifo:
/* Register RX fifo size */ /* Register RX fifo size */
of_property_read_u32(np, "g-rx-fifo-size", &hsotg->g_rx_fifo_sz); of_property_read_u32(np, "g-rx-fifo-size", &hsotg->g_rx_fifo_sz);
@ -3864,13 +3832,10 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
struct device *dev = hsotg->dev; struct device *dev = hsotg->dev;
int epnum; int epnum;
int ret; int ret;
int i;
u32 p_tx_fifo[] = DWC2_G_P_LEGACY_TX_FIFO_SIZE;
/* Initialize to legacy fifo configuration values */ /* Initialize to legacy fifo configuration values */
hsotg->g_rx_fifo_sz = 2048; hsotg->g_rx_fifo_sz = 2048;
hsotg->g_np_g_tx_fifo_sz = 1024; hsotg->g_np_g_tx_fifo_sz = 1024;
memcpy(&hsotg->g_tx_fifo_sz[1], p_tx_fifo, sizeof(p_tx_fifo));
/* Device tree specific probe */ /* Device tree specific probe */
dwc2_hsotg_of_probe(hsotg); dwc2_hsotg_of_probe(hsotg);
@ -3888,9 +3853,6 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
dev_dbg(dev, "NonPeriodic TXFIFO size: %d\n", dev_dbg(dev, "NonPeriodic TXFIFO size: %d\n",
hsotg->g_np_g_tx_fifo_sz); hsotg->g_np_g_tx_fifo_sz);
dev_dbg(dev, "RXFIFO size: %d\n", hsotg->g_rx_fifo_sz); dev_dbg(dev, "RXFIFO size: %d\n", hsotg->g_rx_fifo_sz);
for (i = 0; i < MAX_EPS_CHANNELS; i++)
dev_dbg(dev, "Periodic TXFIFO%2d size: %d\n", i,
hsotg->g_tx_fifo_sz[i]);
hsotg->gadget.max_speed = USB_SPEED_HIGH; hsotg->gadget.max_speed = USB_SPEED_HIGH;
hsotg->gadget.ops = &dwc2_hsotg_gadget_ops; hsotg->gadget.ops = &dwc2_hsotg_gadget_ops;

View file

@ -48,6 +48,7 @@
#define GOTGCTL_ASESVLD (1 << 18) #define GOTGCTL_ASESVLD (1 << 18)
#define GOTGCTL_DBNC_SHORT (1 << 17) #define GOTGCTL_DBNC_SHORT (1 << 17)
#define GOTGCTL_CONID_B (1 << 16) #define GOTGCTL_CONID_B (1 << 16)
#define GOTGCTL_DBNCE_FLTR_BYPASS (1 << 15)
#define GOTGCTL_DEVHNPEN (1 << 11) #define GOTGCTL_DEVHNPEN (1 << 11)
#define GOTGCTL_HSTSETHNPEN (1 << 10) #define GOTGCTL_HSTSETHNPEN (1 << 10)
#define GOTGCTL_HNPREQ (1 << 9) #define GOTGCTL_HNPREQ (1 << 9)

View file

@ -49,6 +49,57 @@
#define DWC3_DEFAULT_AUTOSUSPEND_DELAY 5000 /* ms */ #define DWC3_DEFAULT_AUTOSUSPEND_DELAY 5000 /* ms */
/**
* dwc3_get_dr_mode - Validates and sets dr_mode
* @dwc: pointer to our context structure
*/
static int dwc3_get_dr_mode(struct dwc3 *dwc)
{
enum usb_dr_mode mode;
struct device *dev = dwc->dev;
unsigned int hw_mode;
if (dwc->dr_mode == USB_DR_MODE_UNKNOWN)
dwc->dr_mode = USB_DR_MODE_OTG;
mode = dwc->dr_mode;
hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0);
switch (hw_mode) {
case DWC3_GHWPARAMS0_MODE_GADGET:
if (IS_ENABLED(CONFIG_USB_DWC3_HOST)) {
dev_err(dev,
"Controller does not support host mode.\n");
return -EINVAL;
}
mode = USB_DR_MODE_PERIPHERAL;
break;
case DWC3_GHWPARAMS0_MODE_HOST:
if (IS_ENABLED(CONFIG_USB_DWC3_GADGET)) {
dev_err(dev,
"Controller does not support device mode.\n");
return -EINVAL;
}
mode = USB_DR_MODE_HOST;
break;
default:
if (IS_ENABLED(CONFIG_USB_DWC3_HOST))
mode = USB_DR_MODE_HOST;
else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET))
mode = USB_DR_MODE_PERIPHERAL;
}
if (mode != dwc->dr_mode) {
dev_warn(dev,
"Configuration mismatch. dr_mode forced to %s\n",
mode == USB_DR_MODE_HOST ? "host" : "gadget");
dwc->dr_mode = mode;
}
return 0;
}
void dwc3_set_mode(struct dwc3 *dwc, u32 mode) void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
{ {
u32 reg; u32 reg;
@ -448,6 +499,9 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
if (dwc->dis_u3_susphy_quirk) if (dwc->dis_u3_susphy_quirk)
reg &= ~DWC3_GUSB3PIPECTL_SUSPHY; reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
if (dwc->dis_del_phy_power_chg_quirk)
reg &= ~DWC3_GUSB3PIPECTL_DEPOCHANGE;
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
@ -485,6 +539,23 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
break; break;
} }
switch (dwc->hsphy_mode) {
case USBPHY_INTERFACE_MODE_UTMI:
reg &= ~(DWC3_GUSB2PHYCFG_PHYIF_MASK |
DWC3_GUSB2PHYCFG_USBTRDTIM_MASK);
reg |= DWC3_GUSB2PHYCFG_PHYIF(UTMI_PHYIF_8_BIT) |
DWC3_GUSB2PHYCFG_USBTRDTIM(USBTRDTIM_UTMI_8_BIT);
break;
case USBPHY_INTERFACE_MODE_UTMIW:
reg &= ~(DWC3_GUSB2PHYCFG_PHYIF_MASK |
DWC3_GUSB2PHYCFG_USBTRDTIM_MASK);
reg |= DWC3_GUSB2PHYCFG_PHYIF(UTMI_PHYIF_16_BIT) |
DWC3_GUSB2PHYCFG_USBTRDTIM(USBTRDTIM_UTMI_16_BIT);
break;
default:
break;
}
/* /*
* Above 1.94a, it is recommended to set DWC3_GUSB2PHYCFG_SUSPHY to * Above 1.94a, it is recommended to set DWC3_GUSB2PHYCFG_SUSPHY to
* '0' during coreConsultant configuration. So default value will * '0' during coreConsultant configuration. So default value will
@ -500,6 +571,9 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
if (dwc->dis_enblslpm_quirk) if (dwc->dis_enblslpm_quirk)
reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM; reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM;
if (dwc->dis_u2_freeclk_exists_quirk)
reg &= ~DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
return 0; return 0;
@ -666,6 +740,32 @@ static int dwc3_core_init(struct dwc3 *dwc)
goto err4; goto err4;
} }
switch (dwc->dr_mode) {
case USB_DR_MODE_PERIPHERAL:
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
break;
case USB_DR_MODE_HOST:
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
break;
case USB_DR_MODE_OTG:
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
break;
default:
dev_warn(dwc->dev, "Unsupported mode %d\n", dwc->dr_mode);
break;
}
/*
* ENDXFER polling is available on version 3.10a and later of
* the DWC_usb3 controller. It is NOT available in the
* DWC_usb31 controller.
*/
if (!dwc3_is_usb31(dwc) && dwc->revision >= DWC3_REVISION_310A) {
reg = dwc3_readl(dwc->regs, DWC3_GUCTL2);
reg |= DWC3_GUCTL2_RST_ACTBITLATER;
dwc3_writel(dwc->regs, DWC3_GUCTL2, reg);
}
return 0; return 0;
err4: err4:
@ -763,7 +863,6 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
switch (dwc->dr_mode) { switch (dwc->dr_mode) {
case USB_DR_MODE_PERIPHERAL: case USB_DR_MODE_PERIPHERAL:
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
ret = dwc3_gadget_init(dwc); ret = dwc3_gadget_init(dwc);
if (ret) { if (ret) {
if (ret != -EPROBE_DEFER) if (ret != -EPROBE_DEFER)
@ -772,7 +871,6 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
} }
break; break;
case USB_DR_MODE_HOST: case USB_DR_MODE_HOST:
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
ret = dwc3_host_init(dwc); ret = dwc3_host_init(dwc);
if (ret) { if (ret) {
if (ret != -EPROBE_DEFER) if (ret != -EPROBE_DEFER)
@ -781,7 +879,6 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
} }
break; break;
case USB_DR_MODE_OTG: case USB_DR_MODE_OTG:
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
ret = dwc3_host_init(dwc); ret = dwc3_host_init(dwc);
if (ret) { if (ret) {
if (ret != -EPROBE_DEFER) if (ret != -EPROBE_DEFER)
@ -888,6 +985,7 @@ static int dwc3_probe(struct platform_device *pdev)
dwc->maximum_speed = usb_get_maximum_speed(dev); dwc->maximum_speed = usb_get_maximum_speed(dev);
dwc->dr_mode = usb_get_dr_mode(dev); dwc->dr_mode = usb_get_dr_mode(dev);
dwc->hsphy_mode = of_usb_get_phy_mode(dev->of_node);
dwc->has_lpm_erratum = device_property_read_bool(dev, dwc->has_lpm_erratum = device_property_read_bool(dev,
"snps,has-lpm-erratum"); "snps,has-lpm-erratum");
@ -924,6 +1022,10 @@ static int dwc3_probe(struct platform_device *pdev)
"snps,dis_enblslpm_quirk"); "snps,dis_enblslpm_quirk");
dwc->dis_rxdet_inp3_quirk = device_property_read_bool(dev, dwc->dis_rxdet_inp3_quirk = device_property_read_bool(dev,
"snps,dis_rxdet_inp3_quirk"); "snps,dis_rxdet_inp3_quirk");
dwc->dis_u2_freeclk_exists_quirk = device_property_read_bool(dev,
"snps,dis-u2-freeclk-exists-quirk");
dwc->dis_del_phy_power_chg_quirk = device_property_read_bool(dev,
"snps,dis-del-phy-power-chg-quirk");
dwc->tx_de_emphasis_quirk = device_property_read_bool(dev, dwc->tx_de_emphasis_quirk = device_property_read_bool(dev,
"snps,tx_de_emphasis_quirk"); "snps,tx_de_emphasis_quirk");
@ -972,17 +1074,9 @@ static int dwc3_probe(struct platform_device *pdev)
goto err2; goto err2;
} }
if (IS_ENABLED(CONFIG_USB_DWC3_HOST) && ret = dwc3_get_dr_mode(dwc);
(dwc->dr_mode == USB_DR_MODE_OTG || if (ret)
dwc->dr_mode == USB_DR_MODE_UNKNOWN)) goto err3;
dwc->dr_mode = USB_DR_MODE_HOST;
else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET) &&
(dwc->dr_mode == USB_DR_MODE_OTG ||
dwc->dr_mode == USB_DR_MODE_UNKNOWN))
dwc->dr_mode = USB_DR_MODE_PERIPHERAL;
if (dwc->dr_mode == USB_DR_MODE_UNKNOWN)
dwc->dr_mode = USB_DR_MODE_OTG;
ret = dwc3_alloc_scratch_buffers(dwc); ret = dwc3_alloc_scratch_buffers(dwc);
if (ret) if (ret)

View file

@ -109,6 +109,7 @@
#define DWC3_GPRTBIMAP_HS1 0xc184 #define DWC3_GPRTBIMAP_HS1 0xc184
#define DWC3_GPRTBIMAP_FS0 0xc188 #define DWC3_GPRTBIMAP_FS0 0xc188
#define DWC3_GPRTBIMAP_FS1 0xc18c #define DWC3_GPRTBIMAP_FS1 0xc18c
#define DWC3_GUCTL2 0xc19c
#define DWC3_VER_NUMBER 0xc1a0 #define DWC3_VER_NUMBER 0xc1a0
#define DWC3_VER_TYPE 0xc1a4 #define DWC3_VER_TYPE 0xc1a4
@ -199,9 +200,18 @@
/* Global USB2 PHY Configuration Register */ /* Global USB2 PHY Configuration Register */
#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31) #define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31)
#define DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS (1 << 30)
#define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6) #define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6)
#define DWC3_GUSB2PHYCFG_ULPI_UTMI (1 << 4) #define DWC3_GUSB2PHYCFG_ULPI_UTMI (1 << 4)
#define DWC3_GUSB2PHYCFG_ENBLSLPM (1 << 8) #define DWC3_GUSB2PHYCFG_ENBLSLPM (1 << 8)
#define DWC3_GUSB2PHYCFG_PHYIF(n) (n << 3)
#define DWC3_GUSB2PHYCFG_PHYIF_MASK DWC3_GUSB2PHYCFG_PHYIF(1)
#define DWC3_GUSB2PHYCFG_USBTRDTIM(n) (n << 10)
#define DWC3_GUSB2PHYCFG_USBTRDTIM_MASK DWC3_GUSB2PHYCFG_USBTRDTIM(0xf)
#define USBTRDTIM_UTMI_8_BIT 9
#define USBTRDTIM_UTMI_16_BIT 5
#define UTMI_PHYIF_16_BIT 1
#define UTMI_PHYIF_8_BIT 0
/* Global USB2 PHY Vendor Control Register */ /* Global USB2 PHY Vendor Control Register */
#define DWC3_GUSB2PHYACC_NEWREGREQ (1 << 25) #define DWC3_GUSB2PHYACC_NEWREGREQ (1 << 25)
@ -235,7 +245,10 @@
#define DWC3_GEVNTSIZ_SIZE(n) ((n) & 0xffff) #define DWC3_GEVNTSIZ_SIZE(n) ((n) & 0xffff)
/* Global HWPARAMS0 Register */ /* Global HWPARAMS0 Register */
#define DWC3_GHWPARAMS0_USB3_MODE(n) ((n) & 0x3) #define DWC3_GHWPARAMS0_MODE(n) ((n) & 0x3)
#define DWC3_GHWPARAMS0_MODE_GADGET 0
#define DWC3_GHWPARAMS0_MODE_HOST 1
#define DWC3_GHWPARAMS0_MODE_DRD 2
#define DWC3_GHWPARAMS0_MBUS_TYPE(n) (((n) >> 3) & 0x7) #define DWC3_GHWPARAMS0_MBUS_TYPE(n) (((n) >> 3) & 0x7)
#define DWC3_GHWPARAMS0_SBUS_TYPE(n) (((n) >> 6) & 0x3) #define DWC3_GHWPARAMS0_SBUS_TYPE(n) (((n) >> 6) & 0x3)
#define DWC3_GHWPARAMS0_MDWIDTH(n) (((n) >> 8) & 0xff) #define DWC3_GHWPARAMS0_MDWIDTH(n) (((n) >> 8) & 0xff)
@ -279,6 +292,9 @@
#define DWC3_GFLADJ_30MHZ_SDBND_SEL (1 << 7) #define DWC3_GFLADJ_30MHZ_SDBND_SEL (1 << 7)
#define DWC3_GFLADJ_30MHZ_MASK 0x3f #define DWC3_GFLADJ_30MHZ_MASK 0x3f
/* Global User Control Register 2 */
#define DWC3_GUCTL2_RST_ACTBITLATER (1 << 14)
/* Device Configuration Register */ /* Device Configuration Register */
#define DWC3_DCFG_DEVADDR(addr) ((addr) << 3) #define DWC3_DCFG_DEVADDR(addr) ((addr) << 3)
#define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f) #define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f)
@ -685,6 +701,8 @@ struct dwc3_hwparams {
* @request: struct usb_request to be transferred * @request: struct usb_request to be transferred
* @list: a list_head used for request queueing * @list: a list_head used for request queueing
* @dep: struct dwc3_ep owning this request * @dep: struct dwc3_ep owning this request
* @sg: pointer to first incomplete sg
* @num_pending_sgs: counter to pending sgs
* @first_trb_index: index to first trb used by this request * @first_trb_index: index to first trb used by this request
* @epnum: endpoint number to which this request refers * @epnum: endpoint number to which this request refers
* @trb: pointer to struct dwc3_trb * @trb: pointer to struct dwc3_trb
@ -697,7 +715,9 @@ struct dwc3_request {
struct usb_request request; struct usb_request request;
struct list_head list; struct list_head list;
struct dwc3_ep *dep; struct dwc3_ep *dep;
struct scatterlist *sg;
unsigned num_pending_sgs;
u8 first_trb_index; u8 first_trb_index;
u8 epnum; u8 epnum;
struct dwc3_trb *trb; struct dwc3_trb *trb;
@ -743,6 +763,9 @@ struct dwc3_scratchpad_array {
* @maximum_speed: maximum speed requested (mainly for testing purposes) * @maximum_speed: maximum speed requested (mainly for testing purposes)
* @revision: revision register contents * @revision: revision register contents
* @dr_mode: requested mode of operation * @dr_mode: requested mode of operation
* @hsphy_mode: UTMI phy mode, one of following:
* - USBPHY_INTERFACE_MODE_UTMI
* - USBPHY_INTERFACE_MODE_UTMIW
* @usb2_phy: pointer to USB2 PHY * @usb2_phy: pointer to USB2 PHY
* @usb3_phy: pointer to USB3 PHY * @usb3_phy: pointer to USB3 PHY
* @usb2_generic_phy: pointer to USB2 PHY * @usb2_generic_phy: pointer to USB2 PHY
@ -799,6 +822,11 @@ struct dwc3_scratchpad_array {
* @dis_u2_susphy_quirk: set if we disable usb2 suspend phy * @dis_u2_susphy_quirk: set if we disable usb2 suspend phy
* @dis_enblslpm_quirk: set if we clear enblslpm in GUSB2PHYCFG, * @dis_enblslpm_quirk: set if we clear enblslpm in GUSB2PHYCFG,
* disabling the suspend signal to the PHY. * disabling the suspend signal to the PHY.
* @dis_u2_freeclk_exists_quirk : set if we clear u2_freeclk_exists
* in GUSB2PHYCFG, specify that USB2 PHY doesn't
* provide a free-running PHY clock.
* @dis_del_phy_power_chg_quirk: set if we disable delay phy power
* change quirk.
* @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk * @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk
* @tx_de_emphasis: Tx de-emphasis value * @tx_de_emphasis: Tx de-emphasis value
* 0 - -6dB de-emphasis * 0 - -6dB de-emphasis
@ -845,6 +873,7 @@ struct dwc3 {
size_t regs_size; size_t regs_size;
enum usb_dr_mode dr_mode; enum usb_dr_mode dr_mode;
enum usb_phy_interface hsphy_mode;
u32 fladj; u32 fladj;
u32 irq_gadget; u32 irq_gadget;
@ -880,6 +909,8 @@ struct dwc3 {
#define DWC3_REVISION_260A 0x5533260a #define DWC3_REVISION_260A 0x5533260a
#define DWC3_REVISION_270A 0x5533270a #define DWC3_REVISION_270A 0x5533270a
#define DWC3_REVISION_280A 0x5533280a #define DWC3_REVISION_280A 0x5533280a
#define DWC3_REVISION_300A 0x5533300a
#define DWC3_REVISION_310A 0x5533310a
/* /*
* NOTICE: we're using bit 31 as a "is usb 3.1" flag. This is really * NOTICE: we're using bit 31 as a "is usb 3.1" flag. This is really
@ -942,6 +973,8 @@ struct dwc3 {
unsigned dis_u2_susphy_quirk:1; unsigned dis_u2_susphy_quirk:1;
unsigned dis_enblslpm_quirk:1; unsigned dis_enblslpm_quirk:1;
unsigned dis_rxdet_inp3_quirk:1; unsigned dis_rxdet_inp3_quirk:1;
unsigned dis_u2_freeclk_exists_quirk:1;
unsigned dis_del_phy_power_chg_quirk:1;
unsigned tx_de_emphasis_quirk:1; unsigned tx_de_emphasis_quirk:1;
unsigned tx_de_emphasis:2; unsigned tx_de_emphasis:2;

View file

@ -36,36 +36,25 @@ struct dwc3_of_simple {
int num_clocks; int num_clocks;
}; };
static int dwc3_of_simple_probe(struct platform_device *pdev) static int dwc3_of_simple_clk_init(struct dwc3_of_simple *simple, int count)
{ {
struct dwc3_of_simple *simple; struct device *dev = simple->dev;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node; struct device_node *np = dev->of_node;
unsigned int count;
int ret;
int i; int i;
simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL);
if (!simple)
return -ENOMEM;
count = of_clk_get_parent_count(np);
if (!count)
return -ENOENT;
simple->num_clocks = count; simple->num_clocks = count;
if (!count)
return 0;
simple->clks = devm_kcalloc(dev, simple->num_clocks, simple->clks = devm_kcalloc(dev, simple->num_clocks,
sizeof(struct clk *), GFP_KERNEL); sizeof(struct clk *), GFP_KERNEL);
if (!simple->clks) if (!simple->clks)
return -ENOMEM; return -ENOMEM;
platform_set_drvdata(pdev, simple);
simple->dev = dev;
for (i = 0; i < simple->num_clocks; i++) { for (i = 0; i < simple->num_clocks; i++) {
struct clk *clk; struct clk *clk;
int ret;
clk = of_clk_get(np, i); clk = of_clk_get(np, i);
if (IS_ERR(clk)) { if (IS_ERR(clk)) {
@ -88,6 +77,29 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
simple->clks[i] = clk; simple->clks[i] = clk;
} }
return 0;
}
static int dwc3_of_simple_probe(struct platform_device *pdev)
{
struct dwc3_of_simple *simple;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
int ret;
int i;
simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL);
if (!simple)
return -ENOMEM;
platform_set_drvdata(pdev, simple);
simple->dev = dev;
ret = dwc3_of_simple_clk_init(simple, of_clk_get_parent_count(np));
if (ret)
return ret;
ret = of_platform_populate(np, NULL, NULL, dev); ret = of_platform_populate(np, NULL, NULL, dev);
if (ret) { if (ret) {
for (i = 0; i < simple->num_clocks; i++) { for (i = 0; i < simple->num_clocks; i++) {
@ -112,7 +124,7 @@ static int dwc3_of_simple_remove(struct platform_device *pdev)
int i; int i;
for (i = 0; i < simple->num_clocks; i++) { for (i = 0; i < simple->num_clocks; i++) {
clk_unprepare(simple->clks[i]); clk_disable_unprepare(simple->clks[i]);
clk_put(simple->clks[i]); clk_put(simple->clks[i]);
} }
@ -162,7 +174,9 @@ static const struct dev_pm_ops dwc3_of_simple_dev_pm_ops = {
static const struct of_device_id of_dwc3_simple_match[] = { static const struct of_device_id of_dwc3_simple_match[] = {
{ .compatible = "qcom,dwc3" }, { .compatible = "qcom,dwc3" },
{ .compatible = "rockchip,rk3399-dwc3" },
{ .compatible = "xlnx,zynqmp-dwc3" }, { .compatible = "xlnx,zynqmp-dwc3" },
{ .compatible = "cavium,octeon-7130-usb-uctl" },
{ /* Sentinel */ } { /* Sentinel */ }
}; };
MODULE_DEVICE_TABLE(of, of_dwc3_simple_match); MODULE_DEVICE_TABLE(of, of_dwc3_simple_match);

View file

@ -174,15 +174,8 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
int status) int status)
{ {
struct dwc3 *dwc = dep->dwc; struct dwc3 *dwc = dep->dwc;
int i;
if (req->started) { req->started = false;
i = 0;
do {
dwc3_ep_inc_deq(dep);
} while(++i < req->request.num_mapped_sgs);
req->started = false;
}
list_del(&req->list); list_del(&req->list);
req->trb = NULL; req->trb = NULL;
@ -348,7 +341,8 @@ static int dwc3_send_clear_stall_ep_cmd(struct dwc3_ep *dep)
* IN transfers due to a mishandled error condition. Synopsys * IN transfers due to a mishandled error condition. Synopsys
* STAR 9000614252. * STAR 9000614252.
*/ */
if (dep->direction && (dwc->revision >= DWC3_REVISION_260A)) if (dep->direction && (dwc->revision >= DWC3_REVISION_260A) &&
(dwc->gadget.speed >= USB_SPEED_SUPER))
cmd |= DWC3_DEPCMD_CLEARPENDIN; cmd |= DWC3_DEPCMD_CLEARPENDIN;
memset(&params, 0, sizeof(params)); memset(&params, 0, sizeof(params));
@ -490,7 +484,8 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
params.param0 |= DWC3_DEPCFG_ACTION_INIT; params.param0 |= DWC3_DEPCFG_ACTION_INIT;
} }
params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN; if (usb_endpoint_xfer_control(desc))
params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN;
if (dep->number <= 1 || usb_endpoint_xfer_isoc(desc)) if (dep->number <= 1 || usb_endpoint_xfer_isoc(desc))
params.param1 |= DWC3_DEPCFG_XFER_NOT_READY_EN; params.param1 |= DWC3_DEPCFG_XFER_NOT_READY_EN;
@ -764,6 +759,8 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep,
kfree(req); kfree(req);
} }
static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep);
/** /**
* dwc3_prepare_one_trb - setup one TRB from one request * dwc3_prepare_one_trb - setup one TRB from one request
* @dep: endpoint for which this request is prepared * @dep: endpoint for which this request is prepared
@ -771,15 +768,13 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep,
*/ */
static void dwc3_prepare_one_trb(struct dwc3_ep *dep, static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
struct dwc3_request *req, dma_addr_t dma, struct dwc3_request *req, dma_addr_t dma,
unsigned length, unsigned last, unsigned chain, unsigned node) unsigned length, unsigned chain, unsigned node)
{ {
struct dwc3_trb *trb; struct dwc3_trb *trb;
dwc3_trace(trace_dwc3_gadget, "%s: req %p dma %08llx length %d%s%s", dwc3_trace(trace_dwc3_gadget, "%s: req %p dma %08llx length %d%s",
dep->name, req, (unsigned long long) dma, dep->name, req, (unsigned long long) dma,
length, last ? " last" : "", length, chain ? " chain" : "");
chain ? " chain" : "");
trb = &dep->trb_pool[dep->trb_enqueue]; trb = &dep->trb_pool[dep->trb_enqueue];
@ -826,12 +821,10 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
/* always enable Continue on Short Packet */ /* always enable Continue on Short Packet */
trb->ctrl |= DWC3_TRB_CTRL_CSP; trb->ctrl |= DWC3_TRB_CTRL_CSP;
if (!req->request.no_interrupt && !chain) if ((!req->request.no_interrupt && !chain) ||
(dwc3_calc_trbs_left(dep) == 0))
trb->ctrl |= DWC3_TRB_CTRL_IOC | DWC3_TRB_CTRL_ISP_IMI; trb->ctrl |= DWC3_TRB_CTRL_IOC | DWC3_TRB_CTRL_ISP_IMI;
if (last && !usb_endpoint_xfer_isoc(dep->endpoint.desc))
trb->ctrl |= DWC3_TRB_CTRL_LST;
if (chain) if (chain)
trb->ctrl |= DWC3_TRB_CTRL_CHN; trb->ctrl |= DWC3_TRB_CTRL_CHN;
@ -856,12 +849,12 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
*/ */
static struct dwc3_trb *dwc3_ep_prev_trb(struct dwc3_ep *dep, u8 index) static struct dwc3_trb *dwc3_ep_prev_trb(struct dwc3_ep *dep, u8 index)
{ {
if (!index) u8 tmp = index;
index = DWC3_TRB_NUM - 2;
else
index = dep->trb_enqueue - 1;
return &dep->trb_pool[index]; if (!tmp)
tmp = DWC3_TRB_NUM - 1;
return &dep->trb_pool[tmp - 1];
} }
static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep) static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
@ -894,65 +887,42 @@ static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
} }
static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
struct dwc3_request *req, unsigned int trbs_left, struct dwc3_request *req)
unsigned int more_coming)
{ {
struct usb_request *request = &req->request; struct scatterlist *sg = req->sg;
struct scatterlist *sg = request->sg;
struct scatterlist *s; struct scatterlist *s;
unsigned int last = false;
unsigned int length; unsigned int length;
dma_addr_t dma; dma_addr_t dma;
int i; int i;
for_each_sg(sg, s, request->num_mapped_sgs, i) { for_each_sg(sg, s, req->num_pending_sgs, i) {
unsigned chain = true; unsigned chain = true;
length = sg_dma_len(s); length = sg_dma_len(s);
dma = sg_dma_address(s); dma = sg_dma_address(s);
if (sg_is_last(s)) { if (sg_is_last(s))
if (usb_endpoint_xfer_int(dep->endpoint.desc) ||
!more_coming)
last = true;
chain = false;
}
if (!trbs_left--)
last = true;
if (last)
chain = false; chain = false;
dwc3_prepare_one_trb(dep, req, dma, length, dwc3_prepare_one_trb(dep, req, dma, length,
last, chain, i); chain, i);
if (last) if (!dwc3_calc_trbs_left(dep))
break; break;
} }
} }
static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep, static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
struct dwc3_request *req, unsigned int trbs_left, struct dwc3_request *req)
unsigned int more_coming)
{ {
unsigned int last = false;
unsigned int length; unsigned int length;
dma_addr_t dma; dma_addr_t dma;
dma = req->request.dma; dma = req->request.dma;
length = req->request.length; length = req->request.length;
if (!trbs_left)
last = true;
/* Is this the last request? */
if (usb_endpoint_xfer_int(dep->endpoint.desc) || !more_coming)
last = true;
dwc3_prepare_one_trb(dep, req, dma, length, dwc3_prepare_one_trb(dep, req, dma, length,
last, false, 0); false, 0);
} }
/* /*
@ -966,26 +936,19 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
static void dwc3_prepare_trbs(struct dwc3_ep *dep) static void dwc3_prepare_trbs(struct dwc3_ep *dep)
{ {
struct dwc3_request *req, *n; struct dwc3_request *req, *n;
unsigned int more_coming;
u32 trbs_left;
BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM); BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM);
trbs_left = dwc3_calc_trbs_left(dep); if (!dwc3_calc_trbs_left(dep))
if (!trbs_left)
return; return;
more_coming = dep->allocated_requests - dep->queued_requests;
list_for_each_entry_safe(req, n, &dep->pending_list, list) { list_for_each_entry_safe(req, n, &dep->pending_list, list) {
if (req->request.num_mapped_sgs > 0) if (req->num_pending_sgs > 0)
dwc3_prepare_one_trb_sg(dep, req, trbs_left--, dwc3_prepare_one_trb_sg(dep, req);
more_coming);
else else
dwc3_prepare_one_trb_linear(dep, req, trbs_left--, dwc3_prepare_one_trb_linear(dep, req);
more_coming);
if (!trbs_left) if (!dwc3_calc_trbs_left(dep))
return; return;
} }
} }
@ -1101,93 +1064,29 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
trace_dwc3_ep_queue(req); trace_dwc3_ep_queue(req);
/*
* We only add to our list of requests now and
* start consuming the list once we get XferNotReady
* IRQ.
*
* That way, we avoid doing anything that we don't need
* to do now and defer it until the point we receive a
* particular token from the Host side.
*
* This will also avoid Host cancelling URBs due to too
* many NAKs.
*/
ret = usb_gadget_map_request(&dwc->gadget, &req->request, ret = usb_gadget_map_request(&dwc->gadget, &req->request,
dep->direction); dep->direction);
if (ret) if (ret)
return ret; return ret;
req->sg = req->request.sg;
req->num_pending_sgs = req->request.num_mapped_sgs;
list_add_tail(&req->list, &dep->pending_list); list_add_tail(&req->list, &dep->pending_list);
/*
* If there are no pending requests and the endpoint isn't already
* busy, we will just start the request straight away.
*
* This will save one IRQ (XFER_NOT_READY) and possibly make it a
* little bit faster.
*/
if (!usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
!usb_endpoint_xfer_int(dep->endpoint.desc)) {
ret = __dwc3_gadget_kick_transfer(dep, 0);
goto out;
}
/*
* There are a few special cases:
*
* 1. XferNotReady with empty list of requests. We need to kick the
* transfer here in that situation, otherwise we will be NAKing
* forever. If we get XferNotReady before gadget driver has a
* chance to queue a request, we will ACK the IRQ but won't be
* able to receive the data until the next request is queued.
* The following code is handling exactly that.
*
*/
if (dep->flags & DWC3_EP_PENDING_REQUEST) {
/*
* If xfernotready is already elapsed and it is a case
* of isoc transfer, then issue END TRANSFER, so that
* you can receive xfernotready again and can have
* notion of current microframe.
*/
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
if (list_empty(&dep->started_list)) {
dwc3_stop_active_transfer(dwc, dep->number, true);
dep->flags = DWC3_EP_ENABLED;
}
return 0;
}
ret = __dwc3_gadget_kick_transfer(dep, 0);
if (!ret)
dep->flags &= ~DWC3_EP_PENDING_REQUEST;
goto out;
}
/*
* 2. XferInProgress on Isoc EP with an active transfer. We need to
* kick the transfer here after queuing a request, otherwise the
* core may not see the modified TRB(s).
*/
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
(dep->flags & DWC3_EP_BUSY) && dep->flags & DWC3_EP_PENDING_REQUEST) {
!(dep->flags & DWC3_EP_MISSED_ISOC)) { if (list_empty(&dep->started_list)) {
WARN_ON_ONCE(!dep->resource_index); dwc3_stop_active_transfer(dwc, dep->number, true);
ret = __dwc3_gadget_kick_transfer(dep, dep->resource_index); dep->flags = DWC3_EP_ENABLED;
goto out; }
return 0;
} }
/* if (!dwc3_calc_trbs_left(dep))
* 4. Stream Capable Bulk Endpoints. We need to start the transfer return 0;
* right away, otherwise host will not know we have streams to be
* handled.
*/
if (dep->stream_capable)
ret = __dwc3_gadget_kick_transfer(dep, 0);
out: ret = __dwc3_gadget_kick_transfer(dep, 0);
if (ret && ret != -EBUSY) if (ret && ret != -EBUSY)
dwc3_trace(trace_dwc3_gadget, dwc3_trace(trace_dwc3_gadget,
"%s: failed to kick transfers", "%s: failed to kick transfers",
@ -1963,6 +1862,7 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
unsigned int trb_status; unsigned int trb_status;
dep->queued_requests--; dep->queued_requests--;
dwc3_ep_inc_deq(dep);
trace_dwc3_complete_trb(dep, trb); trace_dwc3_complete_trb(dep, trb);
/* /*
@ -1982,6 +1882,7 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
return 1; return 1;
count = trb->size & DWC3_TRB_SIZE_MASK; count = trb->size & DWC3_TRB_SIZE_MASK;
req->request.actual += count;
if (dep->direction) { if (dep->direction) {
if (count) { if (count) {
@ -2021,48 +1922,51 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
if (s_pkt && !chain) if (s_pkt && !chain)
return 1; return 1;
if ((event->status & DEPEVT_STATUS_LST) &&
(trb->ctrl & (DWC3_TRB_CTRL_LST |
DWC3_TRB_CTRL_HWO)))
return 1;
if ((event->status & DEPEVT_STATUS_IOC) && if ((event->status & DEPEVT_STATUS_IOC) &&
(trb->ctrl & DWC3_TRB_CTRL_IOC)) (trb->ctrl & DWC3_TRB_CTRL_IOC))
return 1; return 1;
return 0; return 0;
} }
static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
const struct dwc3_event_depevt *event, int status) const struct dwc3_event_depevt *event, int status)
{ {
struct dwc3_request *req; struct dwc3_request *req, *n;
struct dwc3_trb *trb; struct dwc3_trb *trb;
unsigned int slot; bool ioc = false;
unsigned int i;
int count = 0;
int ret; int ret;
do { list_for_each_entry_safe(req, n, &dep->started_list, list) {
unsigned length;
unsigned actual;
int chain; int chain;
req = next_request(&dep->started_list); length = req->request.length;
if (WARN_ON_ONCE(!req)) chain = req->num_pending_sgs > 0;
return 1; if (chain) {
struct scatterlist *sg = req->sg;
struct scatterlist *s;
unsigned int pending = req->num_pending_sgs;
unsigned int i;
chain = req->request.num_mapped_sgs > 0; for_each_sg(sg, s, pending, i) {
i = 0; trb = &dep->trb_pool[dep->trb_dequeue];
do {
slot = req->first_trb_index + i;
if (slot == DWC3_TRB_NUM - 1)
slot++;
slot %= DWC3_TRB_NUM;
trb = &dep->trb_pool[slot];
count += trb->size & DWC3_TRB_SIZE_MASK;
req->sg = sg_next(s);
req->num_pending_sgs--;
ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
event, status, chain);
if (ret)
break;
}
} else {
trb = &dep->trb_pool[dep->trb_dequeue];
ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb, ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
event, status, chain); event, status, chain);
if (ret) }
break;
} while (++i < req->request.num_mapped_sgs);
/* /*
* We assume here we will always receive the entire data block * We assume here we will always receive the entire data block
@ -2071,12 +1975,21 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
* should receive and we simply bounce the request back to the * should receive and we simply bounce the request back to the
* gadget driver for further processing. * gadget driver for further processing.
*/ */
req->request.actual += req->request.length - count; actual = length - req->request.actual;
req->request.actual = actual;
if (ret && chain && (actual < length) && req->num_pending_sgs)
return __dwc3_gadget_kick_transfer(dep, 0);
dwc3_gadget_giveback(dep, req, status); dwc3_gadget_giveback(dep, req, status);
if (ret) if (ret) {
if ((event->status & DEPEVT_STATUS_IOC) &&
(trb->ctrl & DWC3_TRB_CTRL_IOC))
ioc = true;
break; break;
} while (1); }
}
/* /*
* Our endpoint might get disabled by another thread during * Our endpoint might get disabled by another thread during
@ -2103,10 +2016,9 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
return 1; return 1;
} }
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && ioc)
if ((event->status & DEPEVT_STATUS_IOC) && return 0;
(trb->ctrl & DWC3_TRB_CTRL_IOC))
return 0;
return 1; return 1;
} }
@ -2322,6 +2234,18 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force)
* *
* - Issue EndTransfer WITH CMDIOC bit set * - Issue EndTransfer WITH CMDIOC bit set
* - Wait 100us * - Wait 100us
*
* As of IP version 3.10a of the DWC_usb3 IP, the controller
* supports a mode to work around the above limitation. The
* software can poll the CMDACT bit in the DEPCMD register
* after issuing a EndTransfer command. This mode is enabled
* by writing GUCTL2[14]. This polling is already done in the
* dwc3_send_gadget_ep_cmd() function so if the mode is
* enabled, the EndTransfer command will have completed upon
* returning from this function and we don't need to delay for
* 100us.
*
* This mode is NOT available on the DWC_usb31 IP.
*/ */
cmd = DWC3_DEPCMD_ENDTRANSFER; cmd = DWC3_DEPCMD_ENDTRANSFER;
@ -2333,7 +2257,9 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force)
WARN_ON_ONCE(ret); WARN_ON_ONCE(ret);
dep->resource_index = 0; dep->resource_index = 0;
dep->flags &= ~DWC3_EP_BUSY; dep->flags &= ~DWC3_EP_BUSY;
udelay(100);
if (dwc3_is_usb31(dwc) || dwc->revision < DWC3_REVISION_310A)
udelay(100);
} }
static void dwc3_stop_active_transfers(struct dwc3 *dwc) static void dwc3_stop_active_transfers(struct dwc3 *dwc)

View file

@ -35,9 +35,9 @@ static int dwc3_ulpi_busyloop(struct dwc3 *dwc)
return -ETIMEDOUT; return -ETIMEDOUT;
} }
static int dwc3_ulpi_read(struct ulpi_ops *ops, u8 addr) static int dwc3_ulpi_read(struct device *dev, u8 addr)
{ {
struct dwc3 *dwc = dev_get_drvdata(ops->dev); struct dwc3 *dwc = dev_get_drvdata(dev);
u32 reg; u32 reg;
int ret; int ret;
@ -53,9 +53,9 @@ static int dwc3_ulpi_read(struct ulpi_ops *ops, u8 addr)
return DWC3_GUSB2PHYACC_DATA(reg); return DWC3_GUSB2PHYACC_DATA(reg);
} }
static int dwc3_ulpi_write(struct ulpi_ops *ops, u8 addr, u8 val) static int dwc3_ulpi_write(struct device *dev, u8 addr, u8 val)
{ {
struct dwc3 *dwc = dev_get_drvdata(ops->dev); struct dwc3 *dwc = dev_get_drvdata(dev);
u32 reg; u32 reg;
reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr); reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr);
@ -65,7 +65,7 @@ static int dwc3_ulpi_write(struct ulpi_ops *ops, u8 addr, u8 val)
return dwc3_ulpi_busyloop(dwc); return dwc3_ulpi_busyloop(dwc);
} }
static struct ulpi_ops dwc3_ulpi_ops = { static const struct ulpi_ops dwc3_ulpi_ops = {
.read = dwc3_ulpi_read, .read = dwc3_ulpi_read,
.write = dwc3_ulpi_write, .write = dwc3_ulpi_write,
}; };

View file

@ -209,25 +209,6 @@ config USB_F_PRINTER
config USB_F_TCM config USB_F_TCM
tristate tristate
choice
tristate "USB Gadget Drivers"
default USB_ETH
help
A Linux "Gadget Driver" talks to the USB Peripheral Controller
driver through the abstract "gadget" API. Some other operating
systems call these "client" drivers, of which "class drivers"
are a subset (implementing a USB device class specification).
A gadget driver implements one or more USB functions using
the peripheral hardware.
Gadget drivers are hardware-neutral, or "platform independent",
except that they sometimes must understand quirks or limitations
of the particular controllers they work with. For example, when
a controller doesn't support alternate configurations or provide
enough of the right types of endpoints, the gadget driver might
not be able work with that controller, or might need to implement
a less common variant of a device class protocol.
# this first set of drivers all depend on bulk-capable hardware. # this first set of drivers all depend on bulk-capable hardware.
config USB_CONFIGFS config USB_CONFIGFS
@ -439,6 +420,7 @@ config USB_CONFIGFS_F_HID
config USB_CONFIGFS_F_UVC config USB_CONFIGFS_F_UVC
bool "USB Webcam function" bool "USB Webcam function"
depends on USB_CONFIGFS depends on USB_CONFIGFS
depends on VIDEO_V4L2
depends on VIDEO_DEV depends on VIDEO_DEV
select VIDEOBUF2_VMALLOC select VIDEOBUF2_VMALLOC
select USB_F_UVC select USB_F_UVC
@ -475,6 +457,25 @@ config USB_CONFIGFS_F_TCM
Both protocols can work on USB2.0 and USB3.0. Both protocols can work on USB2.0 and USB3.0.
UAS utilizes the USB 3.0 feature called streams support. UAS utilizes the USB 3.0 feature called streams support.
choice
tristate "USB Gadget Drivers"
default USB_ETH
help
A Linux "Gadget Driver" talks to the USB Peripheral Controller
driver through the abstract "gadget" API. Some other operating
systems call these "client" drivers, of which "class drivers"
are a subset (implementing a USB device class specification).
A gadget driver implements one or more USB functions using
the peripheral hardware.
Gadget drivers are hardware-neutral, or "platform independent",
except that they sometimes must understand quirks or limitations
of the particular controllers they work with. For example, when
a controller doesn't support alternate configurations or provide
enough of the right types of endpoints, the gadget driver might
not be able work with that controller, or might need to implement
a less common variant of a device class protocol.
source "drivers/usb/gadget/legacy/Kconfig" source "drivers/usb/gadget/legacy/Kconfig"
endchoice endchoice

View file

@ -1893,17 +1893,21 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
/* functions always handle their interfaces and endpoints... /* functions always handle their interfaces and endpoints...
* punt other recipients (other, WUSB, ...) to the current * punt other recipients (other, WUSB, ...) to the current
* configuration code. * configuration code.
*
* REVISIT it could make sense to let the composite device
* take such requests too, if that's ever needed: to work
* in config 0, etc.
*/ */
if (cdev->config) { if (cdev->config) {
list_for_each_entry(f, &cdev->config->functions, list) list_for_each_entry(f, &cdev->config->functions, list)
if (f->req_match && f->req_match(f, ctrl)) if (f->req_match &&
f->req_match(f, ctrl, false))
goto try_fun_setup; goto try_fun_setup;
f = NULL; } else {
struct usb_configuration *c;
list_for_each_entry(c, &cdev->configs, list)
list_for_each_entry(f, &c->functions, list)
if (f->req_match &&
f->req_match(f, ctrl, true))
goto try_fun_setup;
} }
f = NULL;
switch (ctrl->bRequestType & USB_RECIP_MASK) { switch (ctrl->bRequestType & USB_RECIP_MASK) {
case USB_RECIP_INTERFACE: case USB_RECIP_INTERFACE:

View file

@ -1211,8 +1211,9 @@ static void purge_configs_funcs(struct gadget_info *gi)
list_move_tail(&f->list, &cfg->func_list); list_move_tail(&f->list, &cfg->func_list);
if (f->unbind) { if (f->unbind) {
dev_err(&gi->cdev.gadget->dev, "unbind function" dev_dbg(&gi->cdev.gadget->dev,
" '%s'/%p\n", f->name, f); "unbind function '%s'/%p\n",
f->name, f);
f->unbind(c, f); f->unbind(c, f);
} }
} }

View file

@ -98,6 +98,9 @@ static int ffs_func_set_alt(struct usb_function *, unsigned, unsigned);
static void ffs_func_disable(struct usb_function *); static void ffs_func_disable(struct usb_function *);
static int ffs_func_setup(struct usb_function *, static int ffs_func_setup(struct usb_function *,
const struct usb_ctrlrequest *); const struct usb_ctrlrequest *);
static bool ffs_func_req_match(struct usb_function *,
const struct usb_ctrlrequest *,
bool config0);
static void ffs_func_suspend(struct usb_function *); static void ffs_func_suspend(struct usb_function *);
static void ffs_func_resume(struct usb_function *); static void ffs_func_resume(struct usb_function *);
@ -2243,7 +2246,9 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
FUNCTIONFS_HAS_SS_DESC | FUNCTIONFS_HAS_SS_DESC |
FUNCTIONFS_HAS_MS_OS_DESC | FUNCTIONFS_HAS_MS_OS_DESC |
FUNCTIONFS_VIRTUAL_ADDR | FUNCTIONFS_VIRTUAL_ADDR |
FUNCTIONFS_EVENTFD)) { FUNCTIONFS_EVENTFD |
FUNCTIONFS_ALL_CTRL_RECIP |
FUNCTIONFS_CONFIG0_SETUP)) {
ret = -ENOSYS; ret = -ENOSYS;
goto error; goto error;
} }
@ -3094,8 +3099,9 @@ static int ffs_func_setup(struct usb_function *f,
* handle them. All other either handled by composite or * handle them. All other either handled by composite or
* passed to usb_configuration->setup() (if one is set). No * passed to usb_configuration->setup() (if one is set). No
* matter, we will handle requests directed to endpoint here * matter, we will handle requests directed to endpoint here
* as well (as it's straightforward) but what to do with any * as well (as it's straightforward). Other request recipient
* other request? * types are only handled when the user flag FUNCTIONFS_ALL_CTRL_RECIP
* is being used.
*/ */
if (ffs->state != FFS_ACTIVE) if (ffs->state != FFS_ACTIVE)
return -ENODEV; return -ENODEV;
@ -3116,7 +3122,10 @@ static int ffs_func_setup(struct usb_function *f,
break; break;
default: default:
return -EOPNOTSUPP; if (func->ffs->user_flags & FUNCTIONFS_ALL_CTRL_RECIP)
ret = le16_to_cpu(creq->wIndex);
else
return -EOPNOTSUPP;
} }
spin_lock_irqsave(&ffs->ev.waitq.lock, flags); spin_lock_irqsave(&ffs->ev.waitq.lock, flags);
@ -3128,6 +3137,28 @@ static int ffs_func_setup(struct usb_function *f,
return 0; return 0;
} }
static bool ffs_func_req_match(struct usb_function *f,
const struct usb_ctrlrequest *creq,
bool config0)
{
struct ffs_function *func = ffs_func_from_usb(f);
if (config0 && !(func->ffs->user_flags & FUNCTIONFS_CONFIG0_SETUP))
return false;
switch (creq->bRequestType & USB_RECIP_MASK) {
case USB_RECIP_INTERFACE:
return ffs_func_revmap_intf(func,
le16_to_cpu(creq->wIndex) >= 0);
case USB_RECIP_ENDPOINT:
return ffs_func_revmap_ep(func,
le16_to_cpu(creq->wIndex) >= 0);
default:
return (bool) (func->ffs->user_flags &
FUNCTIONFS_ALL_CTRL_RECIP);
}
}
static void ffs_func_suspend(struct usb_function *f) static void ffs_func_suspend(struct usb_function *f)
{ {
ENTER(); ENTER();
@ -3378,6 +3409,7 @@ static struct usb_function *ffs_alloc(struct usb_function_instance *fi)
func->function.set_alt = ffs_func_set_alt; func->function.set_alt = ffs_func_set_alt;
func->function.disable = ffs_func_disable; func->function.disable = ffs_func_disable;
func->function.setup = ffs_func_setup; func->function.setup = ffs_func_setup;
func->function.req_match = ffs_func_req_match;
func->function.suspend = ffs_func_suspend; func->function.suspend = ffs_func_suspend;
func->function.resume = ffs_func_resume; func->function.resume = ffs_func_resume;
func->function.free_func = ffs_free; func->function.free_func = ffs_free;
@ -3470,6 +3502,11 @@ static void _ffs_free_dev(struct ffs_dev *dev)
list_del(&dev->entry); list_del(&dev->entry);
if (dev->name_allocated) if (dev->name_allocated)
kfree(dev->name); kfree(dev->name);
/* Clear the private_data pointer to stop incorrect dev access */
if (dev->ffs_data)
dev->ffs_data->private_data = NULL;
kfree(dev); kfree(dev);
if (list_empty(&ffs_devices)) if (list_empty(&ffs_devices))
functionfs_cleanup(); functionfs_cleanup();

View file

@ -365,7 +365,7 @@ static int f_hidg_open(struct inode *inode, struct file *fd)
static inline struct usb_request *hidg_alloc_ep_req(struct usb_ep *ep, static inline struct usb_request *hidg_alloc_ep_req(struct usb_ep *ep,
unsigned length) unsigned length)
{ {
return alloc_ep_req(ep, length, length); return alloc_ep_req(ep, length);
} }
static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req) static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req)
@ -617,14 +617,10 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
/* preallocate request and buffer */ /* preallocate request and buffer */
status = -ENOMEM; status = -ENOMEM;
hidg->req = usb_ep_alloc_request(hidg->in_ep, GFP_KERNEL); hidg->req = alloc_ep_req(hidg->in_ep, hidg->report_length);
if (!hidg->req) if (!hidg->req)
goto fail; goto fail;
hidg->req->buf = kmalloc(hidg->report_length, GFP_KERNEL);
if (!hidg->req->buf)
goto fail;
/* set descriptor dynamic values */ /* set descriptor dynamic values */
hidg_interface_desc.bInterfaceSubClass = hidg->bInterfaceSubClass; hidg_interface_desc.bInterfaceSubClass = hidg->bInterfaceSubClass;
hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol; hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol;
@ -677,11 +673,8 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
usb_free_all_descriptors(f); usb_free_all_descriptors(f);
fail: fail:
ERROR(f->config->cdev, "hidg_bind FAILED\n"); ERROR(f->config->cdev, "hidg_bind FAILED\n");
if (hidg->req != NULL) { if (hidg->req != NULL)
kfree(hidg->req->buf); free_ep_req(hidg->in_ep, hidg->req);
if (hidg->in_ep != NULL)
usb_ep_free_request(hidg->in_ep, hidg->req);
}
return status; return status;
} }
@ -809,11 +802,21 @@ static ssize_t f_hid_opts_report_desc_store(struct config_item *item,
CONFIGFS_ATTR(f_hid_opts_, report_desc); CONFIGFS_ATTR(f_hid_opts_, report_desc);
static ssize_t f_hid_opts_dev_show(struct config_item *item, char *page)
{
struct f_hid_opts *opts = to_f_hid_opts(item);
return sprintf(page, "%d:%d\n", major, opts->minor);
}
CONFIGFS_ATTR_RO(f_hid_opts_, dev);
static struct configfs_attribute *hid_attrs[] = { static struct configfs_attribute *hid_attrs[] = {
&f_hid_opts_attr_subclass, &f_hid_opts_attr_subclass,
&f_hid_opts_attr_protocol, &f_hid_opts_attr_protocol,
&f_hid_opts_attr_report_length, &f_hid_opts_attr_report_length,
&f_hid_opts_attr_report_desc, &f_hid_opts_attr_report_desc,
&f_hid_opts_attr_dev,
NULL, NULL,
}; };
@ -910,8 +913,7 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
/* disable/free request and end point */ /* disable/free request and end point */
usb_ep_disable(hidg->in_ep); usb_ep_disable(hidg->in_ep);
kfree(hidg->req->buf); free_ep_req(hidg->in_ep, hidg->req);
usb_ep_free_request(hidg->in_ep, hidg->req);
usb_free_all_descriptors(f); usb_free_all_descriptors(f);
} }

View file

@ -308,9 +308,7 @@ static void disable_loopback(struct f_loopback *loop)
static inline struct usb_request *lb_alloc_ep_req(struct usb_ep *ep, int len) static inline struct usb_request *lb_alloc_ep_req(struct usb_ep *ep, int len)
{ {
struct f_loopback *loop = ep->driver_data; return alloc_ep_req(ep, len);
return alloc_ep_req(ep, len, loop->buflen);
} }
static int alloc_requests(struct usb_composite_dev *cdev, static int alloc_requests(struct usb_composite_dev *cdev,
@ -333,7 +331,7 @@ static int alloc_requests(struct usb_composite_dev *cdev,
if (!in_req) if (!in_req)
goto fail; goto fail;
out_req = lb_alloc_ep_req(loop->out_ep, 0); out_req = lb_alloc_ep_req(loop->out_ep, loop->buflen);
if (!out_req) if (!out_req)
goto fail_in; goto fail_in;
@ -593,13 +591,9 @@ DECLARE_USB_FUNCTION(Loopback, loopback_alloc_instance, loopback_alloc);
int __init lb_modinit(void) int __init lb_modinit(void)
{ {
int ret; return usb_function_register(&Loopbackusb_func);
ret = usb_function_register(&Loopbackusb_func);
if (ret)
return ret;
return ret;
} }
void __exit lb_modexit(void) void __exit lb_modexit(void)
{ {
usb_function_unregister(&Loopbackusb_func); usb_function_unregister(&Loopbackusb_func);

View file

@ -311,11 +311,7 @@ struct fsg_common {
/* Gadget's private data. */ /* Gadget's private data. */
void *private_data; void *private_data;
/* char inquiry_string[INQUIRY_STRING_LEN];
* Vendor (8 chars), product (16 chars), release (4
* hexadecimal digits) and NUL byte
*/
char inquiry_string[8 + 16 + 4 + 1];
struct kref ref; struct kref ref;
}; };
@ -1107,7 +1103,12 @@ static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh)
buf[5] = 0; /* No special options */ buf[5] = 0; /* No special options */
buf[6] = 0; buf[6] = 0;
buf[7] = 0; buf[7] = 0;
memcpy(buf + 8, common->inquiry_string, sizeof common->inquiry_string); if (curlun->inquiry_string[0])
memcpy(buf + 8, curlun->inquiry_string,
sizeof(curlun->inquiry_string));
else
memcpy(buf + 8, common->inquiry_string,
sizeof(common->inquiry_string));
return 36; return 36;
} }
@ -3209,12 +3210,27 @@ static ssize_t fsg_lun_opts_nofua_store(struct config_item *item,
CONFIGFS_ATTR(fsg_lun_opts_, nofua); CONFIGFS_ATTR(fsg_lun_opts_, nofua);
static ssize_t fsg_lun_opts_inquiry_string_show(struct config_item *item,
char *page)
{
return fsg_show_inquiry_string(to_fsg_lun_opts(item)->lun, page);
}
static ssize_t fsg_lun_opts_inquiry_string_store(struct config_item *item,
const char *page, size_t len)
{
return fsg_store_inquiry_string(to_fsg_lun_opts(item)->lun, page, len);
}
CONFIGFS_ATTR(fsg_lun_opts_, inquiry_string);
static struct configfs_attribute *fsg_lun_attrs[] = { static struct configfs_attribute *fsg_lun_attrs[] = {
&fsg_lun_opts_attr_file, &fsg_lun_opts_attr_file,
&fsg_lun_opts_attr_ro, &fsg_lun_opts_attr_ro,
&fsg_lun_opts_attr_removable, &fsg_lun_opts_attr_removable,
&fsg_lun_opts_attr_cdrom, &fsg_lun_opts_attr_cdrom,
&fsg_lun_opts_attr_nofua, &fsg_lun_opts_attr_nofua,
&fsg_lun_opts_attr_inquiry_string,
NULL, NULL,
}; };

View file

@ -100,6 +100,7 @@ struct fsg_lun_config {
char removable; char removable;
char cdrom; char cdrom;
char nofua; char nofua;
char inquiry_string[INQUIRY_STRING_LEN];
}; };
struct fsg_config { struct fsg_config {

View file

@ -51,6 +51,19 @@ static const char f_midi_longname[] = "MIDI Gadget";
*/ */
#define MAX_PORTS 16 #define MAX_PORTS 16
/* MIDI message states */
enum {
STATE_INITIAL = 0, /* pseudo state */
STATE_1PARAM,
STATE_2PARAM_1,
STATE_2PARAM_2,
STATE_SYSEX_0,
STATE_SYSEX_1,
STATE_SYSEX_2,
STATE_REAL_TIME,
STATE_FINISHED, /* pseudo state */
};
/* /*
* This is a gadget, and the IN/OUT naming is from the host's perspective. * This is a gadget, and the IN/OUT naming is from the host's perspective.
* USB -> OUT endpoint -> rawmidi * USB -> OUT endpoint -> rawmidi
@ -61,13 +74,6 @@ struct gmidi_in_port {
int active; int active;
uint8_t cable; uint8_t cable;
uint8_t state; uint8_t state;
#define STATE_UNKNOWN 0
#define STATE_1PARAM 1
#define STATE_2PARAM_1 2
#define STATE_2PARAM_2 3
#define STATE_SYSEX_0 4
#define STATE_SYSEX_1 5
#define STATE_SYSEX_2 6
uint8_t data[2]; uint8_t data[2];
}; };
@ -205,7 +211,7 @@ static struct usb_gadget_strings *midi_strings[] = {
static inline struct usb_request *midi_alloc_ep_req(struct usb_ep *ep, static inline struct usb_request *midi_alloc_ep_req(struct usb_ep *ep,
unsigned length) unsigned length)
{ {
return alloc_ep_req(ep, length, length); return alloc_ep_req(ep, length);
} }
static const uint8_t f_midi_cin_length[] = { static const uint8_t f_midi_cin_length[] = {
@ -299,6 +305,19 @@ f_midi_complete(struct usb_ep *ep, struct usb_request *req)
} }
} }
static void f_midi_drop_out_substreams(struct f_midi *midi)
{
unsigned int i;
for (i = 0; i < midi->in_ports; i++) {
struct gmidi_in_port *port = midi->in_ports_array + i;
struct snd_rawmidi_substream *substream = port->substream;
if (port->active && substream)
snd_rawmidi_drop_output(substream);
}
}
static int f_midi_start_ep(struct f_midi *midi, static int f_midi_start_ep(struct f_midi *midi,
struct usb_function *f, struct usb_function *f,
struct usb_ep *ep) struct usb_ep *ep)
@ -360,9 +379,8 @@ static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
/* allocate a bunch of read buffers and queue them all at once. */ /* allocate a bunch of read buffers and queue them all at once. */
for (i = 0; i < midi->qlen && err == 0; i++) { for (i = 0; i < midi->qlen && err == 0; i++) {
struct usb_request *req = struct usb_request *req =
midi_alloc_ep_req(midi->out_ep, midi_alloc_ep_req(midi->out_ep, midi->buflen);
max_t(unsigned, midi->buflen,
bulk_out_desc.wMaxPacketSize));
if (req == NULL) if (req == NULL)
return -ENOMEM; return -ENOMEM;
@ -397,6 +415,8 @@ static void f_midi_disable(struct usb_function *f)
/* release IN requests */ /* release IN requests */
while (kfifo_get(&midi->in_req_fifo, &req)) while (kfifo_get(&midi->in_req_fifo, &req))
free_ep_req(midi->in_ep, req); free_ep_req(midi->in_ep, req);
f_midi_drop_out_substreams(midi);
} }
static int f_midi_snd_free(struct snd_device *device) static int f_midi_snd_free(struct snd_device *device)
@ -404,130 +424,166 @@ static int f_midi_snd_free(struct snd_device *device)
return 0; return 0;
} }
static void f_midi_transmit_packet(struct usb_request *req, uint8_t p0,
uint8_t p1, uint8_t p2, uint8_t p3)
{
unsigned length = req->length;
u8 *buf = (u8 *)req->buf + length;
buf[0] = p0;
buf[1] = p1;
buf[2] = p2;
buf[3] = p3;
req->length = length + 4;
}
/* /*
* Converts MIDI commands to USB MIDI packets. * Converts MIDI commands to USB MIDI packets.
*/ */
static void f_midi_transmit_byte(struct usb_request *req, static void f_midi_transmit_byte(struct usb_request *req,
struct gmidi_in_port *port, uint8_t b) struct gmidi_in_port *port, uint8_t b)
{ {
uint8_t p0 = port->cable << 4; uint8_t p[4] = { port->cable << 4, 0, 0, 0 };
uint8_t next_state = STATE_INITIAL;
if (b >= 0xf8) { switch (b) {
f_midi_transmit_packet(req, p0 | 0x0f, b, 0, 0); case 0xf8 ... 0xff:
} else if (b >= 0xf0) { /* System Real-Time Messages */
p[0] |= 0x0f;
p[1] = b;
next_state = port->state;
port->state = STATE_REAL_TIME;
break;
case 0xf7:
/* End of SysEx */
switch (port->state) {
case STATE_SYSEX_0:
p[0] |= 0x05;
p[1] = 0xf7;
next_state = STATE_FINISHED;
break;
case STATE_SYSEX_1:
p[0] |= 0x06;
p[1] = port->data[0];
p[2] = 0xf7;
next_state = STATE_FINISHED;
break;
case STATE_SYSEX_2:
p[0] |= 0x07;
p[1] = port->data[0];
p[2] = port->data[1];
p[3] = 0xf7;
next_state = STATE_FINISHED;
break;
default:
/* Ignore byte */
next_state = port->state;
port->state = STATE_INITIAL;
}
break;
case 0xf0 ... 0xf6:
/* System Common Messages */
port->data[0] = port->data[1] = 0;
port->state = STATE_INITIAL;
switch (b) { switch (b) {
case 0xf0: case 0xf0:
port->data[0] = b; port->data[0] = b;
port->state = STATE_SYSEX_1; port->data[1] = 0;
next_state = STATE_SYSEX_1;
break; break;
case 0xf1: case 0xf1:
case 0xf3: case 0xf3:
port->data[0] = b; port->data[0] = b;
port->state = STATE_1PARAM; next_state = STATE_1PARAM;
break; break;
case 0xf2: case 0xf2:
port->data[0] = b; port->data[0] = b;
port->state = STATE_2PARAM_1; next_state = STATE_2PARAM_1;
break; break;
case 0xf4: case 0xf4:
case 0xf5: case 0xf5:
port->state = STATE_UNKNOWN; next_state = STATE_INITIAL;
break; break;
case 0xf6: case 0xf6:
f_midi_transmit_packet(req, p0 | 0x05, 0xf6, 0, 0); p[0] |= 0x05;
port->state = STATE_UNKNOWN; p[1] = 0xf6;
break; next_state = STATE_FINISHED;
case 0xf7:
switch (port->state) {
case STATE_SYSEX_0:
f_midi_transmit_packet(req,
p0 | 0x05, 0xf7, 0, 0);
break;
case STATE_SYSEX_1:
f_midi_transmit_packet(req,
p0 | 0x06, port->data[0], 0xf7, 0);
break;
case STATE_SYSEX_2:
f_midi_transmit_packet(req,
p0 | 0x07, port->data[0],
port->data[1], 0xf7);
break;
}
port->state = STATE_UNKNOWN;
break; break;
} }
} else if (b >= 0x80) { break;
case 0x80 ... 0xef:
/*
* Channel Voice Messages, Channel Mode Messages
* and Control Change Messages.
*/
port->data[0] = b; port->data[0] = b;
port->data[1] = 0;
port->state = STATE_INITIAL;
if (b >= 0xc0 && b <= 0xdf) if (b >= 0xc0 && b <= 0xdf)
port->state = STATE_1PARAM; next_state = STATE_1PARAM;
else else
port->state = STATE_2PARAM_1; next_state = STATE_2PARAM_1;
} else { /* b < 0x80 */ break;
case 0x00 ... 0x7f:
/* Message parameters */
switch (port->state) { switch (port->state) {
case STATE_1PARAM: case STATE_1PARAM:
if (port->data[0] < 0xf0) { if (port->data[0] < 0xf0)
p0 |= port->data[0] >> 4; p[0] |= port->data[0] >> 4;
} else { else
p0 |= 0x02; p[0] |= 0x02;
port->state = STATE_UNKNOWN;
} p[1] = port->data[0];
f_midi_transmit_packet(req, p0, port->data[0], b, 0); p[2] = b;
/* This is to allow Running State Messages */
next_state = STATE_1PARAM;
break; break;
case STATE_2PARAM_1: case STATE_2PARAM_1:
port->data[1] = b; port->data[1] = b;
port->state = STATE_2PARAM_2; next_state = STATE_2PARAM_2;
break; break;
case STATE_2PARAM_2: case STATE_2PARAM_2:
if (port->data[0] < 0xf0) { if (port->data[0] < 0xf0)
p0 |= port->data[0] >> 4; p[0] |= port->data[0] >> 4;
port->state = STATE_2PARAM_1; else
} else { p[0] |= 0x03;
p0 |= 0x03;
port->state = STATE_UNKNOWN; p[1] = port->data[0];
} p[2] = port->data[1];
f_midi_transmit_packet(req, p[3] = b;
p0, port->data[0], port->data[1], b); /* This is to allow Running State Messages */
next_state = STATE_2PARAM_1;
break; break;
case STATE_SYSEX_0: case STATE_SYSEX_0:
port->data[0] = b; port->data[0] = b;
port->state = STATE_SYSEX_1; next_state = STATE_SYSEX_1;
break; break;
case STATE_SYSEX_1: case STATE_SYSEX_1:
port->data[1] = b; port->data[1] = b;
port->state = STATE_SYSEX_2; next_state = STATE_SYSEX_2;
break; break;
case STATE_SYSEX_2: case STATE_SYSEX_2:
f_midi_transmit_packet(req, p[0] |= 0x04;
p0 | 0x04, port->data[0], port->data[1], b); p[1] = port->data[0];
port->state = STATE_SYSEX_0; p[2] = port->data[1];
p[3] = b;
next_state = STATE_SYSEX_0;
break; break;
} }
break;
} }
}
static void f_midi_drop_out_substreams(struct f_midi *midi) /* States where we have to write into the USB request */
{ if (next_state == STATE_FINISHED ||
unsigned int i; port->state == STATE_SYSEX_2 ||
port->state == STATE_1PARAM ||
port->state == STATE_2PARAM_2 ||
port->state == STATE_REAL_TIME) {
for (i = 0; i < midi->in_ports; i++) { unsigned int length = req->length;
struct gmidi_in_port *port = midi->in_ports_array + i; u8 *buf = (u8 *)req->buf + length;
struct snd_rawmidi_substream *substream = port->substream;
if (port->active && substream) memcpy(buf, p, sizeof(p));
snd_rawmidi_drop_output(substream); req->length = length + sizeof(p);
if (next_state == STATE_FINISHED) {
next_state = STATE_INITIAL;
port->data[0] = port->data[1] = 0;
}
} }
port->state = next_state;
} }
static int f_midi_do_transmit(struct f_midi *midi, struct usb_ep *ep) static int f_midi_do_transmit(struct f_midi *midi, struct usb_ep *ep)
@ -642,7 +698,7 @@ static int f_midi_in_open(struct snd_rawmidi_substream *substream)
VDBG(midi, "%s()\n", __func__); VDBG(midi, "%s()\n", __func__);
port = midi->in_ports_array + substream->number; port = midi->in_ports_array + substream->number;
port->substream = substream; port->substream = substream;
port->state = STATE_UNKNOWN; port->state = STATE_INITIAL;
return 0; return 0;
} }
@ -1123,7 +1179,7 @@ static struct usb_function_instance *f_midi_alloc_inst(void)
opts->func_inst.free_func_inst = f_midi_free_inst; opts->func_inst.free_func_inst = f_midi_free_inst;
opts->index = SNDRV_DEFAULT_IDX1; opts->index = SNDRV_DEFAULT_IDX1;
opts->id = SNDRV_DEFAULT_STR1; opts->id = SNDRV_DEFAULT_STR1;
opts->buflen = 256; opts->buflen = 512;
opts->qlen = 32; opts->qlen = 32;
opts->in_ports = 1; opts->in_ports = 1;
opts->out_ports = 1; opts->out_ports = 1;

View file

@ -90,7 +90,9 @@ static inline struct f_ncm *func_to_ncm(struct usb_function *f)
/* peak (theoretical) bulk transfer rate in bits-per-second */ /* peak (theoretical) bulk transfer rate in bits-per-second */
static inline unsigned ncm_bitrate(struct usb_gadget *g) static inline unsigned ncm_bitrate(struct usb_gadget *g)
{ {
if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER)
return 13 * 1024 * 8 * 1000 * 8;
else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
return 13 * 512 * 8 * 1000 * 8; return 13 * 512 * 8 * 1000 * 8;
else else
return 19 * 64 * 1 * 1000 * 8; return 19 * 64 * 1 * 1000 * 8;
@ -333,6 +335,76 @@ static struct usb_descriptor_header *ncm_hs_function[] = {
NULL, NULL,
}; };
/* super speed support: */
static struct usb_endpoint_descriptor ss_ncm_notify_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
.wMaxPacketSize = cpu_to_le16(NCM_STATUS_BYTECOUNT),
.bInterval = USB_MS_TO_HS_INTERVAL(NCM_STATUS_INTERVAL_MS)
};
static struct usb_ss_ep_comp_descriptor ss_ncm_notify_comp_desc = {
.bLength = sizeof(ss_ncm_notify_comp_desc),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
/* the following 3 values can be tweaked if necessary */
/* .bMaxBurst = 0, */
/* .bmAttributes = 0, */
.wBytesPerInterval = cpu_to_le16(NCM_STATUS_BYTECOUNT),
};
static struct usb_endpoint_descriptor ss_ncm_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
static struct usb_endpoint_descriptor ss_ncm_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
static struct usb_ss_ep_comp_descriptor ss_ncm_bulk_comp_desc = {
.bLength = sizeof(ss_ncm_bulk_comp_desc),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
/* the following 2 values can be tweaked if necessary */
/* .bMaxBurst = 0, */
/* .bmAttributes = 0, */
};
static struct usb_descriptor_header *ncm_ss_function[] = {
(struct usb_descriptor_header *) &ncm_iad_desc,
/* CDC NCM control descriptors */
(struct usb_descriptor_header *) &ncm_control_intf,
(struct usb_descriptor_header *) &ncm_header_desc,
(struct usb_descriptor_header *) &ncm_union_desc,
(struct usb_descriptor_header *) &ecm_desc,
(struct usb_descriptor_header *) &ncm_desc,
(struct usb_descriptor_header *) &ss_ncm_notify_desc,
(struct usb_descriptor_header *) &ss_ncm_notify_comp_desc,
/* data interface, altsettings 0 and 1 */
(struct usb_descriptor_header *) &ncm_data_nop_intf,
(struct usb_descriptor_header *) &ncm_data_intf,
(struct usb_descriptor_header *) &ss_ncm_in_desc,
(struct usb_descriptor_header *) &ss_ncm_bulk_comp_desc,
(struct usb_descriptor_header *) &ss_ncm_out_desc,
(struct usb_descriptor_header *) &ss_ncm_bulk_comp_desc,
NULL,
};
/* string descriptors: */ /* string descriptors: */
#define STRING_CTRL_IDX 0 #define STRING_CTRL_IDX 0
@ -852,6 +924,8 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
*/ */
ncm->port.is_zlp_ok = ncm->port.is_zlp_ok =
gadget_is_zlp_supported(cdev->gadget); gadget_is_zlp_supported(cdev->gadget);
ncm->port.no_skb_reserve =
gadget_avoids_skb_reserve(cdev->gadget);
ncm->port.cdc_filter = DEFAULT_FILTER; ncm->port.cdc_filter = DEFAULT_FILTER;
DBG(cdev, "activate ncm\n"); DBG(cdev, "activate ncm\n");
net = gether_connect(&ncm->port); net = gether_connect(&ncm->port);
@ -1431,8 +1505,13 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
hs_ncm_notify_desc.bEndpointAddress = hs_ncm_notify_desc.bEndpointAddress =
fs_ncm_notify_desc.bEndpointAddress; fs_ncm_notify_desc.bEndpointAddress;
ss_ncm_in_desc.bEndpointAddress = fs_ncm_in_desc.bEndpointAddress;
ss_ncm_out_desc.bEndpointAddress = fs_ncm_out_desc.bEndpointAddress;
ss_ncm_notify_desc.bEndpointAddress =
fs_ncm_notify_desc.bEndpointAddress;
status = usb_assign_descriptors(f, ncm_fs_function, ncm_hs_function, status = usb_assign_descriptors(f, ncm_fs_function, ncm_hs_function,
NULL, NULL); ncm_ss_function, NULL);
if (status) if (status)
goto fail; goto fail;
@ -1450,6 +1529,7 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
ncm->task_timer.function = ncm_tx_timeout; ncm->task_timer.function = ncm_tx_timeout;
DBG(cdev, "CDC Network: %s speed IN/%s OUT/%s NOTIFY/%s\n", DBG(cdev, "CDC Network: %s speed IN/%s OUT/%s NOTIFY/%s\n",
gadget_is_superspeed(c->cdev->gadget) ? "super" :
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
ncm->port.in_ep->name, ncm->port.out_ep->name, ncm->port.in_ep->name, ncm->port.out_ep->name,
ncm->notify->name); ncm->notify->name);

View file

@ -889,13 +889,17 @@ static void printer_soft_reset(struct printer_dev *dev)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
static bool gprinter_req_match(struct usb_function *f, static bool gprinter_req_match(struct usb_function *f,
const struct usb_ctrlrequest *ctrl) const struct usb_ctrlrequest *ctrl,
bool config0)
{ {
struct printer_dev *dev = func_to_printer(f); struct printer_dev *dev = func_to_printer(f);
u16 w_index = le16_to_cpu(ctrl->wIndex); u16 w_index = le16_to_cpu(ctrl->wIndex);
u16 w_value = le16_to_cpu(ctrl->wValue); u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength); u16 w_length = le16_to_cpu(ctrl->wLength);
if (config0)
return false;
if ((ctrl->bRequestType & USB_RECIP_MASK) != USB_RECIP_INTERFACE || if ((ctrl->bRequestType & USB_RECIP_MASK) != USB_RECIP_INTERFACE ||
(ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_CLASS) (ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_CLASS)
return false; return false;

View file

@ -293,9 +293,7 @@ static struct usb_gadget_strings *sourcesink_strings[] = {
static inline struct usb_request *ss_alloc_ep_req(struct usb_ep *ep, int len) static inline struct usb_request *ss_alloc_ep_req(struct usb_ep *ep, int len)
{ {
struct f_sourcesink *ss = ep->driver_data; return alloc_ep_req(ep, len);
return alloc_ep_req(ep, len, ss->buflen);
} }
static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep) static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep)
@ -606,7 +604,7 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in,
} else { } else {
ep = is_in ? ss->in_ep : ss->out_ep; ep = is_in ? ss->in_ep : ss->out_ep;
qlen = ss->bulk_qlen; qlen = ss->bulk_qlen;
size = 0; size = ss->buflen;
} }
for (i = 0; i < qlen; i++) { for (i = 0; i < qlen; i++) {

View file

@ -258,6 +258,13 @@ uvc_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
memcpy(&uvc_event->req, ctrl, sizeof(uvc_event->req)); memcpy(&uvc_event->req, ctrl, sizeof(uvc_event->req));
v4l2_event_queue(&uvc->vdev, &v4l2_event); v4l2_event_queue(&uvc->vdev, &v4l2_event);
/* Pass additional setup data to userspace */
if (uvc->event_setup_out && uvc->event_length) {
uvc->control_req->length = uvc->event_length;
return usb_ep_queue(uvc->func.config->cdev->gadget->ep0,
uvc->control_req, GFP_ATOMIC);
}
return 0; return 0;
} }

View file

@ -369,6 +369,12 @@ ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf)
} }
EXPORT_SYMBOL_GPL(fsg_show_removable); EXPORT_SYMBOL_GPL(fsg_show_removable);
ssize_t fsg_show_inquiry_string(struct fsg_lun *curlun, char *buf)
{
return sprintf(buf, "%s\n", curlun->inquiry_string);
}
EXPORT_SYMBOL_GPL(fsg_show_inquiry_string);
/* /*
* The caller must hold fsg->filesem for reading when calling this function. * The caller must hold fsg->filesem for reading when calling this function.
*/ */
@ -499,4 +505,22 @@ ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf,
} }
EXPORT_SYMBOL_GPL(fsg_store_removable); EXPORT_SYMBOL_GPL(fsg_store_removable);
ssize_t fsg_store_inquiry_string(struct fsg_lun *curlun, const char *buf,
size_t count)
{
const size_t len = min(count, sizeof(curlun->inquiry_string));
if (len == 0 || buf[0] == '\n') {
curlun->inquiry_string[0] = 0;
} else {
snprintf(curlun->inquiry_string,
sizeof(curlun->inquiry_string), "%-28s", buf);
if (curlun->inquiry_string[len-1] == '\n')
curlun->inquiry_string[len-1] = ' ';
}
return count;
}
EXPORT_SYMBOL_GPL(fsg_store_inquiry_string);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");

View file

@ -88,6 +88,12 @@ do { \
#define ASC(x) ((u8) ((x) >> 8)) #define ASC(x) ((u8) ((x) >> 8))
#define ASCQ(x) ((u8) (x)) #define ASCQ(x) ((u8) (x))
/*
* Vendor (8 chars), product (16 chars), release (4 hexadecimal digits) and NUL
* byte
*/
#define INQUIRY_STRING_LEN ((size_t) (8 + 16 + 4 + 1))
struct fsg_lun { struct fsg_lun {
struct file *filp; struct file *filp;
loff_t file_length; loff_t file_length;
@ -112,6 +118,7 @@ struct fsg_lun {
struct device dev; struct device dev;
const char *name; /* "lun.name" */ const char *name; /* "lun.name" */
const char **name_pfx; /* "function.name" */ const char **name_pfx; /* "function.name" */
char inquiry_string[INQUIRY_STRING_LEN];
}; };
static inline bool fsg_lun_is_open(struct fsg_lun *curlun) static inline bool fsg_lun_is_open(struct fsg_lun *curlun)
@ -210,6 +217,7 @@ ssize_t fsg_show_ro(struct fsg_lun *curlun, char *buf);
ssize_t fsg_show_nofua(struct fsg_lun *curlun, char *buf); ssize_t fsg_show_nofua(struct fsg_lun *curlun, char *buf);
ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem, ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
char *buf); char *buf);
ssize_t fsg_show_inquiry_string(struct fsg_lun *curlun, char *buf);
ssize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf); ssize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf);
ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf); ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf);
ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem, ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem,
@ -221,5 +229,7 @@ ssize_t fsg_store_cdrom(struct fsg_lun *curlun, struct rw_semaphore *filesem,
const char *buf, size_t count); const char *buf, size_t count);
ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf, ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf,
size_t count); size_t count);
ssize_t fsg_store_inquiry_string(struct fsg_lun *curlun, const char *buf,
size_t count);
#endif /* USB_STORAGE_COMMON_H */ #endif /* USB_STORAGE_COMMON_H */

View file

@ -82,6 +82,7 @@ struct eth_dev {
#define WORK_RX_MEMORY 0 #define WORK_RX_MEMORY 0
bool zlp; bool zlp;
bool no_skb_reserve;
u8 host_mac[ETH_ALEN]; u8 host_mac[ETH_ALEN];
u8 dev_mac[ETH_ALEN]; u8 dev_mac[ETH_ALEN];
}; };
@ -233,7 +234,8 @@ rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
* but on at least one, checksumming fails otherwise. Note: * but on at least one, checksumming fails otherwise. Note:
* RNDIS headers involve variable numbers of LE32 values. * RNDIS headers involve variable numbers of LE32 values.
*/ */
skb_reserve(skb, NET_IP_ALIGN); if (likely(!dev->no_skb_reserve))
skb_reserve(skb, NET_IP_ALIGN);
req->buf = skb->data; req->buf = skb->data;
req->length = size; req->length = size;
@ -551,14 +553,16 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
spin_lock_irqsave(&dev->lock, flags); spin_lock_irqsave(&dev->lock, flags);
if (dev->port_usb) if (dev->port_usb)
skb = dev->wrap(dev->port_usb, skb); skb = dev->wrap(dev->port_usb, skb);
spin_unlock_irqrestore(&dev->lock, flags);
if (!skb) { if (!skb) {
/* Multi frame CDC protocols may store the frame for /* Multi frame CDC protocols may store the frame for
* later which is not a dropped frame. * later which is not a dropped frame.
*/ */
if (dev->port_usb && if (dev->port_usb &&
dev->port_usb->supports_multi_frame) dev->port_usb->supports_multi_frame) {
spin_unlock_irqrestore(&dev->lock, flags);
goto multiframe; goto multiframe;
}
spin_unlock_irqrestore(&dev->lock, flags);
goto drop; goto drop;
} }
} }
@ -569,12 +573,14 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
req->complete = tx_complete; req->complete = tx_complete;
/* NCM requires no zlp if transfer is dwNtbInMaxSize */ /* NCM requires no zlp if transfer is dwNtbInMaxSize */
if (dev->port_usb->is_fixed && if (dev->port_usb &&
dev->port_usb->is_fixed &&
length == dev->port_usb->fixed_in_len && length == dev->port_usb->fixed_in_len &&
(length % in->maxpacket) == 0) (length % in->maxpacket) == 0)
req->zero = 0; req->zero = 0;
else else
req->zero = 1; req->zero = 1;
spin_unlock_irqrestore(&dev->lock, flags);
/* use zlp framing on tx for strict CDC-Ether conformance, /* use zlp framing on tx for strict CDC-Ether conformance,
* though any robust network rx path ignores extra padding. * though any robust network rx path ignores extra padding.
@ -1063,6 +1069,7 @@ struct net_device *gether_connect(struct gether *link)
if (result == 0) { if (result == 0) {
dev->zlp = link->is_zlp_ok; dev->zlp = link->is_zlp_ok;
dev->no_skb_reserve = link->no_skb_reserve;
DBG(dev, "qlen %d\n", qlen(dev->gadget, dev->qmult)); DBG(dev, "qlen %d\n", qlen(dev->gadget, dev->qmult));
dev->header_len = link->header_len; dev->header_len = link->header_len;

View file

@ -64,6 +64,7 @@ struct gether {
struct usb_ep *out_ep; struct usb_ep *out_ep;
bool is_zlp_ok; bool is_zlp_ok;
bool no_skb_reserve;
u16 cdc_filter; u16 cdc_filter;

View file

@ -47,7 +47,7 @@ static char *id = SNDRV_DEFAULT_STR1;
module_param(id, charp, S_IRUGO); module_param(id, charp, S_IRUGO);
MODULE_PARM_DESC(id, "ID string for the USB MIDI Gadget adapter."); MODULE_PARM_DESC(id, "ID string for the USB MIDI Gadget adapter.");
static unsigned int buflen = 256; static unsigned int buflen = 512;
module_param(buflen, uint, S_IRUGO); module_param(buflen, uint, S_IRUGO);
MODULE_PARM_DESC(buflen, "MIDI buffer length"); MODULE_PARM_DESC(buflen, "MIDI buffer length");

View file

@ -12,14 +12,16 @@
*/ */
#include "u_f.h" #include "u_f.h"
#include <linux/usb/ch9.h>
struct usb_request *alloc_ep_req(struct usb_ep *ep, int len, int default_len) struct usb_request *alloc_ep_req(struct usb_ep *ep, size_t len)
{ {
struct usb_request *req; struct usb_request *req;
req = usb_ep_alloc_request(ep, GFP_ATOMIC); req = usb_ep_alloc_request(ep, GFP_ATOMIC);
if (req) { if (req) {
req->length = len ?: default_len; req->length = usb_endpoint_dir_out(ep->desc) ?
usb_ep_align(ep, len) : len;
req->buf = kmalloc(req->length, GFP_ATOMIC); req->buf = kmalloc(req->length, GFP_ATOMIC);
if (!req->buf) { if (!req->buf) {
usb_ep_free_request(ep, req); usb_ep_free_request(ep, req);

View file

@ -47,8 +47,21 @@
struct usb_ep; struct usb_ep;
struct usb_request; struct usb_request;
/* Requests allocated via alloc_ep_req() must be freed by free_ep_req(). */ /**
struct usb_request *alloc_ep_req(struct usb_ep *ep, int len, int default_len); * alloc_ep_req - returns a usb_request allocated by the gadget driver and
* allocates the request's buffer.
*
* @ep: the endpoint to allocate a usb_request
* @len: usb_requests's buffer suggested size
*
* In case @ep direction is OUT, the @len will be aligned to ep's
* wMaxPacketSize. In order to avoid memory leaks or drops, *always* use
* usb_requests's length (req->length) to refer to the allocated buffer size.
* Requests allocated via alloc_ep_req() *must* be freed by free_ep_req().
*/
struct usb_request *alloc_ep_req(struct usb_ep *ep, size_t len);
/* Frees a usb_request previously allocated by alloc_ep_req() */
static inline void free_ep_req(struct usb_ep *ep, struct usb_request *req) static inline void free_ep_req(struct usb_ep *ep, struct usb_request *req)
{ {
kfree(req->buf); kfree(req->buf);

View file

@ -107,10 +107,8 @@ int usb_ep_enable(struct usb_ep *ep)
goto out; goto out;
ret = ep->ops->enable(ep, ep->desc); ret = ep->ops->enable(ep, ep->desc);
if (ret) { if (ret)
ret = ret;
goto out; goto out;
}
ep->enabled = true; ep->enabled = true;

View file

@ -1838,6 +1838,8 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id)
err: err:
if (dev) if (dev)
goku_remove (pdev); goku_remove (pdev);
/* gadget_release is not registered yet, kfree explicitly */
kfree(dev);
return retval; return retval;
} }

View file

@ -589,7 +589,7 @@ static void net2280_free_request(struct usb_ep *_ep, struct usb_request *_req)
ep = container_of(_ep, struct net2280_ep, ep); ep = container_of(_ep, struct net2280_ep, ep);
if (!_ep || !_req) { if (!_ep || !_req) {
dev_err(&ep->dev->pdev->dev, "%s: Inavlid ep=%p or req=%p\n", dev_err(&ep->dev->pdev->dev, "%s: Invalid ep=%p or req=%p\n",
__func__, _ep, _req); __func__, _ep, _req);
return; return;
} }
@ -1137,8 +1137,10 @@ dma_done(struct net2280_ep *ep, struct net2280_request *req, u32 dmacount,
done(ep, req, status); done(ep, req, status);
} }
static void scan_dma_completions(struct net2280_ep *ep) static int scan_dma_completions(struct net2280_ep *ep)
{ {
int num_completed = 0;
/* only look at descriptors that were "naturally" retired, /* only look at descriptors that were "naturally" retired,
* so fifo and list head state won't matter * so fifo and list head state won't matter
*/ */
@ -1166,6 +1168,7 @@ static void scan_dma_completions(struct net2280_ep *ep)
break; break;
/* single transfer mode */ /* single transfer mode */
dma_done(ep, req, tmp, 0); dma_done(ep, req, tmp, 0);
num_completed++;
break; break;
} else if (!ep->is_in && } else if (!ep->is_in &&
(req->req.length % ep->ep.maxpacket) && (req->req.length % ep->ep.maxpacket) &&
@ -1194,7 +1197,10 @@ static void scan_dma_completions(struct net2280_ep *ep)
} }
} }
dma_done(ep, req, tmp, 0); dma_done(ep, req, tmp, 0);
num_completed++;
} }
return num_completed;
} }
static void restart_dma(struct net2280_ep *ep) static void restart_dma(struct net2280_ep *ep)
@ -1567,6 +1573,44 @@ static struct usb_ep *net2280_match_ep(struct usb_gadget *_gadget,
return ep; return ep;
} }
/* USB3380: Only first four endpoints have DMA channels. Allocate
* slower interrupt endpoints from PIO hw endpoints, to allow bulk/isoc
* endpoints use DMA hw endpoints.
*/
if (usb_endpoint_type(desc) == USB_ENDPOINT_XFER_INT &&
usb_endpoint_dir_in(desc)) {
ep = gadget_find_ep_by_name(_gadget, "ep2in");
if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
return ep;
ep = gadget_find_ep_by_name(_gadget, "ep4in");
if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
return ep;
} else if (usb_endpoint_type(desc) == USB_ENDPOINT_XFER_INT &&
!usb_endpoint_dir_in(desc)) {
ep = gadget_find_ep_by_name(_gadget, "ep1out");
if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
return ep;
ep = gadget_find_ep_by_name(_gadget, "ep3out");
if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
return ep;
} else if (usb_endpoint_type(desc) != USB_ENDPOINT_XFER_BULK &&
usb_endpoint_dir_in(desc)) {
ep = gadget_find_ep_by_name(_gadget, "ep1in");
if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
return ep;
ep = gadget_find_ep_by_name(_gadget, "ep3in");
if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
return ep;
} else if (usb_endpoint_type(desc) != USB_ENDPOINT_XFER_BULK &&
!usb_endpoint_dir_in(desc)) {
ep = gadget_find_ep_by_name(_gadget, "ep2out");
if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
return ep;
ep = gadget_find_ep_by_name(_gadget, "ep4out");
if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
return ep;
}
/* USB3380: use same address for usb and hardware endpoints */ /* USB3380: use same address for usb and hardware endpoints */
snprintf(name, sizeof(name), "ep%d%s", usb_endpoint_num(desc), snprintf(name, sizeof(name), "ep%d%s", usb_endpoint_num(desc),
usb_endpoint_dir_in(desc) ? "in" : "out"); usb_endpoint_dir_in(desc) ? "in" : "out");
@ -2547,8 +2591,11 @@ static void handle_ep_small(struct net2280_ep *ep)
/* manual DMA queue advance after short OUT */ /* manual DMA queue advance after short OUT */
if (likely(ep->dma)) { if (likely(ep->dma)) {
if (t & BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT)) { if (t & BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT)) {
u32 count; struct net2280_request *stuck_req = NULL;
int stopped = ep->stopped; int stopped = ep->stopped;
int num_completed;
int stuck = 0;
u32 count;
/* TRANSFERRED works around OUT_DONE erratum 0112. /* TRANSFERRED works around OUT_DONE erratum 0112.
* we expect (N <= maxpacket) bytes; host wrote M. * we expect (N <= maxpacket) bytes; host wrote M.
@ -2560,7 +2607,7 @@ static void handle_ep_small(struct net2280_ep *ep)
/* any preceding dma transfers must finish. /* any preceding dma transfers must finish.
* dma handles (M >= N), may empty the queue * dma handles (M >= N), may empty the queue
*/ */
scan_dma_completions(ep); num_completed = scan_dma_completions(ep);
if (unlikely(list_empty(&ep->queue) || if (unlikely(list_empty(&ep->queue) ||
ep->out_overflow)) { ep->out_overflow)) {
req = NULL; req = NULL;
@ -2580,6 +2627,31 @@ static void handle_ep_small(struct net2280_ep *ep)
req = NULL; req = NULL;
break; break;
} }
/* Escape loop if no dma transfers completed
* after few retries.
*/
if (num_completed == 0) {
if (stuck_req == req &&
readl(&ep->dma->dmadesc) !=
req->td_dma && stuck++ > 5) {
count = readl(
&ep->dma->dmacount);
count &= DMA_BYTE_COUNT_MASK;
req = NULL;
ep_dbg(ep->dev, "%s escape stuck %d, count %u\n",
ep->ep.name, stuck,
count);
break;
} else if (stuck_req != req) {
stuck_req = req;
stuck = 0;
}
} else {
stuck_req = NULL;
stuck = 0;
}
udelay(1); udelay(1);
} }

View file

@ -33,6 +33,7 @@
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/usb/ch9.h> #include <linux/usb/ch9.h>
#include <linux/usb/gadget.h> #include <linux/usb/gadget.h>
#include <linux/usb/phy.h>
#include "pxa27x_udc.h" #include "pxa27x_udc.h"
@ -1655,6 +1656,37 @@ static int pxa_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
/**
* pxa_udc_phy_event - Called by phy upon VBus event
* @nb: notifier block
* @action: phy action, is vbus connect or disconnect
* @data: the usb_gadget structure in pxa_udc
*
* Called by the USB Phy when a cable connect or disconnect is sensed.
*
* Returns 0
*/
static int pxa_udc_phy_event(struct notifier_block *nb, unsigned long action,
void *data)
{
struct usb_gadget *gadget = data;
switch (action) {
case USB_EVENT_VBUS:
usb_gadget_vbus_connect(gadget);
return NOTIFY_OK;
case USB_EVENT_NONE:
usb_gadget_vbus_disconnect(gadget);
return NOTIFY_OK;
default:
return NOTIFY_DONE;
}
}
static struct notifier_block pxa27x_udc_phy = {
.notifier_call = pxa_udc_phy_event,
};
static int pxa27x_udc_start(struct usb_gadget *g, static int pxa27x_udc_start(struct usb_gadget *g,
struct usb_gadget_driver *driver); struct usb_gadget_driver *driver);
static int pxa27x_udc_stop(struct usb_gadget *g); static int pxa27x_udc_stop(struct usb_gadget *g);
@ -2432,7 +2464,14 @@ static int pxa_udc_probe(struct platform_device *pdev)
return udc->irq; return udc->irq;
udc->dev = &pdev->dev; udc->dev = &pdev->dev;
udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (of_have_populated_dt()) {
udc->transceiver =
devm_usb_get_phy_by_phandle(udc->dev, "phys", 0);
if (IS_ERR(udc->transceiver))
return PTR_ERR(udc->transceiver);
} else {
udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
}
if (IS_ERR(udc->gpiod)) { if (IS_ERR(udc->gpiod)) {
dev_err(&pdev->dev, "Couldn't find or request D+ gpio : %ld\n", dev_err(&pdev->dev, "Couldn't find or request D+ gpio : %ld\n",
@ -2465,14 +2504,20 @@ static int pxa_udc_probe(struct platform_device *pdev)
goto err; goto err;
} }
if (!IS_ERR_OR_NULL(udc->transceiver))
usb_register_notifier(udc->transceiver, &pxa27x_udc_phy);
retval = usb_add_gadget_udc(&pdev->dev, &udc->gadget); retval = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
if (retval) if (retval)
goto err; goto err_add_gadget;
pxa_init_debugfs(udc); pxa_init_debugfs(udc);
if (should_enable_udc(udc)) if (should_enable_udc(udc))
udc_enable(udc); udc_enable(udc);
return 0; return 0;
err_add_gadget:
if (!IS_ERR_OR_NULL(udc->transceiver))
usb_unregister_notifier(udc->transceiver, &pxa27x_udc_phy);
err: err:
clk_unprepare(udc->clk); clk_unprepare(udc->clk);
return retval; return retval;
@ -2489,6 +2534,8 @@ static int pxa_udc_remove(struct platform_device *_dev)
usb_del_gadget_udc(&udc->gadget); usb_del_gadget_udc(&udc->gadget);
pxa_cleanup_debugfs(udc); pxa_cleanup_debugfs(udc);
if (!IS_ERR_OR_NULL(udc->transceiver))
usb_unregister_notifier(udc->transceiver, &pxa27x_udc_phy);
usb_put_phy(udc->transceiver); usb_put_phy(udc->transceiver);
udc->transceiver = NULL; udc->transceiver = NULL;

View file

@ -1248,7 +1248,7 @@ static void ab8500_usb_set_ab8500_tuning_values(struct ab8500_usb *ab)
err = abx500_set_register_interruptible(ab->dev, err = abx500_set_register_interruptible(ab->dev,
AB8500_DEBUG, AB8500_USB_PHY_TUNE3, 0x78); AB8500_DEBUG, AB8500_USB_PHY_TUNE3, 0x78);
if (err < 0) if (err < 0)
dev_err(ab->dev, "Failed to set PHY_TUNE3 regester err=%d\n", dev_err(ab->dev, "Failed to set PHY_TUNE3 register err=%d\n",
err); err);
/* Switch to normal mode/disable Bank 0x12 access */ /* Switch to normal mode/disable Bank 0x12 access */
@ -1290,7 +1290,7 @@ static void ab8500_usb_set_ab8505_tuning_values(struct ab8500_usb *ab)
0xFC, 0x80); 0xFC, 0x80);
if (err < 0) if (err < 0)
dev_err(ab->dev, "Failed to set PHY_TUNE3 regester err=%d\n", dev_err(ab->dev, "Failed to set PHY_TUNE3 register err=%d\n",
err); err);
/* Switch to normal mode/disable Bank 0x12 access */ /* Switch to normal mode/disable Bank 0x12 access */
@ -1321,7 +1321,7 @@ static void ab8500_usb_set_ab8540_tuning_values(struct ab8500_usb *ab)
err = abx500_set_register_interruptible(ab->dev, err = abx500_set_register_interruptible(ab->dev,
AB8540_DEBUG, AB8500_USB_PHY_TUNE3, 0x90); AB8540_DEBUG, AB8500_USB_PHY_TUNE3, 0x90);
if (err < 0) if (err < 0)
dev_err(ab->dev, "Failed to set PHY_TUNE3 regester ret=%d\n", dev_err(ab->dev, "Failed to set PHY_TUNE3 register ret=%d\n",
err); err);
} }
@ -1351,7 +1351,7 @@ static void ab8500_usb_set_ab9540_tuning_values(struct ab8500_usb *ab)
err = abx500_set_register_interruptible(ab->dev, err = abx500_set_register_interruptible(ab->dev,
AB8500_DEBUG, AB8500_USB_PHY_TUNE3, 0x80); AB8500_DEBUG, AB8500_USB_PHY_TUNE3, 0x80);
if (err < 0) if (err < 0)
dev_err(ab->dev, "Failed to set PHY_TUNE3 regester err=%d\n", dev_err(ab->dev, "Failed to set PHY_TUNE3 register err=%d\n",
err); err);
/* Switch to normal mode/disable Bank 0x12 access */ /* Switch to normal mode/disable Bank 0x12 access */

View file

@ -118,8 +118,6 @@ static irqreturn_t nop_gpio_vbus_thread(int irq, void *data)
status = USB_EVENT_VBUS; status = USB_EVENT_VBUS;
otg->state = OTG_STATE_B_PERIPHERAL; otg->state = OTG_STATE_B_PERIPHERAL;
nop->phy.last_event = status; nop->phy.last_event = status;
if (otg->gadget)
usb_gadget_vbus_connect(otg->gadget);
/* drawing a "unit load" is *always* OK, except for OTG */ /* drawing a "unit load" is *always* OK, except for OTG */
nop_set_vbus_draw(nop, 100); nop_set_vbus_draw(nop, 100);
@ -129,8 +127,6 @@ static irqreturn_t nop_gpio_vbus_thread(int irq, void *data)
} else { } else {
nop_set_vbus_draw(nop, 0); nop_set_vbus_draw(nop, 0);
if (otg->gadget)
usb_gadget_vbus_disconnect(otg->gadget);
status = USB_EVENT_NONE; status = USB_EVENT_NONE;
otg->state = OTG_STATE_B_IDLE; otg->state = OTG_STATE_B_IDLE;
nop->phy.last_event = status; nop->phy.last_event = status;
@ -191,7 +187,8 @@ static int nop_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget)
otg->gadget = gadget; otg->gadget = gadget;
if (otg->state == OTG_STATE_B_PERIPHERAL) if (otg->state == OTG_STATE_B_PERIPHERAL)
usb_gadget_vbus_connect(gadget); atomic_notifier_call_chain(&otg->usb_phy->notifier,
USB_EVENT_VBUS, otg->gadget);
else else
otg->state = OTG_STATE_B_IDLE; otg->state = OTG_STATE_B_IDLE;
return 0; return 0;
@ -326,6 +323,8 @@ static int usb_phy_generic_probe(struct platform_device *pdev)
gpiod_to_irq(nop->gpiod_vbus), err); gpiod_to_irq(nop->gpiod_vbus), err);
return err; return err;
} }
nop->phy.otg->state = gpiod_get_value(nop->gpiod_vbus) ?
OTG_STATE_B_PERIPHERAL : OTG_STATE_B_IDLE;
} }
nop->phy.init = usb_gen_phy_init; nop->phy.init = usb_gen_phy_init;

View file

@ -27,6 +27,7 @@
#define DRIVER_NAME "mxs_phy" #define DRIVER_NAME "mxs_phy"
#define HW_USBPHY_PWD 0x00 #define HW_USBPHY_PWD 0x00
#define HW_USBPHY_TX 0x10
#define HW_USBPHY_CTRL 0x30 #define HW_USBPHY_CTRL 0x30
#define HW_USBPHY_CTRL_SET 0x34 #define HW_USBPHY_CTRL_SET 0x34
#define HW_USBPHY_CTRL_CLR 0x38 #define HW_USBPHY_CTRL_CLR 0x38
@ -38,6 +39,10 @@
#define HW_USBPHY_IP_SET 0x94 #define HW_USBPHY_IP_SET 0x94
#define HW_USBPHY_IP_CLR 0x98 #define HW_USBPHY_IP_CLR 0x98
#define GM_USBPHY_TX_TXCAL45DP(x) (((x) & 0xf) << 16)
#define GM_USBPHY_TX_TXCAL45DN(x) (((x) & 0xf) << 8)
#define GM_USBPHY_TX_D_CAL(x) (((x) & 0xf) << 0)
#define BM_USBPHY_CTRL_SFTRST BIT(31) #define BM_USBPHY_CTRL_SFTRST BIT(31)
#define BM_USBPHY_CTRL_CLKGATE BIT(30) #define BM_USBPHY_CTRL_CLKGATE BIT(30)
#define BM_USBPHY_CTRL_OTG_ID_VALUE BIT(27) #define BM_USBPHY_CTRL_OTG_ID_VALUE BIT(27)
@ -115,6 +120,12 @@
*/ */
#define MXS_PHY_NEED_IP_FIX BIT(3) #define MXS_PHY_NEED_IP_FIX BIT(3)
/* Minimum and maximum values for device tree entries */
#define MXS_PHY_TX_CAL45_MIN 30
#define MXS_PHY_TX_CAL45_MAX 55
#define MXS_PHY_TX_D_CAL_MIN 79
#define MXS_PHY_TX_D_CAL_MAX 119
struct mxs_phy_data { struct mxs_phy_data {
unsigned int flags; unsigned int flags;
}; };
@ -164,6 +175,8 @@ struct mxs_phy {
const struct mxs_phy_data *data; const struct mxs_phy_data *data;
struct regmap *regmap_anatop; struct regmap *regmap_anatop;
int port_id; int port_id;
u32 tx_reg_set;
u32 tx_reg_mask;
}; };
static inline bool is_imx6q_phy(struct mxs_phy *mxs_phy) static inline bool is_imx6q_phy(struct mxs_phy *mxs_phy)
@ -185,6 +198,20 @@ static void mxs_phy_clock_switch_delay(void)
usleep_range(300, 400); usleep_range(300, 400);
} }
static void mxs_phy_tx_init(struct mxs_phy *mxs_phy)
{
void __iomem *base = mxs_phy->phy.io_priv;
u32 phytx;
/* Update TX register if there is anything to write */
if (mxs_phy->tx_reg_mask) {
phytx = readl(base + HW_USBPHY_TX);
phytx &= ~mxs_phy->tx_reg_mask;
phytx |= mxs_phy->tx_reg_set;
writel(phytx, base + HW_USBPHY_TX);
}
}
static int mxs_phy_hw_init(struct mxs_phy *mxs_phy) static int mxs_phy_hw_init(struct mxs_phy *mxs_phy)
{ {
int ret; int ret;
@ -214,6 +241,8 @@ static int mxs_phy_hw_init(struct mxs_phy *mxs_phy)
if (mxs_phy->data->flags & MXS_PHY_NEED_IP_FIX) if (mxs_phy->data->flags & MXS_PHY_NEED_IP_FIX)
writel(BM_USBPHY_IP_FIX, base + HW_USBPHY_IP_SET); writel(BM_USBPHY_IP_FIX, base + HW_USBPHY_IP_SET);
mxs_phy_tx_init(mxs_phy);
return 0; return 0;
} }
@ -459,6 +488,7 @@ static int mxs_phy_probe(struct platform_device *pdev)
int ret; int ret;
const struct of_device_id *of_id; const struct of_device_id *of_id;
struct device_node *np = pdev->dev.of_node; struct device_node *np = pdev->dev.of_node;
u32 val;
of_id = of_match_device(mxs_phy_dt_ids, &pdev->dev); of_id = of_match_device(mxs_phy_dt_ids, &pdev->dev);
if (!of_id) if (!of_id)
@ -491,6 +521,37 @@ static int mxs_phy_probe(struct platform_device *pdev)
} }
} }
/* Precompute which bits of the TX register are to be updated, if any */
if (!of_property_read_u32(np, "fsl,tx-cal-45-dn-ohms", &val) &&
val >= MXS_PHY_TX_CAL45_MIN && val <= MXS_PHY_TX_CAL45_MAX) {
/* Scale to a 4-bit value */
val = (MXS_PHY_TX_CAL45_MAX - val) * 0xF
/ (MXS_PHY_TX_CAL45_MAX - MXS_PHY_TX_CAL45_MIN);
mxs_phy->tx_reg_mask |= GM_USBPHY_TX_TXCAL45DN(~0);
mxs_phy->tx_reg_set |= GM_USBPHY_TX_TXCAL45DN(val);
}
if (!of_property_read_u32(np, "fsl,tx-cal-45-dp-ohms", &val) &&
val >= MXS_PHY_TX_CAL45_MIN && val <= MXS_PHY_TX_CAL45_MAX) {
/* Scale to a 4-bit value. */
val = (MXS_PHY_TX_CAL45_MAX - val) * 0xF
/ (MXS_PHY_TX_CAL45_MAX - MXS_PHY_TX_CAL45_MIN);
mxs_phy->tx_reg_mask |= GM_USBPHY_TX_TXCAL45DP(~0);
mxs_phy->tx_reg_set |= GM_USBPHY_TX_TXCAL45DP(val);
}
if (!of_property_read_u32(np, "fsl,tx-d-cal", &val) &&
val >= MXS_PHY_TX_D_CAL_MIN && val <= MXS_PHY_TX_D_CAL_MAX) {
/* Scale to a 4-bit value. Round up the values and heavily
* weight the rounding by adding 2/3 of the denominator.
*/
val = ((MXS_PHY_TX_D_CAL_MAX - val) * 0xF
+ (MXS_PHY_TX_D_CAL_MAX - MXS_PHY_TX_D_CAL_MIN) * 2/3)
/ (MXS_PHY_TX_D_CAL_MAX - MXS_PHY_TX_D_CAL_MIN);
mxs_phy->tx_reg_mask |= GM_USBPHY_TX_D_CAL(~0);
mxs_phy->tx_reg_set |= GM_USBPHY_TX_D_CAL(val);
}
ret = of_alias_get_id(np, "usbphy"); ret = of_alias_get_id(np, "usbphy");
if (ret < 0) if (ret < 0)
dev_dbg(&pdev->dev, "failed to get alias id, errno %d\n", ret); dev_dbg(&pdev->dev, "failed to get alias id, errno %d\n", ret);

View file

@ -481,6 +481,10 @@ static const struct of_device_id usbhs_of_match[] = {
.compatible = "renesas,usbhs-r8a7795", .compatible = "renesas,usbhs-r8a7795",
.data = (void *)USBHS_TYPE_RCAR_GEN3, .data = (void *)USBHS_TYPE_RCAR_GEN3,
}, },
{
.compatible = "renesas,usbhs-r8a7796",
.data = (void *)USBHS_TYPE_RCAR_GEN3,
},
{ {
.compatible = "renesas,rcar-gen2-usbhs", .compatible = "renesas,rcar-gen2-usbhs",
.data = (void *)USBHS_TYPE_RCAR_GEN2, .data = (void *)USBHS_TYPE_RCAR_GEN2,

View file

@ -1102,6 +1102,8 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv)
gpriv->gadget.name = "renesas_usbhs_udc"; gpriv->gadget.name = "renesas_usbhs_udc";
gpriv->gadget.ops = &usbhsg_gadget_ops; gpriv->gadget.ops = &usbhsg_gadget_ops;
gpriv->gadget.max_speed = USB_SPEED_HIGH; gpriv->gadget.max_speed = USB_SPEED_HIGH;
gpriv->gadget.quirk_avoids_skb_reserve = usbhs_get_dparam(priv,
has_usb_dmac);
INIT_LIST_HEAD(&gpriv->gadget.ep_list); INIT_LIST_HEAD(&gpriv->gadget.ep_list);

View file

@ -15,7 +15,7 @@ struct ulpi_ops;
*/ */
struct ulpi { struct ulpi {
struct ulpi_device_id id; struct ulpi_device_id id;
struct ulpi_ops *ops; const struct ulpi_ops *ops;
struct device dev; struct device dev;
}; };

View file

@ -4,20 +4,19 @@
#include <linux/types.h> #include <linux/types.h>
struct ulpi; struct ulpi;
struct device;
/** /**
* struct ulpi_ops - ULPI register access * struct ulpi_ops - ULPI register access
* @dev: the interface provider
* @read: read operation for ULPI register access * @read: read operation for ULPI register access
* @write: write operation for ULPI register access * @write: write operation for ULPI register access
*/ */
struct ulpi_ops { struct ulpi_ops {
struct device *dev; int (*read)(struct device *dev, u8 addr);
int (*read)(struct ulpi_ops *ops, u8 addr); int (*write)(struct device *dev, u8 addr, u8 val);
int (*write)(struct ulpi_ops *ops, u8 addr, u8 val);
}; };
struct ulpi *ulpi_register_interface(struct device *, struct ulpi_ops *); struct ulpi *ulpi_register_interface(struct device *, const struct ulpi_ops *);
void ulpi_unregister_interface(struct ulpi *); void ulpi_unregister_interface(struct ulpi *);
#endif /* __LINUX_ULPI_INTERFACE_H */ #endif /* __LINUX_ULPI_INTERFACE_H */

View file

@ -220,7 +220,8 @@ struct usb_function {
int (*setup)(struct usb_function *, int (*setup)(struct usb_function *,
const struct usb_ctrlrequest *); const struct usb_ctrlrequest *);
bool (*req_match)(struct usb_function *, bool (*req_match)(struct usb_function *,
const struct usb_ctrlrequest *); const struct usb_ctrlrequest *,
bool config0);
void (*suspend)(struct usb_function *); void (*suspend)(struct usb_function *);
void (*resume)(struct usb_function *); void (*resume)(struct usb_function *);

View file

@ -346,6 +346,8 @@ struct usb_gadget_ops {
* or B-Peripheral wants to take host role. * or B-Peripheral wants to take host role.
* @quirk_ep_out_aligned_size: epout requires buffer size to be aligned to * @quirk_ep_out_aligned_size: epout requires buffer size to be aligned to
* MaxPacketSize. * MaxPacketSize.
* @quirk_avoids_skb_reserve: udc/platform wants to avoid skb_reserve() in
* u_ether.c to improve performance.
* @is_selfpowered: if the gadget is self-powered. * @is_selfpowered: if the gadget is self-powered.
* @deactivated: True if gadget is deactivated - in deactivated state it cannot * @deactivated: True if gadget is deactivated - in deactivated state it cannot
* be connected. * be connected.
@ -398,6 +400,7 @@ struct usb_gadget {
unsigned quirk_altset_not_supp:1; unsigned quirk_altset_not_supp:1;
unsigned quirk_stall_not_supp:1; unsigned quirk_stall_not_supp:1;
unsigned quirk_zlp_not_supp:1; unsigned quirk_zlp_not_supp:1;
unsigned quirk_avoids_skb_reserve:1;
unsigned is_selfpowered:1; unsigned is_selfpowered:1;
unsigned deactivated:1; unsigned deactivated:1;
unsigned connected:1; unsigned connected:1;
@ -417,9 +420,21 @@ static inline struct usb_gadget *dev_to_usb_gadget(struct device *dev)
#define gadget_for_each_ep(tmp, gadget) \ #define gadget_for_each_ep(tmp, gadget) \
list_for_each_entry(tmp, &(gadget)->ep_list, ep_list) list_for_each_entry(tmp, &(gadget)->ep_list, ep_list)
/**
* usb_ep_align - returns @len aligned to ep's maxpacketsize.
* @ep: the endpoint whose maxpacketsize is used to align @len
* @len: buffer size's length to align to @ep's maxpacketsize
*
* This helper is used to align buffer's size to an ep's maxpacketsize.
*/
static inline size_t usb_ep_align(struct usb_ep *ep, size_t len)
{
return round_up(len, (size_t)le16_to_cpu(ep->desc->wMaxPacketSize));
}
/** /**
* usb_ep_align_maybe - returns @len aligned to ep's maxpacketsize if gadget * usb_ep_align_maybe - returns @len aligned to ep's maxpacketsize if gadget
* requires quirk_ep_out_aligned_size, otherwise reguens len. * requires quirk_ep_out_aligned_size, otherwise returns len.
* @g: controller to check for quirk * @g: controller to check for quirk
* @ep: the endpoint whose maxpacketsize is used to align @len * @ep: the endpoint whose maxpacketsize is used to align @len
* @len: buffer size's length to align to @ep's maxpacketsize * @len: buffer size's length to align to @ep's maxpacketsize
@ -430,8 +445,7 @@ static inline struct usb_gadget *dev_to_usb_gadget(struct device *dev)
static inline size_t static inline size_t
usb_ep_align_maybe(struct usb_gadget *g, struct usb_ep *ep, size_t len) usb_ep_align_maybe(struct usb_gadget *g, struct usb_ep *ep, size_t len)
{ {
return !g->quirk_ep_out_aligned_size ? len : return g->quirk_ep_out_aligned_size ? usb_ep_align(ep, len) : len;
round_up(len, (size_t)ep->desc->wMaxPacketSize);
} }
/** /**
@ -462,6 +476,16 @@ static inline int gadget_is_zlp_supported(struct usb_gadget *g)
return !g->quirk_zlp_not_supp; return !g->quirk_zlp_not_supp;
} }
/**
* gadget_avoids_skb_reserve - return true iff the hardware would like to avoid
* skb_reserve to improve performance.
* @g: controller to check for quirk
*/
static inline int gadget_avoids_skb_reserve(struct usb_gadget *g)
{
return g->quirk_avoids_skb_reserve;
}
/** /**
* gadget_is_dualspeed - return true iff the hardware handles high speed * gadget_is_dualspeed - return true iff the hardware handles high speed
* @g: controller that might support both high and full speeds * @g: controller that might support both high and full speeds

View file

@ -21,6 +21,8 @@ enum functionfs_flags {
FUNCTIONFS_HAS_MS_OS_DESC = 8, FUNCTIONFS_HAS_MS_OS_DESC = 8,
FUNCTIONFS_VIRTUAL_ADDR = 16, FUNCTIONFS_VIRTUAL_ADDR = 16,
FUNCTIONFS_EVENTFD = 32, FUNCTIONFS_EVENTFD = 32,
FUNCTIONFS_ALL_CTRL_RECIP = 64,
FUNCTIONFS_CONFIG0_SETUP = 128,
}; };
/* Descriptor of an non-audio endpoint */ /* Descriptor of an non-audio endpoint */