1
0
Fork 0

USB patches for 4.5-rc1

Here is the big USB drivers update for 4.5-rc1.  Lots of gadget driver
 updates and fixes, like usual, and a mix of other USB driver updates as
 well.  Full details in the shortlog.  All of these have been in
 linux-next for a while.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iEYEABECAAYFAlaV2rUACgkQMUfUDdst+ym2XQCgqdDOlyGX5B//9CZ2kH1DrDW9
 qLsAoLSBvw4hk+Aotv6tn8AayMpHwqV1
 =pFLC
 -----END PGP SIGNATURE-----

Merge tag 'usb-4.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb

Pull USB updates from Greg KH:
 "Here is the big USB drivers update for 4.5-rc1.

  Lots of gadget driver updates and fixes, like usual, and a mix of
  other USB driver updates as well.  Full details in the shortlog.  All
  of these have been in linux-next for a while"

* tag 'usb-4.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (191 commits)
  MAINTAINERS: change my email address
  USB: usbmon: remove assignment from IS_ERR argument
  USB: mxu11x0: drop redundant function name from error messages
  USB: mxu11x0: fix debug-message typos
  USB: mxu11x0: rename usb-serial driver
  USB: mxu11x0: fix modem-control handling on B0-transitions
  USB: mxu11x0: fix memory leak on firmware download
  USB: mxu11x0: fix memory leak in port-probe error path
  USB: serial: add Moxa UPORT 11x0 driver
  USB: cp210x: add ID for ELV Marble Sound Board 1
  usb: chipidea: otg: use usb autosuspend to suspend bus for HNP
  usb: chipidea: host: set host to be null after hcd is freed
  usb: chipidea: removing of_find_property
  usb: chipidea: implement platform shutdown callback
  usb: chipidea: clean up CONFIG_USB_CHIPIDEA_DEBUG reference
  usb: chipidea: delete static debug support
  usb: chipidea: support debugfs without CONFIG_USB_CHIPIDEA_DEBUG
  usb: chipidea: udc: improve error handling on _hardware_enqueue
  usb: chipidea: udc: _ep_queue and _hw_queue cleanup
  usb: dwc3: of-simple: fix build warning on !PM
  ...
steinar/wifi_calib_4_9_kernel
Linus Torvalds 2016-01-13 09:26:40 -08:00
commit 237f38c3b3
174 changed files with 9147 additions and 2053 deletions

View File

@ -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

View File

@ -134,19 +134,21 @@ Description:
enabled for the device. Developer can write y/Y/1 or n/N/0 to
the file to enable/disable the feature.
What: /sys/bus/usb/devices/.../power/usb3_hardware_lpm
Date: June 2015
What: /sys/bus/usb/devices/.../power/usb3_hardware_lpm_u1
/sys/bus/usb/devices/.../power/usb3_hardware_lpm_u2
Date: November 2015
Contact: Kevin Strasser <kevin.strasser@linux.intel.com>
Lu Baolu <baolu.lu@linux.intel.com>
Description:
If CONFIG_PM is set and a USB 3.0 lpm-capable device is plugged
in to a xHCI host which supports link PM, it will check if U1
and U2 exit latencies have been set in the BOS descriptor; if
the check is is passed and the host supports USB3 hardware LPM,
the check is passed and the host supports USB3 hardware LPM,
USB3 hardware LPM will be enabled for the device and the USB
device directory will contain a file named
power/usb3_hardware_lpm. The file holds a string value (enable
or disable) indicating whether or not USB3 hardware LPM is
enabled for the device.
device directory will contain two files named
power/usb3_hardware_lpm_u1 and power/usb3_hardware_lpm_u2. These
files hold a string value (enable or disable) indicating whether
or not USB3 hardware LPM U1 or U2 is enabled for the device.
What: /sys/bus/usb/devices/.../removable
Date: February 2012
@ -187,6 +189,17 @@ Description:
The file will read "hotplug", "wired" and "not used" if the
information is available, and "unknown" otherwise.
What: /sys/bus/usb/devices/.../(hub interface)/portX/usb3_lpm_permit
Date: November 2015
Contact: Lu Baolu <baolu.lu@linux.intel.com>
Description:
Some USB3.0 devices are not friendly to USB3 LPM. usb3_lpm_permit
attribute allows enabling/disabling usb3 lpm of a port. It takes
effect both before and after a usb device is enumerated. Supported
values are "0" if both u1 and u2 are NOT permitted, "u1" if only u1
is permitted, "u2" if only u2 is permitted, "u1_u2" if both u1 and
u2 are permitted.
What: /sys/bus/usb/devices/.../power/usb2_lpm_l1_timeout
Date: May 2013
Contact: Mathias Nyman <mathias.nyman@linux.intel.com>

View File

@ -2,6 +2,7 @@
Required properties:
- compatible: should be one or more of
"brcm,bcm7425-sata-phy"
"brcm,bcm7445-sata-phy"
"brcm,phy-sata3"
- address-cells: should be 1

View File

@ -0,0 +1,16 @@
Hisilicon hi6220 usb PHY
-----------------------
Required properties:
- compatible: should be "hisilicon,hi6220-usb-phy"
- #phy-cells: must be 0
- hisilicon,peripheral-syscon: phandle of syscon used to control phy.
Refer to phy/phy-bindings.txt for the generic PHY binding properties
Example:
usb_phy: usbphy {
compatible = "hisilicon,hi6220-usb-phy";
#phy-cells = <0>;
phy-supply = <&fixed_5v_hub>;
hisilicon,peripheral-syscon = <&sys_ctrl>;
};

View File

@ -0,0 +1,39 @@
* Renesas R-Car generation 3 USB 2.0 PHY
This file provides information on what the device node for the R-Car generation
3 USB 2.0 PHY contains.
Required properties:
- compatible: "renesas,usb2-phy-r8a7795" if the device is a part of an R8A7795
SoC.
- reg: offset and length of the partial USB 2.0 Host register block.
- reg-names: must be "usb2_host".
- clocks: clock phandle and specifier pair(s).
- #phy-cells: see phy-bindings.txt in the same directory, must be <0>.
Optional properties:
To use a USB channel where USB 2.0 Host and HSUSB (USB 2.0 Peripheral) are
combined, the device tree node should set HSUSB properties to reg and reg-names
properties. This is because HSUSB has registers to select USB 2.0 host or
peripheral at that channel:
- reg: offset and length of the partial HSUSB register block.
- reg-names: must be "hsusb".
- interrupts: interrupt specifier for the PHY.
Example (R-Car H3):
usb-phy@ee080200 {
compatible = "renesas,usb2-phy-r8a7795";
reg = <0 0xee080200 0 0x700>, <0 0xe6590100 0 0x100>;
reg-names = "usb2_host", "hsusb";
interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&mstp7_clks R8A7795_CLK_EHCI0>,
<&mstp7_clks R8A7795_CLK_HSUSB>;
};
usb-phy@ee0a0200 {
compatible = "renesas,usb2-phy-r8a7795";
reg = <0 0xee0a0200 0 0x700>;
reg-names = "usb2_host";
clocks = <&mstp7_clks R8A7795_CLK_EHCI0>;
};

View File

@ -1,7 +1,10 @@
ROCKCHIP USB2 PHY
Required properties:
- compatible: rockchip,rk3288-usb-phy
- compatible: matching the soc type, one of
"rockchip,rk3066a-usb-phy"
"rockchip,rk3188-usb-phy"
"rockchip,rk3288-usb-phy"
- rockchip,grf : phandle to the syscon managing the "general
register files"
- #address-cells: should be 1
@ -21,6 +24,7 @@ required properties:
Optional Properties:
- clocks : phandle + clock specifier for the phy clocks
- clock-names: string, clock name, must be "phyclk"
- #clock-cells: for users of the phy-pll, should be 0
Example:

View File

@ -9,6 +9,7 @@ Required properties:
* allwinner,sun7i-a20-usb-phy
* allwinner,sun8i-a23-usb-phy
* allwinner,sun8i-a33-usb-phy
* allwinner,sun8i-h3-usb-phy
- reg : a list of offset + length pairs
- reg-names :
* "phy_ctrl"

View File

@ -31,6 +31,8 @@ OMAP USB2 PHY
Required properties:
- compatible: Should be "ti,omap-usb2"
Should be "ti,dra7x-usb2-phy2" for the 2nd instance of USB2 PHY
in DRA7x
- reg : Address and length of the register set for the device.
- #phy-cells: determine the number of cells that should be given in the
phandle while referencing this phy.
@ -40,10 +42,14 @@ Required properties:
* "wkupclk" - wakeup clock.
* "refclk" - reference clock (optional).
Optional properties:
Deprecated properties:
- ctrl-module : phandle of the control module used by PHY driver to power on
the PHY.
Recommended properies:
- syscon-phy-power : phandle/offset pair. Phandle to the system control
module and the register offset to power on/off the PHY.
This is usually a subnode of ocp2scp to which it is connected.
usb2phy@4a0ad080 {
@ -77,14 +83,22 @@ Required properties:
* "div-clk" - apll clock
Optional properties:
- ctrl-module : phandle of the control module used by PHY driver to power on
the PHY.
- id: If there are multiple instance of the same type, in order to
differentiate between each instance "id" can be used (e.g., multi-lane PCIe
PHY). If "id" is not provided, it is set to default value of '1'.
- syscon-pllreset: Handle to system control region that contains the
CTRL_CORE_SMA_SW_0 register and register offset to the CTRL_CORE_SMA_SW_0
register that contains the SATA_PLL_SOFT_RESET bit. Only valid for sata_phy.
- syscon-pcs : phandle/offset pair. Phandle to the system control module and the
register offset to write the PCS delay value.
Deprecated properties:
- ctrl-module : phandle of the control module used by PHY driver to power on
the PHY.
Recommended properies:
- syscon-phy-power : phandle/offset pair. Phandle to the system control
module and the register offset to power on/off the PHY.
This is usually a subnode of ocp2scp to which it is connected.

View File

@ -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;

View 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";
};
};

View File

@ -0,0 +1,51 @@
MT8173 xHCI
The device node for Mediatek SOC USB3.0 host controller
Required properties:
- compatible : should contain "mediatek,mt8173-xhci"
- reg : specifies physical base address and size of the registers,
the first one for MAC, the second for IPPC
- interrupts : interrupt used by the controller
- power-domains : a phandle to USB power domain node to control USB's
mtcmos
- vusb33-supply : regulator of USB avdd3.3v
- clocks : a list of phandle + clock-specifier pairs, one for each
entry in clock-names
- clock-names : must contain
"sys_ck": for clock of xHCI MAC
"wakeup_deb_p0": for USB wakeup debounce clock of port0
"wakeup_deb_p1": for USB wakeup debounce clock of port1
- phys : a list of phandle + phy specifier pairs
Optional properties:
- mediatek,wakeup-src : 1: ip sleep wakeup mode; 2: line state wakeup
mode;
- mediatek,syscon-wakeup : phandle to syscon used to access USB wakeup
control register, it depends on "mediatek,wakeup-src".
- vbus-supply : reference to the VBUS regulator;
- usb3-lpm-capable : supports USB3.0 LPM
Example:
usb30: usb@11270000 {
compatible = "mediatek,mt8173-xhci";
reg = <0 0x11270000 0 0x1000>,
<0 0x11280700 0 0x0100>;
interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_LOW>;
power-domains = <&scpsys MT8173_POWER_DOMAIN_USB>;
clocks = <&topckgen CLK_TOP_USB30_SEL>,
<&pericfg CLK_PERI_USB0>,
<&pericfg CLK_PERI_USB1>;
clock-names = "sys_ck",
"wakeup_deb_p0",
"wakeup_deb_p1";
phys = <&phy_port0 PHY_TYPE_USB3>,
<&phy_port1 PHY_TYPE_USB2>;
vusb33-supply = <&mt6397_vusb_reg>;
vbus-supply = <&usb_p1_vbus>;
usb3-lpm-capable;
mediatek,syscon-wakeup = <&pericfg>;
mediatek,wakeup-src = <1>;
};

View 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>;
};

View File

@ -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>;

View File

@ -3,8 +3,8 @@ USB xHCI controllers
Required properties:
- compatible: should be one of "generic-xhci",
"marvell,armada-375-xhci", "marvell,armada-380-xhci",
"renesas,xhci-r8a7790", "renesas,xhci-r8a7791" (deprecated:
"xhci-platform").
"renesas,xhci-r8a7790", "renesas,xhci-r8a7791", "renesas,xhci-r8a7793",
"renesas,xhci-r8a7795" (deprecated: "xhci-platform").
- reg: should contain address and length of the standard XHCI
register set for the device.
- interrupts: one XHCI interrupt should be described here.

View File

@ -2584,8 +2584,6 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
notsc [BUGS=X86-32] Disable Time Stamp Counter
nousb [USB] Disable the USB subsystem
nowatchdog [KNL] Disable both lockup detectors, i.e.
soft-lockup and NMI watchdog (hard-lockup).
@ -3900,6 +3898,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
usbcore.usbfs_snoop=
[USB] Set to log all usbfs traffic (default 0 = off).
usbcore.usbfs_snoop_max=
[USB] Maximum number of bytes to snoop in each URB
(default = 65536).
usbcore.blinkenlights=
[USB] Set to cycle leds on hubs (default 0 = off).
@ -3920,6 +3922,8 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
USB_REQ_GET_DESCRIPTOR request in milliseconds
(default 5000 = 5.0 seconds).
usbcore.nousb [USB] Disable the USB subsystem
usbhid.mousepoll=
[USBHID] The interval which mice are to be polled at.

View File

@ -7,8 +7,8 @@ with 2 Freescale i.MX6Q sabre SD boards.
---------------------------------------
Select CONFIG_USB_OTG_FSM, rebuild kernel Image and modules.
If you want to check some internal variables for otg fsm,
select CONFIG_USB_CHIPIDEA_DEBUG, there are 2 files which
can show otg fsm variables and some controller registers value:
mount debugfs, there are 2 files which can show otg fsm
variables and some controller registers value:
cat /sys/kernel/debug/ci_hdrc.0/otg
cat /sys/kernel/debug/ci_hdrc.0/registers

View File

@ -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
-------------------------------

View File

@ -537,17 +537,18 @@ relevant attribute files are usb2_hardware_lpm and usb3_hardware_lpm.
can write y/Y/1 or n/N/0 to the file to enable/disable
USB2 hardware LPM manually. This is for test purpose mainly.
power/usb3_hardware_lpm
power/usb3_hardware_lpm_u1
power/usb3_hardware_lpm_u2
When a USB 3.0 lpm-capable device is plugged in to a
xHCI host which supports link PM, it will check if U1
and U2 exit latencies have been set in the BOS
descriptor; if the check is is passed and the host
supports USB3 hardware LPM, USB3 hardware LPM will be
enabled for the device and this file will be created.
The file holds a string value (enable or disable)
indicating whether or not USB3 hardware LPM is
enabled for the device.
enabled for the device and these files will be created.
The files hold a string value (enable or disable)
indicating whether or not USB3 hardware LPM U1 or U2
is enabled for the device.
USB Port Power Control
----------------------

