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 ...
This commit is contained in:
commit
237f38c3b3
174 changed files with 9147 additions and 2053 deletions
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
16
Documentation/devicetree/bindings/phy/phy-hi6220-usb.txt
Normal file
16
Documentation/devicetree/bindings/phy/phy-hi6220-usb.txt
Normal 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>;
|
||||
};
|
39
Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt
Normal file
39
Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt
Normal 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>;
|
||||
};
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ Platform DesignWare HS OTG USB 2.0 controller
|
|||
Required properties:
|
||||
- compatible : One of:
|
||||
- brcm,bcm2835-usb: The DWC2 USB controller instance in the BCM2835 SoC.
|
||||
- hisilicon,hi6220-usb: The DWC2 USB controller instance in the hi6220 SoC.
|
||||
- rockchip,rk3066-usb: The DWC2 USB controller instance in the rk3066 Soc;
|
||||
- "rockchip,rk3188-usb", "rockchip,rk3066-usb", "snps,dwc2": for rk3188 Soc;
|
||||
- "rockchip,rk3288-usb", "rockchip,rk3066-usb", "snps,dwc2": for rk3288 Soc;
|
||||
|
|
33
Documentation/devicetree/bindings/usb/dwc3-xilinx.txt
Normal file
33
Documentation/devicetree/bindings/usb/dwc3-xilinx.txt
Normal file
|
@ -0,0 +1,33 @@
|
|||
Xilinx SuperSpeed DWC3 USB SoC controller
|
||||
|
||||
Required properties:
|
||||
- compatible: Should contain "xlnx,zynqmp-dwc3"
|
||||
- clocks: A list of phandles for the clocks listed in clock-names
|
||||
- clock-names: Should contain the following:
|
||||
"bus_clk" Master/Core clock, have to be >= 125 MHz for SS
|
||||
operation and >= 60MHz for HS operation
|
||||
|
||||
"ref_clk" Clock source to core during PHY power down
|
||||
|
||||
Required child node:
|
||||
A child node must exist to represent the core DWC3 IP block. The name of
|
||||
the node is not important. The content of the node is defined in dwc3.txt.
|
||||
|
||||
Example device node:
|
||||
|
||||
usb@0 {
|
||||
#address-cells = <0x2>;
|
||||
#size-cells = <0x1>;
|
||||
status = "okay";
|
||||
compatible = "xlnx,zynqmp-dwc3";
|
||||
clock-names = "bus_clk" "ref_clk";
|
||||
clocks = <&clk125>, <&clk125>;
|
||||
ranges;
|
||||
|
||||
dwc3@fe200000 {
|
||||
compatible = "snps,dwc3";
|
||||
reg = <0x0 0xfe200000 0x40000>;
|
||||
interrupts = <0x0 0x41 0x4>;
|
||||
dr_mode = "host";
|
||||
};
|
||||
};
|
51
Documentation/devicetree/bindings/usb/mt8173-xhci.txt
Normal file
51
Documentation/devicetree/bindings/usb/mt8173-xhci.txt
Normal 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>;
|
||||
};
|
23
Documentation/devicetree/bindings/usb/renesas_usb3.txt
Normal file
23
Documentation/devicetree/bindings/usb/renesas_usb3.txt
Normal file
|
@ -0,0 +1,23 @@
|
|||
Renesas Electronics USB3.0 Peripheral driver
|
||||
|
||||
Required properties:
|
||||
- compatible: Must contain one of the following:
|
||||
- "renesas,r8a7795-usb3-peri"
|
||||
- reg: Base address and length of the register for the USB3.0 Peripheral
|
||||
- interrupts: Interrupt specifier for the USB3.0 Peripheral
|
||||
- clocks: clock phandle and specifier pair
|
||||
|
||||
Example:
|
||||
usb3_peri0: usb@ee020000 {
|
||||
compatible = "renesas,r8a7795-usb3-peri";
|
||||
reg = <0 0xee020000 0 0x400>;
|
||||
interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&cpg CPG_MOD 328>;
|
||||
};
|
||||
|
||||
usb3_peri1: usb@ee060000 {
|
||||
compatible = "renesas,r8a7795-usb3-peri";
|
||||
reg = <0 0xee060000 0 0x400>;
|
||||
interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&cpg CPG_MOD 327>;
|
||||
};
|
|
@ -1,11 +1,21 @@
|
|||
Renesas Electronics USBHS driver
|
||||
|
||||
Required properties:
|
||||
- compatible: Must contain one of the following:
|
||||
- "renesas,usbhs-r8a7790"
|
||||
- "renesas,usbhs-r8a7791"
|
||||
- "renesas,usbhs-r8a7794"
|
||||
- "renesas,usbhs-r8a7795"
|
||||
- compatible: Must contain one or more of the following:
|
||||
|
||||
- "renesas,usbhs-r8a7790" for r8a7790 (R-Car H2) compatible device
|
||||
- "renesas,usbhs-r8a7791" for r8a7791 (R-Car M2-W) compatible device
|
||||
- "renesas,usbhs-r8a7792" for r8a7792 (R-Car V2H) compatible device
|
||||
- "renesas,usbhs-r8a7793" for r8a7793 (R-Car M2-N) compatible device
|
||||
- "renesas,usbhs-r8a7794" for r8a7794 (R-Car E2) compatible device
|
||||
- "renesas,usbhs-r8a7795" for r8a7795 (R-Car H3) compatible device
|
||||
- "renesas,rcar-gen2-usbhs" for R-Car Gen2 compatible device
|
||||
- "renesas,rcar-gen3-usbhs" for R-Car Gen3 compatible device
|
||||
|
||||
When compatible with the generic version, nodes must list the
|
||||
SoC-specific version corresponding to the platform first followed
|
||||
by the generic version.
|
||||
|
||||
- reg: Base address and length of the register for the USBHS
|
||||
- interrupts: Interrupt specifier for the USBHS
|
||||
- clocks: A list of phandle + clock specifier pairs
|
||||
|
@ -22,7 +32,7 @@ Optional properties:
|
|||
|
||||
Example:
|
||||
usbhs: usb@e6590000 {
|
||||
compatible = "renesas,usbhs-r8a7790";
|
||||
compatible = "renesas,usbhs-r8a7790", "renesas,rcar-gen2-usbhs";
|
||||
reg = <0 0xe6590000 0 0x100>;
|
||||
interrupts = <0 107 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&mstp7_clks R8A7790_CLK_HSUSB>;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
-------------------------------
|
||||
|
|
|
@ -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
|
||||
----------------------
|
||||
|
|
10
MAINTAINERS
10
MAINTAINERS
|
@ -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
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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");
|
||||
|
|
168
drivers/phy/phy-hi6220-usb.c
Normal file
168
drivers/phy/phy-hi6220-usb.c
Normal 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");
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
378
drivers/phy/phy-rcar-gen3-usb2.c
Normal file
378
drivers/phy/phy-rcar-gen3-usb2.c
Normal 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>");
|
|
@ -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", ®_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", ®_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 },
|
||||
{}
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#include "ci.h"
|
||||
#include "udc.h"
|
||||
#include "bits.h"
|
||||
#include "debug.h"
|
||||
#include "otg.h"
|
||||
|
||||
/**
|
||||
|
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -481,32 +481,19 @@ static void dwc2_init_fs_ls_pclk_sel(struct dwc2_hsotg *hsotg)
|
|||
* Do core a soft reset of the core. Be careful with this because it
|
||||
* resets all the internal state machines of the core.
|
||||
*/
|
||||
static int dwc2_core_reset(struct dwc2_hsotg *hsotg)
|
||||
int dwc2_core_reset(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 greset;
|
||||
int count = 0;
|
||||
u32 gusbcfg;
|
||||
|
||||
dev_vdbg(hsotg->dev, "%s()\n", __func__);
|
||||
|
||||
/* Wait for AHB master IDLE state */
|
||||
do {
|
||||
usleep_range(20000, 40000);
|
||||
greset = dwc2_readl(hsotg->regs + GRSTCTL);
|
||||
if (++count > 50) {
|
||||
dev_warn(hsotg->dev,
|
||||
"%s() HANG! AHB Idle GRSTCTL=%0x\n",
|
||||
__func__, greset);
|
||||
return -EBUSY;
|
||||
}
|
||||
} while (!(greset & GRSTCTL_AHBIDLE));
|
||||
|
||||
/* Core Soft Reset */
|
||||
count = 0;
|
||||
greset = dwc2_readl(hsotg->regs + GRSTCTL);
|
||||
greset |= GRSTCTL_CSFTRST;
|
||||
dwc2_writel(greset, hsotg->regs + GRSTCTL);
|
||||
do {
|
||||
usleep_range(20000, 40000);
|
||||
udelay(1);
|
||||
greset = dwc2_readl(hsotg->regs + GRSTCTL);
|
||||
if (++count > 50) {
|
||||
dev_warn(hsotg->dev,
|
||||
|
@ -516,29 +503,146 @@ static int dwc2_core_reset(struct dwc2_hsotg *hsotg)
|
|||
}
|
||||
} while (greset & GRSTCTL_CSFTRST);
|
||||
|
||||
if (hsotg->dr_mode == USB_DR_MODE_HOST) {
|
||||
gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
|
||||
gusbcfg &= ~GUSBCFG_FORCEDEVMODE;
|
||||
gusbcfg |= GUSBCFG_FORCEHOSTMODE;
|
||||
dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
|
||||
} else if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) {
|
||||
gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
|
||||
gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
|
||||
gusbcfg |= GUSBCFG_FORCEDEVMODE;
|
||||
dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
|
||||
} else if (hsotg->dr_mode == USB_DR_MODE_OTG) {
|
||||
gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
|
||||
gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
|
||||
gusbcfg &= ~GUSBCFG_FORCEDEVMODE;
|
||||
dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
|
||||
}
|
||||
/* Wait for AHB master IDLE state */
|
||||
count = 0;
|
||||
do {
|
||||
udelay(1);
|
||||
greset = dwc2_readl(hsotg->regs + GRSTCTL);
|
||||
if (++count > 50) {
|
||||
dev_warn(hsotg->dev,
|
||||
"%s() HANG! AHB Idle GRSTCTL=%0x\n",
|
||||
__func__, greset);
|
||||
return -EBUSY;
|
||||
}
|
||||
} while (!(greset & GRSTCTL_AHBIDLE));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Force the mode of the controller.
|
||||
*
|
||||
* Forcing the mode is needed for two cases:
|
||||
*
|
||||
* 1) If the dr_mode is set to either HOST or PERIPHERAL we force the
|
||||
* controller to stay in a particular mode regardless of ID pin
|
||||
* changes. We do this usually after a core reset.
|
||||
*
|
||||
* 2) During probe we want to read reset values of the hw
|
||||
* configuration registers that are only available in either host or
|
||||
* device mode. We may need to force the mode if the current mode does
|
||||
* not allow us to access the register in the mode that we want.
|
||||
*
|
||||
* In either case it only makes sense to force the mode if the
|
||||
* controller hardware is OTG capable.
|
||||
*
|
||||
* Checks are done in this function to determine whether doing a force
|
||||
* would be valid or not.
|
||||
*
|
||||
* If a force is done, it requires a 25ms delay to take effect.
|
||||
*
|
||||
* Returns true if the mode was forced.
|
||||
*/
|
||||
static bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
|
||||
{
|
||||
u32 gusbcfg;
|
||||
u32 set;
|
||||
u32 clear;
|
||||
|
||||
dev_dbg(hsotg->dev, "Forcing mode to %s\n", host ? "host" : "device");
|
||||
|
||||
/*
|
||||
* Force mode has no effect if the hardware is not OTG.
|
||||
*/
|
||||
if (!dwc2_hw_is_otg(hsotg))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* If dr_mode is either peripheral or host only, there is no
|
||||
* need to ever force the mode to the opposite mode.
|
||||
*/
|
||||
if (WARN_ON(host && hsotg->dr_mode == USB_DR_MODE_PERIPHERAL))
|
||||
return false;
|
||||
|
||||
if (WARN_ON(!host && hsotg->dr_mode == USB_DR_MODE_HOST))
|
||||
return false;
|
||||
|
||||
gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
|
||||
|
||||
set = host ? GUSBCFG_FORCEHOSTMODE : GUSBCFG_FORCEDEVMODE;
|
||||
clear = host ? GUSBCFG_FORCEDEVMODE : GUSBCFG_FORCEHOSTMODE;
|
||||
|
||||
/*
|
||||
* If the force mode bit is already set, don't set it.
|
||||
*/
|
||||
if ((gusbcfg & set) && !(gusbcfg & clear))
|
||||
return false;
|
||||
|
||||
gusbcfg &= ~clear;
|
||||
gusbcfg |= set;
|
||||
dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
|
||||
|
||||
msleep(25);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clears the force mode bits.
|
||||
*/
|
||||
static void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 gusbcfg;
|
||||
|
||||
gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
|
||||
gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
|
||||
gusbcfg &= ~GUSBCFG_FORCEDEVMODE;
|
||||
dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
|
||||
|
||||
/*
|
||||
* NOTE: This long sleep is _very_ important, otherwise the core will
|
||||
* not stay in host mode after a connector ID change!
|
||||
*/
|
||||
usleep_range(150000, 200000);
|
||||
msleep(25);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets or clears force mode based on the dr_mode parameter.
|
||||
*/
|
||||
void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
switch (hsotg->dr_mode) {
|
||||
case USB_DR_MODE_HOST:
|
||||
dwc2_force_mode(hsotg, true);
|
||||
break;
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
dwc2_force_mode(hsotg, false);
|
||||
break;
|
||||
case USB_DR_MODE_OTG:
|
||||
dwc2_clear_force_mode(hsotg);
|
||||
break;
|
||||
default:
|
||||
dev_warn(hsotg->dev, "%s() Invalid dr_mode=%d\n",
|
||||
__func__, hsotg->dr_mode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Do core a soft reset of the core. Be careful with this because it
|
||||
* resets all the internal state machines of the core.
|
||||
*
|
||||
* Additionally this will apply force mode as per the hsotg->dr_mode
|
||||
* parameter.
|
||||
*/
|
||||
int dwc2_core_reset_and_force_dr_mode(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = dwc2_core_reset(hsotg);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
dwc2_force_dr_mode(hsotg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -553,16 +657,20 @@ static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
|
|||
*/
|
||||
if (select_phy) {
|
||||
dev_dbg(hsotg->dev, "FS PHY selected\n");
|
||||
usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
|
||||
usbcfg |= GUSBCFG_PHYSEL;
|
||||
dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
|
||||
|
||||
/* Reset after a PHY select */
|
||||
retval = dwc2_core_reset(hsotg);
|
||||
if (retval) {
|
||||
dev_err(hsotg->dev, "%s() Reset failed, aborting",
|
||||
__func__);
|
||||
return retval;
|
||||
usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
|
||||
if (!(usbcfg & GUSBCFG_PHYSEL)) {
|
||||
usbcfg |= GUSBCFG_PHYSEL;
|
||||
dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
|
||||
|
||||
/* Reset after a PHY select */
|
||||
retval = dwc2_core_reset_and_force_dr_mode(hsotg);
|
||||
|
||||
if (retval) {
|
||||
dev_err(hsotg->dev,
|
||||
"%s: Reset failed, aborting", __func__);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -597,13 +705,13 @@ static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
|
|||
|
||||
static int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
|
||||
{
|
||||
u32 usbcfg;
|
||||
u32 usbcfg, usbcfg_old;
|
||||
int retval = 0;
|
||||
|
||||
if (!select_phy)
|
||||
return 0;
|
||||
|
||||
usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
|
||||
usbcfg = usbcfg_old = dwc2_readl(hsotg->regs + GUSBCFG);
|
||||
|
||||
/*
|
||||
* HS PHY parameters. These parameters are preserved during soft reset
|
||||
|
@ -631,14 +739,16 @@ static int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
|
|||
break;
|
||||
}
|
||||
|
||||
dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
|
||||
if (usbcfg != usbcfg_old) {
|
||||
dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
|
||||
|
||||
/* Reset after setting the PHY parameters */
|
||||
retval = dwc2_core_reset(hsotg);
|
||||
if (retval) {
|
||||
dev_err(hsotg->dev, "%s() Reset failed, aborting",
|
||||
__func__);
|
||||
return retval;
|
||||
/* Reset after setting the PHY parameters */
|
||||
retval = dwc2_core_reset_and_force_dr_mode(hsotg);
|
||||
if (retval) {
|
||||
dev_err(hsotg->dev,
|
||||
"%s: Reset failed, aborting", __func__);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
|
@ -765,11 +875,10 @@ static void dwc2_gusbcfg_init(struct dwc2_hsotg *hsotg)
|
|||
* dwc2_core_init() - Initializes the DWC_otg controller registers and
|
||||
* prepares the core for device mode or host mode operation
|
||||
*
|
||||
* @hsotg: Programming view of the DWC_otg controller
|
||||
* @select_phy: If true then also set the Phy type
|
||||
* @irq: If >= 0, the irq to register
|
||||
* @hsotg: Programming view of the DWC_otg controller
|
||||
* @initial_setup: If true then this is the first init for this instance.
|
||||
*/
|
||||
int dwc2_core_init(struct dwc2_hsotg *hsotg, bool select_phy, int irq)
|
||||
int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup)
|
||||
{
|
||||
u32 usbcfg, otgctl;
|
||||
int retval;
|
||||
|
@ -791,18 +900,26 @@ int dwc2_core_init(struct dwc2_hsotg *hsotg, bool select_phy, int irq)
|
|||
|
||||
dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
|
||||
|
||||
/* Reset the Controller */
|
||||
retval = dwc2_core_reset(hsotg);
|
||||
if (retval) {
|
||||
dev_err(hsotg->dev, "%s(): Reset failed, aborting\n",
|
||||
__func__);
|
||||
return retval;
|
||||
/*
|
||||
* Reset the Controller
|
||||
*
|
||||
* We only need to reset the controller if this is a re-init.
|
||||
* For the first init we know for sure that earlier code reset us (it
|
||||
* needed to in order to properly detect various parameters).
|
||||
*/
|
||||
if (!initial_setup) {
|
||||
retval = dwc2_core_reset_and_force_dr_mode(hsotg);
|
||||
if (retval) {
|
||||
dev_err(hsotg->dev, "%s(): Reset failed, aborting\n",
|
||||
__func__);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This needs to happen in FS mode before any other programming occurs
|
||||
*/
|
||||
retval = dwc2_phy_init(hsotg, select_phy);
|
||||
retval = dwc2_phy_init(hsotg, initial_setup);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
|
@ -1707,6 +1824,7 @@ void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
|
|||
u32 hcchar;
|
||||
u32 hctsiz = 0;
|
||||
u16 num_packets;
|
||||
u32 ec_mc;
|
||||
|
||||
if (dbg_hc(chan))
|
||||
dev_vdbg(hsotg->dev, "%s()\n", __func__);
|
||||
|
@ -1743,6 +1861,13 @@ void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
|
|||
|
||||
hctsiz |= chan->xfer_len << TSIZ_XFERSIZE_SHIFT &
|
||||
TSIZ_XFERSIZE_MASK;
|
||||
|
||||
/* For split set ec_mc for immediate retries */
|
||||
if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
|
||||
chan->ep_type == USB_ENDPOINT_XFER_ISOC)
|
||||
ec_mc = 3;
|
||||
else
|
||||
ec_mc = 1;
|
||||
} else {
|
||||
if (dbg_hc(chan))
|
||||
dev_vdbg(hsotg->dev, "no split\n");
|
||||
|
@ -1805,6 +1930,9 @@ void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
|
|||
|
||||
hctsiz |= chan->xfer_len << TSIZ_XFERSIZE_SHIFT &
|
||||
TSIZ_XFERSIZE_MASK;
|
||||
|
||||
/* The ec_mc gets the multi_count for non-split */
|
||||
ec_mc = chan->multi_count;
|
||||
}
|
||||
|
||||
chan->start_pkt_count = num_packets;
|
||||
|
@ -1855,8 +1983,7 @@ void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
|
|||
|
||||
hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num));
|
||||
hcchar &= ~HCCHAR_MULTICNT_MASK;
|
||||
hcchar |= chan->multi_count << HCCHAR_MULTICNT_SHIFT &
|
||||
HCCHAR_MULTICNT_MASK;
|
||||
hcchar |= (ec_mc << HCCHAR_MULTICNT_SHIFT) & HCCHAR_MULTICNT_MASK;
|
||||
dwc2_hc_set_even_odd_frame(hsotg, chan, &hcchar);
|
||||
|
||||
if (hcchar & HCCHAR_CHDIS)
|
||||
|
@ -1905,7 +2032,6 @@ void dwc2_hc_start_transfer_ddma(struct dwc2_hsotg *hsotg,
|
|||
struct dwc2_host_chan *chan)
|
||||
{
|
||||
u32 hcchar;
|
||||
u32 hc_dma;
|
||||
u32 hctsiz = 0;
|
||||
|
||||
if (chan->do_ping)
|
||||
|
@ -1934,14 +2060,14 @@ void dwc2_hc_start_transfer_ddma(struct dwc2_hsotg *hsotg,
|
|||
|
||||
dwc2_writel(hctsiz, hsotg->regs + HCTSIZ(chan->hc_num));
|
||||
|
||||
hc_dma = (u32)chan->desc_list_addr & HCDMA_DMA_ADDR_MASK;
|
||||
dma_sync_single_for_device(hsotg->dev, chan->desc_list_addr,
|
||||
chan->desc_list_sz, DMA_TO_DEVICE);
|
||||
|
||||
dwc2_writel(chan->desc_list_addr, hsotg->regs + HCDMA(chan->hc_num));
|
||||
|
||||
/* Always start from first descriptor */
|
||||
hc_dma &= ~HCDMA_CTD_MASK;
|
||||
dwc2_writel(hc_dma, hsotg->regs + HCDMA(chan->hc_num));
|
||||
if (dbg_hc(chan))
|
||||
dev_vdbg(hsotg->dev, "Wrote %08x to HCDMA(%d)\n",
|
||||
hc_dma, chan->hc_num);
|
||||
dev_vdbg(hsotg->dev, "Wrote %pad to HCDMA(%d)\n",
|
||||
&chan->desc_list_addr, chan->hc_num);
|
||||
|
||||
hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num));
|
||||
hcchar &= ~HCCHAR_MULTICNT_MASK;
|
||||
|
@ -2485,6 +2611,29 @@ void dwc2_set_param_dma_desc_enable(struct dwc2_hsotg *hsotg, int val)
|
|||
hsotg->core_params->dma_desc_enable = val;
|
||||
}
|
||||
|
||||
void dwc2_set_param_dma_desc_fs_enable(struct dwc2_hsotg *hsotg, int val)
|
||||
{
|
||||
int valid = 1;
|
||||
|
||||
if (val > 0 && (hsotg->core_params->dma_enable <= 0 ||
|
||||
!hsotg->hw_params.dma_desc_enable))
|
||||
valid = 0;
|
||||
if (val < 0)
|
||||
valid = 0;
|
||||
|
||||
if (!valid) {
|
||||
if (val >= 0)
|
||||
dev_err(hsotg->dev,
|
||||
"%d invalid for dma_desc_fs_enable parameter. Check HW configuration.\n",
|
||||
val);
|
||||
val = (hsotg->core_params->dma_enable > 0 &&
|
||||
hsotg->hw_params.dma_desc_enable);
|
||||
}
|
||||
|
||||
hsotg->core_params->dma_desc_fs_enable = val;
|
||||
dev_dbg(hsotg->dev, "Setting dma_desc_fs_enable to %d\n", val);
|
||||
}
|
||||
|
||||
void dwc2_set_param_host_support_fs_ls_low_power(struct dwc2_hsotg *hsotg,
|
||||
int val)
|
||||
{
|
||||
|
@ -3016,6 +3165,7 @@ void dwc2_set_parameters(struct dwc2_hsotg *hsotg,
|
|||
dwc2_set_param_otg_cap(hsotg, params->otg_cap);
|
||||
dwc2_set_param_dma_enable(hsotg, params->dma_enable);
|
||||
dwc2_set_param_dma_desc_enable(hsotg, params->dma_desc_enable);
|
||||
dwc2_set_param_dma_desc_fs_enable(hsotg, params->dma_desc_fs_enable);
|
||||
dwc2_set_param_host_support_fs_ls_low_power(hsotg,
|
||||
params->host_support_fs_ls_low_power);
|
||||
dwc2_set_param_enable_dynamic_fifo(hsotg,
|
||||
|
@ -3052,17 +3202,93 @@ void dwc2_set_parameters(struct dwc2_hsotg *hsotg,
|
|||
dwc2_set_param_hibernation(hsotg, params->hibernation);
|
||||
}
|
||||
|
||||
/*
|
||||
* Forces either host or device mode if the controller is not
|
||||
* currently in that mode.
|
||||
*
|
||||
* Returns true if the mode was forced.
|
||||
*/
|
||||
static bool dwc2_force_mode_if_needed(struct dwc2_hsotg *hsotg, bool host)
|
||||
{
|
||||
if (host && dwc2_is_host_mode(hsotg))
|
||||
return false;
|
||||
else if (!host && dwc2_is_device_mode(hsotg))
|
||||
return false;
|
||||
|
||||
return dwc2_force_mode(hsotg, host);
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets host hardware parameters. Forces host mode if not currently in
|
||||
* host mode. Should be called immediately after a core soft reset in
|
||||
* order to get the reset values.
|
||||
*/
|
||||
static void dwc2_get_host_hwparams(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_hw_params *hw = &hsotg->hw_params;
|
||||
u32 gnptxfsiz;
|
||||
u32 hptxfsiz;
|
||||
bool forced;
|
||||
|
||||
if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)
|
||||
return;
|
||||
|
||||
forced = dwc2_force_mode_if_needed(hsotg, true);
|
||||
|
||||
gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ);
|
||||
hptxfsiz = dwc2_readl(hsotg->regs + HPTXFSIZ);
|
||||
dev_dbg(hsotg->dev, "gnptxfsiz=%08x\n", gnptxfsiz);
|
||||
dev_dbg(hsotg->dev, "hptxfsiz=%08x\n", hptxfsiz);
|
||||
|
||||
if (forced)
|
||||
dwc2_clear_force_mode(hsotg);
|
||||
|
||||
hw->host_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >>
|
||||
FIFOSIZE_DEPTH_SHIFT;
|
||||
hw->host_perio_tx_fifo_size = (hptxfsiz & FIFOSIZE_DEPTH_MASK) >>
|
||||
FIFOSIZE_DEPTH_SHIFT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets device hardware parameters. Forces device mode if not
|
||||
* currently in device mode. Should be called immediately after a core
|
||||
* soft reset in order to get the reset values.
|
||||
*/
|
||||
static void dwc2_get_dev_hwparams(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_hw_params *hw = &hsotg->hw_params;
|
||||
bool forced;
|
||||
u32 gnptxfsiz;
|
||||
|
||||
if (hsotg->dr_mode == USB_DR_MODE_HOST)
|
||||
return;
|
||||
|
||||
forced = dwc2_force_mode_if_needed(hsotg, false);
|
||||
|
||||
gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ);
|
||||
dev_dbg(hsotg->dev, "gnptxfsiz=%08x\n", gnptxfsiz);
|
||||
|
||||
if (forced)
|
||||
dwc2_clear_force_mode(hsotg);
|
||||
|
||||
hw->dev_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >>
|
||||
FIFOSIZE_DEPTH_SHIFT;
|
||||
}
|
||||
|
||||
/**
|
||||
* During device initialization, read various hardware configuration
|
||||
* registers and interpret the contents.
|
||||
*
|
||||
* This should be called during driver probe. It will perform a core
|
||||
* soft reset in order to get the reset values of the parameters.
|
||||
*/
|
||||
int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_hw_params *hw = &hsotg->hw_params;
|
||||
unsigned width;
|
||||
u32 hwcfg1, hwcfg2, hwcfg3, hwcfg4;
|
||||
u32 hptxfsiz, grxfsiz, gnptxfsiz;
|
||||
u32 gusbcfg;
|
||||
u32 grxfsiz;
|
||||
int retval;
|
||||
|
||||
/*
|
||||
* Attempt to ensure this device is really a DWC_otg Controller.
|
||||
|
@ -3082,6 +3308,10 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
|
|||
hw->snpsid >> 12 & 0xf, hw->snpsid >> 8 & 0xf,
|
||||
hw->snpsid >> 4 & 0xf, hw->snpsid & 0xf, hw->snpsid);
|
||||
|
||||
retval = dwc2_core_reset(hsotg);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
hwcfg1 = dwc2_readl(hsotg->regs + GHWCFG1);
|
||||
hwcfg2 = dwc2_readl(hsotg->regs + GHWCFG2);
|
||||
hwcfg3 = dwc2_readl(hsotg->regs + GHWCFG3);
|
||||
|
@ -3094,20 +3324,16 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
|
|||
dev_dbg(hsotg->dev, "hwcfg4=%08x\n", hwcfg4);
|
||||
dev_dbg(hsotg->dev, "grxfsiz=%08x\n", grxfsiz);
|
||||
|
||||
/* Force host mode to get HPTXFSIZ / GNPTXFSIZ exact power on value */
|
||||
gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
|
||||
gusbcfg |= GUSBCFG_FORCEHOSTMODE;
|
||||
dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
|
||||
usleep_range(100000, 150000);
|
||||
/*
|
||||
* Host specific hardware parameters. Reading these parameters
|
||||
* requires the controller to be in host mode. The mode will
|
||||
* be forced, if necessary, to read these values.
|
||||
*/
|
||||
dwc2_get_host_hwparams(hsotg);
|
||||
dwc2_get_dev_hwparams(hsotg);
|
||||
|
||||
gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ);
|
||||
hptxfsiz = dwc2_readl(hsotg->regs + HPTXFSIZ);
|
||||
dev_dbg(hsotg->dev, "gnptxfsiz=%08x\n", gnptxfsiz);
|
||||
dev_dbg(hsotg->dev, "hptxfsiz=%08x\n", hptxfsiz);
|
||||
gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
|
||||
gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
|
||||
dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
|
||||
usleep_range(100000, 150000);
|
||||
/* hwcfg1 */
|
||||
hw->dev_ep_dirs = hwcfg1;
|
||||
|
||||
/* hwcfg2 */
|
||||
hw->op_mode = (hwcfg2 & GHWCFG2_OP_MODE_MASK) >>
|
||||
|
@ -3163,10 +3389,6 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
|
|||
/* fifo sizes */
|
||||
hw->host_rx_fifo_size = (grxfsiz & GRXFSIZ_DEPTH_MASK) >>
|
||||
GRXFSIZ_DEPTH_SHIFT;
|
||||
hw->host_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >>
|
||||
FIFOSIZE_DEPTH_SHIFT;
|
||||
hw->host_perio_tx_fifo_size = (hptxfsiz & FIFOSIZE_DEPTH_MASK) >>
|
||||
FIFOSIZE_DEPTH_SHIFT;
|
||||
|
||||
dev_dbg(hsotg->dev, "Detected values from hardware:\n");
|
||||
dev_dbg(hsotg->dev, " op_mode=%d\n",
|
||||
|
@ -3275,6 +3497,43 @@ void dwc2_disable_global_interrupts(struct dwc2_hsotg *hsotg)
|
|||
dwc2_writel(ahbcfg, hsotg->regs + GAHBCFG);
|
||||
}
|
||||
|
||||
/* Returns the controller's GHWCFG2.OTG_MODE. */
|
||||
unsigned dwc2_op_mode(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 ghwcfg2 = dwc2_readl(hsotg->regs + GHWCFG2);
|
||||
|
||||
return (ghwcfg2 & GHWCFG2_OP_MODE_MASK) >>
|
||||
GHWCFG2_OP_MODE_SHIFT;
|
||||
}
|
||||
|
||||
/* Returns true if the controller is capable of DRD. */
|
||||
bool dwc2_hw_is_otg(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
unsigned op_mode = dwc2_op_mode(hsotg);
|
||||
|
||||
return (op_mode == GHWCFG2_OP_MODE_HNP_SRP_CAPABLE) ||
|
||||
(op_mode == GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE) ||
|
||||
(op_mode == GHWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE);
|
||||
}
|
||||
|
||||
/* Returns true if the controller is host-only. */
|
||||
bool dwc2_hw_is_host(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
unsigned op_mode = dwc2_op_mode(hsotg);
|
||||
|
||||
return (op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_HOST) ||
|
||||
(op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST);
|
||||
}
|
||||
|
||||
/* Returns true if the controller is device-only. */
|
||||
bool dwc2_hw_is_device(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
unsigned op_mode = dwc2_op_mode(hsotg);
|
||||
|
||||
return (op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE) ||
|
||||
(op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE);
|
||||
}
|
||||
|
||||
MODULE_DESCRIPTION("DESIGNWARE HS OTG Core");
|
||||
MODULE_AUTHOR("Synopsys, Inc.");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
|
|
|
@ -246,6 +246,13 @@ enum dwc2_ep0_state {
|
|||
* value for this if none is specified.
|
||||
* 0 - Address DMA
|
||||
* 1 - Descriptor DMA (default, if available)
|
||||
* @dma_desc_fs_enable: When DMA mode is enabled, specifies whether to use
|
||||
* address DMA mode or descriptor DMA mode for accessing
|
||||
* the data FIFOs in Full Speed mode only. The driver
|
||||
* will automatically detect the value for this if none is
|
||||
* specified.
|
||||
* 0 - Address DMA
|
||||
* 1 - Descriptor DMA in FS (default, if available)
|
||||
* @speed: Specifies the maximum speed of operation in host and
|
||||
* device mode. The actual speed depends on the speed of
|
||||
* the attached device and the value of phy_type.
|
||||
|
@ -375,6 +382,7 @@ struct dwc2_core_params {
|
|||
int otg_ver;
|
||||
int dma_enable;
|
||||
int dma_desc_enable;
|
||||
int dma_desc_fs_enable;
|
||||
int speed;
|
||||
int enable_dynamic_fifo;
|
||||
int en_multiple_tx_fifo;
|
||||
|
@ -451,15 +459,18 @@ struct dwc2_core_params {
|
|||
* 1 - 16 bits
|
||||
* 2 - 8 or 16 bits
|
||||
* @snpsid: Value from SNPSID register
|
||||
* @dev_ep_dirs: Direction of device endpoints (GHWCFG1)
|
||||
*/
|
||||
struct dwc2_hw_params {
|
||||
unsigned op_mode:3;
|
||||
unsigned arch:2;
|
||||
unsigned dma_desc_enable:1;
|
||||
unsigned dma_desc_fs_enable:1;
|
||||
unsigned enable_dynamic_fifo:1;
|
||||
unsigned en_multiple_tx_fifo:1;
|
||||
unsigned host_rx_fifo_size:16;
|
||||
unsigned host_nperio_tx_fifo_size:16;
|
||||
unsigned dev_nperio_tx_fifo_size:16;
|
||||
unsigned host_perio_tx_fifo_size:16;
|
||||
unsigned nperio_tx_q_depth:3;
|
||||
unsigned host_perio_tx_q_depth:3;
|
||||
|
@ -476,6 +487,7 @@ struct dwc2_hw_params {
|
|||
unsigned power_optimized:1;
|
||||
unsigned utmi_phy_data_width:2;
|
||||
u32 snpsid;
|
||||
u32 dev_ep_dirs;
|
||||
};
|
||||
|
||||
/* Size of control and EP0 buffers */
|
||||
|
@ -676,6 +688,9 @@ struct dwc2_hregs_backup {
|
|||
* @otg_port: OTG port number
|
||||
* @frame_list: Frame list
|
||||
* @frame_list_dma: Frame list DMA address
|
||||
* @frame_list_sz: Frame list size
|
||||
* @desc_gen_cache: Kmem cache for generic descriptors
|
||||
* @desc_hsisoc_cache: Kmem cache for hs isochronous descriptors
|
||||
*
|
||||
* These are for peripheral mode:
|
||||
*
|
||||
|
@ -770,6 +785,7 @@ struct dwc2_hsotg {
|
|||
u16 frame_number;
|
||||
u16 periodic_qh_count;
|
||||
bool bus_suspended;
|
||||
bool new_connection;
|
||||
|
||||
#ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
|
||||
#define FRAME_NUM_ARRAY_SIZE 1000
|
||||
|
@ -794,6 +810,9 @@ struct dwc2_hsotg {
|
|||
u8 otg_port;
|
||||
u32 *frame_list;
|
||||
dma_addr_t frame_list_dma;
|
||||
u32 frame_list_sz;
|
||||
struct kmem_cache *desc_gen_cache;
|
||||
struct kmem_cache *desc_hsisoc_cache;
|
||||
|
||||
#ifdef DEBUG
|
||||
u32 frrem_samples;
|
||||
|
@ -864,10 +883,14 @@ enum dwc2_halt_status {
|
|||
* The following functions support initialization of the core driver component
|
||||
* and the DWC_otg controller
|
||||
*/
|
||||
extern int dwc2_core_reset(struct dwc2_hsotg *hsotg);
|
||||
extern int dwc2_core_reset_and_force_dr_mode(struct dwc2_hsotg *hsotg);
|
||||
extern void dwc2_core_host_init(struct dwc2_hsotg *hsotg);
|
||||
extern int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg);
|
||||
extern int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore);
|
||||
|
||||
void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg);
|
||||
|
||||
/*
|
||||
* Host core Functions.
|
||||
* The following functions support managing the DWC_otg controller in host
|
||||
|
@ -901,7 +924,7 @@ extern void dwc2_read_packet(struct dwc2_hsotg *hsotg, u8 *dest, u16 bytes);
|
|||
extern void dwc2_flush_tx_fifo(struct dwc2_hsotg *hsotg, const int num);
|
||||
extern void dwc2_flush_rx_fifo(struct dwc2_hsotg *hsotg);
|
||||
|
||||
extern int dwc2_core_init(struct dwc2_hsotg *hsotg, bool select_phy, int irq);
|
||||
extern int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup);
|
||||
extern void dwc2_enable_global_interrupts(struct dwc2_hsotg *hcd);
|
||||
extern void dwc2_disable_global_interrupts(struct dwc2_hsotg *hcd);
|
||||
|
||||
|
@ -941,6 +964,16 @@ extern void dwc2_set_param_dma_enable(struct dwc2_hsotg *hsotg, int val);
|
|||
*/
|
||||
extern void dwc2_set_param_dma_desc_enable(struct dwc2_hsotg *hsotg, int val);
|
||||
|
||||
/*
|
||||
* When DMA mode is enabled specifies whether to use
|
||||
* address DMA or DMA Descritor mode with full speed devices
|
||||
* for accessing the data FIFOs in host mode.
|
||||
* 0 - address DMA
|
||||
* 1 - FS DMA Descriptor(default, if available)
|
||||
*/
|
||||
extern void dwc2_set_param_dma_desc_fs_enable(struct dwc2_hsotg *hsotg,
|
||||
int val);
|
||||
|
||||
/*
|
||||
* Specifies the maximum speed of operation in host and device mode.
|
||||
* The actual speed depends on the speed of the attached device and
|
||||
|
@ -1109,6 +1142,31 @@ extern int dwc2_get_hwparams(struct dwc2_hsotg *hsotg);
|
|||
extern int dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg);
|
||||
extern int dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg);
|
||||
|
||||
/*
|
||||
* The following functions check the controller's OTG operation mode
|
||||
* capability (GHWCFG2.OTG_MODE).
|
||||
*
|
||||
* These functions can be used before the internal hsotg->hw_params
|
||||
* are read in and cached so they always read directly from the
|
||||
* GHWCFG2 register.
|
||||
*/
|
||||
unsigned dwc2_op_mode(struct dwc2_hsotg *hsotg);
|
||||
bool dwc2_hw_is_otg(struct dwc2_hsotg *hsotg);
|
||||
bool dwc2_hw_is_host(struct dwc2_hsotg *hsotg);
|
||||
bool dwc2_hw_is_device(struct dwc2_hsotg *hsotg);
|
||||
|
||||
/*
|
||||
* Returns the mode of operation, host or device
|
||||
*/
|
||||
static inline int dwc2_is_host_mode(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
return (dwc2_readl(hsotg->regs + GINTSTS) & GINTSTS_CURMODE_HOST) != 0;
|
||||
}
|
||||
static inline int dwc2_is_device_mode(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
return (dwc2_readl(hsotg->regs + GINTSTS) & GINTSTS_CURMODE_HOST) == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Dump core registers and SPRAM
|
||||
*/
|
||||
|
@ -1154,12 +1212,14 @@ static inline int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg,
|
|||
|
||||
#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
|
||||
extern int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg);
|
||||
extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg);
|
||||
extern void dwc2_hcd_connect(struct dwc2_hsotg *hsotg);
|
||||
extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force);
|
||||
extern void dwc2_hcd_start(struct dwc2_hsotg *hsotg);
|
||||
#else
|
||||
static inline int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg)
|
||||
{ return 0; }
|
||||
static inline void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg) {}
|
||||
static inline void dwc2_hcd_connect(struct dwc2_hsotg *hsotg) {}
|
||||
static inline void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force) {}
|
||||
static inline void dwc2_hcd_start(struct dwc2_hsotg *hsotg) {}
|
||||
static inline void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) {}
|
||||
static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
|
||||
|
|
|
@ -86,9 +86,6 @@ static void dwc2_handle_usb_port_intr(struct dwc2_hsotg *hsotg)
|
|||
hprt0 &= ~HPRT0_ENA;
|
||||
dwc2_writel(hprt0, hsotg->regs + HPRT0);
|
||||
}
|
||||
|
||||
/* Clear interrupt */
|
||||
dwc2_writel(GINTSTS_PRTINT, hsotg->regs + GINTSTS);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -98,11 +95,11 @@ static void dwc2_handle_usb_port_intr(struct dwc2_hsotg *hsotg)
|
|||
*/
|
||||
static void dwc2_handle_mode_mismatch_intr(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
dev_warn(hsotg->dev, "Mode Mismatch Interrupt: currently in %s mode\n",
|
||||
dwc2_is_host_mode(hsotg) ? "Host" : "Device");
|
||||
|
||||
/* Clear interrupt */
|
||||
dwc2_writel(GINTSTS_MODEMIS, hsotg->regs + GINTSTS);
|
||||
|
||||
dev_warn(hsotg->dev, "Mode Mismatch Interrupt: currently in %s mode\n",
|
||||
dwc2_is_host_mode(hsotg) ? "Host" : "Device");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -239,7 +236,7 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
|
|||
dev_dbg(hsotg->dev, "a_suspend->a_peripheral (%d)\n",
|
||||
hsotg->op_state);
|
||||
spin_unlock(&hsotg->lock);
|
||||
dwc2_hcd_disconnect(hsotg);
|
||||
dwc2_hcd_disconnect(hsotg, false);
|
||||
spin_lock(&hsotg->lock);
|
||||
hsotg->op_state = OTG_STATE_A_PERIPHERAL;
|
||||
} else {
|
||||
|
@ -276,9 +273,13 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
|
|||
*/
|
||||
static void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
|
||||
u32 gintmsk;
|
||||
|
||||
/* Clear interrupt */
|
||||
dwc2_writel(GINTSTS_CONIDSTSCHNG, hsotg->regs + GINTSTS);
|
||||
|
||||
/* Need to disable SOF interrupt immediately */
|
||||
gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
|
||||
gintmsk &= ~GINTSTS_SOF;
|
||||
dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
|
||||
|
||||
|
@ -295,9 +296,6 @@ static void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg)
|
|||
queue_work(hsotg->wq_otg, &hsotg->wf_otg);
|
||||
spin_lock(&hsotg->lock);
|
||||
}
|
||||
|
||||
/* Clear interrupt */
|
||||
dwc2_writel(GINTSTS_CONIDSTSCHNG, hsotg->regs + GINTSTS);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -315,12 +313,12 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
|
|||
{
|
||||
int ret;
|
||||
|
||||
dev_dbg(hsotg->dev, "Session request interrupt - lx_state=%d\n",
|
||||
hsotg->lx_state);
|
||||
|
||||
/* Clear interrupt */
|
||||
dwc2_writel(GINTSTS_SESSREQINT, hsotg->regs + GINTSTS);
|
||||
|
||||
dev_dbg(hsotg->dev, "Session request interrupt - lx_state=%d\n",
|
||||
hsotg->lx_state);
|
||||
|
||||
if (dwc2_is_device_mode(hsotg)) {
|
||||
if (hsotg->lx_state == DWC2_L2) {
|
||||
ret = dwc2_exit_hibernation(hsotg, true);
|
||||
|
@ -347,6 +345,10 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
|
|||
static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Clear interrupt */
|
||||
dwc2_writel(GINTSTS_WKUPINT, hsotg->regs + GINTSTS);
|
||||
|
||||
dev_dbg(hsotg->dev, "++Resume or Remote Wakeup Detected Interrupt++\n");
|
||||
dev_dbg(hsotg->dev, "%s lxstate = %d\n", __func__, hsotg->lx_state);
|
||||
|
||||
|
@ -368,10 +370,9 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
|
|||
/* Change to L0 state */
|
||||
hsotg->lx_state = DWC2_L0;
|
||||
} else {
|
||||
if (hsotg->core_params->hibernation) {
|
||||
dwc2_writel(GINTSTS_WKUPINT, hsotg->regs + GINTSTS);
|
||||
if (hsotg->core_params->hibernation)
|
||||
return;
|
||||
}
|
||||
|
||||
if (hsotg->lx_state != DWC2_L1) {
|
||||
u32 pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
|
||||
|
||||
|
@ -385,9 +386,6 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
|
|||
hsotg->lx_state = DWC2_L0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear interrupt */
|
||||
dwc2_writel(GINTSTS_WKUPINT, hsotg->regs + GINTSTS);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -396,14 +394,14 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
|
|||
*/
|
||||
static void dwc2_handle_disconnect_intr(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
dwc2_writel(GINTSTS_DISCONNINT, hsotg->regs + GINTSTS);
|
||||
|
||||
dev_dbg(hsotg->dev, "++Disconnect Detected Interrupt++ (%s) %s\n",
|
||||
dwc2_is_host_mode(hsotg) ? "Host" : "Device",
|
||||
dwc2_op_state_str(hsotg));
|
||||
|
||||
if (hsotg->op_state == OTG_STATE_A_HOST)
|
||||
dwc2_hcd_disconnect(hsotg);
|
||||
|
||||
dwc2_writel(GINTSTS_DISCONNINT, hsotg->regs + GINTSTS);
|
||||
dwc2_hcd_disconnect(hsotg, false);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -419,6 +417,9 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
|
|||
u32 dsts;
|
||||
int ret;
|
||||
|
||||
/* Clear interrupt */
|
||||
dwc2_writel(GINTSTS_USBSUSP, hsotg->regs + GINTSTS);
|
||||
|
||||
dev_dbg(hsotg->dev, "USB SUSPEND\n");
|
||||
|
||||
if (dwc2_is_device_mode(hsotg)) {
|
||||
|
@ -437,7 +438,7 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
|
|||
if (!dwc2_is_device_connected(hsotg)) {
|
||||
dev_dbg(hsotg->dev,
|
||||
"ignore suspend request before enumeration\n");
|
||||
goto clear_int;
|
||||
return;
|
||||
}
|
||||
|
||||
ret = dwc2_enter_hibernation(hsotg);
|
||||
|
@ -476,10 +477,6 @@ skip_power_saving:
|
|||
hsotg->op_state = OTG_STATE_A_HOST;
|
||||
}
|
||||
}
|
||||
|
||||
clear_int:
|
||||
/* Clear interrupt */
|
||||
dwc2_writel(GINTSTS_USBSUSP, hsotg->regs + GINTSTS);
|
||||
}
|
||||
|
||||
#define GINTMSK_COMMON (GINTSTS_WKUPINT | GINTSTS_SESSREQINT | \
|
||||
|
|
|
@ -2095,7 +2095,7 @@ static void dwc2_hsotg_irq_enumdone(struct dwc2_hsotg *hsotg)
|
|||
*/
|
||||
|
||||
/* catch both EnumSpd_FS and EnumSpd_FS48 */
|
||||
switch (dsts & DSTS_ENUMSPD_MASK) {
|
||||
switch ((dsts & DSTS_ENUMSPD_MASK) >> DSTS_ENUMSPD_SHIFT) {
|
||||
case DSTS_ENUMSPD_FS:
|
||||
case DSTS_ENUMSPD_FS48:
|
||||
hsotg->gadget.speed = USB_SPEED_FULL;
|
||||
|
@ -2243,54 +2243,6 @@ static void dwc2_hsotg_irq_fifoempty(struct dwc2_hsotg *hsotg, bool periodic)
|
|||
GINTSTS_PTXFEMP | \
|
||||
GINTSTS_RXFLVL)
|
||||
|
||||
/**
|
||||
* dwc2_hsotg_corereset - issue softreset to the core
|
||||
* @hsotg: The device state
|
||||
*
|
||||
* Issue a soft reset to the core, and await the core finishing it.
|
||||
*/
|
||||
static int dwc2_hsotg_corereset(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
int timeout;
|
||||
u32 grstctl;
|
||||
|
||||
dev_dbg(hsotg->dev, "resetting core\n");
|
||||
|
||||
/* issue soft reset */
|
||||
dwc2_writel(GRSTCTL_CSFTRST, hsotg->regs + GRSTCTL);
|
||||
|
||||
timeout = 10000;
|
||||
do {
|
||||
grstctl = dwc2_readl(hsotg->regs + GRSTCTL);
|
||||
} while ((grstctl & GRSTCTL_CSFTRST) && timeout-- > 0);
|
||||
|
||||
if (grstctl & GRSTCTL_CSFTRST) {
|
||||
dev_err(hsotg->dev, "Failed to get CSftRst asserted\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
timeout = 10000;
|
||||
|
||||
while (1) {
|
||||
u32 grstctl = dwc2_readl(hsotg->regs + GRSTCTL);
|
||||
|
||||
if (timeout-- < 0) {
|
||||
dev_info(hsotg->dev,
|
||||
"%s: reset failed, GRSTCTL=%08x\n",
|
||||
__func__, grstctl);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
if (!(grstctl & GRSTCTL_AHBIDLE))
|
||||
continue;
|
||||
|
||||
break; /* reset done */
|
||||
}
|
||||
|
||||
dev_dbg(hsotg->dev, "reset successful\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_hsotg_core_init - issue softreset to the core
|
||||
* @hsotg: The device state
|
||||
|
@ -2307,7 +2259,7 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
|
|||
kill_all_requests(hsotg, hsotg->eps_out[0], -ECONNRESET);
|
||||
|
||||
if (!is_usb_reset)
|
||||
if (dwc2_hsotg_corereset(hsotg))
|
||||
if (dwc2_core_reset(hsotg))
|
||||
return;
|
||||
|
||||
/*
|
||||
|
@ -2585,7 +2537,7 @@ irq_retry:
|
|||
if (gintsts & GINTSTS_GOUTNAKEFF) {
|
||||
dev_info(hsotg->dev, "GOUTNakEff triggered\n");
|
||||
|
||||
dwc2_writel(DCTL_CGOUTNAK, hsotg->regs + DCTL);
|
||||
__orr32(hsotg->regs + DCTL, DCTL_CGOUTNAK);
|
||||
|
||||
dwc2_hsotg_dump(hsotg);
|
||||
}
|
||||
|
@ -2593,7 +2545,7 @@ irq_retry:
|
|||
if (gintsts & GINTSTS_GINNAKEFF) {
|
||||
dev_info(hsotg->dev, "GINNakEff triggered\n");
|
||||
|
||||
dwc2_writel(DCTL_CGNPINNAK, hsotg->regs + DCTL);
|
||||
__orr32(hsotg->regs + DCTL, DCTL_CGNPINNAK);
|
||||
|
||||
dwc2_hsotg_dump(hsotg);
|
||||
}
|
||||
|
@ -2911,15 +2863,15 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
|
|||
"%s: timeout DIEPINT.NAKEFF\n", __func__);
|
||||
} else {
|
||||
/* Clear any pending nak effect interrupt */
|
||||
dwc2_writel(GINTSTS_GINNAKEFF, hsotg->regs + GINTSTS);
|
||||
dwc2_writel(GINTSTS_GOUTNAKEFF, hsotg->regs + GINTSTS);
|
||||
|
||||
__orr32(hsotg->regs + DCTL, DCTL_SGNPINNAK);
|
||||
__orr32(hsotg->regs + DCTL, DCTL_SGOUTNAK);
|
||||
|
||||
/* Wait for global nak to take effect */
|
||||
if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS,
|
||||
GINTSTS_GINNAKEFF, 100))
|
||||
GINTSTS_GOUTNAKEFF, 100))
|
||||
dev_warn(hsotg->dev,
|
||||
"%s: timeout GINTSTS.GINNAKEFF\n", __func__);
|
||||
"%s: timeout GINTSTS.GOUTNAKEFF\n", __func__);
|
||||
}
|
||||
|
||||
/* Disable ep */
|
||||
|
@ -2944,7 +2896,7 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
|
|||
/* TODO: Flush shared tx fifo */
|
||||
} else {
|
||||
/* Remove global NAKs */
|
||||
__bic32(hsotg->regs + DCTL, DCTL_SGNPINNAK);
|
||||
__bic32(hsotg->regs + DCTL, DCTL_SGOUTNAK);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3403,8 +3355,8 @@ static int dwc2_hsotg_hw_cfg(struct dwc2_hsotg *hsotg)
|
|||
|
||||
/* check hardware configuration */
|
||||
|
||||
cfg = dwc2_readl(hsotg->regs + GHWCFG2);
|
||||
hsotg->num_of_eps = (cfg >> GHWCFG2_NUM_DEV_EP_SHIFT) & 0xF;
|
||||
hsotg->num_of_eps = hsotg->hw_params.num_dev_ep;
|
||||
|
||||
/* Add ep0 */
|
||||
hsotg->num_of_eps++;
|
||||
|
||||
|
@ -3415,7 +3367,7 @@ static int dwc2_hsotg_hw_cfg(struct dwc2_hsotg *hsotg)
|
|||
/* Same dwc2_hsotg_ep is used in both directions for ep0 */
|
||||
hsotg->eps_out[0] = hsotg->eps_in[0];
|
||||
|
||||
cfg = dwc2_readl(hsotg->regs + GHWCFG1);
|
||||
cfg = hsotg->hw_params.dev_ep_dirs;
|
||||
for (i = 1, cfg >>= 2; i < hsotg->num_of_eps; i++, cfg >>= 2) {
|
||||
ep_type = cfg & 3;
|
||||
/* Direction in or both */
|
||||
|
@ -3434,11 +3386,8 @@ static int dwc2_hsotg_hw_cfg(struct dwc2_hsotg *hsotg)
|
|||
}
|
||||
}
|
||||
|
||||
cfg = dwc2_readl(hsotg->regs + GHWCFG3);
|
||||
hsotg->fifo_mem = (cfg >> GHWCFG3_DFIFO_DEPTH_SHIFT);
|
||||
|
||||
cfg = dwc2_readl(hsotg->regs + GHWCFG4);
|
||||
hsotg->dedicated_fifos = (cfg >> GHWCFG4_DED_FIFO_SHIFT) & 1;
|
||||
hsotg->fifo_mem = hsotg->hw_params.total_fifo_size;
|
||||
hsotg->dedicated_fifos = hsotg->hw_params.en_multiple_tx_fifo;
|
||||
|
||||
dev_info(hsotg->dev, "EPs: %d, %s fifos, %d entries in SPRAM\n",
|
||||
hsotg->num_of_eps,
|
||||
|
@ -3563,6 +3512,17 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
|
|||
memcpy(&hsotg->g_tx_fifo_sz[1], p_tx_fifo, sizeof(p_tx_fifo));
|
||||
/* Device tree specific probe */
|
||||
dwc2_hsotg_of_probe(hsotg);
|
||||
|
||||
/* Check against largest possible value. */
|
||||
if (hsotg->g_np_g_tx_fifo_sz >
|
||||
hsotg->hw_params.dev_nperio_tx_fifo_size) {
|
||||
dev_warn(dev, "Specified GNPTXFDEP=%d > %d\n",
|
||||
hsotg->g_np_g_tx_fifo_sz,
|
||||
hsotg->hw_params.dev_nperio_tx_fifo_size);
|
||||
hsotg->g_np_g_tx_fifo_sz =
|
||||
hsotg->hw_params.dev_nperio_tx_fifo_size;
|
||||
}
|
||||
|
||||
/* Dump fifo information */
|
||||
dev_dbg(dev, "NonPeriodic TXFIFO size: %d\n",
|
||||
hsotg->g_np_g_tx_fifo_sz);
|
||||
|
@ -3579,31 +3539,12 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
|
|||
else if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)
|
||||
hsotg->op_state = OTG_STATE_B_PERIPHERAL;
|
||||
|
||||
/*
|
||||
* Force Device mode before initialization.
|
||||
* This allows correctly configuring fifo for device mode.
|
||||
*/
|
||||
__bic32(hsotg->regs + GUSBCFG, GUSBCFG_FORCEHOSTMODE);
|
||||
__orr32(hsotg->regs + GUSBCFG, GUSBCFG_FORCEDEVMODE);
|
||||
|
||||
/*
|
||||
* According to Synopsys databook, this sleep is needed for the force
|
||||
* device mode to take effect.
|
||||
*/
|
||||
msleep(25);
|
||||
|
||||
dwc2_hsotg_corereset(hsotg);
|
||||
ret = dwc2_hsotg_hw_cfg(hsotg);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "Hardware configuration failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dwc2_hsotg_init(hsotg);
|
||||
|
||||
/* Switch back to default configuration */
|
||||
__bic32(hsotg->regs + GUSBCFG, GUSBCFG_FORCEDEVMODE);
|
||||
|
||||
hsotg->ctrl_buff = devm_kzalloc(hsotg->dev,
|
||||
DWC2_CTRL_BUFF_SIZE, GFP_KERNEL);
|
||||
if (!hsotg->ctrl_buff) {
|
||||
|
|
|
@ -268,15 +268,33 @@ static void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg)
|
|||
}
|
||||
|
||||
/**
|
||||
* dwc2_hcd_disconnect() - Handles disconnect of the HCD
|
||||
* dwc2_hcd_connect() - Handles connect of the HCD
|
||||
*
|
||||
* @hsotg: Pointer to struct dwc2_hsotg
|
||||
*
|
||||
* Must be called with interrupt disabled and spinlock held
|
||||
*/
|
||||
void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg)
|
||||
void dwc2_hcd_connect(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
if (hsotg->lx_state != DWC2_L0)
|
||||
usb_hcd_resume_root_hub(hsotg->priv);
|
||||
|
||||
hsotg->flags.b.port_connect_status_change = 1;
|
||||
hsotg->flags.b.port_connect_status = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_hcd_disconnect() - Handles disconnect of the HCD
|
||||
*
|
||||
* @hsotg: Pointer to struct dwc2_hsotg
|
||||
* @force: If true, we won't try to reconnect even if we see device connected.
|
||||
*
|
||||
* Must be called with interrupt disabled and spinlock held
|
||||
*/
|
||||
void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force)
|
||||
{
|
||||
u32 intr;
|
||||
u32 hprt0;
|
||||
|
||||
/* Set status flags for the hub driver */
|
||||
hsotg->flags.b.port_connect_status_change = 1;
|
||||
|
@ -315,6 +333,24 @@ void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg)
|
|||
dwc2_hcd_cleanup_channels(hsotg);
|
||||
|
||||
dwc2_host_disconnect(hsotg);
|
||||
|
||||
/*
|
||||
* Add an extra check here to see if we're actually connected but
|
||||
* we don't have a detection interrupt pending. This can happen if:
|
||||
* 1. hardware sees connect
|
||||
* 2. hardware sees disconnect
|
||||
* 3. hardware sees connect
|
||||
* 4. dwc2_port_intr() - clears connect interrupt
|
||||
* 5. dwc2_handle_common_intr() - calls here
|
||||
*
|
||||
* Without the extra check here we will end calling disconnect
|
||||
* and won't get any future interrupts to handle the connect.
|
||||
*/
|
||||
if (!force) {
|
||||
hprt0 = dwc2_readl(hsotg->regs + HPRT0);
|
||||
if (!(hprt0 & HPRT0_CONNDET) && (hprt0 & HPRT0_CONNSTS))
|
||||
dwc2_hcd_connect(hsotg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -881,8 +917,10 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
|
|||
*/
|
||||
chan->multi_count = dwc2_hb_mult(qh->maxp);
|
||||
|
||||
if (hsotg->core_params->dma_desc_enable > 0)
|
||||
if (hsotg->core_params->dma_desc_enable > 0) {
|
||||
chan->desc_list_addr = qh->desc_list_dma;
|
||||
chan->desc_list_sz = qh->desc_list_sz;
|
||||
}
|
||||
|
||||
dwc2_hc_init(hsotg, chan);
|
||||
chan->qh = qh;
|
||||
|
@ -1382,7 +1420,7 @@ static void dwc2_conn_id_status_change(struct work_struct *work)
|
|||
dev_err(hsotg->dev,
|
||||
"Connection id status change timed out\n");
|
||||
hsotg->op_state = OTG_STATE_B_PERIPHERAL;
|
||||
dwc2_core_init(hsotg, false, -1);
|
||||
dwc2_core_init(hsotg, false);
|
||||
dwc2_enable_global_interrupts(hsotg);
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
dwc2_hsotg_core_init_disconnected(hsotg, false);
|
||||
|
@ -1405,7 +1443,7 @@ static void dwc2_conn_id_status_change(struct work_struct *work)
|
|||
hsotg->op_state = OTG_STATE_A_HOST;
|
||||
|
||||
/* Initialize the Core for Host mode */
|
||||
dwc2_core_init(hsotg, false, -1);
|
||||
dwc2_core_init(hsotg, false);
|
||||
dwc2_enable_global_interrupts(hsotg);
|
||||
dwc2_hcd_start(hsotg);
|
||||
}
|
||||
|
@ -1734,6 +1772,28 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
|
|||
port_status |= USB_PORT_STAT_TEST;
|
||||
/* USB_PORT_FEAT_INDICATOR unsupported always 0 */
|
||||
|
||||
if (hsotg->core_params->dma_desc_fs_enable) {
|
||||
/*
|
||||
* Enable descriptor DMA only if a full speed
|
||||
* device is connected.
|
||||
*/
|
||||
if (hsotg->new_connection &&
|
||||
((port_status &
|
||||
(USB_PORT_STAT_CONNECTION |
|
||||
USB_PORT_STAT_HIGH_SPEED |
|
||||
USB_PORT_STAT_LOW_SPEED)) ==
|
||||
USB_PORT_STAT_CONNECTION)) {
|
||||
u32 hcfg;
|
||||
|
||||
dev_info(hsotg->dev, "Enabling descriptor DMA mode\n");
|
||||
hsotg->core_params->dma_desc_enable = 1;
|
||||
hcfg = dwc2_readl(hsotg->regs + HCFG);
|
||||
hcfg |= HCFG_DESCDMA;
|
||||
dwc2_writel(hcfg, hsotg->regs + HCFG);
|
||||
hsotg->new_connection = false;
|
||||
}
|
||||
}
|
||||
|
||||
dev_vdbg(hsotg->dev, "port_status=%08x\n", port_status);
|
||||
*(__le32 *)buf = cpu_to_le32(port_status);
|
||||
break;
|
||||
|
@ -2298,13 +2358,19 @@ static void dwc2_hcd_reset_func(struct work_struct *work)
|
|||
{
|
||||
struct dwc2_hsotg *hsotg = container_of(work, struct dwc2_hsotg,
|
||||
reset_work.work);
|
||||
unsigned long flags;
|
||||
u32 hprt0;
|
||||
|
||||
dev_dbg(hsotg->dev, "USB RESET function called\n");
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
|
||||
hprt0 = dwc2_read_hprt0(hsotg);
|
||||
hprt0 &= ~HPRT0_RST;
|
||||
dwc2_writel(hprt0, hsotg->regs + HPRT0);
|
||||
hsotg->flags.b.port_reset_change = 1;
|
||||
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2366,7 +2432,7 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd)
|
|||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
/* Ensure hcd is disconnected */
|
||||
dwc2_hcd_disconnect(hsotg);
|
||||
dwc2_hcd_disconnect(hsotg, true);
|
||||
dwc2_hcd_stop(hsotg);
|
||||
hsotg->lx_state = DWC2_L3;
|
||||
hcd->state = HC_STATE_HALT;
|
||||
|
@ -3054,7 +3120,7 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
|
|||
dwc2_disable_global_interrupts(hsotg);
|
||||
|
||||
/* Initialize the DWC_otg core, and select the Phy type */
|
||||
retval = dwc2_core_init(hsotg, true, irq);
|
||||
retval = dwc2_core_init(hsotg, true);
|
||||
if (retval)
|
||||
goto error2;
|
||||
|
||||
|
@ -3122,6 +3188,47 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
|
|||
if (!hsotg->status_buf)
|
||||
goto error3;
|
||||
|
||||
/*
|
||||
* Create kmem caches to handle descriptor buffers in descriptor
|
||||
* DMA mode.
|
||||
* Alignment must be set to 512 bytes.
|
||||
*/
|
||||
if (hsotg->core_params->dma_desc_enable ||
|
||||
hsotg->core_params->dma_desc_fs_enable) {
|
||||
hsotg->desc_gen_cache = kmem_cache_create("dwc2-gen-desc",
|
||||
sizeof(struct dwc2_hcd_dma_desc) *
|
||||
MAX_DMA_DESC_NUM_GENERIC, 512, SLAB_CACHE_DMA,
|
||||
NULL);
|
||||
if (!hsotg->desc_gen_cache) {
|
||||
dev_err(hsotg->dev,
|
||||
"unable to create dwc2 generic desc cache\n");
|
||||
|
||||
/*
|
||||
* Disable descriptor dma mode since it will not be
|
||||
* usable.
|
||||
*/
|
||||
hsotg->core_params->dma_desc_enable = 0;
|
||||
hsotg->core_params->dma_desc_fs_enable = 0;
|
||||
}
|
||||
|
||||
hsotg->desc_hsisoc_cache = kmem_cache_create("dwc2-hsisoc-desc",
|
||||
sizeof(struct dwc2_hcd_dma_desc) *
|
||||
MAX_DMA_DESC_NUM_HS_ISOC, 512, 0, NULL);
|
||||
if (!hsotg->desc_hsisoc_cache) {
|
||||
dev_err(hsotg->dev,
|
||||
"unable to create dwc2 hs isoc desc cache\n");
|
||||
|
||||
kmem_cache_destroy(hsotg->desc_gen_cache);
|
||||
|
||||
/*
|
||||
* Disable descriptor dma mode since it will not be
|
||||
* usable.
|
||||
*/
|
||||
hsotg->core_params->dma_desc_enable = 0;
|
||||
hsotg->core_params->dma_desc_fs_enable = 0;
|
||||
}
|
||||
}
|
||||
|
||||
hsotg->otg_port = 1;
|
||||
hsotg->frame_list = NULL;
|
||||
hsotg->frame_list_dma = 0;
|
||||
|
@ -3145,7 +3252,7 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
|
|||
*/
|
||||
retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
|
||||
if (retval < 0)
|
||||
goto error3;
|
||||
goto error4;
|
||||
|
||||
device_wakeup_enable(hcd->self.controller);
|
||||
|
||||
|
@ -3155,6 +3262,9 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
|
|||
|
||||
return 0;
|
||||
|
||||
error4:
|
||||
kmem_cache_destroy(hsotg->desc_gen_cache);
|
||||
kmem_cache_destroy(hsotg->desc_hsisoc_cache);
|
||||
error3:
|
||||
dwc2_hcd_release(hsotg);
|
||||
error2:
|
||||
|
@ -3195,6 +3305,10 @@ void dwc2_hcd_remove(struct dwc2_hsotg *hsotg)
|
|||
|
||||
usb_remove_hcd(hcd);
|
||||
hsotg->priv = NULL;
|
||||
|
||||
kmem_cache_destroy(hsotg->desc_gen_cache);
|
||||
kmem_cache_destroy(hsotg->desc_hsisoc_cache);
|
||||
|
||||
dwc2_hcd_release(hsotg);
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
|
|
|
@ -107,6 +107,7 @@ struct dwc2_qh;
|
|||
* @qh: QH for the transfer being processed by this channel
|
||||
* @hc_list_entry: For linking to list of host channels
|
||||
* @desc_list_addr: Current QH's descriptor list DMA address
|
||||
* @desc_list_sz: Current QH's descriptor list size
|
||||
*
|
||||
* This structure represents the state of a single host channel when acting in
|
||||
* host mode. It contains the data items needed to transfer packets to an
|
||||
|
@ -159,6 +160,7 @@ struct dwc2_host_chan {
|
|||
struct dwc2_qh *qh;
|
||||
struct list_head hc_list_entry;
|
||||
dma_addr_t desc_list_addr;
|
||||
u32 desc_list_sz;
|
||||
};
|
||||
|
||||
struct dwc2_hcd_pipe_info {
|
||||
|
@ -251,6 +253,7 @@ enum dwc2_transaction_type {
|
|||
* schedule
|
||||
* @desc_list: List of transfer descriptors
|
||||
* @desc_list_dma: Physical address of desc_list
|
||||
* @desc_list_sz: Size of descriptors list
|
||||
* @n_bytes: Xfer Bytes array. Each element corresponds to a transfer
|
||||
* descriptor and indicates original XferSize value for the
|
||||
* descriptor
|
||||
|
@ -284,6 +287,7 @@ struct dwc2_qh {
|
|||
struct list_head qh_list_entry;
|
||||
struct dwc2_hcd_dma_desc *desc_list;
|
||||
dma_addr_t desc_list_dma;
|
||||
u32 desc_list_sz;
|
||||
u32 *n_bytes;
|
||||
unsigned tt_buffer_dirty:1;
|
||||
};
|
||||
|
@ -340,6 +344,8 @@ struct dwc2_qtd {
|
|||
u8 isoc_split_pos;
|
||||
u16 isoc_frame_index;
|
||||
u16 isoc_split_offset;
|
||||
u16 isoc_td_last;
|
||||
u16 isoc_td_first;
|
||||
u32 ssplit_out_xfer_count;
|
||||
u8 error_count;
|
||||
u8 n_desc;
|
||||
|
@ -377,18 +383,6 @@ static inline void disable_hc_int(struct dwc2_hsotg *hsotg, int chnum, u32 intr)
|
|||
dwc2_writel(mask, hsotg->regs + HCINTMSK(chnum));
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the mode of operation, host or device
|
||||
*/
|
||||
static inline int dwc2_is_host_mode(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
return (dwc2_readl(hsotg->regs + GINTSTS) & GINTSTS_CURMODE_HOST) != 0;
|
||||
}
|
||||
static inline int dwc2_is_device_mode(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
return (dwc2_readl(hsotg->regs + GINTSTS) & GINTSTS_CURMODE_HOST) == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads HPRT0 in preparation to modify. It keeps the WC bits 0 so that if they
|
||||
* are read as 1, they won't clear when written back.
|
||||
|
@ -534,6 +528,19 @@ static inline bool dbg_perio(void) { return false; }
|
|||
/* Packet size for any kind of endpoint descriptor */
|
||||
#define dwc2_max_packet(wmaxpacketsize) ((wmaxpacketsize) & 0x07ff)
|
||||
|
||||
/*
|
||||
* Returns true if frame1 index is greater than frame2 index. The comparison
|
||||
* is done modulo FRLISTEN_64_SIZE. This accounts for the rollover of the
|
||||
* frame number when the max index frame number is reached.
|
||||
*/
|
||||
static inline bool dwc2_frame_idx_num_gt(u16 fr_idx1, u16 fr_idx2)
|
||||
{
|
||||
u16 diff = fr_idx1 - fr_idx2;
|
||||
u16 sign = diff & (FRLISTEN_64_SIZE >> 1);
|
||||
|
||||
return diff && !sign;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if frame1 is less than or equal to frame2. The comparison is
|
||||
* done modulo HFNUM_MAX_FRNUM. This accounts for the rollover of the
|
||||
|
|
|
@ -87,22 +87,31 @@ static u16 dwc2_frame_incr_val(struct dwc2_qh *qh)
|
|||
static int dwc2_desc_list_alloc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
|
||||
gfp_t flags)
|
||||
{
|
||||
qh->desc_list = dma_alloc_coherent(hsotg->dev,
|
||||
sizeof(struct dwc2_hcd_dma_desc) *
|
||||
dwc2_max_desc_num(qh), &qh->desc_list_dma,
|
||||
flags);
|
||||
struct kmem_cache *desc_cache;
|
||||
|
||||
if (qh->ep_type == USB_ENDPOINT_XFER_ISOC
|
||||
&& qh->dev_speed == USB_SPEED_HIGH)
|
||||
desc_cache = hsotg->desc_hsisoc_cache;
|
||||
else
|
||||
desc_cache = hsotg->desc_gen_cache;
|
||||
|
||||
qh->desc_list_sz = sizeof(struct dwc2_hcd_dma_desc) *
|
||||
dwc2_max_desc_num(qh);
|
||||
|
||||
qh->desc_list = kmem_cache_zalloc(desc_cache, flags | GFP_DMA);
|
||||
if (!qh->desc_list)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(qh->desc_list, 0,
|
||||
sizeof(struct dwc2_hcd_dma_desc) * dwc2_max_desc_num(qh));
|
||||
qh->desc_list_dma = dma_map_single(hsotg->dev, qh->desc_list,
|
||||
qh->desc_list_sz,
|
||||
DMA_TO_DEVICE);
|
||||
|
||||
qh->n_bytes = kzalloc(sizeof(u32) * dwc2_max_desc_num(qh), flags);
|
||||
if (!qh->n_bytes) {
|
||||
dma_free_coherent(hsotg->dev, sizeof(struct dwc2_hcd_dma_desc)
|
||||
* dwc2_max_desc_num(qh), qh->desc_list,
|
||||
qh->desc_list_dma);
|
||||
dma_unmap_single(hsotg->dev, qh->desc_list_dma,
|
||||
qh->desc_list_sz,
|
||||
DMA_FROM_DEVICE);
|
||||
kfree(qh->desc_list);
|
||||
qh->desc_list = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
@ -112,10 +121,18 @@ static int dwc2_desc_list_alloc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
|
|||
|
||||
static void dwc2_desc_list_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
|
||||
{
|
||||
struct kmem_cache *desc_cache;
|
||||
|
||||
if (qh->ep_type == USB_ENDPOINT_XFER_ISOC
|
||||
&& qh->dev_speed == USB_SPEED_HIGH)
|
||||
desc_cache = hsotg->desc_hsisoc_cache;
|
||||
else
|
||||
desc_cache = hsotg->desc_gen_cache;
|
||||
|
||||
if (qh->desc_list) {
|
||||
dma_free_coherent(hsotg->dev, sizeof(struct dwc2_hcd_dma_desc)
|
||||
* dwc2_max_desc_num(qh), qh->desc_list,
|
||||
qh->desc_list_dma);
|
||||
dma_unmap_single(hsotg->dev, qh->desc_list_dma,
|
||||
qh->desc_list_sz, DMA_FROM_DEVICE);
|
||||
kmem_cache_free(desc_cache, qh->desc_list);
|
||||
qh->desc_list = NULL;
|
||||
}
|
||||
|
||||
|
@ -128,21 +145,20 @@ static int dwc2_frame_list_alloc(struct dwc2_hsotg *hsotg, gfp_t mem_flags)
|
|||
if (hsotg->frame_list)
|
||||
return 0;
|
||||
|
||||
hsotg->frame_list = dma_alloc_coherent(hsotg->dev,
|
||||
4 * FRLISTEN_64_SIZE,
|
||||
&hsotg->frame_list_dma,
|
||||
mem_flags);
|
||||
hsotg->frame_list_sz = 4 * FRLISTEN_64_SIZE;
|
||||
hsotg->frame_list = kzalloc(hsotg->frame_list_sz, GFP_ATOMIC | GFP_DMA);
|
||||
if (!hsotg->frame_list)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(hsotg->frame_list, 0, 4 * FRLISTEN_64_SIZE);
|
||||
hsotg->frame_list_dma = dma_map_single(hsotg->dev, hsotg->frame_list,
|
||||
hsotg->frame_list_sz,
|
||||
DMA_TO_DEVICE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dwc2_frame_list_free(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 *frame_list;
|
||||
dma_addr_t frame_list_dma;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
|
@ -152,14 +168,14 @@ static void dwc2_frame_list_free(struct dwc2_hsotg *hsotg)
|
|||
return;
|
||||
}
|
||||
|
||||
frame_list = hsotg->frame_list;
|
||||
frame_list_dma = hsotg->frame_list_dma;
|
||||
dma_unmap_single(hsotg->dev, hsotg->frame_list_dma,
|
||||
hsotg->frame_list_sz, DMA_FROM_DEVICE);
|
||||
|
||||
kfree(hsotg->frame_list);
|
||||
hsotg->frame_list = NULL;
|
||||
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
|
||||
dma_free_coherent(hsotg->dev, 4 * FRLISTEN_64_SIZE, frame_list,
|
||||
frame_list_dma);
|
||||
}
|
||||
|
||||
static void dwc2_per_sched_enable(struct dwc2_hsotg *hsotg, u32 fr_list_en)
|
||||
|
@ -249,6 +265,15 @@ static void dwc2_update_frame_list(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
|
|||
j = (j + inc) & (FRLISTEN_64_SIZE - 1);
|
||||
} while (j != i);
|
||||
|
||||
/*
|
||||
* Sync frame list since controller will access it if periodic
|
||||
* channel is currently enabled.
|
||||
*/
|
||||
dma_sync_single_for_device(hsotg->dev,
|
||||
hsotg->frame_list_dma,
|
||||
hsotg->frame_list_sz,
|
||||
DMA_TO_DEVICE);
|
||||
|
||||
if (!enable)
|
||||
return;
|
||||
|
||||
|
@ -278,6 +303,7 @@ static void dwc2_release_channel_ddma(struct dwc2_hsotg *hsotg,
|
|||
hsotg->non_periodic_channels--;
|
||||
} else {
|
||||
dwc2_update_frame_list(hsotg, qh, 0);
|
||||
hsotg->available_host_channels++;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -360,6 +386,8 @@ err0:
|
|||
*/
|
||||
void dwc2_hcd_qh_free_ddma(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
dwc2_desc_list_free(hsotg, qh);
|
||||
|
||||
/*
|
||||
|
@ -369,8 +397,10 @@ void dwc2_hcd_qh_free_ddma(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
|
|||
* when it comes here from endpoint disable routine
|
||||
* channel remains assigned.
|
||||
*/
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
if (qh->channel)
|
||||
dwc2_release_channel_ddma(hsotg, qh);
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
|
||||
if ((qh->ep_type == USB_ENDPOINT_XFER_ISOC ||
|
||||
qh->ep_type == USB_ENDPOINT_XFER_INT) &&
|
||||
|
@ -524,14 +554,23 @@ static void dwc2_fill_host_isoc_dma_desc(struct dwc2_hsotg *hsotg,
|
|||
dma_desc->status = qh->n_bytes[idx] << HOST_DMA_ISOC_NBYTES_SHIFT &
|
||||
HOST_DMA_ISOC_NBYTES_MASK;
|
||||
|
||||
/* Set active bit */
|
||||
dma_desc->status |= HOST_DMA_A;
|
||||
|
||||
qh->ntd++;
|
||||
qtd->isoc_frame_index_last++;
|
||||
|
||||
#ifdef ISOC_URB_GIVEBACK_ASAP
|
||||
/* Set IOC for each descriptor corresponding to last frame of URB */
|
||||
if (qtd->isoc_frame_index_last == qtd->urb->packet_count)
|
||||
dma_desc->status |= HOST_DMA_IOC;
|
||||
#endif
|
||||
|
||||
qh->ntd++;
|
||||
qtd->isoc_frame_index_last++;
|
||||
dma_sync_single_for_device(hsotg->dev,
|
||||
qh->desc_list_dma +
|
||||
(idx * sizeof(struct dwc2_hcd_dma_desc)),
|
||||
sizeof(struct dwc2_hcd_dma_desc),
|
||||
DMA_TO_DEVICE);
|
||||
}
|
||||
|
||||
static void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
|
||||
|
@ -539,11 +578,32 @@ static void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
|
|||
{
|
||||
struct dwc2_qtd *qtd;
|
||||
u32 max_xfer_size;
|
||||
u16 idx, inc, n_desc, ntd_max = 0;
|
||||
u16 idx, inc, n_desc = 0, ntd_max = 0;
|
||||
u16 cur_idx;
|
||||
u16 next_idx;
|
||||
|
||||
idx = qh->td_last;
|
||||
inc = qh->interval;
|
||||
n_desc = 0;
|
||||
hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
|
||||
cur_idx = dwc2_frame_list_idx(hsotg->frame_number);
|
||||
next_idx = dwc2_desclist_idx_inc(qh->td_last, inc, qh->dev_speed);
|
||||
|
||||
/*
|
||||
* Ensure current frame number didn't overstep last scheduled
|
||||
* descriptor. If it happens, the only way to recover is to move
|
||||
* qh->td_last to current frame number + 1.
|
||||
* So that next isoc descriptor will be scheduled on frame number + 1
|
||||
* and not on a past frame.
|
||||
*/
|
||||
if (dwc2_frame_idx_num_gt(cur_idx, next_idx) || (cur_idx == next_idx)) {
|
||||
if (inc < 32) {
|
||||
dev_vdbg(hsotg->dev,
|
||||
"current frame number overstep last descriptor\n");
|
||||
qh->td_last = dwc2_desclist_idx_inc(cur_idx, inc,
|
||||
qh->dev_speed);
|
||||
idx = qh->td_last;
|
||||
}
|
||||
}
|
||||
|
||||
if (qh->interval) {
|
||||
ntd_max = (dwc2_max_desc_num(qh) + qh->interval - 1) /
|
||||
|
@ -556,15 +616,20 @@ static void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
|
|||
MAX_ISOC_XFER_SIZE_HS : MAX_ISOC_XFER_SIZE_FS;
|
||||
|
||||
list_for_each_entry(qtd, &qh->qtd_list, qtd_list_entry) {
|
||||
if (qtd->in_process &&
|
||||
qtd->isoc_frame_index_last ==
|
||||
qtd->urb->packet_count)
|
||||
continue;
|
||||
|
||||
qtd->isoc_td_first = idx;
|
||||
while (qh->ntd < ntd_max && qtd->isoc_frame_index_last <
|
||||
qtd->urb->packet_count) {
|
||||
if (n_desc > 1)
|
||||
qh->desc_list[n_desc - 1].status |= HOST_DMA_A;
|
||||
dwc2_fill_host_isoc_dma_desc(hsotg, qtd, qh,
|
||||
max_xfer_size, idx);
|
||||
idx = dwc2_desclist_idx_inc(idx, inc, qh->dev_speed);
|
||||
n_desc++;
|
||||
}
|
||||
qtd->isoc_td_last = idx;
|
||||
qtd->in_process = 1;
|
||||
}
|
||||
|
||||
|
@ -575,6 +640,11 @@ static void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
|
|||
if (qh->ntd == ntd_max) {
|
||||
idx = dwc2_desclist_idx_dec(qh->td_last, inc, qh->dev_speed);
|
||||
qh->desc_list[idx].status |= HOST_DMA_IOC;
|
||||
dma_sync_single_for_device(hsotg->dev,
|
||||
qh->desc_list_dma + (idx *
|
||||
sizeof(struct dwc2_hcd_dma_desc)),
|
||||
sizeof(struct dwc2_hcd_dma_desc),
|
||||
DMA_TO_DEVICE);
|
||||
}
|
||||
#else
|
||||
/*
|
||||
|
@ -604,13 +674,12 @@ static void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
|
|||
idx = dwc2_desclist_idx_dec(qh->td_last, inc, qh->dev_speed);
|
||||
|
||||
qh->desc_list[idx].status |= HOST_DMA_IOC;
|
||||
dma_sync_single_for_device(hsotg->dev,
|
||||
qh->desc_list_dma +
|
||||
(idx * sizeof(struct dwc2_hcd_dma_desc)),
|
||||
sizeof(struct dwc2_hcd_dma_desc),
|
||||
DMA_TO_DEVICE);
|
||||
#endif
|
||||
|
||||
if (n_desc) {
|
||||
qh->desc_list[n_desc - 1].status |= HOST_DMA_A;
|
||||
if (n_desc > 1)
|
||||
qh->desc_list[0].status |= HOST_DMA_A;
|
||||
}
|
||||
}
|
||||
|
||||
static void dwc2_fill_host_dma_desc(struct dwc2_hsotg *hsotg,
|
||||
|
@ -647,6 +716,12 @@ static void dwc2_fill_host_dma_desc(struct dwc2_hsotg *hsotg,
|
|||
|
||||
dma_desc->buf = (u32)chan->xfer_dma;
|
||||
|
||||
dma_sync_single_for_device(hsotg->dev,
|
||||
qh->desc_list_dma +
|
||||
(n_desc * sizeof(struct dwc2_hcd_dma_desc)),
|
||||
sizeof(struct dwc2_hcd_dma_desc),
|
||||
DMA_TO_DEVICE);
|
||||
|
||||
/*
|
||||
* Last (or only) descriptor of IN transfer with actual size less
|
||||
* than MaxPacket
|
||||
|
@ -697,6 +772,12 @@ static void dwc2_init_non_isoc_dma_desc(struct dwc2_hsotg *hsotg,
|
|||
"set A bit in desc %d (%p)\n",
|
||||
n_desc - 1,
|
||||
&qh->desc_list[n_desc - 1]);
|
||||
dma_sync_single_for_device(hsotg->dev,
|
||||
qh->desc_list_dma +
|
||||
((n_desc - 1) *
|
||||
sizeof(struct dwc2_hcd_dma_desc)),
|
||||
sizeof(struct dwc2_hcd_dma_desc),
|
||||
DMA_TO_DEVICE);
|
||||
}
|
||||
dwc2_fill_host_dma_desc(hsotg, chan, qtd, qh, n_desc);
|
||||
dev_vdbg(hsotg->dev,
|
||||
|
@ -722,10 +803,19 @@ static void dwc2_init_non_isoc_dma_desc(struct dwc2_hsotg *hsotg,
|
|||
HOST_DMA_IOC | HOST_DMA_EOL | HOST_DMA_A;
|
||||
dev_vdbg(hsotg->dev, "set IOC/EOL/A bits in desc %d (%p)\n",
|
||||
n_desc - 1, &qh->desc_list[n_desc - 1]);
|
||||
dma_sync_single_for_device(hsotg->dev,
|
||||
qh->desc_list_dma + (n_desc - 1) *
|
||||
sizeof(struct dwc2_hcd_dma_desc),
|
||||
sizeof(struct dwc2_hcd_dma_desc),
|
||||
DMA_TO_DEVICE);
|
||||
if (n_desc > 1) {
|
||||
qh->desc_list[0].status |= HOST_DMA_A;
|
||||
dev_vdbg(hsotg->dev, "set A bit in desc 0 (%p)\n",
|
||||
&qh->desc_list[0]);
|
||||
dma_sync_single_for_device(hsotg->dev,
|
||||
qh->desc_list_dma,
|
||||
sizeof(struct dwc2_hcd_dma_desc),
|
||||
DMA_TO_DEVICE);
|
||||
}
|
||||
chan->ntd = n_desc;
|
||||
}
|
||||
|
@ -800,7 +890,7 @@ static int dwc2_cmpl_host_isoc_dma_desc(struct dwc2_hsotg *hsotg,
|
|||
struct dwc2_qtd *qtd,
|
||||
struct dwc2_qh *qh, u16 idx)
|
||||
{
|
||||
struct dwc2_hcd_dma_desc *dma_desc = &qh->desc_list[idx];
|
||||
struct dwc2_hcd_dma_desc *dma_desc;
|
||||
struct dwc2_hcd_iso_packet_desc *frame_desc;
|
||||
u16 remain = 0;
|
||||
int rc = 0;
|
||||
|
@ -808,6 +898,13 @@ static int dwc2_cmpl_host_isoc_dma_desc(struct dwc2_hsotg *hsotg,
|
|||
if (!qtd->urb)
|
||||
return -EINVAL;
|
||||
|
||||
dma_sync_single_for_cpu(hsotg->dev, qh->desc_list_dma + (idx *
|
||||
sizeof(struct dwc2_hcd_dma_desc)),
|
||||
sizeof(struct dwc2_hcd_dma_desc),
|
||||
DMA_FROM_DEVICE);
|
||||
|
||||
dma_desc = &qh->desc_list[idx];
|
||||
|
||||
frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index_last];
|
||||
dma_desc->buf = (u32)(qtd->urb->dma + frame_desc->offset);
|
||||
if (chan->ep_is_in)
|
||||
|
@ -911,17 +1008,51 @@ static void dwc2_complete_isoc_xfer_ddma(struct dwc2_hsotg *hsotg,
|
|||
list_for_each_entry_safe(qtd, qtd_tmp, &qh->qtd_list, qtd_list_entry) {
|
||||
if (!qtd->in_process)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Ensure idx corresponds to descriptor where first urb of this
|
||||
* qtd was added. In fact, during isoc desc init, dwc2 may skip
|
||||
* an index if current frame number is already over this index.
|
||||
*/
|
||||
if (idx != qtd->isoc_td_first) {
|
||||
dev_vdbg(hsotg->dev,
|
||||
"try to complete %d instead of %d\n",
|
||||
idx, qtd->isoc_td_first);
|
||||
idx = qtd->isoc_td_first;
|
||||
}
|
||||
|
||||
do {
|
||||
struct dwc2_qtd *qtd_next;
|
||||
u16 cur_idx;
|
||||
|
||||
rc = dwc2_cmpl_host_isoc_dma_desc(hsotg, chan, qtd, qh,
|
||||
idx);
|
||||
if (rc < 0)
|
||||
return;
|
||||
idx = dwc2_desclist_idx_inc(idx, qh->interval,
|
||||
chan->speed);
|
||||
if (rc == DWC2_CMPL_STOP)
|
||||
goto stop_scan;
|
||||
if (!rc)
|
||||
continue;
|
||||
|
||||
if (rc == DWC2_CMPL_DONE)
|
||||
break;
|
||||
|
||||
/* rc == DWC2_CMPL_STOP */
|
||||
|
||||
if (qh->interval >= 32)
|
||||
goto stop_scan;
|
||||
|
||||
qh->td_first = idx;
|
||||
cur_idx = dwc2_frame_list_idx(hsotg->frame_number);
|
||||
qtd_next = list_first_entry(&qh->qtd_list,
|
||||
struct dwc2_qtd,
|
||||
qtd_list_entry);
|
||||
if (dwc2_frame_idx_num_gt(cur_idx,
|
||||
qtd_next->isoc_td_last))
|
||||
break;
|
||||
|
||||
goto stop_scan;
|
||||
|
||||
} while (idx != qh->td_first);
|
||||
}
|
||||
|
||||
|
@ -1029,6 +1160,12 @@ static int dwc2_process_non_isoc_desc(struct dwc2_hsotg *hsotg,
|
|||
if (!urb)
|
||||
return -EINVAL;
|
||||
|
||||
dma_sync_single_for_cpu(hsotg->dev,
|
||||
qh->desc_list_dma + (desc_num *
|
||||
sizeof(struct dwc2_hcd_dma_desc)),
|
||||
sizeof(struct dwc2_hcd_dma_desc),
|
||||
DMA_FROM_DEVICE);
|
||||
|
||||
dma_desc = &qh->desc_list[desc_num];
|
||||
n_bytes = qh->n_bytes[desc_num];
|
||||
dev_vdbg(hsotg->dev,
|
||||
|
@ -1037,7 +1174,10 @@ static int dwc2_process_non_isoc_desc(struct dwc2_hsotg *hsotg,
|
|||
failed = dwc2_update_non_isoc_urb_state_ddma(hsotg, chan, qtd, dma_desc,
|
||||
halt_status, n_bytes,
|
||||
xfer_done);
|
||||
if (failed || (*xfer_done && urb->status != -EINPROGRESS)) {
|
||||
if (*xfer_done && urb->status != -EINPROGRESS)
|
||||
failed = 1;
|
||||
|
||||
if (failed) {
|
||||
dwc2_host_complete(hsotg, qtd, urb->status);
|
||||
dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh);
|
||||
dev_vdbg(hsotg->dev, "failed=%1x xfer_done=%1x status=%08x\n",
|
||||
|
@ -1165,6 +1305,21 @@ void dwc2_hcd_complete_xfer_ddma(struct dwc2_hsotg *hsotg,
|
|||
/* Release the channel if halted or session completed */
|
||||
if (halt_status != DWC2_HC_XFER_COMPLETE ||
|
||||
list_empty(&qh->qtd_list)) {
|
||||
struct dwc2_qtd *qtd, *qtd_tmp;
|
||||
|
||||
/*
|
||||
* Kill all remainings QTDs since channel has been
|
||||
* halted.
|
||||
*/
|
||||
list_for_each_entry_safe(qtd, qtd_tmp,
|
||||
&qh->qtd_list,
|
||||
qtd_list_entry) {
|
||||
dwc2_host_complete(hsotg, qtd,
|
||||
-ECONNRESET);
|
||||
dwc2_hcd_qtd_unlink_and_free(hsotg,
|
||||
qtd, qh);
|
||||
}
|
||||
|
||||
/* Halt the channel if session completed */
|
||||
if (halt_status == DWC2_HC_XFER_COMPLETE)
|
||||
dwc2_hc_halt(hsotg, chan, halt_status);
|
||||
|
@ -1174,7 +1329,12 @@ void dwc2_hcd_complete_xfer_ddma(struct dwc2_hsotg *hsotg,
|
|||
/* Keep in assigned schedule to continue transfer */
|
||||
list_move(&qh->qh_list_entry,
|
||||
&hsotg->periodic_sched_assigned);
|
||||
continue_isoc_xfer = 1;
|
||||
/*
|
||||
* If channel has been halted during giveback of urb
|
||||
* then prevent any new scheduling.
|
||||
*/
|
||||
if (!chan->halt_status)
|
||||
continue_isoc_xfer = 1;
|
||||
}
|
||||
/*
|
||||
* Todo: Consider the case when period exceeds FrameList size.
|
||||
|
|
|
@ -122,6 +122,9 @@ static void dwc2_sof_intr(struct dwc2_hsotg *hsotg)
|
|||
struct dwc2_qh *qh;
|
||||
enum dwc2_transaction_type tr_type;
|
||||
|
||||
/* Clear interrupt */
|
||||
dwc2_writel(GINTSTS_SOF, hsotg->regs + GINTSTS);
|
||||
|
||||
#ifdef DEBUG_SOF
|
||||
dev_vdbg(hsotg->dev, "--Start of Frame Interrupt--\n");
|
||||
#endif
|
||||
|
@ -146,9 +149,6 @@ static void dwc2_sof_intr(struct dwc2_hsotg *hsotg)
|
|||
tr_type = dwc2_hcd_select_transactions(hsotg);
|
||||
if (tr_type != DWC2_TRANSACTION_NONE)
|
||||
dwc2_hcd_queue_transactions(hsotg, tr_type);
|
||||
|
||||
/* Clear interrupt */
|
||||
dwc2_writel(GINTSTS_SOF, hsotg->regs + GINTSTS);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -312,6 +312,7 @@ static void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0,
|
|||
|
||||
if (do_reset) {
|
||||
*hprt0_modify |= HPRT0_RST;
|
||||
dwc2_writel(*hprt0_modify, hsotg->regs + HPRT0);
|
||||
queue_delayed_work(hsotg->wq_otg, &hsotg->reset_work,
|
||||
msecs_to_jiffies(60));
|
||||
} else {
|
||||
|
@ -347,15 +348,12 @@ static void dwc2_port_intr(struct dwc2_hsotg *hsotg)
|
|||
* Set flag and clear if detected
|
||||
*/
|
||||
if (hprt0 & HPRT0_CONNDET) {
|
||||
dwc2_writel(hprt0_modify | HPRT0_CONNDET, hsotg->regs + HPRT0);
|
||||
|
||||
dev_vdbg(hsotg->dev,
|
||||
"--Port Interrupt HPRT0=0x%08x Port Connect Detected--\n",
|
||||
hprt0);
|
||||
if (hsotg->lx_state != DWC2_L0)
|
||||
usb_hcd_resume_root_hub(hsotg->priv);
|
||||
|
||||
hsotg->flags.b.port_connect_status_change = 1;
|
||||
hsotg->flags.b.port_connect_status = 1;
|
||||
hprt0_modify |= HPRT0_CONNDET;
|
||||
dwc2_hcd_connect(hsotg);
|
||||
|
||||
/*
|
||||
* The Hub driver asserts a reset when it sees port connect
|
||||
|
@ -368,27 +366,36 @@ static void dwc2_port_intr(struct dwc2_hsotg *hsotg)
|
|||
* Clear if detected - Set internal flag if disabled
|
||||
*/
|
||||
if (hprt0 & HPRT0_ENACHG) {
|
||||
dwc2_writel(hprt0_modify | HPRT0_ENACHG, hsotg->regs + HPRT0);
|
||||
dev_vdbg(hsotg->dev,
|
||||
" --Port Interrupt HPRT0=0x%08x Port Enable Changed (now %d)--\n",
|
||||
hprt0, !!(hprt0 & HPRT0_ENA));
|
||||
hprt0_modify |= HPRT0_ENACHG;
|
||||
if (hprt0 & HPRT0_ENA)
|
||||
if (hprt0 & HPRT0_ENA) {
|
||||
hsotg->new_connection = true;
|
||||
dwc2_hprt0_enable(hsotg, hprt0, &hprt0_modify);
|
||||
else
|
||||
} else {
|
||||
hsotg->flags.b.port_enable_change = 1;
|
||||
if (hsotg->core_params->dma_desc_fs_enable) {
|
||||
u32 hcfg;
|
||||
|
||||
hsotg->core_params->dma_desc_enable = 0;
|
||||
hsotg->new_connection = false;
|
||||
hcfg = dwc2_readl(hsotg->regs + HCFG);
|
||||
hcfg &= ~HCFG_DESCDMA;
|
||||
dwc2_writel(hcfg, hsotg->regs + HCFG);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Overcurrent Change Interrupt */
|
||||
if (hprt0 & HPRT0_OVRCURRCHG) {
|
||||
dwc2_writel(hprt0_modify | HPRT0_OVRCURRCHG,
|
||||
hsotg->regs + HPRT0);
|
||||
dev_vdbg(hsotg->dev,
|
||||
" --Port Interrupt HPRT0=0x%08x Port Overcurrent Changed--\n",
|
||||
hprt0);
|
||||
hsotg->flags.b.port_over_current_change = 1;
|
||||
hprt0_modify |= HPRT0_OVRCURRCHG;
|
||||
}
|
||||
|
||||
/* Clear Port Interrupts */
|
||||
dwc2_writel(hprt0_modify, hsotg->regs + HPRT0);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -232,7 +232,7 @@ struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
|
|||
*/
|
||||
void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
|
||||
{
|
||||
if (hsotg->core_params->dma_desc_enable > 0) {
|
||||
if (qh->desc_list) {
|
||||
dwc2_hcd_qh_free_ddma(hsotg, qh);
|
||||
} else {
|
||||
/* kfree(NULL) is safe */
|
||||
|
|
|
@ -769,10 +769,6 @@
|
|||
#define TSIZ_XFERSIZE_SHIFT 0
|
||||
|
||||
#define HCDMA(_ch) HSOTG_REG(0x0514 + 0x20 * (_ch))
|
||||
#define HCDMA_DMA_ADDR_MASK (0x1fffff << 11)
|
||||
#define HCDMA_DMA_ADDR_SHIFT 11
|
||||
#define HCDMA_CTD_MASK (0xff << 3)
|
||||
#define HCDMA_CTD_SHIFT 3
|
||||
|
||||
#define HCDMAB(_ch) HSOTG_REG(0x051c + 0x20 * (_ch))
|
||||
|
||||
|
|
|
@ -54,11 +54,44 @@
|
|||
|
||||
static const char dwc2_driver_name[] = "dwc2";
|
||||
|
||||
static const struct dwc2_core_params params_hi6220 = {
|
||||
.otg_cap = 2, /* No HNP/SRP capable */
|
||||
.otg_ver = 0, /* 1.3 */
|
||||
.dma_enable = 1,
|
||||
.dma_desc_enable = 0,
|
||||
.dma_desc_fs_enable = 0,
|
||||
.speed = 0, /* High Speed */
|
||||
.enable_dynamic_fifo = 1,
|
||||
.en_multiple_tx_fifo = 1,
|
||||
.host_rx_fifo_size = 512,
|
||||
.host_nperio_tx_fifo_size = 512,
|
||||
.host_perio_tx_fifo_size = 512,
|
||||
.max_transfer_size = 65535,
|
||||
.max_packet_count = 511,
|
||||
.host_channels = 16,
|
||||
.phy_type = 1, /* UTMI */
|
||||
.phy_utmi_width = 8,
|
||||
.phy_ulpi_ddr = 0, /* Single */
|
||||
.phy_ulpi_ext_vbus = 0,
|
||||
.i2c_enable = 0,
|
||||
.ulpi_fs_ls = 0,
|
||||
.host_support_fs_ls_low_power = 0,
|
||||
.host_ls_low_power_phy_clk = 0, /* 48 MHz */
|
||||
.ts_dline = 0,
|
||||
.reload_ctl = 0,
|
||||
.ahbcfg = GAHBCFG_HBSTLEN_INCR16 <<
|
||||
GAHBCFG_HBSTLEN_SHIFT,
|
||||
.uframe_sched = 0,
|
||||
.external_id_pin_ctl = -1,
|
||||
.hibernation = -1,
|
||||
};
|
||||
|
||||
static const struct dwc2_core_params params_bcm2835 = {
|
||||
.otg_cap = 0, /* HNP/SRP capable */
|
||||
.otg_ver = 0, /* 1.3 */
|
||||
.dma_enable = 1,
|
||||
.dma_desc_enable = 0,
|
||||
.dma_desc_fs_enable = 0,
|
||||
.speed = 0, /* High Speed */
|
||||
.enable_dynamic_fifo = 1,
|
||||
.en_multiple_tx_fifo = 1,
|
||||
|
@ -89,6 +122,7 @@ static const struct dwc2_core_params params_rk3066 = {
|
|||
.otg_ver = -1,
|
||||
.dma_enable = -1,
|
||||
.dma_desc_enable = 0,
|
||||
.dma_desc_fs_enable = 0,
|
||||
.speed = -1,
|
||||
.enable_dynamic_fifo = 1,
|
||||
.en_multiple_tx_fifo = -1,
|
||||
|
@ -115,6 +149,71 @@ static const struct dwc2_core_params params_rk3066 = {
|
|||
.hibernation = -1,
|
||||
};
|
||||
|
||||
/*
|
||||
* Check the dr_mode against the module configuration and hardware
|
||||
* capabilities.
|
||||
*
|
||||
* The hardware, module, and dr_mode, can each be set to host, device,
|
||||
* or otg. Check that all these values are compatible and adjust the
|
||||
* value of dr_mode if possible.
|
||||
*
|
||||
* actual
|
||||
* HW MOD dr_mode dr_mode
|
||||
* ------------------------------
|
||||
* HST HST any : HST
|
||||
* HST DEV any : ---
|
||||
* HST OTG any : HST
|
||||
*
|
||||
* DEV HST any : ---
|
||||
* DEV DEV any : DEV
|
||||
* DEV OTG any : DEV
|
||||
*
|
||||
* OTG HST any : HST
|
||||
* OTG DEV any : DEV
|
||||
* OTG OTG any : dr_mode
|
||||
*/
|
||||
static int dwc2_get_dr_mode(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
enum usb_dr_mode mode;
|
||||
|
||||
hsotg->dr_mode = usb_get_dr_mode(hsotg->dev);
|
||||
if (hsotg->dr_mode == USB_DR_MODE_UNKNOWN)
|
||||
hsotg->dr_mode = USB_DR_MODE_OTG;
|
||||
|
||||
mode = hsotg->dr_mode;
|
||||
|
||||
if (dwc2_hw_is_device(hsotg)) {
|
||||
if (IS_ENABLED(CONFIG_USB_DWC2_HOST)) {
|
||||
dev_err(hsotg->dev,
|
||||
"Controller does not support host mode.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
mode = USB_DR_MODE_PERIPHERAL;
|
||||
} else if (dwc2_hw_is_host(hsotg)) {
|
||||
if (IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL)) {
|
||||
dev_err(hsotg->dev,
|
||||
"Controller does not support device mode.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
mode = USB_DR_MODE_HOST;
|
||||
} else {
|
||||
if (IS_ENABLED(CONFIG_USB_DWC2_HOST))
|
||||
mode = USB_DR_MODE_HOST;
|
||||
else if (IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL))
|
||||
mode = USB_DR_MODE_PERIPHERAL;
|
||||
}
|
||||
|
||||
if (mode != hsotg->dr_mode) {
|
||||
dev_warn(hsotg->dev,
|
||||
"Configuration mismatch. dr_mode forced to %s\n",
|
||||
mode == USB_DR_MODE_HOST ? "host" : "device");
|
||||
|
||||
hsotg->dr_mode = mode;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(hsotg->dev);
|
||||
|
@ -306,8 +405,28 @@ static int dwc2_driver_remove(struct platform_device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_driver_shutdown() - Called on device shutdown
|
||||
*
|
||||
* @dev: Platform device
|
||||
*
|
||||
* In specific conditions (involving usb hubs) dwc2 devices can create a
|
||||
* lot of interrupts, even to the point of overwhelming devices running
|
||||
* at low frequencies. Some devices need to do special clock handling
|
||||
* at shutdown-time which may bring the system clock below the threshold
|
||||
* of being able to handle the dwc2 interrupts. Disabling dwc2-irqs
|
||||
* prevents reboots/poweroffs from getting stuck in such cases.
|
||||
*/
|
||||
static void dwc2_driver_shutdown(struct platform_device *dev)
|
||||
{
|
||||
struct dwc2_hsotg *hsotg = platform_get_drvdata(dev);
|
||||
|
||||
disable_irq(hsotg->irq);
|
||||
}
|
||||
|
||||
static const struct of_device_id dwc2_of_match_table[] = {
|
||||
{ .compatible = "brcm,bcm2835-usb", .data = ¶ms_bcm2835 },
|
||||
{ .compatible = "hisilicon,hi6220-usb", .data = ¶ms_hi6220 },
|
||||
{ .compatible = "rockchip,rk3066-usb", .data = ¶ms_rk3066 },
|
||||
{ .compatible = "snps,dwc2", .data = NULL },
|
||||
{ .compatible = "samsung,s3c6400-hsotg", .data = NULL},
|
||||
|
@ -335,7 +454,6 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
|||
struct dwc2_hsotg *hsotg;
|
||||
struct resource *res;
|
||||
int retval;
|
||||
int irq;
|
||||
|
||||
match = of_match_device(dwc2_of_match_table, &dev->dev);
|
||||
if (match && match->data) {
|
||||
|
@ -348,8 +466,10 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
|||
/*
|
||||
* Disable descriptor dma mode by default as the HW can support
|
||||
* it, but does not support it for SPLIT transactions.
|
||||
* Disable it for FS devices as well.
|
||||
*/
|
||||
defparams.dma_desc_enable = 0;
|
||||
defparams.dma_desc_fs_enable = 0;
|
||||
}
|
||||
|
||||
hsotg = devm_kzalloc(&dev->dev, sizeof(*hsotg), GFP_KERNEL);
|
||||
|
@ -375,19 +495,6 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
|||
dev_dbg(&dev->dev, "mapped PA %08lx to VA %p\n",
|
||||
(unsigned long)res->start, hsotg->regs);
|
||||
|
||||
hsotg->dr_mode = usb_get_dr_mode(&dev->dev);
|
||||
if (IS_ENABLED(CONFIG_USB_DWC2_HOST) &&
|
||||
hsotg->dr_mode != USB_DR_MODE_HOST) {
|
||||
hsotg->dr_mode = USB_DR_MODE_HOST;
|
||||
dev_warn(hsotg->dev,
|
||||
"Configuration mismatch. Forcing host mode\n");
|
||||
} else if (IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) &&
|
||||
hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) {
|
||||
hsotg->dr_mode = USB_DR_MODE_PERIPHERAL;
|
||||
dev_warn(hsotg->dev,
|
||||
"Configuration mismatch. Forcing peripheral mode\n");
|
||||
}
|
||||
|
||||
retval = dwc2_lowlevel_hw_init(hsotg);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
@ -401,15 +508,15 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
|||
|
||||
dwc2_set_all_params(hsotg->core_params, -1);
|
||||
|
||||
irq = platform_get_irq(dev, 0);
|
||||
if (irq < 0) {
|
||||
hsotg->irq = platform_get_irq(dev, 0);
|
||||
if (hsotg->irq < 0) {
|
||||
dev_err(&dev->dev, "missing IRQ resource\n");
|
||||
return irq;
|
||||
return hsotg->irq;
|
||||
}
|
||||
|
||||
dev_dbg(hsotg->dev, "registering common handler for irq%d\n",
|
||||
irq);
|
||||
retval = devm_request_irq(hsotg->dev, irq,
|
||||
hsotg->irq);
|
||||
retval = devm_request_irq(hsotg->dev, hsotg->irq,
|
||||
dwc2_handle_common_intr, IRQF_SHARED,
|
||||
dev_name(hsotg->dev), hsotg);
|
||||
if (retval)
|
||||
|
@ -419,7 +526,11 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
|||
if (retval)
|
||||
return retval;
|
||||
|
||||
/* Detect config values from hardware */
|
||||
retval = dwc2_get_dr_mode(hsotg);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
/* Reset the controller and detect hardware config values */
|
||||
retval = dwc2_get_hwparams(hsotg);
|
||||
if (retval)
|
||||
goto error;
|
||||
|
@ -427,15 +538,17 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
|||
/* Validate parameter values */
|
||||
dwc2_set_parameters(hsotg, params);
|
||||
|
||||
dwc2_force_dr_mode(hsotg);
|
||||
|
||||
if (hsotg->dr_mode != USB_DR_MODE_HOST) {
|
||||
retval = dwc2_gadget_init(hsotg, irq);
|
||||
retval = dwc2_gadget_init(hsotg, hsotg->irq);
|
||||
if (retval)
|
||||
goto error;
|
||||
hsotg->gadget_enabled = 1;
|
||||
}
|
||||
|
||||
if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) {
|
||||
retval = dwc2_hcd_init(hsotg, irq);
|
||||
retval = dwc2_hcd_init(hsotg, hsotg->irq);
|
||||
if (retval) {
|
||||
if (hsotg->gadget_enabled)
|
||||
dwc2_hsotg_remove(hsotg);
|
||||
|
@ -502,6 +615,7 @@ static struct platform_driver dwc2_platform_driver = {
|
|||
},
|
||||
.probe = dwc2_driver_probe,
|
||||
.remove = dwc2_driver_remove,
|
||||
.shutdown = dwc2_driver_shutdown,
|
||||
};
|
||||
|
||||
module_platform_driver(dwc2_platform_driver);
|
||||
|
|
|
@ -87,6 +87,15 @@ config USB_DWC3_KEYSTONE
|
|||
Support of USB2/3 functionality in TI Keystone2 platforms.
|
||||
Say 'Y' or 'M' here if you have one such device
|
||||
|
||||
config USB_DWC3_OF_SIMPLE
|
||||
tristate "Generic OF Simple Glue Layer"
|
||||
depends on OF && COMMON_CLK
|
||||
default USB_DWC3
|
||||
help
|
||||
Support USB2/3 functionality in simple SoC integrations.
|
||||
Currently supports Xilinx and Qualcomm DWC USB3 IP.
|
||||
Say 'Y' or 'M' if you have one such device.
|
||||
|
||||
config USB_DWC3_ST
|
||||
tristate "STMicroelectronics Platforms"
|
||||
depends on ARCH_STI && OF
|
||||
|
@ -96,12 +105,4 @@ config USB_DWC3_ST
|
|||
inside (i.e. STiH407).
|
||||
Say 'Y' or 'M' if you have one such device.
|
||||
|
||||
config USB_DWC3_QCOM
|
||||
tristate "Qualcomm Platforms"
|
||||
depends on ARCH_QCOM || COMPILE_TEST
|
||||
default USB_DWC3
|
||||
help
|
||||
Recent Qualcomm SoCs ship with one DesignWare Core USB3 IP inside,
|
||||
say 'Y' or 'M' if you have one such device.
|
||||
|
||||
endif
|
||||
|
|
|
@ -37,5 +37,5 @@ obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o
|
|||
obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o
|
||||
obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o
|
||||
obj-$(CONFIG_USB_DWC3_KEYSTONE) += dwc3-keystone.o
|
||||
obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o
|
||||
obj-$(CONFIG_USB_DWC3_OF_SIMPLE) += dwc3-of-simple.o
|
||||
obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o
|
||||
|
|
|
@ -272,7 +272,8 @@ static int dwc3_event_buffers_setup(struct dwc3 *dwc)
|
|||
|
||||
for (n = 0; n < dwc->num_event_buffers; n++) {
|
||||
evt = dwc->ev_buffs[n];
|
||||
dev_dbg(dwc->dev, "Event buf %p dma %08llx length %d\n",
|
||||
dwc3_trace(trace_dwc3_core,
|
||||
"Event buf %p dma %08llx length %d\n",
|
||||
evt->buf, (unsigned long long) evt->dma,
|
||||
evt->length);
|
||||
|
||||
|
@ -608,12 +609,13 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
|||
reg |= DWC3_GCTL_GBLHIBERNATIONEN;
|
||||
break;
|
||||
default:
|
||||
dev_dbg(dwc->dev, "No power optimization available\n");
|
||||
dwc3_trace(trace_dwc3_core, "No power optimization available\n");
|
||||
}
|
||||
|
||||
/* check if current dwc3 is on simulation board */
|
||||
if (dwc->hwparams.hwparams6 & DWC3_GHWPARAMS6_EN_FPGA) {
|
||||
dev_dbg(dwc->dev, "it is on FPGA board\n");
|
||||
dwc3_trace(trace_dwc3_core,
|
||||
"running on FPGA platform\n");
|
||||
dwc->is_fpga = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#define DWC3_MSG_MAX 500
|
||||
|
||||
/* Global constants */
|
||||
#define DWC3_ZLP_BUF_SIZE 1024 /* size of a superspeed bulk */
|
||||
#define DWC3_EP0_BOUNCE_SIZE 512
|
||||
#define DWC3_ENDPOINTS_NUM 32
|
||||
#define DWC3_XHCI_RESOURCES_NUM 2
|
||||
|
@ -647,6 +648,7 @@ struct dwc3_scratchpad_array {
|
|||
* @ctrl_req: usb control request which is used for ep0
|
||||
* @ep0_trb: trb which is used for the ctrl_req
|
||||
* @ep0_bounce: bounce buffer for ep0
|
||||
* @zlp_buf: used when request->zero is set
|
||||
* @setup_buf: used while precessing STD USB requests
|
||||
* @ctrl_req_addr: dma address of ctrl_req
|
||||
* @ep0_trb: dma address of ep0_trb
|
||||
|
@ -734,6 +736,7 @@ struct dwc3 {
|
|||
struct usb_ctrlrequest *ctrl_req;
|
||||
struct dwc3_trb *ep0_trb;
|
||||
void *ep0_bounce;
|
||||
void *zlp_buf;
|
||||
void *scratchbuf;
|
||||
u8 *setup_buf;
|
||||
dma_addr_t ctrl_req_addr;
|
||||
|
|
180
drivers/usb/dwc3/dwc3-of-simple.c
Normal file
180
drivers/usb/dwc3/dwc3-of-simple.c
Normal file
|
@ -0,0 +1,180 @@
|
|||
/**
|
||||
* dwc3-of-simple.c - OF glue layer for simple integrations
|
||||
*
|
||||
* Copyright (c) 2015 Texas Instruments Incorporated - http://www.ti.com
|
||||
*
|
||||
* Author: Felipe Balbi <balbi@ti.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 of
|
||||
* the License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* This is a combination of the old dwc3-qcom.c by Ivan T. Ivanov
|
||||
* <iivanov@mm-sol.com> and the original patch adding support for Xilinx' SoC
|
||||
* by Subbaraya Sundeep Bhatta <subbaraya.sundeep.bhatta@xilinx.com>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
struct dwc3_of_simple {
|
||||
struct device *dev;
|
||||
struct clk **clks;
|
||||
int num_clocks;
|
||||
};
|
||||
|
||||
static int dwc3_of_simple_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3_of_simple *simple;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL);
|
||||
if (!simple)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = of_clk_get_parent_count(np);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
simple->num_clocks = ret;
|
||||
|
||||
simple->clks = devm_kcalloc(dev, simple->num_clocks,
|
||||
sizeof(struct clk *), GFP_KERNEL);
|
||||
if (!simple->clks)
|
||||
return -ENOMEM;
|
||||
|
||||
simple->dev = dev;
|
||||
|
||||
for (i = 0; i < simple->num_clocks; i++) {
|
||||
struct clk *clk;
|
||||
|
||||
clk = of_clk_get(np, i);
|
||||
if (IS_ERR(clk)) {
|
||||
while (--i >= 0)
|
||||
clk_put(simple->clks[i]);
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret < 0) {
|
||||
while (--i >= 0) {
|
||||
clk_disable_unprepare(simple->clks[i]);
|
||||
clk_put(simple->clks[i]);
|
||||
}
|
||||
clk_put(clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
simple->clks[i] = clk;
|
||||
}
|
||||
|
||||
ret = of_platform_populate(np, NULL, NULL, dev);
|
||||
if (ret) {
|
||||
for (i = 0; i < simple->num_clocks; i++) {
|
||||
clk_disable_unprepare(simple->clks[i]);
|
||||
clk_put(simple->clks[i]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_get_sync(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_of_simple_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3_of_simple *simple = platform_get_drvdata(pdev);
|
||||
struct device *dev = &pdev->dev;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < simple->num_clocks; i++) {
|
||||
clk_unprepare(simple->clks[i]);
|
||||
clk_put(simple->clks[i]);
|
||||
}
|
||||
|
||||
of_platform_depopulate(dev);
|
||||
|
||||
pm_runtime_put_sync(dev);
|
||||
pm_runtime_disable(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int dwc3_of_simple_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct dwc3_of_simple *simple = dev_get_drvdata(dev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < simple->num_clocks; i++)
|
||||
clk_disable(simple->clks[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_of_simple_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct dwc3_of_simple *simple = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < simple->num_clocks; i++) {
|
||||
ret = clk_enable(simple->clks[i]);
|
||||
if (ret < 0) {
|
||||
while (--i >= 0)
|
||||
clk_disable(simple->clks[i]);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops dwc3_of_simple_dev_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(dwc3_of_simple_runtime_suspend,
|
||||
dwc3_of_simple_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct of_device_id of_dwc3_simple_match[] = {
|
||||
{ .compatible = "qcom,dwc3" },
|
||||
{ .compatible = "xlnx,zynqmp-dwc3" },
|
||||
{ /* Sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_dwc3_simple_match);
|
||||
|
||||
static struct platform_driver dwc3_of_simple_driver = {
|
||||
.probe = dwc3_of_simple_probe,
|
||||
.remove = dwc3_of_simple_remove,
|
||||
.driver = {
|
||||
.name = "dwc3-of-simple",
|
||||
.of_match_table = of_dwc3_simple_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(dwc3_of_simple_driver);
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("DesignWare USB3 OF Simple Glue Layer");
|
||||
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
|
|
@ -1,130 +0,0 @@
|
|||
/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
struct dwc3_qcom {
|
||||
struct device *dev;
|
||||
|
||||
struct clk *core_clk;
|
||||
struct clk *iface_clk;
|
||||
struct clk *sleep_clk;
|
||||
};
|
||||
|
||||
static int dwc3_qcom_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct dwc3_qcom *qdwc;
|
||||
int ret;
|
||||
|
||||
qdwc = devm_kzalloc(&pdev->dev, sizeof(*qdwc), GFP_KERNEL);
|
||||
if (!qdwc)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, qdwc);
|
||||
|
||||
qdwc->dev = &pdev->dev;
|
||||
|
||||
qdwc->core_clk = devm_clk_get(qdwc->dev, "core");
|
||||
if (IS_ERR(qdwc->core_clk)) {
|
||||
dev_err(qdwc->dev, "failed to get core clock\n");
|
||||
return PTR_ERR(qdwc->core_clk);
|
||||
}
|
||||
|
||||
qdwc->iface_clk = devm_clk_get(qdwc->dev, "iface");
|
||||
if (IS_ERR(qdwc->iface_clk)) {
|
||||
dev_info(qdwc->dev, "failed to get optional iface clock\n");
|
||||
qdwc->iface_clk = NULL;
|
||||
}
|
||||
|
||||
qdwc->sleep_clk = devm_clk_get(qdwc->dev, "sleep");
|
||||
if (IS_ERR(qdwc->sleep_clk)) {
|
||||
dev_info(qdwc->dev, "failed to get optional sleep clock\n");
|
||||
qdwc->sleep_clk = NULL;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(qdwc->core_clk);
|
||||
if (ret) {
|
||||
dev_err(qdwc->dev, "failed to enable core clock\n");
|
||||
goto err_core;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(qdwc->iface_clk);
|
||||
if (ret) {
|
||||
dev_err(qdwc->dev, "failed to enable optional iface clock\n");
|
||||
goto err_iface;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(qdwc->sleep_clk);
|
||||
if (ret) {
|
||||
dev_err(qdwc->dev, "failed to enable optional sleep clock\n");
|
||||
goto err_sleep;
|
||||
}
|
||||
|
||||
ret = of_platform_populate(node, NULL, NULL, qdwc->dev);
|
||||
if (ret) {
|
||||
dev_err(qdwc->dev, "failed to register core - %d\n", ret);
|
||||
goto err_clks;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_clks:
|
||||
clk_disable_unprepare(qdwc->sleep_clk);
|
||||
err_sleep:
|
||||
clk_disable_unprepare(qdwc->iface_clk);
|
||||
err_iface:
|
||||
clk_disable_unprepare(qdwc->core_clk);
|
||||
err_core:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dwc3_qcom_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3_qcom *qdwc = platform_get_drvdata(pdev);
|
||||
|
||||
of_platform_depopulate(&pdev->dev);
|
||||
|
||||
clk_disable_unprepare(qdwc->sleep_clk);
|
||||
clk_disable_unprepare(qdwc->iface_clk);
|
||||
clk_disable_unprepare(qdwc->core_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id of_dwc3_match[] = {
|
||||
{ .compatible = "qcom,dwc3" },
|
||||
{ /* Sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_dwc3_match);
|
||||
|
||||
static struct platform_driver dwc3_qcom_driver = {
|
||||
.probe = dwc3_qcom_probe,
|
||||
.remove = dwc3_qcom_remove,
|
||||
.driver = {
|
||||
.name = "qcom-dwc3",
|
||||
.of_match_table = of_dwc3_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(dwc3_qcom_driver);
|
||||
|
||||
MODULE_ALIAS("platform:qcom-dwc3");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("DesignWare USB3 QCOM Glue Layer");
|
||||
MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");
|
|
@ -817,6 +817,8 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
|
|||
|
||||
status = DWC3_TRB_SIZE_TRBSTS(trb->size);
|
||||
if (status == DWC3_TRBSTS_SETUP_PENDING) {
|
||||
dwc->setup_packet_pending = true;
|
||||
|
||||
dwc3_trace(trace_dwc3_ep0, "Setup Pending received");
|
||||
|
||||
if (r)
|
||||
|
@ -916,8 +918,10 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
|
|||
}
|
||||
|
||||
status = DWC3_TRB_SIZE_TRBSTS(trb->size);
|
||||
if (status == DWC3_TRBSTS_SETUP_PENDING)
|
||||
if (status == DWC3_TRBSTS_SETUP_PENDING) {
|
||||
dwc->setup_packet_pending = true;
|
||||
dwc3_trace(trace_dwc3_ep0, "Setup Pending received");
|
||||
}
|
||||
|
||||
dwc->ep0state = EP0_SETUP_PHASE;
|
||||
dwc3_ep0_out_start(dwc);
|
||||
|
@ -971,7 +975,7 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
|
|||
ret = usb_gadget_map_request(&dwc->gadget, &req->request,
|
||||
dep->number);
|
||||
if (ret) {
|
||||
dev_dbg(dwc->dev, "failed to map request\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "failed to map request\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -999,7 +1003,7 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
|
|||
ret = usb_gadget_map_request(&dwc->gadget, &req->request,
|
||||
dep->number);
|
||||
if (ret) {
|
||||
dev_dbg(dwc->dev, "failed to map request\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "failed to map request\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1063,8 +1067,6 @@ static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
|
|||
static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
|
||||
const struct dwc3_event_depevt *event)
|
||||
{
|
||||
dwc->setup_packet_pending = true;
|
||||
|
||||
switch (event->status) {
|
||||
case DEPEVT_STATUS_CONTROL_DATA:
|
||||
dwc3_trace(trace_dwc3_ep0, "Control Data");
|
||||
|
|
|
@ -265,9 +265,6 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
|
|||
usb_gadget_unmap_request(&dwc->gadget, &req->request,
|
||||
req->direction);
|
||||
|
||||
dev_dbg(dwc->dev, "request %p from %s completed %d/%d ===> %d\n",
|
||||
req, dep->name, req->request.actual,
|
||||
req->request.length, status);
|
||||
trace_dwc3_gadget_giveback(req);
|
||||
|
||||
spin_unlock(&dwc->lock);
|
||||
|
@ -664,11 +661,10 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
|
|||
dep = to_dwc3_ep(ep);
|
||||
dwc = dep->dwc;
|
||||
|
||||
if (dep->flags & DWC3_EP_ENABLED) {
|
||||
dev_WARN_ONCE(dwc->dev, true, "%s is already enabled\n",
|
||||
dep->name);
|
||||
if (dev_WARN_ONCE(dwc->dev, dep->flags & DWC3_EP_ENABLED,
|
||||
"%s is already enabled\n",
|
||||
dep->name))
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc, false, false);
|
||||
|
@ -692,11 +688,10 @@ static int dwc3_gadget_ep_disable(struct usb_ep *ep)
|
|||
dep = to_dwc3_ep(ep);
|
||||
dwc = dep->dwc;
|
||||
|
||||
if (!(dep->flags & DWC3_EP_ENABLED)) {
|
||||
dev_WARN_ONCE(dwc->dev, true, "%s is already disabled\n",
|
||||
dep->name);
|
||||
if (dev_WARN_ONCE(dwc->dev, !(dep->flags & DWC3_EP_ENABLED),
|
||||
"%s is already disabled\n",
|
||||
dep->name))
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
ret = __dwc3_gadget_ep_disable(dep);
|
||||
|
@ -985,8 +980,6 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
|
|||
cmd |= DWC3_DEPCMD_PARAM(cmd_param);
|
||||
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms);
|
||||
if (ret < 0) {
|
||||
dev_dbg(dwc->dev, "failed to send STARTTRANSFER command\n");
|
||||
|
||||
/*
|
||||
* FIXME we need to iterate over the list of requests
|
||||
* here and stop, unmap, free and del each of the linked
|
||||
|
@ -1044,6 +1037,20 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
|||
struct dwc3 *dwc = dep->dwc;
|
||||
int ret;
|
||||
|
||||
if (!dep->endpoint.desc) {
|
||||
dwc3_trace(trace_dwc3_gadget,
|
||||
"trying to queue request %p to disabled %s\n",
|
||||
&req->request, dep->endpoint.name);
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
|
||||
if (WARN(req->dep != dep, "request %p belongs to '%s'\n",
|
||||
&req->request, req->dep->name)) {
|
||||
dwc3_trace(trace_dwc3_gadget, "request %p belongs to '%s'\n",
|
||||
&req->request, req->dep->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
req->request.actual = 0;
|
||||
req->request.status = -EINPROGRESS;
|
||||
req->direction = dep->direction;
|
||||
|
@ -1141,7 +1148,8 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
|||
|
||||
out:
|
||||
if (ret && ret != -EBUSY)
|
||||
dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
|
||||
dwc3_trace(trace_dwc3_gadget,
|
||||
"%s: failed to kick transfers\n",
|
||||
dep->name);
|
||||
if (ret == -EBUSY)
|
||||
ret = 0;
|
||||
|
@ -1149,6 +1157,32 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void __dwc3_gadget_ep_zlp_complete(struct usb_ep *ep,
|
||||
struct usb_request *request)
|
||||
{
|
||||
dwc3_gadget_ep_free_request(ep, request);
|
||||
}
|
||||
|
||||
static int __dwc3_gadget_ep_queue_zlp(struct dwc3 *dwc, struct dwc3_ep *dep)
|
||||
{
|
||||
struct dwc3_request *req;
|
||||
struct usb_request *request;
|
||||
struct usb_ep *ep = &dep->endpoint;
|
||||
|
||||
dwc3_trace(trace_dwc3_gadget, "queueing ZLP\n");
|
||||
request = dwc3_gadget_ep_alloc_request(ep, GFP_ATOMIC);
|
||||
if (!request)
|
||||
return -ENOMEM;
|
||||
|
||||
request->length = 0;
|
||||
request->buf = dwc->zlp_buf;
|
||||
request->complete = __dwc3_gadget_ep_zlp_complete;
|
||||
|
||||
req = to_dwc3_request(request);
|
||||
|
||||
return __dwc3_gadget_ep_queue(dep, req);
|
||||
}
|
||||
|
||||
static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
|
||||
gfp_t gfp_flags)
|
||||
{
|
||||
|
@ -1161,22 +1195,18 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
|
|||
int ret;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
if (!dep->endpoint.desc) {
|
||||
dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n",
|
||||
request, ep->name);
|
||||
ret = -ESHUTDOWN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (WARN(req->dep != dep, "request %p belongs to '%s'\n",
|
||||
request, req->dep->name)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = __dwc3_gadget_ep_queue(dep, req);
|
||||
|
||||
out:
|
||||
/*
|
||||
* Okay, here's the thing, if gadget driver has requested for a ZLP by
|
||||
* setting request->zero, instead of doing magic, we will just queue an
|
||||
* extra usb_request ourselves so that it gets handled the same way as
|
||||
* any other request.
|
||||
*/
|
||||
if (ret == 0 && request->zero && request->length &&
|
||||
(request->length % ep->maxpacket == 0))
|
||||
ret = __dwc3_gadget_ep_queue_zlp(dwc, dep);
|
||||
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return ret;
|
||||
|
@ -1246,7 +1276,8 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
|
|||
if (!protocol && ((dep->direction && dep->flags & DWC3_EP_BUSY) ||
|
||||
(!list_empty(&dep->req_queued) ||
|
||||
!list_empty(&dep->request_list)))) {
|
||||
dev_dbg(dwc->dev, "%s: pending request, cannot halt\n",
|
||||
dwc3_trace(trace_dwc3_gadget,
|
||||
"%s: pending request, cannot halt\n",
|
||||
dep->name);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
@ -1373,7 +1404,7 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
|
|||
|
||||
speed = reg & DWC3_DSTS_CONNECTSPD;
|
||||
if (speed == DWC3_DSTS_SUPERSPEED) {
|
||||
dev_dbg(dwc->dev, "no wakeup on SuperSpeed\n");
|
||||
dwc3_trace(trace_dwc3_gadget, "no wakeup on SuperSpeed\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
@ -1385,8 +1416,9 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
|
|||
case DWC3_LINK_STATE_U3: /* in HS, means SUSPEND */
|
||||
break;
|
||||
default:
|
||||
dev_dbg(dwc->dev, "can't wakeup from link state %d\n",
|
||||
link_state);
|
||||
dwc3_trace(trace_dwc3_gadget,
|
||||
"can't wakeup from '%s'\n",
|
||||
dwc3_gadget_link_string(link_state));
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
@ -1825,7 +1857,8 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
|||
if (count) {
|
||||
trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size);
|
||||
if (trb_status == DWC3_TRBSTS_MISSED_ISOC) {
|
||||
dev_dbg(dwc->dev, "incomplete IN transfer %s\n",
|
||||
dwc3_trace(trace_dwc3_gadget,
|
||||
"%s: incomplete IN transfer\n",
|
||||
dep->name);
|
||||
/*
|
||||
* If missed isoc occurred and there is
|
||||
|
@ -1887,10 +1920,9 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
|||
|
||||
do {
|
||||
req = next_request(&dep->req_queued);
|
||||
if (!req) {
|
||||
WARN_ON_ONCE(1);
|
||||
if (WARN_ON_ONCE(!req))
|
||||
return 1;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
do {
|
||||
slot = req->start_slot + i;
|
||||
|
@ -2004,7 +2036,8 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
|
|||
dep->resource_index = 0;
|
||||
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
||||
dev_dbg(dwc->dev, "%s is an Isochronous endpoint\n",
|
||||
dwc3_trace(trace_dwc3_gadget,
|
||||
"%s is an Isochronous endpoint\n",
|
||||
dep->name);
|
||||
return;
|
||||
}
|
||||
|
@ -2031,7 +2064,8 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
|
|||
if (!ret || ret == -EBUSY)
|
||||
return;
|
||||
|
||||
dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
|
||||
dwc3_trace(trace_dwc3_gadget,
|
||||
"%s: failed to kick transfers\n",
|
||||
dep->name);
|
||||
}
|
||||
|
||||
|
@ -2053,11 +2087,12 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
|
|||
case DEPEVT_STREAMEVT_NOTFOUND:
|
||||
/* FALLTHROUGH */
|
||||
default:
|
||||
dev_dbg(dwc->dev, "Couldn't find suitable stream\n");
|
||||
dwc3_trace(trace_dwc3_gadget,
|
||||
"unable to find suitable stream\n");
|
||||
}
|
||||
break;
|
||||
case DWC3_DEPEVT_RXTXFIFOEVT:
|
||||
dev_dbg(dwc->dev, "%s FIFO Overrun\n", dep->name);
|
||||
dwc3_trace(trace_dwc3_gadget, "%s FIFO Overrun\n", dep->name);
|
||||
break;
|
||||
case DWC3_DEPEVT_EPCMDCMPLT:
|
||||
dwc3_trace(trace_dwc3_gadget, "Endpoint Command Complete");
|
||||
|
@ -2230,8 +2265,8 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
|
|||
*
|
||||
* Our suggested workaround is to follow the Disconnect
|
||||
* Event steps here, instead, based on a setup_packet_pending
|
||||
* flag. Such flag gets set whenever we have a XferNotReady
|
||||
* event on EP0 and gets cleared on XferComplete for the
|
||||
* flag. Such flag gets set whenever we have a SETUP_PENDING
|
||||
* status for EP0 TRBs and gets cleared on XferComplete for the
|
||||
* same endpoint.
|
||||
*
|
||||
* Refers to:
|
||||
|
@ -2744,6 +2779,12 @@ int dwc3_gadget_init(struct dwc3 *dwc)
|
|||
goto err3;
|
||||
}
|
||||
|
||||
dwc->zlp_buf = kzalloc(DWC3_ZLP_BUF_SIZE, GFP_KERNEL);
|
||||
if (!dwc->zlp_buf) {
|
||||
ret = -ENOMEM;
|
||||
goto err4;
|
||||
}
|
||||
|
||||
dwc->gadget.ops = &dwc3_gadget_ops;
|
||||
dwc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
dwc->gadget.sg_supported = true;
|
||||
|
@ -2785,16 +2826,19 @@ int dwc3_gadget_init(struct dwc3 *dwc)
|
|||
|
||||
ret = dwc3_gadget_init_endpoints(dwc);
|
||||
if (ret)
|
||||
goto err4;
|
||||
goto err5;
|
||||
|
||||
ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to register udc\n");
|
||||
goto err4;
|
||||
goto err5;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err5:
|
||||
kfree(dwc->zlp_buf);
|
||||
|
||||
err4:
|
||||
dwc3_gadget_free_endpoints(dwc);
|
||||
dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE,
|
||||
|
@ -2827,6 +2871,7 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
|
|||
dwc->ep0_bounce, dwc->ep0_bounce_addr);
|
||||
|
||||
kfree(dwc->setup_buf);
|
||||
kfree(dwc->zlp_buf);
|
||||
|
||||
dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb),
|
||||
dwc->ep0_trb, dwc->ep0_trb_addr);
|
||||
|
|
|
@ -117,6 +117,9 @@ DECLARE_EVENT_CLASS(dwc3_log_request,
|
|||
__field(unsigned, actual)
|
||||
__field(unsigned, length)
|
||||
__field(int, status)
|
||||
__field(int, zero)
|
||||
__field(int, short_not_ok)
|
||||
__field(int, no_interrupt)
|
||||
),
|
||||
TP_fast_assign(
|
||||
snprintf(__get_str(name), DWC3_MSG_MAX, "%s", req->dep->name);
|
||||
|
@ -124,9 +127,15 @@ DECLARE_EVENT_CLASS(dwc3_log_request,
|
|||
__entry->actual = req->request.actual;
|
||||
__entry->length = req->request.length;
|
||||
__entry->status = req->request.status;
|
||||
__entry->zero = req->request.zero;
|
||||
__entry->short_not_ok = req->request.short_not_ok;
|
||||
__entry->no_interrupt = req->request.no_interrupt;
|
||||
),
|
||||
TP_printk("%s: req %p length %u/%u ==> %d",
|
||||
TP_printk("%s: req %p length %u/%u %s%s%s ==> %d",
|
||||
__get_str(name), __entry->req, __entry->actual, __entry->length,
|
||||
__entry->zero ? "Z" : "z",
|
||||
__entry->short_not_ok ? "S" : "s",
|
||||
__entry->no_interrupt ? "i" : "I",
|
||||
__entry->status
|
||||
)
|
||||
);
|
||||
|
|
|
@ -127,6 +127,12 @@ config USB_GADGET_STORAGE_NUM_BUFFERS
|
|||
a module parameter as well.
|
||||
If unsure, say 2.
|
||||
|
||||
config U_SERIAL_CONSOLE
|
||||
bool "Serial gadget console support"
|
||||
depends on USB_G_SERIAL
|
||||
help
|
||||
It supports the serial gadget can be used as a console.
|
||||
|
||||
source "drivers/usb/gadget/udc/Kconfig"
|
||||
|
||||
#
|
||||
|
|
|
@ -56,7 +56,6 @@ struct gadget_info {
|
|||
struct list_head string_list;
|
||||
struct list_head available_func;
|
||||
|
||||
const char *udc_name;
|
||||
struct usb_composite_driver composite;
|
||||
struct usb_composite_dev cdev;
|
||||
bool use_os_desc;
|
||||
|
@ -233,21 +232,23 @@ static ssize_t gadget_dev_desc_bcdUSB_store(struct config_item *item,
|
|||
|
||||
static ssize_t gadget_dev_desc_UDC_show(struct config_item *item, char *page)
|
||||
{
|
||||
return sprintf(page, "%s\n", to_gadget_info(item)->udc_name ?: "");
|
||||
char *udc_name = to_gadget_info(item)->composite.gadget_driver.udc_name;
|
||||
|
||||
return sprintf(page, "%s\n", udc_name ?: "");
|
||||
}
|
||||
|
||||
static int unregister_gadget(struct gadget_info *gi)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!gi->udc_name)
|
||||
if (!gi->composite.gadget_driver.udc_name)
|
||||
return -ENODEV;
|
||||
|
||||
ret = usb_gadget_unregister_driver(&gi->composite.gadget_driver);
|
||||
if (ret)
|
||||
return ret;
|
||||
kfree(gi->udc_name);
|
||||
gi->udc_name = NULL;
|
||||
kfree(gi->composite.gadget_driver.udc_name);
|
||||
gi->composite.gadget_driver.udc_name = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -271,14 +272,16 @@ static ssize_t gadget_dev_desc_UDC_store(struct config_item *item,
|
|||
if (ret)
|
||||
goto err;
|
||||
} else {
|
||||
if (gi->udc_name) {
|
||||
if (gi->composite.gadget_driver.udc_name) {
|
||||
ret = -EBUSY;
|
||||
goto err;
|
||||
}
|
||||
ret = usb_udc_attach_driver(name, &gi->composite.gadget_driver);
|
||||
if (ret)
|
||||
gi->composite.gadget_driver.udc_name = name;
|
||||
ret = usb_gadget_probe_driver(&gi->composite.gadget_driver);
|
||||
if (ret) {
|
||||
gi->composite.gadget_driver.udc_name = NULL;
|
||||
goto err;
|
||||
gi->udc_name = name;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&gi->lock);
|
||||
return len;
|
||||
|
@ -427,9 +430,9 @@ static int config_usb_cfg_unlink(
|
|||
* remove the function.
|
||||
*/
|
||||
mutex_lock(&gi->lock);
|
||||
if (gi->udc_name)
|
||||
if (gi->composite.gadget_driver.udc_name)
|
||||
unregister_gadget(gi);
|
||||
WARN_ON(gi->udc_name);
|
||||
WARN_ON(gi->composite.gadget_driver.udc_name);
|
||||
|
||||
list_for_each_entry(f, &cfg->func_list, list) {
|
||||
if (f->fi == fi) {
|
||||
|
@ -873,10 +876,10 @@ static int os_desc_unlink(struct config_item *os_desc_ci,
|
|||
struct usb_composite_dev *cdev = &gi->cdev;
|
||||
|
||||
mutex_lock(&gi->lock);
|
||||
if (gi->udc_name)
|
||||
if (gi->composite.gadget_driver.udc_name)
|
||||
unregister_gadget(gi);
|
||||
cdev->os_desc_config = NULL;
|
||||
WARN_ON(gi->udc_name);
|
||||
WARN_ON(gi->composite.gadget_driver.udc_name);
|
||||
mutex_unlock(&gi->lock);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kfifo.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/initval.h>
|
||||
|
@ -75,6 +76,7 @@ struct f_midi {
|
|||
struct usb_ep *in_ep, *out_ep;
|
||||
struct snd_card *card;
|
||||
struct snd_rawmidi *rmidi;
|
||||
u8 ms_id;
|
||||
|
||||
struct snd_rawmidi_substream *in_substream[MAX_PORTS];
|
||||
struct snd_rawmidi_substream *out_substream[MAX_PORTS];
|
||||
|
@ -87,6 +89,9 @@ struct f_midi {
|
|||
int index;
|
||||
char *id;
|
||||
unsigned int buflen, qlen;
|
||||
/* This fifo is used as a buffer ring for pre-allocated IN usb_requests */
|
||||
DECLARE_KFIFO_PTR(in_req_fifo, struct usb_request *);
|
||||
unsigned int in_last_port;
|
||||
};
|
||||
|
||||
static inline struct f_midi *func_to_midi(struct usb_function *f)
|
||||
|
@ -94,7 +99,7 @@ static inline struct f_midi *func_to_midi(struct usb_function *f)
|
|||
return container_of(f, struct f_midi, func);
|
||||
}
|
||||
|
||||
static void f_midi_transmit(struct f_midi *midi, struct usb_request *req);
|
||||
static void f_midi_transmit(struct f_midi *midi);
|
||||
|
||||
DECLARE_UAC_AC_HEADER_DESCRIPTOR(1);
|
||||
DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1);
|
||||
|
@ -201,12 +206,6 @@ static inline struct usb_request *midi_alloc_ep_req(struct usb_ep *ep,
|
|||
return alloc_ep_req(ep, length, length);
|
||||
}
|
||||
|
||||
static void free_ep_req(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
kfree(req->buf);
|
||||
usb_ep_free_request(ep, req);
|
||||
}
|
||||
|
||||
static const uint8_t f_midi_cin_length[] = {
|
||||
0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1
|
||||
};
|
||||
|
@ -258,7 +257,8 @@ f_midi_complete(struct usb_ep *ep, struct usb_request *req)
|
|||
} else if (ep == midi->in_ep) {
|
||||
/* Our transmit completed. See if there's more to go.
|
||||
* f_midi_transmit eats req, don't queue it again. */
|
||||
f_midi_transmit(midi, req);
|
||||
req->length = 0;
|
||||
f_midi_transmit(midi);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
@ -269,10 +269,12 @@ f_midi_complete(struct usb_ep *ep, struct usb_request *req)
|
|||
case -ESHUTDOWN: /* disconnect from host */
|
||||
VDBG(cdev, "%s gone (%d), %d/%d\n", ep->name, status,
|
||||
req->actual, req->length);
|
||||
if (ep == midi->out_ep)
|
||||
if (ep == midi->out_ep) {
|
||||
f_midi_handle_out_data(ep, req);
|
||||
|
||||
free_ep_req(ep, req);
|
||||
/* We don't need to free IN requests because it's handled
|
||||
* by the midi->in_req_fifo. */
|
||||
free_ep_req(ep, req);
|
||||
}
|
||||
return;
|
||||
|
||||
case -EOVERFLOW: /* buffer overrun on read means that
|
||||
|
@ -324,12 +326,11 @@ static int f_midi_start_ep(struct f_midi *midi,
|
|||
static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
||||
{
|
||||
struct f_midi *midi = func_to_midi(f);
|
||||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
unsigned i;
|
||||
int err;
|
||||
|
||||
/* For Control Device interface we do nothing */
|
||||
if (intf == 0)
|
||||
/* we only set alt for MIDIStreaming interface */
|
||||
if (intf != midi->ms_id)
|
||||
return 0;
|
||||
|
||||
err = f_midi_start_ep(midi, f, midi->in_ep);
|
||||
|
@ -340,24 +341,20 @@ static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
usb_ep_disable(midi->out_ep);
|
||||
/* pre-allocate write usb requests to use on f_midi_transmit. */
|
||||
while (kfifo_avail(&midi->in_req_fifo)) {
|
||||
struct usb_request *req =
|
||||
midi_alloc_ep_req(midi->in_ep, midi->buflen);
|
||||
|
||||
err = config_ep_by_speed(midi->gadget, f, midi->out_ep);
|
||||
if (err) {
|
||||
ERROR(cdev, "can't configure %s: %d\n",
|
||||
midi->out_ep->name, err);
|
||||
return err;
|
||||
if (req == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
req->length = 0;
|
||||
req->complete = f_midi_complete;
|
||||
|
||||
kfifo_put(&midi->in_req_fifo, req);
|
||||
}
|
||||
|
||||
err = usb_ep_enable(midi->out_ep);
|
||||
if (err) {
|
||||
ERROR(cdev, "can't start %s: %d\n",
|
||||
midi->out_ep->name, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
midi->out_ep->driver_data = midi;
|
||||
|
||||
/* allocate a bunch of read buffers and queue them all at once. */
|
||||
for (i = 0; i < midi->qlen && err == 0; i++) {
|
||||
struct usb_request *req =
|
||||
|
@ -368,9 +365,10 @@ static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
|||
req->complete = f_midi_complete;
|
||||
err = usb_ep_queue(midi->out_ep, req, GFP_ATOMIC);
|
||||
if (err) {
|
||||
ERROR(midi, "%s queue req: %d\n",
|
||||
ERROR(midi, "%s: couldn't enqueue request: %d\n",
|
||||
midi->out_ep->name, err);
|
||||
free_ep_req(midi->out_ep, req);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -381,6 +379,7 @@ static void f_midi_disable(struct usb_function *f)
|
|||
{
|
||||
struct f_midi *midi = func_to_midi(f);
|
||||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
struct usb_request *req = NULL;
|
||||
|
||||
DBG(cdev, "disable\n");
|
||||
|
||||
|
@ -390,6 +389,10 @@ static void f_midi_disable(struct usb_function *f)
|
|||
*/
|
||||
usb_ep_disable(midi->in_ep);
|
||||
usb_ep_disable(midi->out_ep);
|
||||
|
||||
/* release IN requests */
|
||||
while (kfifo_get(&midi->in_req_fifo, &req))
|
||||
free_ep_req(midi->in_ep, req);
|
||||
}
|
||||
|
||||
static int f_midi_snd_free(struct snd_device *device)
|
||||
|
@ -511,57 +514,113 @@ static void f_midi_transmit_byte(struct usb_request *req,
|
|||
}
|
||||
}
|
||||
|
||||
static void f_midi_transmit(struct f_midi *midi, struct usb_request *req)
|
||||
static void f_midi_drop_out_substreams(struct f_midi *midi)
|
||||
{
|
||||
struct usb_ep *ep = midi->in_ep;
|
||||
int i;
|
||||
|
||||
if (!ep)
|
||||
return;
|
||||
|
||||
if (!req)
|
||||
req = midi_alloc_ep_req(ep, midi->buflen);
|
||||
|
||||
if (!req) {
|
||||
ERROR(midi, "%s: alloc_ep_request failed\n", __func__);
|
||||
return;
|
||||
}
|
||||
req->length = 0;
|
||||
req->complete = f_midi_complete;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < MAX_PORTS; i++) {
|
||||
struct gmidi_in_port *port = midi->in_port[i];
|
||||
struct snd_rawmidi_substream *substream = midi->in_substream[i];
|
||||
|
||||
if (!port || !port->active || !substream)
|
||||
if (!port)
|
||||
break;
|
||||
|
||||
if (!port->active || !substream)
|
||||
continue;
|
||||
|
||||
while (req->length + 3 < midi->buflen) {
|
||||
uint8_t b;
|
||||
if (snd_rawmidi_transmit(substream, &b, 1) != 1) {
|
||||
port->active = 0;
|
||||
snd_rawmidi_drop_output(substream);
|
||||
}
|
||||
}
|
||||
|
||||
static void f_midi_transmit(struct f_midi *midi)
|
||||
{
|
||||
struct usb_ep *ep = midi->in_ep;
|
||||
bool active;
|
||||
|
||||
/* We only care about USB requests if IN endpoint is enabled */
|
||||
if (!ep || !ep->enabled)
|
||||
goto drop_out;
|
||||
|
||||
do {
|
||||
struct usb_request *req = NULL;
|
||||
unsigned int len, i;
|
||||
|
||||
active = false;
|
||||
|
||||
/* We peek the request in order to reuse it if it fails
|
||||
* to enqueue on its endpoint */
|
||||
len = kfifo_peek(&midi->in_req_fifo, &req);
|
||||
if (len != 1) {
|
||||
ERROR(midi, "%s: Couldn't get usb request\n", __func__);
|
||||
goto drop_out;
|
||||
}
|
||||
|
||||
/* If buffer overrun, then we ignore this transmission.
|
||||
* IMPORTANT: This will cause the user-space rawmidi device to block until a) usb
|
||||
* requests have been completed or b) snd_rawmidi_write() times out. */
|
||||
if (req->length > 0)
|
||||
return;
|
||||
|
||||
for (i = midi->in_last_port; i < MAX_PORTS; i++) {
|
||||
struct gmidi_in_port *port = midi->in_port[i];
|
||||
struct snd_rawmidi_substream *substream = midi->in_substream[i];
|
||||
|
||||
if (!port) {
|
||||
/* Reset counter when we reach the last available port */
|
||||
midi->in_last_port = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!port->active || !substream)
|
||||
continue;
|
||||
|
||||
while (req->length + 3 < midi->buflen) {
|
||||
uint8_t b;
|
||||
|
||||
if (snd_rawmidi_transmit(substream, &b, 1) != 1) {
|
||||
port->active = 0;
|
||||
break;
|
||||
}
|
||||
f_midi_transmit_byte(req, port, b);
|
||||
}
|
||||
|
||||
active = !!port->active;
|
||||
/* Check if last port is still active, which means that
|
||||
* there is still data on that substream but this current
|
||||
* request run out of space. */
|
||||
if (active) {
|
||||
midi->in_last_port = i;
|
||||
/* There is no need to re-iterate though midi ports. */
|
||||
break;
|
||||
}
|
||||
f_midi_transmit_byte(req, port, b);
|
||||
}
|
||||
}
|
||||
|
||||
if (req->length > 0 && ep->enabled) {
|
||||
int err;
|
||||
if (req->length > 0) {
|
||||
int err;
|
||||
|
||||
err = usb_ep_queue(ep, req, GFP_ATOMIC);
|
||||
if (err < 0)
|
||||
ERROR(midi, "%s queue req: %d\n",
|
||||
midi->in_ep->name, err);
|
||||
} else {
|
||||
free_ep_req(ep, req);
|
||||
}
|
||||
err = usb_ep_queue(ep, req, GFP_ATOMIC);
|
||||
if (err < 0) {
|
||||
ERROR(midi, "%s failed to queue req: %d\n",
|
||||
midi->in_ep->name, err);
|
||||
req->length = 0; /* Re-use request next time. */
|
||||
} else {
|
||||
/* Upon success, put request at the back of the queue. */
|
||||
kfifo_skip(&midi->in_req_fifo);
|
||||
kfifo_put(&midi->in_req_fifo, req);
|
||||
}
|
||||
}
|
||||
} while (active);
|
||||
|
||||
return;
|
||||
|
||||
drop_out:
|
||||
f_midi_drop_out_substreams(midi);
|
||||
}
|
||||
|
||||
static void f_midi_in_tasklet(unsigned long data)
|
||||
{
|
||||
struct f_midi *midi = (struct f_midi *) data;
|
||||
f_midi_transmit(midi, NULL);
|
||||
f_midi_transmit(midi);
|
||||
}
|
||||
|
||||
static int f_midi_in_open(struct snd_rawmidi_substream *substream)
|
||||
|
@ -687,6 +746,7 @@ static int f_midi_register_card(struct f_midi *midi)
|
|||
goto fail;
|
||||
}
|
||||
midi->rmidi = rmidi;
|
||||
midi->in_last_port = 0;
|
||||
strcpy(rmidi->name, card->shortname);
|
||||
rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
|
||||
SNDRV_RAWMIDI_INFO_INPUT |
|
||||
|
@ -755,6 +815,7 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
goto fail;
|
||||
ms_interface_desc.bInterfaceNumber = status;
|
||||
ac_header_desc.baInterfaceNr[0] = status;
|
||||
midi->ms_id = status;
|
||||
|
||||
status = -ENODEV;
|
||||
|
||||
|
@ -1075,6 +1136,7 @@ static void f_midi_free(struct usb_function *f)
|
|||
mutex_lock(&opts->lock);
|
||||
for (i = opts->in_ports - 1; i >= 0; --i)
|
||||
kfree(midi->in_port[i]);
|
||||
kfifo_free(&midi->in_req_fifo);
|
||||
kfree(midi);
|
||||
--opts->refcnt;
|
||||
mutex_unlock(&opts->lock);
|
||||
|
@ -1148,6 +1210,12 @@ static struct usb_function *f_midi_alloc(struct usb_function_instance *fi)
|
|||
midi->index = opts->index;
|
||||
midi->buflen = opts->buflen;
|
||||
midi->qlen = opts->qlen;
|
||||
midi->in_last_port = 0;
|
||||
|
||||
status = kfifo_alloc(&midi->in_req_fifo, midi->qlen, GFP_KERNEL);
|
||||
if (status)
|
||||
goto setup_fail;
|
||||
|
||||
++opts->refcnt;
|
||||
mutex_unlock(&opts->lock);
|
||||
|
||||
|
|
|
@ -34,13 +34,6 @@
|
|||
* plus two that support control-OUT tests. If the optional "autoresume"
|
||||
* mode is enabled, it provides good functional coverage for the "USBCV"
|
||||
* test harness from USB-IF.
|
||||
*
|
||||
* Note that because this doesn't queue more than one request at a time,
|
||||
* some other function must be used to test queueing logic. The network
|
||||
* link (g_ether) is the best overall option for that, since its TX and RX
|
||||
* queues are relatively independent, will receive a range of packet sizes,
|
||||
* and can often be made to run out completely. Those issues are important
|
||||
* when stress testing peripheral controller drivers.
|
||||
*/
|
||||
struct f_sourcesink {
|
||||
struct usb_function function;
|
||||
|
@ -57,6 +50,8 @@ struct f_sourcesink {
|
|||
unsigned isoc_mult;
|
||||
unsigned isoc_maxburst;
|
||||
unsigned buflen;
|
||||
unsigned bulk_qlen;
|
||||
unsigned iso_qlen;
|
||||
};
|
||||
|
||||
static inline struct f_sourcesink *func_to_ss(struct usb_function *f)
|
||||
|
@ -303,12 +298,6 @@ static inline struct usb_request *ss_alloc_ep_req(struct usb_ep *ep, int len)
|
|||
return alloc_ep_req(ep, len, ss->buflen);
|
||||
}
|
||||
|
||||
void free_ep_req(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
kfree(req->buf);
|
||||
usb_ep_free_request(ep, req);
|
||||
}
|
||||
|
||||
static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep)
|
||||
{
|
||||
int value;
|
||||
|
@ -595,31 +584,33 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in,
|
|||
{
|
||||
struct usb_ep *ep;
|
||||
struct usb_request *req;
|
||||
int i, size, status;
|
||||
int i, size, qlen, status = 0;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (is_iso) {
|
||||
switch (speed) {
|
||||
case USB_SPEED_SUPER:
|
||||
size = ss->isoc_maxpacket *
|
||||
(ss->isoc_mult + 1) *
|
||||
(ss->isoc_maxburst + 1);
|
||||
break;
|
||||
case USB_SPEED_HIGH:
|
||||
size = ss->isoc_maxpacket * (ss->isoc_mult + 1);
|
||||
break;
|
||||
default:
|
||||
size = ss->isoc_maxpacket > 1023 ?
|
||||
1023 : ss->isoc_maxpacket;
|
||||
break;
|
||||
}
|
||||
ep = is_in ? ss->iso_in_ep : ss->iso_out_ep;
|
||||
req = ss_alloc_ep_req(ep, size);
|
||||
} else {
|
||||
ep = is_in ? ss->in_ep : ss->out_ep;
|
||||
req = ss_alloc_ep_req(ep, 0);
|
||||
if (is_iso) {
|
||||
switch (speed) {
|
||||
case USB_SPEED_SUPER:
|
||||
size = ss->isoc_maxpacket *
|
||||
(ss->isoc_mult + 1) *
|
||||
(ss->isoc_maxburst + 1);
|
||||
break;
|
||||
case USB_SPEED_HIGH:
|
||||
size = ss->isoc_maxpacket * (ss->isoc_mult + 1);
|
||||
break;
|
||||
default:
|
||||
size = ss->isoc_maxpacket > 1023 ?
|
||||
1023 : ss->isoc_maxpacket;
|
||||
break;
|
||||
}
|
||||
ep = is_in ? ss->iso_in_ep : ss->iso_out_ep;
|
||||
qlen = ss->iso_qlen;
|
||||
} else {
|
||||
ep = is_in ? ss->in_ep : ss->out_ep;
|
||||
qlen = ss->bulk_qlen;
|
||||
size = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < qlen; i++) {
|
||||
req = ss_alloc_ep_req(ep, size);
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -638,10 +629,8 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in,
|
|||
is_iso ? "ISO-" : "", is_in ? "IN" : "OUT",
|
||||
ep->name, status);
|
||||
free_ep_req(ep, req);
|
||||
return status;
|
||||
}
|
||||
|
||||
if (!is_iso)
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
|
@ -869,6 +858,8 @@ static struct usb_function *source_sink_alloc_func(
|
|||
ss->isoc_mult = ss_opts->isoc_mult;
|
||||
ss->isoc_maxburst = ss_opts->isoc_maxburst;
|
||||
ss->buflen = ss_opts->bulk_buflen;
|
||||
ss->bulk_qlen = ss_opts->bulk_qlen;
|
||||
ss->iso_qlen = ss_opts->iso_qlen;
|
||||
|
||||
ss->function.name = "source/sink";
|
||||
ss->function.bind = sourcesink_bind;
|
||||
|
@ -1153,6 +1144,82 @@ end:
|
|||
|
||||
CONFIGFS_ATTR(f_ss_opts_, bulk_buflen);
|
||||
|
||||
static ssize_t f_ss_opts_bulk_qlen_show(struct config_item *item, char *page)
|
||||
{
|
||||
struct f_ss_opts *opts = to_f_ss_opts(item);
|
||||
int result;
|
||||
|
||||
mutex_lock(&opts->lock);
|
||||
result = sprintf(page, "%u\n", opts->bulk_qlen);
|
||||
mutex_unlock(&opts->lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static ssize_t f_ss_opts_bulk_qlen_store(struct config_item *item,
|
||||
const char *page, size_t len)
|
||||
{
|
||||
struct f_ss_opts *opts = to_f_ss_opts(item);
|
||||
int ret;
|
||||
u32 num;
|
||||
|
||||
mutex_lock(&opts->lock);
|
||||
if (opts->refcnt) {
|
||||
ret = -EBUSY;
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = kstrtou32(page, 0, &num);
|
||||
if (ret)
|
||||
goto end;
|
||||
|
||||
opts->bulk_qlen = num;
|
||||
ret = len;
|
||||
end:
|
||||
mutex_unlock(&opts->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
CONFIGFS_ATTR(f_ss_opts_, bulk_qlen);
|
||||
|
||||
static ssize_t f_ss_opts_iso_qlen_show(struct config_item *item, char *page)
|
||||
{
|
||||
struct f_ss_opts *opts = to_f_ss_opts(item);
|
||||
int result;
|
||||
|
||||
mutex_lock(&opts->lock);
|
||||
result = sprintf(page, "%u\n", opts->iso_qlen);
|
||||
mutex_unlock(&opts->lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static ssize_t f_ss_opts_iso_qlen_store(struct config_item *item,
|
||||
const char *page, size_t len)
|
||||
{
|
||||
struct f_ss_opts *opts = to_f_ss_opts(item);
|
||||
int ret;
|
||||
u32 num;
|
||||
|
||||
mutex_lock(&opts->lock);
|
||||
if (opts->refcnt) {
|
||||
ret = -EBUSY;
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = kstrtou32(page, 0, &num);
|
||||
if (ret)
|
||||
goto end;
|
||||
|
||||
opts->iso_qlen = num;
|
||||
ret = len;
|
||||
end:
|
||||
mutex_unlock(&opts->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
CONFIGFS_ATTR(f_ss_opts_, iso_qlen);
|
||||
|
||||
static struct configfs_attribute *ss_attrs[] = {
|
||||
&f_ss_opts_attr_pattern,
|
||||
&f_ss_opts_attr_isoc_interval,
|
||||
|
@ -1160,6 +1227,8 @@ static struct configfs_attribute *ss_attrs[] = {
|
|||
&f_ss_opts_attr_isoc_mult,
|
||||
&f_ss_opts_attr_isoc_maxburst,
|
||||
&f_ss_opts_attr_bulk_buflen,
|
||||
&f_ss_opts_attr_bulk_qlen,
|
||||
&f_ss_opts_attr_iso_qlen,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
@ -1189,6 +1258,8 @@ static struct usb_function_instance *source_sink_alloc_inst(void)
|
|||
ss_opts->isoc_interval = GZERO_ISOC_INTERVAL;
|
||||
ss_opts->isoc_maxpacket = GZERO_ISOC_MAXPACKET;
|
||||
ss_opts->bulk_buflen = GZERO_BULK_BUFLEN;
|
||||
ss_opts->bulk_qlen = GZERO_SS_BULK_QLEN;
|
||||
ss_opts->iso_qlen = GZERO_SS_ISO_QLEN;
|
||||
|
||||
config_group_init_type_name(&ss_opts->func_inst.group, "",
|
||||
&ss_func_type);
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#define GZERO_QLEN 32
|
||||
#define GZERO_ISOC_INTERVAL 4
|
||||
#define GZERO_ISOC_MAXPACKET 1024
|
||||
#define GZERO_SS_BULK_QLEN 1
|
||||
#define GZERO_SS_ISO_QLEN 8
|
||||
|
||||
struct usb_zero_options {
|
||||
unsigned pattern;
|
||||
|
@ -19,6 +21,8 @@ struct usb_zero_options {
|
|||
unsigned isoc_maxburst;
|
||||
unsigned bulk_buflen;
|
||||
unsigned qlen;
|
||||
unsigned ss_bulk_qlen;
|
||||
unsigned ss_iso_qlen;
|
||||
};
|
||||
|
||||
struct f_ss_opts {
|
||||
|
@ -29,6 +33,8 @@ struct f_ss_opts {
|
|||
unsigned isoc_mult;
|
||||
unsigned isoc_maxburst;
|
||||
unsigned bulk_buflen;
|
||||
unsigned bulk_qlen;
|
||||
unsigned iso_qlen;
|
||||
|
||||
/*
|
||||
* Read/write access to configfs attributes is handled by configfs.
|
||||
|
@ -59,7 +65,6 @@ void lb_modexit(void);
|
|||
int lb_modinit(void);
|
||||
|
||||
/* common utilities */
|
||||
void free_ep_req(struct usb_ep *ep, struct usb_request *req);
|
||||
void disable_endpoints(struct usb_composite_dev *cdev,
|
||||
struct usb_ep *in, struct usb_ep *out,
|
||||
struct usb_ep *iso_in, struct usb_ep *iso_out);
|
||||
|
|
|
@ -143,21 +143,11 @@ static inline int qlen(struct usb_gadget *gadget, unsigned qmult)
|
|||
|
||||
static int ueth_change_mtu(struct net_device *net, int new_mtu)
|
||||
{
|
||||
struct eth_dev *dev = netdev_priv(net);
|
||||
unsigned long flags;
|
||||
int status = 0;
|
||||
if (new_mtu <= ETH_HLEN || new_mtu > GETHER_MAX_ETH_FRAME_LEN)
|
||||
return -ERANGE;
|
||||
net->mtu = new_mtu;
|
||||
|
||||
/* don't change MTU on "live" link (peer won't know) */
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
if (dev->port_usb)
|
||||
status = -EBUSY;
|
||||
else if (new_mtu <= ETH_HLEN || new_mtu > GETHER_MAX_ETH_FRAME_LEN)
|
||||
status = -ERANGE;
|
||||
else
|
||||
net->mtu = new_mtu;
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
||||
return status;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p)
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/kthread.h>
|
||||
|
||||
#include "u_serial.h"
|
||||
|
||||
|
@ -79,6 +81,7 @@
|
|||
*/
|
||||
#define QUEUE_SIZE 16
|
||||
#define WRITE_BUF_SIZE 8192 /* TX only */
|
||||
#define GS_CONSOLE_BUF_SIZE 8192
|
||||
|
||||
/* circular buffer */
|
||||
struct gs_buf {
|
||||
|
@ -88,6 +91,17 @@ struct gs_buf {
|
|||
char *buf_put;
|
||||
};
|
||||
|
||||
/* console info */
|
||||
struct gscons_info {
|
||||
struct gs_port *port;
|
||||
struct task_struct *console_thread;
|
||||
struct gs_buf con_buf;
|
||||
/* protect the buf and busy flag */
|
||||
spinlock_t con_lock;
|
||||
int req_busy;
|
||||
struct usb_request *console_req;
|
||||
};
|
||||
|
||||
/*
|
||||
* The port structure holds info for each port, one for each minor number
|
||||
* (and thus for each /dev/ node).
|
||||
|
@ -1023,6 +1037,246 @@ static const struct tty_operations gs_tty_ops = {
|
|||
|
||||
static struct tty_driver *gs_tty_driver;
|
||||
|
||||
#ifdef CONFIG_U_SERIAL_CONSOLE
|
||||
|
||||
static struct gscons_info gscons_info;
|
||||
static struct console gserial_cons;
|
||||
|
||||
static struct usb_request *gs_request_new(struct usb_ep *ep)
|
||||
{
|
||||
struct usb_request *req = usb_ep_alloc_request(ep, GFP_ATOMIC);
|
||||
if (!req)
|
||||
return NULL;
|
||||
|
||||
req->buf = kmalloc(ep->maxpacket, GFP_ATOMIC);
|
||||
if (!req->buf) {
|
||||
usb_ep_free_request(ep, req);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
static void gs_request_free(struct usb_request *req, struct usb_ep *ep)
|
||||
{
|
||||
if (!req)
|
||||
return;
|
||||
|
||||
kfree(req->buf);
|
||||
usb_ep_free_request(ep, req);
|
||||
}
|
||||
|
||||
static void gs_complete_out(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct gscons_info *info = &gscons_info;
|
||||
|
||||
switch (req->status) {
|
||||
default:
|
||||
pr_warn("%s: unexpected %s status %d\n",
|
||||
__func__, ep->name, req->status);
|
||||
case 0:
|
||||
/* normal completion */
|
||||
spin_lock(&info->con_lock);
|
||||
info->req_busy = 0;
|
||||
spin_unlock(&info->con_lock);
|
||||
|
||||
wake_up_process(info->console_thread);
|
||||
break;
|
||||
case -ESHUTDOWN:
|
||||
/* disconnect */
|
||||
pr_vdebug("%s: %s shutdown\n", __func__, ep->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int gs_console_connect(int port_num)
|
||||
{
|
||||
struct gscons_info *info = &gscons_info;
|
||||
struct gs_port *port;
|
||||
struct usb_ep *ep;
|
||||
|
||||
if (port_num != gserial_cons.index) {
|
||||
pr_err("%s: port num [%d] is not support console\n",
|
||||
__func__, port_num);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
port = ports[port_num].port;
|
||||
ep = port->port_usb->in;
|
||||
if (!info->console_req) {
|
||||
info->console_req = gs_request_new(ep);
|
||||
if (!info->console_req)
|
||||
return -ENOMEM;
|
||||
info->console_req->complete = gs_complete_out;
|
||||
}
|
||||
|
||||
info->port = port;
|
||||
spin_lock(&info->con_lock);
|
||||
info->req_busy = 0;
|
||||
spin_unlock(&info->con_lock);
|
||||
pr_vdebug("port[%d] console connect!\n", port_num);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gs_console_disconnect(struct usb_ep *ep)
|
||||
{
|
||||
struct gscons_info *info = &gscons_info;
|
||||
struct usb_request *req = info->console_req;
|
||||
|
||||
gs_request_free(req, ep);
|
||||
info->console_req = NULL;
|
||||
}
|
||||
|
||||
static int gs_console_thread(void *data)
|
||||
{
|
||||
struct gscons_info *info = &gscons_info;
|
||||
struct gs_port *port;
|
||||
struct usb_request *req;
|
||||
struct usb_ep *ep;
|
||||
int xfer, ret, count, size;
|
||||
|
||||
do {
|
||||
port = info->port;
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
if (!port || !port->port_usb
|
||||
|| !port->port_usb->in || !info->console_req)
|
||||
goto sched;
|
||||
|
||||
req = info->console_req;
|
||||
ep = port->port_usb->in;
|
||||
|
||||
spin_lock_irq(&info->con_lock);
|
||||
count = gs_buf_data_avail(&info->con_buf);
|
||||
size = ep->maxpacket;
|
||||
|
||||
if (count > 0 && !info->req_busy) {
|
||||
set_current_state(TASK_RUNNING);
|
||||
if (count < size)
|
||||
size = count;
|
||||
|
||||
xfer = gs_buf_get(&info->con_buf, req->buf, size);
|
||||
req->length = xfer;
|
||||
|
||||
spin_unlock(&info->con_lock);
|
||||
ret = usb_ep_queue(ep, req, GFP_ATOMIC);
|
||||
spin_lock(&info->con_lock);
|
||||
if (ret < 0)
|
||||
info->req_busy = 0;
|
||||
else
|
||||
info->req_busy = 1;
|
||||
|
||||
spin_unlock_irq(&info->con_lock);
|
||||
} else {
|
||||
spin_unlock_irq(&info->con_lock);
|
||||
sched:
|
||||
if (kthread_should_stop()) {
|
||||
set_current_state(TASK_RUNNING);
|
||||
break;
|
||||
}
|
||||
schedule();
|
||||
}
|
||||
} while (1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gs_console_setup(struct console *co, char *options)
|
||||
{
|
||||
struct gscons_info *info = &gscons_info;
|
||||
int status;
|
||||
|
||||
info->port = NULL;
|
||||
info->console_req = NULL;
|
||||
info->req_busy = 0;
|
||||
spin_lock_init(&info->con_lock);
|
||||
|
||||
status = gs_buf_alloc(&info->con_buf, GS_CONSOLE_BUF_SIZE);
|
||||
if (status) {
|
||||
pr_err("%s: allocate console buffer failed\n", __func__);
|
||||
return status;
|
||||
}
|
||||
|
||||
info->console_thread = kthread_create(gs_console_thread,
|
||||
co, "gs_console");
|
||||
if (IS_ERR(info->console_thread)) {
|
||||
pr_err("%s: cannot create console thread\n", __func__);
|
||||
gs_buf_free(&info->con_buf);
|
||||
return PTR_ERR(info->console_thread);
|
||||
}
|
||||
wake_up_process(info->console_thread);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gs_console_write(struct console *co,
|
||||
const char *buf, unsigned count)
|
||||
{
|
||||
struct gscons_info *info = &gscons_info;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&info->con_lock, flags);
|
||||
gs_buf_put(&info->con_buf, buf, count);
|
||||
spin_unlock_irqrestore(&info->con_lock, flags);
|
||||
|
||||
wake_up_process(info->console_thread);
|
||||
}
|
||||
|
||||
static struct tty_driver *gs_console_device(struct console *co, int *index)
|
||||
{
|
||||
struct tty_driver **p = (struct tty_driver **)co->data;
|
||||
|
||||
if (!*p)
|
||||
return NULL;
|
||||
|
||||
*index = co->index;
|
||||
return *p;
|
||||
}
|
||||
|
||||
static struct console gserial_cons = {
|
||||
.name = "ttyGS",
|
||||
.write = gs_console_write,
|
||||
.device = gs_console_device,
|
||||
.setup = gs_console_setup,
|
||||
.flags = CON_PRINTBUFFER,
|
||||
.index = -1,
|
||||
.data = &gs_tty_driver,
|
||||
};
|
||||
|
||||
static void gserial_console_init(void)
|
||||
{
|
||||
register_console(&gserial_cons);
|
||||
}
|
||||
|
||||
static void gserial_console_exit(void)
|
||||
{
|
||||
struct gscons_info *info = &gscons_info;
|
||||
|
||||
unregister_console(&gserial_cons);
|
||||
kthread_stop(info->console_thread);
|
||||
gs_buf_free(&info->con_buf);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int gs_console_connect(int port_num)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gs_console_disconnect(struct usb_ep *ep)
|
||||
{
|
||||
}
|
||||
|
||||
static void gserial_console_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
static void gserial_console_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int
|
||||
gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding)
|
||||
{
|
||||
|
@ -1096,6 +1350,7 @@ void gserial_free_line(unsigned char port_num)
|
|||
|
||||
gserial_free_port(port);
|
||||
tty_unregister_device(gs_tty_driver, port_num);
|
||||
gserial_console_exit();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gserial_free_line);
|
||||
|
||||
|
@ -1138,6 +1393,7 @@ int gserial_alloc_line(unsigned char *line_num)
|
|||
goto err;
|
||||
}
|
||||
*line_num = port_num;
|
||||
gserial_console_init();
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
@ -1219,6 +1475,7 @@ int gserial_connect(struct gserial *gser, u8 port_num)
|
|||
gser->disconnect(gser);
|
||||
}
|
||||
|
||||
status = gs_console_connect(port_num);
|
||||
spin_unlock_irqrestore(&port->port_lock, flags);
|
||||
|
||||
return status;
|
||||
|
@ -1277,6 +1534,7 @@ void gserial_disconnect(struct gserial *gser)
|
|||
port->read_allocated = port->read_started =
|
||||
port->write_allocated = port->write_started = 0;
|
||||
|
||||
gs_console_disconnect(gser->in);
|
||||
spin_unlock_irqrestore(&port->port_lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gserial_disconnect);
|
||||
|
|
|
@ -40,7 +40,7 @@ static struct usb_device_descriptor device_desc = {
|
|||
.bLength = sizeof device_desc,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
|
||||
.bcdUSB = cpu_to_le16(0x0200),
|
||||
/* .bcdUSB = DYNAMIC */
|
||||
|
||||
.bDeviceClass = USB_CLASS_MISC /* 0xEF */,
|
||||
.bDeviceSubClass = 2,
|
||||
|
|
|
@ -123,7 +123,7 @@ static struct usb_device_descriptor device_desc = {
|
|||
.bLength = sizeof device_desc,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
|
||||
.bcdUSB = cpu_to_le16(0x200),
|
||||
/* .bcdUSB = DYNAMIC */
|
||||
|
||||
#ifdef CONFIG_GADGET_UAC1
|
||||
.bDeviceClass = USB_CLASS_PER_INTERFACE,
|
||||
|
|
|
@ -43,7 +43,7 @@ static struct usb_device_descriptor device_desc = {
|
|||
.bLength = sizeof device_desc,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
|
||||
.bcdUSB = cpu_to_le16(0x0200),
|
||||
/* .bcdUSB = DYNAMIC */
|
||||
|
||||
.bDeviceClass = USB_CLASS_COMM,
|
||||
.bDeviceSubClass = 0,
|
||||
|
|
|
@ -151,7 +151,7 @@ static struct usb_device_descriptor device_desc = {
|
|||
.bLength = sizeof device_desc,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
|
||||
.bcdUSB = cpu_to_le16 (0x0200),
|
||||
/* .bcdUSB = DYNAMIC */
|
||||
|
||||
.bDeviceClass = USB_CLASS_COMM,
|
||||
.bDeviceSubClass = 0,
|
||||
|
|
|
@ -69,7 +69,7 @@ static struct usb_device_descriptor gfs_dev_desc = {
|
|||
.bLength = sizeof gfs_dev_desc,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
|
||||
.bcdUSB = cpu_to_le16(0x0200),
|
||||
/* .bcdUSB = DYNAMIC */
|
||||
.bDeviceClass = USB_CLASS_PER_INTERFACE,
|
||||
|
||||
.idVendor = cpu_to_le16(GFS_VENDOR_ID),
|
||||
|
|
|
@ -21,19 +21,12 @@
|
|||
/* #define VERBOSE_DEBUG */
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/rawmidi.h>
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/composite.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/audio.h>
|
||||
#include <linux/usb/midi.h>
|
||||
|
||||
#include "u_midi.h"
|
||||
|
||||
|
@ -42,7 +35,6 @@
|
|||
MODULE_AUTHOR("Ben Williamson");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
static const char shortname[] = "g_midi";
|
||||
static const char longname[] = "MIDI Gadget";
|
||||
|
||||
USB_GADGET_COMPOSITE_OPTIONS();
|
||||
|
@ -61,7 +53,7 @@ MODULE_PARM_DESC(buflen, "MIDI buffer length");
|
|||
|
||||
static unsigned int qlen = 32;
|
||||
module_param(qlen, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(qlen, "USB read request queue length");
|
||||
MODULE_PARM_DESC(qlen, "USB read and write request queue length");
|
||||
|
||||
static unsigned int in_ports = 1;
|
||||
module_param(in_ports, uint, S_IRUGO);
|
||||
|
@ -86,7 +78,7 @@ MODULE_PARM_DESC(out_ports, "Number of MIDI output ports");
|
|||
static struct usb_device_descriptor device_desc = {
|
||||
.bLength = USB_DT_DEVICE_SIZE,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
.bcdUSB = cpu_to_le16(0x0200),
|
||||
/* .bcdUSB = DYNAMIC */
|
||||
.bDeviceClass = USB_CLASS_PER_INTERFACE,
|
||||
.idVendor = cpu_to_le16(DRIVER_VENDOR_NUM),
|
||||
.idProduct = cpu_to_le16(DRIVER_PRODUCT_NUM),
|
||||
|
|
|
@ -47,7 +47,7 @@ static struct usb_device_descriptor device_desc = {
|
|||
.bLength = sizeof device_desc,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
|
||||
.bcdUSB = cpu_to_le16(0x0200),
|
||||
/* .bcdUSB = DYNAMIC */
|
||||
|
||||
/* .bDeviceClass = USB_CLASS_COMM, */
|
||||
/* .bDeviceSubClass = 0, */
|
||||
|
|
|
@ -1137,10 +1137,9 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
|
|||
dev->gadget->ep0, dev->req,
|
||||
GFP_KERNEL);
|
||||
}
|
||||
spin_lock_irq(&dev->lock);
|
||||
if (retval < 0) {
|
||||
spin_lock_irq (&dev->lock);
|
||||
clean_req (dev->gadget->ep0, dev->req);
|
||||
spin_unlock_irq (&dev->lock);
|
||||
} else
|
||||
retval = len;
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ static struct usb_device_descriptor msg_device_desc = {
|
|||
.bLength = sizeof msg_device_desc,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
|
||||
.bcdUSB = cpu_to_le16(0x0200),
|
||||
/* .bcdUSB = DYNAMIC */
|
||||
.bDeviceClass = USB_CLASS_PER_INTERFACE,
|
||||
|
||||
/* Vendor and product id can be overridden by module parameters. */
|
||||
|
|
|
@ -67,7 +67,7 @@ static struct usb_device_descriptor device_desc = {
|
|||
.bLength = sizeof device_desc,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
|
||||
.bcdUSB = cpu_to_le16(0x0200),
|
||||
/* .bcdUSB = DYNAMIC */
|
||||
|
||||
.bDeviceClass = USB_CLASS_MISC /* 0xEF */,
|
||||
.bDeviceSubClass = 2,
|
||||
|
|
|
@ -49,7 +49,7 @@ static struct usb_device_descriptor device_desc = {
|
|||
.bLength = sizeof device_desc,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
|
||||
.bcdUSB = cpu_to_le16 (0x0200),
|
||||
/* .bcdUSB = DYNAMIC */
|
||||
|
||||
.bDeviceClass = USB_CLASS_COMM,
|
||||
.bDeviceSubClass = 0,
|
||||
|
|
|
@ -89,7 +89,7 @@ static struct usb_gadget_strings *dev_strings[] = {
|
|||
static struct usb_device_descriptor device_desc = {
|
||||
.bLength = USB_DT_DEVICE_SIZE,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
.bcdUSB = cpu_to_le16(0x0200),
|
||||
/* .bcdUSB = DYNAMIC */
|
||||
.bDeviceClass = USB_CLASS_COMM,
|
||||
.idVendor = cpu_to_le16(NOKIA_VENDOR_ID),
|
||||
.idProduct = cpu_to_le16(NOKIA_PRODUCT_ID),
|
||||
|
|
|
@ -71,7 +71,7 @@ static struct usb_function *f_printer;
|
|||
static struct usb_device_descriptor device_desc = {
|
||||
.bLength = sizeof device_desc,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
.bcdUSB = cpu_to_le16(0x0200),
|
||||
/* .bcdUSB = DYNAMIC */
|
||||
.bDeviceClass = USB_CLASS_PER_INTERFACE,
|
||||
.bDeviceSubClass = 0,
|
||||
.bDeviceProtocol = 0,
|
||||
|
|
|
@ -65,7 +65,7 @@ static struct usb_gadget_strings *dev_strings[] = {
|
|||
static struct usb_device_descriptor device_desc = {
|
||||
.bLength = USB_DT_DEVICE_SIZE,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
.bcdUSB = cpu_to_le16(0x0200),
|
||||
/* .bcdUSB = DYNAMIC */
|
||||
/* .bDeviceClass = f(use_acm) */
|
||||
.bDeviceSubClass = 0,
|
||||
.bDeviceProtocol = 0,
|
||||
|
|
|
@ -1974,7 +1974,7 @@ static struct usb_descriptor_header *uasp_ss_function_desc[] = {
|
|||
static struct usb_device_descriptor usbg_device_desc = {
|
||||
.bLength = sizeof(usbg_device_desc),
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
.bcdUSB = cpu_to_le16(0x0200),
|
||||
/* .bcdUSB = DYNAMIC */
|
||||
.bDeviceClass = USB_CLASS_PER_INTERFACE,
|
||||
.idVendor = cpu_to_le16(UAS_VENDOR_ID),
|
||||
.idProduct = cpu_to_le16(UAS_PRODUCT_ID),
|
||||
|
|
|
@ -77,7 +77,7 @@ static struct usb_function *f_uvc;
|
|||
static struct usb_device_descriptor webcam_device_descriptor = {
|
||||
.bLength = USB_DT_DEVICE_SIZE,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
.bcdUSB = cpu_to_le16(0x0200),
|
||||
/* .bcdUSB = DYNAMIC */
|
||||
.bDeviceClass = USB_CLASS_MISC,
|
||||
.bDeviceSubClass = 0x02,
|
||||
.bDeviceProtocol = 0x01,
|
||||
|
|
|
@ -68,6 +68,8 @@ static struct usb_zero_options gzero_options = {
|
|||
.isoc_maxpacket = GZERO_ISOC_MAXPACKET,
|
||||
.bulk_buflen = GZERO_BULK_BUFLEN,
|
||||
.qlen = GZERO_QLEN,
|
||||
.ss_bulk_qlen = GZERO_SS_BULK_QLEN,
|
||||
.ss_iso_qlen = GZERO_SS_ISO_QLEN,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
@ -113,7 +115,7 @@ static struct usb_device_descriptor device_desc = {
|
|||
.bLength = sizeof device_desc,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
|
||||
.bcdUSB = cpu_to_le16(0x0200),
|
||||
/* .bcdUSB = DYNAMIC */
|
||||
.bDeviceClass = USB_CLASS_VENDOR_SPEC,
|
||||
|
||||
.idVendor = cpu_to_le16(DRIVER_VENDOR_NUM),
|
||||
|
@ -255,6 +257,14 @@ static struct usb_function_instance *func_inst_lb;
|
|||
module_param_named(qlen, gzero_options.qlen, uint, S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(qlen, "depth of loopback queue");
|
||||
|
||||
module_param_named(ss_bulk_qlen, gzero_options.ss_bulk_qlen, uint,
|
||||
S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(bulk_qlen, "depth of sourcesink queue for bulk transfer");
|
||||
|
||||
module_param_named(ss_iso_qlen, gzero_options.ss_iso_qlen, uint,
|
||||
S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(iso_qlen, "depth of sourcesink queue for iso transfer");
|
||||
|
||||
static int zero_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
struct f_ss_opts *ss_opts;
|
||||
|
@ -285,6 +295,8 @@ static int zero_bind(struct usb_composite_dev *cdev)
|
|||
ss_opts->isoc_mult = gzero_options.isoc_mult;
|
||||
ss_opts->isoc_maxburst = gzero_options.isoc_maxburst;
|
||||
ss_opts->bulk_buflen = gzero_options.bulk_buflen;
|
||||
ss_opts->bulk_qlen = gzero_options.ss_bulk_qlen;
|
||||
ss_opts->iso_qlen = gzero_options.ss_iso_qlen;
|
||||
|
||||
func_ss = usb_get_function(func_inst_ss);
|
||||
if (IS_ERR(func_ss)) {
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/usb/gadget.h>
|
||||
#include "u_f.h"
|
||||
|
||||
struct usb_request *alloc_ep_req(struct usb_ep *ep, int len, int default_len)
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
#ifndef __U_F_H__
|
||||
#define __U_F_H__
|
||||
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
/* Variable Length Array Macros **********************************************/
|
||||
#define vla_group(groupname) size_t groupname##__next = 0
|
||||
#define vla_group_size(groupname) groupname##__next
|
||||
|
@ -45,8 +47,12 @@
|
|||
struct usb_ep;
|
||||
struct usb_request;
|
||||
|
||||
/* Requests allocated via alloc_ep_req() must be freed by free_ep_req(). */
|
||||
struct usb_request *alloc_ep_req(struct usb_ep *ep, int len, int default_len);
|
||||
static inline void free_ep_req(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
kfree(req->buf);
|
||||
usb_ep_free_request(ep, req);
|
||||
}
|
||||
|
||||
#endif /* __U_F_H__ */
|
||||
|
||||
|
||||
|
|
|
@ -174,6 +174,17 @@ config USB_RENESAS_USBHS_UDC
|
|||
dynamically linked module called "renesas_usbhs" and force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
config USB_RENESAS_USB3
|
||||
tristate 'Renesas USB3.0 Peripheral controller'
|
||||
depends on ARCH_SHMOBILE || COMPILE_TEST
|
||||
help
|
||||
Renesas USB3.0 Peripheral controller is a USB peripheral controller
|
||||
that supports super, high, and full speed USB 3.0 data transfers.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "renesas_usb3" and force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
config USB_PXA27X
|
||||
tristate "PXA 27x"
|
||||
help
|
||||
|
|
|
@ -19,6 +19,7 @@ fsl_usb2_udc-y := fsl_udc_core.o
|
|||
fsl_usb2_udc-$(CONFIG_ARCH_MXC) += fsl_mxc_udc.o
|
||||
obj-$(CONFIG_USB_M66592) += m66592-udc.o
|
||||
obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o
|
||||
obj-$(CONFIG_USB_RENESAS_USB3) += renesas_usb3.o
|
||||
obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o
|
||||
obj-$(CONFIG_USB_S3C_HSUDC) += s3c-hsudc.o
|
||||
obj-$(CONFIG_USB_LPC32XX) += lpc32xx_udc.o
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue