mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-12 08:09:56 +00:00
usb: patches for v4.5
A ton of improvements to dwc2 have been made. The driver should be a lot more stable on v4.5 then ever before. Our good old dwc3 got a few cleanups and misc fixes and also added support to Xilinx's integration of this IP. Yoshihiro Shimoda gives us support for a new USB3 peripheral controller from Renesas. Other than these, the usual misc fixes all over the place. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWes7RAAoJEIaOsuA1yqREC5cP/jzIWxyHj5rNPr8VGnjzd4Wh wcsxGmTLHA+c5FFer0+hnj8DXSELyYV2Lcfng1pkPuGWAsPzdd9FvNV/9Q1RmqPG R96JB9N3dnDY7erJZkmpDOJvGg+p/Ec6GmFkrTVWQeXTwlkI1mdvaZwV9iFQfmb0 V/gdQWdlmbTarmwPLxEpaDN+JDB9Gm6RdyK/B5pC8ZeF9S42aQIFCrfOfjP3OiZj o5Q6Gv2YxElSKscObLFyltfgb05YD/yO0al/wm7p0lGXdsaMMokjyxrbibnoFNic Z5lexn8DqJ/yjy9w5K3Tj+b4nSfokUibUB3r0Cwbv6di7g0gUNclUwg79X8tSoDS T2hkpmpAIvb2z80wlLFpGptfC39JoLtzAkxfBbnu025QxFeI4AknzQuXiW8UgvSO /t1Aq6jg3j+GTuU+BiBnLjEPBZSsh/b7qIpreaXIIb7A01e2T7QT5i8kB7Zk5d5F 4aLBH/QHHXS+RGJmY9qgW6wee0XcaCysCUctwVuPY1fGukPrIFxjhPnJtjjqKnW7 41RCTwv2DKmm6CV5xVXYfSMVkmWBrNlpjugZkYH9yRbAzwPA/H/A5iPJwoOnGshn VNuFJOBKJJFR0u072LidgMWZ7AaeBmWnageAU1sFr8iXWiWESwaAjI4ouPNVugPI PxYdhPlc+lHBPXDrPh8p =8vM4 -----END PGP SIGNATURE----- Merge tag 'usb-for-v4.5' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next Felipe writes: usb: patches for v4.5 A ton of improvements to dwc2 have been made. The driver should be a lot more stable on v4.5 then ever before. Our good old dwc3 got a few cleanups and misc fixes and also added support to Xilinx's integration of this IP. Yoshihiro Shimoda gives us support for a new USB3 peripheral controller from Renesas. Other than these, the usual misc fixes all over the place.
This commit is contained in:
commit
87cf5586fd
@ -10,3 +10,5 @@ Description:
|
||||
isoc_mult - 0..2 (hs/ss only)
|
||||
isoc_maxburst - 0..15 (ss only)
|
||||
buflen - buffer length
|
||||
bulk_qlen - depth of queue for bulk
|
||||
iso_qlen - depth of queue for iso
|
||||
|
@ -4,6 +4,7 @@ Platform DesignWare HS OTG USB 2.0 controller
|
||||
Required properties:
|
||||
- compatible : One of:
|
||||
- brcm,bcm2835-usb: The DWC2 USB controller instance in the BCM2835 SoC.
|
||||
- hisilicon,hi6220-usb: The DWC2 USB controller instance in the hi6220 SoC.
|
||||
- rockchip,rk3066-usb: The DWC2 USB controller instance in the rk3066 Soc;
|
||||
- "rockchip,rk3188-usb", "rockchip,rk3066-usb", "snps,dwc2": for rk3188 Soc;
|
||||
- "rockchip,rk3288-usb", "rockchip,rk3066-usb", "snps,dwc2": for rk3288 Soc;
|
||||
|
33
Documentation/devicetree/bindings/usb/dwc3-xilinx.txt
Normal file
33
Documentation/devicetree/bindings/usb/dwc3-xilinx.txt
Normal file
@ -0,0 +1,33 @@
|
||||
Xilinx SuperSpeed DWC3 USB SoC controller
|
||||
|
||||
Required properties:
|
||||
- compatible: Should contain "xlnx,zynqmp-dwc3"
|
||||
- clocks: A list of phandles for the clocks listed in clock-names
|
||||
- clock-names: Should contain the following:
|
||||
"bus_clk" Master/Core clock, have to be >= 125 MHz for SS
|
||||
operation and >= 60MHz for HS operation
|
||||
|
||||
"ref_clk" Clock source to core during PHY power down
|
||||
|
||||
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:
|
||||
|
||||
usb@0 {
|
||||
#address-cells = <0x2>;
|
||||
#size-cells = <0x1>;
|
||||
status = "okay";
|
||||
compatible = "xlnx,zynqmp-dwc3";
|
||||
clock-names = "bus_clk" "ref_clk";
|
||||
clocks = <&clk125>, <&clk125>;
|
||||
ranges;
|
||||
|
||||
dwc3@fe200000 {
|
||||
compatible = "snps,dwc3";
|
||||
reg = <0x0 0xfe200000 0x40000>;
|
||||
interrupts = <0x0 0x41 0x4>;
|
||||
dr_mode = "host";
|
||||
};
|
||||
};
|
23
Documentation/devicetree/bindings/usb/renesas_usb3.txt
Normal file
23
Documentation/devicetree/bindings/usb/renesas_usb3.txt
Normal file
@ -0,0 +1,23 @@
|
||||
Renesas Electronics USB3.0 Peripheral driver
|
||||
|
||||
Required properties:
|
||||
- compatible: Must contain one of the following:
|
||||
- "renesas,r8a7795-usb3-peri"
|
||||
- reg: Base address and length of the register for the USB3.0 Peripheral
|
||||
- interrupts: Interrupt specifier for the USB3.0 Peripheral
|
||||
- clocks: clock phandle and specifier pair
|
||||
|
||||
Example:
|
||||
usb3_peri0: usb@ee020000 {
|
||||
compatible = "renesas,r8a7795-usb3-peri";
|
||||
reg = <0 0xee020000 0 0x400>;
|
||||
interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&cpg CPG_MOD 328>;
|
||||
};
|
||||
|
||||
usb3_peri1: usb@ee060000 {
|
||||
compatible = "renesas,r8a7795-usb3-peri";
|
||||
reg = <0 0xee060000 0 0x400>;
|
||||
interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&cpg CPG_MOD 327>;
|
||||
};
|
@ -1,11 +1,21 @@
|
||||
Renesas Electronics USBHS driver
|
||||
|
||||
Required properties:
|
||||
- compatible: Must contain one of the following:
|
||||
- "renesas,usbhs-r8a7790"
|
||||
- "renesas,usbhs-r8a7791"
|
||||
- "renesas,usbhs-r8a7794"
|
||||
- "renesas,usbhs-r8a7795"
|
||||
- compatible: Must contain one or more of the following:
|
||||
|
||||
- "renesas,usbhs-r8a7790" for r8a7790 (R-Car H2) compatible device
|
||||
- "renesas,usbhs-r8a7791" for r8a7791 (R-Car M2-W) compatible device
|
||||
- "renesas,usbhs-r8a7792" for r8a7792 (R-Car V2H) 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-r8a7795" for r8a7795 (R-Car H3) compatible device
|
||||
- "renesas,rcar-gen2-usbhs" for R-Car Gen2 compatible device
|
||||
- "renesas,rcar-gen3-usbhs" for R-Car Gen3 compatible device
|
||||
|
||||
When compatible with the generic version, nodes must list the
|
||||
SoC-specific version corresponding to the platform first followed
|
||||
by the generic version.
|
||||
|
||||
- reg: Base address and length of the register for the USBHS
|
||||
- interrupts: Interrupt specifier for the USBHS
|
||||
- clocks: A list of phandle + clock specifier pairs
|
||||
@ -22,7 +32,7 @@ Optional properties:
|
||||
|
||||
Example:
|
||||
usbhs: usb@e6590000 {
|
||||
compatible = "renesas,usbhs-r8a7790";
|
||||
compatible = "renesas,usbhs-r8a7790", "renesas,rcar-gen2-usbhs";
|
||||
reg = <0 0xe6590000 0 0x100>;
|
||||
interrupts = <0 107 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&mstp7_clks R8A7790_CLK_HSUSB>;
|
||||
|
@ -434,7 +434,7 @@ On host: serialc -v <vendorID> -p <productID> -i<interface#> -a1 -s1024 \
|
||||
|
||||
where seriald and serialc are Felipe's utilities found here:
|
||||
|
||||
https://git.gitorious.org/usb/usb-tools.git master
|
||||
https://github.com/felipebalbi/usb-tools.git master
|
||||
|
||||
12. PHONET function
|
||||
===================
|
||||
@ -579,6 +579,8 @@ The SOURCESINK function provides these attributes in its function directory:
|
||||
isoc_mult - 0..2 (hs/ss only)
|
||||
isoc_maxburst - 0..15 (ss only)
|
||||
bulk_buflen - buffer length
|
||||
bulk_qlen - depth of queue for bulk
|
||||
iso_qlen - depth of queue for iso
|
||||
|
||||
Testing the SOURCESINK function
|
||||
-------------------------------
|
||||
|
@ -34,7 +34,7 @@
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/usb/musb-omap.h>
|
||||
#include <linux/usb/musb.h>
|
||||
#include <linux/usb/ulpi.h>
|
||||
#include <linux/i2c/twl.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
@ -148,10 +148,10 @@
|
||||
* If VBUS is valid or ID is ground, then we know a
|
||||
* cable is present and we need to be runtime-enabled
|
||||
*/
|
||||
static inline bool cable_present(enum omap_musb_vbus_id_status stat)
|
||||
static inline bool cable_present(enum musb_vbus_id_status stat)
|
||||
{
|
||||
return stat == OMAP_MUSB_VBUS_VALID ||
|
||||
stat == OMAP_MUSB_ID_GROUND;
|
||||
return stat == MUSB_VBUS_VALID ||
|
||||
stat == MUSB_ID_GROUND;
|
||||
}
|
||||
|
||||
struct twl4030_usb {
|
||||
@ -170,7 +170,7 @@ struct twl4030_usb {
|
||||
enum twl4030_usb_mode usb_mode;
|
||||
|
||||
int irq;
|
||||
enum omap_musb_vbus_id_status linkstat;
|
||||
enum musb_vbus_id_status linkstat;
|
||||
bool vbus_supplied;
|
||||
|
||||
struct delayed_work id_workaround_work;
|
||||
@ -276,11 +276,11 @@ static bool twl4030_is_driving_vbus(struct twl4030_usb *twl)
|
||||
return (ret & (ULPI_OTG_DRVVBUS | ULPI_OTG_CHRGVBUS)) ? true : false;
|
||||
}
|
||||
|
||||
static enum omap_musb_vbus_id_status
|
||||
static enum musb_vbus_id_status
|
||||
twl4030_usb_linkstat(struct twl4030_usb *twl)
|
||||
{
|
||||
int status;
|
||||
enum omap_musb_vbus_id_status linkstat = OMAP_MUSB_UNKNOWN;
|
||||
enum musb_vbus_id_status linkstat = MUSB_UNKNOWN;
|
||||
|
||||
twl->vbus_supplied = false;
|
||||
|
||||
@ -306,14 +306,14 @@ static enum omap_musb_vbus_id_status
|
||||
}
|
||||
|
||||
if (status & BIT(2))
|
||||
linkstat = OMAP_MUSB_ID_GROUND;
|
||||
linkstat = MUSB_ID_GROUND;
|
||||
else if (status & BIT(7))
|
||||
linkstat = OMAP_MUSB_VBUS_VALID;
|
||||
linkstat = MUSB_VBUS_VALID;
|
||||
else
|
||||
linkstat = OMAP_MUSB_VBUS_OFF;
|
||||
linkstat = MUSB_VBUS_OFF;
|
||||
} else {
|
||||
if (twl->linkstat != OMAP_MUSB_UNKNOWN)
|
||||
linkstat = OMAP_MUSB_VBUS_OFF;
|
||||
if (twl->linkstat != MUSB_UNKNOWN)
|
||||
linkstat = MUSB_VBUS_OFF;
|
||||
}
|
||||
|
||||
dev_dbg(twl->dev, "HW_CONDITIONS 0x%02x/%d; link %d\n",
|
||||
@ -535,7 +535,7 @@ static DEVICE_ATTR(vbus, 0444, twl4030_usb_vbus_show, NULL);
|
||||
static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
|
||||
{
|
||||
struct twl4030_usb *twl = _twl;
|
||||
enum omap_musb_vbus_id_status status;
|
||||
enum musb_vbus_id_status status;
|
||||
bool status_changed = false;
|
||||
|
||||
status = twl4030_usb_linkstat(twl);
|
||||
@ -567,11 +567,11 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
|
||||
pm_runtime_mark_last_busy(twl->dev);
|
||||
pm_runtime_put_autosuspend(twl->dev);
|
||||
}
|
||||
omap_musb_mailbox(status);
|
||||
musb_mailbox(status);
|
||||
}
|
||||
|
||||
/* don't schedule during sleep - irq works right then */
|
||||
if (status == OMAP_MUSB_ID_GROUND && pm_runtime_active(twl->dev)) {
|
||||
if (status == MUSB_ID_GROUND && pm_runtime_active(twl->dev)) {
|
||||
cancel_delayed_work(&twl->id_workaround_work);
|
||||
schedule_delayed_work(&twl->id_workaround_work, HZ);
|
||||
}
|
||||
@ -670,7 +670,7 @@ static int twl4030_usb_probe(struct platform_device *pdev)
|
||||
twl->dev = &pdev->dev;
|
||||
twl->irq = platform_get_irq(pdev, 0);
|
||||
twl->vbus_supplied = false;
|
||||
twl->linkstat = OMAP_MUSB_UNKNOWN;
|
||||
twl->linkstat = MUSB_UNKNOWN;
|
||||
|
||||
twl->phy.dev = twl->dev;
|
||||
twl->phy.label = "twl4030";
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/of.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
const char *usb_otg_state_string(enum usb_otg_state state)
|
||||
{
|
||||
@ -106,24 +107,71 @@ static const char *const usb_dr_modes[] = {
|
||||
[USB_DR_MODE_OTG] = "otg",
|
||||
};
|
||||
|
||||
static enum usb_dr_mode usb_get_dr_mode_from_string(const char *str)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(usb_dr_modes); i++)
|
||||
if (!strcmp(usb_dr_modes[i], str))
|
||||
return i;
|
||||
|
||||
return USB_DR_MODE_UNKNOWN;
|
||||
}
|
||||
|
||||
enum usb_dr_mode usb_get_dr_mode(struct device *dev)
|
||||
{
|
||||
const char *dr_mode;
|
||||
int err, i;
|
||||
int err;
|
||||
|
||||
err = device_property_read_string(dev, "dr_mode", &dr_mode);
|
||||
if (err < 0)
|
||||
return USB_DR_MODE_UNKNOWN;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(usb_dr_modes); i++)
|
||||
if (!strcmp(dr_mode, usb_dr_modes[i]))
|
||||
return i;
|
||||
|
||||
return USB_DR_MODE_UNKNOWN;
|
||||
return usb_get_dr_mode_from_string(dr_mode);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_get_dr_mode);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
/**
|
||||
* of_usb_get_dr_mode_by_phy - Get dual role mode for the controller device
|
||||
* which is associated with the given phy device_node
|
||||
* @np: Pointer to the given phy device_node
|
||||
*
|
||||
* In dts a usb controller associates with phy devices. The function gets
|
||||
* the string from property 'dr_mode' of the controller associated with the
|
||||
* given phy device node, and returns the correspondig enum usb_dr_mode.
|
||||
*/
|
||||
enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *phy_np)
|
||||
{
|
||||
struct device_node *controller = NULL;
|
||||
struct device_node *phy;
|
||||
const char *dr_mode;
|
||||
int index;
|
||||
int err;
|
||||
|
||||
do {
|
||||
controller = of_find_node_with_property(controller, "phys");
|
||||
index = 0;
|
||||
do {
|
||||
phy = of_parse_phandle(controller, "phys", index);
|
||||
of_node_put(phy);
|
||||
if (phy == phy_np)
|
||||
goto finish;
|
||||
index++;
|
||||
} while (phy);
|
||||
} while (controller);
|
||||
|
||||
finish:
|
||||
err = of_property_read_string(controller, "dr_mode", &dr_mode);
|
||||
of_node_put(controller);
|
||||
|
||||
if (err < 0)
|
||||
return USB_DR_MODE_UNKNOWN;
|
||||
|
||||
return usb_get_dr_mode_from_string(dr_mode);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_usb_get_dr_mode_by_phy);
|
||||
|
||||
/**
|
||||
* of_usb_host_tpl_support - to get if Targeted Peripheral List is supported
|
||||
* for given targeted hosts (non-PC hosts)
|
||||
|
@ -481,32 +481,19 @@ static void dwc2_init_fs_ls_pclk_sel(struct dwc2_hsotg *hsotg)
|
||||
* Do core a soft reset of the core. Be careful with this because it
|
||||
* resets all the internal state machines of the core.
|
||||
*/
|
||||
static int dwc2_core_reset(struct dwc2_hsotg *hsotg)
|
||||
int dwc2_core_reset(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 greset;
|
||||
int count = 0;
|
||||
u32 gusbcfg;
|
||||
|
||||
dev_vdbg(hsotg->dev, "%s()\n", __func__);
|
||||
|
||||
/* Wait for AHB master IDLE state */
|
||||
do {
|
||||
usleep_range(20000, 40000);
|
||||
greset = dwc2_readl(hsotg->regs + GRSTCTL);
|
||||
if (++count > 50) {
|
||||
dev_warn(hsotg->dev,
|
||||
"%s() HANG! AHB Idle GRSTCTL=%0x\n",
|
||||
__func__, greset);
|
||||
return -EBUSY;
|
||||
}
|
||||
} while (!(greset & GRSTCTL_AHBIDLE));
|
||||
|
||||
/* Core Soft Reset */
|
||||
count = 0;
|
||||
greset = dwc2_readl(hsotg->regs + GRSTCTL);
|
||||
greset |= GRSTCTL_CSFTRST;
|
||||
dwc2_writel(greset, hsotg->regs + GRSTCTL);
|
||||
do {
|
||||
usleep_range(20000, 40000);
|
||||
udelay(1);
|
||||
greset = dwc2_readl(hsotg->regs + GRSTCTL);
|
||||
if (++count > 50) {
|
||||
dev_warn(hsotg->dev,
|
||||
@ -516,29 +503,146 @@ static int dwc2_core_reset(struct dwc2_hsotg *hsotg)
|
||||
}
|
||||
} while (greset & GRSTCTL_CSFTRST);
|
||||
|
||||
if (hsotg->dr_mode == USB_DR_MODE_HOST) {
|
||||
gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
|
||||
gusbcfg &= ~GUSBCFG_FORCEDEVMODE;
|
||||
gusbcfg |= GUSBCFG_FORCEHOSTMODE;
|
||||
dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
|
||||
} else if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) {
|
||||
gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
|
||||
gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
|
||||
gusbcfg |= GUSBCFG_FORCEDEVMODE;
|
||||
dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
|
||||
} else if (hsotg->dr_mode == USB_DR_MODE_OTG) {
|
||||
gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
|
||||
gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
|
||||
gusbcfg &= ~GUSBCFG_FORCEDEVMODE;
|
||||
dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
|
||||
}
|
||||
/* Wait for AHB master IDLE state */
|
||||
count = 0;
|
||||
do {
|
||||
udelay(1);
|
||||
greset = dwc2_readl(hsotg->regs + GRSTCTL);
|
||||
if (++count > 50) {
|
||||
dev_warn(hsotg->dev,
|
||||
"%s() HANG! AHB Idle GRSTCTL=%0x\n",
|
||||
__func__, greset);
|
||||
return -EBUSY;
|
||||
}
|
||||
} while (!(greset & GRSTCTL_AHBIDLE));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Force the mode of the controller.
|
||||
*
|
||||
* Forcing the mode is needed for two cases:
|
||||
*
|
||||
* 1) If the dr_mode is set to either HOST or PERIPHERAL we force the
|
||||
* controller to stay in a particular mode regardless of ID pin
|
||||
* changes. We do this usually after a core reset.
|
||||
*
|
||||
* 2) During probe we want to read reset values of the hw
|
||||
* configuration registers that are only available in either host or
|
||||
* device mode. We may need to force the mode if the current mode does
|
||||
* not allow us to access the register in the mode that we want.
|
||||
*
|
||||
* In either case it only makes sense to force the mode if the
|
||||
* controller hardware is OTG capable.
|
||||
*
|
||||
* Checks are done in this function to determine whether doing a force
|
||||
* would be valid or not.
|
||||
*
|
||||
* If a force is done, it requires a 25ms delay to take effect.
|
||||
*
|
||||
* Returns true if the mode was forced.
|
||||
*/
|
||||
static bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
|
||||
{
|
||||
u32 gusbcfg;
|
||||
u32 set;
|
||||
u32 clear;
|
||||
|
||||
dev_dbg(hsotg->dev, "Forcing mode to %s\n", host ? "host" : "device");
|
||||
|
||||
/*
|
||||
* Force mode has no effect if the hardware is not OTG.
|
||||
*/
|
||||
if (!dwc2_hw_is_otg(hsotg))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* If dr_mode is either peripheral or host only, there is no
|
||||
* need to ever force the mode to the opposite mode.
|
||||
*/
|
||||
if (WARN_ON(host && hsotg->dr_mode == USB_DR_MODE_PERIPHERAL))
|
||||
return false;
|
||||
|
||||
if (WARN_ON(!host && hsotg->dr_mode == USB_DR_MODE_HOST))
|
||||
return false;
|
||||
|
||||
gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
|
||||
|
||||
set = host ? GUSBCFG_FORCEHOSTMODE : GUSBCFG_FORCEDEVMODE;
|
||||
clear = host ? GUSBCFG_FORCEDEVMODE : GUSBCFG_FORCEHOSTMODE;
|
||||
|
||||
/*
|
||||
* If the force mode bit is already set, don't set it.
|
||||
*/
|
||||
if ((gusbcfg & set) && !(gusbcfg & clear))
|
||||
return false;
|
||||
|
||||
gusbcfg &= ~clear;
|
||||
gusbcfg |= set;
|
||||
dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
|
||||
|
||||
msleep(25);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clears the force mode bits.
|
||||
*/
|
||||
static void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 gusbcfg;
|
||||
|
||||
gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
|
||||
gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
|
||||
gusbcfg &= ~GUSBCFG_FORCEDEVMODE;
|
||||
dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
|
||||
|
||||
/*
|
||||
* NOTE: This long sleep is _very_ important, otherwise the core will
|
||||
* not stay in host mode after a connector ID change!
|
||||
*/
|
||||
usleep_range(150000, 200000);
|
||||
msleep(25);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets or clears force mode based on the dr_mode parameter.
|
||||
*/
|
||||
void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
switch (hsotg->dr_mode) {
|
||||
case USB_DR_MODE_HOST:
|
||||
dwc2_force_mode(hsotg, true);
|
||||
break;
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
dwc2_force_mode(hsotg, false);
|
||||
break;
|
||||
case USB_DR_MODE_OTG:
|
||||
dwc2_clear_force_mode(hsotg);
|
||||
break;
|
||||
default:
|
||||
dev_warn(hsotg->dev, "%s() Invalid dr_mode=%d\n",
|
||||
__func__, hsotg->dr_mode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Do core a soft reset of the core. Be careful with this because it
|
||||
* resets all the internal state machines of the core.
|
||||
*
|
||||
* Additionally this will apply force mode as per the hsotg->dr_mode
|
||||
* parameter.
|
||||
*/
|
||||
int dwc2_core_reset_and_force_dr_mode(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = dwc2_core_reset(hsotg);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
dwc2_force_dr_mode(hsotg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -553,16 +657,20 @@ static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
|
||||
*/
|
||||
if (select_phy) {
|
||||
dev_dbg(hsotg->dev, "FS PHY selected\n");
|
||||
usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
|
||||
usbcfg |= GUSBCFG_PHYSEL;
|
||||
dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
|
||||
|
||||
/* Reset after a PHY select */
|
||||
retval = dwc2_core_reset(hsotg);
|
||||
if (retval) {
|
||||
dev_err(hsotg->dev, "%s() Reset failed, aborting",
|
||||
__func__);
|
||||
return retval;
|
||||
usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
|
||||
if (!(usbcfg & GUSBCFG_PHYSEL)) {
|
||||
usbcfg |= GUSBCFG_PHYSEL;
|
||||
dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
|
||||
|
||||
/* Reset after a PHY select */
|
||||
retval = dwc2_core_reset_and_force_dr_mode(hsotg);
|
||||
|
||||
if (retval) {
|
||||
dev_err(hsotg->dev,
|
||||
"%s: Reset failed, aborting", __func__);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -597,13 +705,13 @@ static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
|
||||
|
||||
static int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
|
||||
{
|
||||
u32 usbcfg;
|
||||
u32 usbcfg, usbcfg_old;
|
||||
int retval = 0;
|
||||
|
||||
if (!select_phy)
|
||||
return 0;
|
||||
|
||||
usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
|
||||
usbcfg = usbcfg_old = dwc2_readl(hsotg->regs + GUSBCFG);
|
||||
|
||||
/*
|
||||
* HS PHY parameters. These parameters are preserved during soft reset
|
||||
@ -631,14 +739,16 @@ static int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
|
||||
break;
|
||||
}
|
||||
|
||||
dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
|
||||
if (usbcfg != usbcfg_old) {
|
||||
dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
|
||||
|
||||
/* Reset after setting the PHY parameters */
|
||||
retval = dwc2_core_reset(hsotg);
|
||||
if (retval) {
|
||||
dev_err(hsotg->dev, "%s() Reset failed, aborting",
|
||||
__func__);
|
||||
return retval;
|
||||
/* Reset after setting the PHY parameters */
|
||||
retval = dwc2_core_reset_and_force_dr_mode(hsotg);
|
||||
if (retval) {
|
||||
dev_err(hsotg->dev,
|
||||
"%s: Reset failed, aborting", __func__);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
@ -765,11 +875,10 @@ static void dwc2_gusbcfg_init(struct dwc2_hsotg *hsotg)
|
||||
* dwc2_core_init() - Initializes the DWC_otg controller registers and
|
||||
* prepares the core for device mode or host mode operation
|
||||
*
|
||||
* @hsotg: Programming view of the DWC_otg controller
|
||||
* @select_phy: If true then also set the Phy type
|
||||
* @irq: If >= 0, the irq to register
|
||||
* @hsotg: Programming view of the DWC_otg controller
|
||||
* @initial_setup: If true then this is the first init for this instance.
|
||||
*/
|
||||
int dwc2_core_init(struct dwc2_hsotg *hsotg, bool select_phy, int irq)
|
||||
int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup)
|
||||
{
|
||||
u32 usbcfg, otgctl;
|
||||
int retval;
|
||||
@ -791,18 +900,26 @@ int dwc2_core_init(struct dwc2_hsotg *hsotg, bool select_phy, int irq)
|
||||
|
||||
dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
|
||||
|
||||
/* Reset the Controller */
|
||||
retval = dwc2_core_reset(hsotg);
|
||||
if (retval) {
|
||||
dev_err(hsotg->dev, "%s(): Reset failed, aborting\n",
|
||||
__func__);
|
||||
return retval;
|
||||
/*
|
||||
* Reset the Controller
|
||||
*
|
||||
* We only need to reset the controller if this is a re-init.
|
||||
* For the first init we know for sure that earlier code reset us (it
|
||||
* needed to in order to properly detect various parameters).
|
||||
*/
|
||||
if (!initial_setup) {
|
||||
retval = dwc2_core_reset_and_force_dr_mode(hsotg);
|
||||
if (retval) {
|
||||
dev_err(hsotg->dev, "%s(): Reset failed, aborting\n",
|
||||
__func__);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This needs to happen in FS mode before any other programming occurs
|
||||
*/
|
||||
retval = dwc2_phy_init(hsotg, select_phy);
|
||||
retval = dwc2_phy_init(hsotg, initial_setup);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
@ -1707,6 +1824,7 @@ void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
|
||||
u32 hcchar;
|
||||
u32 hctsiz = 0;
|
||||
u16 num_packets;
|
||||
u32 ec_mc;
|
||||
|
||||
if (dbg_hc(chan))
|
||||
dev_vdbg(hsotg->dev, "%s()\n", __func__);
|
||||
@ -1743,6 +1861,13 @@ void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
|
||||
|
||||
hctsiz |= chan->xfer_len << TSIZ_XFERSIZE_SHIFT &
|
||||
TSIZ_XFERSIZE_MASK;
|
||||
|
||||
/* For split set ec_mc for immediate retries */
|
||||
if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
|
||||
chan->ep_type == USB_ENDPOINT_XFER_ISOC)
|
||||
ec_mc = 3;
|
||||
else
|
||||
ec_mc = 1;
|
||||
} else {
|
||||
if (dbg_hc(chan))
|
||||
dev_vdbg(hsotg->dev, "no split\n");
|
||||
@ -1805,6 +1930,9 @@ void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
|
||||
|
||||
hctsiz |= chan->xfer_len << TSIZ_XFERSIZE_SHIFT &
|
||||
TSIZ_XFERSIZE_MASK;
|
||||
|
||||
/* The ec_mc gets the multi_count for non-split */
|
||||
ec_mc = chan->multi_count;
|
||||
}
|
||||
|
||||
chan->start_pkt_count = num_packets;
|
||||
@ -1855,8 +1983,7 @@ void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
|
||||
|
||||
hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num));
|
||||
hcchar &= ~HCCHAR_MULTICNT_MASK;
|
||||
hcchar |= chan->multi_count << HCCHAR_MULTICNT_SHIFT &
|
||||
HCCHAR_MULTICNT_MASK;
|
||||
hcchar |= (ec_mc << HCCHAR_MULTICNT_SHIFT) & HCCHAR_MULTICNT_MASK;
|
||||
dwc2_hc_set_even_odd_frame(hsotg, chan, &hcchar);
|
||||
|
||||
if (hcchar & HCCHAR_CHDIS)
|
||||
@ -1905,7 +2032,6 @@ void dwc2_hc_start_transfer_ddma(struct dwc2_hsotg *hsotg,
|
||||
struct dwc2_host_chan *chan)
|
||||
{
|
||||
u32 hcchar;
|
||||
u32 hc_dma;
|
||||
u32 hctsiz = 0;
|
||||
|
||||
if (chan->do_ping)
|
||||
@ -1934,14 +2060,14 @@ void dwc2_hc_start_transfer_ddma(struct dwc2_hsotg *hsotg,
|
||||
|
||||
dwc2_writel(hctsiz, hsotg->regs + HCTSIZ(chan->hc_num));
|
||||
|
||||
hc_dma = (u32)chan->desc_list_addr & HCDMA_DMA_ADDR_MASK;
|
||||
dma_sync_single_for_device(hsotg->dev, chan->desc_list_addr,
|
||||
chan->desc_list_sz, DMA_TO_DEVICE);
|
||||
|
||||
dwc2_writel(chan->desc_list_addr, hsotg->regs + HCDMA(chan->hc_num));
|
||||
|
||||
/* Always start from first descriptor */
|
||||
hc_dma &= ~HCDMA_CTD_MASK;
|
||||
dwc2_writel(hc_dma, hsotg->regs + HCDMA(chan->hc_num));
|
||||
if (dbg_hc(chan))
|
||||
dev_vdbg(hsotg->dev, "Wrote %08x to HCDMA(%d)\n",
|
||||
hc_dma, chan->hc_num);
|
||||
dev_vdbg(hsotg->dev, "Wrote %pad to HCDMA(%d)\n",
|
||||
&chan->desc_list_addr, chan->hc_num);
|
||||
|
||||
hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num));
|
||||
hcchar &= ~HCCHAR_MULTICNT_MASK;
|
||||
@ -2485,6 +2611,29 @@ void dwc2_set_param_dma_desc_enable(struct dwc2_hsotg *hsotg, int val)
|
||||
hsotg->core_params->dma_desc_enable = val;
|
||||
}
|
||||
|
||||
void dwc2_set_param_dma_desc_fs_enable(struct dwc2_hsotg *hsotg, int val)
|
||||
{
|
||||
int valid = 1;
|
||||
|
||||
if (val > 0 && (hsotg->core_params->dma_enable <= 0 ||
|
||||
!hsotg->hw_params.dma_desc_enable))
|
||||
valid = 0;
|
||||
if (val < 0)
|
||||
valid = 0;
|
||||
|
||||
if (!valid) {
|
||||
if (val >= 0)
|
||||
dev_err(hsotg->dev,
|
||||
"%d invalid for dma_desc_fs_enable parameter. Check HW configuration.\n",
|
||||
val);
|
||||
val = (hsotg->core_params->dma_enable > 0 &&
|
||||
hsotg->hw_params.dma_desc_enable);
|
||||
}
|
||||
|
||||
hsotg->core_params->dma_desc_fs_enable = val;
|
||||
dev_dbg(hsotg->dev, "Setting dma_desc_fs_enable to %d\n", val);
|
||||
}
|
||||
|
||||
void dwc2_set_param_host_support_fs_ls_low_power(struct dwc2_hsotg *hsotg,
|
||||
int val)
|
||||
{
|
||||
@ -3016,6 +3165,7 @@ void dwc2_set_parameters(struct dwc2_hsotg *hsotg,
|
||||
dwc2_set_param_otg_cap(hsotg, params->otg_cap);
|
||||
dwc2_set_param_dma_enable(hsotg, params->dma_enable);
|
||||
dwc2_set_param_dma_desc_enable(hsotg, params->dma_desc_enable);
|
||||
dwc2_set_param_dma_desc_fs_enable(hsotg, params->dma_desc_fs_enable);
|
||||
dwc2_set_param_host_support_fs_ls_low_power(hsotg,
|
||||
params->host_support_fs_ls_low_power);
|
||||
dwc2_set_param_enable_dynamic_fifo(hsotg,
|
||||
@ -3052,17 +3202,93 @@ void dwc2_set_parameters(struct dwc2_hsotg *hsotg,
|
||||
dwc2_set_param_hibernation(hsotg, params->hibernation);
|
||||
}
|
||||
|
||||
/*
|
||||
* Forces either host or device mode if the controller is not
|
||||
* currently in that mode.
|
||||
*
|
||||
* Returns true if the mode was forced.
|
||||
*/
|
||||
static bool dwc2_force_mode_if_needed(struct dwc2_hsotg *hsotg, bool host)
|
||||
{
|
||||
if (host && dwc2_is_host_mode(hsotg))
|
||||
return false;
|
||||
else if (!host && dwc2_is_device_mode(hsotg))
|
||||
return false;
|
||||
|
||||
return dwc2_force_mode(hsotg, host);
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets host hardware parameters. Forces host mode if not currently in
|
||||
* host mode. Should be called immediately after a core soft reset in
|
||||
* order to get the reset values.
|
||||
*/
|
||||
static void dwc2_get_host_hwparams(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_hw_params *hw = &hsotg->hw_params;
|
||||
u32 gnptxfsiz;
|
||||
u32 hptxfsiz;
|
||||
bool forced;
|
||||
|
||||
if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)
|
||||
return;
|
||||
|
||||
forced = dwc2_force_mode_if_needed(hsotg, true);
|
||||
|
||||
gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ);
|
||||
hptxfsiz = dwc2_readl(hsotg->regs + HPTXFSIZ);
|
||||
dev_dbg(hsotg->dev, "gnptxfsiz=%08x\n", gnptxfsiz);
|
||||
dev_dbg(hsotg->dev, "hptxfsiz=%08x\n", hptxfsiz);
|
||||
|
||||
if (forced)
|
||||
dwc2_clear_force_mode(hsotg);
|
||||
|
||||
hw->host_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >>
|
||||
FIFOSIZE_DEPTH_SHIFT;
|
||||
hw->host_perio_tx_fifo_size = (hptxfsiz & FIFOSIZE_DEPTH_MASK) >>
|
||||
FIFOSIZE_DEPTH_SHIFT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets device hardware parameters. Forces device mode if not
|
||||
* currently in device mode. Should be called immediately after a core
|
||||
* soft reset in order to get the reset values.
|
||||
*/
|
||||
static void dwc2_get_dev_hwparams(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_hw_params *hw = &hsotg->hw_params;
|
||||
bool forced;
|
||||
u32 gnptxfsiz;
|
||||
|
||||
if (hsotg->dr_mode == USB_DR_MODE_HOST)
|
||||
return;
|
||||
|
||||
forced = dwc2_force_mode_if_needed(hsotg, false);
|
||||
|
||||
gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ);
|
||||
dev_dbg(hsotg->dev, "gnptxfsiz=%08x\n", gnptxfsiz);
|
||||
|
||||
if (forced)
|
||||
dwc2_clear_force_mode(hsotg);
|
||||
|
||||
hw->dev_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >>
|
||||
FIFOSIZE_DEPTH_SHIFT;
|
||||
}
|
||||
|
||||
/**
|
||||
* During device initialization, read various hardware configuration
|
||||
* registers and interpret the contents.
|
||||
*
|
||||
* This should be called during driver probe. It will perform a core
|
||||
* soft reset in order to get the reset values of the parameters.
|
||||
*/
|
||||
int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_hw_params *hw = &hsotg->hw_params;
|
||||
unsigned width;
|
||||
u32 hwcfg1, hwcfg2, hwcfg3, hwcfg4;
|
||||
u32 hptxfsiz, grxfsiz, gnptxfsiz;
|
||||
u32 gusbcfg;
|
||||
u32 grxfsiz;
|
||||
int retval;
|
||||
|
||||
/*
|
||||
* Attempt to ensure this device is really a DWC_otg Controller.
|
||||
@ -3082,6 +3308,10 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
|
||||
hw->snpsid >> 12 & 0xf, hw->snpsid >> 8 & 0xf,
|
||||
hw->snpsid >> 4 & 0xf, hw->snpsid & 0xf, hw->snpsid);
|
||||
|
||||
retval = dwc2_core_reset(hsotg);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
hwcfg1 = dwc2_readl(hsotg->regs + GHWCFG1);
|
||||
hwcfg2 = dwc2_readl(hsotg->regs + GHWCFG2);
|
||||
hwcfg3 = dwc2_readl(hsotg->regs + GHWCFG3);
|
||||
@ -3094,20 +3324,16 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
|
||||
dev_dbg(hsotg->dev, "hwcfg4=%08x\n", hwcfg4);
|
||||
dev_dbg(hsotg->dev, "grxfsiz=%08x\n", grxfsiz);
|
||||
|
||||
/* Force host mode to get HPTXFSIZ / GNPTXFSIZ exact power on value */
|
||||
gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
|
||||
gusbcfg |= GUSBCFG_FORCEHOSTMODE;
|
||||
dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
|
||||
usleep_range(100000, 150000);
|
||||
/*
|
||||
* Host specific hardware parameters. Reading these parameters
|
||||
* requires the controller to be in host mode. The mode will
|
||||
* be forced, if necessary, to read these values.
|
||||
*/
|
||||
dwc2_get_host_hwparams(hsotg);
|
||||
dwc2_get_dev_hwparams(hsotg);
|
||||
|
||||
gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ);
|
||||
hptxfsiz = dwc2_readl(hsotg->regs + HPTXFSIZ);
|
||||
dev_dbg(hsotg->dev, "gnptxfsiz=%08x\n", gnptxfsiz);
|
||||
dev_dbg(hsotg->dev, "hptxfsiz=%08x\n", hptxfsiz);
|
||||
gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
|
||||
gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
|
||||
dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
|
||||
usleep_range(100000, 150000);
|
||||
/* hwcfg1 */
|
||||
hw->dev_ep_dirs = hwcfg1;
|
||||
|
||||
/* hwcfg2 */
|
||||
hw->op_mode = (hwcfg2 & GHWCFG2_OP_MODE_MASK) >>
|
||||
@ -3163,10 +3389,6 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
|
||||
/* fifo sizes */
|
||||
hw->host_rx_fifo_size = (grxfsiz & GRXFSIZ_DEPTH_MASK) >>
|
||||
GRXFSIZ_DEPTH_SHIFT;
|
||||
hw->host_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >>
|
||||
FIFOSIZE_DEPTH_SHIFT;
|
||||
hw->host_perio_tx_fifo_size = (hptxfsiz & FIFOSIZE_DEPTH_MASK) >>
|
||||
FIFOSIZE_DEPTH_SHIFT;
|
||||
|
||||
dev_dbg(hsotg->dev, "Detected values from hardware:\n");
|
||||
dev_dbg(hsotg->dev, " op_mode=%d\n",
|
||||
@ -3275,6 +3497,43 @@ void dwc2_disable_global_interrupts(struct dwc2_hsotg *hsotg)
|
||||
dwc2_writel(ahbcfg, hsotg->regs + GAHBCFG);
|
||||
}
|
||||
|
||||
/* Returns the controller's GHWCFG2.OTG_MODE. */
|
||||
unsigned dwc2_op_mode(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 ghwcfg2 = dwc2_readl(hsotg->regs + GHWCFG2);
|
||||
|
||||
return (ghwcfg2 & GHWCFG2_OP_MODE_MASK) >>
|
||||
GHWCFG2_OP_MODE_SHIFT;
|
||||
}
|
||||
|
||||
/* Returns true if the controller is capable of DRD. */
|
||||
bool dwc2_hw_is_otg(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
unsigned op_mode = dwc2_op_mode(hsotg);
|
||||
|
||||
return (op_mode == GHWCFG2_OP_MODE_HNP_SRP_CAPABLE) ||
|
||||
(op_mode == GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE) ||
|
||||
(op_mode == GHWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE);
|
||||
}
|
||||
|
||||
/* Returns true if the controller is host-only. */
|
||||
bool dwc2_hw_is_host(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
unsigned op_mode = dwc2_op_mode(hsotg);
|
||||
|
||||
return (op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_HOST) ||
|
||||
(op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST);
|
||||
}
|
||||
|
||||
/* Returns true if the controller is device-only. */
|
||||
bool dwc2_hw_is_device(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
unsigned op_mode = dwc2_op_mode(hsotg);
|
||||
|
||||
return (op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE) ||
|
||||
(op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE);
|
||||
}
|
||||
|
||||
MODULE_DESCRIPTION("DESIGNWARE HS OTG Core");
|
||||
MODULE_AUTHOR("Synopsys, Inc.");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
|
@ -246,6 +246,13 @@ enum dwc2_ep0_state {
|
||||
* value for this if none is specified.
|
||||
* 0 - Address DMA
|
||||
* 1 - Descriptor DMA (default, if available)
|
||||
* @dma_desc_fs_enable: When DMA mode is enabled, specifies whether to use
|
||||
* address DMA mode or descriptor DMA mode for accessing
|
||||
* the data FIFOs in Full Speed mode only. The driver
|
||||
* will automatically detect the value for this if none is
|
||||
* specified.
|
||||
* 0 - Address DMA
|
||||
* 1 - Descriptor DMA in FS (default, if available)
|
||||
* @speed: Specifies the maximum speed of operation in host and
|
||||
* device mode. The actual speed depends on the speed of
|
||||
* the attached device and the value of phy_type.
|
||||
@ -375,6 +382,7 @@ struct dwc2_core_params {
|
||||
int otg_ver;
|
||||
int dma_enable;
|
||||
int dma_desc_enable;
|
||||
int dma_desc_fs_enable;
|
||||
int speed;
|
||||
int enable_dynamic_fifo;
|
||||
int en_multiple_tx_fifo;
|
||||
@ -451,15 +459,18 @@ struct dwc2_core_params {
|
||||
* 1 - 16 bits
|
||||
* 2 - 8 or 16 bits
|
||||
* @snpsid: Value from SNPSID register
|
||||
* @dev_ep_dirs: Direction of device endpoints (GHWCFG1)
|
||||
*/
|
||||
struct dwc2_hw_params {
|
||||
unsigned op_mode:3;
|
||||
unsigned arch:2;
|
||||
unsigned dma_desc_enable:1;
|
||||
unsigned dma_desc_fs_enable:1;
|
||||
unsigned enable_dynamic_fifo:1;
|
||||
unsigned en_multiple_tx_fifo:1;
|
||||
unsigned host_rx_fifo_size:16;
|
||||
unsigned host_nperio_tx_fifo_size:16;
|
||||
unsigned dev_nperio_tx_fifo_size:16;
|
||||
unsigned host_perio_tx_fifo_size:16;
|
||||
unsigned nperio_tx_q_depth:3;
|
||||
unsigned host_perio_tx_q_depth:3;
|
||||
@ -476,6 +487,7 @@ struct dwc2_hw_params {
|
||||
unsigned power_optimized:1;
|
||||
unsigned utmi_phy_data_width:2;
|
||||
u32 snpsid;
|
||||
u32 dev_ep_dirs;
|
||||
};
|
||||
|
||||
/* Size of control and EP0 buffers */
|
||||
@ -676,6 +688,9 @@ struct dwc2_hregs_backup {
|
||||
* @otg_port: OTG port number
|
||||
* @frame_list: Frame list
|
||||
* @frame_list_dma: Frame list DMA address
|
||||
* @frame_list_sz: Frame list size
|
||||
* @desc_gen_cache: Kmem cache for generic descriptors
|
||||
* @desc_hsisoc_cache: Kmem cache for hs isochronous descriptors
|
||||
*
|
||||
* These are for peripheral mode:
|
||||
*
|
||||
@ -770,6 +785,7 @@ struct dwc2_hsotg {
|
||||
u16 frame_number;
|
||||
u16 periodic_qh_count;
|
||||
bool bus_suspended;
|
||||
bool new_connection;
|
||||
|
||||
#ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
|
||||
#define FRAME_NUM_ARRAY_SIZE 1000
|
||||
@ -794,6 +810,9 @@ struct dwc2_hsotg {
|
||||
u8 otg_port;
|
||||
u32 *frame_list;
|
||||
dma_addr_t frame_list_dma;
|
||||
u32 frame_list_sz;
|
||||
struct kmem_cache *desc_gen_cache;
|
||||
struct kmem_cache *desc_hsisoc_cache;
|
||||
|
||||
#ifdef DEBUG
|
||||
u32 frrem_samples;
|
||||
@ -864,10 +883,14 @@ enum dwc2_halt_status {
|
||||
* The following functions support initialization of the core driver component
|
||||
* and the DWC_otg controller
|
||||
*/
|
||||
extern int dwc2_core_reset(struct dwc2_hsotg *hsotg);
|
||||
extern int dwc2_core_reset_and_force_dr_mode(struct dwc2_hsotg *hsotg);
|
||||
extern void dwc2_core_host_init(struct dwc2_hsotg *hsotg);
|
||||
extern int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg);
|
||||
extern int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore);
|
||||
|
||||
void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg);
|
||||
|
||||
/*
|
||||
* Host core Functions.
|
||||
* The following functions support managing the DWC_otg controller in host
|
||||
@ -901,7 +924,7 @@ extern void dwc2_read_packet(struct dwc2_hsotg *hsotg, u8 *dest, u16 bytes);
|
||||
extern void dwc2_flush_tx_fifo(struct dwc2_hsotg *hsotg, const int num);
|
||||
extern void dwc2_flush_rx_fifo(struct dwc2_hsotg *hsotg);
|
||||
|
||||
extern int dwc2_core_init(struct dwc2_hsotg *hsotg, bool select_phy, int irq);
|
||||
extern int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup);
|
||||
extern void dwc2_enable_global_interrupts(struct dwc2_hsotg *hcd);
|
||||
extern void dwc2_disable_global_interrupts(struct dwc2_hsotg *hcd);
|
||||
|
||||
@ -941,6 +964,16 @@ extern void dwc2_set_param_dma_enable(struct dwc2_hsotg *hsotg, int val);
|
||||
*/
|
||||
extern void dwc2_set_param_dma_desc_enable(struct dwc2_hsotg *hsotg, int val);
|
||||
|
||||
/*
|
||||
* When DMA mode is enabled specifies whether to use
|
||||
* address DMA or DMA Descritor mode with full speed devices
|
||||
* for accessing the data FIFOs in host mode.
|
||||
* 0 - address DMA
|
||||
* 1 - FS DMA Descriptor(default, if available)
|
||||
*/
|
||||
extern void dwc2_set_param_dma_desc_fs_enable(struct dwc2_hsotg *hsotg,
|
||||
int val);
|
||||
|
||||
/*
|
||||
* Specifies the maximum speed of operation in host and device mode.
|
||||
* The actual speed depends on the speed of the attached device and
|
||||
@ -1109,6 +1142,31 @@ extern int dwc2_get_hwparams(struct dwc2_hsotg *hsotg);
|
||||
extern int dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg);
|
||||
extern int dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg);
|
||||
|
||||
/*
|
||||
* The following functions check the controller's OTG operation mode
|
||||
* capability (GHWCFG2.OTG_MODE).
|
||||
*
|
||||
* These functions can be used before the internal hsotg->hw_params
|
||||
* are read in and cached so they always read directly from the
|
||||
* GHWCFG2 register.
|
||||
*/
|
||||
unsigned dwc2_op_mode(struct dwc2_hsotg *hsotg);
|
||||
bool dwc2_hw_is_otg(struct dwc2_hsotg *hsotg);
|
||||
bool dwc2_hw_is_host(struct dwc2_hsotg *hsotg);
|
||||
bool dwc2_hw_is_device(struct dwc2_hsotg *hsotg);
|
||||
|
||||
/*
|
||||
* Returns the mode of operation, host or device
|
||||
*/
|
||||
static inline int dwc2_is_host_mode(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
return (dwc2_readl(hsotg->regs + GINTSTS) & GINTSTS_CURMODE_HOST) != 0;
|
||||
}
|
||||
static inline int dwc2_is_device_mode(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
return (dwc2_readl(hsotg->regs + GINTSTS) & GINTSTS_CURMODE_HOST) == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Dump core registers and SPRAM
|
||||
*/
|
||||
@ -1154,12 +1212,14 @@ static inline int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg,
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
|
||||
extern int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg);
|
||||
extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg);
|
||||
extern void dwc2_hcd_connect(struct dwc2_hsotg *hsotg);
|
||||
extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force);
|
||||
extern void dwc2_hcd_start(struct dwc2_hsotg *hsotg);
|
||||
#else
|
||||
static inline int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg)
|
||||
{ return 0; }
|
||||
static inline void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg) {}
|
||||
static inline void dwc2_hcd_connect(struct dwc2_hsotg *hsotg) {}
|
||||
static inline void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force) {}
|
||||
static inline void dwc2_hcd_start(struct dwc2_hsotg *hsotg) {}
|
||||
static inline void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) {}
|
||||
static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
|
||||
|
@ -86,9 +86,6 @@ static void dwc2_handle_usb_port_intr(struct dwc2_hsotg *hsotg)
|
||||
hprt0 &= ~HPRT0_ENA;
|
||||
dwc2_writel(hprt0, hsotg->regs + HPRT0);
|
||||
}
|
||||
|
||||
/* Clear interrupt */
|
||||
dwc2_writel(GINTSTS_PRTINT, hsotg->regs + GINTSTS);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -98,11 +95,11 @@ static void dwc2_handle_usb_port_intr(struct dwc2_hsotg *hsotg)
|
||||
*/
|
||||
static void dwc2_handle_mode_mismatch_intr(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
dev_warn(hsotg->dev, "Mode Mismatch Interrupt: currently in %s mode\n",
|
||||
dwc2_is_host_mode(hsotg) ? "Host" : "Device");
|
||||
|
||||
/* Clear interrupt */
|
||||
dwc2_writel(GINTSTS_MODEMIS, hsotg->regs + GINTSTS);
|
||||
|
||||
dev_warn(hsotg->dev, "Mode Mismatch Interrupt: currently in %s mode\n",
|
||||
dwc2_is_host_mode(hsotg) ? "Host" : "Device");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -239,7 +236,7 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
|
||||
dev_dbg(hsotg->dev, "a_suspend->a_peripheral (%d)\n",
|
||||
hsotg->op_state);
|
||||
spin_unlock(&hsotg->lock);
|
||||
dwc2_hcd_disconnect(hsotg);
|
||||
dwc2_hcd_disconnect(hsotg, false);
|
||||
spin_lock(&hsotg->lock);
|
||||
hsotg->op_state = OTG_STATE_A_PERIPHERAL;
|
||||
} else {
|
||||
@ -276,9 +273,13 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
|
||||
*/
|
||||
static void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
|
||||
u32 gintmsk;
|
||||
|
||||
/* Clear interrupt */
|
||||
dwc2_writel(GINTSTS_CONIDSTSCHNG, hsotg->regs + GINTSTS);
|
||||
|
||||
/* Need to disable SOF interrupt immediately */
|
||||
gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
|
||||
gintmsk &= ~GINTSTS_SOF;
|
||||
dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
|
||||
|
||||
@ -295,9 +296,6 @@ static void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg)
|
||||
queue_work(hsotg->wq_otg, &hsotg->wf_otg);
|
||||
spin_lock(&hsotg->lock);
|
||||
}
|
||||
|
||||
/* Clear interrupt */
|
||||
dwc2_writel(GINTSTS_CONIDSTSCHNG, hsotg->regs + GINTSTS);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -315,12 +313,12 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
dev_dbg(hsotg->dev, "Session request interrupt - lx_state=%d\n",
|
||||
hsotg->lx_state);
|
||||
|
||||
/* Clear interrupt */
|
||||
dwc2_writel(GINTSTS_SESSREQINT, hsotg->regs + GINTSTS);
|
||||
|
||||
dev_dbg(hsotg->dev, "Session request interrupt - lx_state=%d\n",
|
||||
hsotg->lx_state);
|
||||
|
||||
if (dwc2_is_device_mode(hsotg)) {
|
||||
if (hsotg->lx_state == DWC2_L2) {
|
||||
ret = dwc2_exit_hibernation(hsotg, true);
|
||||
@ -347,6 +345,10 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
|
||||
static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Clear interrupt */
|
||||
dwc2_writel(GINTSTS_WKUPINT, hsotg->regs + GINTSTS);
|
||||
|
||||
dev_dbg(hsotg->dev, "++Resume or Remote Wakeup Detected Interrupt++\n");
|
||||
dev_dbg(hsotg->dev, "%s lxstate = %d\n", __func__, hsotg->lx_state);
|
||||
|
||||
@ -368,10 +370,9 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
|
||||
/* Change to L0 state */
|
||||
hsotg->lx_state = DWC2_L0;
|
||||
} else {
|
||||
if (hsotg->core_params->hibernation) {
|
||||
dwc2_writel(GINTSTS_WKUPINT, hsotg->regs + GINTSTS);
|
||||
if (hsotg->core_params->hibernation)
|
||||
return;
|
||||
}
|
||||
|
||||
if (hsotg->lx_state != DWC2_L1) {
|
||||
u32 pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
|
||||
|
||||
@ -385,9 +386,6 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
|
||||
hsotg->lx_state = DWC2_L0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear interrupt */
|
||||
dwc2_writel(GINTSTS_WKUPINT, hsotg->regs + GINTSTS);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -396,14 +394,14 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
|
||||
*/
|
||||
static void dwc2_handle_disconnect_intr(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
dwc2_writel(GINTSTS_DISCONNINT, hsotg->regs + GINTSTS);
|
||||
|
||||
dev_dbg(hsotg->dev, "++Disconnect Detected Interrupt++ (%s) %s\n",
|
||||
dwc2_is_host_mode(hsotg) ? "Host" : "Device",
|
||||
dwc2_op_state_str(hsotg));
|
||||
|
||||
if (hsotg->op_state == OTG_STATE_A_HOST)
|
||||
dwc2_hcd_disconnect(hsotg);
|
||||
|
||||
dwc2_writel(GINTSTS_DISCONNINT, hsotg->regs + GINTSTS);
|
||||
dwc2_hcd_disconnect(hsotg, false);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -419,6 +417,9 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
|
||||
u32 dsts;
|
||||
int ret;
|
||||
|
||||
/* Clear interrupt */
|
||||
dwc2_writel(GINTSTS_USBSUSP, hsotg->regs + GINTSTS);
|
||||
|
||||
dev_dbg(hsotg->dev, "USB SUSPEND\n");
|
||||
|
||||
if (dwc2_is_device_mode(hsotg)) {
|
||||
@ -437,7 +438,7 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
|
||||
if (!dwc2_is_device_connected(hsotg)) {
|
||||
dev_dbg(hsotg->dev,
|
||||
"ignore suspend request before enumeration\n");
|
||||
goto clear_int;
|
||||
return;
|
||||
}
|
||||
|
||||
ret = dwc2_enter_hibernation(hsotg);
|
||||
@ -476,10 +477,6 @@ skip_power_saving:
|
||||
hsotg->op_state = OTG_STATE_A_HOST;
|
||||
}
|
||||
}
|
||||
|
||||
clear_int:
|
||||
/* Clear interrupt */
|
||||
dwc2_writel(GINTSTS_USBSUSP, hsotg->regs + GINTSTS);
|
||||
}
|
||||
|
||||
#define GINTMSK_COMMON (GINTSTS_WKUPINT | GINTSTS_SESSREQINT | \
|
||||
|
@ -2095,7 +2095,7 @@ static void dwc2_hsotg_irq_enumdone(struct dwc2_hsotg *hsotg)
|
||||
*/
|
||||
|
||||
/* catch both EnumSpd_FS and EnumSpd_FS48 */
|
||||
switch (dsts & DSTS_ENUMSPD_MASK) {
|
||||
switch ((dsts & DSTS_ENUMSPD_MASK) >> DSTS_ENUMSPD_SHIFT) {
|
||||
case DSTS_ENUMSPD_FS:
|
||||
case DSTS_ENUMSPD_FS48:
|
||||
hsotg->gadget.speed = USB_SPEED_FULL;
|
||||
@ -2243,54 +2243,6 @@ static void dwc2_hsotg_irq_fifoempty(struct dwc2_hsotg *hsotg, bool periodic)
|
||||
GINTSTS_PTXFEMP | \
|
||||
GINTSTS_RXFLVL)
|
||||
|
||||
/**
|
||||
* dwc2_hsotg_corereset - issue softreset to the core
|
||||
* @hsotg: The device state
|
||||
*
|
||||
* Issue a soft reset to the core, and await the core finishing it.
|
||||
*/
|
||||
static int dwc2_hsotg_corereset(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
int timeout;
|
||||
u32 grstctl;
|
||||
|
||||
dev_dbg(hsotg->dev, "resetting core\n");
|
||||
|
||||
/* issue soft reset */
|
||||
dwc2_writel(GRSTCTL_CSFTRST, hsotg->regs + GRSTCTL);
|
||||
|
||||
timeout = 10000;
|
||||
do {
|
||||
grstctl = dwc2_readl(hsotg->regs + GRSTCTL);
|
||||
} while ((grstctl & GRSTCTL_CSFTRST) && timeout-- > 0);
|
||||
|
||||
if (grstctl & GRSTCTL_CSFTRST) {
|
||||
dev_err(hsotg->dev, "Failed to get CSftRst asserted\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
timeout = 10000;
|
||||
|
||||
while (1) {
|
||||
u32 grstctl = dwc2_readl(hsotg->regs + GRSTCTL);
|
||||
|
||||
if (timeout-- < 0) {
|
||||
dev_info(hsotg->dev,
|
||||
"%s: reset failed, GRSTCTL=%08x\n",
|
||||
__func__, grstctl);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
if (!(grstctl & GRSTCTL_AHBIDLE))
|
||||
continue;
|
||||
|
||||
break; /* reset done */
|
||||
}
|
||||
|
||||
dev_dbg(hsotg->dev, "reset successful\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_hsotg_core_init - issue softreset to the core
|
||||
* @hsotg: The device state
|
||||
@ -2307,7 +2259,7 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
|
||||
kill_all_requests(hsotg, hsotg->eps_out[0], -ECONNRESET);
|
||||
|
||||
if (!is_usb_reset)
|
||||
if (dwc2_hsotg_corereset(hsotg))
|
||||
if (dwc2_core_reset(hsotg))
|
||||
return;
|
||||
|
||||
/*
|
||||
@ -2585,7 +2537,7 @@ irq_retry:
|
||||
if (gintsts & GINTSTS_GOUTNAKEFF) {
|
||||
dev_info(hsotg->dev, "GOUTNakEff triggered\n");
|
||||
|
||||
dwc2_writel(DCTL_CGOUTNAK, hsotg->regs + DCTL);
|
||||
__orr32(hsotg->regs + DCTL, DCTL_CGOUTNAK);
|
||||
|
||||
dwc2_hsotg_dump(hsotg);
|
||||
}
|
||||
@ -2593,7 +2545,7 @@ irq_retry:
|
||||
if (gintsts & GINTSTS_GINNAKEFF) {
|
||||
dev_info(hsotg->dev, "GINNakEff triggered\n");
|
||||
|
||||
dwc2_writel(DCTL_CGNPINNAK, hsotg->regs + DCTL);
|
||||
__orr32(hsotg->regs + DCTL, DCTL_CGNPINNAK);
|
||||
|
||||
dwc2_hsotg_dump(hsotg);
|
||||
}
|
||||
@ -2911,15 +2863,15 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
|
||||
"%s: timeout DIEPINT.NAKEFF\n", __func__);
|
||||
} else {
|
||||
/* Clear any pending nak effect interrupt */
|
||||
dwc2_writel(GINTSTS_GINNAKEFF, hsotg->regs + GINTSTS);
|
||||
dwc2_writel(GINTSTS_GOUTNAKEFF, hsotg->regs + GINTSTS);
|
||||
|
||||
__orr32(hsotg->regs + DCTL, DCTL_SGNPINNAK);
|
||||
__orr32(hsotg->regs + DCTL, DCTL_SGOUTNAK);
|
||||
|
||||
/* Wait for global nak to take effect */
|
||||
if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS,
|
||||
GINTSTS_GINNAKEFF, 100))
|
||||
GINTSTS_GOUTNAKEFF, 100))
|
||||
dev_warn(hsotg->dev,
|
||||
"%s: timeout GINTSTS.GINNAKEFF\n", __func__);
|
||||
"%s: timeout GINTSTS.GOUTNAKEFF\n", __func__);
|
||||
}
|
||||
|
||||
/* Disable ep */
|
||||
@ -2944,7 +2896,7 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
|
||||
/* TODO: Flush shared tx fifo */
|
||||
} else {
|
||||
/* Remove global NAKs */
|
||||
__bic32(hsotg->regs + DCTL, DCTL_SGNPINNAK);
|
||||
__bic32(hsotg->regs + DCTL, DCTL_SGOUTNAK);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3403,8 +3355,8 @@ static int dwc2_hsotg_hw_cfg(struct dwc2_hsotg *hsotg)
|
||||
|
||||
/* check hardware configuration */
|
||||
|
||||
cfg = dwc2_readl(hsotg->regs + GHWCFG2);
|
||||
hsotg->num_of_eps = (cfg >> GHWCFG2_NUM_DEV_EP_SHIFT) & 0xF;
|
||||
hsotg->num_of_eps = hsotg->hw_params.num_dev_ep;
|
||||
|
||||
/* Add ep0 */
|
||||
hsotg->num_of_eps++;
|
||||
|
||||
@ -3415,7 +3367,7 @@ static int dwc2_hsotg_hw_cfg(struct dwc2_hsotg *hsotg)
|
||||
/* Same dwc2_hsotg_ep is used in both directions for ep0 */
|
||||
hsotg->eps_out[0] = hsotg->eps_in[0];
|
||||
|
||||
cfg = dwc2_readl(hsotg->regs + GHWCFG1);
|
||||
cfg = hsotg->hw_params.dev_ep_dirs;
|
||||
for (i = 1, cfg >>= 2; i < hsotg->num_of_eps; i++, cfg >>= 2) {
|
||||
ep_type = cfg & 3;
|
||||
/* Direction in or both */
|
||||
@ -3434,11 +3386,8 @@ static int dwc2_hsotg_hw_cfg(struct dwc2_hsotg *hsotg)
|
||||
}
|
||||
}
|
||||
|
||||
cfg = dwc2_readl(hsotg->regs + GHWCFG3);
|
||||
hsotg->fifo_mem = (cfg >> GHWCFG3_DFIFO_DEPTH_SHIFT);
|
||||
|
||||
cfg = dwc2_readl(hsotg->regs + GHWCFG4);
|
||||
hsotg->dedicated_fifos = (cfg >> GHWCFG4_DED_FIFO_SHIFT) & 1;
|
||||
hsotg->fifo_mem = hsotg->hw_params.total_fifo_size;
|
||||
hsotg->dedicated_fifos = hsotg->hw_params.en_multiple_tx_fifo;
|
||||
|
||||
dev_info(hsotg->dev, "EPs: %d, %s fifos, %d entries in SPRAM\n",
|
||||
hsotg->num_of_eps,
|
||||
@ -3563,6 +3512,17 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
|
||||
memcpy(&hsotg->g_tx_fifo_sz[1], p_tx_fifo, sizeof(p_tx_fifo));
|
||||
/* Device tree specific probe */
|
||||
dwc2_hsotg_of_probe(hsotg);
|
||||
|
||||
/* Check against largest possible value. */
|
||||
if (hsotg->g_np_g_tx_fifo_sz >
|
||||
hsotg->hw_params.dev_nperio_tx_fifo_size) {
|
||||
dev_warn(dev, "Specified GNPTXFDEP=%d > %d\n",
|
||||
hsotg->g_np_g_tx_fifo_sz,
|
||||
hsotg->hw_params.dev_nperio_tx_fifo_size);
|
||||
hsotg->g_np_g_tx_fifo_sz =
|
||||
hsotg->hw_params.dev_nperio_tx_fifo_size;
|
||||
}
|
||||
|
||||
/* Dump fifo information */
|
||||
dev_dbg(dev, "NonPeriodic TXFIFO size: %d\n",
|
||||
hsotg->g_np_g_tx_fifo_sz);
|
||||
@ -3579,31 +3539,12 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
|
||||
else if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)
|
||||
hsotg->op_state = OTG_STATE_B_PERIPHERAL;
|
||||
|
||||
/*
|
||||
* Force Device mode before initialization.
|
||||
* This allows correctly configuring fifo for device mode.
|
||||
*/
|
||||
__bic32(hsotg->regs + GUSBCFG, GUSBCFG_FORCEHOSTMODE);
|
||||
__orr32(hsotg->regs + GUSBCFG, GUSBCFG_FORCEDEVMODE);
|
||||
|
||||
/*
|
||||
* According to Synopsys databook, this sleep is needed for the force
|
||||
* device mode to take effect.
|
||||
*/
|
||||
msleep(25);
|
||||
|
||||
dwc2_hsotg_corereset(hsotg);
|
||||
ret = dwc2_hsotg_hw_cfg(hsotg);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "Hardware configuration failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dwc2_hsotg_init(hsotg);
|
||||
|
||||
/* Switch back to default configuration */
|
||||
__bic32(hsotg->regs + GUSBCFG, GUSBCFG_FORCEDEVMODE);
|
||||
|
||||
hsotg->ctrl_buff = devm_kzalloc(hsotg->dev,
|
||||
DWC2_CTRL_BUFF_SIZE, GFP_KERNEL);
|
||||
if (!hsotg->ctrl_buff) {
|
||||
|
@ -268,15 +268,33 @@ static void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg)
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_hcd_disconnect() - Handles disconnect of the HCD
|
||||
* dwc2_hcd_connect() - Handles connect of the HCD
|
||||
*
|
||||
* @hsotg: Pointer to struct dwc2_hsotg
|
||||
*
|
||||
* Must be called with interrupt disabled and spinlock held
|
||||
*/
|
||||
void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg)
|
||||
void dwc2_hcd_connect(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
if (hsotg->lx_state != DWC2_L0)
|
||||
usb_hcd_resume_root_hub(hsotg->priv);
|
||||
|
||||
hsotg->flags.b.port_connect_status_change = 1;
|
||||
hsotg->flags.b.port_connect_status = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_hcd_disconnect() - Handles disconnect of the HCD
|
||||
*
|
||||
* @hsotg: Pointer to struct dwc2_hsotg
|
||||
* @force: If true, we won't try to reconnect even if we see device connected.
|
||||
*
|
||||
* Must be called with interrupt disabled and spinlock held
|
||||
*/
|
||||
void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force)
|
||||
{
|
||||
u32 intr;
|
||||
u32 hprt0;
|
||||
|
||||
/* Set status flags for the hub driver */
|
||||
hsotg->flags.b.port_connect_status_change = 1;
|
||||
@ -315,6 +333,24 @@ void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg)
|
||||
dwc2_hcd_cleanup_channels(hsotg);
|
||||
|
||||
dwc2_host_disconnect(hsotg);
|
||||
|
||||
/*
|
||||
* Add an extra check here to see if we're actually connected but
|
||||
* we don't have a detection interrupt pending. This can happen if:
|
||||
* 1. hardware sees connect
|
||||
* 2. hardware sees disconnect
|
||||
* 3. hardware sees connect
|
||||
* 4. dwc2_port_intr() - clears connect interrupt
|
||||
* 5. dwc2_handle_common_intr() - calls here
|
||||
*
|
||||
* Without the extra check here we will end calling disconnect
|
||||
* and won't get any future interrupts to handle the connect.
|
||||
*/
|
||||
if (!force) {
|
||||
hprt0 = dwc2_readl(hsotg->regs + HPRT0);
|
||||
if (!(hprt0 & HPRT0_CONNDET) && (hprt0 & HPRT0_CONNSTS))
|
||||
dwc2_hcd_connect(hsotg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -881,8 +917,10 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
|
||||
*/
|
||||
chan->multi_count = dwc2_hb_mult(qh->maxp);
|
||||
|
||||
if (hsotg->core_params->dma_desc_enable > 0)
|
||||
if (hsotg->core_params->dma_desc_enable > 0) {
|
||||
chan->desc_list_addr = qh->desc_list_dma;
|
||||
chan->desc_list_sz = qh->desc_list_sz;
|
||||
}
|
||||
|
||||
dwc2_hc_init(hsotg, chan);
|
||||
chan->qh = qh;
|
||||
@ -1382,7 +1420,7 @@ static void dwc2_conn_id_status_change(struct work_struct *work)
|
||||
dev_err(hsotg->dev,
|
||||
"Connection id status change timed out\n");
|
||||
hsotg->op_state = OTG_STATE_B_PERIPHERAL;
|
||||
dwc2_core_init(hsotg, false, -1);
|
||||
dwc2_core_init(hsotg, false);
|
||||
dwc2_enable_global_interrupts(hsotg);
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
dwc2_hsotg_core_init_disconnected(hsotg, false);
|
||||
@ -1405,7 +1443,7 @@ static void dwc2_conn_id_status_change(struct work_struct *work)
|
||||
hsotg->op_state = OTG_STATE_A_HOST;
|
||||
|
||||
/* Initialize the Core for Host mode */
|
||||
dwc2_core_init(hsotg, false, -1);
|
||||
dwc2_core_init(hsotg, false);
|
||||
dwc2_enable_global_interrupts(hsotg);
|
||||
dwc2_hcd_start(hsotg);
|
||||
}
|
||||
@ -1734,6 +1772,28 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
|
||||
port_status |= USB_PORT_STAT_TEST;
|
||||
/* USB_PORT_FEAT_INDICATOR unsupported always 0 */
|
||||
|
||||
if (hsotg->core_params->dma_desc_fs_enable) {
|
||||
/*
|
||||
* Enable descriptor DMA only if a full speed
|
||||
* device is connected.
|
||||
*/
|
||||
if (hsotg->new_connection &&
|
||||
((port_status &
|
||||
(USB_PORT_STAT_CONNECTION |
|
||||
USB_PORT_STAT_HIGH_SPEED |
|
||||
USB_PORT_STAT_LOW_SPEED)) ==
|
||||
USB_PORT_STAT_CONNECTION)) {
|
||||
u32 hcfg;
|
||||
|
||||
dev_info(hsotg->dev, "Enabling descriptor DMA mode\n");
|
||||
hsotg->core_params->dma_desc_enable = 1;
|
||||
hcfg = dwc2_readl(hsotg->regs + HCFG);
|
||||
hcfg |= HCFG_DESCDMA;
|
||||
dwc2_writel(hcfg, hsotg->regs + HCFG);
|
||||
hsotg->new_connection = false;
|
||||
}
|
||||
}
|
||||
|
||||
dev_vdbg(hsotg->dev, "port_status=%08x\n", port_status);
|
||||
*(__le32 *)buf = cpu_to_le32(port_status);
|
||||
break;
|
||||
@ -2298,13 +2358,19 @@ static void dwc2_hcd_reset_func(struct work_struct *work)
|
||||
{
|
||||
struct dwc2_hsotg *hsotg = container_of(work, struct dwc2_hsotg,
|
||||
reset_work.work);
|
||||
unsigned long flags;
|
||||
u32 hprt0;
|
||||
|
||||
dev_dbg(hsotg->dev, "USB RESET function called\n");
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
|
||||
hprt0 = dwc2_read_hprt0(hsotg);
|
||||
hprt0 &= ~HPRT0_RST;
|
||||
dwc2_writel(hprt0, hsotg->regs + HPRT0);
|
||||
hsotg->flags.b.port_reset_change = 1;
|
||||
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2366,7 +2432,7 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd)
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
/* Ensure hcd is disconnected */
|
||||
dwc2_hcd_disconnect(hsotg);
|
||||
dwc2_hcd_disconnect(hsotg, true);
|
||||
dwc2_hcd_stop(hsotg);
|
||||
hsotg->lx_state = DWC2_L3;
|
||||
hcd->state = HC_STATE_HALT;
|
||||
@ -3054,7 +3120,7 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
|
||||
dwc2_disable_global_interrupts(hsotg);
|
||||
|
||||
/* Initialize the DWC_otg core, and select the Phy type */
|
||||
retval = dwc2_core_init(hsotg, true, irq);
|
||||
retval = dwc2_core_init(hsotg, true);
|
||||
if (retval)
|
||||
goto error2;
|
||||
|
||||
@ -3122,6 +3188,47 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
|
||||
if (!hsotg->status_buf)
|
||||
goto error3;
|
||||
|
||||
/*
|
||||
* Create kmem caches to handle descriptor buffers in descriptor
|
||||
* DMA mode.
|
||||
* Alignment must be set to 512 bytes.
|
||||
*/
|
||||
if (hsotg->core_params->dma_desc_enable ||
|
||||
hsotg->core_params->dma_desc_fs_enable) {
|
||||
hsotg->desc_gen_cache = kmem_cache_create("dwc2-gen-desc",
|
||||
sizeof(struct dwc2_hcd_dma_desc) *
|
||||
MAX_DMA_DESC_NUM_GENERIC, 512, SLAB_CACHE_DMA,
|
||||
NULL);
|
||||
if (!hsotg->desc_gen_cache) {
|
||||
dev_err(hsotg->dev,
|
||||
"unable to create dwc2 generic desc cache\n");
|
||||
|
||||
/*
|
||||
* Disable descriptor dma mode since it will not be
|
||||
* usable.
|
||||
*/
|
||||
hsotg->core_params->dma_desc_enable = 0;
|
||||
hsotg->core_params->dma_desc_fs_enable = 0;
|
||||
}
|
||||
|
||||
hsotg->desc_hsisoc_cache = kmem_cache_create("dwc2-hsisoc-desc",
|
||||
sizeof(struct dwc2_hcd_dma_desc) *
|
||||
MAX_DMA_DESC_NUM_HS_ISOC, 512, 0, NULL);
|
||||
if (!hsotg->desc_hsisoc_cache) {
|
||||
dev_err(hsotg->dev,
|
||||
"unable to create dwc2 hs isoc desc cache\n");
|
||||
|
||||
kmem_cache_destroy(hsotg->desc_gen_cache);
|
||||
|
||||
/*
|
||||
* Disable descriptor dma mode since it will not be
|
||||
* usable.
|
||||
*/
|
||||
hsotg->core_params->dma_desc_enable = 0;
|
||||
hsotg->core_params->dma_desc_fs_enable = 0;
|
||||
}
|
||||
}
|
||||
|
||||
hsotg->otg_port = 1;
|
||||
hsotg->frame_list = NULL;
|
||||
hsotg->frame_list_dma = 0;
|
||||
@ -3145,7 +3252,7 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
|
||||
*/
|
||||
retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
|
||||
if (retval < 0)
|
||||
goto error3;
|
||||
goto error4;
|
||||
|
||||
device_wakeup_enable(hcd->self.controller);
|
||||
|
||||
@ -3155,6 +3262,9 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
|
||||
|
||||
return 0;
|
||||
|
||||
error4:
|
||||
kmem_cache_destroy(hsotg->desc_gen_cache);
|
||||
kmem_cache_destroy(hsotg->desc_hsisoc_cache);
|
||||
error3:
|
||||
dwc2_hcd_release(hsotg);
|
||||
error2:
|
||||
@ -3195,6 +3305,10 @@ void dwc2_hcd_remove(struct dwc2_hsotg *hsotg)
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
hsotg->priv = NULL;
|
||||
|
||||
kmem_cache_destroy(hsotg->desc_gen_cache);
|
||||
kmem_cache_destroy(hsotg->desc_hsisoc_cache);
|
||||
|
||||
dwc2_hcd_release(hsotg);
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
|
@ -107,6 +107,7 @@ struct dwc2_qh;
|
||||
* @qh: QH for the transfer being processed by this channel
|
||||
* @hc_list_entry: For linking to list of host channels
|
||||
* @desc_list_addr: Current QH's descriptor list DMA address
|
||||
* @desc_list_sz: Current QH's descriptor list size
|
||||
*
|
||||
* This structure represents the state of a single host channel when acting in
|
||||
* host mode. It contains the data items needed to transfer packets to an
|
||||
@ -159,6 +160,7 @@ struct dwc2_host_chan {
|
||||
struct dwc2_qh *qh;
|
||||
struct list_head hc_list_entry;
|
||||
dma_addr_t desc_list_addr;
|
||||
u32 desc_list_sz;
|
||||
};
|
||||
|
||||
struct dwc2_hcd_pipe_info {
|
||||
@ -251,6 +253,7 @@ enum dwc2_transaction_type {
|
||||
* schedule
|
||||
* @desc_list: List of transfer descriptors
|
||||
* @desc_list_dma: Physical address of desc_list
|
||||
* @desc_list_sz: Size of descriptors list
|
||||
* @n_bytes: Xfer Bytes array. Each element corresponds to a transfer
|
||||
* descriptor and indicates original XferSize value for the
|
||||
* descriptor
|
||||
@ -284,6 +287,7 @@ struct dwc2_qh {
|
||||
struct list_head qh_list_entry;
|
||||
struct dwc2_hcd_dma_desc *desc_list;
|
||||
dma_addr_t desc_list_dma;
|
||||
u32 desc_list_sz;
|
||||
u32 *n_bytes;
|
||||
unsigned tt_buffer_dirty:1;
|
||||
};
|
||||
@ -340,6 +344,8 @@ struct dwc2_qtd {
|
||||
u8 isoc_split_pos;
|
||||
u16 isoc_frame_index;
|
||||
u16 isoc_split_offset;
|
||||
u16 isoc_td_last;
|
||||
u16 isoc_td_first;
|
||||
u32 ssplit_out_xfer_count;
|
||||
u8 error_count;
|
||||
u8 n_desc;
|
||||
@ -377,18 +383,6 @@ static inline void disable_hc_int(struct dwc2_hsotg *hsotg, int chnum, u32 intr)
|
||||
dwc2_writel(mask, hsotg->regs + HCINTMSK(chnum));
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the mode of operation, host or device
|
||||
*/
|
||||
static inline int dwc2_is_host_mode(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
return (dwc2_readl(hsotg->regs + GINTSTS) & GINTSTS_CURMODE_HOST) != 0;
|
||||
}
|
||||
static inline int dwc2_is_device_mode(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
return (dwc2_readl(hsotg->regs + GINTSTS) & GINTSTS_CURMODE_HOST) == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads HPRT0 in preparation to modify. It keeps the WC bits 0 so that if they
|
||||
* are read as 1, they won't clear when written back.
|
||||
@ -534,6 +528,19 @@ static inline bool dbg_perio(void) { return false; }
|
||||
/* Packet size for any kind of endpoint descriptor */
|
||||
#define dwc2_max_packet(wmaxpacketsize) ((wmaxpacketsize) & 0x07ff)
|
||||
|
||||
/*
|
||||
* Returns true if frame1 index is greater than frame2 index. The comparison
|
||||
* is done modulo FRLISTEN_64_SIZE. This accounts for the rollover of the
|
||||
* frame number when the max index frame number is reached.
|
||||
*/
|
||||
static inline bool dwc2_frame_idx_num_gt(u16 fr_idx1, u16 fr_idx2)
|
||||
{
|
||||
u16 diff = fr_idx1 - fr_idx2;
|
||||
u16 sign = diff & (FRLISTEN_64_SIZE >> 1);
|
||||
|
||||
return diff && !sign;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if frame1 is less than or equal to frame2. The comparison is
|
||||
* done modulo HFNUM_MAX_FRNUM. This accounts for the rollover of the
|
||||
|
@ -87,22 +87,31 @@ static u16 dwc2_frame_incr_val(struct dwc2_qh *qh)
|
||||
static int dwc2_desc_list_alloc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
|
||||
gfp_t flags)
|
||||
{
|
||||
qh->desc_list = dma_alloc_coherent(hsotg->dev,
|
||||
sizeof(struct dwc2_hcd_dma_desc) *
|
||||
dwc2_max_desc_num(qh), &qh->desc_list_dma,
|
||||
flags);
|
||||
struct kmem_cache *desc_cache;
|
||||
|
||||
if (qh->ep_type == USB_ENDPOINT_XFER_ISOC
|
||||
&& qh->dev_speed == USB_SPEED_HIGH)
|
||||
desc_cache = hsotg->desc_hsisoc_cache;
|
||||
else
|
||||
desc_cache = hsotg->desc_gen_cache;
|
||||
|
||||
qh->desc_list_sz = sizeof(struct dwc2_hcd_dma_desc) *
|
||||
dwc2_max_desc_num(qh);
|
||||
|
||||
qh->desc_list = kmem_cache_zalloc(desc_cache, flags | GFP_DMA);
|
||||
if (!qh->desc_list)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(qh->desc_list, 0,
|
||||
sizeof(struct dwc2_hcd_dma_desc) * dwc2_max_desc_num(qh));
|
||||
qh->desc_list_dma = dma_map_single(hsotg->dev, qh->desc_list,
|
||||
qh->desc_list_sz,
|
||||
DMA_TO_DEVICE);
|
||||
|
||||
qh->n_bytes = kzalloc(sizeof(u32) * dwc2_max_desc_num(qh), flags);
|
||||
if (!qh->n_bytes) {
|
||||
dma_free_coherent(hsotg->dev, sizeof(struct dwc2_hcd_dma_desc)
|
||||
* dwc2_max_desc_num(qh), qh->desc_list,
|
||||
qh->desc_list_dma);
|
||||
dma_unmap_single(hsotg->dev, qh->desc_list_dma,
|
||||
qh->desc_list_sz,
|
||||
DMA_FROM_DEVICE);
|
||||
kfree(qh->desc_list);
|
||||
qh->desc_list = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
@ -112,10 +121,18 @@ static int dwc2_desc_list_alloc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
|
||||
|
||||
static void dwc2_desc_list_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
|
||||
{
|
||||
struct kmem_cache *desc_cache;
|
||||
|
||||
if (qh->ep_type == USB_ENDPOINT_XFER_ISOC
|
||||
&& qh->dev_speed == USB_SPEED_HIGH)
|
||||
desc_cache = hsotg->desc_hsisoc_cache;
|
||||
else
|
||||
desc_cache = hsotg->desc_gen_cache;
|
||||
|
||||
if (qh->desc_list) {
|
||||
dma_free_coherent(hsotg->dev, sizeof(struct dwc2_hcd_dma_desc)
|
||||
* dwc2_max_desc_num(qh), qh->desc_list,
|
||||
qh->desc_list_dma);
|
||||
dma_unmap_single(hsotg->dev, qh->desc_list_dma,
|
||||
qh->desc_list_sz, DMA_FROM_DEVICE);
|
||||
kmem_cache_free(desc_cache, qh->desc_list);
|
||||
qh->desc_list = NULL;
|
||||
}
|
||||
|
||||
@ -128,21 +145,20 @@ static int dwc2_frame_list_alloc(struct dwc2_hsotg *hsotg, gfp_t mem_flags)
|
||||
if (hsotg->frame_list)
|
||||
return 0;
|
||||
|
||||
hsotg->frame_list = dma_alloc_coherent(hsotg->dev,
|
||||
4 * FRLISTEN_64_SIZE,
|
||||
&hsotg->frame_list_dma,
|
||||
mem_flags);
|
||||
hsotg->frame_list_sz = 4 * FRLISTEN_64_SIZE;
|
||||
hsotg->frame_list = kzalloc(hsotg->frame_list_sz, GFP_ATOMIC | GFP_DMA);
|
||||
if (!hsotg->frame_list)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(hsotg->frame_list, 0, 4 * FRLISTEN_64_SIZE);
|
||||
hsotg->frame_list_dma = dma_map_single(hsotg->dev, hsotg->frame_list,
|
||||
hsotg->frame_list_sz,
|
||||
DMA_TO_DEVICE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dwc2_frame_list_free(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 *frame_list;
|
||||
dma_addr_t frame_list_dma;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
@ -152,14 +168,14 @@ static void dwc2_frame_list_free(struct dwc2_hsotg *hsotg)
|
||||
return;
|
||||
}
|
||||
|
||||
frame_list = hsotg->frame_list;
|
||||
frame_list_dma = hsotg->frame_list_dma;
|
||||
dma_unmap_single(hsotg->dev, hsotg->frame_list_dma,
|
||||
hsotg->frame_list_sz, DMA_FROM_DEVICE);
|
||||
|
||||
kfree(hsotg->frame_list);
|
||||
hsotg->frame_list = NULL;
|
||||
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
|
||||
dma_free_coherent(hsotg->dev, 4 * FRLISTEN_64_SIZE, frame_list,
|
||||
frame_list_dma);
|
||||
}
|
||||
|
||||
static void dwc2_per_sched_enable(struct dwc2_hsotg *hsotg, u32 fr_list_en)
|
||||
@ -249,6 +265,15 @@ static void dwc2_update_frame_list(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
|
||||
j = (j + inc) & (FRLISTEN_64_SIZE - 1);
|
||||
} while (j != i);
|
||||
|
||||
/*
|
||||
* Sync frame list since controller will access it if periodic
|
||||
* channel is currently enabled.
|
||||
*/
|
||||
dma_sync_single_for_device(hsotg->dev,
|
||||
hsotg->frame_list_dma,
|
||||
hsotg->frame_list_sz,
|
||||
DMA_TO_DEVICE);
|
||||
|
||||
if (!enable)
|
||||
return;
|
||||
|
||||
@ -278,6 +303,7 @@ static void dwc2_release_channel_ddma(struct dwc2_hsotg *hsotg,
|
||||
hsotg->non_periodic_channels--;
|
||||
} else {
|
||||
dwc2_update_frame_list(hsotg, qh, 0);
|
||||
hsotg->available_host_channels++;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -360,6 +386,8 @@ err0:
|
||||
*/
|
||||
void dwc2_hcd_qh_free_ddma(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
dwc2_desc_list_free(hsotg, qh);
|
||||
|
||||
/*
|
||||
@ -369,8 +397,10 @@ void dwc2_hcd_qh_free_ddma(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
|
||||
* when it comes here from endpoint disable routine
|
||||
* channel remains assigned.
|
||||
*/
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
if (qh->channel)
|
||||
dwc2_release_channel_ddma(hsotg, qh);
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
|
||||
if ((qh->ep_type == USB_ENDPOINT_XFER_ISOC ||
|
||||
qh->ep_type == USB_ENDPOINT_XFER_INT) &&
|
||||
@ -524,14 +554,23 @@ static void dwc2_fill_host_isoc_dma_desc(struct dwc2_hsotg *hsotg,
|
||||
dma_desc->status = qh->n_bytes[idx] << HOST_DMA_ISOC_NBYTES_SHIFT &
|
||||
HOST_DMA_ISOC_NBYTES_MASK;
|
||||
|
||||
/* Set active bit */
|
||||
dma_desc->status |= HOST_DMA_A;
|
||||
|
||||
qh->ntd++;
|
||||
qtd->isoc_frame_index_last++;
|
||||
|
||||
#ifdef ISOC_URB_GIVEBACK_ASAP
|
||||
/* Set IOC for each descriptor corresponding to last frame of URB */
|
||||
if (qtd->isoc_frame_index_last == qtd->urb->packet_count)
|
||||
dma_desc->status |= HOST_DMA_IOC;
|
||||
#endif
|
||||
|
||||
qh->ntd++;
|
||||
qtd->isoc_frame_index_last++;
|
||||
dma_sync_single_for_device(hsotg->dev,
|
||||
qh->desc_list_dma +
|
||||
(idx * sizeof(struct dwc2_hcd_dma_desc)),
|
||||
sizeof(struct dwc2_hcd_dma_desc),
|
||||
DMA_TO_DEVICE);
|
||||
}
|
||||
|
||||
static void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
|
||||
@ -539,11 +578,32 @@ static void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
|
||||
{
|
||||
struct dwc2_qtd *qtd;
|
||||
u32 max_xfer_size;
|
||||
u16 idx, inc, n_desc, ntd_max = 0;
|
||||
u16 idx, inc, n_desc = 0, ntd_max = 0;
|
||||
u16 cur_idx;
|
||||
u16 next_idx;
|
||||
|
||||
idx = qh->td_last;
|
||||
inc = qh->interval;
|
||||
n_desc = 0;
|
||||
hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
|
||||
cur_idx = dwc2_frame_list_idx(hsotg->frame_number);
|
||||
next_idx = dwc2_desclist_idx_inc(qh->td_last, inc, qh->dev_speed);
|
||||
|
||||
/*
|
||||
* Ensure current frame number didn't overstep last scheduled
|
||||
* descriptor. If it happens, the only way to recover is to move
|
||||
* qh->td_last to current frame number + 1.
|
||||
* So that next isoc descriptor will be scheduled on frame number + 1
|
||||
* and not on a past frame.
|
||||
*/
|
||||
if (dwc2_frame_idx_num_gt(cur_idx, next_idx) || (cur_idx == next_idx)) {
|
||||
if (inc < 32) {
|
||||
dev_vdbg(hsotg->dev,
|
||||
"current frame number overstep last descriptor\n");
|
||||
qh->td_last = dwc2_desclist_idx_inc(cur_idx, inc,
|
||||
qh->dev_speed);
|
||||
idx = qh->td_last;
|
||||
}
|
||||
}
|
||||
|
||||
if (qh->interval) {
|
||||
ntd_max = (dwc2_max_desc_num(qh) + qh->interval - 1) /
|
||||
@ -556,15 +616,20 @@ static void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
|
||||
MAX_ISOC_XFER_SIZE_HS : MAX_ISOC_XFER_SIZE_FS;
|
||||
|
||||
list_for_each_entry(qtd, &qh->qtd_list, qtd_list_entry) {
|
||||
if (qtd->in_process &&
|
||||
qtd->isoc_frame_index_last ==
|
||||
qtd->urb->packet_count)
|
||||
continue;
|
||||
|
||||
qtd->isoc_td_first = idx;
|
||||
while (qh->ntd < ntd_max && qtd->isoc_frame_index_last <
|
||||
qtd->urb->packet_count) {
|
||||
if (n_desc > 1)
|
||||
qh->desc_list[n_desc - 1].status |= HOST_DMA_A;
|
||||
dwc2_fill_host_isoc_dma_desc(hsotg, qtd, qh,
|
||||
max_xfer_size, idx);
|
||||
idx = dwc2_desclist_idx_inc(idx, inc, qh->dev_speed);
|
||||
n_desc++;
|
||||
}
|
||||
qtd->isoc_td_last = idx;
|
||||
qtd->in_process = 1;
|
||||
}
|
||||
|
||||
@ -575,6 +640,11 @@ static void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
|
||||
if (qh->ntd == ntd_max) {
|
||||
idx = dwc2_desclist_idx_dec(qh->td_last, inc, qh->dev_speed);
|
||||
qh->desc_list[idx].status |= HOST_DMA_IOC;
|
||||
dma_sync_single_for_device(hsotg->dev,
|
||||
qh->desc_list_dma + (idx *
|
||||
sizeof(struct dwc2_hcd_dma_desc)),
|
||||
sizeof(struct dwc2_hcd_dma_desc),
|
||||
DMA_TO_DEVICE);
|
||||
}
|
||||
#else
|
||||
/*
|
||||
@ -604,13 +674,12 @@ static void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
|
||||
idx = dwc2_desclist_idx_dec(qh->td_last, inc, qh->dev_speed);
|
||||
|
||||
qh->desc_list[idx].status |= HOST_DMA_IOC;
|
||||
dma_sync_single_for_device(hsotg->dev,
|
||||
qh->desc_list_dma +
|
||||
(idx * sizeof(struct dwc2_hcd_dma_desc)),
|
||||
sizeof(struct dwc2_hcd_dma_desc),
|
||||
DMA_TO_DEVICE);
|
||||
#endif
|
||||
|
||||
if (n_desc) {
|
||||
qh->desc_list[n_desc - 1].status |= HOST_DMA_A;
|
||||
if (n_desc > 1)
|
||||
qh->desc_list[0].status |= HOST_DMA_A;
|
||||
}
|
||||
}
|
||||
|
||||
static void dwc2_fill_host_dma_desc(struct dwc2_hsotg *hsotg,
|
||||
@ -647,6 +716,12 @@ static void dwc2_fill_host_dma_desc(struct dwc2_hsotg *hsotg,
|
||||
|
||||
dma_desc->buf = (u32)chan->xfer_dma;
|
||||
|
||||
dma_sync_single_for_device(hsotg->dev,
|
||||
qh->desc_list_dma +
|
||||
(n_desc * sizeof(struct dwc2_hcd_dma_desc)),
|
||||
sizeof(struct dwc2_hcd_dma_desc),
|
||||
DMA_TO_DEVICE);
|
||||
|
||||
/*
|
||||
* Last (or only) descriptor of IN transfer with actual size less
|
||||
* than MaxPacket
|
||||
@ -697,6 +772,12 @@ static void dwc2_init_non_isoc_dma_desc(struct dwc2_hsotg *hsotg,
|
||||
"set A bit in desc %d (%p)\n",
|
||||
n_desc - 1,
|
||||
&qh->desc_list[n_desc - 1]);
|
||||
dma_sync_single_for_device(hsotg->dev,
|
||||
qh->desc_list_dma +
|
||||
((n_desc - 1) *
|
||||
sizeof(struct dwc2_hcd_dma_desc)),
|
||||
sizeof(struct dwc2_hcd_dma_desc),
|
||||
DMA_TO_DEVICE);
|
||||
}
|
||||
dwc2_fill_host_dma_desc(hsotg, chan, qtd, qh, n_desc);
|
||||
dev_vdbg(hsotg->dev,
|
||||
@ -722,10 +803,19 @@ static void dwc2_init_non_isoc_dma_desc(struct dwc2_hsotg *hsotg,
|
||||
HOST_DMA_IOC | HOST_DMA_EOL | HOST_DMA_A;
|
||||
dev_vdbg(hsotg->dev, "set IOC/EOL/A bits in desc %d (%p)\n",
|
||||
n_desc - 1, &qh->desc_list[n_desc - 1]);
|
||||
dma_sync_single_for_device(hsotg->dev,
|
||||
qh->desc_list_dma + (n_desc - 1) *
|
||||
sizeof(struct dwc2_hcd_dma_desc),
|
||||
sizeof(struct dwc2_hcd_dma_desc),
|
||||
DMA_TO_DEVICE);
|
||||
if (n_desc > 1) {
|
||||
qh->desc_list[0].status |= HOST_DMA_A;
|
||||
dev_vdbg(hsotg->dev, "set A bit in desc 0 (%p)\n",
|
||||
&qh->desc_list[0]);
|
||||
dma_sync_single_for_device(hsotg->dev,
|
||||
qh->desc_list_dma,
|
||||
sizeof(struct dwc2_hcd_dma_desc),
|
||||
DMA_TO_DEVICE);
|
||||
}
|
||||
chan->ntd = n_desc;
|
||||
}
|
||||
@ -800,7 +890,7 @@ static int dwc2_cmpl_host_isoc_dma_desc(struct dwc2_hsotg *hsotg,
|
||||
struct dwc2_qtd *qtd,
|
||||
struct dwc2_qh *qh, u16 idx)
|
||||
{
|
||||
struct dwc2_hcd_dma_desc *dma_desc = &qh->desc_list[idx];
|
||||
struct dwc2_hcd_dma_desc *dma_desc;
|
||||
struct dwc2_hcd_iso_packet_desc *frame_desc;
|
||||
u16 remain = 0;
|
||||
int rc = 0;
|
||||
@ -808,6 +898,13 @@ static int dwc2_cmpl_host_isoc_dma_desc(struct dwc2_hsotg *hsotg,
|
||||
if (!qtd->urb)
|
||||
return -EINVAL;
|
||||
|
||||
dma_sync_single_for_cpu(hsotg->dev, qh->desc_list_dma + (idx *
|
||||
sizeof(struct dwc2_hcd_dma_desc)),
|
||||
sizeof(struct dwc2_hcd_dma_desc),
|
||||
DMA_FROM_DEVICE);
|
||||
|
||||
dma_desc = &qh->desc_list[idx];
|
||||
|
||||
frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index_last];
|
||||
dma_desc->buf = (u32)(qtd->urb->dma + frame_desc->offset);
|
||||
if (chan->ep_is_in)
|
||||
@ -911,17 +1008,51 @@ static void dwc2_complete_isoc_xfer_ddma(struct dwc2_hsotg *hsotg,
|
||||
list_for_each_entry_safe(qtd, qtd_tmp, &qh->qtd_list, qtd_list_entry) {
|
||||
if (!qtd->in_process)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Ensure idx corresponds to descriptor where first urb of this
|
||||
* qtd was added. In fact, during isoc desc init, dwc2 may skip
|
||||
* an index if current frame number is already over this index.
|
||||
*/
|
||||
if (idx != qtd->isoc_td_first) {
|
||||
dev_vdbg(hsotg->dev,
|
||||
"try to complete %d instead of %d\n",
|
||||
idx, qtd->isoc_td_first);
|
||||
idx = qtd->isoc_td_first;
|
||||
}
|
||||
|
||||
do {
|
||||
struct dwc2_qtd *qtd_next;
|
||||
u16 cur_idx;
|
||||
|
||||
rc = dwc2_cmpl_host_isoc_dma_desc(hsotg, chan, qtd, qh,
|
||||
idx);
|
||||
if (rc < 0)
|
||||
return;
|
||||
idx = dwc2_desclist_idx_inc(idx, qh->interval,
|
||||
chan->speed);
|
||||
if (rc == DWC2_CMPL_STOP)
|
||||
goto stop_scan;
|
||||
if (!rc)
|
||||
continue;
|
||||
|
||||
if (rc == DWC2_CMPL_DONE)
|
||||
break;
|
||||
|
||||
/* rc == DWC2_CMPL_STOP */
|
||||
|
||||
if (qh->interval >= 32)
|
||||
goto stop_scan;
|
||||
|
||||
qh->td_first = idx;
|
||||
cur_idx = dwc2_frame_list_idx(hsotg->frame_number);
|
||||
qtd_next = list_first_entry(&qh->qtd_list,
|
||||
struct dwc2_qtd,
|
||||
qtd_list_entry);
|
||||
if (dwc2_frame_idx_num_gt(cur_idx,
|
||||
qtd_next->isoc_td_last))
|
||||
break;
|
||||
|
||||
goto stop_scan;
|
||||
|
||||
} while (idx != qh->td_first);
|
||||
}
|
||||
|
||||
@ -1029,6 +1160,12 @@ static int dwc2_process_non_isoc_desc(struct dwc2_hsotg *hsotg,
|
||||
if (!urb)
|
||||
return -EINVAL;
|
||||
|
||||
dma_sync_single_for_cpu(hsotg->dev,
|
||||
qh->desc_list_dma + (desc_num *
|
||||
sizeof(struct dwc2_hcd_dma_desc)),
|
||||
sizeof(struct dwc2_hcd_dma_desc),
|
||||
DMA_FROM_DEVICE);
|
||||
|
||||
dma_desc = &qh->desc_list[desc_num];
|
||||
n_bytes = qh->n_bytes[desc_num];
|
||||
dev_vdbg(hsotg->dev,
|
||||
@ -1037,7 +1174,10 @@ static int dwc2_process_non_isoc_desc(struct dwc2_hsotg *hsotg,
|
||||
failed = dwc2_update_non_isoc_urb_state_ddma(hsotg, chan, qtd, dma_desc,
|
||||
halt_status, n_bytes,
|
||||
xfer_done);
|
||||
if (failed || (*xfer_done && urb->status != -EINPROGRESS)) {
|
||||
if (*xfer_done && urb->status != -EINPROGRESS)
|
||||
failed = 1;
|
||||
|
||||
if (failed) {
|
||||
dwc2_host_complete(hsotg, qtd, urb->status);
|
||||
dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh);
|
||||
dev_vdbg(hsotg->dev, "failed=%1x xfer_done=%1x status=%08x\n",
|
||||
@ -1165,6 +1305,21 @@ void dwc2_hcd_complete_xfer_ddma(struct dwc2_hsotg *hsotg,
|
||||
/* Release the channel if halted or session completed */
|
||||
if (halt_status != DWC2_HC_XFER_COMPLETE ||
|
||||
list_empty(&qh->qtd_list)) {
|
||||
struct dwc2_qtd *qtd, *qtd_tmp;
|
||||
|
||||
/*
|
||||
* Kill all remainings QTDs since channel has been
|
||||
* halted.
|
||||
*/
|
||||
list_for_each_entry_safe(qtd, qtd_tmp,
|
||||
&qh->qtd_list,
|
||||
qtd_list_entry) {
|
||||
dwc2_host_complete(hsotg, qtd,
|
||||
-ECONNRESET);
|
||||
dwc2_hcd_qtd_unlink_and_free(hsotg,
|
||||
qtd, qh);
|
||||
}
|
||||
|
||||
/* Halt the channel if session completed */
|
||||
if (halt_status == DWC2_HC_XFER_COMPLETE)
|
||||
dwc2_hc_halt(hsotg, chan, halt_status);
|
||||
@ -1174,7 +1329,12 @@ void dwc2_hcd_complete_xfer_ddma(struct dwc2_hsotg *hsotg,
|
||||
/* Keep in assigned schedule to continue transfer */
|
||||
list_move(&qh->qh_list_entry,
|
||||
&hsotg->periodic_sched_assigned);
|
||||
continue_isoc_xfer = 1;
|
||||
/*
|
||||
* If channel has been halted during giveback of urb
|
||||
* then prevent any new scheduling.
|
||||
*/
|
||||
if (!chan->halt_status)
|
||||
continue_isoc_xfer = 1;
|
||||
}
|
||||
/*
|
||||
* Todo: Consider the case when period exceeds FrameList size.
|
||||
|
@ -122,6 +122,9 @@ static void dwc2_sof_intr(struct dwc2_hsotg *hsotg)
|
||||
struct dwc2_qh *qh;
|
||||
enum dwc2_transaction_type tr_type;
|
||||
|
||||
/* Clear interrupt */
|
||||
dwc2_writel(GINTSTS_SOF, hsotg->regs + GINTSTS);
|
||||
|
||||
#ifdef DEBUG_SOF
|
||||
dev_vdbg(hsotg->dev, "--Start of Frame Interrupt--\n");
|
||||
#endif
|
||||
@ -146,9 +149,6 @@ static void dwc2_sof_intr(struct dwc2_hsotg *hsotg)
|
||||
tr_type = dwc2_hcd_select_transactions(hsotg);
|
||||
if (tr_type != DWC2_TRANSACTION_NONE)
|
||||
dwc2_hcd_queue_transactions(hsotg, tr_type);
|
||||
|
||||
/* Clear interrupt */
|
||||
dwc2_writel(GINTSTS_SOF, hsotg->regs + GINTSTS);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -312,6 +312,7 @@ static void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0,
|
||||
|
||||
if (do_reset) {
|
||||
*hprt0_modify |= HPRT0_RST;
|
||||
dwc2_writel(*hprt0_modify, hsotg->regs + HPRT0);
|
||||
queue_delayed_work(hsotg->wq_otg, &hsotg->reset_work,
|
||||
msecs_to_jiffies(60));
|
||||
} else {
|
||||
@ -347,15 +348,12 @@ static void dwc2_port_intr(struct dwc2_hsotg *hsotg)
|
||||
* Set flag and clear if detected
|
||||
*/
|
||||
if (hprt0 & HPRT0_CONNDET) {
|
||||
dwc2_writel(hprt0_modify | HPRT0_CONNDET, hsotg->regs + HPRT0);
|
||||
|
||||
dev_vdbg(hsotg->dev,
|
||||
"--Port Interrupt HPRT0=0x%08x Port Connect Detected--\n",
|
||||
hprt0);
|
||||
if (hsotg->lx_state != DWC2_L0)
|
||||
usb_hcd_resume_root_hub(hsotg->priv);
|
||||
|
||||
hsotg->flags.b.port_connect_status_change = 1;
|
||||
hsotg->flags.b.port_connect_status = 1;
|
||||
hprt0_modify |= HPRT0_CONNDET;
|
||||
dwc2_hcd_connect(hsotg);
|
||||
|
||||
/*
|
||||
* The Hub driver asserts a reset when it sees port connect
|
||||
@ -368,27 +366,36 @@ static void dwc2_port_intr(struct dwc2_hsotg *hsotg)
|
||||
* Clear if detected - Set internal flag if disabled
|
||||
*/
|
||||
if (hprt0 & HPRT0_ENACHG) {
|
||||
dwc2_writel(hprt0_modify | HPRT0_ENACHG, hsotg->regs + HPRT0);
|
||||
dev_vdbg(hsotg->dev,
|
||||
" --Port Interrupt HPRT0=0x%08x Port Enable Changed (now %d)--\n",
|
||||
hprt0, !!(hprt0 & HPRT0_ENA));
|
||||
hprt0_modify |= HPRT0_ENACHG;
|
||||
if (hprt0 & HPRT0_ENA)
|
||||
if (hprt0 & HPRT0_ENA) {
|
||||
hsotg->new_connection = true;
|
||||
dwc2_hprt0_enable(hsotg, hprt0, &hprt0_modify);
|
||||
else
|
||||
} else {
|
||||
hsotg->flags.b.port_enable_change = 1;
|
||||
if (hsotg->core_params->dma_desc_fs_enable) {
|
||||
u32 hcfg;
|
||||
|
||||
hsotg->core_params->dma_desc_enable = 0;
|
||||
hsotg->new_connection = false;
|
||||
hcfg = dwc2_readl(hsotg->regs + HCFG);
|
||||
hcfg &= ~HCFG_DESCDMA;
|
||||
dwc2_writel(hcfg, hsotg->regs + HCFG);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Overcurrent Change Interrupt */
|
||||
if (hprt0 & HPRT0_OVRCURRCHG) {
|
||||
dwc2_writel(hprt0_modify | HPRT0_OVRCURRCHG,
|
||||
hsotg->regs + HPRT0);
|
||||
dev_vdbg(hsotg->dev,
|
||||
" --Port Interrupt HPRT0=0x%08x Port Overcurrent Changed--\n",
|
||||
hprt0);
|
||||
hsotg->flags.b.port_over_current_change = 1;
|
||||
hprt0_modify |= HPRT0_OVRCURRCHG;
|
||||
}
|
||||
|
||||
/* Clear Port Interrupts */
|
||||
dwc2_writel(hprt0_modify, hsotg->regs + HPRT0);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -232,7 +232,7 @@ struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
|
||||
*/
|
||||
void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
|
||||
{
|
||||
if (hsotg->core_params->dma_desc_enable > 0) {
|
||||
if (qh->desc_list) {
|
||||
dwc2_hcd_qh_free_ddma(hsotg, qh);
|
||||
} else {
|
||||
/* kfree(NULL) is safe */
|
||||
|
@ -769,10 +769,6 @@
|
||||
#define TSIZ_XFERSIZE_SHIFT 0
|
||||
|
||||
#define HCDMA(_ch) HSOTG_REG(0x0514 + 0x20 * (_ch))
|
||||
#define HCDMA_DMA_ADDR_MASK (0x1fffff << 11)
|
||||
#define HCDMA_DMA_ADDR_SHIFT 11
|
||||
#define HCDMA_CTD_MASK (0xff << 3)
|
||||
#define HCDMA_CTD_SHIFT 3
|
||||
|
||||
#define HCDMAB(_ch) HSOTG_REG(0x051c + 0x20 * (_ch))
|
||||
|
||||
|
@ -54,11 +54,44 @@
|
||||
|
||||
static const char dwc2_driver_name[] = "dwc2";
|
||||
|
||||
static const struct dwc2_core_params params_hi6220 = {
|
||||
.otg_cap = 2, /* No HNP/SRP capable */
|
||||
.otg_ver = 0, /* 1.3 */
|
||||
.dma_enable = 1,
|
||||
.dma_desc_enable = 0,
|
||||
.dma_desc_fs_enable = 0,
|
||||
.speed = 0, /* High Speed */
|
||||
.enable_dynamic_fifo = 1,
|
||||
.en_multiple_tx_fifo = 1,
|
||||
.host_rx_fifo_size = 512,
|
||||
.host_nperio_tx_fifo_size = 512,
|
||||
.host_perio_tx_fifo_size = 512,
|
||||
.max_transfer_size = 65535,
|
||||
.max_packet_count = 511,
|
||||
.host_channels = 16,
|
||||
.phy_type = 1, /* UTMI */
|
||||
.phy_utmi_width = 8,
|
||||
.phy_ulpi_ddr = 0, /* Single */
|
||||
.phy_ulpi_ext_vbus = 0,
|
||||
.i2c_enable = 0,
|
||||
.ulpi_fs_ls = 0,
|
||||
.host_support_fs_ls_low_power = 0,
|
||||
.host_ls_low_power_phy_clk = 0, /* 48 MHz */
|
||||
.ts_dline = 0,
|
||||
.reload_ctl = 0,
|
||||
.ahbcfg = GAHBCFG_HBSTLEN_INCR16 <<
|
||||
GAHBCFG_HBSTLEN_SHIFT,
|
||||
.uframe_sched = 0,
|
||||
.external_id_pin_ctl = -1,
|
||||
.hibernation = -1,
|
||||
};
|
||||
|
||||
static const struct dwc2_core_params params_bcm2835 = {
|
||||
.otg_cap = 0, /* HNP/SRP capable */
|
||||
.otg_ver = 0, /* 1.3 */
|
||||
.dma_enable = 1,
|
||||
.dma_desc_enable = 0,
|
||||
.dma_desc_fs_enable = 0,
|
||||
.speed = 0, /* High Speed */
|
||||
.enable_dynamic_fifo = 1,
|
||||
.en_multiple_tx_fifo = 1,
|
||||
@ -89,6 +122,7 @@ static const struct dwc2_core_params params_rk3066 = {
|
||||
.otg_ver = -1,
|
||||
.dma_enable = -1,
|
||||
.dma_desc_enable = 0,
|
||||
.dma_desc_fs_enable = 0,
|
||||
.speed = -1,
|
||||
.enable_dynamic_fifo = 1,
|
||||
.en_multiple_tx_fifo = -1,
|
||||
@ -115,6 +149,71 @@ static const struct dwc2_core_params params_rk3066 = {
|
||||
.hibernation = -1,
|
||||
};
|
||||
|
||||
/*
|
||||
* Check the dr_mode against the module configuration and hardware
|
||||
* capabilities.
|
||||
*
|
||||
* The hardware, module, and dr_mode, can each be set to host, device,
|
||||
* or otg. Check that all these values are compatible and adjust the
|
||||
* value of dr_mode if possible.
|
||||
*
|
||||
* actual
|
||||
* HW MOD dr_mode dr_mode
|
||||
* ------------------------------
|
||||
* HST HST any : HST
|
||||
* HST DEV any : ---
|
||||
* HST OTG any : HST
|
||||
*
|
||||
* DEV HST any : ---
|
||||
* DEV DEV any : DEV
|
||||
* DEV OTG any : DEV
|
||||
*
|
||||
* OTG HST any : HST
|
||||
* OTG DEV any : DEV
|
||||
* OTG OTG any : dr_mode
|
||||
*/
|
||||
static int dwc2_get_dr_mode(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
enum usb_dr_mode mode;
|
||||
|
||||
hsotg->dr_mode = usb_get_dr_mode(hsotg->dev);
|
||||
if (hsotg->dr_mode == USB_DR_MODE_UNKNOWN)
|
||||
hsotg->dr_mode = USB_DR_MODE_OTG;
|
||||
|
||||
mode = hsotg->dr_mode;
|
||||
|
||||
if (dwc2_hw_is_device(hsotg)) {
|
||||
if (IS_ENABLED(CONFIG_USB_DWC2_HOST)) {
|
||||
dev_err(hsotg->dev,
|
||||
"Controller does not support host mode.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
mode = USB_DR_MODE_PERIPHERAL;
|
||||
} else if (dwc2_hw_is_host(hsotg)) {
|
||||
if (IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL)) {
|
||||
dev_err(hsotg->dev,
|
||||
"Controller does not support device mode.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
mode = USB_DR_MODE_HOST;
|
||||
} else {
|
||||
if (IS_ENABLED(CONFIG_USB_DWC2_HOST))
|
||||
mode = USB_DR_MODE_HOST;
|
||||
else if (IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL))
|
||||
mode = USB_DR_MODE_PERIPHERAL;
|
||||
}
|
||||
|
||||
if (mode != hsotg->dr_mode) {
|
||||
dev_warn(hsotg->dev,
|
||||
"Configuration mismatch. dr_mode forced to %s\n",
|
||||
mode == USB_DR_MODE_HOST ? "host" : "device");
|
||||
|
||||
hsotg->dr_mode = mode;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(hsotg->dev);
|
||||
@ -306,8 +405,28 @@ static int dwc2_driver_remove(struct platform_device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_driver_shutdown() - Called on device shutdown
|
||||
*
|
||||
* @dev: Platform device
|
||||
*
|
||||
* In specific conditions (involving usb hubs) dwc2 devices can create a
|
||||
* lot of interrupts, even to the point of overwhelming devices running
|
||||
* at low frequencies. Some devices need to do special clock handling
|
||||
* at shutdown-time which may bring the system clock below the threshold
|
||||
* of being able to handle the dwc2 interrupts. Disabling dwc2-irqs
|
||||
* prevents reboots/poweroffs from getting stuck in such cases.
|
||||
*/
|
||||
static void dwc2_driver_shutdown(struct platform_device *dev)
|
||||
{
|
||||
struct dwc2_hsotg *hsotg = platform_get_drvdata(dev);
|
||||
|
||||
disable_irq(hsotg->irq);
|
||||
}
|
||||
|
||||
static const struct of_device_id dwc2_of_match_table[] = {
|
||||
{ .compatible = "brcm,bcm2835-usb", .data = ¶ms_bcm2835 },
|
||||
{ .compatible = "hisilicon,hi6220-usb", .data = ¶ms_hi6220 },
|
||||
{ .compatible = "rockchip,rk3066-usb", .data = ¶ms_rk3066 },
|
||||
{ .compatible = "snps,dwc2", .data = NULL },
|
||||
{ .compatible = "samsung,s3c6400-hsotg", .data = NULL},
|
||||
@ -335,7 +454,6 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
||||
struct dwc2_hsotg *hsotg;
|
||||
struct resource *res;
|
||||
int retval;
|
||||
int irq;
|
||||
|
||||
match = of_match_device(dwc2_of_match_table, &dev->dev);
|
||||
if (match && match->data) {
|
||||
@ -348,8 +466,10 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
||||
/*
|
||||
* Disable descriptor dma mode by default as the HW can support
|
||||
* it, but does not support it for SPLIT transactions.
|
||||
* Disable it for FS devices as well.
|
||||
*/
|
||||
defparams.dma_desc_enable = 0;
|
||||
defparams.dma_desc_fs_enable = 0;
|
||||
}
|
||||
|
||||
hsotg = devm_kzalloc(&dev->dev, sizeof(*hsotg), GFP_KERNEL);
|
||||
@ -375,19 +495,6 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
||||
dev_dbg(&dev->dev, "mapped PA %08lx to VA %p\n",
|
||||
(unsigned long)res->start, hsotg->regs);
|
||||
|
||||
hsotg->dr_mode = usb_get_dr_mode(&dev->dev);
|
||||
if (IS_ENABLED(CONFIG_USB_DWC2_HOST) &&
|
||||
hsotg->dr_mode != USB_DR_MODE_HOST) {
|
||||
hsotg->dr_mode = USB_DR_MODE_HOST;
|
||||
dev_warn(hsotg->dev,
|
||||
"Configuration mismatch. Forcing host mode\n");
|
||||
} else if (IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) &&
|
||||
hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) {
|
||||
hsotg->dr_mode = USB_DR_MODE_PERIPHERAL;
|
||||
dev_warn(hsotg->dev,
|
||||
"Configuration mismatch. Forcing peripheral mode\n");
|
||||
}
|
||||
|
||||
retval = dwc2_lowlevel_hw_init(hsotg);
|
||||
if (retval)
|
||||
return retval;
|
||||
@ -401,15 +508,15 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
||||
|
||||
dwc2_set_all_params(hsotg->core_params, -1);
|
||||
|
||||
irq = platform_get_irq(dev, 0);
|
||||
if (irq < 0) {
|
||||
hsotg->irq = platform_get_irq(dev, 0);
|
||||
if (hsotg->irq < 0) {
|
||||
dev_err(&dev->dev, "missing IRQ resource\n");
|
||||
return irq;
|
||||
return hsotg->irq;
|
||||
}
|
||||
|
||||
dev_dbg(hsotg->dev, "registering common handler for irq%d\n",
|
||||
irq);
|
||||
retval = devm_request_irq(hsotg->dev, irq,
|
||||
hsotg->irq);
|
||||
retval = devm_request_irq(hsotg->dev, hsotg->irq,
|
||||
dwc2_handle_common_intr, IRQF_SHARED,
|
||||
dev_name(hsotg->dev), hsotg);
|
||||
if (retval)
|
||||
@ -419,7 +526,11 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
/* Detect config values from hardware */
|
||||
retval = dwc2_get_dr_mode(hsotg);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
/* Reset the controller and detect hardware config values */
|
||||
retval = dwc2_get_hwparams(hsotg);
|
||||
if (retval)
|
||||
goto error;
|
||||
@ -427,15 +538,17 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
||||
/* Validate parameter values */
|
||||
dwc2_set_parameters(hsotg, params);
|
||||
|
||||
dwc2_force_dr_mode(hsotg);
|
||||
|
||||
if (hsotg->dr_mode != USB_DR_MODE_HOST) {
|
||||
retval = dwc2_gadget_init(hsotg, irq);
|
||||
retval = dwc2_gadget_init(hsotg, hsotg->irq);
|
||||
if (retval)
|
||||
goto error;
|
||||
hsotg->gadget_enabled = 1;
|
||||
}
|
||||
|
||||
if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) {
|
||||
retval = dwc2_hcd_init(hsotg, irq);
|
||||
retval = dwc2_hcd_init(hsotg, hsotg->irq);
|
||||
if (retval) {
|
||||
if (hsotg->gadget_enabled)
|
||||
dwc2_hsotg_remove(hsotg);
|
||||
@ -502,6 +615,7 @@ static struct platform_driver dwc2_platform_driver = {
|
||||
},
|
||||
.probe = dwc2_driver_probe,
|
||||
.remove = dwc2_driver_remove,
|
||||
.shutdown = dwc2_driver_shutdown,
|
||||
};
|
||||
|
||||
module_platform_driver(dwc2_platform_driver);
|
||||
|
@ -87,6 +87,15 @@ config USB_DWC3_KEYSTONE
|
||||
Support of USB2/3 functionality in TI Keystone2 platforms.
|
||||
Say 'Y' or 'M' here if you have one such device
|
||||
|
||||
config USB_DWC3_OF_SIMPLE
|
||||
tristate "Generic OF Simple Glue Layer"
|
||||
depends on OF && COMMON_CLK
|
||||
default USB_DWC3
|
||||
help
|
||||
Support USB2/3 functionality in simple SoC integrations.
|
||||
Currently supports Xilinx and Qualcomm DWC USB3 IP.
|
||||
Say 'Y' or 'M' if you have one such device.
|
||||
|
||||
config USB_DWC3_ST
|
||||
tristate "STMicroelectronics Platforms"
|
||||
depends on ARCH_STI && OF
|
||||
@ -96,12 +105,4 @@ config USB_DWC3_ST
|
||||
inside (i.e. STiH407).
|
||||
Say 'Y' or 'M' if you have one such device.
|
||||
|
||||
config USB_DWC3_QCOM
|
||||
tristate "Qualcomm Platforms"
|
||||
depends on ARCH_QCOM || COMPILE_TEST
|
||||
default USB_DWC3
|
||||
help
|
||||
Recent Qualcomm SoCs ship with one DesignWare Core USB3 IP inside,
|
||||
say 'Y' or 'M' if you have one such device.
|
||||
|
||||
endif
|
||||
|
@ -37,5 +37,5 @@ obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o
|
||||
obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o
|
||||
obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o
|
||||
obj-$(CONFIG_USB_DWC3_KEYSTONE) += dwc3-keystone.o
|
||||
obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o
|
||||
obj-$(CONFIG_USB_DWC3_OF_SIMPLE) += dwc3-of-simple.o
|
||||
obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o
|
||||
|
@ -272,7 +272,8 @@ static int dwc3_event_buffers_setup(struct dwc3 *dwc)
|
||||
|
||||
for (n = 0; n < dwc->num_event_buffers; n++) {
|
||||
evt = dwc->ev_buffs[n];
|
||||
dev_dbg(dwc->dev, "Event buf %p dma %08llx length %d\n",
|
||||
dwc3_trace(trace_dwc3_core,
|
||||
"Event buf %p dma %08llx length %d\n",
|
||||
evt->buf, (unsigned long long) evt->dma,
|
||||
evt->length);
|
||||
|
||||
@ -608,12 +609,13 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
||||
reg |= DWC3_GCTL_GBLHIBERNATIONEN;
|
||||
break;
|
||||
default:
|
||||
dev_dbg(dwc->dev, "No power optimization available\n");
|
||||
dwc3_trace(trace_dwc3_core, "No power optimization available\n");
|
||||
}
|
||||
|
||||
/* check if current dwc3 is on simulation board */
|
||||
if (dwc->hwparams.hwparams6 & DWC3_GHWPARAMS6_EN_FPGA) {
|
||||
dev_dbg(dwc->dev, "it is on FPGA board\n");
|
||||
dwc3_trace(trace_dwc3_core,
|
||||
"running on FPGA platform\n");
|
||||
dwc->is_fpga = true;
|
||||
}
|
||||
|
||||
|
@ -37,6 +37,7 @@
|
||||
#define DWC3_MSG_MAX 500
|
||||
|
||||
/* Global constants */
|
||||
#define DWC3_ZLP_BUF_SIZE 1024 /* size of a superspeed bulk */
|
||||
#define DWC3_EP0_BOUNCE_SIZE 512
|
||||
#define DWC3_ENDPOINTS_NUM 32
|
||||
#define DWC3_XHCI_RESOURCES_NUM 2
|
||||
@ -647,6 +648,7 @@ struct dwc3_scratchpad_array {
|
||||
* @ctrl_req: usb control request which is used for ep0
|
||||
* @ep0_trb: trb which is used for the ctrl_req
|
||||
* @ep0_bounce: bounce buffer for ep0
|
||||
* @zlp_buf: used when request->zero is set
|
||||
* @setup_buf: used while precessing STD USB requests
|
||||
* @ctrl_req_addr: dma address of ctrl_req
|
||||
* @ep0_trb: dma address of ep0_trb
|
||||
@ -734,6 +736,7 @@ struct dwc3 {
|
||||
struct usb_ctrlrequest *ctrl_req;
|
||||
struct dwc3_trb *ep0_trb;
|
||||
void *ep0_bounce;
|
||||
void *zlp_buf;
|
||||
void *scratchbuf;
|
||||
u8 *setup_buf;
|
||||
dma_addr_t ctrl_req_addr;
|
||||
|
180
drivers/usb/dwc3/dwc3-of-simple.c
Normal file
180
drivers/usb/dwc3/dwc3-of-simple.c
Normal file
@ -0,0 +1,180 @@
|
||||
/**
|
||||
* dwc3-of-simple.c - OF glue layer for simple integrations
|
||||
*
|
||||
* Copyright (c) 2015 Texas Instruments Incorporated - http://www.ti.com
|
||||
*
|
||||
* Author: Felipe Balbi <balbi@ti.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 of
|
||||
* the License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* This is a combination of the old dwc3-qcom.c by Ivan T. Ivanov
|
||||
* <iivanov@mm-sol.com> and the original patch adding support for Xilinx' SoC
|
||||
* by Subbaraya Sundeep Bhatta <subbaraya.sundeep.bhatta@xilinx.com>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
struct dwc3_of_simple {
|
||||
struct device *dev;
|
||||
struct clk **clks;
|
||||
int num_clocks;
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
ret = of_clk_get_parent_count(np);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
simple->num_clocks = ret;
|
||||
|
||||
simple->clks = devm_kcalloc(dev, simple->num_clocks,
|
||||
sizeof(struct clk *), GFP_KERNEL);
|
||||
if (!simple->clks)
|
||||
return -ENOMEM;
|
||||
|
||||
simple->dev = dev;
|
||||
|
||||
for (i = 0; i < simple->num_clocks; i++) {
|
||||
struct clk *clk;
|
||||
|
||||
clk = of_clk_get(np, i);
|
||||
if (IS_ERR(clk)) {
|
||||
while (--i >= 0)
|
||||
clk_put(simple->clks[i]);
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret < 0) {
|
||||
while (--i >= 0) {
|
||||
clk_disable_unprepare(simple->clks[i]);
|
||||
clk_put(simple->clks[i]);
|
||||
}
|
||||
clk_put(clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
simple->clks[i] = clk;
|
||||
}
|
||||
|
||||
ret = of_platform_populate(np, NULL, NULL, dev);
|
||||
if (ret) {
|
||||
for (i = 0; i < simple->num_clocks; i++) {
|
||||
clk_disable_unprepare(simple->clks[i]);
|
||||
clk_put(simple->clks[i]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_get_sync(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_of_simple_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3_of_simple *simple = platform_get_drvdata(pdev);
|
||||
struct device *dev = &pdev->dev;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < simple->num_clocks; i++) {
|
||||
clk_unprepare(simple->clks[i]);
|
||||
clk_put(simple->clks[i]);
|
||||
}
|
||||
|
||||
of_platform_depopulate(dev);
|
||||
|
||||
pm_runtime_put_sync(dev);
|
||||
pm_runtime_disable(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int dwc3_of_simple_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct dwc3_of_simple *simple = dev_get_drvdata(dev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < simple->num_clocks; i++)
|
||||
clk_disable(simple->clks[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_of_simple_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct dwc3_of_simple *simple = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < simple->num_clocks; i++) {
|
||||
ret = clk_enable(simple->clks[i]);
|
||||
if (ret < 0) {
|
||||
while (--i >= 0)
|
||||
clk_disable(simple->clks[i]);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops dwc3_of_simple_dev_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(dwc3_of_simple_runtime_suspend,
|
||||
dwc3_of_simple_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct of_device_id of_dwc3_simple_match[] = {
|
||||
{ .compatible = "qcom,dwc3" },
|
||||
{ .compatible = "xlnx,zynqmp-dwc3" },
|
||||
{ /* Sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_dwc3_simple_match);
|
||||
|
||||
static struct platform_driver dwc3_of_simple_driver = {
|
||||
.probe = dwc3_of_simple_probe,
|
||||
.remove = dwc3_of_simple_remove,
|
||||
.driver = {
|
||||
.name = "dwc3-of-simple",
|
||||
.of_match_table = of_dwc3_simple_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(dwc3_of_simple_driver);
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("DesignWare USB3 OF Simple Glue Layer");
|
||||
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
|
@ -1,130 +0,0 @@
|
||||
/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
struct dwc3_qcom {
|
||||
struct device *dev;
|
||||
|
||||
struct clk *core_clk;
|
||||
struct clk *iface_clk;
|
||||
struct clk *sleep_clk;
|
||||
};
|
||||
|
||||
static int dwc3_qcom_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct dwc3_qcom *qdwc;
|
||||
int ret;
|
||||
|
||||
qdwc = devm_kzalloc(&pdev->dev, sizeof(*qdwc), GFP_KERNEL);
|
||||
if (!qdwc)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, qdwc);
|
||||
|
||||
qdwc->dev = &pdev->dev;
|
||||
|
||||
qdwc->core_clk = devm_clk_get(qdwc->dev, "core");
|
||||
if (IS_ERR(qdwc->core_clk)) {
|
||||
dev_err(qdwc->dev, "failed to get core clock\n");
|
||||
return PTR_ERR(qdwc->core_clk);
|
||||
}
|
||||
|
||||
qdwc->iface_clk = devm_clk_get(qdwc->dev, "iface");
|
||||
if (IS_ERR(qdwc->iface_clk)) {
|
||||
dev_info(qdwc->dev, "failed to get optional iface clock\n");
|
||||
qdwc->iface_clk = NULL;
|
||||
}
|
||||
|
||||
qdwc->sleep_clk = devm_clk_get(qdwc->dev, "sleep");
|
||||
if (IS_ERR(qdwc->sleep_clk)) {
|
||||
dev_info(qdwc->dev, "failed to get optional sleep clock\n");
|
||||
qdwc->sleep_clk = NULL;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(qdwc->core_clk);
|
||||
if (ret) {
|
||||
dev_err(qdwc->dev, "failed to enable core clock\n");
|
||||
goto err_core;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(qdwc->iface_clk);
|
||||
if (ret) {
|
||||
dev_err(qdwc->dev, "failed to enable optional iface clock\n");
|
||||
goto err_iface;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(qdwc->sleep_clk);
|
||||
if (ret) {
|
||||
dev_err(qdwc->dev, "failed to enable optional sleep clock\n");
|
||||
goto err_sleep;
|
||||
}
|
||||
|
||||
ret = of_platform_populate(node, NULL, NULL, qdwc->dev);
|
||||
if (ret) {
|
||||
dev_err(qdwc->dev, "failed to register core - %d\n", ret);
|
||||
goto err_clks;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_clks:
|
||||
clk_disable_unprepare(qdwc->sleep_clk);
|
||||
err_sleep:
|
||||
clk_disable_unprepare(qdwc->iface_clk);
|
||||
err_iface:
|
||||
clk_disable_unprepare(qdwc->core_clk);
|
||||
err_core:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dwc3_qcom_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3_qcom *qdwc = platform_get_drvdata(pdev);
|
||||
|
||||
of_platform_depopulate(&pdev->dev);
|
||||
|
||||
clk_disable_unprepare(qdwc->sleep_clk);
|
||||
clk_disable_unprepare(qdwc->iface_clk);
|
||||
clk_disable_unprepare(qdwc->core_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id of_dwc3_match[] = {
|
||||
{ .compatible = "qcom,dwc3" },
|
||||
{ /* Sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_dwc3_match);
|
||||
|
||||
static struct platform_driver dwc3_qcom_driver = {
|
||||
.probe = dwc3_qcom_probe,
|
||||
.remove = dwc3_qcom_remove,
|
||||
.driver = {
|
||||
.name = "qcom-dwc3",
|
||||
.of_match_table = of_dwc3_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(dwc3_qcom_driver);
|
||||
|
||||
MODULE_ALIAS("platform:qcom-dwc3");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("DesignWare USB3 QCOM Glue Layer");
|
||||
MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");
|
@ -817,6 +817,8 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
|
||||
|
||||
status = DWC3_TRB_SIZE_TRBSTS(trb->size);
|
||||
if (status == DWC3_TRBSTS_SETUP_PENDING) {
|
||||
dwc->setup_packet_pending = true;
|
||||
|
||||
dwc3_trace(trace_dwc3_ep0, "Setup Pending received");
|
||||
|
||||
if (r)
|
||||
@ -916,8 +918,10 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
|
||||
}
|
||||
|
||||
status = DWC3_TRB_SIZE_TRBSTS(trb->size);
|
||||
if (status == DWC3_TRBSTS_SETUP_PENDING)
|
||||
if (status == DWC3_TRBSTS_SETUP_PENDING) {
|
||||
dwc->setup_packet_pending = true;
|
||||
dwc3_trace(trace_dwc3_ep0, "Setup Pending received");
|
||||
}
|
||||
|
||||
dwc->ep0state = EP0_SETUP_PHASE;
|
||||
dwc3_ep0_out_start(dwc);
|
||||
@ -971,7 +975,7 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
|
||||
ret = usb_gadget_map_request(&dwc->gadget, &req->request,
|
||||
dep->number);
|
||||
if (ret) {
|
||||
dev_dbg(dwc->dev, "failed to map request\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "failed to map request\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -999,7 +1003,7 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
|
||||
ret = usb_gadget_map_request(&dwc->gadget, &req->request,
|
||||
dep->number);
|
||||
if (ret) {
|
||||
dev_dbg(dwc->dev, "failed to map request\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "failed to map request\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1063,8 +1067,6 @@ static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
|
||||
static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
|
||||
const struct dwc3_event_depevt *event)
|
||||
{
|
||||
dwc->setup_packet_pending = true;
|
||||
|
||||
switch (event->status) {
|
||||
case DEPEVT_STATUS_CONTROL_DATA:
|
||||
dwc3_trace(trace_dwc3_ep0, "Control Data");
|
||||
|
@ -265,9 +265,6 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
|
||||
usb_gadget_unmap_request(&dwc->gadget, &req->request,
|
||||
req->direction);
|
||||
|
||||
dev_dbg(dwc->dev, "request %p from %s completed %d/%d ===> %d\n",
|
||||
req, dep->name, req->request.actual,
|
||||
req->request.length, status);
|
||||
trace_dwc3_gadget_giveback(req);
|
||||
|
||||
spin_unlock(&dwc->lock);
|
||||
@ -664,11 +661,10 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
|
||||
dep = to_dwc3_ep(ep);
|
||||
dwc = dep->dwc;
|
||||
|
||||
if (dep->flags & DWC3_EP_ENABLED) {
|
||||
dev_WARN_ONCE(dwc->dev, true, "%s is already enabled\n",
|
||||
dep->name);
|
||||
if (dev_WARN_ONCE(dwc->dev, dep->flags & DWC3_EP_ENABLED,
|
||||
"%s is already enabled\n",
|
||||
dep->name))
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc, false, false);
|
||||
@ -692,11 +688,10 @@ static int dwc3_gadget_ep_disable(struct usb_ep *ep)
|
||||
dep = to_dwc3_ep(ep);
|
||||
dwc = dep->dwc;
|
||||
|
||||
if (!(dep->flags & DWC3_EP_ENABLED)) {
|
||||
dev_WARN_ONCE(dwc->dev, true, "%s is already disabled\n",
|
||||
dep->name);
|
||||
if (dev_WARN_ONCE(dwc->dev, !(dep->flags & DWC3_EP_ENABLED),
|
||||
"%s is already disabled\n",
|
||||
dep->name))
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
ret = __dwc3_gadget_ep_disable(dep);
|
||||
@ -985,8 +980,6 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
|
||||
cmd |= DWC3_DEPCMD_PARAM(cmd_param);
|
||||
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms);
|
||||
if (ret < 0) {
|
||||
dev_dbg(dwc->dev, "failed to send STARTTRANSFER command\n");
|
||||
|
||||
/*
|
||||
* FIXME we need to iterate over the list of requests
|
||||
* here and stop, unmap, free and del each of the linked
|
||||
@ -1044,6 +1037,20 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
int ret;
|
||||
|
||||
if (!dep->endpoint.desc) {
|
||||
dwc3_trace(trace_dwc3_gadget,
|
||||
"trying to queue request %p to disabled %s\n",
|
||||
&req->request, dep->endpoint.name);
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
|
||||
if (WARN(req->dep != dep, "request %p belongs to '%s'\n",
|
||||
&req->request, req->dep->name)) {
|
||||
dwc3_trace(trace_dwc3_gadget, "request %p belongs to '%s'\n",
|
||||
&req->request, req->dep->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
req->request.actual = 0;
|
||||
req->request.status = -EINPROGRESS;
|
||||
req->direction = dep->direction;
|
||||
@ -1141,7 +1148,8 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
||||
|
||||
out:
|
||||
if (ret && ret != -EBUSY)
|
||||
dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
|
||||
dwc3_trace(trace_dwc3_gadget,
|
||||
"%s: failed to kick transfers\n",
|
||||
dep->name);
|
||||
if (ret == -EBUSY)
|
||||
ret = 0;
|
||||
@ -1149,6 +1157,32 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __dwc3_gadget_ep_zlp_complete(struct usb_ep *ep,
|
||||
struct usb_request *request)
|
||||
{
|
||||
dwc3_gadget_ep_free_request(ep, request);
|
||||
}
|
||||
|
||||
static int __dwc3_gadget_ep_queue_zlp(struct dwc3 *dwc, struct dwc3_ep *dep)
|
||||
{
|
||||
struct dwc3_request *req;
|
||||
struct usb_request *request;
|
||||
struct usb_ep *ep = &dep->endpoint;
|
||||
|
||||
dwc3_trace(trace_dwc3_gadget, "queueing ZLP\n");
|
||||
request = dwc3_gadget_ep_alloc_request(ep, GFP_ATOMIC);
|
||||
if (!request)
|
||||
return -ENOMEM;
|
||||
|
||||
request->length = 0;
|
||||
request->buf = dwc->zlp_buf;
|
||||
request->complete = __dwc3_gadget_ep_zlp_complete;
|
||||
|
||||
req = to_dwc3_request(request);
|
||||
|
||||
return __dwc3_gadget_ep_queue(dep, req);
|
||||
}
|
||||
|
||||
static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
|
||||
gfp_t gfp_flags)
|
||||
{
|
||||
@ -1161,22 +1195,18 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
if (!dep->endpoint.desc) {
|
||||
dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n",
|
||||
request, ep->name);
|
||||
ret = -ESHUTDOWN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (WARN(req->dep != dep, "request %p belongs to '%s'\n",
|
||||
request, req->dep->name)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = __dwc3_gadget_ep_queue(dep, req);
|
||||
|
||||
out:
|
||||
/*
|
||||
* Okay, here's the thing, if gadget driver has requested for a ZLP by
|
||||
* setting request->zero, instead of doing magic, we will just queue an
|
||||
* extra usb_request ourselves so that it gets handled the same way as
|
||||
* any other request.
|
||||
*/
|
||||
if (ret == 0 && request->zero && request->length &&
|
||||
(request->length % ep->maxpacket == 0))
|
||||
ret = __dwc3_gadget_ep_queue_zlp(dwc, dep);
|
||||
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return ret;
|
||||
@ -1246,7 +1276,8 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
|
||||
if (!protocol && ((dep->direction && dep->flags & DWC3_EP_BUSY) ||
|
||||
(!list_empty(&dep->req_queued) ||
|
||||
!list_empty(&dep->request_list)))) {
|
||||
dev_dbg(dwc->dev, "%s: pending request, cannot halt\n",
|
||||
dwc3_trace(trace_dwc3_gadget,
|
||||
"%s: pending request, cannot halt\n",
|
||||
dep->name);
|
||||
return -EAGAIN;
|
||||
}
|
||||
@ -1373,7 +1404,7 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
|
||||
|
||||
speed = reg & DWC3_DSTS_CONNECTSPD;
|
||||
if (speed == DWC3_DSTS_SUPERSPEED) {
|
||||
dev_dbg(dwc->dev, "no wakeup on SuperSpeed\n");
|
||||
dwc3_trace(trace_dwc3_gadget, "no wakeup on SuperSpeed\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
@ -1385,8 +1416,9 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
|
||||
case DWC3_LINK_STATE_U3: /* in HS, means SUSPEND */
|
||||
break;
|
||||
default:
|
||||
dev_dbg(dwc->dev, "can't wakeup from link state %d\n",
|
||||
link_state);
|
||||
dwc3_trace(trace_dwc3_gadget,
|
||||
"can't wakeup from '%s'\n",
|
||||
dwc3_gadget_link_string(link_state));
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
@ -1825,7 +1857,8 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
||||
if (count) {
|
||||
trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size);
|
||||
if (trb_status == DWC3_TRBSTS_MISSED_ISOC) {
|
||||
dev_dbg(dwc->dev, "incomplete IN transfer %s\n",
|
||||
dwc3_trace(trace_dwc3_gadget,
|
||||
"%s: incomplete IN transfer\n",
|
||||
dep->name);
|
||||
/*
|
||||
* If missed isoc occurred and there is
|
||||
@ -1887,10 +1920,9 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
||||
|
||||
do {
|
||||
req = next_request(&dep->req_queued);
|
||||
if (!req) {
|
||||
WARN_ON_ONCE(1);
|
||||
if (WARN_ON_ONCE(!req))
|
||||
return 1;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
do {
|
||||
slot = req->start_slot + i;
|
||||
@ -2004,7 +2036,8 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
|
||||
dep->resource_index = 0;
|
||||
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
||||
dev_dbg(dwc->dev, "%s is an Isochronous endpoint\n",
|
||||
dwc3_trace(trace_dwc3_gadget,
|
||||
"%s is an Isochronous endpoint\n",
|
||||
dep->name);
|
||||
return;
|
||||
}
|
||||
@ -2031,7 +2064,8 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
|
||||
if (!ret || ret == -EBUSY)
|
||||
return;
|
||||
|
||||
dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
|
||||
dwc3_trace(trace_dwc3_gadget,
|
||||
"%s: failed to kick transfers\n",
|
||||
dep->name);
|
||||
}
|
||||
|
||||
@ -2053,11 +2087,12 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
|
||||
case DEPEVT_STREAMEVT_NOTFOUND:
|
||||
/* FALLTHROUGH */
|
||||
default:
|
||||
dev_dbg(dwc->dev, "Couldn't find suitable stream\n");
|
||||
dwc3_trace(trace_dwc3_gadget,
|
||||
"unable to find suitable stream\n");
|
||||
}
|
||||
break;
|
||||
case DWC3_DEPEVT_RXTXFIFOEVT:
|
||||
dev_dbg(dwc->dev, "%s FIFO Overrun\n", dep->name);
|
||||
dwc3_trace(trace_dwc3_gadget, "%s FIFO Overrun\n", dep->name);
|
||||
break;
|
||||
case DWC3_DEPEVT_EPCMDCMPLT:
|
||||
dwc3_trace(trace_dwc3_gadget, "Endpoint Command Complete");
|
||||
@ -2230,8 +2265,8 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
|
||||
*
|
||||
* Our suggested workaround is to follow the Disconnect
|
||||
* Event steps here, instead, based on a setup_packet_pending
|
||||
* flag. Such flag gets set whenever we have a XferNotReady
|
||||
* event on EP0 and gets cleared on XferComplete for the
|
||||
* flag. Such flag gets set whenever we have a SETUP_PENDING
|
||||
* status for EP0 TRBs and gets cleared on XferComplete for the
|
||||
* same endpoint.
|
||||
*
|
||||
* Refers to:
|
||||
@ -2744,6 +2779,12 @@ int dwc3_gadget_init(struct dwc3 *dwc)
|
||||
goto err3;
|
||||
}
|
||||
|
||||
dwc->zlp_buf = kzalloc(DWC3_ZLP_BUF_SIZE, GFP_KERNEL);
|
||||
if (!dwc->zlp_buf) {
|
||||
ret = -ENOMEM;
|
||||
goto err4;
|
||||
}
|
||||
|
||||
dwc->gadget.ops = &dwc3_gadget_ops;
|
||||
dwc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
dwc->gadget.sg_supported = true;
|
||||
@ -2785,16 +2826,19 @@ int dwc3_gadget_init(struct dwc3 *dwc)
|
||||
|
||||
ret = dwc3_gadget_init_endpoints(dwc);
|
||||
if (ret)
|
||||
goto err4;
|
||||
goto err5;
|
||||
|
||||
ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to register udc\n");
|
||||
goto err4;
|
||||
goto err5;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err5:
|
||||
kfree(dwc->zlp_buf);
|
||||
|
||||
err4:
|
||||
dwc3_gadget_free_endpoints(dwc);
|
||||
dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE,
|
||||
@ -2827,6 +2871,7 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
|
||||
dwc->ep0_bounce, dwc->ep0_bounce_addr);
|
||||
|
||||
kfree(dwc->setup_buf);
|
||||
kfree(dwc->zlp_buf);
|
||||
|
||||
dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb),
|
||||
dwc->ep0_trb, dwc->ep0_trb_addr);
|
||||
|
@ -117,6 +117,9 @@ DECLARE_EVENT_CLASS(dwc3_log_request,
|
||||
__field(unsigned, actual)
|
||||
__field(unsigned, length)
|
||||
__field(int, status)
|
||||
__field(int, zero)
|
||||
__field(int, short_not_ok)
|
||||
__field(int, no_interrupt)
|
||||
),
|
||||
TP_fast_assign(
|
||||
snprintf(__get_str(name), DWC3_MSG_MAX, "%s", req->dep->name);
|
||||
@ -124,9 +127,15 @@ DECLARE_EVENT_CLASS(dwc3_log_request,
|
||||
__entry->actual = req->request.actual;
|
||||
__entry->length = req->request.length;
|
||||
__entry->status = req->request.status;
|
||||
__entry->zero = req->request.zero;
|
||||
__entry->short_not_ok = req->request.short_not_ok;
|
||||
__entry->no_interrupt = req->request.no_interrupt;
|
||||
),
|
||||
TP_printk("%s: req %p length %u/%u ==> %d",
|
||||
TP_printk("%s: req %p length %u/%u %s%s%s ==> %d",
|
||||
__get_str(name), __entry->req, __entry->actual, __entry->length,
|
||||
__entry->zero ? "Z" : "z",
|
||||
__entry->short_not_ok ? "S" : "s",
|
||||
__entry->no_interrupt ? "i" : "I",
|
||||
__entry->status
|
||||
)
|
||||
);
|
||||
|
@ -127,6 +127,12 @@ config USB_GADGET_STORAGE_NUM_BUFFERS
|
||||
a module parameter as well.
|
||||
If unsure, say 2.
|
||||
|
||||
config U_SERIAL_CONSOLE
|
||||
bool "Serial gadget console support"
|
||||
depends on USB_G_SERIAL
|
||||
help
|
||||
It supports the serial gadget can be used as a console.
|
||||
|
||||
source "drivers/usb/gadget/udc/Kconfig"
|
||||
|
||||
#
|
||||
|
@ -56,7 +56,6 @@ struct gadget_info {
|
||||
struct list_head string_list;
|
||||
struct list_head available_func;
|
||||
|
||||
const char *udc_name;
|
||||
struct usb_composite_driver composite;
|
||||
struct usb_composite_dev cdev;
|
||||
bool use_os_desc;
|
||||
@ -233,21 +232,23 @@ static ssize_t gadget_dev_desc_bcdUSB_store(struct config_item *item,
|
||||
|
||||
static ssize_t gadget_dev_desc_UDC_show(struct config_item *item, char *page)
|
||||
{
|
||||
return sprintf(page, "%s\n", to_gadget_info(item)->udc_name ?: "");
|
||||
char *udc_name = to_gadget_info(item)->composite.gadget_driver.udc_name;
|
||||
|
||||
return sprintf(page, "%s\n", udc_name ?: "");
|
||||
}
|
||||
|
||||
static int unregister_gadget(struct gadget_info *gi)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!gi->udc_name)
|
||||
if (!gi->composite.gadget_driver.udc_name)
|
||||
return -ENODEV;
|
||||
|
||||
ret = usb_gadget_unregister_driver(&gi->composite.gadget_driver);
|
||||
if (ret)
|
||||
return ret;
|
||||
kfree(gi->udc_name);
|
||||
gi->udc_name = NULL;
|
||||
kfree(gi->composite.gadget_driver.udc_name);
|
||||
gi->composite.gadget_driver.udc_name = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -271,14 +272,16 @@ static ssize_t gadget_dev_desc_UDC_store(struct config_item *item,
|
||||
if (ret)
|
||||
goto err;
|
||||
} else {
|
||||
if (gi->udc_name) {
|
||||
if (gi->composite.gadget_driver.udc_name) {
|
||||
ret = -EBUSY;
|
||||
goto err;
|
||||
}
|
||||
ret = usb_udc_attach_driver(name, &gi->composite.gadget_driver);
|
||||
if (ret)
|
||||
gi->composite.gadget_driver.udc_name = name;
|
||||
ret = usb_gadget_probe_driver(&gi->composite.gadget_driver);
|
||||
if (ret) {
|
||||
gi->composite.gadget_driver.udc_name = NULL;
|
||||
goto err;
|
||||
gi->udc_name = name;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&gi->lock);
|
||||
return len;
|
||||
@ -427,9 +430,9 @@ static int config_usb_cfg_unlink(
|
||||
* remove the function.
|
||||
*/
|
||||
mutex_lock(&gi->lock);
|
||||
if (gi->udc_name)
|
||||
if (gi->composite.gadget_driver.udc_name)
|
||||
unregister_gadget(gi);
|
||||
WARN_ON(gi->udc_name);
|
||||
WARN_ON(gi->composite.gadget_driver.udc_name);
|
||||
|
||||
list_for_each_entry(f, &cfg->func_list, list) {
|
||||
if (f->fi == fi) {
|
||||
@ -873,10 +876,10 @@ static int os_desc_unlink(struct config_item *os_desc_ci,
|
||||
struct usb_composite_dev *cdev = &gi->cdev;
|
||||
|
||||
mutex_lock(&gi->lock);
|
||||
if (gi->udc_name)
|
||||
if (gi->composite.gadget_driver.udc_name)
|
||||
unregister_gadget(gi);
|
||||
cdev->os_desc_config = NULL;
|
||||
WARN_ON(gi->udc_name);
|
||||
WARN_ON(gi->composite.gadget_driver.udc_name);
|
||||
mutex_unlock(&gi->lock);
|
||||
return 0;
|
||||
}
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kfifo.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/initval.h>
|
||||
@ -75,6 +76,7 @@ struct f_midi {
|
||||
struct usb_ep *in_ep, *out_ep;
|
||||
struct snd_card *card;
|
||||
struct snd_rawmidi *rmidi;
|
||||
u8 ms_id;
|
||||
|
||||
struct snd_rawmidi_substream *in_substream[MAX_PORTS];
|
||||
struct snd_rawmidi_substream *out_substream[MAX_PORTS];
|
||||
@ -87,6 +89,9 @@ struct f_midi {
|
||||
int index;
|
||||
char *id;
|
||||
unsigned int buflen, qlen;
|
||||
/* This fifo is used as a buffer ring for pre-allocated IN usb_requests */
|
||||
DECLARE_KFIFO_PTR(in_req_fifo, struct usb_request *);
|
||||
unsigned int in_last_port;
|
||||
};
|
||||
|
||||
static inline struct f_midi *func_to_midi(struct usb_function *f)
|
||||
@ -94,7 +99,7 @@ static inline struct f_midi *func_to_midi(struct usb_function *f)
|
||||
return container_of(f, struct f_midi, func);
|
||||
}
|
||||
|
||||
static void f_midi_transmit(struct f_midi *midi, struct usb_request *req);
|
||||
static void f_midi_transmit(struct f_midi *midi);
|
||||
|
||||
DECLARE_UAC_AC_HEADER_DESCRIPTOR(1);
|
||||
DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1);
|
||||
@ -201,12 +206,6 @@ static inline struct usb_request *midi_alloc_ep_req(struct usb_ep *ep,
|
||||
return alloc_ep_req(ep, length, length);
|
||||
}
|
||||
|
||||
static void free_ep_req(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
kfree(req->buf);
|
||||
usb_ep_free_request(ep, req);
|
||||
}
|
||||
|
||||
static const uint8_t f_midi_cin_length[] = {
|
||||
0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1
|
||||
};
|
||||
@ -258,7 +257,8 @@ f_midi_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
} else if (ep == midi->in_ep) {
|
||||
/* Our transmit completed. See if there's more to go.
|
||||
* f_midi_transmit eats req, don't queue it again. */
|
||||
f_midi_transmit(midi, req);
|
||||
req->length = 0;
|
||||
f_midi_transmit(midi);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@ -269,10 +269,12 @@ f_midi_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
case -ESHUTDOWN: /* disconnect from host */
|
||||
VDBG(cdev, "%s gone (%d), %d/%d\n", ep->name, status,
|
||||
req->actual, req->length);
|
||||
if (ep == midi->out_ep)
|
||||
if (ep == midi->out_ep) {
|
||||
f_midi_handle_out_data(ep, req);
|
||||
|
||||
free_ep_req(ep, req);
|
||||
/* We don't need to free IN requests because it's handled
|
||||
* by the midi->in_req_fifo. */
|
||||
free_ep_req(ep, req);
|
||||
}
|
||||
return;
|
||||
|
||||
case -EOVERFLOW: /* buffer overrun on read means that
|
||||
@ -324,12 +326,11 @@ static int f_midi_start_ep(struct f_midi *midi,
|
||||
static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
||||
{
|
||||
struct f_midi *midi = func_to_midi(f);
|
||||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
unsigned i;
|
||||
int err;
|
||||
|
||||
/* For Control Device interface we do nothing */
|
||||
if (intf == 0)
|
||||
/* we only set alt for MIDIStreaming interface */
|
||||
if (intf != midi->ms_id)
|
||||
return 0;
|
||||
|
||||
err = f_midi_start_ep(midi, f, midi->in_ep);
|
||||
@ -340,24 +341,20 @@ static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
usb_ep_disable(midi->out_ep);
|
||||
/* pre-allocate write usb requests to use on f_midi_transmit. */
|
||||
while (kfifo_avail(&midi->in_req_fifo)) {
|
||||
struct usb_request *req =
|
||||
midi_alloc_ep_req(midi->in_ep, midi->buflen);
|
||||
|
||||
err = config_ep_by_speed(midi->gadget, f, midi->out_ep);
|
||||
if (err) {
|
||||
ERROR(cdev, "can't configure %s: %d\n",
|
||||
midi->out_ep->name, err);
|
||||
return err;
|
||||
if (req == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
req->length = 0;
|
||||
req->complete = f_midi_complete;
|
||||
|
||||
kfifo_put(&midi->in_req_fifo, req);
|
||||
}
|
||||
|
||||
err = usb_ep_enable(midi->out_ep);
|
||||
if (err) {
|
||||
ERROR(cdev, "can't start %s: %d\n",
|
||||
midi->out_ep->name, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
midi->out_ep->driver_data = midi;
|
||||
|
||||
/* allocate a bunch of read buffers and queue them all at once. */
|
||||
for (i = 0; i < midi->qlen && err == 0; i++) {
|
||||
struct usb_request *req =
|
||||
@ -368,9 +365,10 @@ static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
||||
req->complete = f_midi_complete;
|
||||
err = usb_ep_queue(midi->out_ep, req, GFP_ATOMIC);
|
||||
if (err) {
|
||||
ERROR(midi, "%s queue req: %d\n",
|
||||
ERROR(midi, "%s: couldn't enqueue request: %d\n",
|
||||
midi->out_ep->name, err);
|
||||
free_ep_req(midi->out_ep, req);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
@ -381,6 +379,7 @@ static void f_midi_disable(struct usb_function *f)
|
||||
{
|
||||
struct f_midi *midi = func_to_midi(f);
|
||||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
struct usb_request *req = NULL;
|
||||
|
||||
DBG(cdev, "disable\n");
|
||||
|
||||
@ -390,6 +389,10 @@ static void f_midi_disable(struct usb_function *f)
|
||||
*/
|
||||
usb_ep_disable(midi->in_ep);
|
||||
usb_ep_disable(midi->out_ep);
|
||||
|
||||
/* release IN requests */
|
||||
while (kfifo_get(&midi->in_req_fifo, &req))
|
||||
free_ep_req(midi->in_ep, req);
|
||||
}
|
||||
|
||||
static int f_midi_snd_free(struct snd_device *device)
|
||||
@ -511,57 +514,113 @@ static void f_midi_transmit_byte(struct usb_request *req,
|
||||
}
|
||||
}
|
||||
|
||||
static void f_midi_transmit(struct f_midi *midi, struct usb_request *req)
|
||||
static void f_midi_drop_out_substreams(struct f_midi *midi)
|
||||
{
|
||||
struct usb_ep *ep = midi->in_ep;
|
||||
int i;
|
||||
|
||||
if (!ep)
|
||||
return;
|
||||
|
||||
if (!req)
|
||||
req = midi_alloc_ep_req(ep, midi->buflen);
|
||||
|
||||
if (!req) {
|
||||
ERROR(midi, "%s: alloc_ep_request failed\n", __func__);
|
||||
return;
|
||||
}
|
||||
req->length = 0;
|
||||
req->complete = f_midi_complete;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < MAX_PORTS; i++) {
|
||||
struct gmidi_in_port *port = midi->in_port[i];
|
||||
struct snd_rawmidi_substream *substream = midi->in_substream[i];
|
||||
|
||||
if (!port || !port->active || !substream)
|
||||
if (!port)
|
||||
break;
|
||||
|
||||
if (!port->active || !substream)
|
||||
continue;
|
||||
|
||||
while (req->length + 3 < midi->buflen) {
|
||||
uint8_t b;
|
||||
if (snd_rawmidi_transmit(substream, &b, 1) != 1) {
|
||||
port->active = 0;
|
||||
snd_rawmidi_drop_output(substream);
|
||||
}
|
||||
}
|
||||
|
||||
static void f_midi_transmit(struct f_midi *midi)
|
||||
{
|
||||
struct usb_ep *ep = midi->in_ep;
|
||||
bool active;
|
||||
|
||||
/* We only care about USB requests if IN endpoint is enabled */
|
||||
if (!ep || !ep->enabled)
|
||||
goto drop_out;
|
||||
|
||||
do {
|
||||
struct usb_request *req = NULL;
|
||||
unsigned int len, i;
|
||||
|
||||
active = false;
|
||||
|
||||
/* We peek the request in order to reuse it if it fails
|
||||
* to enqueue on its endpoint */
|
||||
len = kfifo_peek(&midi->in_req_fifo, &req);
|
||||
if (len != 1) {
|
||||
ERROR(midi, "%s: Couldn't get usb request\n", __func__);
|
||||
goto drop_out;
|
||||
}
|
||||
|
||||
/* If buffer overrun, then we ignore this transmission.
|
||||
* IMPORTANT: This will cause the user-space rawmidi device to block until a) usb
|
||||
* requests have been completed or b) snd_rawmidi_write() times out. */
|
||||
if (req->length > 0)
|
||||
return;
|
||||
|
||||
for (i = midi->in_last_port; i < MAX_PORTS; i++) {
|
||||
struct gmidi_in_port *port = midi->in_port[i];
|
||||
struct snd_rawmidi_substream *substream = midi->in_substream[i];
|
||||
|
||||
if (!port) {
|
||||
/* Reset counter when we reach the last available port */
|
||||
midi->in_last_port = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!port->active || !substream)
|
||||
continue;
|
||||
|
||||
while (req->length + 3 < midi->buflen) {
|
||||
uint8_t b;
|
||||
|
||||
if (snd_rawmidi_transmit(substream, &b, 1) != 1) {
|
||||
port->active = 0;
|
||||
break;
|
||||
}
|
||||
f_midi_transmit_byte(req, port, b);
|
||||
}
|
||||
|
||||
active = !!port->active;
|
||||
/* Check if last port is still active, which means that
|
||||
* there is still data on that substream but this current
|
||||
* request run out of space. */
|
||||
if (active) {
|
||||
midi->in_last_port = i;
|
||||
/* There is no need to re-iterate though midi ports. */
|
||||
break;
|
||||
}
|
||||
f_midi_transmit_byte(req, port, b);
|
||||
}
|
||||
}
|
||||
|
||||
if (req->length > 0 && ep->enabled) {
|
||||
int err;
|
||||
if (req->length > 0) {
|
||||
int err;
|
||||
|
||||
err = usb_ep_queue(ep, req, GFP_ATOMIC);
|
||||
if (err < 0)
|
||||
ERROR(midi, "%s queue req: %d\n",
|
||||
midi->in_ep->name, err);
|
||||
} else {
|
||||
free_ep_req(ep, req);
|
||||
}
|
||||
err = usb_ep_queue(ep, req, GFP_ATOMIC);
|
||||
if (err < 0) {
|
||||
ERROR(midi, "%s failed to queue req: %d\n",
|
||||
midi->in_ep->name, err);
|
||||
req->length = 0; /* Re-use request next time. */
|
||||
} else {
|
||||
/* Upon success, put request at the back of the queue. */
|
||||
kfifo_skip(&midi->in_req_fifo);
|
||||
kfifo_put(&midi->in_req_fifo, req);
|
||||
}
|
||||
}
|
||||
} while (active);
|
||||
|
||||
return;
|
||||
|
||||
drop_out:
|
||||
f_midi_drop_out_substreams(midi);
|
||||
}
|
||||
|
||||
static void f_midi_in_tasklet(unsigned long data)
|
||||
{
|
||||
struct f_midi *midi = (struct f_midi *) data;
|
||||
f_midi_transmit(midi, NULL);
|
||||
f_midi_transmit(midi);
|
||||
}
|
||||
|
||||
static int f_midi_in_open(struct snd_rawmidi_substream *substream)
|
||||
@ -687,6 +746,7 @@ static int f_midi_register_card(struct f_midi *midi)
|
||||
goto fail;
|
||||
}
|
||||
midi->rmidi = rmidi;
|
||||
midi->in_last_port = 0;
|
||||
strcpy(rmidi->name, card->shortname);
|
||||
rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
|
||||
SNDRV_RAWMIDI_INFO_INPUT |
|
||||
@ -755,6 +815,7 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
goto fail;
|
||||
ms_interface_desc.bInterfaceNumber = status;
|
||||
ac_header_desc.baInterfaceNr[0] = status;
|
||||
midi->ms_id = status;
|
||||
|
||||
status = -ENODEV;
|
||||
|
||||
@ -1075,6 +1136,7 @@ static void f_midi_free(struct usb_function *f)
|
||||
mutex_lock(&opts->lock);
|
||||
for (i = opts->in_ports - 1; i >= 0; --i)
|
||||
kfree(midi->in_port[i]);
|
||||
kfifo_free(&midi->in_req_fifo);
|
||||
kfree(midi);
|
||||
--opts->refcnt;
|
||||
mutex_unlock(&opts->lock);
|
||||
@ -1148,6 +1210,12 @@ static struct usb_function *f_midi_alloc(struct usb_function_instance *fi)
|
||||
midi->index = opts->index;
|
||||
midi->buflen = opts->buflen;
|
||||
midi->qlen = opts->qlen;
|
||||
midi->in_last_port = 0;
|
||||
|
||||
status = kfifo_alloc(&midi->in_req_fifo, midi->qlen, GFP_KERNEL);
|
||||
if (status)
|
||||
goto setup_fail;
|
||||
|
||||
++opts->refcnt;
|
||||
mutex_unlock(&opts->lock);
|
||||
|
||||
|
@ -34,13 +34,6 @@
|
||||
* plus two that support control-OUT tests. If the optional "autoresume"
|
||||
* mode is enabled, it provides good functional coverage for the "USBCV"
|
||||
* test harness from USB-IF.
|
||||
*
|
||||
* Note that because this doesn't queue more than one request at a time,
|
||||
* some other function must be used to test queueing logic. The network
|
||||
* link (g_ether) is the best overall option for that, since its TX and RX
|
||||
* queues are relatively independent, will receive a range of packet sizes,
|
||||
* and can often be made to run out completely. Those issues are important
|
||||
* when stress testing peripheral controller drivers.
|
||||
*/
|
||||
struct f_sourcesink {
|
||||
struct usb_function function;
|
||||
@ -57,6 +50,8 @@ struct f_sourcesink {
|
||||
unsigned isoc_mult;
|
||||
unsigned isoc_maxburst;
|
||||
unsigned buflen;
|
||||
unsigned bulk_qlen;
|
||||
unsigned iso_qlen;
|
||||
};
|
||||
|
||||
static inline struct f_sourcesink *func_to_ss(struct usb_function *f)
|
||||
@ -303,12 +298,6 @@ static inline struct usb_request *ss_alloc_ep_req(struct usb_ep *ep, int len)
|
||||
return alloc_ep_req(ep, len, ss->buflen);
|
||||
}
|
||||
|
||||
void free_ep_req(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
kfree(req->buf);
|
||||
usb_ep_free_request(ep, req);
|
||||
}
|
||||
|
||||
static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep)
|
||||
{
|
||||
int value;
|
||||
@ -595,31 +584,33 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in,
|
||||
{
|
||||
struct usb_ep *ep;
|
||||
struct usb_request *req;
|
||||
int i, size, status;
|
||||
int i, size, qlen, status = 0;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (is_iso) {
|
||||
switch (speed) {
|
||||
case USB_SPEED_SUPER:
|
||||
size = ss->isoc_maxpacket *
|
||||
(ss->isoc_mult + 1) *
|
||||
(ss->isoc_maxburst + 1);
|
||||
break;
|
||||
case USB_SPEED_HIGH:
|
||||
size = ss->isoc_maxpacket * (ss->isoc_mult + 1);
|
||||
break;
|
||||
default:
|
||||
size = ss->isoc_maxpacket > 1023 ?
|
||||
1023 : ss->isoc_maxpacket;
|
||||
break;
|
||||
}
|
||||
ep = is_in ? ss->iso_in_ep : ss->iso_out_ep;
|
||||
req = ss_alloc_ep_req(ep, size);
|
||||
} else {
|
||||
ep = is_in ? ss->in_ep : ss->out_ep;
|
||||
req = ss_alloc_ep_req(ep, 0);
|
||||
if (is_iso) {
|
||||
switch (speed) {
|
||||
case USB_SPEED_SUPER:
|
||||
size = ss->isoc_maxpacket *
|
||||
(ss->isoc_mult + 1) *
|
||||
(ss->isoc_maxburst + 1);
|
||||
break;
|
||||
case USB_SPEED_HIGH:
|
||||
size = ss->isoc_maxpacket * (ss->isoc_mult + 1);
|
||||
break;
|
||||
default:
|
||||
size = ss->isoc_maxpacket > 1023 ?
|
||||
1023 : ss->isoc_maxpacket;
|
||||
break;
|
||||
}
|
||||
ep = is_in ? ss->iso_in_ep : ss->iso_out_ep;
|
||||
qlen = ss->iso_qlen;
|
||||
} else {
|
||||
ep = is_in ? ss->in_ep : ss->out_ep;
|
||||
qlen = ss->bulk_qlen;
|
||||
size = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < qlen; i++) {
|
||||
req = ss_alloc_ep_req(ep, size);
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -638,10 +629,8 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in,
|
||||
is_iso ? "ISO-" : "", is_in ? "IN" : "OUT",
|
||||
ep->name, status);
|
||||
free_ep_req(ep, req);
|
||||
return status;
|
||||
}
|
||||
|
||||
if (!is_iso)
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
@ -869,6 +858,8 @@ static struct usb_function *source_sink_alloc_func(
|
||||
ss->isoc_mult = ss_opts->isoc_mult;
|
||||
ss->isoc_maxburst = ss_opts->isoc_maxburst;
|
||||
ss->buflen = ss_opts->bulk_buflen;
|
||||
ss->bulk_qlen = ss_opts->bulk_qlen;
|
||||
ss->iso_qlen = ss_opts->iso_qlen;
|
||||
|
||||
ss->function.name = "source/sink";
|
||||
ss->function.bind = sourcesink_bind;
|
||||
@ -1153,6 +1144,82 @@ end:
|
||||
|
||||
CONFIGFS_ATTR(f_ss_opts_, bulk_buflen);
|
||||
|
||||
static ssize_t f_ss_opts_bulk_qlen_show(struct config_item *item, char *page)
|
||||
{
|
||||
struct f_ss_opts *opts = to_f_ss_opts(item);
|
||||
int result;
|
||||
|
||||
mutex_lock(&opts->lock);
|
||||
result = sprintf(page, "%u\n", opts->bulk_qlen);
|
||||
mutex_unlock(&opts->lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static ssize_t f_ss_opts_bulk_qlen_store(struct config_item *item,
|
||||
const char *page, size_t len)
|
||||
{
|
||||
struct f_ss_opts *opts = to_f_ss_opts(item);
|
||||
int ret;
|
||||
u32 num;
|
||||
|
||||
mutex_lock(&opts->lock);
|
||||
if (opts->refcnt) {
|
||||
ret = -EBUSY;
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = kstrtou32(page, 0, &num);
|
||||
if (ret)
|
||||
goto end;
|
||||
|
||||
opts->bulk_qlen = num;
|
||||
ret = len;
|
||||
end:
|
||||
mutex_unlock(&opts->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
CONFIGFS_ATTR(f_ss_opts_, bulk_qlen);
|
||||
|
||||
static ssize_t f_ss_opts_iso_qlen_show(struct config_item *item, char *page)
|
||||
{
|
||||
struct f_ss_opts *opts = to_f_ss_opts(item);
|
||||
int result;
|
||||
|
||||
mutex_lock(&opts->lock);
|
||||
result = sprintf(page, "%u\n", opts->iso_qlen);
|
||||
mutex_unlock(&opts->lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static ssize_t f_ss_opts_iso_qlen_store(struct config_item *item,
|
||||
const char *page, size_t len)
|
||||
{
|
||||
struct f_ss_opts *opts = to_f_ss_opts(item);
|
||||
int ret;
|
||||
u32 num;
|
||||
|
||||
mutex_lock(&opts->lock);
|
||||
if (opts->refcnt) {
|
||||
ret = -EBUSY;
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = kstrtou32(page, 0, &num);
|
||||
if (ret)
|
||||
goto end;
|
||||
|
||||
opts->iso_qlen = num;
|
||||
ret = len;
|
||||
end:
|
||||
mutex_unlock(&opts->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
CONFIGFS_ATTR(f_ss_opts_, iso_qlen);
|
||||
|
||||
static struct configfs_attribute *ss_attrs[] = {
|
||||
&f_ss_opts_attr_pattern,
|
||||
&f_ss_opts_attr_isoc_interval,
|
||||
@ -1160,6 +1227,8 @@ static struct configfs_attribute *ss_attrs[] = {
|
||||
&f_ss_opts_attr_isoc_mult,
|
||||
&f_ss_opts_attr_isoc_maxburst,
|
||||
&f_ss_opts_attr_bulk_buflen,
|
||||
&f_ss_opts_attr_bulk_qlen,
|
||||
&f_ss_opts_attr_iso_qlen,
|
||||
NULL,
|
||||
};
|
||||
|
||||
@ -1189,6 +1258,8 @@ static struct usb_function_instance *source_sink_alloc_inst(void)
|
||||
ss_opts->isoc_interval = GZERO_ISOC_INTERVAL;
|
||||
ss_opts->isoc_maxpacket = GZERO_ISOC_MAXPACKET;
|
||||
ss_opts->bulk_buflen = GZERO_BULK_BUFLEN;
|
||||
ss_opts->bulk_qlen = GZERO_SS_BULK_QLEN;
|
||||
ss_opts->iso_qlen = GZERO_SS_ISO_QLEN;
|
||||
|
||||
config_group_init_type_name(&ss_opts->func_inst.group, "",
|
||||
&ss_func_type);
|
||||
|
@ -10,6 +10,8 @@
|
||||
#define GZERO_QLEN 32
|
||||
#define GZERO_ISOC_INTERVAL 4
|
||||
#define GZERO_ISOC_MAXPACKET 1024
|
||||
#define GZERO_SS_BULK_QLEN 1
|
||||
#define GZERO_SS_ISO_QLEN 8
|
||||
|
||||
struct usb_zero_options {
|
||||
unsigned pattern;
|
||||
@ -19,6 +21,8 @@ struct usb_zero_options {
|
||||
unsigned isoc_maxburst;
|
||||
unsigned bulk_buflen;
|
||||
unsigned qlen;
|
||||
unsigned ss_bulk_qlen;
|
||||
unsigned ss_iso_qlen;
|
||||
};
|
||||
|
||||
struct f_ss_opts {
|
||||
@ -29,6 +33,8 @@ struct f_ss_opts {
|
||||
unsigned isoc_mult;
|
||||
unsigned isoc_maxburst;
|
||||
unsigned bulk_buflen;
|
||||
unsigned bulk_qlen;
|
||||
unsigned iso_qlen;
|
||||
|
||||
/*
|
||||
* Read/write access to configfs attributes is handled by configfs.
|
||||
@ -59,7 +65,6 @@ void lb_modexit(void);
|
||||
int lb_modinit(void);
|
||||
|
||||
/* common utilities */
|
||||
void free_ep_req(struct usb_ep *ep, struct usb_request *req);
|
||||
void disable_endpoints(struct usb_composite_dev *cdev,
|
||||
struct usb_ep *in, struct usb_ep *out,
|
||||
struct usb_ep *iso_in, struct usb_ep *iso_out);
|
||||
|
@ -143,21 +143,11 @@ static inline int qlen(struct usb_gadget *gadget, unsigned qmult)
|
||||
|
||||
static int ueth_change_mtu(struct net_device *net, int new_mtu)
|
||||
{
|
||||
struct eth_dev *dev = netdev_priv(net);
|
||||
unsigned long flags;
|
||||
int status = 0;
|
||||
if (new_mtu <= ETH_HLEN || new_mtu > GETHER_MAX_ETH_FRAME_LEN)
|
||||
return -ERANGE;
|
||||
net->mtu = new_mtu;
|
||||
|
||||
/* don't change MTU on "live" link (peer won't know) */
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
if (dev->port_usb)
|
||||
status = -EBUSY;
|
||||
else if (new_mtu <= ETH_HLEN || new_mtu > GETHER_MAX_ETH_FRAME_LEN)
|
||||
status = -ERANGE;
|
||||
else
|
||||
net->mtu = new_mtu;
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
||||
return status;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p)
|
||||
|
@ -27,6 +27,8 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/kthread.h>
|
||||
|
||||
#include "u_serial.h"
|
||||
|
||||
@ -79,6 +81,7 @@
|
||||
*/
|
||||
#define QUEUE_SIZE 16
|
||||
#define WRITE_BUF_SIZE 8192 /* TX only */
|
||||
#define GS_CONSOLE_BUF_SIZE 8192
|
||||
|
||||
/* circular buffer */
|
||||
struct gs_buf {
|
||||
@ -88,6 +91,17 @@ struct gs_buf {
|
||||
char *buf_put;
|
||||
};
|
||||
|
||||
/* console info */
|
||||
struct gscons_info {
|
||||
struct gs_port *port;
|
||||
struct task_struct *console_thread;
|
||||
struct gs_buf con_buf;
|
||||
/* protect the buf and busy flag */
|
||||
spinlock_t con_lock;
|
||||
int req_busy;
|
||||
struct usb_request *console_req;
|
||||
};
|
||||
|
||||
/*
|
||||
* The port structure holds info for each port, one for each minor number
|
||||
* (and thus for each /dev/ node).
|
||||
@ -1023,6 +1037,246 @@ static const struct tty_operations gs_tty_ops = {
|
||||
|
||||
static struct tty_driver *gs_tty_driver;
|
||||
|
||||
#ifdef CONFIG_U_SERIAL_CONSOLE
|
||||
|
||||
static struct gscons_info gscons_info;
|
||||
static struct console gserial_cons;
|
||||
|
||||
static struct usb_request *gs_request_new(struct usb_ep *ep)
|
||||
{
|
||||
struct usb_request *req = usb_ep_alloc_request(ep, GFP_ATOMIC);
|
||||
if (!req)
|
||||
return NULL;
|
||||
|
||||
req->buf = kmalloc(ep->maxpacket, GFP_ATOMIC);
|
||||
if (!req->buf) {
|
||||
usb_ep_free_request(ep, req);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
static void gs_request_free(struct usb_request *req, struct usb_ep *ep)
|
||||
{
|
||||
if (!req)
|
||||
return;
|
||||
|
||||
kfree(req->buf);
|
||||
usb_ep_free_request(ep, req);
|
||||
}
|
||||
|
||||
static void gs_complete_out(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct gscons_info *info = &gscons_info;
|
||||
|
||||
switch (req->status) {
|
||||
default:
|
||||
pr_warn("%s: unexpected %s status %d\n",
|
||||
__func__, ep->name, req->status);
|
||||
case 0:
|
||||
/* normal completion */
|
||||
spin_lock(&info->con_lock);
|
||||
info->req_busy = 0;
|
||||
spin_unlock(&info->con_lock);
|
||||
|
||||
wake_up_process(info->console_thread);
|
||||
break;
|
||||
case -ESHUTDOWN:
|
||||
/* disconnect */
|
||||
pr_vdebug("%s: %s shutdown\n", __func__, ep->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int gs_console_connect(int port_num)
|
||||
{
|
||||
struct gscons_info *info = &gscons_info;
|
||||
struct gs_port *port;
|
||||
struct usb_ep *ep;
|
||||
|
||||
if (port_num != gserial_cons.index) {
|
||||
pr_err("%s: port num [%d] is not support console\n",
|
||||
__func__, port_num);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
port = ports[port_num].port;
|
||||
ep = port->port_usb->in;
|
||||
if (!info->console_req) {
|
||||
info->console_req = gs_request_new(ep);
|
||||
if (!info->console_req)
|
||||
return -ENOMEM;
|
||||
info->console_req->complete = gs_complete_out;
|
||||
}
|
||||
|
||||
info->port = port;
|
||||
spin_lock(&info->con_lock);
|
||||
info->req_busy = 0;
|
||||
spin_unlock(&info->con_lock);
|
||||
pr_vdebug("port[%d] console connect!\n", port_num);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gs_console_disconnect(struct usb_ep *ep)
|
||||
{
|
||||
struct gscons_info *info = &gscons_info;
|
||||
struct usb_request *req = info->console_req;
|
||||
|
||||
gs_request_free(req, ep);
|
||||
info->console_req = NULL;
|
||||
}
|
||||
|
||||
static int gs_console_thread(void *data)
|
||||
{
|
||||
struct gscons_info *info = &gscons_info;
|
||||
struct gs_port *port;
|
||||
struct usb_request *req;
|
||||
struct usb_ep *ep;
|
||||
int xfer, ret, count, size;
|
||||
|
||||
do {
|
||||
port = info->port;
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
if (!port || !port->port_usb
|
||||
|| !port->port_usb->in || !info->console_req)
|
||||
goto sched;
|
||||
|
||||
req = info->console_req;
|
||||
ep = port->port_usb->in;
|
||||
|
||||
spin_lock_irq(&info->con_lock);
|
||||
count = gs_buf_data_avail(&info->con_buf);
|
||||
size = ep->maxpacket;
|
||||
|
||||
if (count > 0 && !info->req_busy) {
|
||||
set_current_state(TASK_RUNNING);
|
||||
if (count < size)
|
||||
size = count;
|
||||
|
||||
xfer = gs_buf_get(&info->con_buf, req->buf, size);
|
||||
req->length = xfer;
|
||||
|
||||
spin_unlock(&info->con_lock);
|
||||
ret = usb_ep_queue(ep, req, GFP_ATOMIC);
|
||||
spin_lock(&info->con_lock);
|
||||
if (ret < 0)
|
||||
info->req_busy = 0;
|
||||
else
|
||||
info->req_busy = 1;
|
||||
|
||||
spin_unlock_irq(&info->con_lock);
|
||||
} else {
|
||||
spin_unlock_irq(&info->con_lock);
|
||||
sched:
|
||||
if (kthread_should_stop()) {
|
||||
set_current_state(TASK_RUNNING);
|
||||
break;
|
||||
}
|
||||
schedule();
|
||||
}
|
||||
} while (1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gs_console_setup(struct console *co, char *options)
|
||||
{
|
||||
struct gscons_info *info = &gscons_info;
|
||||
int status;
|
||||
|
||||
info->port = NULL;
|
||||
info->console_req = NULL;
|
||||
info->req_busy = 0;
|
||||
spin_lock_init(&info->con_lock);
|
||||
|
||||
status = gs_buf_alloc(&info->con_buf, GS_CONSOLE_BUF_SIZE);
|
||||
if (status) {
|
||||
pr_err("%s: allocate console buffer failed\n", __func__);
|
||||
return status;
|
||||
}
|
||||
|
||||
info->console_thread = kthread_create(gs_console_thread,
|
||||
co, "gs_console");
|
||||
if (IS_ERR(info->console_thread)) {
|
||||
pr_err("%s: cannot create console thread\n", __func__);
|
||||
gs_buf_free(&info->con_buf);
|
||||
return PTR_ERR(info->console_thread);
|
||||
}
|
||||
wake_up_process(info->console_thread);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gs_console_write(struct console *co,
|
||||
const char *buf, unsigned count)
|
||||
{
|
||||
struct gscons_info *info = &gscons_info;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&info->con_lock, flags);
|
||||
gs_buf_put(&info->con_buf, buf, count);
|
||||
spin_unlock_irqrestore(&info->con_lock, flags);
|
||||
|
||||
wake_up_process(info->console_thread);
|
||||
}
|
||||
|
||||
static struct tty_driver *gs_console_device(struct console *co, int *index)
|
||||
{
|
||||
struct tty_driver **p = (struct tty_driver **)co->data;
|
||||
|
||||
if (!*p)
|
||||
return NULL;
|
||||
|
||||
*index = co->index;
|
||||
return *p;
|
||||
}
|
||||
|
||||
static struct console gserial_cons = {
|
||||
.name = "ttyGS",
|
||||
.write = gs_console_write,
|
||||
.device = gs_console_device,
|
||||
.setup = gs_console_setup,
|
||||
.flags = CON_PRINTBUFFER,
|
||||
.index = -1,
|
||||
.data = &gs_tty_driver,
|
||||
};
|
||||
|
||||
static void gserial_console_init(void)
|
||||
{
|
||||
register_console(&gserial_cons);
|
||||
}
|
||||
|
||||
static void gserial_console_exit(void)
|
||||
{
|
||||
struct gscons_info *info = &gscons_info;
|
||||
|
||||
unregister_console(&gserial_cons);
|
||||
kthread_stop(info->console_thread);
|
||||
gs_buf_free(&info->con_buf);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int gs_console_connect(int port_num)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gs_console_disconnect(struct usb_ep *ep)
|
||||
{
|
||||
}
|
||||
|
||||
static void gserial_console_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
static void gserial_console_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int
|
||||
gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding)
|
||||
{
|
||||
@ -1096,6 +1350,7 @@ void gserial_free_line(unsigned char port_num)
|
||||
|
||||
gserial_free_port(port);
|
||||
tty_unregister_device(gs_tty_driver, port_num);
|
||||
gserial_console_exit();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gserial_free_line);
|
||||
|
||||
@ -1138,6 +1393,7 @@ int gserial_alloc_line(unsigned char *line_num)
|
||||
goto err;
|
||||
}
|
||||
*line_num = port_num;
|
||||
gserial_console_init();
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
@ -1219,6 +1475,7 @@ int gserial_connect(struct gserial *gser, u8 port_num)
|
||||
gser->disconnect(gser);
|
||||
}
|
||||
|
||||
status = gs_console_connect(port_num);
|
||||
spin_unlock_irqrestore(&port->port_lock, flags);
|
||||
|
||||
return status;
|
||||
@ -1277,6 +1534,7 @@ void gserial_disconnect(struct gserial *gser)
|
||||
port->read_allocated = port->read_started =
|
||||
port->write_allocated = port->write_started = 0;
|
||||
|
||||
gs_console_disconnect(gser->in);
|
||||
spin_unlock_irqrestore(&port->port_lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gserial_disconnect);
|
||||
|
@ -40,7 +40,7 @@ static struct usb_device_descriptor device_desc = {
|
||||
.bLength = sizeof device_desc,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
|
||||
.bcdUSB = cpu_to_le16(0x0200),
|
||||
/* .bcdUSB = DYNAMIC */
|
||||
|
||||
.bDeviceClass = USB_CLASS_MISC /* 0xEF */,
|
||||
.bDeviceSubClass = 2,
|
||||
|
@ -123,7 +123,7 @@ static struct usb_device_descriptor device_desc = {
|
||||
.bLength = sizeof device_desc,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
|
||||
.bcdUSB = cpu_to_le16(0x200),
|
||||
/* .bcdUSB = DYNAMIC */
|
||||
|
||||
#ifdef CONFIG_GADGET_UAC1
|
||||
.bDeviceClass = USB_CLASS_PER_INTERFACE,
|
||||
|
@ -43,7 +43,7 @@ static struct usb_device_descriptor device_desc = {
|
||||
.bLength = sizeof device_desc,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
|
||||
.bcdUSB = cpu_to_le16(0x0200),
|
||||
/* .bcdUSB = DYNAMIC */
|
||||
|
||||
.bDeviceClass = USB_CLASS_COMM,
|
||||
.bDeviceSubClass = 0,
|
||||
|
@ -151,7 +151,7 @@ static struct usb_device_descriptor device_desc = {
|
||||
.bLength = sizeof device_desc,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
|
||||
.bcdUSB = cpu_to_le16 (0x0200),
|
||||
/* .bcdUSB = DYNAMIC */
|
||||
|
||||
.bDeviceClass = USB_CLASS_COMM,
|
||||
.bDeviceSubClass = 0,
|
||||
|
@ -69,7 +69,7 @@ static struct usb_device_descriptor gfs_dev_desc = {
|
||||
.bLength = sizeof gfs_dev_desc,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
|
||||
.bcdUSB = cpu_to_le16(0x0200),
|
||||
/* .bcdUSB = DYNAMIC */
|
||||
.bDeviceClass = USB_CLASS_PER_INTERFACE,
|
||||
|
||||
.idVendor = cpu_to_le16(GFS_VENDOR_ID),
|
||||
|
@ -21,19 +21,12 @@
|
||||
/* #define VERBOSE_DEBUG */
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/rawmidi.h>
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/composite.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/audio.h>
|
||||
#include <linux/usb/midi.h>
|
||||
|
||||
#include "u_midi.h"
|
||||
|
||||
@ -42,7 +35,6 @@
|
||||
MODULE_AUTHOR("Ben Williamson");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
static const char shortname[] = "g_midi";
|
||||
static const char longname[] = "MIDI Gadget";
|
||||
|
||||
USB_GADGET_COMPOSITE_OPTIONS();
|
||||
@ -61,7 +53,7 @@ MODULE_PARM_DESC(buflen, "MIDI buffer length");
|
||||
|
||||
static unsigned int qlen = 32;
|
||||
module_param(qlen, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(qlen, "USB read request queue length");
|
||||
MODULE_PARM_DESC(qlen, "USB read and write request queue length");
|
||||
|
||||
static unsigned int in_ports = 1;
|
||||
module_param(in_ports, uint, S_IRUGO);
|
||||
@ -86,7 +78,7 @@ MODULE_PARM_DESC(out_ports, "Number of MIDI output ports");
|
||||
static struct usb_device_descriptor device_desc = {
|
||||
.bLength = USB_DT_DEVICE_SIZE,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
.bcdUSB = cpu_to_le16(0x0200),
|
||||
/* .bcdUSB = DYNAMIC */
|
||||
.bDeviceClass = USB_CLASS_PER_INTERFACE,
|
||||
.idVendor = cpu_to_le16(DRIVER_VENDOR_NUM),
|
||||
.idProduct = cpu_to_le16(DRIVER_PRODUCT_NUM),
|
||||
|
@ -47,7 +47,7 @@ static struct usb_device_descriptor device_desc = {
|
||||
.bLength = sizeof device_desc,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
|
||||
.bcdUSB = cpu_to_le16(0x0200),
|
||||
/* .bcdUSB = DYNAMIC */
|
||||
|
||||
/* .bDeviceClass = USB_CLASS_COMM, */
|
||||
/* .bDeviceSubClass = 0, */
|
||||
|
@ -1137,10 +1137,9 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
|
||||
dev->gadget->ep0, dev->req,
|
||||
GFP_KERNEL);
|
||||
}
|
||||
spin_lock_irq(&dev->lock);
|
||||
if (retval < 0) {
|
||||
spin_lock_irq (&dev->lock);
|
||||
clean_req (dev->gadget->ep0, dev->req);
|
||||
spin_unlock_irq (&dev->lock);
|
||||
} else
|
||||
retval = len;
|
||||
|
||||
|
@ -55,7 +55,7 @@ static struct usb_device_descriptor msg_device_desc = {
|
||||
.bLength = sizeof msg_device_desc,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
|
||||
.bcdUSB = cpu_to_le16(0x0200),
|
||||
/* .bcdUSB = DYNAMIC */
|
||||
.bDeviceClass = USB_CLASS_PER_INTERFACE,
|
||||
|
||||
/* Vendor and product id can be overridden by module parameters. */
|
||||
|
@ -67,7 +67,7 @@ static struct usb_device_descriptor device_desc = {
|
||||
.bLength = sizeof device_desc,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
|
||||
.bcdUSB = cpu_to_le16(0x0200),
|
||||
/* .bcdUSB = DYNAMIC */
|
||||
|
||||
.bDeviceClass = USB_CLASS_MISC /* 0xEF */,
|
||||
.bDeviceSubClass = 2,
|
||||
|
@ -49,7 +49,7 @@ static struct usb_device_descriptor device_desc = {
|
||||
.bLength = sizeof device_desc,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
|
||||
.bcdUSB = cpu_to_le16 (0x0200),
|
||||
/* .bcdUSB = DYNAMIC */
|
||||
|
||||
.bDeviceClass = USB_CLASS_COMM,
|
||||
.bDeviceSubClass = 0,
|
||||
|
@ -89,7 +89,7 @@ static struct usb_gadget_strings *dev_strings[] = {
|
||||
static struct usb_device_descriptor device_desc = {
|
||||
.bLength = USB_DT_DEVICE_SIZE,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
.bcdUSB = cpu_to_le16(0x0200),
|
||||
/* .bcdUSB = DYNAMIC */
|
||||
.bDeviceClass = USB_CLASS_COMM,
|
||||
.idVendor = cpu_to_le16(NOKIA_VENDOR_ID),
|
||||
.idProduct = cpu_to_le16(NOKIA_PRODUCT_ID),
|
||||
|
@ -71,7 +71,7 @@ static struct usb_function *f_printer;
|
||||
static struct usb_device_descriptor device_desc = {
|
||||
.bLength = sizeof device_desc,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
.bcdUSB = cpu_to_le16(0x0200),
|
||||
/* .bcdUSB = DYNAMIC */
|
||||
.bDeviceClass = USB_CLASS_PER_INTERFACE,
|
||||
.bDeviceSubClass = 0,
|
||||
.bDeviceProtocol = 0,
|
||||
|
@ -65,7 +65,7 @@ static struct usb_gadget_strings *dev_strings[] = {
|
||||
static struct usb_device_descriptor device_desc = {
|
||||
.bLength = USB_DT_DEVICE_SIZE,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
.bcdUSB = cpu_to_le16(0x0200),
|
||||
/* .bcdUSB = DYNAMIC */
|
||||
/* .bDeviceClass = f(use_acm) */
|
||||
.bDeviceSubClass = 0,
|
||||
.bDeviceProtocol = 0,
|
||||
|
@ -1974,7 +1974,7 @@ static struct usb_descriptor_header *uasp_ss_function_desc[] = {
|
||||
static struct usb_device_descriptor usbg_device_desc = {
|
||||
.bLength = sizeof(usbg_device_desc),
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
.bcdUSB = cpu_to_le16(0x0200),
|
||||
/* .bcdUSB = DYNAMIC */
|
||||
.bDeviceClass = USB_CLASS_PER_INTERFACE,
|
||||
.idVendor = cpu_to_le16(UAS_VENDOR_ID),
|
||||
.idProduct = cpu_to_le16(UAS_PRODUCT_ID),
|
||||
|
@ -77,7 +77,7 @@ static struct usb_function *f_uvc;
|
||||
static struct usb_device_descriptor webcam_device_descriptor = {
|
||||
.bLength = USB_DT_DEVICE_SIZE,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
.bcdUSB = cpu_to_le16(0x0200),
|
||||
/* .bcdUSB = DYNAMIC */
|
||||
.bDeviceClass = USB_CLASS_MISC,
|
||||
.bDeviceSubClass = 0x02,
|
||||
.bDeviceProtocol = 0x01,
|
||||
|
@ -68,6 +68,8 @@ static struct usb_zero_options gzero_options = {
|
||||
.isoc_maxpacket = GZERO_ISOC_MAXPACKET,
|
||||
.bulk_buflen = GZERO_BULK_BUFLEN,
|
||||
.qlen = GZERO_QLEN,
|
||||
.ss_bulk_qlen = GZERO_SS_BULK_QLEN,
|
||||
.ss_iso_qlen = GZERO_SS_ISO_QLEN,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
@ -113,7 +115,7 @@ static struct usb_device_descriptor device_desc = {
|
||||
.bLength = sizeof device_desc,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
|
||||
.bcdUSB = cpu_to_le16(0x0200),
|
||||
/* .bcdUSB = DYNAMIC */
|
||||
.bDeviceClass = USB_CLASS_VENDOR_SPEC,
|
||||
|
||||
.idVendor = cpu_to_le16(DRIVER_VENDOR_NUM),
|
||||
@ -255,6 +257,14 @@ static struct usb_function_instance *func_inst_lb;
|
||||
module_param_named(qlen, gzero_options.qlen, uint, S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(qlen, "depth of loopback queue");
|
||||
|
||||
module_param_named(ss_bulk_qlen, gzero_options.ss_bulk_qlen, uint,
|
||||
S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(bulk_qlen, "depth of sourcesink queue for bulk transfer");
|
||||
|
||||
module_param_named(ss_iso_qlen, gzero_options.ss_iso_qlen, uint,
|
||||
S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(iso_qlen, "depth of sourcesink queue for iso transfer");
|
||||
|
||||
static int zero_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
struct f_ss_opts *ss_opts;
|
||||
@ -285,6 +295,8 @@ static int zero_bind(struct usb_composite_dev *cdev)
|
||||
ss_opts->isoc_mult = gzero_options.isoc_mult;
|
||||
ss_opts->isoc_maxburst = gzero_options.isoc_maxburst;
|
||||
ss_opts->bulk_buflen = gzero_options.bulk_buflen;
|
||||
ss_opts->bulk_qlen = gzero_options.ss_bulk_qlen;
|
||||
ss_opts->iso_qlen = gzero_options.ss_iso_qlen;
|
||||
|
||||
func_ss = usb_get_function(func_inst_ss);
|
||||
if (IS_ERR(func_ss)) {
|
||||
|
@ -11,7 +11,6 @@
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/usb/gadget.h>
|
||||
#include "u_f.h"
|
||||
|
||||
struct usb_request *alloc_ep_req(struct usb_ep *ep, int len, int default_len)
|
||||
|
@ -16,6 +16,8 @@
|
||||
#ifndef __U_F_H__
|
||||
#define __U_F_H__
|
||||
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
/* Variable Length Array Macros **********************************************/
|
||||
#define vla_group(groupname) size_t groupname##__next = 0
|
||||
#define vla_group_size(groupname) groupname##__next
|
||||
@ -45,8 +47,12 @@
|
||||
struct usb_ep;
|
||||
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);
|
||||
static inline void free_ep_req(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
kfree(req->buf);
|
||||
usb_ep_free_request(ep, req);
|
||||
}
|
||||
|
||||
#endif /* __U_F_H__ */
|
||||
|
||||
|
||||
|
@ -174,6 +174,17 @@ config USB_RENESAS_USBHS_UDC
|
||||
dynamically linked module called "renesas_usbhs" and force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
config USB_RENESAS_USB3
|
||||
tristate 'Renesas USB3.0 Peripheral controller'
|
||||
depends on ARCH_SHMOBILE || COMPILE_TEST
|
||||
help
|
||||
Renesas USB3.0 Peripheral controller is a USB peripheral controller
|
||||
that supports super, high, and full speed USB 3.0 data transfers.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "renesas_usb3" and force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
config USB_PXA27X
|
||||
tristate "PXA 27x"
|
||||
help
|
||||
|
@ -19,6 +19,7 @@ fsl_usb2_udc-y := fsl_udc_core.o
|
||||
fsl_usb2_udc-$(CONFIG_ARCH_MXC) += fsl_mxc_udc.o
|
||||
obj-$(CONFIG_USB_M66592) += m66592-udc.o
|
||||
obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o
|
||||
obj-$(CONFIG_USB_RENESAS_USB3) += renesas_usb3.o
|
||||
obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o
|
||||
obj-$(CONFIG_USB_S3C_HSUDC) += s3c-hsudc.o
|
||||
obj-$(CONFIG_USB_LPC32XX) += lpc32xx_udc.o
|
||||
|
@ -1083,7 +1083,7 @@ static int bcm63xx_ep_disable(struct usb_ep *ep)
|
||||
struct bcm63xx_ep *bep = our_ep(ep);
|
||||
struct bcm63xx_udc *udc = bep->udc;
|
||||
struct iudma_ch *iudma = bep->iudma;
|
||||
struct list_head *pos, *n;
|
||||
struct bcm63xx_req *breq, *n;
|
||||
unsigned long flags;
|
||||
|
||||
if (!ep || !ep->desc)
|
||||
@ -1099,10 +1099,7 @@ static int bcm63xx_ep_disable(struct usb_ep *ep)
|
||||
iudma_reset_channel(udc, iudma);
|
||||
|
||||
if (!list_empty(&bep->queue)) {
|
||||
list_for_each_safe(pos, n, &bep->queue) {
|
||||
struct bcm63xx_req *breq =
|
||||
list_entry(pos, struct bcm63xx_req, queue);
|
||||
|
||||
list_for_each_entry_safe(breq, n, &bep->queue, queue) {
|
||||
usb_gadget_unmap_request(&udc->gadget, &breq->req,
|
||||
iudma->is_tx);
|
||||
list_del(&breq->queue);
|
||||
|
@ -28,42 +28,29 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmapool.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/isp1301.h>
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <mach/hardware.h>
|
||||
#include <linux/io.h>
|
||||
#include <asm/irq.h>
|
||||
|
||||
#include <mach/platform.h>
|
||||
#include <mach/irqs.h>
|
||||
#include <mach/board.h>
|
||||
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#endif
|
||||
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/platform.h>
|
||||
|
||||
/*
|
||||
* USB device configuration structure
|
||||
*/
|
||||
|
1975
drivers/usb/gadget/udc/renesas_usb3.c
Normal file
1975
drivers/usb/gadget/udc/renesas_usb3.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -569,7 +569,7 @@ static int s3c_hsudc_handle_reqfeat(struct s3c_hsudc *hsudc,
|
||||
hsep = &hsudc->ep[ep_num];
|
||||
switch (le16_to_cpu(ctrl->wValue)) {
|
||||
case USB_ENDPOINT_HALT:
|
||||
if (set || (!set && !hsep->wedge))
|
||||
if (set || !hsep->wedge)
|
||||
s3c_hsudc_set_halt(&hsep->ep, set);
|
||||
return 0;
|
||||
}
|
||||
|
@ -51,8 +51,12 @@ struct usb_udc {
|
||||
|
||||
static struct class *udc_class;
|
||||
static LIST_HEAD(udc_list);
|
||||
static LIST_HEAD(gadget_driver_pending_list);
|
||||
static DEFINE_MUTEX(udc_lock);
|
||||
|
||||
static int udc_bind_to_driver(struct usb_udc *udc,
|
||||
struct usb_gadget_driver *driver);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
#ifdef CONFIG_HAS_DMA
|
||||
@ -356,6 +360,7 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
|
||||
void (*release)(struct device *dev))
|
||||
{
|
||||
struct usb_udc *udc;
|
||||
struct usb_gadget_driver *driver;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
udc = kzalloc(sizeof(*udc), GFP_KERNEL);
|
||||
@ -403,6 +408,18 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
|
||||
usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);
|
||||
udc->vbus = true;
|
||||
|
||||
/* pick up one of pending gadget drivers */
|
||||
list_for_each_entry(driver, &gadget_driver_pending_list, pending) {
|
||||
if (!driver->udc_name || strcmp(driver->udc_name,
|
||||
dev_name(&udc->dev)) == 0) {
|
||||
ret = udc_bind_to_driver(udc, driver);
|
||||
if (ret)
|
||||
goto err4;
|
||||
list_del(&driver->pending);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&udc_lock);
|
||||
|
||||
return 0;
|
||||
@ -473,10 +490,14 @@ void usb_del_gadget_udc(struct usb_gadget *gadget)
|
||||
|
||||
mutex_lock(&udc_lock);
|
||||
list_del(&udc->list);
|
||||
mutex_unlock(&udc_lock);
|
||||
|
||||
if (udc->driver)
|
||||
if (udc->driver) {
|
||||
struct usb_gadget_driver *driver = udc->driver;
|
||||
|
||||
usb_gadget_remove_driver(udc);
|
||||
list_add(&driver->pending, &gadget_driver_pending_list);
|
||||
}
|
||||
mutex_unlock(&udc_lock);
|
||||
|
||||
kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE);
|
||||
flush_work(&gadget->work);
|
||||
@ -520,50 +541,36 @@ err1:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int usb_udc_attach_driver(const char *name, struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct usb_udc *udc = NULL;
|
||||
int ret = -ENODEV;
|
||||
|
||||
mutex_lock(&udc_lock);
|
||||
list_for_each_entry(udc, &udc_list, list) {
|
||||
ret = strcmp(name, dev_name(&udc->dev));
|
||||
if (!ret)
|
||||
break;
|
||||
}
|
||||
if (ret) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
if (udc->driver) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
ret = udc_bind_to_driver(udc, driver);
|
||||
out:
|
||||
mutex_unlock(&udc_lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_udc_attach_driver);
|
||||
|
||||
int usb_gadget_probe_driver(struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct usb_udc *udc = NULL;
|
||||
int ret;
|
||||
int ret = -ENODEV;
|
||||
|
||||
if (!driver || !driver->bind || !driver->setup)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&udc_lock);
|
||||
list_for_each_entry(udc, &udc_list, list) {
|
||||
/* For now we take the first one */
|
||||
if (!udc->driver)
|
||||
if (driver->udc_name) {
|
||||
list_for_each_entry(udc, &udc_list, list) {
|
||||
ret = strcmp(driver->udc_name, dev_name(&udc->dev));
|
||||
if (!ret)
|
||||
break;
|
||||
}
|
||||
if (!ret && !udc->driver)
|
||||
goto found;
|
||||
} else {
|
||||
list_for_each_entry(udc, &udc_list, list) {
|
||||
/* For now we take the first one */
|
||||
if (!udc->driver)
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
|
||||
pr_debug("couldn't find an available UDC\n");
|
||||
list_add_tail(&driver->pending, &gadget_driver_pending_list);
|
||||
pr_info("udc-core: couldn't find an available UDC - added [%s] to list of pending drivers\n",
|
||||
driver->function);
|
||||
mutex_unlock(&udc_lock);
|
||||
return -ENODEV;
|
||||
return 0;
|
||||
found:
|
||||
ret = udc_bind_to_driver(udc, driver);
|
||||
mutex_unlock(&udc_lock);
|
||||
@ -589,6 +596,10 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
list_del(&driver->pending);
|
||||
ret = 0;
|
||||
}
|
||||
mutex_unlock(&udc_lock);
|
||||
return ret;
|
||||
}
|
||||
|
@ -1360,8 +1360,7 @@ static int ep_config_from_table(struct musb *musb)
|
||||
break;
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "%s: setup fifo_mode %d\n",
|
||||
musb_driver_name, fifo_mode);
|
||||
pr_debug("%s: setup fifo_mode %d\n", musb_driver_name, fifo_mode);
|
||||
|
||||
|
||||
done:
|
||||
@ -1390,7 +1389,7 @@ done:
|
||||
musb->nr_endpoints = max(epn, musb->nr_endpoints);
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "%s: %d/%d max ep, %d/%d memory\n",
|
||||
pr_debug("%s: %d/%d max ep, %d/%d memory\n",
|
||||
musb_driver_name,
|
||||
n + 1, musb->config->num_eps * 2 - 1,
|
||||
offset, (1 << (musb->config->ram_bits + 2)));
|
||||
@ -1491,8 +1490,7 @@ static int musb_core_init(u16 musb_type, struct musb *musb)
|
||||
if (reg & MUSB_CONFIGDATA_SOFTCONE)
|
||||
strcat(aInfo, ", SoftConn");
|
||||
|
||||
printk(KERN_DEBUG "%s: ConfigData=0x%02x (%s)\n",
|
||||
musb_driver_name, reg, aInfo);
|
||||
pr_debug("%s: ConfigData=0x%02x (%s)\n", musb_driver_name, reg, aInfo);
|
||||
|
||||
aDate[0] = 0;
|
||||
if (MUSB_CONTROLLER_MHDRC == musb_type) {
|
||||
@ -1502,9 +1500,8 @@ static int musb_core_init(u16 musb_type, struct musb *musb)
|
||||
musb->is_multipoint = 0;
|
||||
type = "";
|
||||
#ifndef CONFIG_USB_OTG_BLACKLIST_HUB
|
||||
printk(KERN_ERR
|
||||
"%s: kernel must blacklist external hubs\n",
|
||||
musb_driver_name);
|
||||
pr_err("%s: kernel must blacklist external hubs\n",
|
||||
musb_driver_name);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -1513,8 +1510,8 @@ static int musb_core_init(u16 musb_type, struct musb *musb)
|
||||
snprintf(aRevision, 32, "%d.%d%s", MUSB_HWVERS_MAJOR(musb->hwvers),
|
||||
MUSB_HWVERS_MINOR(musb->hwvers),
|
||||
(musb->hwvers & MUSB_HWVERS_RC) ? "RC" : "");
|
||||
printk(KERN_DEBUG "%s: %sHDRC RTL version %s %s\n",
|
||||
musb_driver_name, type, aRevision, aDate);
|
||||
pr_debug("%s: %sHDRC RTL version %s %s\n",
|
||||
musb_driver_name, type, aRevision, aDate);
|
||||
|
||||
/* configure ep0 */
|
||||
musb_configure_ep0(musb);
|
||||
@ -1705,6 +1702,23 @@ EXPORT_SYMBOL_GPL(musb_dma_completion);
|
||||
#define use_dma 0
|
||||
#endif
|
||||
|
||||
static void (*musb_phy_callback)(enum musb_vbus_id_status status);
|
||||
|
||||
/*
|
||||
* musb_mailbox - optional phy notifier function
|
||||
* @status phy state change
|
||||
*
|
||||
* Optionally gets called from the USB PHY. Note that the USB PHY must be
|
||||
* disabled at the point the phy_callback is registered or unregistered.
|
||||
*/
|
||||
void musb_mailbox(enum musb_vbus_id_status status)
|
||||
{
|
||||
if (musb_phy_callback)
|
||||
musb_phy_callback(status);
|
||||
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(musb_mailbox);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static ssize_t
|
||||
@ -2117,8 +2131,15 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
|
||||
musb->xceiv->io_ops = &musb_ulpi_access;
|
||||
}
|
||||
|
||||
if (musb->ops->phy_callback)
|
||||
musb_phy_callback = musb->ops->phy_callback;
|
||||
|
||||
pm_runtime_get_sync(musb->controller);
|
||||
|
||||
status = usb_phy_init(musb->xceiv);
|
||||
if (status < 0)
|
||||
goto err_usb_phy_init;
|
||||
|
||||
if (use_dma && dev->dma_mask) {
|
||||
musb->dma_controller =
|
||||
musb_dma_controller_create(musb, musb->mregs);
|
||||
@ -2239,7 +2260,11 @@ fail3:
|
||||
cancel_delayed_work_sync(&musb->deassert_reset_work);
|
||||
if (musb->dma_controller)
|
||||
musb_dma_controller_destroy(musb->dma_controller);
|
||||
|
||||
fail2_5:
|
||||
usb_phy_shutdown(musb->xceiv);
|
||||
|
||||
err_usb_phy_init:
|
||||
pm_runtime_put_sync(musb->controller);
|
||||
|
||||
fail2:
|
||||
@ -2295,10 +2320,13 @@ static int musb_remove(struct platform_device *pdev)
|
||||
*/
|
||||
musb_exit_debugfs(musb);
|
||||
musb_shutdown(pdev);
|
||||
musb_phy_callback = NULL;
|
||||
|
||||
if (musb->dma_controller)
|
||||
musb_dma_controller_destroy(musb->dma_controller);
|
||||
|
||||
usb_phy_shutdown(musb->xceiv);
|
||||
|
||||
cancel_work_sync(&musb->irq_work);
|
||||
cancel_delayed_work_sync(&musb->finish_resume_work);
|
||||
cancel_delayed_work_sync(&musb->deassert_reset_work);
|
||||
|
@ -168,6 +168,7 @@ struct musb_io;
|
||||
* @adjust_channel_params: pre check for standard dma channel_program func
|
||||
* @pre_root_reset_end: called before the root usb port reset flag gets cleared
|
||||
* @post_root_reset_end: called after the root usb port reset flag gets cleared
|
||||
* @phy_callback: optional callback function for the phy to call
|
||||
*/
|
||||
struct musb_platform_ops {
|
||||
|
||||
@ -214,6 +215,7 @@ struct musb_platform_ops {
|
||||
dma_addr_t *dma_addr, u32 *len);
|
||||
void (*pre_root_reset_end)(struct musb *musb);
|
||||
void (*post_root_reset_end)(struct musb *musb);
|
||||
void (*phy_callback)(enum musb_vbus_id_status status);
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -353,9 +353,8 @@ static void txstate(struct musb *musb, struct musb_request *req)
|
||||
* 1 >0 Yes(FS bulk)
|
||||
*/
|
||||
if (!musb_ep->hb_mult ||
|
||||
(musb_ep->hb_mult &&
|
||||
can_bulk_split(musb,
|
||||
musb_ep->type)))
|
||||
can_bulk_split(musb,
|
||||
musb_ep->type))
|
||||
csr |= MUSB_TXCSR_AUTOSET;
|
||||
}
|
||||
csr &= ~MUSB_TXCSR_P_UNDERRUN;
|
||||
|
@ -36,7 +36,7 @@
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/usb/musb-omap.h>
|
||||
#include <linux/usb/musb.h>
|
||||
#include <linux/phy/omap_control_phy.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
struct omap2430_glue {
|
||||
struct device *dev;
|
||||
struct platform_device *musb;
|
||||
enum omap_musb_vbus_id_status status;
|
||||
enum musb_vbus_id_status status;
|
||||
struct work_struct omap_musb_mailbox_work;
|
||||
struct device *control_otghs;
|
||||
};
|
||||
@ -234,7 +234,7 @@ static inline void omap2430_low_level_init(struct musb *musb)
|
||||
musb_writel(musb->mregs, OTG_FORCESTDBY, l);
|
||||
}
|
||||
|
||||
void omap_musb_mailbox(enum omap_musb_vbus_id_status status)
|
||||
static void omap2430_musb_mailbox(enum musb_vbus_id_status status)
|
||||
{
|
||||
struct omap2430_glue *glue = _glue;
|
||||
|
||||
@ -251,7 +251,6 @@ void omap_musb_mailbox(enum omap_musb_vbus_id_status status)
|
||||
|
||||
schedule_work(&glue->omap_musb_mailbox_work);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(omap_musb_mailbox);
|
||||
|
||||
static void omap_musb_set_mailbox(struct omap2430_glue *glue)
|
||||
{
|
||||
@ -262,7 +261,7 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue)
|
||||
struct usb_otg *otg = musb->xceiv->otg;
|
||||
|
||||
switch (glue->status) {
|
||||
case OMAP_MUSB_ID_GROUND:
|
||||
case MUSB_ID_GROUND:
|
||||
dev_dbg(dev, "ID GND\n");
|
||||
|
||||
otg->default_a = true;
|
||||
@ -276,7 +275,7 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue)
|
||||
}
|
||||
break;
|
||||
|
||||
case OMAP_MUSB_VBUS_VALID:
|
||||
case MUSB_VBUS_VALID:
|
||||
dev_dbg(dev, "VBUS Connect\n");
|
||||
|
||||
otg->default_a = false;
|
||||
@ -287,8 +286,8 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue)
|
||||
omap_control_usb_set_mode(glue->control_otghs, USB_MODE_DEVICE);
|
||||
break;
|
||||
|
||||
case OMAP_MUSB_ID_FLOAT:
|
||||
case OMAP_MUSB_VBUS_OFF:
|
||||
case MUSB_ID_FLOAT:
|
||||
case MUSB_VBUS_OFF:
|
||||
dev_dbg(dev, "VBUS Disconnect\n");
|
||||
|
||||
musb->xceiv->last_event = USB_EVENT_NONE;
|
||||
@ -430,7 +429,7 @@ static int omap2430_musb_init(struct musb *musb)
|
||||
|
||||
setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb);
|
||||
|
||||
if (glue->status != OMAP_MUSB_UNKNOWN)
|
||||
if (glue->status != MUSB_UNKNOWN)
|
||||
omap_musb_set_mailbox(glue);
|
||||
|
||||
phy_init(musb->phy);
|
||||
@ -455,7 +454,7 @@ static void omap2430_musb_enable(struct musb *musb)
|
||||
|
||||
switch (glue->status) {
|
||||
|
||||
case OMAP_MUSB_ID_GROUND:
|
||||
case MUSB_ID_GROUND:
|
||||
omap_control_usb_set_mode(glue->control_otghs, USB_MODE_HOST);
|
||||
if (data->interface_type != MUSB_INTERFACE_UTMI)
|
||||
break;
|
||||
@ -474,7 +473,7 @@ static void omap2430_musb_enable(struct musb *musb)
|
||||
}
|
||||
break;
|
||||
|
||||
case OMAP_MUSB_VBUS_VALID:
|
||||
case MUSB_VBUS_VALID:
|
||||
omap_control_usb_set_mode(glue->control_otghs, USB_MODE_DEVICE);
|
||||
break;
|
||||
|
||||
@ -488,7 +487,7 @@ static void omap2430_musb_disable(struct musb *musb)
|
||||
struct device *dev = musb->controller;
|
||||
struct omap2430_glue *glue = dev_get_drvdata(dev->parent);
|
||||
|
||||
if (glue->status != OMAP_MUSB_UNKNOWN)
|
||||
if (glue->status != MUSB_UNKNOWN)
|
||||
omap_control_usb_set_mode(glue->control_otghs,
|
||||
USB_MODE_DISCONNECT);
|
||||
}
|
||||
@ -520,6 +519,8 @@ static const struct musb_platform_ops omap2430_ops = {
|
||||
|
||||
.enable = omap2430_musb_enable,
|
||||
.disable = omap2430_musb_disable,
|
||||
|
||||
.phy_callback = omap2430_musb_mailbox,
|
||||
};
|
||||
|
||||
static u64 omap2430_dmamask = DMA_BIT_MASK(32);
|
||||
@ -551,7 +552,7 @@ static int omap2430_probe(struct platform_device *pdev)
|
||||
|
||||
glue->dev = &pdev->dev;
|
||||
glue->musb = musb;
|
||||
glue->status = OMAP_MUSB_UNKNOWN;
|
||||
glue->status = MUSB_UNKNOWN;
|
||||
glue->control_otghs = ERR_PTR(-ENODEV);
|
||||
|
||||
if (np) {
|
||||
@ -663,8 +664,11 @@ static int omap2430_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct omap2430_glue *glue = platform_get_drvdata(pdev);
|
||||
|
||||
pm_runtime_get_sync(glue->dev);
|
||||
cancel_work_sync(&glue->omap_musb_mailbox_work);
|
||||
platform_device_unregister(glue->musb);
|
||||
pm_runtime_put_sync(glue->dev);
|
||||
pm_runtime_disable(glue->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -66,6 +66,7 @@ config AM335X_PHY_USB
|
||||
select USB_PHY
|
||||
select AM335X_CONTROL_USB
|
||||
select NOP_USB_XCEIV
|
||||
select USB_COMMON
|
||||
help
|
||||
This driver provides PHY support for that phy which part for the
|
||||
AM335x SoC.
|
||||
@ -186,19 +187,6 @@ config USB_MXS_PHY
|
||||
|
||||
MXS Phy is used by some of the i.MX SoCs, for example imx23/28/6x.
|
||||
|
||||
config USB_RCAR_PHY
|
||||
tristate "Renesas R-Car USB PHY support"
|
||||
depends on USB || USB_GADGET
|
||||
depends on ARCH_R8A7778 || ARCH_R8A7779 || COMPILE_TEST
|
||||
select USB_PHY
|
||||
help
|
||||
Say Y here to add support for the Renesas R-Car USB common PHY driver.
|
||||
This chip is typically used as USB PHY for USB host, gadget.
|
||||
This driver supports R8A7778 and R8A7779.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called phy-rcar-usb.
|
||||
|
||||
config USB_ULPI
|
||||
bool "Generic ULPI Transceiver Driver"
|
||||
depends on ARM || ARM64
|
||||
|
@ -23,7 +23,6 @@ obj-$(CONFIG_USB_MSM_OTG) += phy-msm-usb.o
|
||||
obj-$(CONFIG_USB_QCOM_8X16_PHY) += phy-qcom-8x16-usb.o
|
||||
obj-$(CONFIG_USB_MV_OTG) += phy-mv-usb.o
|
||||
obj-$(CONFIG_USB_MXS_PHY) += phy-mxs-usb.o
|
||||
obj-$(CONFIG_USB_RCAR_PHY) += phy-rcar-usb.o
|
||||
obj-$(CONFIG_USB_ULPI) += phy-ulpi.o
|
||||
obj-$(CONFIG_USB_ULPI_VIEWPORT) += phy-ulpi-viewport.o
|
||||
obj-$(CONFIG_KEYSTONE_USB_PHY) += phy-keystone.o
|
||||
|
@ -4,7 +4,8 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/delay.h>
|
||||
#include "am35x-phy-control.h"
|
||||
#include <linux/usb/otg.h>
|
||||
#include "phy-am335x-control.h"
|
||||
|
||||
struct am335x_control_usb {
|
||||
struct device *dev;
|
||||
@ -58,7 +59,8 @@ static void am335x_phy_wkup(struct phy_control *phy_ctrl, u32 id, bool on)
|
||||
spin_unlock(&usb_ctrl->lock);
|
||||
}
|
||||
|
||||
static void am335x_phy_power(struct phy_control *phy_ctrl, u32 id, bool on)
|
||||
static void am335x_phy_power(struct phy_control *phy_ctrl, u32 id,
|
||||
enum usb_dr_mode dr_mode, bool on)
|
||||
{
|
||||
struct am335x_control_usb *usb_ctrl;
|
||||
u32 val;
|
||||
@ -80,8 +82,14 @@ static void am335x_phy_power(struct phy_control *phy_ctrl, u32 id, bool on)
|
||||
|
||||
val = readl(usb_ctrl->phy_reg + reg);
|
||||
if (on) {
|
||||
val &= ~(USBPHY_CM_PWRDN | USBPHY_OTG_PWRDN);
|
||||
val |= USBPHY_OTGVDET_EN | USBPHY_OTGSESSEND_EN;
|
||||
if (dr_mode == USB_DR_MODE_HOST) {
|
||||
val &= ~(USBPHY_CM_PWRDN | USBPHY_OTG_PWRDN |
|
||||
USBPHY_OTGVDET_EN);
|
||||
val |= USBPHY_OTGSESSEND_EN;
|
||||
} else {
|
||||
val &= ~(USBPHY_CM_PWRDN | USBPHY_OTG_PWRDN);
|
||||
val |= USBPHY_OTGVDET_EN | USBPHY_OTGSESSEND_EN;
|
||||
}
|
||||
} else {
|
||||
val |= USBPHY_CM_PWRDN | USBPHY_OTG_PWRDN;
|
||||
}
|
||||
|
@ -2,13 +2,15 @@
|
||||
#define _AM335x_PHY_CONTROL_H_
|
||||
|
||||
struct phy_control {
|
||||
void (*phy_power)(struct phy_control *phy_ctrl, u32 id, bool on);
|
||||
void (*phy_power)(struct phy_control *phy_ctrl, u32 id,
|
||||
enum usb_dr_mode dr_mode, bool on);
|
||||
void (*phy_wkup)(struct phy_control *phy_ctrl, u32 id, bool on);
|
||||
};
|
||||
|
||||
static inline void phy_ctrl_power(struct phy_control *phy_ctrl, u32 id, bool on)
|
||||
static inline void phy_ctrl_power(struct phy_control *phy_ctrl, u32 id,
|
||||
enum usb_dr_mode dr_mode, bool on)
|
||||
{
|
||||
phy_ctrl->phy_power(phy_ctrl, id, on);
|
||||
phy_ctrl->phy_power(phy_ctrl, id, dr_mode, on);
|
||||
}
|
||||
|
||||
static inline void phy_ctrl_wkup(struct phy_control *phy_ctrl, u32 id, bool on)
|
@ -8,21 +8,23 @@
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/usb/of.h>
|
||||
|
||||
#include "am35x-phy-control.h"
|
||||
#include "phy-am335x-control.h"
|
||||
#include "phy-generic.h"
|
||||
|
||||
struct am335x_phy {
|
||||
struct usb_phy_generic usb_phy_gen;
|
||||
struct phy_control *phy_ctrl;
|
||||
int id;
|
||||
enum usb_dr_mode dr_mode;
|
||||
};
|
||||
|
||||
static int am335x_init(struct usb_phy *phy)
|
||||
{
|
||||
struct am335x_phy *am_phy = dev_get_drvdata(phy->dev);
|
||||
|
||||
phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, true);
|
||||
phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, am_phy->dr_mode, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -30,7 +32,7 @@ static void am335x_shutdown(struct usb_phy *phy)
|
||||
{
|
||||
struct am335x_phy *am_phy = dev_get_drvdata(phy->dev);
|
||||
|
||||
phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, false);
|
||||
phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, am_phy->dr_mode, false);
|
||||
}
|
||||
|
||||
static int am335x_phy_probe(struct platform_device *pdev)
|
||||
@ -46,12 +48,15 @@ static int am335x_phy_probe(struct platform_device *pdev)
|
||||
am_phy->phy_ctrl = am335x_get_phy_control(dev);
|
||||
if (!am_phy->phy_ctrl)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
am_phy->id = of_alias_get_id(pdev->dev.of_node, "phy");
|
||||
if (am_phy->id < 0) {
|
||||
dev_err(&pdev->dev, "Missing PHY id: %d\n", am_phy->id);
|
||||
return am_phy->id;
|
||||
}
|
||||
|
||||
am_phy->dr_mode = of_usb_get_dr_mode_by_phy(pdev->dev.of_node);
|
||||
|
||||
ret = usb_phy_gen_create_phy(dev, &am_phy->usb_phy_gen, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -75,7 +80,7 @@ static int am335x_phy_probe(struct platform_device *pdev)
|
||||
*/
|
||||
|
||||
device_set_wakeup_enable(dev, false);
|
||||
phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, false);
|
||||
phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, am_phy->dr_mode, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -105,7 +110,7 @@ static int am335x_phy_suspend(struct device *dev)
|
||||
if (device_may_wakeup(dev))
|
||||
phy_ctrl_wkup(am_phy->phy_ctrl, am_phy->id, true);
|
||||
|
||||
phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, false);
|
||||
phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, am_phy->dr_mode, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -115,7 +120,7 @@ static int am335x_phy_resume(struct device *dev)
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct am335x_phy *am_phy = platform_get_drvdata(pdev);
|
||||
|
||||
phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, true);
|
||||
phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, am_phy->dr_mode, true);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
phy_ctrl_wkup(am_phy->phy_ctrl, am_phy->id, false);
|
||||
|
@ -1,247 +0,0 @@
|
||||
/*
|
||||
* Renesas R-Car USB phy driver
|
||||
*
|
||||
* Copyright (C) 2012-2013 Renesas Solutions Corp.
|
||||
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
|
||||
* Copyright (C) 2013 Cogent Embedded, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_data/usb-rcar-phy.h>
|
||||
|
||||
/* REGS block */
|
||||
#define USBPCTRL0 0x00
|
||||
#define USBPCTRL1 0x04
|
||||
#define USBST 0x08
|
||||
#define USBEH0 0x0C
|
||||
#define USBOH0 0x1C
|
||||
#define USBCTL0 0x58
|
||||
|
||||
/* High-speed signal quality characteristic control registers (R8A7778 only) */
|
||||
#define HSQCTL1 0x24
|
||||
#define HSQCTL2 0x28
|
||||
|
||||
/* USBPCTRL0 */
|
||||
#define OVC2 (1 << 10) /* (R8A7779 only) */
|
||||
/* Switches the OVC input pin for port 2: */
|
||||
/* 1: USB_OVC2, 0: OVC2 */
|
||||
#define OVC1_VBUS1 (1 << 9) /* Switches the OVC input pin for port 1: */
|
||||
/* 1: USB_OVC1, 0: OVC1/VBUS1 */
|
||||
/* Function mode: set to 0 */
|
||||
#define OVC0 (1 << 8) /* Switches the OVC input pin for port 0: */
|
||||
/* 1: USB_OVC0 pin, 0: OVC0 */
|
||||
#define OVC2_ACT (1 << 6) /* (R8A7779 only) */
|
||||
/* Host mode: OVC2 polarity: */
|
||||
/* 1: active-high, 0: active-low */
|
||||
#define PENC (1 << 4) /* Function mode: output level of PENC1 pin: */
|
||||
/* 1: high, 0: low */
|
||||
#define OVC0_ACT (1 << 3) /* Host mode: OVC0 polarity: */
|
||||
/* 1: active-high, 0: active-low */
|
||||
#define OVC1_ACT (1 << 1) /* Host mode: OVC1 polarity: */
|
||||
/* 1: active-high, 0: active-low */
|
||||
/* Function mode: be sure to set to 1 */
|
||||
#define PORT1 (1 << 0) /* Selects port 1 mode: */
|
||||
/* 1: function, 0: host */
|
||||
/* USBPCTRL1 */
|
||||
#define PHY_RST (1 << 2)
|
||||
#define PLL_ENB (1 << 1)
|
||||
#define PHY_ENB (1 << 0)
|
||||
|
||||
/* USBST */
|
||||
#define ST_ACT (1 << 31)
|
||||
#define ST_PLL (1 << 30)
|
||||
|
||||
struct rcar_usb_phy_priv {
|
||||
struct usb_phy phy;
|
||||
spinlock_t lock;
|
||||
|
||||
void __iomem *reg0;
|
||||
void __iomem *reg1;
|
||||
int counter;
|
||||
};
|
||||
|
||||
#define usb_phy_to_priv(p) container_of(p, struct rcar_usb_phy_priv, phy)
|
||||
|
||||
|
||||
/*
|
||||
* USB initial/install operation.
|
||||
*
|
||||
* This function setup USB phy.
|
||||
* The used value and setting order came from
|
||||
* [USB :: Initial setting] on datasheet.
|
||||
*/
|
||||
static int rcar_usb_phy_init(struct usb_phy *phy)
|
||||
{
|
||||
struct rcar_usb_phy_priv *priv = usb_phy_to_priv(phy);
|
||||
struct device *dev = phy->dev;
|
||||
struct rcar_phy_platform_data *pdata = dev_get_platdata(dev);
|
||||
void __iomem *reg0 = priv->reg0;
|
||||
void __iomem *reg1 = priv->reg1;
|
||||
static const u8 ovcn_act[] = { OVC0_ACT, OVC1_ACT, OVC2_ACT };
|
||||
int i;
|
||||
u32 val;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
if (priv->counter++ == 0) {
|
||||
|
||||
/*
|
||||
* USB phy start-up
|
||||
*/
|
||||
|
||||
/* (1) USB-PHY standby release */
|
||||
iowrite32(PHY_ENB, (reg0 + USBPCTRL1));
|
||||
|
||||
/* (2) start USB-PHY internal PLL */
|
||||
iowrite32(PHY_ENB | PLL_ENB, (reg0 + USBPCTRL1));
|
||||
|
||||
/* (3) set USB-PHY in accord with the conditions of usage */
|
||||
if (reg1) {
|
||||
u32 hsqctl1 = pdata->ferrite_bead ? 0x41 : 0;
|
||||
u32 hsqctl2 = pdata->ferrite_bead ? 0x0d : 7;
|
||||
|
||||
iowrite32(hsqctl1, reg1 + HSQCTL1);
|
||||
iowrite32(hsqctl2, reg1 + HSQCTL2);
|
||||
}
|
||||
|
||||
/* (4) USB module status check */
|
||||
for (i = 0; i < 1024; i++) {
|
||||
udelay(10);
|
||||
val = ioread32(reg0 + USBST);
|
||||
if (val == (ST_ACT | ST_PLL))
|
||||
break;
|
||||
}
|
||||
|
||||
if (val != (ST_ACT | ST_PLL)) {
|
||||
dev_err(dev, "USB phy not ready\n");
|
||||
goto phy_init_end;
|
||||
}
|
||||
|
||||
/* (5) USB-PHY reset clear */
|
||||
iowrite32(PHY_ENB | PLL_ENB | PHY_RST, (reg0 + USBPCTRL1));
|
||||
|
||||
/* Board specific port settings */
|
||||
val = 0;
|
||||
if (pdata->port1_func)
|
||||
val |= PORT1;
|
||||
if (pdata->penc1)
|
||||
val |= PENC;
|
||||
for (i = 0; i < 3; i++) {
|
||||
/* OVCn bits follow each other in the right order */
|
||||
if (pdata->ovc_pin[i].select_3_3v)
|
||||
val |= OVC0 << i;
|
||||
/* OVCn_ACT bits are spaced by irregular intervals */
|
||||
if (pdata->ovc_pin[i].active_high)
|
||||
val |= ovcn_act[i];
|
||||
}
|
||||
iowrite32(val, (reg0 + USBPCTRL0));
|
||||
|
||||
/*
|
||||
* Bus alignment settings
|
||||
*/
|
||||
|
||||
/* (1) EHCI bus alignment (little endian) */
|
||||
iowrite32(0x00000000, (reg0 + USBEH0));
|
||||
|
||||
/* (1) OHCI bus alignment (little endian) */
|
||||
iowrite32(0x00000000, (reg0 + USBOH0));
|
||||
}
|
||||
|
||||
phy_init_end:
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rcar_usb_phy_shutdown(struct usb_phy *phy)
|
||||
{
|
||||
struct rcar_usb_phy_priv *priv = usb_phy_to_priv(phy);
|
||||
void __iomem *reg0 = priv->reg0;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
|
||||
if (priv->counter-- == 1) /* last user */
|
||||
iowrite32(0x00000000, (reg0 + USBPCTRL1));
|
||||
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
}
|
||||
|
||||
static int rcar_usb_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct rcar_usb_phy_priv *priv;
|
||||
struct resource *res0, *res1;
|
||||
struct device *dev = &pdev->dev;
|
||||
void __iomem *reg0, *reg1 = NULL;
|
||||
int ret;
|
||||
|
||||
if (!dev_get_platdata(&pdev->dev)) {
|
||||
dev_err(dev, "No platform data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
reg0 = devm_ioremap_resource(dev, res0);
|
||||
if (IS_ERR(reg0))
|
||||
return PTR_ERR(reg0);
|
||||
|
||||
res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
reg1 = devm_ioremap_resource(dev, res1);
|
||||
if (IS_ERR(reg1))
|
||||
return PTR_ERR(reg1);
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->reg0 = reg0;
|
||||
priv->reg1 = reg1;
|
||||
priv->counter = 0;
|
||||
priv->phy.dev = dev;
|
||||
priv->phy.label = dev_name(dev);
|
||||
priv->phy.init = rcar_usb_phy_init;
|
||||
priv->phy.shutdown = rcar_usb_phy_shutdown;
|
||||
spin_lock_init(&priv->lock);
|
||||
|
||||
ret = usb_add_phy(&priv->phy, USB_PHY_TYPE_USB2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "usb phy addition error\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rcar_usb_phy_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rcar_usb_phy_priv *priv = platform_get_drvdata(pdev);
|
||||
|
||||
usb_remove_phy(&priv->phy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver rcar_usb_phy_driver = {
|
||||
.driver = {
|
||||
.name = "rcar_usb_phy",
|
||||
},
|
||||
.probe = rcar_usb_phy_probe,
|
||||
.remove = rcar_usb_phy_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(rcar_usb_phy_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("Renesas R-Car USB phy");
|
||||
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
|
@ -25,7 +25,7 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/usb/musb-omap.h>
|
||||
#include <linux/usb/musb.h>
|
||||
#include <linux/usb/phy_companion.h>
|
||||
#include <linux/phy/omap_usb.h>
|
||||
#include <linux/i2c/twl.h>
|
||||
@ -102,7 +102,7 @@ struct twl6030_usb {
|
||||
|
||||
int irq1;
|
||||
int irq2;
|
||||
enum omap_musb_vbus_id_status linkstat;
|
||||
enum musb_vbus_id_status linkstat;
|
||||
u8 asleep;
|
||||
bool vbus_enable;
|
||||
const char *regulator;
|
||||
@ -189,13 +189,13 @@ static ssize_t twl6030_usb_vbus_show(struct device *dev,
|
||||
spin_lock_irqsave(&twl->lock, flags);
|
||||
|
||||
switch (twl->linkstat) {
|
||||
case OMAP_MUSB_VBUS_VALID:
|
||||
case MUSB_VBUS_VALID:
|
||||
ret = snprintf(buf, PAGE_SIZE, "vbus\n");
|
||||
break;
|
||||
case OMAP_MUSB_ID_GROUND:
|
||||
case MUSB_ID_GROUND:
|
||||
ret = snprintf(buf, PAGE_SIZE, "id\n");
|
||||
break;
|
||||
case OMAP_MUSB_VBUS_OFF:
|
||||
case MUSB_VBUS_OFF:
|
||||
ret = snprintf(buf, PAGE_SIZE, "none\n");
|
||||
break;
|
||||
default:
|
||||
@ -210,7 +210,7 @@ static DEVICE_ATTR(vbus, 0444, twl6030_usb_vbus_show, NULL);
|
||||
static irqreturn_t twl6030_usb_irq(int irq, void *_twl)
|
||||
{
|
||||
struct twl6030_usb *twl = _twl;
|
||||
enum omap_musb_vbus_id_status status = OMAP_MUSB_UNKNOWN;
|
||||
enum musb_vbus_id_status status = MUSB_UNKNOWN;
|
||||
u8 vbus_state, hw_state;
|
||||
int ret;
|
||||
|
||||
@ -225,14 +225,14 @@ static irqreturn_t twl6030_usb_irq(int irq, void *_twl)
|
||||
dev_err(twl->dev, "Failed to enable usb3v3\n");
|
||||
|
||||
twl->asleep = 1;
|
||||
status = OMAP_MUSB_VBUS_VALID;
|
||||
status = MUSB_VBUS_VALID;
|
||||
twl->linkstat = status;
|
||||
omap_musb_mailbox(status);
|
||||
musb_mailbox(status);
|
||||
} else {
|
||||
if (twl->linkstat != OMAP_MUSB_UNKNOWN) {
|
||||
status = OMAP_MUSB_VBUS_OFF;
|
||||
if (twl->linkstat != MUSB_UNKNOWN) {
|
||||
status = MUSB_VBUS_OFF;
|
||||
twl->linkstat = status;
|
||||
omap_musb_mailbox(status);
|
||||
musb_mailbox(status);
|
||||
if (twl->asleep) {
|
||||
regulator_disable(twl->usb3v3);
|
||||
twl->asleep = 0;
|
||||
@ -248,7 +248,7 @@ static irqreturn_t twl6030_usb_irq(int irq, void *_twl)
|
||||
static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl)
|
||||
{
|
||||
struct twl6030_usb *twl = _twl;
|
||||
enum omap_musb_vbus_id_status status = OMAP_MUSB_UNKNOWN;
|
||||
enum musb_vbus_id_status status = MUSB_UNKNOWN;
|
||||
u8 hw_state;
|
||||
int ret;
|
||||
|
||||
@ -262,9 +262,9 @@ static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl)
|
||||
twl->asleep = 1;
|
||||
twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_CLR);
|
||||
twl6030_writeb(twl, TWL_MODULE_USB, 0x10, USB_ID_INT_EN_HI_SET);
|
||||
status = OMAP_MUSB_ID_GROUND;
|
||||
status = MUSB_ID_GROUND;
|
||||
twl->linkstat = status;
|
||||
omap_musb_mailbox(status);
|
||||
musb_mailbox(status);
|
||||
} else {
|
||||
twl6030_writeb(twl, TWL_MODULE_USB, 0x10, USB_ID_INT_EN_HI_CLR);
|
||||
twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_SET);
|
||||
@ -334,7 +334,7 @@ static int twl6030_usb_probe(struct platform_device *pdev)
|
||||
twl->dev = &pdev->dev;
|
||||
twl->irq1 = platform_get_irq(pdev, 0);
|
||||
twl->irq2 = platform_get_irq(pdev, 1);
|
||||
twl->linkstat = OMAP_MUSB_UNKNOWN;
|
||||
twl->linkstat = MUSB_UNKNOWN;
|
||||
|
||||
twl->comparator.set_vbus = twl6030_set_vbus;
|
||||
twl->comparator.start_srp = twl6030_start_srp;
|
||||
|
@ -302,37 +302,37 @@ static void usbhsc_set_buswait(struct usbhs_priv *priv)
|
||||
*/
|
||||
|
||||
/* commonly used on old SH-Mobile SoCs */
|
||||
static u32 usbhsc_default_pipe_type[] = {
|
||||
USB_ENDPOINT_XFER_CONTROL,
|
||||
USB_ENDPOINT_XFER_ISOC,
|
||||
USB_ENDPOINT_XFER_ISOC,
|
||||
USB_ENDPOINT_XFER_BULK,
|
||||
USB_ENDPOINT_XFER_BULK,
|
||||
USB_ENDPOINT_XFER_BULK,
|
||||
USB_ENDPOINT_XFER_INT,
|
||||
USB_ENDPOINT_XFER_INT,
|
||||
USB_ENDPOINT_XFER_INT,
|
||||
USB_ENDPOINT_XFER_INT,
|
||||
static struct renesas_usbhs_driver_pipe_config usbhsc_default_pipe[] = {
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_CONTROL, 64, 0x00, false),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_ISOC, 1024, 0x08, false),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_ISOC, 1024, 0x18, false),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x28, true),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x38, true),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x48, true),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x04, false),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x05, false),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x06, false),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x07, false),
|
||||
};
|
||||
|
||||
/* commonly used on newer SH-Mobile and R-Car SoCs */
|
||||
static u32 usbhsc_new_pipe_type[] = {
|
||||
USB_ENDPOINT_XFER_CONTROL,
|
||||
USB_ENDPOINT_XFER_ISOC,
|
||||
USB_ENDPOINT_XFER_ISOC,
|
||||
USB_ENDPOINT_XFER_BULK,
|
||||
USB_ENDPOINT_XFER_BULK,
|
||||
USB_ENDPOINT_XFER_BULK,
|
||||
USB_ENDPOINT_XFER_INT,
|
||||
USB_ENDPOINT_XFER_INT,
|
||||
USB_ENDPOINT_XFER_INT,
|
||||
USB_ENDPOINT_XFER_BULK,
|
||||
USB_ENDPOINT_XFER_BULK,
|
||||
USB_ENDPOINT_XFER_BULK,
|
||||
USB_ENDPOINT_XFER_BULK,
|
||||
USB_ENDPOINT_XFER_BULK,
|
||||
USB_ENDPOINT_XFER_BULK,
|
||||
USB_ENDPOINT_XFER_BULK,
|
||||
static struct renesas_usbhs_driver_pipe_config usbhsc_new_pipe[] = {
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_CONTROL, 64, 0x00, false),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_ISOC, 1024, 0x08, true),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_ISOC, 1024, 0x28, true),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x48, true),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x58, true),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x68, true),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x04, false),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x05, false),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x06, false),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x78, true),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x88, true),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x98, true),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0xa8, true),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0xb8, true),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0xc8, true),
|
||||
RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0xd8, true),
|
||||
};
|
||||
|
||||
/*
|
||||
@ -481,6 +481,15 @@ static const struct of_device_id usbhs_of_match[] = {
|
||||
.compatible = "renesas,usbhs-r8a7795",
|
||||
.data = (void *)USBHS_TYPE_RCAR_GEN2,
|
||||
},
|
||||
{
|
||||
.compatible = "renesas,rcar-gen2-usbhs",
|
||||
.data = (void *)USBHS_TYPE_RCAR_GEN2,
|
||||
},
|
||||
{
|
||||
/* Gen3 is compatible with Gen2 */
|
||||
.compatible = "renesas,rcar-gen3-usbhs",
|
||||
.data = (void *)USBHS_TYPE_RCAR_GEN2,
|
||||
},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, usbhs_of_match);
|
||||
@ -564,10 +573,9 @@ static int usbhs_probe(struct platform_device *pdev)
|
||||
switch (priv->dparam.type) {
|
||||
case USBHS_TYPE_RCAR_GEN2:
|
||||
priv->pfunc = usbhs_rcar2_ops;
|
||||
if (!priv->dparam.pipe_type) {
|
||||
priv->dparam.pipe_type = usbhsc_new_pipe_type;
|
||||
priv->dparam.pipe_size =
|
||||
ARRAY_SIZE(usbhsc_new_pipe_type);
|
||||
if (!priv->dparam.pipe_configs) {
|
||||
priv->dparam.pipe_configs = usbhsc_new_pipe;
|
||||
priv->dparam.pipe_size = ARRAY_SIZE(usbhsc_new_pipe);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -586,9 +594,9 @@ static int usbhs_probe(struct platform_device *pdev)
|
||||
dfunc->notify_hotplug = usbhsc_drvcllbck_notify_hotplug;
|
||||
|
||||
/* set default param if platform doesn't have */
|
||||
if (!priv->dparam.pipe_type) {
|
||||
priv->dparam.pipe_type = usbhsc_default_pipe_type;
|
||||
priv->dparam.pipe_size = ARRAY_SIZE(usbhsc_default_pipe_type);
|
||||
if (!priv->dparam.pipe_configs) {
|
||||
priv->dparam.pipe_configs = usbhsc_default_pipe;
|
||||
priv->dparam.pipe_size = ARRAY_SIZE(usbhsc_default_pipe);
|
||||
}
|
||||
if (!priv->dparam.pio_dma_border)
|
||||
priv->dparam.pio_dma_border = 64; /* 64byte */
|
||||
|
@ -1042,6 +1042,8 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv)
|
||||
struct usbhsg_gpriv *gpriv;
|
||||
struct usbhsg_uep *uep;
|
||||
struct device *dev = usbhs_priv_to_dev(priv);
|
||||
struct renesas_usbhs_driver_pipe_config *pipe_configs =
|
||||
usbhs_get_dparam(priv, pipe_configs);
|
||||
int pipe_size = usbhs_get_dparam(priv, pipe_size);
|
||||
int i;
|
||||
int ret;
|
||||
@ -1111,13 +1113,16 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv)
|
||||
gpriv->gadget.ep0 = &uep->ep;
|
||||
usb_ep_set_maxpacket_limit(&uep->ep, 64);
|
||||
uep->ep.caps.type_control = true;
|
||||
}
|
||||
/* init normal pipe */
|
||||
else {
|
||||
usb_ep_set_maxpacket_limit(&uep->ep, 512);
|
||||
uep->ep.caps.type_iso = true;
|
||||
uep->ep.caps.type_bulk = true;
|
||||
uep->ep.caps.type_int = true;
|
||||
} else {
|
||||
/* init normal pipe */
|
||||
if (pipe_configs[i].type == USB_ENDPOINT_XFER_ISOC)
|
||||
uep->ep.caps.type_iso = true;
|
||||
if (pipe_configs[i].type == USB_ENDPOINT_XFER_BULK)
|
||||
uep->ep.caps.type_bulk = true;
|
||||
if (pipe_configs[i].type == USB_ENDPOINT_XFER_INT)
|
||||
uep->ep.caps.type_int = true;
|
||||
usb_ep_set_maxpacket_limit(&uep->ep,
|
||||
pipe_configs[i].bufsize);
|
||||
list_add_tail(&uep->ep.ep_list, &gpriv->gadget.ep_list);
|
||||
}
|
||||
uep->ep.caps.dir_in = true;
|
||||
|
@ -1414,7 +1414,8 @@ static void usbhsh_pipe_init_for_host(struct usbhs_priv *priv)
|
||||
{
|
||||
struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
|
||||
struct usbhs_pipe *pipe;
|
||||
u32 *pipe_type = usbhs_get_dparam(priv, pipe_type);
|
||||
struct renesas_usbhs_driver_pipe_config *pipe_configs =
|
||||
usbhs_get_dparam(priv, pipe_configs);
|
||||
int pipe_size = usbhs_get_dparam(priv, pipe_size);
|
||||
int old_type, dir_in, i;
|
||||
|
||||
@ -1442,15 +1443,15 @@ static void usbhsh_pipe_init_for_host(struct usbhs_priv *priv)
|
||||
* USB_ENDPOINT_XFER_BULK -> dir in
|
||||
* ...
|
||||
*/
|
||||
dir_in = (pipe_type[i] == old_type);
|
||||
old_type = pipe_type[i];
|
||||
dir_in = (pipe_configs[i].type == old_type);
|
||||
old_type = pipe_configs[i].type;
|
||||
|
||||
if (USB_ENDPOINT_XFER_CONTROL == pipe_type[i]) {
|
||||
if (USB_ENDPOINT_XFER_CONTROL == pipe_configs[i].type) {
|
||||
pipe = usbhs_dcp_malloc(priv);
|
||||
usbhsh_hpriv_to_dcp(hpriv) = pipe;
|
||||
} else {
|
||||
pipe = usbhs_pipe_malloc(priv,
|
||||
pipe_type[i],
|
||||
pipe_configs[i].type,
|
||||
dir_in);
|
||||
}
|
||||
|
||||
|
@ -44,6 +44,15 @@ char *usbhs_pipe_name(struct usbhs_pipe *pipe)
|
||||
return usbhsp_pipe_name[usbhs_pipe_type(pipe)];
|
||||
}
|
||||
|
||||
static struct renesas_usbhs_driver_pipe_config
|
||||
*usbhsp_get_pipe_config(struct usbhs_priv *priv, int pipe_num)
|
||||
{
|
||||
struct renesas_usbhs_driver_pipe_config *pipe_configs =
|
||||
usbhs_get_dparam(priv, pipe_configs);
|
||||
|
||||
return &pipe_configs[pipe_num];
|
||||
}
|
||||
|
||||
/*
|
||||
* DCPCTR/PIPEnCTR functions
|
||||
*/
|
||||
@ -384,18 +393,6 @@ void usbhs_pipe_set_trans_count_if_bulk(struct usbhs_pipe *pipe, int len)
|
||||
/*
|
||||
* pipe setup
|
||||
*/
|
||||
static int usbhsp_possible_double_buffer(struct usbhs_pipe *pipe)
|
||||
{
|
||||
/*
|
||||
* only ISO / BULK pipe can use double buffer
|
||||
*/
|
||||
if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK) ||
|
||||
usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_ISOC))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u16 usbhsp_setup_pipecfg(struct usbhs_pipe *pipe,
|
||||
int is_host,
|
||||
int dir_in)
|
||||
@ -412,7 +409,6 @@ static u16 usbhsp_setup_pipecfg(struct usbhs_pipe *pipe,
|
||||
[USB_ENDPOINT_XFER_INT] = TYPE_INT,
|
||||
[USB_ENDPOINT_XFER_ISOC] = TYPE_ISO,
|
||||
};
|
||||
int is_double = usbhsp_possible_double_buffer(pipe);
|
||||
|
||||
if (usbhs_pipe_is_dcp(pipe))
|
||||
return -EINVAL;
|
||||
@ -434,10 +430,7 @@ static u16 usbhsp_setup_pipecfg(struct usbhs_pipe *pipe,
|
||||
usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK))
|
||||
bfre = 0; /* FIXME */
|
||||
|
||||
/* DBLB */
|
||||
if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_ISOC) ||
|
||||
usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK))
|
||||
dblb = (is_double) ? DBLB : 0;
|
||||
/* DBLB: see usbhs_pipe_config_update() */
|
||||
|
||||
/* CNTMD */
|
||||
if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK))
|
||||
@ -473,13 +466,13 @@ static u16 usbhsp_setup_pipecfg(struct usbhs_pipe *pipe,
|
||||
static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe)
|
||||
{
|
||||
struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
|
||||
struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv);
|
||||
struct device *dev = usbhs_priv_to_dev(priv);
|
||||
int pipe_num = usbhs_pipe_number(pipe);
|
||||
int is_double = usbhsp_possible_double_buffer(pipe);
|
||||
u16 buff_size;
|
||||
u16 bufnmb;
|
||||
u16 bufnmb_cnt;
|
||||
struct renesas_usbhs_driver_pipe_config *pipe_config =
|
||||
usbhsp_get_pipe_config(priv, pipe_num);
|
||||
|
||||
/*
|
||||
* PIPEBUF
|
||||
@ -489,56 +482,13 @@ static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe)
|
||||
* - "Features" - "Pipe configuration"
|
||||
* - "Operation" - "FIFO Buffer Memory"
|
||||
* - "Operation" - "Pipe Control"
|
||||
*
|
||||
* ex) if pipe6 - pipe9 are USB_ENDPOINT_XFER_INT (SH7724)
|
||||
*
|
||||
* BUFNMB: PIPE
|
||||
* 0: pipe0 (DCP 256byte)
|
||||
* 1: -
|
||||
* 2: -
|
||||
* 3: -
|
||||
* 4: pipe6 (INT 64byte)
|
||||
* 5: pipe7 (INT 64byte)
|
||||
* 6: pipe8 (INT 64byte)
|
||||
* 7: pipe9 (INT 64byte)
|
||||
* 8 - xx: free (for BULK, ISOC)
|
||||
*/
|
||||
|
||||
/*
|
||||
* FIXME
|
||||
*
|
||||
* it doesn't have good buffer allocator
|
||||
*
|
||||
* DCP : 256 byte
|
||||
* BULK: 512 byte
|
||||
* INT : 64 byte
|
||||
* ISOC: 512 byte
|
||||
*/
|
||||
if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_CONTROL))
|
||||
buff_size = 256;
|
||||
else if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_INT))
|
||||
buff_size = 64;
|
||||
else
|
||||
buff_size = 512;
|
||||
buff_size = pipe_config->bufsize;
|
||||
bufnmb = pipe_config->bufnum;
|
||||
|
||||
/* change buff_size to register value */
|
||||
bufnmb_cnt = (buff_size / 64) - 1;
|
||||
|
||||
/* BUFNMB has been reserved for INT pipe
|
||||
* see above */
|
||||
if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_INT)) {
|
||||
bufnmb = pipe_num - 2;
|
||||
} else {
|
||||
bufnmb = info->bufnmb_last;
|
||||
info->bufnmb_last += bufnmb_cnt + 1;
|
||||
|
||||
/*
|
||||
* double buffer
|
||||
*/
|
||||
if (is_double)
|
||||
info->bufnmb_last += bufnmb_cnt + 1;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "pipe : %d : buff_size 0x%x: bufnmb 0x%x\n",
|
||||
pipe_num, buff_size, bufnmb);
|
||||
|
||||
@ -549,8 +499,13 @@ static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe)
|
||||
void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 devsel,
|
||||
u16 epnum, u16 maxp)
|
||||
{
|
||||
struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
|
||||
int pipe_num = usbhs_pipe_number(pipe);
|
||||
struct renesas_usbhs_driver_pipe_config *pipe_config =
|
||||
usbhsp_get_pipe_config(priv, pipe_num);
|
||||
u16 dblb = pipe_config->double_buf ? DBLB : 0;
|
||||
|
||||
if (devsel > 0xA) {
|
||||
struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
|
||||
struct device *dev = usbhs_priv_to_dev(priv);
|
||||
|
||||
dev_err(dev, "devsel error %d\n", devsel);
|
||||
@ -568,7 +523,7 @@ void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 devsel,
|
||||
maxp);
|
||||
|
||||
if (!usbhs_pipe_is_dcp(pipe))
|
||||
usbhsp_pipe_cfg_set(pipe, 0x000F, epnum);
|
||||
usbhsp_pipe_cfg_set(pipe, 0x000F | DBLB, epnum | dblb);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -708,23 +663,7 @@ void usbhs_pipe_init(struct usbhs_priv *priv,
|
||||
struct usbhs_pipe *pipe;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* FIXME
|
||||
*
|
||||
* driver needs good allocator.
|
||||
*
|
||||
* find first free buffer area (BULK, ISOC)
|
||||
* (DCP, INT area is fixed)
|
||||
*
|
||||
* buffer number 0 - 3 have been reserved for DCP
|
||||
* see
|
||||
* usbhsp_to_bufnmb
|
||||
*/
|
||||
info->bufnmb_last = 4;
|
||||
usbhs_for_each_pipe_with_dcp(pipe, priv, i) {
|
||||
if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_INT))
|
||||
info->bufnmb_last++;
|
||||
|
||||
usbhsp_flags_init(pipe);
|
||||
pipe->fifo = NULL;
|
||||
pipe->mod_private = NULL;
|
||||
@ -851,12 +790,13 @@ int usbhs_pipe_probe(struct usbhs_priv *priv)
|
||||
struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv);
|
||||
struct usbhs_pipe *pipe;
|
||||
struct device *dev = usbhs_priv_to_dev(priv);
|
||||
u32 *pipe_type = usbhs_get_dparam(priv, pipe_type);
|
||||
struct renesas_usbhs_driver_pipe_config *pipe_configs =
|
||||
usbhs_get_dparam(priv, pipe_configs);
|
||||
int pipe_size = usbhs_get_dparam(priv, pipe_size);
|
||||
int i;
|
||||
|
||||
/* This driver expects 1st pipe is DCP */
|
||||
if (pipe_type[0] != USB_ENDPOINT_XFER_CONTROL) {
|
||||
if (pipe_configs[0].type != USB_ENDPOINT_XFER_CONTROL) {
|
||||
dev_err(dev, "1st PIPE is not DCP\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -876,10 +816,10 @@ int usbhs_pipe_probe(struct usbhs_priv *priv)
|
||||
pipe->priv = priv;
|
||||
|
||||
usbhs_pipe_type(pipe) =
|
||||
pipe_type[i] & USB_ENDPOINT_XFERTYPE_MASK;
|
||||
pipe_configs[i].type & USB_ENDPOINT_XFERTYPE_MASK;
|
||||
|
||||
dev_dbg(dev, "pipe %x\t: %s\n",
|
||||
i, usbhsp_pipe_name[pipe_type[i]]);
|
||||
i, usbhsp_pipe_name[pipe_configs[i].type]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -46,7 +46,6 @@ struct usbhs_pipe {
|
||||
struct usbhs_pipe_info {
|
||||
struct usbhs_pipe *pipe;
|
||||
int size; /* array size of "pipe" */
|
||||
int bufnmb_last; /* FIXME : driver needs good allocator */
|
||||
|
||||
int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map);
|
||||
};
|
||||
|
@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Renesas Solutions Corp.
|
||||
* Copyright (C) 2013 Cogent Embedded, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __USB_RCAR_PHY_H
|
||||
#define __USB_RCAR_PHY_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct rcar_phy_platform_data {
|
||||
bool ferrite_bead:1; /* (R8A7778 only) */
|
||||
|
||||
bool port1_func:1; /* true: port 1 used by function, false: host */
|
||||
unsigned penc1:1; /* Output of the PENC1 pin in function mode */
|
||||
struct { /* Overcurrent pin control for ports 0..2 */
|
||||
bool select_3_3v:1; /* true: USB_OVCn pin, false: OVCn pin */
|
||||
/* Set to false on port 1 in function mode */
|
||||
bool active_high:1; /* true: active high, false: active low */
|
||||
/* Set to true on port 1 in function mode */
|
||||
} ovc_pin[3]; /* (R8A7778 only has 2 ports) */
|
||||
};
|
||||
|
||||
#endif /* __USB_RCAR_PHY_H */
|
@ -402,6 +402,9 @@ static inline void usb_ep_free_request(struct usb_ep *ep,
|
||||
static inline int usb_ep_queue(struct usb_ep *ep,
|
||||
struct usb_request *req, gfp_t gfp_flags)
|
||||
{
|
||||
if (WARN_ON_ONCE(!ep->enabled && ep->address))
|
||||
return -ESHUTDOWN;
|
||||
|
||||
return ep->ops->queue(ep, req, gfp_flags);
|
||||
}
|
||||
|
||||
@ -1012,6 +1015,9 @@ static inline int usb_gadget_activate(struct usb_gadget *gadget)
|
||||
* @reset: Invoked on USB bus reset. It is mandatory for all gadget drivers
|
||||
* and should be called in_interrupt.
|
||||
* @driver: Driver model state for this driver.
|
||||
* @udc_name: A name of UDC this driver should be bound to. If udc_name is NULL,
|
||||
* this driver will be bound to any available UDC.
|
||||
* @pending: UDC core private data used for deferred probe of this driver.
|
||||
*
|
||||
* Devices are disabled till a gadget driver successfully bind()s, which
|
||||
* means the driver will handle setup() requests needed to enumerate (and
|
||||
@ -1072,6 +1078,9 @@ struct usb_gadget_driver {
|
||||
|
||||
/* FIXME support safe rmmod */
|
||||
struct device_driver driver;
|
||||
|
||||
char *udc_name;
|
||||
struct list_head pending;
|
||||
};
|
||||
|
||||
|
||||
@ -1117,8 +1126,6 @@ extern int usb_add_gadget_udc_release(struct device *parent,
|
||||
struct usb_gadget *gadget, void (*release)(struct device *dev));
|
||||
extern int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget);
|
||||
extern void usb_del_gadget_udc(struct usb_gadget *gadget);
|
||||
extern int usb_udc_attach_driver(const char *name,
|
||||
struct usb_gadget_driver *driver);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
|
@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2012 by Texas Instruments
|
||||
*
|
||||
* The Inventra Controller Driver for Linux is free software; you
|
||||
* can redistribute it and/or modify it under the terms of the GNU
|
||||
* General Public License version 2 as published by the Free Software
|
||||
* Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __MUSB_OMAP_H__
|
||||
#define __MUSB_OMAP_H__
|
||||
|
||||
enum omap_musb_vbus_id_status {
|
||||
OMAP_MUSB_UNKNOWN = 0,
|
||||
OMAP_MUSB_ID_GROUND,
|
||||
OMAP_MUSB_ID_FLOAT,
|
||||
OMAP_MUSB_VBUS_VALID,
|
||||
OMAP_MUSB_VBUS_OFF,
|
||||
};
|
||||
|
||||
#if (defined(CONFIG_USB_MUSB_OMAP2PLUS) || \
|
||||
defined(CONFIG_USB_MUSB_OMAP2PLUS_MODULE))
|
||||
void omap_musb_mailbox(enum omap_musb_vbus_id_status status);
|
||||
#else
|
||||
static inline void omap_musb_mailbox(enum omap_musb_vbus_id_status status)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __MUSB_OMAP_H__ */
|
@ -133,6 +133,21 @@ struct musb_hdrc_platform_data {
|
||||
const void *platform_ops;
|
||||
};
|
||||
|
||||
enum musb_vbus_id_status {
|
||||
MUSB_UNKNOWN = 0,
|
||||
MUSB_ID_GROUND,
|
||||
MUSB_ID_FLOAT,
|
||||
MUSB_VBUS_VALID,
|
||||
MUSB_VBUS_OFF,
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_MUSB_HDRC)
|
||||
void musb_mailbox(enum musb_vbus_id_status status);
|
||||
#else
|
||||
static inline void musb_mailbox(enum musb_vbus_id_status status)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/* TUSB 6010 support */
|
||||
|
||||
|
@ -12,10 +12,16 @@
|
||||
#include <linux/usb/phy.h>
|
||||
|
||||
#if IS_ENABLED(CONFIG_OF)
|
||||
enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *phy_np);
|
||||
bool of_usb_host_tpl_support(struct device_node *np);
|
||||
int of_usb_update_otg_caps(struct device_node *np,
|
||||
struct usb_otg_caps *otg_caps);
|
||||
#else
|
||||
static inline enum usb_dr_mode
|
||||
of_usb_get_dr_mode_by_phy(struct device_node *phy_np)
|
||||
{
|
||||
return USB_DR_MODE_UNKNOWN;
|
||||
}
|
||||
static inline bool of_usb_host_tpl_support(struct device_node *np)
|
||||
{
|
||||
return false;
|
||||
|
@ -105,12 +105,26 @@ struct renesas_usbhs_platform_callback {
|
||||
* some register needs USB chip specific parameters.
|
||||
* This struct show it to driver
|
||||
*/
|
||||
|
||||
struct renesas_usbhs_driver_pipe_config {
|
||||
u8 type; /* USB_ENDPOINT_XFER_xxx */
|
||||
u16 bufsize;
|
||||
u8 bufnum;
|
||||
bool double_buf;
|
||||
};
|
||||
#define RENESAS_USBHS_PIPE(_type, _size, _num, _double_buf) { \
|
||||
.type = (_type), \
|
||||
.bufsize = (_size), \
|
||||
.bufnum = (_num), \
|
||||
.double_buf = (_double_buf), \
|
||||
}
|
||||
|
||||
struct renesas_usbhs_driver_param {
|
||||
/*
|
||||
* pipe settings
|
||||
*/
|
||||
u32 *pipe_type; /* array of USB_ENDPOINT_XFER_xxx (from ep0) */
|
||||
int pipe_size; /* pipe_type array size */
|
||||
struct renesas_usbhs_driver_pipe_config *pipe_configs;
|
||||
int pipe_size; /* pipe_configs array size */
|
||||
|
||||
/*
|
||||
* option:
|
||||
|
Loading…
x
Reference in New Issue
Block a user