View File

@ -2767,7 +2767,7 @@ S: Maintained
F: Documentation/zh_CN/
CHIPIDEA USB HIGH SPEED DUAL ROLE CONTROLLER
M: Peter Chen <Peter.Chen@freescale.com>
M: Peter Chen <Peter.Chen@nxp.com>
T: git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git
L: linux-usb@vger.kernel.org
S: Maintained
@ -8992,6 +8992,12 @@ L: linux-sh@vger.kernel.org
F: drivers/net/ethernet/renesas/
F: include/linux/sh_eth.h
RENESAS USB2 PHY DRIVER
M: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
L: linux-sh@vger.kernel.org
S: Maintained
F: drivers/phy/phy-rcar-gen3-usb2.c
RESET CONTROLLER FRAMEWORK
M: Philipp Zabel <p.zabel@pengutronix.de>
S: Maintained
@ -11188,7 +11194,7 @@ F: Documentation/usb/ohci.txt
F: drivers/usb/host/ohci*
USB OTG FSM (Finite State Machine)
M: Peter Chen <Peter.Chen@freescale.com>
M: Peter Chen <Peter.Chen@nxp.com>
T: git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git
L: linux-usb@vger.kernel.org
S: Maintained

View File

@ -13,6 +13,7 @@
*/
/dts-v1/;
#include <dt-bindings/gpio/gpio.h>
#include "mt8173.dtsi"
/ {
@ -32,6 +33,15 @@
};
chosen { };
usb_p1_vbus: regulator@0 {
compatible = "regulator-fixed";
regulator-name = "usb_vbus";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
gpio = <&pio 130 GPIO_ACTIVE_HIGH>;
enable-active-high;
};
};
&i2c1 {
@ -408,3 +418,9 @@
&uart0 {
status = "okay";
};
&usb30 {
vusb33-supply = <&mt6397_vusb_reg>;
vbus-supply = <&usb_p1_vbus>;
mediatek,wakeup-src = <1>;
};

View File

@ -14,6 +14,7 @@
#include <dt-bindings/clock/mt8173-clk.h>
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/phy/phy.h>
#include <dt-bindings/power/mt8173-power.h>
#include <dt-bindings/reset-controller/mt8173-resets.h>
#include "mt8173-pinfunc.h"
@ -510,6 +511,47 @@
status = "disabled";
};
usb30: usb@11270000 {
compatible = "mediatek,mt8173-xhci";
reg = <0 0x11270000 0 0x1000>,
<0 0x11280700 0 0x0100>;
interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_LOW>;
power-domains = <&scpsys MT8173_POWER_DOMAIN_USB>;
clocks = <&topckgen CLK_TOP_USB30_SEL>,
<&pericfg CLK_PERI_USB0>,
<&pericfg CLK_PERI_USB1>;
clock-names = "sys_ck",
"wakeup_deb_p0",
"wakeup_deb_p1";
phys = <&phy_port0 PHY_TYPE_USB3>,
<&phy_port1 PHY_TYPE_USB2>;
mediatek,syscon-wakeup = <&pericfg>;
status = "okay";
};
u3phy: usb-phy@11290000 {
compatible = "mediatek,mt8173-u3phy";
reg = <0 0x11290000 0 0x800>;
clocks = <&apmixedsys CLK_APMIXED_REF2USB_TX>;
clock-names = "u3phya_ref";
#address-cells = <2>;
#size-cells = <2>;
ranges;
status = "okay";
phy_port0: port@11290800 {
reg = <0 0x11290800 0 0x800>;
#phy-cells = <1>;
status = "okay";
};
phy_port1: port@11291000 {
reg = <0 0x11291000 0 0x800>;
#phy-cells = <1>;
status = "okay";
};
};
mmsys: clock-controller@14000000 {
compatible = "mediatek,mt8173-mmsys", "syscon";
reg = <0 0x14000000 0 0x1000>;

View File

@ -118,6 +118,13 @@ config PHY_RCAR_GEN2
help
Support for USB PHY found on Renesas R-Car generation 2 SoCs.
config PHY_RCAR_GEN3_USB2
tristate "Renesas R-Car generation 3 USB 2.0 PHY driver"
depends on OF && ARCH_SHMOBILE
select GENERIC_PHY
help
Support for USB 2.0 PHY found on Renesas R-Car generation 3 SoCs.
config OMAP_CONTROL_PHY
tristate "OMAP CONTROL PHY Driver"
depends on ARCH_OMAP2PLUS || COMPILE_TEST
@ -215,6 +222,15 @@ config PHY_MT65XX_USB3
for mt65xx SoCs. it supports two usb2.0 ports and
one usb3.0 port.
config PHY_HI6220_USB
tristate "hi6220 USB PHY support"
select GENERIC_PHY
select MFD_SYSCON
help
Enable this to support the HISILICON HI6220 USB PHY.
To compile this driver as a module, choose M here.
config PHY_SUN4I_USB
tristate "Allwinner sunxi SoC USB PHY driver"
depends on ARCH_SUNXI && HAS_IOMEM && OF
@ -374,11 +390,11 @@ config PHY_TUSB1210
config PHY_BRCMSTB_SATA
tristate "Broadcom STB SATA PHY driver"
depends on ARCH_BRCMSTB
depends on ARCH_BRCMSTB || BMIPS_GENERIC
depends on OF
select GENERIC_PHY
help
Enable this to support the SATA3 PHY on 28nm Broadcom STB SoCs.
Enable this to support the SATA3 PHY on 28nm or 40nm Broadcom STB SoCs.
Likely useful only with CONFIG_SATA_BRCMSTB enabled.
config PHY_CYGNUS_PCIE

View File

@ -17,12 +17,14 @@ obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o
obj-$(CONFIG_PHY_MIPHY28LP) += phy-miphy28lp.o
obj-$(CONFIG_PHY_MIPHY365X) += phy-miphy365x.o
obj-$(CONFIG_PHY_RCAR_GEN2) += phy-rcar-gen2.o
obj-$(CONFIG_PHY_RCAR_GEN3_USB2) += phy-rcar-gen3-usb2.o
obj-$(CONFIG_OMAP_CONTROL_PHY) += phy-omap-control.o
obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o
obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
obj-$(CONFIG_PHY_EXYNOS5250_SATA) += phy-exynos5250-sata.o
obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o
obj-$(CONFIG_PHY_HI6220_USB) += phy-hi6220-usb.o
obj-$(CONFIG_PHY_MT65XX_USB3) += phy-mt65xx-usb3.o
obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o
obj-$(CONFIG_PHY_SUN9I_USB) += phy-sun9i-usb.o

View File

@ -9,11 +9,9 @@
* warranty of any kind, whether express or implied.
*/
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
@ -195,7 +193,6 @@ static int phy_berlin_usb_probe(struct platform_device *pdev)
return PTR_ERR(phy);
}
platform_set_drvdata(pdev, priv);
phy_set_drvdata(phy, priv);
phy_provider =

View File

@ -26,13 +26,21 @@
#define SATA_MDIO_BANK_OFFSET 0x23c
#define SATA_MDIO_REG_OFFSET(ofs) ((ofs) * 4)
#define SATA_MDIO_REG_SPACE_SIZE 0x1000
#define SATA_MDIO_REG_LENGTH 0x1f00
#define MAX_PORTS 2
/* Register offset between PHYs in PCB space */
#define SATA_MDIO_REG_SPACE_SIZE 0x1000
#define SATA_MDIO_REG_28NM_SPACE_SIZE 0x1000
/* The older SATA PHY registers duplicated per port registers within the map,
* rather than having a separate map per port.
*/
#define SATA_MDIO_REG_40NM_SPACE_SIZE 0x10
enum brcm_sata_phy_version {
BRCM_SATA_PHY_28NM,
BRCM_SATA_PHY_40NM,
};
struct brcm_sata_port {
int portnum;
@ -44,11 +52,12 @@ struct brcm_sata_port {
struct brcm_sata_phy {
struct device *dev;
void __iomem *phy_base;
enum brcm_sata_phy_version version;
struct brcm_sata_port phys[MAX_PORTS];
};
enum sata_mdio_phy_regs_28nm {
enum sata_mdio_phy_regs {
PLL_REG_BANK_0 = 0x50,
PLL_REG_BANK_0_PLLCONTROL_0 = 0x81,
@ -66,8 +75,16 @@ enum sata_mdio_phy_regs_28nm {
static inline void __iomem *brcm_sata_phy_base(struct brcm_sata_port *port)
{
struct brcm_sata_phy *priv = port->phy_priv;
u32 offset = 0;
return priv->phy_base + (port->portnum * SATA_MDIO_REG_SPACE_SIZE);
if (priv->version == BRCM_SATA_PHY_28NM)
offset = SATA_MDIO_REG_28NM_SPACE_SIZE;
else if (priv->version == BRCM_SATA_PHY_40NM)
offset = SATA_MDIO_REG_40NM_SPACE_SIZE;
else
dev_err(priv->dev, "invalid phy version\n");
return priv->phy_base + (port->portnum * offset);
}
static void brcm_sata_mdio_wr(void __iomem *addr, u32 bank, u32 ofs,
@ -86,7 +103,7 @@ static void brcm_sata_mdio_wr(void __iomem *addr, u32 bank, u32 ofs,
#define FMAX_VAL_DEFAULT 0x3df
#define FMAX_VAL_SSC 0x83
static void brcm_sata_cfg_ssc_28nm(struct brcm_sata_port *port)
static void brcm_sata_cfg_ssc(struct brcm_sata_port *port)
{
void __iomem *base = brcm_sata_phy_base(port);
struct brcm_sata_phy *priv = port->phy_priv;
@ -117,18 +134,21 @@ static int brcm_sata_phy_init(struct phy *phy)
{
struct brcm_sata_port *port = phy_get_drvdata(phy);
brcm_sata_cfg_ssc_28nm(port);
brcm_sata_cfg_ssc(port);
return 0;
}
static const struct phy_ops phy_ops_28nm = {
static const struct phy_ops phy_ops = {
.init = brcm_sata_phy_init,
.owner = THIS_MODULE,
};
static const struct of_device_id brcm_sata_phy_of_match[] = {
{ .compatible = "brcm,bcm7445-sata-phy" },
{ .compatible = "brcm,bcm7445-sata-phy",
.data = (void *)BRCM_SATA_PHY_28NM },
{ .compatible = "brcm,bcm7425-sata-phy",
.data = (void *)BRCM_SATA_PHY_40NM },
{},
};
MODULE_DEVICE_TABLE(of, brcm_sata_phy_of_match);
@ -137,6 +157,7 @@ static int brcm_sata_phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *dn = dev->of_node, *child;
const struct of_device_id *of_id;
struct brcm_sata_phy *priv;
struct resource *res;
struct phy_provider *provider;
@ -156,6 +177,12 @@ static int brcm_sata_phy_probe(struct platform_device *pdev)
if (IS_ERR(priv->phy_base))
return PTR_ERR(priv->phy_base);
of_id = of_match_node(brcm_sata_phy_of_match, dn);
if (of_id)
priv->version = (enum brcm_sata_phy_version)of_id->data;
else
priv->version = BRCM_SATA_PHY_28NM;
for_each_available_child_of_node(dn, child) {
unsigned int id;
struct brcm_sata_port *port;
@ -181,7 +208,7 @@ static int brcm_sata_phy_probe(struct platform_device *pdev)
port = &priv->phys[id];
port->portnum = id;
port->phy_priv = priv;
port->phy = devm_phy_create(dev, child, &phy_ops_28nm);
port->phy = devm_phy_create(dev, child, &phy_ops);
port->ssc_en = of_property_read_bool(child, "brcm,enable-ssc");
if (IS_ERR(port->phy)) {
dev_err(dev, "failed to create PHY\n");

View File

@ -0,0 +1,168 @@
/*
* Copyright (c) 2015 Linaro Ltd.
* Copyright (c) 2015 Hisilicon Limited.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/phy/phy.h>
#include <linux/regmap.h>
#define SC_PERIPH_CTRL4 0x00c
#define CTRL4_PICO_SIDDQ BIT(6)
#define CTRL4_PICO_OGDISABLE BIT(8)
#define CTRL4_PICO_VBUSVLDEXT BIT(10)
#define CTRL4_PICO_VBUSVLDEXTSEL BIT(11)
#define CTRL4_OTG_PHY_SEL BIT(21)
#define SC_PERIPH_CTRL5 0x010
#define CTRL5_USBOTG_RES_SEL BIT(3)
#define CTRL5_PICOPHY_ACAENB BIT(4)
#define CTRL5_PICOPHY_BC_MODE BIT(5)
#define CTRL5_PICOPHY_CHRGSEL BIT(6)
#define CTRL5_PICOPHY_VDATSRCEND BIT(7)
#define CTRL5_PICOPHY_VDATDETENB BIT(8)
#define CTRL5_PICOPHY_DCDENB BIT(9)
#define CTRL5_PICOPHY_IDDIG BIT(10)
#define SC_PERIPH_CTRL8 0x018
#define SC_PERIPH_RSTEN0 0x300
#define SC_PERIPH_RSTDIS0 0x304
#define RST0_USBOTG_BUS BIT(4)
#define RST0_POR_PICOPHY BIT(5)
#define RST0_USBOTG BIT(6)
#define RST0_USBOTG_32K BIT(7)
#define EYE_PATTERN_PARA 0x7053348c
struct hi6220_priv {
struct regmap *reg;
struct device *dev;
};
static void hi6220_phy_init(struct hi6220_priv *priv)
{
struct regmap *reg = priv->reg;
u32 val, mask;
val = RST0_USBOTG_BUS | RST0_POR_PICOPHY |
RST0_USBOTG | RST0_USBOTG_32K;
mask = val;
regmap_update_bits(reg, SC_PERIPH_RSTEN0, mask, val);
regmap_update_bits(reg, SC_PERIPH_RSTDIS0, mask, val);
}
static int hi6220_phy_setup(struct hi6220_priv *priv, bool on)
{
struct regmap *reg = priv->reg;
u32 val, mask;
int ret;
if (on) {
val = CTRL5_USBOTG_RES_SEL | CTRL5_PICOPHY_ACAENB;
mask = val | CTRL5_PICOPHY_BC_MODE;
ret = regmap_update_bits(reg, SC_PERIPH_CTRL5, mask, val);
if (ret)
goto out;
val = CTRL4_PICO_VBUSVLDEXT | CTRL4_PICO_VBUSVLDEXTSEL |
CTRL4_OTG_PHY_SEL;
mask = val | CTRL4_PICO_SIDDQ | CTRL4_PICO_OGDISABLE;
ret = regmap_update_bits(reg, SC_PERIPH_CTRL4, mask, val);
if (ret)
goto out;
ret = regmap_write(reg, SC_PERIPH_CTRL8, EYE_PATTERN_PARA);
if (ret)
goto out;
} else {
val = CTRL4_PICO_SIDDQ;
mask = val;
ret = regmap_update_bits(reg, SC_PERIPH_CTRL4, mask, val);
if (ret)
goto out;
}
return 0;
out:
dev_err(priv->dev, "failed to setup phy ret: %d\n", ret);
return ret;
}
static int hi6220_phy_start(struct phy *phy)
{
struct hi6220_priv *priv = phy_get_drvdata(phy);
return hi6220_phy_setup(priv, true);
}
static int hi6220_phy_exit(struct phy *phy)
{
struct hi6220_priv *priv = phy_get_drvdata(phy);
return hi6220_phy_setup(priv, false);
}
static struct phy_ops hi6220_phy_ops = {
.init = hi6220_phy_start,
.exit = hi6220_phy_exit,
.owner = THIS_MODULE,
};
static int hi6220_phy_probe(struct platform_device *pdev)
{
struct phy_provider *phy_provider;
struct device *dev = &pdev->dev;
struct phy *phy;
struct hi6220_priv *priv;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = dev;
priv->reg = syscon_regmap_lookup_by_phandle(dev->of_node,
"hisilicon,peripheral-syscon");
if (IS_ERR(priv->reg)) {
dev_err(dev, "no hisilicon,peripheral-syscon\n");
return PTR_ERR(priv->reg);
}
hi6220_phy_init(priv);
phy = devm_phy_create(dev, NULL, &hi6220_phy_ops);
if (IS_ERR(phy))
return PTR_ERR(phy);
phy_set_drvdata(phy, priv);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
}
static const struct of_device_id hi6220_phy_of_match[] = {
{.compatible = "hisilicon,hi6220-usb-phy",},
{ },
};
MODULE_DEVICE_TABLE(of, hi6220_phy_of_match);
static struct platform_driver hi6220_phy_driver = {
.probe = hi6220_phy_probe,
.driver = {
.name = "hi6220-usb-phy",
.of_match_table = hi6220_phy_of_match,
}
};
module_platform_driver(hi6220_phy_driver);
MODULE_DESCRIPTION("HISILICON HI6220 USB PHY driver");
MODULE_ALIAS("platform:hi6220-usb-phy");
MODULE_LICENSE("GPL");

View File

@ -17,6 +17,7 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/phy/phy.h>
@ -27,6 +28,7 @@
* relative to USB3_SIF2_BASE base address
*/
#define SSUSB_SIFSLV_SPLLC 0x0000
#define SSUSB_SIFSLV_U2FREQ 0x0100
/* offsets of sub-segment in each port registers */
#define SSUSB_SIFSLV_U2PHY_COM_BASE 0x0000
@ -41,6 +43,7 @@
#define PA2_RG_SIF_U2PLL_FORCE_EN BIT(18)
#define U3P_USBPHYACR5 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0014)
#define PA5_RG_U2_HSTX_SRCAL_EN BIT(15)
#define PA5_RG_U2_HSTX_SRCTRL GENMASK(14, 12)
#define PA5_RG_U2_HSTX_SRCTRL_VAL(x) ((0x7 & (x)) << 12)
#define PA5_RG_U2_HS_100U_U3_EN BIT(11)
@ -49,6 +52,8 @@
#define PA6_RG_U2_ISO_EN BIT(31)
#define PA6_RG_U2_BC11_SW_EN BIT(23)
#define PA6_RG_U2_OTG_VBUSCMP_EN BIT(20)
#define PA6_RG_U2_SQTH GENMASK(3, 0)
#define PA6_RG_U2_SQTH_VAL(x) (0xf & (x))
#define U3P_U2PHYACR4 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0020)
#define P2C_RG_USB20_GPIO_CTL BIT(9)
@ -111,6 +116,24 @@
#define XC3_RG_U3_XTAL_RX_PWD BIT(9)
#define XC3_RG_U3_FRC_XTAL_RX_PWD BIT(8)
#define U3P_U2FREQ_FMCR0 (SSUSB_SIFSLV_U2FREQ + 0x00)
#define P2F_RG_MONCLK_SEL GENMASK(27, 26)
#define P2F_RG_MONCLK_SEL_VAL(x) ((0x3 & (x)) << 26)
#define P2F_RG_FREQDET_EN BIT(24)
#define P2F_RG_CYCLECNT GENMASK(23, 0)
#define P2F_RG_CYCLECNT_VAL(x) ((P2F_RG_CYCLECNT) & (x))
#define U3P_U2FREQ_VALUE (SSUSB_SIFSLV_U2FREQ + 0x0c)
#define U3P_U2FREQ_FMMONR1 (SSUSB_SIFSLV_U2FREQ + 0x10)
#define P2F_USB_FM_VALID BIT(0)
#define P2F_RG_FRCK_EN BIT(8)
#define U3P_REF_CLK 26 /* MHZ */
#define U3P_SLEW_RATE_COEF 28
#define U3P_SR_COEF_DIVISOR 1000
#define U3P_FM_DET_CYCLE_CNT 1024
struct mt65xx_phy_instance {
struct phy *phy;
void __iomem *port_base;
@ -126,6 +149,77 @@ struct mt65xx_u3phy {
int nphys;
};
static void hs_slew_rate_calibrate(struct mt65xx_u3phy *u3phy,
struct mt65xx_phy_instance *instance)
{
void __iomem *sif_base = u3phy->sif_base;
int calibration_val;
int fm_out;
u32 tmp;
/* enable USB ring oscillator */
tmp = readl(instance->port_base + U3P_USBPHYACR5);
tmp |= PA5_RG_U2_HSTX_SRCAL_EN;
writel(tmp, instance->port_base + U3P_USBPHYACR5);
udelay(1);
/*enable free run clock */
tmp = readl(sif_base + U3P_U2FREQ_FMMONR1);
tmp |= P2F_RG_FRCK_EN;
writel(tmp, sif_base + U3P_U2FREQ_FMMONR1);
/* set cycle count as 1024, and select u2 channel */
tmp = readl(sif_base + U3P_U2FREQ_FMCR0);
tmp &= ~(P2F_RG_CYCLECNT | P2F_RG_MONCLK_SEL);
tmp |= P2F_RG_CYCLECNT_VAL(U3P_FM_DET_CYCLE_CNT);
tmp |= P2F_RG_MONCLK_SEL_VAL(instance->index);
writel(tmp, sif_base + U3P_U2FREQ_FMCR0);
/* enable frequency meter */
tmp = readl(sif_base + U3P_U2FREQ_FMCR0);
tmp |= P2F_RG_FREQDET_EN;
writel(tmp, sif_base + U3P_U2FREQ_FMCR0);
/* ignore return value */
readl_poll_timeout(sif_base + U3P_U2FREQ_FMMONR1, tmp,
(tmp & P2F_USB_FM_VALID), 10, 200);
fm_out = readl(sif_base + U3P_U2FREQ_VALUE);
/* disable frequency meter */
tmp = readl(sif_base + U3P_U2FREQ_FMCR0);
tmp &= ~P2F_RG_FREQDET_EN;
writel(tmp, sif_base + U3P_U2FREQ_FMCR0);
/*disable free run clock */
tmp = readl(sif_base + U3P_U2FREQ_FMMONR1);
tmp &= ~P2F_RG_FRCK_EN;
writel(tmp, sif_base + U3P_U2FREQ_FMMONR1);
if (fm_out) {
/* ( 1024 / FM_OUT ) x reference clock frequency x 0.028 */
tmp = U3P_FM_DET_CYCLE_CNT * U3P_REF_CLK * U3P_SLEW_RATE_COEF;
tmp /= fm_out;
calibration_val = DIV_ROUND_CLOSEST(tmp, U3P_SR_COEF_DIVISOR);
} else {
/* if FM detection fail, set default value */
calibration_val = 4;
}
dev_dbg(u3phy->dev, "phy:%d, fm_out:%d, calib:%d\n",
instance->index, fm_out, calibration_val);
/* set HS slew rate */
tmp = readl(instance->port_base + U3P_USBPHYACR5);
tmp &= ~PA5_RG_U2_HSTX_SRCTRL;
tmp |= PA5_RG_U2_HSTX_SRCTRL_VAL(calibration_val);
writel(tmp, instance->port_base + U3P_USBPHYACR5);
/* disable USB ring oscillator */
tmp = readl(instance->port_base + U3P_USBPHYACR5);
tmp &= ~PA5_RG_U2_HSTX_SRCAL_EN;
writel(tmp, instance->port_base + U3P_USBPHYACR5);
}
static void phy_instance_init(struct mt65xx_u3phy *u3phy,
struct mt65xx_phy_instance *instance)
{
@ -165,9 +259,10 @@ static void phy_instance_init(struct mt65xx_u3phy *u3phy,
writel(tmp, port_base + U3P_U2PHYDTM0);
}
/* DP/DM BC1.1 path Disable */
tmp = readl(port_base + U3P_USBPHYACR6);
tmp &= ~PA6_RG_U2_BC11_SW_EN;
tmp &= ~PA6_RG_U2_BC11_SW_EN; /* DP/DM BC1.1 path Disable */
tmp &= ~PA6_RG_U2_SQTH;
tmp |= PA6_RG_U2_SQTH_VAL(2);
writel(tmp, port_base + U3P_USBPHYACR6);
tmp = readl(port_base + U3P_U3PHYA_DA_REG0);
@ -223,9 +318,9 @@ static void phy_instance_power_on(struct mt65xx_u3phy *u3phy,
tmp |= XC3_RG_U3_XTAL_RX_PWD | XC3_RG_U3_FRC_XTAL_RX_PWD;
writel(tmp, u3phy->sif_base + U3P_XTALCTL3);
/* [mt8173]disable Change 100uA current from SSUSB */
/* [mt8173]switch 100uA current to SSUSB */
tmp = readl(port_base + U3P_USBPHYACR5);
tmp &= ~PA5_RG_U2_HS_100U_U3_EN;
tmp |= PA5_RG_U2_HS_100U_U3_EN;
writel(tmp, port_base + U3P_USBPHYACR5);
}
@ -270,7 +365,7 @@ static void phy_instance_power_off(struct mt65xx_u3phy *u3phy,
writel(tmp, port_base + U3P_USBPHYACR6);
if (!index) {
/* (also disable)Change 100uA current switch to USB2.0 */
/* switch 100uA current back to USB2.0 */
tmp = readl(port_base + U3P_USBPHYACR5);
tmp &= ~PA5_RG_U2_HS_100U_U3_EN;
writel(tmp, port_base + U3P_USBPHYACR5);
@ -340,6 +435,7 @@ static int mt65xx_phy_power_on(struct phy *phy)
struct mt65xx_u3phy *u3phy = dev_get_drvdata(phy->dev.parent);
phy_instance_power_on(u3phy, instance);
hs_slew_rate_calibrate(u3phy, instance);
return 0;
}

View File

@ -29,6 +29,8 @@
#include <linux/delay.h>
#include <linux/phy/omap_control_phy.h>
#include <linux/phy/phy.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/of_platform.h>
#define USB2PHY_DISCON_BYP_LATCH (1 << 31)
@ -97,22 +99,38 @@ static int omap_usb_set_peripheral(struct usb_otg *otg,
return 0;
}
static int omap_usb_phy_power(struct omap_usb *phy, int on)
{
u32 val;
int ret;
if (!phy->syscon_phy_power) {
omap_control_phy_power(phy->control_dev, on);
return 0;
}
if (on)
val = phy->power_on;
else
val = phy->power_off;
ret = regmap_update_bits(phy->syscon_phy_power, phy->power_reg,
phy->mask, val);
return ret;
}
static int omap_usb_power_off(struct phy *x)
{
struct omap_usb *phy = phy_get_drvdata(x);
omap_control_phy_power(phy->control_dev, 0);
return 0;
return omap_usb_phy_power(phy, false);
}
static int omap_usb_power_on(struct phy *x)
{
struct omap_usb *phy = phy_get_drvdata(x);
omap_control_phy_power(phy->control_dev, 1);
return 0;
return omap_usb_phy_power(phy, true);
}
static int omap_usb_init(struct phy *x)
@ -147,21 +165,38 @@ static const struct phy_ops ops = {
static const struct usb_phy_data omap_usb2_data = {
.label = "omap_usb2",
.flags = OMAP_USB2_HAS_START_SRP | OMAP_USB2_HAS_SET_VBUS,
.mask = OMAP_DEV_PHY_PD,
.power_off = OMAP_DEV_PHY_PD,
};
static const struct usb_phy_data omap5_usb2_data = {
.label = "omap5_usb2",
.flags = 0,
.mask = OMAP_DEV_PHY_PD,
.power_off = OMAP_DEV_PHY_PD,
};
static const struct usb_phy_data dra7x_usb2_data = {
.label = "dra7x_usb2",
.flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT,
.mask = OMAP_DEV_PHY_PD,
.power_off = OMAP_DEV_PHY_PD,
};
static const struct usb_phy_data dra7x_usb2_phy2_data = {
.label = "dra7x_usb2_phy2",
.flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT,
.mask = OMAP_USB2_PHY_PD,
.power_off = OMAP_USB2_PHY_PD,
};
static const struct usb_phy_data am437x_usb2_data = {
.label = "am437x_usb2",
.flags = 0,
.mask = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD |
AM437X_USB2_OTGVDET_EN | AM437X_USB2_OTGSESSEND_EN,
.power_on = AM437X_USB2_OTGVDET_EN | AM437X_USB2_OTGSESSEND_EN,
.power_off = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD,
};
static const struct of_device_id omap_usb2_id_table[] = {
@ -177,6 +212,10 @@ static const struct of_device_id omap_usb2_id_table[] = {
.compatible = "ti,dra7x-usb2",
.data = &dra7x_usb2_data,
},
{
.compatible = "ti,dra7x-usb2-phy2",
.data = &dra7x_usb2_phy2_data,
},
{
.compatible = "ti,am437x-usb2",
.data = &am437x_usb2_data,
@ -219,6 +258,9 @@ static int omap_usb2_probe(struct platform_device *pdev)
phy->phy.label = phy_data->label;
phy->phy.otg = otg;
phy->phy.type = USB_PHY_TYPE_USB2;
phy->mask = phy_data->mask;
phy->power_on = phy_data->power_on;
phy->power_off = phy_data->power_off;
if (phy_data->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) {
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@ -228,20 +270,35 @@ static int omap_usb2_probe(struct platform_device *pdev)
phy->flags |= OMAP_USB2_CALIBRATE_FALSE_DISCONNECT;
}
control_node = of_parse_phandle(node, "ctrl-module", 0);
if (!control_node) {
dev_err(&pdev->dev, "Failed to get control device phandle\n");
return -EINVAL;
}
phy->syscon_phy_power = syscon_regmap_lookup_by_phandle(node,
"syscon-phy-power");
if (IS_ERR(phy->syscon_phy_power)) {
dev_dbg(&pdev->dev,
"can't get syscon-phy-power, using control device\n");
phy->syscon_phy_power = NULL;
control_pdev = of_find_device_by_node(control_node);
if (!control_pdev) {
dev_err(&pdev->dev, "Failed to get control device\n");
return -EINVAL;
}
control_node = of_parse_phandle(node, "ctrl-module", 0);
if (!control_node) {
dev_err(&pdev->dev,
"Failed to get control device phandle\n");
return -EINVAL;
}
phy->control_dev = &control_pdev->dev;
omap_control_phy_power(phy->control_dev, 0);
control_pdev = of_find_device_by_node(control_node);
if (!control_pdev) {
dev_err(&pdev->dev, "Failed to get control device\n");
return -EINVAL;
}
phy->control_dev = &control_pdev->dev;
} else {
if (of_property_read_u32_index(node,
"syscon-phy-power", 1,
&phy->power_reg)) {
dev_err(&pdev->dev,
"couldn't get power reg. offset\n");
return -EINVAL;
}
}
otg->set_host = omap_usb_set_host;
otg->set_peripheral = omap_usb_set_peripheral;
@ -261,6 +318,7 @@ static int omap_usb2_probe(struct platform_device *pdev)
}
phy_set_drvdata(generic_phy, phy);
omap_usb_power_off(generic_phy);
phy_provider = devm_of_phy_provider_register(phy->dev,
of_phy_simple_xlate);

View File

@ -0,0 +1,378 @@
/*
* Renesas R-Car Gen3 for USB2.0 PHY driver
*
* Copyright (C) 2015 Renesas Electronics Corporation
*
* This is based on the phy-rcar-gen2 driver:
* Copyright (C) 2014 Renesas Solutions Corp.
* Copyright (C) 2014 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/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
/******* USB2.0 Host registers (original offset is +0x200) *******/
#define USB2_INT_ENABLE 0x000
#define USB2_USBCTR 0x00c
#define USB2_SPD_RSM_TIMSET 0x10c
#define USB2_OC_TIMSET 0x110
#define USB2_COMMCTRL 0x600
#define USB2_OBINTSTA 0x604
#define USB2_OBINTEN 0x608
#define USB2_VBCTRL 0x60c
#define USB2_LINECTRL1 0x610
#define USB2_ADPCTRL 0x630
/* INT_ENABLE */
#define USB2_INT_ENABLE_UCOM_INTEN BIT(3)
#define USB2_INT_ENABLE_USBH_INTB_EN BIT(2)
#define USB2_INT_ENABLE_USBH_INTA_EN BIT(1)
#define USB2_INT_ENABLE_INIT (USB2_INT_ENABLE_UCOM_INTEN | \
USB2_INT_ENABLE_USBH_INTB_EN | \
USB2_INT_ENABLE_USBH_INTA_EN)
/* USBCTR */
#define USB2_USBCTR_DIRPD BIT(2)
#define USB2_USBCTR_PLL_RST BIT(1)
/* SPD_RSM_TIMSET */
#define USB2_SPD_RSM_TIMSET_INIT 0x014e029b
/* OC_TIMSET */
#define USB2_OC_TIMSET_INIT 0x000209ab
/* COMMCTRL */
#define USB2_COMMCTRL_OTG_PERI BIT(31) /* 1 = Peripheral mode */
/* OBINTSTA and OBINTEN */
#define USB2_OBINT_SESSVLDCHG BIT(12)
#define USB2_OBINT_IDDIGCHG BIT(11)
#define USB2_OBINT_BITS (USB2_OBINT_SESSVLDCHG | \
USB2_OBINT_IDDIGCHG)
/* VBCTRL */
#define USB2_VBCTRL_DRVVBUSSEL BIT(8)
/* LINECTRL1 */
#define USB2_LINECTRL1_DPRPD_EN BIT(19)
#define USB2_LINECTRL1_DP_RPD BIT(18)
#define USB2_LINECTRL1_DMRPD_EN BIT(17)
#define USB2_LINECTRL1_DM_RPD BIT(16)
/* ADPCTRL */
#define USB2_ADPCTRL_OTGSESSVLD BIT(20)
#define USB2_ADPCTRL_IDDIG BIT(19)
#define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */
#define USB2_ADPCTRL_DRVVBUS BIT(4)
/******* HSUSB registers (original offset is +0x100) *******/
#define HSUSB_LPSTS 0x02
#define HSUSB_UGCTRL2 0x84
/* Low Power Status register (LPSTS) */
#define HSUSB_LPSTS_SUSPM 0x4000
/* USB General control register 2 (UGCTRL2) */
#define HSUSB_UGCTRL2_MASK 0x00000031 /* bit[31:6] should be 0 */
#define HSUSB_UGCTRL2_USB0SEL 0x00000030
#define HSUSB_UGCTRL2_USB0SEL_HOST 0x00000010
#define HSUSB_UGCTRL2_USB0SEL_HS_USB 0x00000020
#define HSUSB_UGCTRL2_USB0SEL_OTG 0x00000030
struct rcar_gen3_data {
void __iomem *base;
struct clk *clk;
};
struct rcar_gen3_chan {
struct rcar_gen3_data usb2;
struct rcar_gen3_data hsusb;
struct phy *phy;
};
static void rcar_gen3_set_host_mode(struct rcar_gen3_chan *ch, int host)
{
void __iomem *usb2_base = ch->usb2.base;
u32 val = readl(usb2_base + USB2_COMMCTRL);
dev_vdbg(&ch->phy->dev, "%s: %08x, %d\n", __func__, val, host);
if (host)
val &= ~USB2_COMMCTRL_OTG_PERI;
else
val |= USB2_COMMCTRL_OTG_PERI;
writel(val, usb2_base + USB2_COMMCTRL);
}
static void rcar_gen3_set_linectrl(struct rcar_gen3_chan *ch, int dp, int dm)
{
void __iomem *usb2_base = ch->usb2.base;
u32 val = readl(usb2_base + USB2_LINECTRL1);
dev_vdbg(&ch->phy->dev, "%s: %08x, %d, %d\n", __func__, val, dp, dm);
val &= ~(USB2_LINECTRL1_DP_RPD | USB2_LINECTRL1_DM_RPD);
if (dp)
val |= USB2_LINECTRL1_DP_RPD;
if (dm)
val |= USB2_LINECTRL1_DM_RPD;
writel(val, usb2_base + USB2_LINECTRL1);
}
static void rcar_gen3_enable_vbus_ctrl(struct rcar_gen3_chan *ch, int vbus)
{
void __iomem *usb2_base = ch->usb2.base;
u32 val = readl(usb2_base + USB2_ADPCTRL);
dev_vdbg(&ch->phy->dev, "%s: %08x, %d\n", __func__, val, vbus);
if (vbus)
val |= USB2_ADPCTRL_DRVVBUS;
else
val &= ~USB2_ADPCTRL_DRVVBUS;
writel(val, usb2_base + USB2_ADPCTRL);
}
static void rcar_gen3_init_for_host(struct rcar_gen3_chan *ch)
{
rcar_gen3_set_linectrl(ch, 1, 1);
rcar_gen3_set_host_mode(ch, 1);
rcar_gen3_enable_vbus_ctrl(ch, 1);
}
static void rcar_gen3_init_for_peri(struct rcar_gen3_chan *ch)
{
rcar_gen3_set_linectrl(ch, 0, 1);
rcar_gen3_set_host_mode(ch, 0);
rcar_gen3_enable_vbus_ctrl(ch, 0);
}
static bool rcar_gen3_check_vbus(struct rcar_gen3_chan *ch)
{
return !!(readl(ch->usb2.base + USB2_ADPCTRL) &
USB2_ADPCTRL_OTGSESSVLD);
}
static bool rcar_gen3_check_id(struct rcar_gen3_chan *ch)
{
return !!(readl(ch->usb2.base + USB2_ADPCTRL) & USB2_ADPCTRL_IDDIG);
}
static void rcar_gen3_device_recognition(struct rcar_gen3_chan *ch)
{
bool is_host = true;
/* B-device? */
if (rcar_gen3_check_id(ch) && rcar_gen3_check_vbus(ch))
is_host = false;
if (is_host)
rcar_gen3_init_for_host(ch);
else
rcar_gen3_init_for_peri(ch);
}
static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch)
{
void __iomem *usb2_base = ch->usb2.base;
u32 val;
val = readl(usb2_base + USB2_VBCTRL);
writel(val | USB2_VBCTRL_DRVVBUSSEL, usb2_base + USB2_VBCTRL);
writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTSTA);
val = readl(usb2_base + USB2_OBINTEN);
writel(val | USB2_OBINT_BITS, usb2_base + USB2_OBINTEN);
val = readl(usb2_base + USB2_ADPCTRL);
writel(val | USB2_ADPCTRL_IDPULLUP, usb2_base + USB2_ADPCTRL);
val = readl(usb2_base + USB2_LINECTRL1);
rcar_gen3_set_linectrl(ch, 0, 0);
writel(val | USB2_LINECTRL1_DPRPD_EN | USB2_LINECTRL1_DMRPD_EN,
usb2_base + USB2_LINECTRL1);
rcar_gen3_device_recognition(ch);
}
static int rcar_gen3_phy_usb2_init(struct phy *p)
{
struct rcar_gen3_chan *channel = phy_get_drvdata(p);
void __iomem *usb2_base = channel->usb2.base;
void __iomem *hsusb_base = channel->hsusb.base;
u32 val;
/* Initialize USB2 part */
writel(USB2_INT_ENABLE_INIT, usb2_base + USB2_INT_ENABLE);
writel(USB2_SPD_RSM_TIMSET_INIT, usb2_base + USB2_SPD_RSM_TIMSET);
writel(USB2_OC_TIMSET_INIT, usb2_base + USB2_OC_TIMSET);
/* Initialize HSUSB part */
if (hsusb_base) {
val = readl(hsusb_base + HSUSB_UGCTRL2);
val = (val & ~HSUSB_UGCTRL2_USB0SEL) |
HSUSB_UGCTRL2_USB0SEL_OTG;
writel(val & HSUSB_UGCTRL2_MASK, hsusb_base + HSUSB_UGCTRL2);
/* Initialize otg part */
rcar_gen3_init_otg(channel);
}
return 0;
}
static int rcar_gen3_phy_usb2_exit(struct phy *p)
{
struct rcar_gen3_chan *channel = phy_get_drvdata(p);
writel(0, channel->usb2.base + USB2_INT_ENABLE);
return 0;
}
static int rcar_gen3_phy_usb2_power_on(struct phy *p)
{
struct rcar_gen3_chan *channel = phy_get_drvdata(p);
void __iomem *usb2_base = channel->usb2.base;
void __iomem *hsusb_base = channel->hsusb.base;
u32 val;
val = readl(usb2_base + USB2_USBCTR);
val |= USB2_USBCTR_PLL_RST;
writel(val, usb2_base + USB2_USBCTR);
val &= ~USB2_USBCTR_PLL_RST;
writel(val, usb2_base + USB2_USBCTR);
/*
* TODO: To reduce power consuming, this driver should set the SUSPM
* after the PHY detects ID pin as peripheral.
*/
if (hsusb_base) {
/* Power on HSUSB PHY */
val = readw(hsusb_base + HSUSB_LPSTS);
val |= HSUSB_LPSTS_SUSPM;
writew(val, hsusb_base + HSUSB_LPSTS);
}
return 0;
}
static int rcar_gen3_phy_usb2_power_off(struct phy *p)
{
struct rcar_gen3_chan *channel = phy_get_drvdata(p);
void __iomem *hsusb_base = channel->hsusb.base;
u32 val;
if (hsusb_base) {
/* Power off HSUSB PHY */
val = readw(hsusb_base + HSUSB_LPSTS);
val &= ~HSUSB_LPSTS_SUSPM;
writew(val, hsusb_base + HSUSB_LPSTS);
}
return 0;
}
static struct phy_ops rcar_gen3_phy_usb2_ops = {
.init = rcar_gen3_phy_usb2_init,
.exit = rcar_gen3_phy_usb2_exit,
.power_on = rcar_gen3_phy_usb2_power_on,
.power_off = rcar_gen3_phy_usb2_power_off,
.owner = THIS_MODULE,
};
static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch)
{
struct rcar_gen3_chan *ch = _ch;
void __iomem *usb2_base = ch->usb2.base;
u32 status = readl(usb2_base + USB2_OBINTSTA);
irqreturn_t ret = IRQ_NONE;
if (status & USB2_OBINT_BITS) {
dev_vdbg(&ch->phy->dev, "%s: %08x\n", __func__, status);
writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTSTA);
rcar_gen3_device_recognition(ch);
ret = IRQ_HANDLED;
}
return ret;
}
static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = {
{ .compatible = "renesas,usb2-phy-r8a7795" },
{ }
};
MODULE_DEVICE_TABLE(of, rcar_gen3_phy_usb2_match_table);
static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct rcar_gen3_chan *channel;
struct phy_provider *provider;
struct resource *res;
if (!dev->of_node) {
dev_err(dev, "This driver needs device tree\n");
return -EINVAL;
}
channel = devm_kzalloc(dev, sizeof(*channel), GFP_KERNEL);
if (!channel)
return -ENOMEM;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "usb2_host");
channel->usb2.base = devm_ioremap_resource(dev, res);
if (IS_ERR(channel->usb2.base))
return PTR_ERR(channel->usb2.base);
/* "hsusb" memory resource is optional */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hsusb");
/* To avoid error message by devm_ioremap_resource() */
if (res) {
int irq;
channel->hsusb.base = devm_ioremap_resource(dev, res);
if (IS_ERR(channel->hsusb.base))
channel->hsusb.base = NULL;
/* call request_irq for OTG */
irq = platform_get_irq(pdev, 0);
if (irq >= 0)
irq = devm_request_irq(dev, irq, rcar_gen3_phy_usb2_irq,
IRQF_SHARED, dev_name(dev),
channel);
if (irq < 0)
dev_err(dev, "No irq handler (%d)\n", irq);
}
/* devm_phy_create() will call pm_runtime_enable(dev); */
channel->phy = devm_phy_create(dev, NULL, &rcar_gen3_phy_usb2_ops);
if (IS_ERR(channel->phy)) {
dev_err(dev, "Failed to create USB2 PHY\n");
return PTR_ERR(channel->phy);
}
phy_set_drvdata(channel->phy, channel);
provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
if (IS_ERR(provider))
dev_err(dev, "Failed to register PHY provider\n");
return PTR_ERR_OR_ZERO(provider);
}
static struct platform_driver rcar_gen3_phy_usb2_driver = {
.driver = {
.name = "phy_rcar_gen3_usb2",
.of_match_table = rcar_gen3_phy_usb2_match_table,
},
.probe = rcar_gen3_phy_usb2_probe,
};
module_platform_driver(rcar_gen3_phy_usb2_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Renesas R-Car Gen3 USB 2.0 PHY");
MODULE_AUTHOR("Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>");

View File

@ -15,12 +15,14 @@
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
@ -36,31 +38,91 @@
#define SIDDQ_ON BIT(13)
#define SIDDQ_OFF (0 << 13)
struct rockchip_usb_phys {
int reg;
const char *pll_name;
};
struct rockchip_usb_phy_pdata {
struct rockchip_usb_phys *phys;
};
struct rockchip_usb_phy_base {
struct device *dev;
struct regmap *reg_base;
const struct rockchip_usb_phy_pdata *pdata;
};
struct rockchip_usb_phy {
struct rockchip_usb_phy_base *base;
struct device_node *np;
unsigned int reg_offset;
struct regmap *reg_base;
struct clk *clk;
struct clk *clk480m;
struct clk_hw clk480m_hw;
struct phy *phy;
};
static int rockchip_usb_phy_power(struct rockchip_usb_phy *phy,
bool siddq)
{
return regmap_write(phy->reg_base, phy->reg_offset,
return regmap_write(phy->base->reg_base, phy->reg_offset,
SIDDQ_WRITE_ENA | (siddq ? SIDDQ_ON : SIDDQ_OFF));
}
static unsigned long rockchip_usb_phy480m_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
return 480000000;
}
static void rockchip_usb_phy480m_disable(struct clk_hw *hw)
{
struct rockchip_usb_phy *phy = container_of(hw,
struct rockchip_usb_phy,
clk480m_hw);
/* Power down usb phy analog blocks by set siddq 1 */
rockchip_usb_phy_power(phy, 1);
}
static int rockchip_usb_phy480m_enable(struct clk_hw *hw)
{
struct rockchip_usb_phy *phy = container_of(hw,
struct rockchip_usb_phy,
clk480m_hw);
/* Power up usb phy analog blocks by set siddq 0 */
return rockchip_usb_phy_power(phy, 0);
}
static int rockchip_usb_phy480m_is_enabled(struct clk_hw *hw)
{
struct rockchip_usb_phy *phy = container_of(hw,
struct rockchip_usb_phy,
clk480m_hw);
int ret;
u32 val;
ret = regmap_read(phy->base->reg_base, phy->reg_offset, &val);
if (ret < 0)
return ret;
return (val & SIDDQ_ON) ? 0 : 1;
}
static const struct clk_ops rockchip_usb_phy480m_ops = {
.enable = rockchip_usb_phy480m_enable,
.disable = rockchip_usb_phy480m_disable,
.is_enabled = rockchip_usb_phy480m_is_enabled,
.recalc_rate = rockchip_usb_phy480m_recalc_rate,
};
static int rockchip_usb_phy_power_off(struct phy *_phy)
{
struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
int ret = 0;
/* Power down usb phy analog blocks by set siddq 1 */
ret = rockchip_usb_phy_power(phy, 1);
if (ret)
return ret;
clk_disable_unprepare(phy->clk);
clk_disable_unprepare(phy->clk480m);
return 0;
}
@ -68,20 +130,8 @@ static int rockchip_usb_phy_power_off(struct phy *_phy)
static int rockchip_usb_phy_power_on(struct phy *_phy)
{
struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
int ret = 0;
ret = clk_prepare_enable(phy->clk);
if (ret)
return ret;
/* Power up usb phy analog blocks by set siddq 0 */
ret = rockchip_usb_phy_power(phy, 0);
if (ret) {
clk_disable_unprepare(phy->clk);
return ret;
}
return 0;
return clk_prepare_enable(phy->clk480m);
}
static const struct phy_ops ops = {
@ -90,66 +140,179 @@ static const struct phy_ops ops = {
.owner = THIS_MODULE,
};
static void rockchip_usb_phy_action(void *data)
{
struct rockchip_usb_phy *rk_phy = data;
of_clk_del_provider(rk_phy->np);
clk_unregister(rk_phy->clk480m);
if (rk_phy->clk)
clk_put(rk_phy->clk);
}
static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base,
struct device_node *child)
{
struct rockchip_usb_phy *rk_phy;
unsigned int reg_offset;
const char *clk_name;
struct clk_init_data init;
int err, i;
rk_phy = devm_kzalloc(base->dev, sizeof(*rk_phy), GFP_KERNEL);
if (!rk_phy)
return -ENOMEM;
rk_phy->base = base;
rk_phy->np = child;
if (of_property_read_u32(child, "reg", &reg_offset)) {
dev_err(base->dev, "missing reg property in node %s\n",
child->name);
return -EINVAL;
}
rk_phy->reg_offset = reg_offset;
rk_phy->clk = of_clk_get_by_name(child, "phyclk");
if (IS_ERR(rk_phy->clk))
rk_phy->clk = NULL;
i = 0;
init.name = NULL;
while (base->pdata->phys[i].reg) {
if (base->pdata->phys[i].reg == reg_offset) {
init.name = base->pdata->phys[i].pll_name;
break;
}
i++;
}
if (!init.name) {
dev_err(base->dev, "phy data not found\n");
return -EINVAL;
}
if (rk_phy->clk) {
clk_name = __clk_get_name(rk_phy->clk);
init.flags = 0;
init.parent_names = &clk_name;
init.num_parents = 1;
} else {
init.flags = CLK_IS_ROOT;
init.parent_names = NULL;
init.num_parents = 0;
}
init.ops = &rockchip_usb_phy480m_ops;
rk_phy->clk480m_hw.init = &init;
rk_phy->clk480m = clk_register(base->dev, &rk_phy->clk480m_hw);
if (IS_ERR(rk_phy->clk480m)) {
err = PTR_ERR(rk_phy->clk480m);
goto err_clk;
}
err = of_clk_add_provider(child, of_clk_src_simple_get,
rk_phy->clk480m);
if (err < 0)
goto err_clk_prov;
err = devm_add_action(base->dev, rockchip_usb_phy_action, rk_phy);
if (err)
goto err_devm_action;
rk_phy->phy = devm_phy_create(base->dev, child, &ops);
if (IS_ERR(rk_phy->phy)) {
dev_err(base->dev, "failed to create PHY\n");
return PTR_ERR(rk_phy->phy);
}
phy_set_drvdata(rk_phy->phy, rk_phy);
/* only power up usb phy when it use, so disable it when init*/
return rockchip_usb_phy_power(rk_phy, 1);
err_devm_action:
of_clk_del_provider(child);
err_clk_prov:
clk_unregister(rk_phy->clk480m);
err_clk:
if (rk_phy->clk)
clk_put(rk_phy->clk);
return err;
}
static const struct rockchip_usb_phy_pdata rk3066a_pdata = {
.phys = (struct rockchip_usb_phys[]){
{ .reg = 0x17c, .pll_name = "sclk_otgphy0_480m" },
{ .reg = 0x188, .pll_name = "sclk_otgphy1_480m" },
{ /* sentinel */ }
},
};
static const struct rockchip_usb_phy_pdata rk3188_pdata = {
.phys = (struct rockchip_usb_phys[]){
{ .reg = 0x10c, .pll_name = "sclk_otgphy0_480m" },
{ .reg = 0x11c, .pll_name = "sclk_otgphy1_480m" },
{ /* sentinel */ }
},
};
static const struct rockchip_usb_phy_pdata rk3288_pdata = {
.phys = (struct rockchip_usb_phys[]){
{ .reg = 0x320, .pll_name = "sclk_otgphy0_480m" },
{ .reg = 0x334, .pll_name = "sclk_otgphy1_480m" },
{ .reg = 0x348, .pll_name = "sclk_otgphy2_480m" },
{ /* sentinel */ }
},
};
static int rockchip_usb_phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct rockchip_usb_phy *rk_phy;
struct rockchip_usb_phy_base *phy_base;
struct phy_provider *phy_provider;
const struct of_device_id *match;
struct device_node *child;
struct regmap *grf;
unsigned int reg_offset;
int err;
grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
if (IS_ERR(grf)) {
phy_base = devm_kzalloc(dev, sizeof(*phy_base), GFP_KERNEL);
if (!phy_base)
return -ENOMEM;
match = of_match_device(dev->driver->of_match_table, dev);
if (!match || !match->data) {
dev_err(dev, "missing phy data\n");
return -EINVAL;
}
phy_base->pdata = match->data;
phy_base->dev = dev;
phy_base->reg_base = syscon_regmap_lookup_by_phandle(dev->of_node,
"rockchip,grf");
if (IS_ERR(phy_base->reg_base)) {
dev_err(&pdev->dev, "Missing rockchip,grf property\n");
return PTR_ERR(grf);
return PTR_ERR(phy_base->reg_base);
}
for_each_available_child_of_node(dev->of_node, child) {
rk_phy = devm_kzalloc(dev, sizeof(*rk_phy), GFP_KERNEL);
if (!rk_phy) {
err = -ENOMEM;
goto put_child;
err = rockchip_usb_phy_init(phy_base, child);
if (err) {
of_node_put(child);
return err;
}
if (of_property_read_u32(child, "reg", &reg_offset)) {
dev_err(dev, "missing reg property in node %s\n",
child->name);
err = -EINVAL;
goto put_child;
}
rk_phy->reg_offset = reg_offset;
rk_phy->reg_base = grf;
rk_phy->clk = of_clk_get_by_name(child, "phyclk");
if (IS_ERR(rk_phy->clk))
rk_phy->clk = NULL;
rk_phy->phy = devm_phy_create(dev, child, &ops);
if (IS_ERR(rk_phy->phy)) {
dev_err(dev, "failed to create PHY\n");
err = PTR_ERR(rk_phy->phy);
goto put_child;
}
phy_set_drvdata(rk_phy->phy, rk_phy);
/* only power up usb phy when it use, so disable it when init*/
err = rockchip_usb_phy_power(rk_phy, 1);
if (err)
goto put_child;
}
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
put_child:
of_node_put(child);
return err;
}
static const struct of_device_id rockchip_usb_phy_dt_ids[] = {
{ .compatible = "rockchip,rk3288-usb-phy" },
{ .compatible = "rockchip,rk3066a-usb-phy", .data = &rk3066a_pdata },
{ .compatible = "rockchip,rk3188-usb-phy", .data = &rk3188_pdata },
{ .compatible = "rockchip,rk3288-usb-phy", .data = &rk3288_pdata },
{}
};

View File

@ -32,6 +32,7 @@
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/phy/phy.h>
#include <linux/phy/phy-sun4i-usb.h>
@ -46,6 +47,9 @@
#define REG_PHYBIST 0x08
#define REG_PHYTUNE 0x0c
#define REG_PHYCTL_A33 0x10
#define REG_PHY_UNK_H3 0x20
#define REG_PMU_UNK_H3 0x10
#define PHYCTL_DATA BIT(7)
@ -79,7 +83,7 @@
#define PHY_DISCON_TH_SEL 0x2a
#define PHY_SQUELCH_DETECT 0x3c
#define MAX_PHYS 3
#define MAX_PHYS 4
/*
* Note do not raise the debounce time, we must report Vusb high within 100ms
@ -88,12 +92,24 @@
#define DEBOUNCE_TIME msecs_to_jiffies(50)
#define POLL_TIME msecs_to_jiffies(250)
enum sun4i_usb_phy_type {
sun4i_a10_phy,
sun8i_a33_phy,
sun8i_h3_phy,
};
struct sun4i_usb_phy_cfg {
int num_phys;
enum sun4i_usb_phy_type type;
u32 disc_thresh;
u8 phyctl_offset;
bool dedicated_clocks;
};
struct sun4i_usb_phy_data {
void __iomem *base;
const struct sun4i_usb_phy_cfg *cfg;
struct mutex mutex;
int num_phys;
u32 disc_thresh;
bool has_a33_phyctl;
struct sun4i_usb_phy {
struct phy *phy;
void __iomem *pmu;
@ -159,17 +175,14 @@ static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
{
struct sun4i_usb_phy_data *phy_data = to_sun4i_usb_phy_data(phy);
u32 temp, usbc_bit = BIT(phy->index * 2);
void *phyctl;
void *phyctl = phy_data->base + phy_data->cfg->phyctl_offset;
int i;
mutex_lock(&phy_data->mutex);
if (phy_data->has_a33_phyctl) {
phyctl = phy_data->base + REG_PHYCTL_A33;
if (phy_data->cfg->type == sun8i_a33_phy) {
/* A33 needs us to set phyctl to 0 explicitly */
writel(0, phyctl);
} else {
phyctl = phy_data->base + REG_PHYCTL_A10;
}
for (i = 0; i < len; i++) {
@ -230,6 +243,7 @@ static int sun4i_usb_phy_init(struct phy *_phy)
struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
int ret;
u32 val;
ret = clk_prepare_enable(phy->clk);
if (ret)
@ -241,15 +255,26 @@ static int sun4i_usb_phy_init(struct phy *_phy)
return ret;
}
/* Enable USB 45 Ohm resistor calibration */
if (phy->index == 0)
sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, 0x01, 1);
if (data->cfg->type == sun8i_h3_phy) {
if (phy->index == 0) {
val = readl(data->base + REG_PHY_UNK_H3);
writel(val & ~1, data->base + REG_PHY_UNK_H3);
}
/* Adjust PHY's magnitude and rate */
sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5);
val = readl(phy->pmu + REG_PMU_UNK_H3);
writel(val & ~2, phy->pmu + REG_PMU_UNK_H3);
} else {
/* Enable USB 45 Ohm resistor calibration */
if (phy->index == 0)
sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, 0x01, 1);
/* Disconnect threshold adjustment */
sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL, data->disc_thresh, 2);
/* Adjust PHY's magnitude and rate */
sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5);
/* Disconnect threshold adjustment */
sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL,
data->cfg->disc_thresh, 2);
}
sun4i_usb_phy_passby(phy, 1);
@ -476,7 +501,7 @@ static struct phy *sun4i_usb_phy_xlate(struct device *dev,
{
struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
if (args->args[0] >= data->num_phys)
if (args->args[0] >= data->cfg->num_phys)
return ERR_PTR(-ENODEV);
return data->phys[args->args[0]].phy;
@ -511,7 +536,6 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct phy_provider *phy_provider;
bool dedicated_clocks;
struct resource *res;
int i, ret;
@ -522,29 +546,9 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
mutex_init(&data->mutex);
INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan);
dev_set_drvdata(dev, data);
if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy") ||
of_device_is_compatible(np, "allwinner,sun8i-a23-usb-phy") ||
of_device_is_compatible(np, "allwinner,sun8i-a33-usb-phy"))
data->num_phys = 2;
else
data->num_phys = 3;
if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy") ||
of_device_is_compatible(np, "allwinner,sun7i-a20-usb-phy"))
data->disc_thresh = 2;
else
data->disc_thresh = 3;
if (of_device_is_compatible(np, "allwinner,sun6i-a31-usb-phy") ||
of_device_is_compatible(np, "allwinner,sun8i-a23-usb-phy") ||
of_device_is_compatible(np, "allwinner,sun8i-a33-usb-phy"))
dedicated_clocks = true;
else
dedicated_clocks = false;
if (of_device_is_compatible(np, "allwinner,sun8i-a33-usb-phy"))
data->has_a33_phyctl = true;
data->cfg = of_device_get_match_data(dev);
if (!data->cfg)
return -EINVAL;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_ctrl");
data->base = devm_ioremap_resource(dev, res);
@ -590,7 +594,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
}
}
for (i = 0; i < data->num_phys; i++) {
for (i = 0; i < data->cfg->num_phys; i++) {
struct sun4i_usb_phy *phy = data->phys + i;
char name[16];
@ -602,7 +606,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
phy->vbus = NULL;
}
if (dedicated_clocks)
if (data->cfg->dedicated_clocks)
snprintf(name, sizeof(name), "usb%d_phy", i);
else
strlcpy(name, "usb_phy", sizeof(name));
@ -689,13 +693,69 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
return 0;
}
static const struct sun4i_usb_phy_cfg sun4i_a10_cfg = {
.num_phys = 3,
.type = sun4i_a10_phy,
.disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A10,
.dedicated_clocks = false,
};
static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = {
.num_phys = 2,
.type = sun4i_a10_phy,
.disc_thresh = 2,
.phyctl_offset = REG_PHYCTL_A10,
.dedicated_clocks = false,
};
static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = {
.num_phys = 3,
.type = sun4i_a10_phy,
.disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A10,
.dedicated_clocks = true,
};
static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = {
.num_phys = 3,
.type = sun4i_a10_phy,
.disc_thresh = 2,
.phyctl_offset = REG_PHYCTL_A10,
.dedicated_clocks = false,
};
static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = {
.num_phys = 2,
.type = sun4i_a10_phy,
.disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A10,
.dedicated_clocks = true,
};
static const struct sun4i_usb_phy_cfg sun8i_a33_cfg = {
.num_phys = 2,
.type = sun8i_a33_phy,
.disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A33,
.dedicated_clocks = true,
};
static const struct sun4i_usb_phy_cfg sun8i_h3_cfg = {
.num_phys = 4,
.type = sun8i_h3_phy,
.disc_thresh = 3,
.dedicated_clocks = true,
};
static const struct of_device_id sun4i_usb_phy_of_match[] = {
{ .compatible = "allwinner,sun4i-a10-usb-phy" },
{ .compatible = "allwinner,sun5i-a13-usb-phy" },
{ .compatible = "allwinner,sun6i-a31-usb-phy" },
{ .compatible = "allwinner,sun7i-a20-usb-phy" },
{ .compatible = "allwinner,sun8i-a23-usb-phy" },
{ .compatible = "allwinner,sun8i-a33-usb-phy" },
{ .compatible = "allwinner,sun4i-a10-usb-phy", .data = &sun4i_a10_cfg },
{ .compatible = "allwinner,sun5i-a13-usb-phy", .data = &sun5i_a13_cfg },
{ .compatible = "allwinner,sun6i-a31-usb-phy", .data = &sun6i_a31_cfg },
{ .compatible = "allwinner,sun7i-a20-usb-phy", .data = &sun7i_a20_cfg },
{ .compatible = "allwinner,sun8i-a23-usb-phy", .data = &sun8i_a23_cfg },
{ .compatible = "allwinner,sun8i-a33-usb-phy", .data = &sun8i_a33_cfg },
{ .compatible = "allwinner,sun8i-h3-usb-phy", .data = &sun8i_h3_cfg },
{ },
};
MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);

View File

@ -56,6 +56,18 @@
#define SATA_PLL_SOFT_RESET BIT(18)
#define PIPE3_PHY_PWRCTL_CLK_CMD_MASK 0x003FC000
#define PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT 14
#define PIPE3_PHY_PWRCTL_CLK_FREQ_MASK 0xFFC00000
#define PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT 22
#define PIPE3_PHY_TX_RX_POWERON 0x3
#define PIPE3_PHY_TX_RX_POWEROFF 0x0
#define PCIE_PCS_MASK 0xFF0000
#define PCIE_PCS_DELAY_COUNT_SHIFT 0x10
/*
* This is an Empirical value that works, need to confirm the actual
* value required for the PIPE3PHY_PLL_CONFIGURATION2.PLL_IDLE status
@ -86,8 +98,12 @@ struct ti_pipe3 {
struct clk *refclk;
struct clk *div_clk;
struct pipe3_dpll_map *dpll_map;
struct regmap *phy_power_syscon; /* ctrl. reg. acces */
struct regmap *pcs_syscon; /* ctrl. reg. acces */
struct regmap *dpll_reset_syscon; /* ctrl. reg. acces */
unsigned int dpll_reset_reg; /* reg. index within syscon */
unsigned int power_reg; /* power reg. index within syscon */
unsigned int pcie_pcs_reg; /* pcs reg. index in syscon */
bool sata_refclk_enabled;
};
@ -144,20 +160,49 @@ static void ti_pipe3_disable_clocks(struct ti_pipe3 *phy);
static int ti_pipe3_power_off(struct phy *x)
{
u32 val;
int ret;
struct ti_pipe3 *phy = phy_get_drvdata(x);
omap_control_phy_power(phy->control_dev, 0);
if (!phy->phy_power_syscon) {
omap_control_phy_power(phy->control_dev, 0);
return 0;
}
return 0;
val = PIPE3_PHY_TX_RX_POWEROFF << PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT;
ret = regmap_update_bits(phy->phy_power_syscon, phy->power_reg,
PIPE3_PHY_PWRCTL_CLK_CMD_MASK, val);
return ret;
}
static int ti_pipe3_power_on(struct phy *x)
{
u32 val;
u32 mask;
int ret;
unsigned long rate;
struct ti_pipe3 *phy = phy_get_drvdata(x);
omap_control_phy_power(phy->control_dev, 1);
if (!phy->phy_power_syscon) {
omap_control_phy_power(phy->control_dev, 1);
return 0;
}
return 0;
rate = clk_get_rate(phy->sys_clk);
if (!rate) {
dev_err(phy->dev, "Invalid clock rate\n");
return -EINVAL;
}
rate = rate / 1000000;
mask = OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK |
OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK;
val = PIPE3_PHY_TX_RX_POWERON << PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT;
val |= rate << OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT;
ret = regmap_update_bits(phy->phy_power_syscon, phy->power_reg,
mask, val);
return ret;
}
static int ti_pipe3_dpll_wait_lock(struct ti_pipe3 *phy)
@ -229,8 +274,15 @@ static int ti_pipe3_init(struct phy *x)
* 18-1804.
*/
if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie")) {
omap_control_pcie_pcs(phy->control_dev, 0x96);
return 0;
if (!phy->pcs_syscon) {
omap_control_pcie_pcs(phy->control_dev, 0x96);
return 0;
}
val = 0x96 << OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT;
ret = regmap_update_bits(phy->pcs_syscon, phy->pcie_pcs_reg,
PCIE_PCS_MASK, val);
return ret;
}
/* Bring it out of IDLE if it is IDLE */
@ -308,51 +360,15 @@ static const struct phy_ops ops = {
static const struct of_device_id ti_pipe3_id_table[];
static int ti_pipe3_probe(struct platform_device *pdev)
static int ti_pipe3_get_clk(struct ti_pipe3 *phy)
{
struct ti_pipe3 *phy;
struct phy *generic_phy;
struct phy_provider *phy_provider;
struct resource *res;
struct device_node *node = pdev->dev.of_node;
struct device_node *control_node;
struct platform_device *control_pdev;
const struct of_device_id *match;
struct clk *clk;
struct device *dev = phy->dev;
struct device_node *node = dev->of_node;
phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
if (!phy)
return -ENOMEM;
phy->dev = &pdev->dev;
if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
match = of_match_device(ti_pipe3_id_table, &pdev->dev);
if (!match)
return -EINVAL;
phy->dpll_map = (struct pipe3_dpll_map *)match->data;
if (!phy->dpll_map) {
dev_err(&pdev->dev, "no DPLL data\n");
return -EINVAL;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"pll_ctrl");
phy->pll_ctrl_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(phy->pll_ctrl_base))
return PTR_ERR(phy->pll_ctrl_base);
phy->sys_clk = devm_clk_get(phy->dev, "sysclk");
if (IS_ERR(phy->sys_clk)) {
dev_err(&pdev->dev, "unable to get sysclk\n");
return -EINVAL;
}
}
phy->refclk = devm_clk_get(phy->dev, "refclk");
phy->refclk = devm_clk_get(dev, "refclk");
if (IS_ERR(phy->refclk)) {
dev_err(&pdev->dev, "unable to get refclk\n");
dev_err(dev, "unable to get refclk\n");
/* older DTBs have missing refclk in SATA PHY
* so don't bail out in case of SATA PHY.
*/
@ -361,80 +377,194 @@ static int ti_pipe3_probe(struct platform_device *pdev)
}
if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) {
phy->wkupclk = devm_clk_get(phy->dev, "wkupclk");
phy->wkupclk = devm_clk_get(dev, "wkupclk");
if (IS_ERR(phy->wkupclk)) {
dev_err(&pdev->dev, "unable to get wkupclk\n");
dev_err(dev, "unable to get wkupclk\n");
return PTR_ERR(phy->wkupclk);
}
} else {
phy->wkupclk = ERR_PTR(-ENODEV);
phy->dpll_reset_syscon = syscon_regmap_lookup_by_phandle(node,
"syscon-pllreset");
if (IS_ERR(phy->dpll_reset_syscon)) {
dev_info(&pdev->dev,
"can't get syscon-pllreset, sata dpll won't idle\n");
phy->dpll_reset_syscon = NULL;
} else {
if (of_property_read_u32_index(node,
"syscon-pllreset", 1,
&phy->dpll_reset_reg)) {
dev_err(&pdev->dev,
"couldn't get pllreset reg. offset\n");
return -EINVAL;
}
}
if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie") ||
phy->phy_power_syscon) {
phy->sys_clk = devm_clk_get(dev, "sysclk");
if (IS_ERR(phy->sys_clk)) {
dev_err(dev, "unable to get sysclk\n");
return -EINVAL;
}
}
if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
clk = devm_clk_get(phy->dev, "dpll_ref");
clk = devm_clk_get(dev, "dpll_ref");
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "unable to get dpll ref clk\n");
dev_err(dev, "unable to get dpll ref clk\n");
return PTR_ERR(clk);
}
clk_set_rate(clk, 1500000000);
clk = devm_clk_get(phy->dev, "dpll_ref_m2");
clk = devm_clk_get(dev, "dpll_ref_m2");
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "unable to get dpll ref m2 clk\n");
dev_err(dev, "unable to get dpll ref m2 clk\n");
return PTR_ERR(clk);
}
clk_set_rate(clk, 100000000);
clk = devm_clk_get(phy->dev, "phy-div");
clk = devm_clk_get(dev, "phy-div");
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "unable to get phy-div clk\n");
dev_err(dev, "unable to get phy-div clk\n");
return PTR_ERR(clk);
}
clk_set_rate(clk, 100000000);
phy->div_clk = devm_clk_get(phy->dev, "div-clk");
phy->div_clk = devm_clk_get(dev, "div-clk");
if (IS_ERR(phy->div_clk)) {
dev_err(&pdev->dev, "unable to get div-clk\n");
dev_err(dev, "unable to get div-clk\n");
return PTR_ERR(phy->div_clk);
}
} else {
phy->div_clk = ERR_PTR(-ENODEV);
}
control_node = of_parse_phandle(node, "ctrl-module", 0);
if (!control_node) {
dev_err(&pdev->dev, "Failed to get control device phandle\n");
return 0;
}
static int ti_pipe3_get_sysctrl(struct ti_pipe3 *phy)
{
struct device *dev = phy->dev;
struct device_node *node = dev->of_node;
struct device_node *control_node;
struct platform_device *control_pdev;
phy->phy_power_syscon = syscon_regmap_lookup_by_phandle(node,
"syscon-phy-power");
if (IS_ERR(phy->phy_power_syscon)) {
dev_dbg(dev,
"can't get syscon-phy-power, using control device\n");
phy->phy_power_syscon = NULL;
} else {
if (of_property_read_u32_index(node,
"syscon-phy-power", 1,
&phy->power_reg)) {
dev_err(dev, "couldn't get power reg. offset\n");
return -EINVAL;
}
}
if (!phy->phy_power_syscon) {
control_node = of_parse_phandle(node, "ctrl-module", 0);
if (!control_node) {
dev_err(dev, "Failed to get control device phandle\n");
return -EINVAL;
}
control_pdev = of_find_device_by_node(control_node);
if (!control_pdev) {
dev_err(dev, "Failed to get control device\n");
return -EINVAL;
}
phy->control_dev = &control_pdev->dev;
}
if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
phy->pcs_syscon = syscon_regmap_lookup_by_phandle(node,
"syscon-pcs");
if (IS_ERR(phy->pcs_syscon)) {
dev_dbg(dev,
"can't get syscon-pcs, using omap control\n");
phy->pcs_syscon = NULL;
} else {
if (of_property_read_u32_index(node,
"syscon-pcs", 1,
&phy->pcie_pcs_reg)) {
dev_err(dev,
"couldn't get pcie pcs reg. offset\n");
return -EINVAL;
}
}
}
if (of_device_is_compatible(node, "ti,phy-pipe3-sata")) {
phy->dpll_reset_syscon = syscon_regmap_lookup_by_phandle(node,
"syscon-pllreset");
if (IS_ERR(phy->dpll_reset_syscon)) {
dev_info(dev,
"can't get syscon-pllreset, sata dpll won't idle\n");
phy->dpll_reset_syscon = NULL;
} else {
if (of_property_read_u32_index(node,
"syscon-pllreset", 1,
&phy->dpll_reset_reg)) {
dev_err(dev,
"couldn't get pllreset reg. offset\n");
return -EINVAL;
}
}
}
return 0;
}
static int ti_pipe3_get_pll_base(struct ti_pipe3 *phy)
{
struct resource *res;
const struct of_device_id *match;
struct device *dev = phy->dev;
struct device_node *node = dev->of_node;
struct platform_device *pdev = to_platform_device(dev);
if (of_device_is_compatible(node, "ti,phy-pipe3-pcie"))
return 0;
match = of_match_device(ti_pipe3_id_table, dev);
if (!match)
return -EINVAL;
phy->dpll_map = (struct pipe3_dpll_map *)match->data;
if (!phy->dpll_map) {
dev_err(dev, "no DPLL data\n");
return -EINVAL;
}
control_pdev = of_find_device_by_node(control_node);
if (!control_pdev) {
dev_err(&pdev->dev, "Failed to get control device\n");
return -EINVAL;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"pll_ctrl");
phy->pll_ctrl_base = devm_ioremap_resource(dev, res);
if (IS_ERR(phy->pll_ctrl_base))
return PTR_ERR(phy->pll_ctrl_base);
phy->control_dev = &control_pdev->dev;
return 0;
}
omap_control_phy_power(phy->control_dev, 0);
static int ti_pipe3_probe(struct platform_device *pdev)
{
struct ti_pipe3 *phy;
struct phy *generic_phy;
struct phy_provider *phy_provider;
struct device_node *node = pdev->dev.of_node;
struct device *dev = &pdev->dev;
int ret;
phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
if (!phy)
return -ENOMEM;
phy->dev = dev;
ret = ti_pipe3_get_pll_base(phy);
if (ret)
return ret;
ret = ti_pipe3_get_sysctrl(phy);
if (ret)
return ret;
ret = ti_pipe3_get_clk(phy);
if (ret)
return ret;
platform_set_drvdata(pdev, phy);
pm_runtime_enable(phy->dev);
pm_runtime_enable(dev);
/*
* Prevent auto-disable of refclk for SATA PHY due to Errata i783
@ -446,13 +576,15 @@ static int ti_pipe3_probe(struct platform_device *pdev)
}
}
generic_phy = devm_phy_create(phy->dev, NULL, &ops);
generic_phy = devm_phy_create(dev, NULL, &ops);
if (IS_ERR(generic_phy))
return PTR_ERR(generic_phy);
phy_set_drvdata(generic_phy, phy);
phy_provider = devm_of_phy_provider_register(phy->dev,
of_phy_simple_xlate);
ti_pipe3_power_off(generic_phy);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
if (IS_ERR(phy_provider))
return PTR_ERR(phy_provider);

View File

@ -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";

View File

@ -37,9 +37,4 @@ config USB_CHIPIDEA_HOST
Say Y here to enable host controller functionality of the
ChipIdea driver.
config USB_CHIPIDEA_DEBUG
bool "ChipIdea driver debug"
help
Say Y here to enable debugging output of the ChipIdea driver.
endif

View File

@ -1,11 +1,8 @@
ccflags-$(CONFIG_USB_CHIPIDEA_DEBUG) := -DDEBUG
obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc.o
ci_hdrc-y := core.o otg.o
ci_hdrc-y := core.o otg.o debug.o
ci_hdrc-$(CONFIG_USB_CHIPIDEA_UDC) += udc.o
ci_hdrc-$(CONFIG_USB_CHIPIDEA_HOST) += host.o
ci_hdrc-$(CONFIG_USB_CHIPIDEA_DEBUG) += debug.o
ci_hdrc-$(CONFIG_USB_OTG_FSM) += otg_fsm.o
# Glue/Bridge layers go here

View File

@ -433,4 +433,7 @@ int hw_wait_reg(struct ci_hdrc *ci, enum ci_hw_regs reg, u32 mask,
void ci_platform_configure(struct ci_hdrc *ci);
int dbg_create_files(struct ci_hdrc *ci);
void dbg_remove_files(struct ci_hdrc *ci);
#endif /* __DRIVERS_USB_CHIPIDEA_CI_H */

View File

@ -345,6 +345,11 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev)
return 0;
}
static void ci_hdrc_imx_shutdown(struct platform_device *pdev)
{
ci_hdrc_imx_remove(pdev);
}
#ifdef CONFIG_PM
static int imx_controller_suspend(struct device *dev)
{
@ -462,6 +467,7 @@ static const struct dev_pm_ops ci_hdrc_imx_pm_ops = {
static struct platform_driver ci_hdrc_imx_driver = {
.probe = ci_hdrc_imx_probe,
.remove = ci_hdrc_imx_remove,
.shutdown = ci_hdrc_imx_shutdown,
.driver = {
.name = "imx_usb",
.of_match_table = ci_hdrc_imx_dt_ids,

View File

@ -25,7 +25,8 @@ static void ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event)
case CI_HDRC_CONTROLLER_RESET_EVENT:
dev_dbg(dev, "CI_HDRC_CONTROLLER_RESET_EVENT received\n");
writel(0, USB_AHBBURST);
writel(0, USB_AHBMODE);
/* use AHB transactor, allow posted data writes */
writel(0x8, USB_AHBMODE);
usb_phy_init(ci->usb_phy);
break;
case CI_HDRC_CONTROLLER_STOPPED_EVENT:

View File

@ -23,7 +23,6 @@
* - BUS: bus glue code, bus abstraction layer
*
* Compile Options
* - CONFIG_USB_CHIPIDEA_DEBUG: enable debug facilities
* - STALL_IN: non-empty bulk-in pipes cannot be halted
* if defined mass storage compliance succeeds but with warnings
* => case 4: Hi > Dn
@ -71,7 +70,6 @@
#include "udc.h"
#include "bits.h"
#include "host.h"
#include "debug.h"
#include "otg.h"
#include "otg_fsm.h"
@ -688,52 +686,39 @@ static int ci_get_platdata(struct device *dev,
if (usb_get_maximum_speed(dev) == USB_SPEED_FULL)
platdata->flags |= CI_HDRC_FORCE_FULLSPEED;
if (of_find_property(dev->of_node, "phy-clkgate-delay-us", NULL))
of_property_read_u32(dev->of_node, "phy-clkgate-delay-us",
of_property_read_u32(dev->of_node, "phy-clkgate-delay-us",
&platdata->phy_clkgate_delay_us);
platdata->itc_setting = 1;
if (of_find_property(dev->of_node, "itc-setting", NULL)) {
ret = of_property_read_u32(dev->of_node, "itc-setting",
&platdata->itc_setting);
if (ret) {
dev_err(dev,
"failed to get itc-setting\n");
return ret;
}
}
if (of_find_property(dev->of_node, "ahb-burst-config", NULL)) {
ret = of_property_read_u32(dev->of_node, "ahb-burst-config",
&platdata->ahb_burst_config);
if (ret) {
dev_err(dev,
"failed to get ahb-burst-config\n");
return ret;
}
of_property_read_u32(dev->of_node, "itc-setting",
&platdata->itc_setting);
ret = of_property_read_u32(dev->of_node, "ahb-burst-config",
&platdata->ahb_burst_config);
if (!ret) {
platdata->flags |= CI_HDRC_OVERRIDE_AHB_BURST;
} else if (ret != -EINVAL) {
dev_err(dev, "failed to get ahb-burst-config\n");
return ret;
}
if (of_find_property(dev->of_node, "tx-burst-size-dword", NULL)) {
ret = of_property_read_u32(dev->of_node, "tx-burst-size-dword",
&platdata->tx_burst_size);
if (ret) {
dev_err(dev,
"failed to get tx-burst-size-dword\n");
return ret;
}
ret = of_property_read_u32(dev->of_node, "tx-burst-size-dword",
&platdata->tx_burst_size);
if (!ret) {
platdata->flags |= CI_HDRC_OVERRIDE_TX_BURST;
} else if (ret != -EINVAL) {
dev_err(dev, "failed to get tx-burst-size-dword\n");
return ret;
}
if (of_find_property(dev->of_node, "rx-burst-size-dword", NULL)) {
ret = of_property_read_u32(dev->of_node, "rx-burst-size-dword",
&platdata->rx_burst_size);
if (ret) {
dev_err(dev,
"failed to get rx-burst-size-dword\n");
return ret;
}
ret = of_property_read_u32(dev->of_node, "rx-burst-size-dword",
&platdata->rx_burst_size);
if (!ret) {
platdata->flags |= CI_HDRC_OVERRIDE_RX_BURST;
} else if (ret != -EINVAL) {
dev_err(dev, "failed to get rx-burst-size-dword\n");
return ret;
}
ext_id = ERR_PTR(-ENODEV);

View File

@ -15,7 +15,6 @@
#include "ci.h"
#include "udc.h"
#include "bits.h"
#include "debug.h"
#include "otg.h"
/**

View File

@ -1,30 +0,0 @@
/*
* debug.h - ChipIdea USB driver debug interfaces
*
* Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved.
*
* Author: David Lopo
*
* 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 __DRIVERS_USB_CHIPIDEA_DEBUG_H
#define __DRIVERS_USB_CHIPIDEA_DEBUG_H
#ifdef CONFIG_USB_CHIPIDEA_DEBUG
int dbg_create_files(struct ci_hdrc *ci);
void dbg_remove_files(struct ci_hdrc *ci);
#else
static inline int dbg_create_files(struct ci_hdrc *ci)
{
return 0;
}
static inline void dbg_remove_files(struct ci_hdrc *ci)
{
}
#endif
#endif /* __DRIVERS_USB_CHIPIDEA_DEBUG_H */

View File

@ -190,6 +190,8 @@ static void host_stop(struct ci_hdrc *ci)
(ci->platdata->flags & CI_HDRC_TURN_VBUS_EARLY_ON))
regulator_disable(ci->platdata->reg_vbus);
}
ci->hcd = NULL;
ci->otg.host = NULL;
}

View File

@ -485,20 +485,30 @@ static void ci_otg_loc_conn(struct otg_fsm *fsm, int on)
/*
* Generate SOF by host.
* This is controlled through suspend/resume the port.
* In host mode, controller will automatically send SOF.
* Suspend will block the data on the port.
*
* This is controlled through usbcore by usb autosuspend,
* so the usb device class driver need support autosuspend,
* otherwise the bus suspend will not happen.
*/
static void ci_otg_loc_sof(struct otg_fsm *fsm, int on)
{
struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
struct usb_device *udev;
if (on)
hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_FPR,
PORTSC_FPR);
else
hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_SUSP,
PORTSC_SUSP);
if (!fsm->otg->host)
return;
udev = usb_hub_find_child(fsm->otg->host->root_hub, 1);
if (!udev)
return;
if (on) {
usb_disable_autosuspend(udev);
} else {
pm_runtime_set_autosuspend_delay(&udev->dev, 0);
usb_enable_autosuspend(udev);
}
}
/*

View File

@ -26,7 +26,6 @@
#include "ci.h"
#include "udc.h"
#include "bits.h"
#include "debug.h"
#include "otg.h"
#include "otg_fsm.h"
@ -349,14 +348,13 @@ static int add_td_to_list(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq,
if (node == NULL)
return -ENOMEM;
node->ptr = dma_pool_alloc(hwep->td_pool, GFP_ATOMIC,
node->ptr = dma_pool_zalloc(hwep->td_pool, GFP_ATOMIC,
&node->dma);
if (node->ptr == NULL) {
kfree(node);
return -ENOMEM;
}
memset(node->ptr, 0, sizeof(struct ci_hw_td));
node->ptr->token = cpu_to_le32(length << __ffs(TD_TOTAL_BYTES));
node->ptr->token &= cpu_to_le32(TD_TOTAL_BYTES);
node->ptr->token |= cpu_to_le32(TD_STATUS_ACTIVE);
@ -404,9 +402,9 @@ static inline u8 _usb_addr(struct ci_hw_ep *ep)
}
/**
* _hardware_queue: configures a request at hardware level
* @gadget: gadget
* _hardware_enqueue: configures a request at hardware level
* @hwep: endpoint
* @hwreq: request
*
* This function returns an error code
*/
@ -435,19 +433,28 @@ static int _hardware_enqueue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq)
if (hwreq->req.dma % PAGE_SIZE)
pages--;
if (rest == 0)
add_td_to_list(hwep, hwreq, 0);
if (rest == 0) {
ret = add_td_to_list(hwep, hwreq, 0);
if (ret < 0)
goto done;
}
while (rest > 0) {
unsigned count = min(hwreq->req.length - hwreq->req.actual,
(unsigned)(pages * CI_HDRC_PAGE_SIZE));
add_td_to_list(hwep, hwreq, count);
ret = add_td_to_list(hwep, hwreq, count);
if (ret < 0)
goto done;
rest -= count;
}
if (hwreq->req.zero && hwreq->req.length && hwep->dir == TX
&& (hwreq->req.length % hwep->ep.maxpacket == 0))
add_td_to_list(hwep, hwreq, 0);
&& (hwreq->req.length % hwep->ep.maxpacket == 0)) {
ret = add_td_to_list(hwep, hwreq, 0);
if (ret < 0)
goto done;
}
firstnode = list_first_entry(&hwreq->tds, struct td_node, td);
@ -788,8 +795,12 @@ static void isr_get_status_complete(struct usb_ep *ep, struct usb_request *req)
/**
* _ep_queue: queues (submits) an I/O request to an endpoint
* @ep: endpoint
* @req: request
* @gfp_flags: GFP flags (not used)
*
* Caller must hold lock
* This function returns an error code
*/
static int _ep_queue(struct usb_ep *ep, struct usb_request *req,
gfp_t __maybe_unused gfp_flags)

View File

@ -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)

View File

@ -100,6 +100,11 @@ static bool usbfs_snoop;
module_param(usbfs_snoop, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(usbfs_snoop, "true to log all usbfs traffic");
static unsigned usbfs_snoop_max = 65536;
module_param(usbfs_snoop_max, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(usbfs_snoop_max,
"maximum number of bytes to print while snooping");
#define snoop(dev, format, arg...) \
do { \
if (usbfs_snoop) \
@ -368,6 +373,7 @@ static void snoop_urb(struct usb_device *udev,
ep, t, d, length, timeout_or_status);
}
data_len = min(data_len, usbfs_snoop_max);
if (data && data_len > 0) {
print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_NONE, 32, 1,
data, data_len, 1);
@ -378,7 +384,8 @@ static void snoop_urb_data(struct urb *urb, unsigned len)
{
int i, size;
if (!usbfs_snoop)
len = min(len, usbfs_snoop_max);
if (!usbfs_snoop || len == 0)
return;
if (urb->num_sgs == 0) {
@ -1685,8 +1692,12 @@ static struct async *reap_as(struct usb_dev_state *ps)
static int proc_reapurb(struct usb_dev_state *ps, void __user *arg)
{
struct async *as = reap_as(ps);
if (as) {
int retval = processcompl(as, (void __user * __user *)arg);
int retval;
snoop(&ps->dev->dev, "reap %p\n", as->userurb);
retval = processcompl(as, (void __user * __user *)arg);
free_async(as);
return retval;
}
@ -1702,6 +1713,7 @@ static int proc_reapurbnonblock(struct usb_dev_state *ps, void __user *arg)
as = async_getcompleted(ps);
if (as) {
snoop(&ps->dev->dev, "reap %p\n", as->userurb);
retval = processcompl(as, (void __user * __user *)arg);
free_async(as);
} else {
@ -1828,8 +1840,12 @@ static int processcompl_compat(struct async *as, void __user * __user *arg)
static int proc_reapurb_compat(struct usb_dev_state *ps, void __user *arg)
{
struct async *as = reap_as(ps);
if (as) {
int retval = processcompl_compat(as, (void __user * __user *)arg);
int retval;
snoop(&ps->dev->dev, "reap %p\n", as->userurb);
retval = processcompl_compat(as, (void __user * __user *)arg);
free_async(as);
return retval;
}
@ -1845,6 +1861,7 @@ static int proc_reapurbnonblock_compat(struct usb_dev_state *ps, void __user *ar
as = async_getcompleted(ps);
if (as) {
snoop(&ps->dev->dev, "reap %p\n", as->userurb);
retval = processcompl_compat(as, (void __user * __user *)arg);
free_async(as);
} else {
@ -2249,7 +2266,7 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
#endif
case USBDEVFS_DISCARDURB:
snoop(&dev->dev, "%s: DISCARDURB\n", __func__);
snoop(&dev->dev, "%s: DISCARDURB %p\n", __func__, p);
ret = proc_unlinkurb(ps, p);
break;

View File

@ -3000,7 +3000,7 @@ EXPORT_SYMBOL_GPL(usb_hcd_platform_shutdown);
#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)
struct usb_mon_operations *mon_ops;
const struct usb_mon_operations *mon_ops;
/*
* The registration is unlocked.
@ -3010,7 +3010,7 @@ struct usb_mon_operations *mon_ops;
* symbols from usbcore, usbcore gets referenced and cannot be unloaded first.
*/
int usb_mon_register (struct usb_mon_operations *ops)
int usb_mon_register(const struct usb_mon_operations *ops)
{
if (mon_ops)

View File

@ -3324,7 +3324,7 @@ static int finish_port_resume(struct usb_device *udev)
/*
* There are some SS USB devices which take longer time for link training.
* XHCI specs 4.19.4 says that when Link training is successful, port
* sets CSC bit to 1. So if SW reads port status before successful link
* sets CCS bit to 1. So if SW reads port status before successful link
* training, then it will not find device to be present.
* USB Analyzer log with such buggy devices show that in some cases
* device switch on the RX termination after long delay of host enabling
@ -3335,14 +3335,17 @@ static int finish_port_resume(struct usb_device *udev)
* routine implements a 2000 ms timeout for link training. If in a case
* link trains before timeout, loop will exit earlier.
*
* There are also some 2.0 hard drive based devices and 3.0 thumb
* drives that, when plugged into a 2.0 only port, take a long
* time to set CCS after VBUS enable.
*
* FIXME: If a device was connected before suspend, but was removed
* while system was asleep, then the loop in the following routine will
* only exit at timeout.
*
* This routine should only be called when persist is enabled for a SS
* device.
* This routine should only be called when persist is enabled.
*/
static int wait_for_ss_port_enable(struct usb_device *udev,
static int wait_for_connected(struct usb_device *udev,
struct usb_hub *hub, int *port1,
u16 *portchange, u16 *portstatus)
{
@ -3355,6 +3358,7 @@ static int wait_for_ss_port_enable(struct usb_device *udev,
delay_ms += 20;
status = hub_port_status(hub, *port1, portstatus, portchange);
}
dev_dbg(&udev->dev, "Waited %dms for CONNECT\n", delay_ms);
return status;
}
@ -3454,8 +3458,8 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
}
}
if (udev->persist_enabled && hub_is_superspeed(hub->hdev))
status = wait_for_ss_port_enable(udev, hub, &port1, &portchange,
if (udev->persist_enabled)
status = wait_for_connected(udev, hub, &port1, &portchange,
&portstatus);
status = check_port_resume_type(udev,
@ -3895,17 +3899,30 @@ static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
return;
}
if (usb_set_lpm_timeout(udev, state, timeout))
if (usb_set_lpm_timeout(udev, state, timeout)) {
/* If we can't set the parent hub U1/U2 timeout,
* device-initiated LPM won't be allowed either, so let the xHCI
* host know that this link state won't be enabled.
*/
hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state);
} else {
/* Only a configured device will accept the Set Feature
* U1/U2_ENABLE
*/
if (udev->actconfig)
usb_set_device_initiated_lpm(udev, state, true);
/* Only a configured device will accept the Set Feature U1/U2_ENABLE */
else if (udev->actconfig)
usb_set_device_initiated_lpm(udev, state, true);
/* As soon as usb_set_lpm_timeout(timeout) returns 0, the
* hub-initiated LPM is enabled. Thus, LPM is enabled no
* matter the result of usb_set_device_initiated_lpm().
* The only difference is whether device is able to initiate
* LPM.
*/
if (state == USB3_LPM_U1)
udev->usb3_lpm_u1_enabled = 1;
else if (state == USB3_LPM_U2)
udev->usb3_lpm_u2_enabled = 1;
}
}
/*
@ -3945,6 +3962,18 @@ static int usb_disable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
dev_warn(&udev->dev, "Could not disable xHCI %s timeout, "
"bus schedule bandwidth may be impacted.\n",
usb3_lpm_names[state]);
/* As soon as usb_set_lpm_timeout(0) return 0, hub initiated LPM
* is disabled. Hub will disallows link to enter U1/U2 as well,
* even device is initiating LPM. Hence LPM is disabled if hub LPM
* timeout set to 0, no matter device-initiated LPM is disabled or
* not.
*/
if (state == USB3_LPM_U1)
udev->usb3_lpm_u1_enabled = 0;
else if (state == USB3_LPM_U2)
udev->usb3_lpm_u2_enabled = 0;
return 0;
}
@ -3979,8 +4008,6 @@ int usb_disable_lpm(struct usb_device *udev)
if (usb_disable_link_state(hcd, udev, USB3_LPM_U2))
goto enable_lpm;
udev->usb3_lpm_enabled = 0;
return 0;
enable_lpm:
@ -4017,6 +4044,8 @@ EXPORT_SYMBOL_GPL(usb_unlocked_disable_lpm);
void usb_enable_lpm(struct usb_device *udev)
{
struct usb_hcd *hcd;
struct usb_hub *hub;
struct usb_port *port_dev;
if (!udev || !udev->parent ||
udev->speed != USB_SPEED_SUPER ||
@ -4036,10 +4065,17 @@ void usb_enable_lpm(struct usb_device *udev)
if (udev->lpm_disable_count > 0)
return;
usb_enable_link_state(hcd, udev, USB3_LPM_U1);
usb_enable_link_state(hcd, udev, USB3_LPM_U2);
hub = usb_hub_to_struct_hub(udev->parent);
if (!hub)
return;
udev->usb3_lpm_enabled = 1;
port_dev = hub->ports[udev->portnum - 1];
if (port_dev->usb3_lpm_u1_permit)
usb_enable_link_state(hcd, udev, USB3_LPM_U1);
if (port_dev->usb3_lpm_u2_permit)
usb_enable_link_state(hcd, udev, USB3_LPM_U2);
}
EXPORT_SYMBOL_GPL(usb_enable_lpm);

View File

@ -92,6 +92,8 @@ struct usb_hub {
* @status_lock: synchronize port_event() vs usb_port_{suspend|resume}
* @portnum: port index num based one
* @is_superspeed cache super-speed status
* @usb3_lpm_u1_permit: whether USB3 U1 LPM is permitted.
* @usb3_lpm_u2_permit: whether USB3 U2 LPM is permitted.
*/
struct usb_port {
struct usb_device *child;
@ -104,6 +106,8 @@ struct usb_port {
struct mutex status_lock;
u8 portnum;
unsigned int is_superspeed:1;
unsigned int usb3_lpm_u1_permit:1;
unsigned int usb3_lpm_u2_permit:1;
};
#define to_usb_port(_dev) \
@ -155,4 +159,3 @@ static inline int hub_port_debounce_be_stable(struct usb_hub *hub,
{
return hub_port_debounce(hub, port1, false);
}

View File

@ -50,6 +50,72 @@ static ssize_t connect_type_show(struct device *dev,
}
static DEVICE_ATTR_RO(connect_type);
static ssize_t usb3_lpm_permit_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct usb_port *port_dev = to_usb_port(dev);
const char *p;
if (port_dev->usb3_lpm_u1_permit) {
if (port_dev->usb3_lpm_u2_permit)
p = "u1_u2";
else
p = "u1";
} else {
if (port_dev->usb3_lpm_u2_permit)
p = "u2";
else
p = "0";
}
return sprintf(buf, "%s\n", p);
}
static ssize_t usb3_lpm_permit_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct usb_port *port_dev = to_usb_port(dev);
struct usb_device *udev = port_dev->child;
struct usb_hcd *hcd;
if (!strncmp(buf, "u1_u2", 5)) {
port_dev->usb3_lpm_u1_permit = 1;
port_dev->usb3_lpm_u2_permit = 1;
} else if (!strncmp(buf, "u1", 2)) {
port_dev->usb3_lpm_u1_permit = 1;
port_dev->usb3_lpm_u2_permit = 0;
} else if (!strncmp(buf, "u2", 2)) {
port_dev->usb3_lpm_u1_permit = 0;
port_dev->usb3_lpm_u2_permit = 1;
} else if (!strncmp(buf, "0", 1)) {
port_dev->usb3_lpm_u1_permit = 0;
port_dev->usb3_lpm_u2_permit = 0;
} else
return -EINVAL;
/* If device is connected to the port, disable or enable lpm
* to make new u1 u2 setting take effect immediately.
*/
if (udev) {
hcd = bus_to_hcd(udev->bus);
if (!hcd)
return -EINVAL;
usb_lock_device(udev);
mutex_lock(hcd->bandwidth_mutex);
if (!usb_disable_lpm(udev))
usb_enable_lpm(udev);
mutex_unlock(hcd->bandwidth_mutex);
usb_unlock_device(udev);
}
return count;
}
static DEVICE_ATTR_RW(usb3_lpm_permit);
static struct attribute *port_dev_attrs[] = {
&dev_attr_connect_type.attr,
NULL,
@ -64,6 +130,21 @@ static const struct attribute_group *port_dev_group[] = {
NULL,
};
static struct attribute *port_dev_usb3_attrs[] = {
&dev_attr_usb3_lpm_permit.attr,
NULL,
};
static struct attribute_group port_dev_usb3_attr_grp = {
.attrs = port_dev_usb3_attrs,
};
static const struct attribute_group *port_dev_usb3_group[] = {
&port_dev_attr_grp,
&port_dev_usb3_attr_grp,
NULL,
};
static void usb_port_device_release(struct device *dev)
{
struct usb_port *port_dev = to_usb_port(dev);
@ -401,6 +482,7 @@ static void find_and_link_peer(struct usb_hub *hub, int port1)
int usb_hub_create_port_device(struct usb_hub *hub, int port1)
{
struct usb_port *port_dev;
struct usb_device *hdev = hub->hdev;
int retval;
port_dev = kzalloc(sizeof(*port_dev), GFP_KERNEL);
@ -417,7 +499,12 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
port_dev->portnum = port1;
set_bit(port1, hub->power_bits);
port_dev->dev.parent = hub->intfdev;
port_dev->dev.groups = port_dev_group;
if (hub_is_superspeed(hdev)) {
port_dev->usb3_lpm_u1_permit = 1;
port_dev->usb3_lpm_u2_permit = 1;
port_dev->dev.groups = port_dev_usb3_group;
} else
port_dev->dev.groups = port_dev_group;
port_dev->dev.type = &usb_port_device_type;
port_dev->dev.driver = &usb_port_driver;
if (hub_is_superspeed(hub->hdev))

View File

@ -531,7 +531,7 @@ static ssize_t usb2_lpm_besl_store(struct device *dev,
}
static DEVICE_ATTR_RW(usb2_lpm_besl);
static ssize_t usb3_hardware_lpm_show(struct device *dev,
static ssize_t usb3_hardware_lpm_u1_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct usb_device *udev = to_usb_device(dev);
@ -539,7 +539,7 @@ static ssize_t usb3_hardware_lpm_show(struct device *dev,
usb_lock_device(udev);
if (udev->usb3_lpm_enabled)
if (udev->usb3_lpm_u1_enabled)
p = "enabled";
else
p = "disabled";
@ -548,7 +548,26 @@ static ssize_t usb3_hardware_lpm_show(struct device *dev,
return sprintf(buf, "%s\n", p);
}
static DEVICE_ATTR_RO(usb3_hardware_lpm);
static DEVICE_ATTR_RO(usb3_hardware_lpm_u1);
static ssize_t usb3_hardware_lpm_u2_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct usb_device *udev = to_usb_device(dev);
const char *p;
usb_lock_device(udev);
if (udev->usb3_lpm_u2_enabled)
p = "enabled";
else
p = "disabled";
usb_unlock_device(udev);
return sprintf(buf, "%s\n", p);
}
static DEVICE_ATTR_RO(usb3_hardware_lpm_u2);
static struct attribute *usb2_hardware_lpm_attr[] = {
&dev_attr_usb2_hardware_lpm.attr,
@ -562,7 +581,8 @@ static struct attribute_group usb2_hardware_lpm_attr_group = {
};
static struct attribute *usb3_hardware_lpm_attr[] = {
&dev_attr_usb3_hardware_lpm.attr,
&dev_attr_usb3_hardware_lpm_u1.attr,
&dev_attr_usb3_hardware_lpm_u2.attr,
NULL,
};
static struct attribute_group usb3_hardware_lpm_attr_group = {
@ -592,7 +612,8 @@ static int add_power_attributes(struct device *dev)
if (udev->usb2_hw_lpm_capable == 1)
rc = sysfs_merge_group(&dev->kobj,
&usb2_hardware_lpm_attr_group);
if (udev->lpm_capable == 1)
if (udev->speed == USB_SPEED_SUPER &&
udev->lpm_capable == 1)
rc = sysfs_merge_group(&dev->kobj,
&usb3_hardware_lpm_attr_group);
}

View File

@ -49,12 +49,7 @@ const char *usbcore_name = "usbcore";
static bool nousb; /* Disable USB when built into kernel image */
/* To disable USB, kernel command line is 'nousb' not 'usbcore.nousb' */
#ifdef MODULE
module_param(nousb, bool, 0444);
#else
core_param(nousb, nousb, bool, 0444);
#endif
/*
* for external read access to <nousb>

View File

@ -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");

View File

@ -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)

View File

@ -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 | \

View File

@ -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) {

View File

@ -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);

View File

@ -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

View File

@ -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.

View File

@ -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);
}
/*

View File

@ -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 */

View File

@ -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))

View File

@ -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 = &params_bcm2835 },
{ .compatible = "hisilicon,hi6220-usb", .data = &params_hi6220 },
{ .compatible = "rockchip,rk3066-usb", .data = &params_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);

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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;

View 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>");

View File

@ -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>");

View File

@ -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");

View File

@ -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, &params);
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);

View File

@ -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
)
);

View File

@ -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"
#

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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)

View File

@ -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);

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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),

View File

@ -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),

View File

@ -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, */

View File

@ -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;

View File

@ -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. */

View File

@ -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,

View File

@ -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,

View File

@ -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),

View File

@ -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,

View File

@ -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,

View File

@ -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),

View File

@ -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,

View File

@ -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)) {

View File

@ -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)

View File

@ -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__ */

View File

@ -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

View File

@ -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

Some files were not shown because too many files have changed in this diff Show More