mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 14:11:52 +00:00
USB patches for 4.1-rc1
Here's the big USB (and PHY) driver patchset for 4.1-rc1. Everything here has been in linux-next, and the full details are below in the shortlog. Nothing major, just the normal round of new drivers,api updates, and other changes, mostly in the USB gadget area, as usual. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iEYEABECAAYFAlUsHXYACgkQMUfUDdst+ykGvwCfbI3z0VYJqyvPi7pbn+jtGouQ E7MAoICdP90ofZfyzQzXy+2xKTTCiP5L =jSjh -----END PGP SIGNATURE----- Merge tag 'usb-4.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb Pull USB driver updates from Greg KH: "Here's the big USB (and PHY) driver patchset for 4.1-rc1. Everything here has been in linux-next, and the full details are below in the shortlog. Nothing major, just the normal round of new drivers,api updates, and other changes, mostly in the USB gadget area, as usual" * tag 'usb-4.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (252 commits) drivers/usb/core: devio.c: Removed an uneeded space before tab usb: dwc2: host: sleep USB_RESUME_TIMEOUT during resume usb: chipidea: debug: add low power mode check before print registers usb: chipidea: udc: bypass pullup DP when gadget connect in OTG fsm mode usb: core: hub: use new USB_RESUME_TIMEOUT usb: isp1760: hcd: use new USB_RESUME_TIMEOUT usb: dwc2: hcd: use new USB_RESUME_TIMEOUT usb: host: sl811: use new USB_RESUME_TIMEOUT usb: host: r8a66597: use new USB_RESUME_TIMEOUT usb: host: oxu210hp: use new USB_RESUME_TIMEOUT usb: host: fusbh200: use new USB_RESUME_TIMEOUT usb: host: fotg210: use new USB_RESUME_TIMEOUT usb: host: isp116x: use new USB_RESUME_TIMEOUT usb: musb: use new USB_RESUME_TIMEOUT usb: host: uhci: use new USB_RESUME_TIMEOUT usb: host: ehci: use new USB_RESUME_TIMEOUT usb: host: xhci: use new USB_RESUME_TIMEOUT usb: define a generic USB_RESUME_TIMEOUT macro usb: musb: dsps: fix build on i386 when COMPILE_TEST is set ehci-hub: use USB_DT_HUB ...
This commit is contained in:
commit
42e3a58b02
9
Documentation/ABI/testing/configfs-usb-gadget-printer
Normal file
9
Documentation/ABI/testing/configfs-usb-gadget-printer
Normal file
@ -0,0 +1,9 @@
|
||||
What: /config/usb-gadget/gadget/functions/printer.name
|
||||
Date: Apr 2015
|
||||
KernelVersion: 4.1
|
||||
Description:
|
||||
The attributes:
|
||||
|
||||
pnp_string - Data to be passed to the host in pnp string
|
||||
q_len - Number of requests per endpoint
|
||||
|
24
Documentation/devicetree/bindings/phy/dm816x-phy.txt
Normal file
24
Documentation/devicetree/bindings/phy/dm816x-phy.txt
Normal file
@ -0,0 +1,24 @@
|
||||
Device tree binding documentation for am816x USB PHY
|
||||
=========================
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "ti,dm816x-usb-phy"
|
||||
- reg : offset and length of the PHY register set.
|
||||
- reg-names : name for the phy registers
|
||||
- clocks : phandle to the clock
|
||||
- clock-names : name of the clock
|
||||
- syscon: phandle for the syscon node to access misc registers
|
||||
- #phy-cells : from the generic PHY bindings, must be 1
|
||||
- syscon: phandle for the syscon node to access misc registers
|
||||
|
||||
Example:
|
||||
|
||||
usb_phy0: usb-phy@20 {
|
||||
compatible = "ti,dm8168-usb-phy";
|
||||
reg = <0x20 0x8>;
|
||||
reg-names = "phy";
|
||||
clocks = <&main_fapll 6>;
|
||||
clock-names = "refclk";
|
||||
#phy-cells = <0>;
|
||||
syscon = <&scm_conf>;
|
||||
};
|
@ -20,8 +20,8 @@ Required nodes : A sub-node is required for each channel the controller
|
||||
Required properties (port (child) node):
|
||||
- #phy-cells : Should be 1 (See second example)
|
||||
Cell after port phandle is device type from:
|
||||
- MIPHY_TYPE_SATA
|
||||
- MIPHY_TYPE_PCI
|
||||
- PHY_TYPE_SATA
|
||||
- PHY_TYPE_PCI
|
||||
- reg : Address and length of register sets for each device in
|
||||
"reg-names"
|
||||
- reg-names : The names of the register addresses corresponding to the
|
||||
@ -68,10 +68,10 @@ property, containing a phandle to the phy port node and a device type.
|
||||
|
||||
Example:
|
||||
|
||||
#include <dt-bindings/phy/phy-miphy365x.h>
|
||||
#include <dt-bindings/phy/phy.h>
|
||||
|
||||
sata0: sata@fe380000 {
|
||||
...
|
||||
phys = <&phy_port0 MIPHY_TYPE_SATA>;
|
||||
phys = <&phy_port0 PHY_TYPE_SATA>;
|
||||
...
|
||||
};
|
||||
|
@ -128,6 +128,7 @@ Required properties:
|
||||
- compatible : Should be set to one of the following supported values:
|
||||
- "samsung,exynos5250-usbdrd-phy" - for exynos5250 SoC,
|
||||
- "samsung,exynos5420-usbdrd-phy" - for exynos5420 SoC.
|
||||
- "samsung,exynos5433-usbdrd-phy" - for exynos5433 SoC.
|
||||
- "samsung,exynos7-usbdrd-phy" - for exynos7 SoC.
|
||||
- reg : Register offset and length of USB DRD PHY register set;
|
||||
- clocks: Clock IDs array as required by the controller
|
||||
@ -139,7 +140,7 @@ Required properties:
|
||||
PHY operations, associated by phy name. It is used to
|
||||
determine bit values for clock settings register.
|
||||
For Exynos5420 this is given as 'sclk_usbphy30' in CMU.
|
||||
- optional clocks: Exynos7 SoC has now following additional
|
||||
- optional clocks: Exynos5433 & Exynos7 SoC has now following additional
|
||||
gate clocks available:
|
||||
- phy_pipe: for PIPE3 phy
|
||||
- phy_utmi: for UTMI+ phy
|
||||
|
38
Documentation/devicetree/bindings/phy/sun9i-usb-phy.txt
Normal file
38
Documentation/devicetree/bindings/phy/sun9i-usb-phy.txt
Normal file
@ -0,0 +1,38 @@
|
||||
Allwinner sun9i USB PHY
|
||||
-----------------------
|
||||
|
||||
Required properties:
|
||||
- compatible : should be one of
|
||||
* allwinner,sun9i-a80-usb-phy
|
||||
- reg : a list of offset + length pairs
|
||||
- #phy-cells : from the generic phy bindings, must be 0
|
||||
- phy_type : "hsic" for HSIC usage;
|
||||
other values or absence of this property indicates normal USB
|
||||
- clocks : phandle + clock specifier for the phy clocks
|
||||
- clock-names : depending on the "phy_type" property,
|
||||
* "phy" for normal USB
|
||||
* "hsic_480M", "hsic_12M" for HSIC
|
||||
- resets : a list of phandle + reset specifier pairs
|
||||
- reset-names : depending on the "phy_type" property,
|
||||
* "phy" for normal USB
|
||||
* "hsic" for HSIC
|
||||
|
||||
Optional Properties:
|
||||
- phy-supply : from the generic phy bindings, a phandle to a regulator that
|
||||
provides power to VBUS.
|
||||
|
||||
It is recommended to list all clocks and resets available.
|
||||
The driver will only use those matching the phy_type.
|
||||
|
||||
Example:
|
||||
usbphy1: phy@00a01800 {
|
||||
compatible = "allwinner,sun9i-a80-usb-phy";
|
||||
reg = <0x00a01800 0x4>;
|
||||
clocks = <&usb_phy_clk 2>, <&usb_phy_clk 10>,
|
||||
<&usb_phy_clk 3>;
|
||||
clock-names = "hsic_480M", "hsic_12M", "phy";
|
||||
resets = <&usb_phy_clk 18>, <&usb_phy_clk 19>;
|
||||
reset-names = "hsic", "phy";
|
||||
status = "disabled";
|
||||
#phy-cells = <0>;
|
||||
};
|
@ -14,6 +14,7 @@ Optional properties:
|
||||
- phys: from the *Generic PHY* bindings
|
||||
- phy-names: from the *Generic PHY* bindings
|
||||
- tx-fifo-resize: determines if the FIFO *has* to be reallocated.
|
||||
- snps,usb3_lpm_capable: determines if platform is USB3 LPM capable
|
||||
- snps,disable_scramble_quirk: true when SW should disable data scrambling.
|
||||
Only really useful for FPGA builds.
|
||||
- snps,has-lpm-erratum: true when DWC3 was configured with LPM Erratum enabled
|
||||
|
@ -15,7 +15,10 @@ Optional properties:
|
||||
- phys: phandle + phy specifier pair
|
||||
- phy-names: must be "usb"
|
||||
- dmas: Must contain a list of references to DMA specifiers.
|
||||
- dma-names : Must contain a list of DMA names, "tx" or "rx".
|
||||
- dma-names : Must contain a list of DMA names:
|
||||
- tx0 ... tx<n>
|
||||
- rx0 ... rx<n>
|
||||
- This <n> means DnFIFO in USBHS module.
|
||||
|
||||
Example:
|
||||
usbhs: usb@e6590000 {
|
||||
|
@ -5,6 +5,7 @@ Required properties:
|
||||
- compatible: Should be one of below:
|
||||
"fsl,imx6q-usbmisc" for imx6q
|
||||
"fsl,vf610-usbmisc" for Vybrid vf610
|
||||
"fsl,imx6sx-usbmisc" for imx6sx
|
||||
- reg: Should contain registers location and length
|
||||
|
||||
Examples:
|
||||
|
@ -69,3 +69,24 @@ cat /sys/kernel/debug/ci_hdrc.0/registers
|
||||
----------------------
|
||||
"On-The-Go and Embedded Host Supplement to the USB Revision 2.0 Specification
|
||||
July 27, 2012 Revision 2.0 version 1.1a"
|
||||
|
||||
2. How to enable USB as system wakeup source
|
||||
-----------------------------------
|
||||
Below is the example for how to enable USB as system wakeup source
|
||||
at imx6 platform.
|
||||
|
||||
2.1 Enable core's wakeup
|
||||
echo enabled > /sys/bus/platform/devices/ci_hdrc.0/power/wakeup
|
||||
2.2 Enable glue layer's wakeup
|
||||
echo enabled > /sys/bus/platform/devices/2184000.usb/power/wakeup
|
||||
2.3 Enable PHY's wakeup (optional)
|
||||
echo enabled > /sys/bus/platform/devices/20c9000.usbphy/power/wakeup
|
||||
2.4 Enable roothub's wakeup
|
||||
echo enabled > /sys/bus/usb/devices/usb1/power/wakeup
|
||||
2.5 Enable related device's wakeup
|
||||
echo enabled > /sys/bus/usb/devices/1-1/power/wakeup
|
||||
|
||||
If the system has only one usb port, and you want usb wakeup at this port, you
|
||||
can use below script to enable usb wakeup.
|
||||
for i in $(find /sys -name wakeup | grep usb);do echo enabled > $i;done;
|
||||
|
||||
|
@ -19,6 +19,7 @@ provided by gadgets.
|
||||
16. UAC1 function
|
||||
17. UAC2 function
|
||||
18. UVC function
|
||||
19. PRINTER function
|
||||
|
||||
|
||||
1. ACM function
|
||||
@ -726,3 +727,49 @@ with these patches:
|
||||
http://www.spinics.net/lists/linux-usb/msg99220.html
|
||||
|
||||
host: luvcview -f yuv
|
||||
|
||||
19. PRINTER function
|
||||
====================
|
||||
|
||||
The function is provided by usb_f_printer.ko module.
|
||||
|
||||
Function-specific configfs interface
|
||||
------------------------------------
|
||||
|
||||
The function name to use when creating the function directory is "printer".
|
||||
The printer function provides these attributes in its function directory:
|
||||
|
||||
pnp_string - Data to be passed to the host in pnp string
|
||||
q_len - Number of requests per endpoint
|
||||
|
||||
Testing the PRINTER function
|
||||
----------------------------
|
||||
|
||||
The most basic testing:
|
||||
|
||||
device: run the gadget
|
||||
# ls -l /devices/virtual/usb_printer_gadget/
|
||||
|
||||
should show g_printer<number>.
|
||||
|
||||
If udev is active, then /dev/g_printer<number> should appear automatically.
|
||||
|
||||
host:
|
||||
|
||||
If udev is active, then e.g. /dev/usb/lp0 should appear.
|
||||
|
||||
host->device transmission:
|
||||
|
||||
device:
|
||||
# cat /dev/g_printer<number>
|
||||
host:
|
||||
# cat > /dev/usb/lp0
|
||||
|
||||
device->host transmission:
|
||||
|
||||
# cat > /dev/g_printer<number>
|
||||
host:
|
||||
# cat /dev/usb/lp0
|
||||
|
||||
More advanced testing can be done with the prn_example
|
||||
described in Documentation/usb/gadget-printer.txt.
|
||||
|
12
MAINTAINERS
12
MAINTAINERS
@ -1468,6 +1468,8 @@ F: drivers/clocksource/arm_global_timer.c
|
||||
F: drivers/i2c/busses/i2c-st.c
|
||||
F: drivers/media/rc/st_rc.c
|
||||
F: drivers/mmc/host/sdhci-st.c
|
||||
F: drivers/phy/phy-miphy28lp.c
|
||||
F: drivers/phy/phy-miphy365x.c
|
||||
F: drivers/phy/phy-stih407-usb.c
|
||||
F: drivers/phy/phy-stih41x-usb.c
|
||||
F: drivers/pinctrl/pinctrl-st.c
|
||||
@ -2518,7 +2520,7 @@ F: Documentation/zh_CN/
|
||||
|
||||
CHIPIDEA USB HIGH SPEED DUAL ROLE CONTROLLER
|
||||
M: Peter Chen <Peter.Chen@freescale.com>
|
||||
T: git git://github.com/hzpeterchen/linux-usb.git
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git
|
||||
L: linux-usb@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/usb/chipidea/
|
||||
@ -10132,6 +10134,12 @@ S: Maintained
|
||||
F: drivers/net/usb/cdc_*.c
|
||||
F: include/uapi/linux/usb/cdc.h
|
||||
|
||||
USB CHAOSKEY DRIVER
|
||||
M: Keith Packard <keithp@keithp.com>
|
||||
L: linux-usb@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/usb/misc/chaoskey.c
|
||||
|
||||
USB CYPRESS C67X00 DRIVER
|
||||
M: Peter Korsgaard <jacmet@sunsite.dk>
|
||||
L: linux-usb@vger.kernel.org
|
||||
@ -10212,7 +10220,7 @@ F: drivers/usb/host/ohci*
|
||||
|
||||
USB OTG FSM (Finite State Machine)
|
||||
M: Peter Chen <Peter.Chen@freescale.com>
|
||||
T: git git://github.com/hzpeterchen/linux-usb.git
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git
|
||||
L: linux-usb@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/usb/common/usb-otg-fsm.c
|
||||
|
@ -10,7 +10,7 @@
|
||||
#include "stih416-clock.dtsi"
|
||||
#include "stih416-pinctrl.dtsi"
|
||||
|
||||
#include <dt-bindings/phy/phy-miphy365x.h>
|
||||
#include <dt-bindings/phy/phy.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/reset-controller/stih416-resets.h>
|
||||
/ {
|
||||
@ -306,7 +306,7 @@
|
||||
reg = <0xfe380000 0x1000>;
|
||||
interrupts = <GIC_SPI 157 IRQ_TYPE_NONE>;
|
||||
interrupt-names = "hostc";
|
||||
phys = <&phy_port0 MIPHY_TYPE_SATA>;
|
||||
phys = <&phy_port0 PHY_TYPE_SATA>;
|
||||
phy-names = "sata-phy";
|
||||
resets = <&powerdown STIH416_SATA0_POWERDOWN>,
|
||||
<&softreset STIH416_SATA0_SOFTRESET>;
|
||||
|
@ -35,6 +35,13 @@ config ARMADA375_USBCLUSTER_PHY
|
||||
depends on OF
|
||||
select GENERIC_PHY
|
||||
|
||||
config PHY_DM816X_USB
|
||||
tristate "TI dm816x USB PHY driver"
|
||||
depends on ARCH_OMAP2PLUS
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Enable this for dm816x USB to work.
|
||||
|
||||
config PHY_EXYNOS_MIPI_VIDEO
|
||||
tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver"
|
||||
depends on HAS_IOMEM
|
||||
@ -174,6 +181,17 @@ config PHY_SUN4I_USB
|
||||
This driver controls the entire USB PHY block, both the USB OTG
|
||||
parts, as well as the 2 regular USB 2 host PHYs.
|
||||
|
||||
config PHY_SUN9I_USB
|
||||
tristate "Allwinner sun9i SoC USB PHY driver"
|
||||
depends on ARCH_SUNXI && HAS_IOMEM && OF
|
||||
depends on RESET_CONTROLLER
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Enable this to support the transceiver that is part of Allwinner
|
||||
sun9i SoCs.
|
||||
|
||||
This driver controls each individual USB 2 host PHY.
|
||||
|
||||
config PHY_SAMSUNG_USB2
|
||||
tristate "Samsung USB 2.0 PHY driver"
|
||||
depends on HAS_IOMEM
|
||||
|
@ -5,6 +5,7 @@
|
||||
obj-$(CONFIG_GENERIC_PHY) += phy-core.o
|
||||
obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o
|
||||
obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o
|
||||
obj-$(CONFIG_PHY_DM816X_USB) += phy-dm816x-usb.o
|
||||
obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o
|
||||
obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o
|
||||
obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o
|
||||
@ -20,6 +21,7 @@ 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_SUN4I_USB) += phy-sun4i-usb.o
|
||||
obj-$(CONFIG_PHY_SUN9I_USB) += phy-sun9i-usb.o
|
||||
obj-$(CONFIG_PHY_SAMSUNG_USB2) += phy-exynos-usb2.o
|
||||
phy-exynos-usb2-y += phy-samsung-usb2.o
|
||||
phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4210_USB2) += phy-exynos4210-usb2.o
|
||||
|
@ -218,7 +218,7 @@ static int phy_berlin_sata_probe(struct platform_device *pdev)
|
||||
if (priv->nphys == 0)
|
||||
return -ENODEV;
|
||||
|
||||
priv->phys = devm_kzalloc(dev, priv->nphys * sizeof(*priv->phys),
|
||||
priv->phys = devm_kcalloc(dev, priv->nphys, sizeof(*priv->phys),
|
||||
GFP_KERNEL);
|
||||
if (!priv->phys)
|
||||
return -ENOMEM;
|
||||
|
@ -103,9 +103,6 @@
|
||||
#define MODE_TEST_EN BIT(11)
|
||||
#define ANA_TEST_DC_CTRL(x) ((x) << 12)
|
||||
|
||||
#define to_phy_berlin_usb_priv(p) \
|
||||
container_of((p), struct phy_berlin_usb_priv, phy)
|
||||
|
||||
static const u32 phy_berlin_pll_dividers[] = {
|
||||
/* Berlin 2 */
|
||||
CLK_REF_DIV(0xc) | FEEDBACK_CLK_DIV(0x54),
|
||||
@ -115,14 +112,13 @@ static const u32 phy_berlin_pll_dividers[] = {
|
||||
|
||||
struct phy_berlin_usb_priv {
|
||||
void __iomem *base;
|
||||
struct phy *phy;
|
||||
struct reset_control *rst_ctrl;
|
||||
u32 pll_divider;
|
||||
};
|
||||
|
||||
static int phy_berlin_usb_power_on(struct phy *phy)
|
||||
{
|
||||
struct phy_berlin_usb_priv *priv = dev_get_drvdata(phy->dev.parent);
|
||||
struct phy_berlin_usb_priv *priv = phy_get_drvdata(phy);
|
||||
|
||||
reset_control_reset(priv->rst_ctrl);
|
||||
|
||||
@ -175,6 +171,7 @@ static int phy_berlin_usb_probe(struct platform_device *pdev)
|
||||
of_match_device(phy_berlin_sata_of_match, &pdev->dev);
|
||||
struct phy_berlin_usb_priv *priv;
|
||||
struct resource *res;
|
||||
struct phy *phy;
|
||||
struct phy_provider *phy_provider;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
@ -192,20 +189,18 @@ static int phy_berlin_usb_probe(struct platform_device *pdev)
|
||||
|
||||
priv->pll_divider = *((u32 *)match->data);
|
||||
|
||||
priv->phy = devm_phy_create(&pdev->dev, NULL, &phy_berlin_usb_ops);
|
||||
if (IS_ERR(priv->phy)) {
|
||||
phy = devm_phy_create(&pdev->dev, NULL, &phy_berlin_usb_ops);
|
||||
if (IS_ERR(phy)) {
|
||||
dev_err(&pdev->dev, "failed to create PHY\n");
|
||||
return PTR_ERR(priv->phy);
|
||||
return PTR_ERR(phy);
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
phy_set_drvdata(phy, priv);
|
||||
|
||||
phy_provider =
|
||||
devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate);
|
||||
if (IS_ERR(phy_provider))
|
||||
return PTR_ERR(phy_provider);
|
||||
|
||||
return 0;
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
}
|
||||
|
||||
static struct platform_driver phy_berlin_usb_driver = {
|
||||
|
290
drivers/phy/phy-dm816x-usb.c
Normal file
290
drivers/phy/phy-dm816x-usb.c
Normal file
@ -0,0 +1,290 @@
|
||||
/*
|
||||
* 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 version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/usb/phy_companion.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
#include <linux/mfd/syscon.h>
|
||||
|
||||
/*
|
||||
* TRM has two sets of USB_CTRL registers.. The correct register bits
|
||||
* are in TRM section 24.9.8.2 USB_CTRL Register. The TRM documents the
|
||||
* phy as being SR70LX Synopsys USB 2.0 OTG nanoPHY. It also seems at
|
||||
* least dm816x rev c ignores writes to USB_CTRL register, but the TI
|
||||
* kernel is writing to those so it's possible that later revisions
|
||||
* have worknig USB_CTRL register.
|
||||
*
|
||||
* Also note that At least USB_CTRL register seems to be dm816x specific
|
||||
* according to the TRM. It's possible that USBPHY_CTRL is more generic,
|
||||
* but that would have to be checked against the SR70LX documentation
|
||||
* which does not seem to be publicly available.
|
||||
*
|
||||
* Finally, the phy on dm814x and am335x is different from dm816x.
|
||||
*/
|
||||
#define DM816X_USB_CTRL_PHYCLKSRC BIT(8) /* 1 = PLL ref clock */
|
||||
#define DM816X_USB_CTRL_PHYSLEEP1 BIT(1) /* Enable the first phy */
|
||||
#define DM816X_USB_CTRL_PHYSLEEP0 BIT(0) /* Enable the second phy */
|
||||
|
||||
#define DM816X_USBPHY_CTRL_TXRISETUNE 1
|
||||
#define DM816X_USBPHY_CTRL_TXVREFTUNE 0xc
|
||||
#define DM816X_USBPHY_CTRL_TXPREEMTUNE 0x2
|
||||
|
||||
struct dm816x_usb_phy {
|
||||
struct regmap *syscon;
|
||||
struct device *dev;
|
||||
unsigned int instance;
|
||||
struct clk *refclk;
|
||||
struct usb_phy phy;
|
||||
unsigned int usb_ctrl; /* Shared between phy0 and phy1 */
|
||||
unsigned int usbphy_ctrl;
|
||||
};
|
||||
|
||||
static int dm816x_usb_phy_set_host(struct usb_otg *otg, struct usb_bus *host)
|
||||
{
|
||||
otg->host = host;
|
||||
if (!host)
|
||||
otg->state = OTG_STATE_UNDEFINED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dm816x_usb_phy_set_peripheral(struct usb_otg *otg,
|
||||
struct usb_gadget *gadget)
|
||||
{
|
||||
otg->gadget = gadget;
|
||||
if (!gadget)
|
||||
otg->state = OTG_STATE_UNDEFINED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dm816x_usb_phy_init(struct phy *x)
|
||||
{
|
||||
struct dm816x_usb_phy *phy = phy_get_drvdata(x);
|
||||
unsigned int val;
|
||||
int error;
|
||||
|
||||
if (clk_get_rate(phy->refclk) != 24000000)
|
||||
dev_warn(phy->dev, "nonstandard phy refclk\n");
|
||||
|
||||
/* Set PLL ref clock and put phys to sleep */
|
||||
error = regmap_update_bits(phy->syscon, phy->usb_ctrl,
|
||||
DM816X_USB_CTRL_PHYCLKSRC |
|
||||
DM816X_USB_CTRL_PHYSLEEP1 |
|
||||
DM816X_USB_CTRL_PHYSLEEP0,
|
||||
0);
|
||||
regmap_read(phy->syscon, phy->usb_ctrl, &val);
|
||||
if ((val & 3) != 0)
|
||||
dev_info(phy->dev,
|
||||
"Working dm816x USB_CTRL! (0x%08x)\n",
|
||||
val);
|
||||
|
||||
/*
|
||||
* TI kernel sets these values for "symmetrical eye diagram and
|
||||
* better signal quality" so let's assume somebody checked the
|
||||
* values with a scope and set them here too.
|
||||
*/
|
||||
regmap_read(phy->syscon, phy->usbphy_ctrl, &val);
|
||||
val |= DM816X_USBPHY_CTRL_TXRISETUNE |
|
||||
DM816X_USBPHY_CTRL_TXVREFTUNE |
|
||||
DM816X_USBPHY_CTRL_TXPREEMTUNE;
|
||||
regmap_write(phy->syscon, phy->usbphy_ctrl, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct phy_ops ops = {
|
||||
.init = dm816x_usb_phy_init,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int dm816x_usb_phy_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct dm816x_usb_phy *phy = dev_get_drvdata(dev);
|
||||
unsigned int mask, val;
|
||||
int error = 0;
|
||||
|
||||
mask = BIT(phy->instance);
|
||||
val = ~BIT(phy->instance);
|
||||
error = regmap_update_bits(phy->syscon, phy->usb_ctrl,
|
||||
mask, val);
|
||||
if (error)
|
||||
dev_err(phy->dev, "phy%i failed to power off\n",
|
||||
phy->instance);
|
||||
clk_disable(phy->refclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dm816x_usb_phy_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct dm816x_usb_phy *phy = dev_get_drvdata(dev);
|
||||
unsigned int mask, val;
|
||||
int error;
|
||||
|
||||
error = clk_enable(phy->refclk);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/*
|
||||
* Note that at least dm816x rev c does not seem to do
|
||||
* anything with the USB_CTRL register. But let's follow
|
||||
* what the TI tree is doing in case later revisions use
|
||||
* USB_CTRL.
|
||||
*/
|
||||
mask = BIT(phy->instance);
|
||||
val = BIT(phy->instance);
|
||||
error = regmap_update_bits(phy->syscon, phy->usb_ctrl,
|
||||
mask, val);
|
||||
if (error) {
|
||||
dev_err(phy->dev, "phy%i failed to power on\n",
|
||||
phy->instance);
|
||||
clk_disable(phy->refclk);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static UNIVERSAL_DEV_PM_OPS(dm816x_usb_phy_pm_ops,
|
||||
dm816x_usb_phy_runtime_suspend,
|
||||
dm816x_usb_phy_runtime_resume,
|
||||
NULL);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id dm816x_usb_phy_id_table[] = {
|
||||
{
|
||||
.compatible = "ti,dm8168-usb-phy",
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dm816x_usb_phy_id_table);
|
||||
#endif
|
||||
|
||||
static int dm816x_usb_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct dm816x_usb_phy *phy;
|
||||
struct resource *res;
|
||||
struct phy *generic_phy;
|
||||
struct phy_provider *phy_provider;
|
||||
struct usb_otg *otg;
|
||||
const struct of_device_id *of_id;
|
||||
const struct usb_phy_data *phy_data;
|
||||
int error;
|
||||
|
||||
of_id = of_match_device(of_match_ptr(dm816x_usb_phy_id_table),
|
||||
&pdev->dev);
|
||||
if (!of_id)
|
||||
return -EINVAL;
|
||||
|
||||
phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
|
||||
if (!phy)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -ENOENT;
|
||||
|
||||
phy->syscon = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
|
||||
"syscon");
|
||||
if (IS_ERR(phy->syscon))
|
||||
return PTR_ERR(phy->syscon);
|
||||
|
||||
/*
|
||||
* According to sprs614e.pdf, the first usb_ctrl is shared and
|
||||
* the second instance for usb_ctrl is reserved.. Also the
|
||||
* register bits are different from earlier TRMs.
|
||||
*/
|
||||
phy->usb_ctrl = 0x20;
|
||||
phy->usbphy_ctrl = (res->start & 0xff) + 4;
|
||||
if (phy->usbphy_ctrl == 0x2c)
|
||||
phy->instance = 1;
|
||||
|
||||
phy_data = of_id->data;
|
||||
|
||||
otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL);
|
||||
if (!otg)
|
||||
return -ENOMEM;
|
||||
|
||||
phy->dev = &pdev->dev;
|
||||
phy->phy.dev = phy->dev;
|
||||
phy->phy.label = "dm8168_usb_phy";
|
||||
phy->phy.otg = otg;
|
||||
phy->phy.type = USB_PHY_TYPE_USB2;
|
||||
otg->set_host = dm816x_usb_phy_set_host;
|
||||
otg->set_peripheral = dm816x_usb_phy_set_peripheral;
|
||||
otg->usb_phy = &phy->phy;
|
||||
|
||||
platform_set_drvdata(pdev, phy);
|
||||
|
||||
phy->refclk = devm_clk_get(phy->dev, "refclk");
|
||||
if (IS_ERR(phy->refclk))
|
||||
return PTR_ERR(phy->refclk);
|
||||
error = clk_prepare(phy->refclk);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
pm_runtime_enable(phy->dev);
|
||||
generic_phy = devm_phy_create(phy->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);
|
||||
if (IS_ERR(phy_provider))
|
||||
return PTR_ERR(phy_provider);
|
||||
|
||||
usb_add_phy_dev(&phy->phy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dm816x_usb_phy_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dm816x_usb_phy *phy = platform_get_drvdata(pdev);
|
||||
|
||||
usb_remove_phy(&phy->phy);
|
||||
pm_runtime_disable(phy->dev);
|
||||
clk_unprepare(phy->refclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver dm816x_usb_phy_driver = {
|
||||
.probe = dm816x_usb_phy_probe,
|
||||
.remove = dm816x_usb_phy_remove,
|
||||
.driver = {
|
||||
.name = "dm816x-usb-phy",
|
||||
.pm = &dm816x_usb_phy_pm_ops,
|
||||
.of_match_table = of_match_ptr(dm816x_usb_phy_id_table),
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(dm816x_usb_phy_driver);
|
||||
|
||||
MODULE_ALIAS("platform:dm816x_usb");
|
||||
MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>");
|
||||
MODULE_DESCRIPTION("dm816x usb phy driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -624,6 +624,13 @@ static const struct exynos5_usbdrd_phy_drvdata exynos5250_usbdrd_phy = {
|
||||
.has_common_clk_gate = true,
|
||||
};
|
||||
|
||||
static const struct exynos5_usbdrd_phy_drvdata exynos5433_usbdrd_phy = {
|
||||
.phy_cfg = phy_cfg_exynos5,
|
||||
.pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL,
|
||||
.pmu_offset_usbdrd1_phy = EXYNOS5433_USBHOST30_PHY_CONTROL,
|
||||
.has_common_clk_gate = false,
|
||||
};
|
||||
|
||||
static const struct exynos5_usbdrd_phy_drvdata exynos7_usbdrd_phy = {
|
||||
.phy_cfg = phy_cfg_exynos5,
|
||||
.pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL,
|
||||
@ -637,6 +644,9 @@ static const struct of_device_id exynos5_usbdrd_phy_of_match[] = {
|
||||
}, {
|
||||
.compatible = "samsung,exynos5420-usbdrd-phy",
|
||||
.data = &exynos5420_usbdrd_phy
|
||||
}, {
|
||||
.compatible = "samsung,exynos5433-usbdrd-phy",
|
||||
.data = &exynos5433_usbdrd_phy
|
||||
}, {
|
||||
.compatible = "samsung,exynos7-usbdrd-phy",
|
||||
.data = &exynos7_usbdrd_phy
|
||||
|
@ -1259,10 +1259,7 @@ static int miphy28lp_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
provider = devm_of_phy_provider_register(&pdev->dev, miphy28lp_xlate);
|
||||
if (IS_ERR(provider))
|
||||
return PTR_ERR(provider);
|
||||
|
||||
return 0;
|
||||
return PTR_ERR_OR_ZERO(provider);
|
||||
}
|
||||
|
||||
static const struct of_device_id miphy28lp_of_match[] = {
|
||||
|
@ -25,7 +25,7 @@
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <dt-bindings/phy/phy-miphy365x.h>
|
||||
#include <dt-bindings/phy/phy.h>
|
||||
|
||||
#define HFC_TIMEOUT 100
|
||||
|
||||
@ -177,7 +177,7 @@ static u8 rx_tx_spd[] = {
|
||||
static int miphy365x_set_path(struct miphy365x_phy *miphy_phy,
|
||||
struct miphy365x_dev *miphy_dev)
|
||||
{
|
||||
bool sata = (miphy_phy->type == MIPHY_TYPE_SATA);
|
||||
bool sata = (miphy_phy->type == PHY_TYPE_SATA);
|
||||
|
||||
return regmap_update_bits(miphy_dev->regmap,
|
||||
miphy_phy->ctrlreg,
|
||||
@ -431,7 +431,7 @@ static int miphy365x_init(struct phy *phy)
|
||||
}
|
||||
|
||||
/* Initialise Miphy for PCIe or SATA */
|
||||
if (miphy_phy->type == MIPHY_TYPE_PCIE)
|
||||
if (miphy_phy->type == PHY_TYPE_PCIE)
|
||||
ret = miphy365x_init_pcie_port(miphy_phy, miphy_dev);
|
||||
else
|
||||
ret = miphy365x_init_sata_port(miphy_phy, miphy_dev);
|
||||
@ -455,8 +455,8 @@ int miphy365x_get_addr(struct device *dev, struct miphy365x_phy *miphy_phy,
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!((!strncmp(name, "sata", 4) && type == MIPHY_TYPE_SATA) ||
|
||||
(!strncmp(name, "pcie", 4) && type == MIPHY_TYPE_PCIE)))
|
||||
if (!((!strncmp(name, "sata", 4) && type == PHY_TYPE_SATA) ||
|
||||
(!strncmp(name, "pcie", 4) && type == PHY_TYPE_PCIE)))
|
||||
return 0;
|
||||
|
||||
miphy_phy->base = of_iomap(phynode, index);
|
||||
@ -499,8 +499,8 @@ static struct phy *miphy365x_xlate(struct device *dev,
|
||||
|
||||
miphy_phy->type = args->args[0];
|
||||
|
||||
if (!(miphy_phy->type == MIPHY_TYPE_SATA ||
|
||||
miphy_phy->type == MIPHY_TYPE_PCIE)) {
|
||||
if (!(miphy_phy->type == PHY_TYPE_SATA ||
|
||||
miphy_phy->type == PHY_TYPE_PCIE)) {
|
||||
dev_err(dev, "Unsupported device type: %d\n", miphy_phy->type);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
@ -216,7 +216,6 @@ void omap_control_usb_set_mode(struct device *dev,
|
||||
return;
|
||||
|
||||
ctrl_phy = dev_get_drvdata(dev);
|
||||
|
||||
if (!ctrl_phy) {
|
||||
dev_err(dev, "Invalid control phy device\n");
|
||||
return;
|
||||
@ -241,8 +240,6 @@ void omap_control_usb_set_mode(struct device *dev,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(omap_control_usb_set_mode);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
||||
static const enum omap_control_phy_type otghs_data = OMAP_CTRL_TYPE_OTGHS;
|
||||
static const enum omap_control_phy_type usb2_data = OMAP_CTRL_TYPE_USB2;
|
||||
static const enum omap_control_phy_type pipe3_data = OMAP_CTRL_TYPE_PIPE3;
|
||||
@ -278,8 +275,6 @@ static const struct of_device_id omap_control_phy_id_table[] = {
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, omap_control_phy_id_table);
|
||||
#endif
|
||||
|
||||
|
||||
static int omap_control_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
@ -287,8 +282,7 @@ static int omap_control_phy_probe(struct platform_device *pdev)
|
||||
const struct of_device_id *of_id;
|
||||
struct omap_control_phy *control_phy;
|
||||
|
||||
of_id = of_match_device(of_match_ptr(omap_control_phy_id_table),
|
||||
&pdev->dev);
|
||||
of_id = of_match_device(omap_control_phy_id_table, &pdev->dev);
|
||||
if (!of_id)
|
||||
return -EINVAL;
|
||||
|
||||
@ -344,7 +338,7 @@ static struct platform_driver omap_control_phy_driver = {
|
||||
.probe = omap_control_phy_probe,
|
||||
.driver = {
|
||||
.name = "omap-control-phy",
|
||||
.of_match_table = of_match_ptr(omap_control_phy_id_table),
|
||||
.of_match_table = omap_control_phy_id_table,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -144,7 +144,6 @@ static struct phy_ops ops = {
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct usb_phy_data omap_usb2_data = {
|
||||
.label = "omap_usb2",
|
||||
.flags = OMAP_USB2_HAS_START_SRP | OMAP_USB2_HAS_SET_VBUS,
|
||||
@ -185,7 +184,6 @@ static const struct of_device_id omap_usb2_id_table[] = {
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, omap_usb2_id_table);
|
||||
#endif
|
||||
|
||||
static int omap_usb2_probe(struct platform_device *pdev)
|
||||
{
|
||||
@ -200,7 +198,7 @@ static int omap_usb2_probe(struct platform_device *pdev)
|
||||
const struct of_device_id *of_id;
|
||||
struct usb_phy_data *phy_data;
|
||||
|
||||
of_id = of_match_device(of_match_ptr(omap_usb2_id_table), &pdev->dev);
|
||||
of_id = of_match_device(omap_usb2_id_table, &pdev->dev);
|
||||
|
||||
if (!of_id)
|
||||
return -EINVAL;
|
||||
@ -378,7 +376,7 @@ static struct platform_driver omap_usb2_driver = {
|
||||
.driver = {
|
||||
.name = "omap-usb2",
|
||||
.pm = DEV_PM_OPS,
|
||||
.of_match_table = of_match_ptr(omap_usb2_id_table),
|
||||
.of_match_table = omap_usb2_id_table,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -73,6 +73,7 @@ int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy,
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ufs_qcom_phy_calibrate);
|
||||
|
||||
struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev,
|
||||
struct ufs_qcom_phy *common_cfg,
|
||||
@ -101,6 +102,7 @@ struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev,
|
||||
if (IS_ERR(generic_phy)) {
|
||||
err = PTR_ERR(generic_phy);
|
||||
dev_err(dev, "%s: failed to create phy %d\n", __func__, err);
|
||||
generic_phy = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -110,6 +112,7 @@ struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev,
|
||||
out:
|
||||
return generic_phy;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ufs_qcom_phy_generic_probe);
|
||||
|
||||
/*
|
||||
* This assumes the embedded phy structure inside generic_phy is of type
|
||||
@ -121,6 +124,7 @@ struct ufs_qcom_phy *get_ufs_qcom_phy(struct phy *generic_phy)
|
||||
{
|
||||
return (struct ufs_qcom_phy *)phy_get_drvdata(generic_phy);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(get_ufs_qcom_phy);
|
||||
|
||||
static
|
||||
int ufs_qcom_phy_base_init(struct platform_device *pdev,
|
||||
@ -131,40 +135,23 @@ int ufs_qcom_phy_base_init(struct platform_device *pdev,
|
||||
int err = 0;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_mem");
|
||||
if (!res) {
|
||||
dev_err(dev, "%s: phy_mem resource not found\n", __func__);
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
phy_common->mmio = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR((void const *)phy_common->mmio)) {
|
||||
err = PTR_ERR((void const *)phy_common->mmio);
|
||||
phy_common->mmio = NULL;
|
||||
dev_err(dev, "%s: ioremap for phy_mem resource failed %d\n",
|
||||
__func__, err);
|
||||
goto out;
|
||||
return err;
|
||||
}
|
||||
|
||||
/* "dev_ref_clk_ctrl_mem" is optional resource */
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"dev_ref_clk_ctrl_mem");
|
||||
if (!res) {
|
||||
dev_dbg(dev, "%s: dev_ref_clk_ctrl_mem resource not found\n",
|
||||
__func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
phy_common->dev_ref_clk_ctrl_mmio = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR((void const *)phy_common->dev_ref_clk_ctrl_mmio)) {
|
||||
err = PTR_ERR((void const *)phy_common->dev_ref_clk_ctrl_mmio);
|
||||
if (IS_ERR((void const *)phy_common->dev_ref_clk_ctrl_mmio))
|
||||
phy_common->dev_ref_clk_ctrl_mmio = NULL;
|
||||
dev_err(dev, "%s: ioremap for dev_ref_clk_ctrl_mem resource failed %d\n",
|
||||
__func__, err);
|
||||
}
|
||||
|
||||
out:
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __ufs_qcom_phy_clk_get(struct phy *phy,
|
||||
@ -228,6 +215,7 @@ ufs_qcom_phy_init_clks(struct phy *generic_phy,
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ufs_qcom_phy_init_clks);
|
||||
|
||||
int
|
||||
ufs_qcom_phy_init_vregulators(struct phy *generic_phy,
|
||||
@ -252,6 +240,7 @@ ufs_qcom_phy_init_vregulators(struct phy *generic_phy,
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ufs_qcom_phy_init_vregulators);
|
||||
|
||||
static int __ufs_qcom_phy_init_vreg(struct phy *phy,
|
||||
struct ufs_qcom_phy_vreg *vreg, const char *name, bool optional)
|
||||
@ -647,6 +636,7 @@ int ufs_qcom_phy_remove(struct phy *generic_phy,
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ufs_qcom_phy_remove);
|
||||
|
||||
int ufs_qcom_phy_exit(struct phy *generic_phy)
|
||||
{
|
||||
@ -657,6 +647,7 @@ int ufs_qcom_phy_exit(struct phy *generic_phy)
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ufs_qcom_phy_exit);
|
||||
|
||||
int ufs_qcom_phy_is_pcs_ready(struct phy *generic_phy)
|
||||
{
|
||||
@ -725,6 +716,7 @@ out_disable_phy:
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ufs_qcom_phy_power_on);
|
||||
|
||||
int ufs_qcom_phy_power_off(struct phy *generic_phy)
|
||||
{
|
||||
@ -743,3 +735,4 @@ int ufs_qcom_phy_power_off(struct phy *generic_phy)
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ufs_qcom_phy_power_off);
|
||||
|
@ -37,10 +37,14 @@ static int samsung_usb2_phy_power_on(struct phy *phy)
|
||||
spin_lock(&drv->lock);
|
||||
ret = inst->cfg->power_on(inst);
|
||||
spin_unlock(&drv->lock);
|
||||
if (ret)
|
||||
goto err_power_on;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_power_on:
|
||||
clk_disable_unprepare(drv->ref_clk);
|
||||
err_instance_clk:
|
||||
clk_disable_unprepare(drv->clk);
|
||||
err_main_clk:
|
||||
@ -51,7 +55,7 @@ static int samsung_usb2_phy_power_off(struct phy *phy)
|
||||
{
|
||||
struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy);
|
||||
struct samsung_usb2_phy_driver *drv = inst->drv;
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
dev_dbg(drv->dev, "Request to power_off \"%s\" usb phy\n",
|
||||
inst->cfg->label);
|
||||
@ -59,10 +63,12 @@ static int samsung_usb2_phy_power_off(struct phy *phy)
|
||||
spin_lock(&drv->lock);
|
||||
ret = inst->cfg->power_off(inst);
|
||||
spin_unlock(&drv->lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
clk_disable_unprepare(drv->ref_clk);
|
||||
clk_disable_unprepare(drv->clk);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct phy_ops samsung_usb2_phy_ops = {
|
||||
|
@ -192,14 +192,14 @@ static struct phy *spear1310_miphy_xlate(struct device *dev,
|
||||
|
||||
if (args->args_count < 1) {
|
||||
dev_err(dev, "DT did not pass correct no of args\n");
|
||||
return NULL;
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
priv->mode = args->args[0];
|
||||
|
||||
if (priv->mode != SATA && priv->mode != PCIE) {
|
||||
dev_err(dev, "DT did not pass correct phy mode\n");
|
||||
return NULL;
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
return priv->phy;
|
||||
|
@ -229,14 +229,14 @@ static struct phy *spear1340_miphy_xlate(struct device *dev,
|
||||
|
||||
if (args->args_count < 1) {
|
||||
dev_err(dev, "DT did not pass correct no of args\n");
|
||||
return NULL;
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
priv->mode = args->args[0];
|
||||
|
||||
if (priv->mode != SATA && priv->mode != PCIE) {
|
||||
dev_err(dev, "DT did not pass correct phy mode\n");
|
||||
return NULL;
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
return priv->phy;
|
||||
|
@ -87,8 +87,12 @@ static int stih41x_usb_phy_power_on(struct phy *phy)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return regmap_update_bits(phy_dev->regmap, phy_dev->cfg->syscfg,
|
||||
phy_dev->cfg->oscok, phy_dev->cfg->oscok);
|
||||
ret = regmap_update_bits(phy_dev->regmap, phy_dev->cfg->syscfg,
|
||||
phy_dev->cfg->oscok, phy_dev->cfg->oscok);
|
||||
if (ret)
|
||||
clk_disable_unprepare(phy_dev->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stih41x_usb_phy_power_off(struct phy *phy)
|
||||
|
202
drivers/phy/phy-sun9i-usb.c
Normal file
202
drivers/phy/phy-sun9i-usb.c
Normal file
@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Allwinner sun9i USB phy driver
|
||||
*
|
||||
* Copyright (C) 2014-2015 Chen-Yu Tsai <wens@csie.org>
|
||||
*
|
||||
* Based on phy-sun4i-usb.c from
|
||||
* Hans de Goede <hdegoede@redhat.com>
|
||||
*
|
||||
* and code from
|
||||
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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/phy/phy.h>
|
||||
#include <linux/usb/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#define SUNXI_AHB_INCR16_BURST_EN BIT(11)
|
||||
#define SUNXI_AHB_INCR8_BURST_EN BIT(10)
|
||||
#define SUNXI_AHB_INCR4_BURST_EN BIT(9)
|
||||
#define SUNXI_AHB_INCRX_ALIGN_EN BIT(8)
|
||||
#define SUNXI_ULPI_BYPASS_EN BIT(0)
|
||||
|
||||
/* usb1 HSIC specific bits */
|
||||
#define SUNXI_EHCI_HS_FORCE BIT(20)
|
||||
#define SUNXI_HSIC_CONNECT_DET BIT(17)
|
||||
#define SUNXI_HSIC_CONNECT_INT BIT(16)
|
||||
#define SUNXI_HSIC BIT(1)
|
||||
|
||||
struct sun9i_usb_phy {
|
||||
struct phy *phy;
|
||||
void __iomem *pmu;
|
||||
struct reset_control *reset;
|
||||
struct clk *clk;
|
||||
struct clk *hsic_clk;
|
||||
enum usb_phy_interface type;
|
||||
};
|
||||
|
||||
static void sun9i_usb_phy_passby(struct sun9i_usb_phy *phy, int enable)
|
||||
{
|
||||
u32 bits, reg_value;
|
||||
|
||||
bits = SUNXI_AHB_INCR16_BURST_EN | SUNXI_AHB_INCR8_BURST_EN |
|
||||
SUNXI_AHB_INCR4_BURST_EN | SUNXI_AHB_INCRX_ALIGN_EN |
|
||||
SUNXI_ULPI_BYPASS_EN;
|
||||
|
||||
if (phy->type == USBPHY_INTERFACE_MODE_HSIC)
|
||||
bits |= SUNXI_HSIC | SUNXI_EHCI_HS_FORCE |
|
||||
SUNXI_HSIC_CONNECT_DET | SUNXI_HSIC_CONNECT_INT;
|
||||
|
||||
reg_value = readl(phy->pmu);
|
||||
|
||||
if (enable)
|
||||
reg_value |= bits;
|
||||
else
|
||||
reg_value &= ~bits;
|
||||
|
||||
writel(reg_value, phy->pmu);
|
||||
}
|
||||
|
||||
static int sun9i_usb_phy_init(struct phy *_phy)
|
||||
{
|
||||
struct sun9i_usb_phy *phy = phy_get_drvdata(_phy);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(phy->clk);
|
||||
if (ret)
|
||||
goto err_clk;
|
||||
|
||||
ret = clk_prepare_enable(phy->hsic_clk);
|
||||
if (ret)
|
||||
goto err_hsic_clk;
|
||||
|
||||
ret = reset_control_deassert(phy->reset);
|
||||
if (ret)
|
||||
goto err_reset;
|
||||
|
||||
sun9i_usb_phy_passby(phy, 1);
|
||||
return 0;
|
||||
|
||||
err_reset:
|
||||
clk_disable_unprepare(phy->hsic_clk);
|
||||
|
||||
err_hsic_clk:
|
||||
clk_disable_unprepare(phy->clk);
|
||||
|
||||
err_clk:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sun9i_usb_phy_exit(struct phy *_phy)
|
||||
{
|
||||
struct sun9i_usb_phy *phy = phy_get_drvdata(_phy);
|
||||
|
||||
sun9i_usb_phy_passby(phy, 0);
|
||||
reset_control_assert(phy->reset);
|
||||
clk_disable_unprepare(phy->hsic_clk);
|
||||
clk_disable_unprepare(phy->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct phy_ops sun9i_usb_phy_ops = {
|
||||
.init = sun9i_usb_phy_init,
|
||||
.exit = sun9i_usb_phy_exit,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int sun9i_usb_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sun9i_usb_phy *phy;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct phy_provider *phy_provider;
|
||||
struct resource *res;
|
||||
|
||||
phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
|
||||
if (!phy)
|
||||
return -ENOMEM;
|
||||
|
||||
phy->type = of_usb_get_phy_mode(np);
|
||||
if (phy->type == USBPHY_INTERFACE_MODE_HSIC) {
|
||||
phy->clk = devm_clk_get(dev, "hsic_480M");
|
||||
if (IS_ERR(phy->clk)) {
|
||||
dev_err(dev, "failed to get hsic_480M clock\n");
|
||||
return PTR_ERR(phy->clk);
|
||||
}
|
||||
|
||||
phy->hsic_clk = devm_clk_get(dev, "hsic_12M");
|
||||
if (IS_ERR(phy->clk)) {
|
||||
dev_err(dev, "failed to get hsic_12M clock\n");
|
||||
return PTR_ERR(phy->clk);
|
||||
}
|
||||
|
||||
phy->reset = devm_reset_control_get(dev, "hsic");
|
||||
if (IS_ERR(phy->reset)) {
|
||||
dev_err(dev, "failed to get reset control\n");
|
||||
return PTR_ERR(phy->reset);
|
||||
}
|
||||
} else {
|
||||
phy->clk = devm_clk_get(dev, "phy");
|
||||
if (IS_ERR(phy->clk)) {
|
||||
dev_err(dev, "failed to get phy clock\n");
|
||||
return PTR_ERR(phy->clk);
|
||||
}
|
||||
|
||||
phy->reset = devm_reset_control_get(dev, "phy");
|
||||
if (IS_ERR(phy->reset)) {
|
||||
dev_err(dev, "failed to get reset control\n");
|
||||
return PTR_ERR(phy->reset);
|
||||
}
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
phy->pmu = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(phy->pmu))
|
||||
return PTR_ERR(phy->pmu);
|
||||
|
||||
phy->phy = devm_phy_create(dev, NULL, &sun9i_usb_phy_ops);
|
||||
if (IS_ERR(phy->phy)) {
|
||||
dev_err(dev, "failed to create PHY\n");
|
||||
return PTR_ERR(phy->phy);
|
||||
}
|
||||
|
||||
phy_set_drvdata(phy->phy, phy);
|
||||
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 sun9i_usb_phy_of_match[] = {
|
||||
{ .compatible = "allwinner,sun9i-a80-usb-phy" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sun9i_usb_phy_of_match);
|
||||
|
||||
static struct platform_driver sun9i_usb_phy_driver = {
|
||||
.probe = sun9i_usb_phy_probe,
|
||||
.driver = {
|
||||
.of_match_table = sun9i_usb_phy_of_match,
|
||||
.name = "sun9i-usb-phy",
|
||||
}
|
||||
};
|
||||
module_platform_driver(sun9i_usb_phy_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Allwinner sun9i USB phy driver");
|
||||
MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
|
||||
MODULE_LICENSE("GPL");
|
@ -287,9 +287,7 @@ static struct phy_ops ops = {
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id ti_pipe3_id_table[];
|
||||
#endif
|
||||
|
||||
static int ti_pipe3_probe(struct platform_device *pdev)
|
||||
{
|
||||
@ -311,8 +309,7 @@ static int ti_pipe3_probe(struct platform_device *pdev)
|
||||
spin_lock_init(&phy->lock);
|
||||
|
||||
if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
|
||||
match = of_match_device(of_match_ptr(ti_pipe3_id_table),
|
||||
&pdev->dev);
|
||||
match = of_match_device(ti_pipe3_id_table, &pdev->dev);
|
||||
if (!match)
|
||||
return -EINVAL;
|
||||
|
||||
@ -570,7 +567,6 @@ static const struct dev_pm_ops ti_pipe3_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(ti_pipe3_suspend, ti_pipe3_resume)
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id ti_pipe3_id_table[] = {
|
||||
{
|
||||
.compatible = "ti,phy-usb3",
|
||||
@ -590,7 +586,6 @@ static const struct of_device_id ti_pipe3_id_table[] = {
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ti_pipe3_id_table);
|
||||
#endif
|
||||
|
||||
static struct platform_driver ti_pipe3_driver = {
|
||||
.probe = ti_pipe3_probe,
|
||||
@ -598,7 +593,7 @@ static struct platform_driver ti_pipe3_driver = {
|
||||
.driver = {
|
||||
.name = "ti-pipe3",
|
||||
.pm = &ti_pipe3_pm_ops,
|
||||
.of_match_table = of_match_ptr(ti_pipe3_id_table),
|
||||
.of_match_table = ti_pipe3_id_table,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -1657,7 +1657,6 @@ static int xgene_phy_probe(struct platform_device *pdev)
|
||||
struct phy_provider *phy_provider;
|
||||
struct xgene_phy_ctx *ctx;
|
||||
struct resource *res;
|
||||
int rc = 0;
|
||||
u32 default_spd[] = DEFAULT_SATA_SPD_SEL;
|
||||
u32 default_txboost_gain[] = DEFAULT_SATA_TXBOOST_GAIN;
|
||||
u32 default_txeye_direction[] = DEFAULT_SATA_TXEYEDIRECTION;
|
||||
@ -1676,10 +1675,8 @@ static int xgene_phy_probe(struct platform_device *pdev)
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
ctx->sds_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(ctx->sds_base)) {
|
||||
rc = PTR_ERR(ctx->sds_base);
|
||||
goto error;
|
||||
}
|
||||
if (IS_ERR(ctx->sds_base))
|
||||
return PTR_ERR(ctx->sds_base);
|
||||
|
||||
/* Retrieve optional clock */
|
||||
ctx->clk = clk_get(&pdev->dev, NULL);
|
||||
@ -1709,22 +1706,12 @@ static int xgene_phy_probe(struct platform_device *pdev)
|
||||
ctx->phy = devm_phy_create(ctx->dev, NULL, &xgene_phy_ops);
|
||||
if (IS_ERR(ctx->phy)) {
|
||||
dev_dbg(&pdev->dev, "Failed to create PHY\n");
|
||||
rc = PTR_ERR(ctx->phy);
|
||||
goto error;
|
||||
return PTR_ERR(ctx->phy);
|
||||
}
|
||||
phy_set_drvdata(ctx->phy, ctx);
|
||||
|
||||
phy_provider = devm_of_phy_provider_register(ctx->dev,
|
||||
xgene_phy_xlate);
|
||||
if (IS_ERR(phy_provider)) {
|
||||
rc = PTR_ERR(phy_provider);
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
return rc;
|
||||
phy_provider = devm_of_phy_provider_register(ctx->dev, xgene_phy_xlate);
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
}
|
||||
|
||||
static const struct of_device_id xgene_phy_of_match[] = {
|
||||
|
@ -5,6 +5,7 @@
|
||||
# Object files in subdirectories
|
||||
|
||||
obj-$(CONFIG_USB) += core/
|
||||
obj-$(CONFIG_USB_SUPPORT) += phy/
|
||||
|
||||
obj-$(CONFIG_USB_DWC3) += dwc3/
|
||||
obj-$(CONFIG_USB_DWC2) += dwc2/
|
||||
@ -48,7 +49,6 @@ obj-$(CONFIG_USB_MICROTEK) += image/
|
||||
obj-$(CONFIG_USB_SERIAL) += serial/
|
||||
|
||||
obj-$(CONFIG_USB) += misc/
|
||||
obj-$(CONFIG_USB_SUPPORT) += phy/
|
||||
obj-$(CONFIG_EARLY_PRINTK_DBGP) += early/
|
||||
|
||||
obj-$(CONFIG_USB_ATM) += atm/
|
||||
|
@ -952,7 +952,7 @@ static void uea_load_page_e1(struct work_struct *work)
|
||||
int i;
|
||||
|
||||
/* reload firmware when reboot start and it's loaded already */
|
||||
if (ovl == 0 && pageno == 0 && sc->dsp_firm) {
|
||||
if (ovl == 0 && pageno == 0) {
|
||||
release_firmware(sc->dsp_firm);
|
||||
sc->dsp_firm = NULL;
|
||||
}
|
||||
@ -1074,7 +1074,7 @@ static void uea_load_page_e4(struct work_struct *work)
|
||||
uea_dbg(INS_TO_USBDEV(sc), "sending DSP page %u\n", pageno);
|
||||
|
||||
/* reload firmware when reboot start and it's loaded already */
|
||||
if (pageno == 0 && sc->dsp_firm) {
|
||||
if (pageno == 0) {
|
||||
release_firmware(sc->dsp_firm);
|
||||
sc->dsp_firm = NULL;
|
||||
}
|
||||
|
@ -34,7 +34,7 @@
|
||||
|
||||
static __u8 c67x00_hub_des[] = {
|
||||
0x09, /* __u8 bLength; */
|
||||
0x29, /* __u8 bDescriptorType; Hub-descriptor */
|
||||
USB_DT_HUB, /* __u8 bDescriptorType; Hub-descriptor */
|
||||
0x02, /* __u8 bNbrPorts; */
|
||||
0x00, /* __u16 wHubCharacteristics; */
|
||||
0x00, /* (per-port OC, no power switching) */
|
||||
|
@ -10,6 +10,17 @@ config USB_CHIPIDEA
|
||||
|
||||
if USB_CHIPIDEA
|
||||
|
||||
config USB_CHIPIDEA_OF
|
||||
tristate
|
||||
depends on OF
|
||||
default USB_CHIPIDEA
|
||||
|
||||
config USB_CHIPIDEA_PCI
|
||||
tristate
|
||||
depends on PCI
|
||||
depends on NOP_USB_XCEIV
|
||||
default USB_CHIPIDEA
|
||||
|
||||
config USB_CHIPIDEA_UDC
|
||||
bool "ChipIdea device controller"
|
||||
depends on USB_GADGET
|
||||
|
@ -14,11 +14,6 @@ obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_usb2.o
|
||||
obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_msm.o
|
||||
obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_zevio.o
|
||||
|
||||
# PCI doesn't provide stubs, need to check
|
||||
ifneq ($(CONFIG_PCI),)
|
||||
obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_pci.o
|
||||
endif
|
||||
obj-$(CONFIG_USB_CHIPIDEA_PCI) += ci_hdrc_pci.o
|
||||
|
||||
ifneq ($(CONFIG_OF),)
|
||||
obj-$(CONFIG_USB_CHIPIDEA) += usbmisc_imx.o ci_hdrc_imx.o
|
||||
endif
|
||||
obj-$(CONFIG_USB_CHIPIDEA_OF) += usbmisc_imx.o ci_hdrc_imx.o
|
||||
|
@ -15,6 +15,16 @@
|
||||
|
||||
#include <linux/usb/ehci_def.h>
|
||||
|
||||
/*
|
||||
* ID
|
||||
* For 1.x revision, bit24 - bit31 are reserved
|
||||
* For 2.x revision, bit25 - bit28 are 0x2
|
||||
*/
|
||||
#define TAG (0x1F << 16)
|
||||
#define REVISION (0xF << 21)
|
||||
#define VERSION (0xF << 25)
|
||||
#define CIVERSION (0x7 << 29)
|
||||
|
||||
/* HCCPARAMS */
|
||||
#define HCCPARAMS_LEN BIT(17)
|
||||
|
||||
@ -53,6 +63,7 @@
|
||||
#define PORTSC_HSP BIT(9)
|
||||
#define PORTSC_PP BIT(12)
|
||||
#define PORTSC_PTC (0x0FUL << 16)
|
||||
#define PORTSC_WKCN BIT(20)
|
||||
#define PORTSC_PHCD(d) ((d) ? BIT(22) : BIT(23))
|
||||
/* PTS and PTW for non lpm version only */
|
||||
#define PORTSC_PFSC BIT(24)
|
||||
|
@ -29,6 +29,15 @@
|
||||
/******************************************************************************
|
||||
* REGISTERS
|
||||
*****************************************************************************/
|
||||
/* Identification Registers */
|
||||
#define ID_ID 0x0
|
||||
#define ID_HWGENERAL 0x4
|
||||
#define ID_HWHOST 0x8
|
||||
#define ID_HWDEVICE 0xc
|
||||
#define ID_HWTXBUF 0x10
|
||||
#define ID_HWRXBUF 0x14
|
||||
#define ID_SBUSCFG 0x90
|
||||
|
||||
/* register indices */
|
||||
enum ci_hw_regs {
|
||||
CAP_CAPLENGTH,
|
||||
@ -97,6 +106,18 @@ enum ci_role {
|
||||
CI_ROLE_END,
|
||||
};
|
||||
|
||||
enum ci_revision {
|
||||
CI_REVISION_1X = 10, /* Revision 1.x */
|
||||
CI_REVISION_20 = 20, /* Revision 2.0 */
|
||||
CI_REVISION_21, /* Revision 2.1 */
|
||||
CI_REVISION_22, /* Revision 2.2 */
|
||||
CI_REVISION_23, /* Revision 2.3 */
|
||||
CI_REVISION_24, /* Revision 2.4 */
|
||||
CI_REVISION_25, /* Revision 2.5 */
|
||||
CI_REVISION_25_PLUS, /* Revision above than 2.5 */
|
||||
CI_REVISION_UNKNOWN = 99, /* Unknown Revision */
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ci_role_driver - host/gadget role driver
|
||||
* @start: start this role
|
||||
@ -141,7 +162,10 @@ struct hw_bank {
|
||||
* @role: current role
|
||||
* @is_otg: if the device is otg-capable
|
||||
* @fsm: otg finite state machine
|
||||
* @fsm_timer: pointer to timer list of otg fsm
|
||||
* @otg_fsm_hrtimer: hrtimer for otg fsm timers
|
||||
* @hr_timeouts: time out list for active otg fsm timers
|
||||
* @enabled_otg_timer_bits: bits of enabled otg timers
|
||||
* @next_otg_timer: next nearest enabled timer to be expired
|
||||
* @work: work for role changing
|
||||
* @wq: workqueue thread
|
||||
* @qh_pool: allocation pool for queue heads
|
||||
@ -169,6 +193,10 @@ struct hw_bank {
|
||||
* @b_sess_valid_event: indicates there is a vbus event, and handled
|
||||
* at ci_otg_work
|
||||
* @imx28_write_fix: Freescale imx28 needs swp instruction for writing
|
||||
* @supports_runtime_pm: if runtime pm is supported
|
||||
* @in_lpm: if the core in low power mode
|
||||
* @wakeup_int: if wakeup interrupt occur
|
||||
* @rev: The revision number for controller
|
||||
*/
|
||||
struct ci_hdrc {
|
||||
struct device *dev;
|
||||
@ -180,7 +208,10 @@ struct ci_hdrc {
|
||||
bool is_otg;
|
||||
struct usb_otg otg;
|
||||
struct otg_fsm fsm;
|
||||
struct ci_otg_fsm_timer_list *fsm_timer;
|
||||
struct hrtimer otg_fsm_hrtimer;
|
||||
ktime_t hr_timeouts[NUM_OTG_FSM_TIMERS];
|
||||
unsigned enabled_otg_timer_bits;
|
||||
enum otg_fsm_timer next_otg_timer;
|
||||
struct work_struct work;
|
||||
struct workqueue_struct *wq;
|
||||
|
||||
@ -211,6 +242,10 @@ struct ci_hdrc {
|
||||
bool id_event;
|
||||
bool b_sess_valid_event;
|
||||
bool imx28_write_fix;
|
||||
bool supports_runtime_pm;
|
||||
bool in_lpm;
|
||||
bool wakeup_int;
|
||||
enum ci_revision rev;
|
||||
};
|
||||
|
||||
static inline struct ci_role_driver *ci_role(struct ci_hdrc *ci)
|
||||
@ -247,6 +282,36 @@ static inline void ci_role_stop(struct ci_hdrc *ci)
|
||||
ci->roles[role]->stop(ci);
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_read_id_reg: reads from a identification register
|
||||
* @ci: the controller
|
||||
* @offset: offset from the beginning of identification registers region
|
||||
* @mask: bitfield mask
|
||||
*
|
||||
* This function returns register contents
|
||||
*/
|
||||
static inline u32 hw_read_id_reg(struct ci_hdrc *ci, u32 offset, u32 mask)
|
||||
{
|
||||
return ioread32(ci->hw_bank.abs + offset) & mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_write_id_reg: writes to a identification register
|
||||
* @ci: the controller
|
||||
* @offset: offset from the beginning of identification registers region
|
||||
* @mask: bitfield mask
|
||||
* @data: new value
|
||||
*/
|
||||
static inline void hw_write_id_reg(struct ci_hdrc *ci, u32 offset,
|
||||
u32 mask, u32 data)
|
||||
{
|
||||
if (~mask)
|
||||
data = (ioread32(ci->hw_bank.abs + offset) & ~mask)
|
||||
| (data & mask);
|
||||
|
||||
iowrite32(data, ci->hw_bank.abs + offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_read: reads from a hw register
|
||||
* @ci: the controller
|
||||
|
@ -23,22 +23,40 @@
|
||||
#include "ci.h"
|
||||
#include "ci_hdrc_imx.h"
|
||||
|
||||
#define CI_HDRC_IMX_IMX28_WRITE_FIX BIT(0)
|
||||
|
||||
struct ci_hdrc_imx_platform_flag {
|
||||
unsigned int flags;
|
||||
bool runtime_pm;
|
||||
};
|
||||
|
||||
static const struct ci_hdrc_imx_platform_flag imx27_usb_data = {
|
||||
};
|
||||
|
||||
static const struct ci_hdrc_imx_platform_flag imx28_usb_data = {
|
||||
.flags = CI_HDRC_IMX_IMX28_WRITE_FIX,
|
||||
.flags = CI_HDRC_IMX28_WRITE_FIX |
|
||||
CI_HDRC_TURN_VBUS_EARLY_ON,
|
||||
};
|
||||
|
||||
static const struct ci_hdrc_imx_platform_flag imx6q_usb_data = {
|
||||
.flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
|
||||
CI_HDRC_TURN_VBUS_EARLY_ON,
|
||||
};
|
||||
|
||||
static const struct ci_hdrc_imx_platform_flag imx6sl_usb_data = {
|
||||
.flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
|
||||
CI_HDRC_TURN_VBUS_EARLY_ON,
|
||||
};
|
||||
|
||||
static const struct ci_hdrc_imx_platform_flag imx6sx_usb_data = {
|
||||
.flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
|
||||
CI_HDRC_TURN_VBUS_EARLY_ON,
|
||||
};
|
||||
|
||||
static const struct of_device_id ci_hdrc_imx_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx28-usb", .data = &imx28_usb_data},
|
||||
{ .compatible = "fsl,imx27-usb", .data = &imx27_usb_data},
|
||||
{ .compatible = "fsl,imx6q-usb", .data = &imx6q_usb_data},
|
||||
{ .compatible = "fsl,imx6sl-usb", .data = &imx6sl_usb_data},
|
||||
{ .compatible = "fsl,imx6sx-usb", .data = &imx6sl_usb_data},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids);
|
||||
@ -48,6 +66,8 @@ struct ci_hdrc_imx_data {
|
||||
struct platform_device *ci_pdev;
|
||||
struct clk *clk;
|
||||
struct imx_usbmisc_data *usbmisc_data;
|
||||
bool supports_runtime_pm;
|
||||
bool in_lpm;
|
||||
};
|
||||
|
||||
/* Common functions shared by usbmisc drivers */
|
||||
@ -145,21 +165,18 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
pdata.usb_phy = data->phy;
|
||||
|
||||
if (imx_platform_flag->flags & CI_HDRC_IMX_IMX28_WRITE_FIX)
|
||||
pdata.flags |= CI_HDRC_IMX28_WRITE_FIX;
|
||||
pdata.flags |= imx_platform_flag->flags;
|
||||
if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM)
|
||||
data->supports_runtime_pm = true;
|
||||
|
||||
ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
|
||||
if (ret)
|
||||
goto err_clk;
|
||||
|
||||
if (data->usbmisc_data) {
|
||||
ret = imx_usbmisc_init(data->usbmisc_data);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n",
|
||||
ret);
|
||||
goto err_clk;
|
||||
}
|
||||
ret = imx_usbmisc_init(data->usbmisc_data);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", ret);
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
data->ci_pdev = ci_hdrc_add_device(&pdev->dev,
|
||||
@ -173,19 +190,20 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
if (data->usbmisc_data) {
|
||||
ret = imx_usbmisc_init_post(data->usbmisc_data);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "usbmisc post failed, ret=%d\n",
|
||||
ret);
|
||||
goto disable_device;
|
||||
}
|
||||
ret = imx_usbmisc_init_post(data->usbmisc_data);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "usbmisc post failed, ret=%d\n", ret);
|
||||
goto disable_device;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
pm_runtime_no_callbacks(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
if (data->supports_runtime_pm) {
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
}
|
||||
|
||||
device_set_wakeup_capable(&pdev->dev, true);
|
||||
|
||||
return 0;
|
||||
|
||||
@ -200,14 +218,18 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
if (data->supports_runtime_pm) {
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
}
|
||||
ci_hdrc_remove_device(data->ci_pdev);
|
||||
clk_disable_unprepare(data->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
#ifdef CONFIG_PM
|
||||
static int imx_controller_suspend(struct device *dev)
|
||||
{
|
||||
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
|
||||
@ -215,6 +237,7 @@ static int imx_controller_suspend(struct device *dev)
|
||||
dev_dbg(dev, "at %s\n", __func__);
|
||||
|
||||
clk_disable_unprepare(data->clk);
|
||||
data->in_lpm = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -222,25 +245,103 @@ static int imx_controller_suspend(struct device *dev)
|
||||
static int imx_controller_resume(struct device *dev)
|
||||
{
|
||||
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
dev_dbg(dev, "at %s\n", __func__);
|
||||
|
||||
return clk_prepare_enable(data->clk);
|
||||
if (!data->in_lpm) {
|
||||
WARN_ON(1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(data->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data->in_lpm = false;
|
||||
|
||||
ret = imx_usbmisc_set_wakeup(data->usbmisc_data, false);
|
||||
if (ret) {
|
||||
dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret);
|
||||
goto clk_disable;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
clk_disable:
|
||||
clk_disable_unprepare(data->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int ci_hdrc_imx_suspend(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
|
||||
|
||||
if (data->in_lpm)
|
||||
/* The core's suspend doesn't run */
|
||||
return 0;
|
||||
|
||||
if (device_may_wakeup(dev)) {
|
||||
ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true);
|
||||
if (ret) {
|
||||
dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return imx_controller_suspend(dev);
|
||||
}
|
||||
|
||||
static int ci_hdrc_imx_resume(struct device *dev)
|
||||
{
|
||||
return imx_controller_resume(dev);
|
||||
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = imx_controller_resume(dev);
|
||||
if (!ret && data->supports_runtime_pm) {
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static int ci_hdrc_imx_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
if (data->in_lpm) {
|
||||
WARN_ON(1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true);
|
||||
if (ret) {
|
||||
dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return imx_controller_suspend(dev);
|
||||
}
|
||||
|
||||
static int ci_hdrc_imx_runtime_resume(struct device *dev)
|
||||
{
|
||||
return imx_controller_resume(dev);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static const struct dev_pm_ops ci_hdrc_imx_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume)
|
||||
SET_RUNTIME_PM_OPS(ci_hdrc_imx_runtime_suspend,
|
||||
ci_hdrc_imx_runtime_resume, NULL)
|
||||
};
|
||||
static struct platform_driver ci_hdrc_imx_driver = {
|
||||
.probe = ci_hdrc_imx_probe,
|
||||
|
@ -22,5 +22,6 @@ struct imx_usbmisc_data {
|
||||
|
||||
int imx_usbmisc_init(struct imx_usbmisc_data *);
|
||||
int imx_usbmisc_init_post(struct imx_usbmisc_data *);
|
||||
int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *, bool);
|
||||
|
||||
#endif /* __DRIVER_USB_CHIPIDEA_CI_HDRC_IMX_H */
|
||||
|
@ -16,10 +16,16 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/chipidea.h>
|
||||
#include <linux/usb/usb_phy_generic.h>
|
||||
|
||||
/* driver name */
|
||||
#define UDC_DRIVER_NAME "ci_hdrc_pci"
|
||||
|
||||
struct ci_hdrc_pci {
|
||||
struct platform_device *ci;
|
||||
struct platform_device *phy;
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
* PCI block
|
||||
*****************************************************************************/
|
||||
@ -52,7 +58,7 @@ static int ci_hdrc_pci_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
struct ci_hdrc_platform_data *platdata = (void *)id->driver_data;
|
||||
struct platform_device *plat_ci;
|
||||
struct ci_hdrc_pci *ci;
|
||||
struct resource res[3];
|
||||
int retval = 0, nres = 2;
|
||||
|
||||
@ -61,6 +67,10 @@ static int ci_hdrc_pci_probe(struct pci_dev *pdev,
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ci = devm_kzalloc(&pdev->dev, sizeof(*ci), GFP_KERNEL);
|
||||
if (!ci)
|
||||
return -ENOMEM;
|
||||
|
||||
retval = pcim_enable_device(pdev);
|
||||
if (retval)
|
||||
return retval;
|
||||
@ -73,6 +83,11 @@ static int ci_hdrc_pci_probe(struct pci_dev *pdev,
|
||||
pci_set_master(pdev);
|
||||
pci_try_set_mwi(pdev);
|
||||
|
||||
/* register a nop PHY */
|
||||
ci->phy = usb_phy_generic_register();
|
||||
if (!ci->phy)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(res, 0, sizeof(res));
|
||||
res[0].start = pci_resource_start(pdev, 0);
|
||||
res[0].end = pci_resource_end(pdev, 0);
|
||||
@ -80,13 +95,14 @@ static int ci_hdrc_pci_probe(struct pci_dev *pdev,
|
||||
res[1].start = pdev->irq;
|
||||
res[1].flags = IORESOURCE_IRQ;
|
||||
|
||||
plat_ci = ci_hdrc_add_device(&pdev->dev, res, nres, platdata);
|
||||
if (IS_ERR(plat_ci)) {
|
||||
ci->ci = ci_hdrc_add_device(&pdev->dev, res, nres, platdata);
|
||||
if (IS_ERR(ci->ci)) {
|
||||
dev_err(&pdev->dev, "ci_hdrc_add_device failed!\n");
|
||||
return PTR_ERR(plat_ci);
|
||||
usb_phy_generic_unregister(ci->phy);
|
||||
return PTR_ERR(ci->ci);
|
||||
}
|
||||
|
||||
pci_set_drvdata(pdev, plat_ci);
|
||||
pci_set_drvdata(pdev, ci);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -101,9 +117,10 @@ static int ci_hdrc_pci_probe(struct pci_dev *pdev,
|
||||
*/
|
||||
static void ci_hdrc_pci_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct platform_device *plat_ci = pci_get_drvdata(pdev);
|
||||
struct ci_hdrc_pci *ci = pci_get_drvdata(pdev);
|
||||
|
||||
ci_hdrc_remove_device(plat_ci);
|
||||
ci_hdrc_remove_device(ci->ci);
|
||||
usb_phy_generic_unregister(ci->phy);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
static struct ci_hdrc_platform_data ci_hdrc_zevio_platdata = {
|
||||
.name = "ci_hdrc_zevio",
|
||||
.flags = CI_HDRC_REGS_SHARED,
|
||||
.flags = CI_HDRC_REGS_SHARED | CI_HDRC_FORCE_FULLSPEED,
|
||||
.capoffset = DEF_CAPOFFSET,
|
||||
};
|
||||
|
||||
|
@ -137,6 +137,22 @@ static int hw_alloc_regmap(struct ci_hdrc *ci, bool is_lpm)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum ci_revision ci_get_revision(struct ci_hdrc *ci)
|
||||
{
|
||||
int ver = hw_read_id_reg(ci, ID_ID, VERSION) >> __ffs(VERSION);
|
||||
enum ci_revision rev = CI_REVISION_UNKNOWN;
|
||||
|
||||
if (ver == 0x2) {
|
||||
rev = hw_read_id_reg(ci, ID_ID, REVISION)
|
||||
>> __ffs(REVISION);
|
||||
rev += CI_REVISION_20;
|
||||
} else if (ver == 0x0) {
|
||||
rev = CI_REVISION_1X;
|
||||
}
|
||||
|
||||
return rev;
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_read_intr_enable: returns interrupt enable register
|
||||
*
|
||||
@ -251,8 +267,11 @@ static int hw_device_init(struct ci_hdrc *ci, void __iomem *base)
|
||||
/* Clear all interrupts status bits*/
|
||||
hw_write(ci, OP_USBSTS, 0xffffffff, 0xffffffff);
|
||||
|
||||
dev_dbg(ci->dev, "ChipIdea HDRC found, lpm: %d; cap: %p op: %p\n",
|
||||
ci->hw_bank.lpm, ci->hw_bank.cap, ci->hw_bank.op);
|
||||
ci->rev = ci_get_revision(ci);
|
||||
|
||||
dev_dbg(ci->dev,
|
||||
"ChipIdea HDRC found, revision: %d, lpm: %d; cap: %p op: %p\n",
|
||||
ci->rev, ci->hw_bank.lpm, ci->hw_bank.cap, ci->hw_bank.op);
|
||||
|
||||
/* setup lock mode ? */
|
||||
|
||||
@ -491,6 +510,13 @@ static irqreturn_t ci_irq(int irq, void *data)
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
u32 otgsc = 0;
|
||||
|
||||
if (ci->in_lpm) {
|
||||
disable_irq_nosync(irq);
|
||||
ci->wakeup_int = true;
|
||||
pm_runtime_get(ci->dev);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (ci->is_otg) {
|
||||
otgsc = hw_read_otgsc(ci, ~0);
|
||||
if (ci_otg_is_fsm_mode(ci)) {
|
||||
@ -642,8 +668,12 @@ static void ci_get_otg_capable(struct ci_hdrc *ci)
|
||||
ci->is_otg = (hw_read(ci, CAP_DCCPARAMS,
|
||||
DCCPARAMS_DC | DCCPARAMS_HC)
|
||||
== (DCCPARAMS_DC | DCCPARAMS_HC));
|
||||
if (ci->is_otg)
|
||||
if (ci->is_otg) {
|
||||
dev_dbg(ci->dev, "It is OTG capable controller\n");
|
||||
/* Disable and clear all OTG irq */
|
||||
hw_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS,
|
||||
OTGSC_INT_STATUS_BITS);
|
||||
}
|
||||
}
|
||||
|
||||
static int ci_hdrc_probe(struct platform_device *pdev)
|
||||
@ -673,6 +703,8 @@ static int ci_hdrc_probe(struct platform_device *pdev)
|
||||
ci->platdata = dev_get_platdata(dev);
|
||||
ci->imx28_write_fix = !!(ci->platdata->flags &
|
||||
CI_HDRC_IMX28_WRITE_FIX);
|
||||
ci->supports_runtime_pm = !!(ci->platdata->flags &
|
||||
CI_HDRC_SUPPORTS_RUNTIME_PM);
|
||||
|
||||
ret = hw_device_init(ci, base);
|
||||
if (ret < 0) {
|
||||
@ -740,9 +772,6 @@ static int ci_hdrc_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
if (ci->is_otg && ci->roles[CI_ROLE_GADGET]) {
|
||||
/* Disable and clear all OTG irq */
|
||||
hw_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS,
|
||||
OTGSC_INT_STATUS_BITS);
|
||||
ret = ci_hdrc_otg_init(ci);
|
||||
if (ret) {
|
||||
dev_err(dev, "init otg fails, ret = %d\n", ret);
|
||||
@ -769,11 +798,11 @@ static int ci_hdrc_probe(struct platform_device *pdev)
|
||||
: CI_ROLE_GADGET;
|
||||
}
|
||||
|
||||
/* only update vbus status for peripheral */
|
||||
if (ci->role == CI_ROLE_GADGET)
|
||||
ci_handle_vbus_change(ci);
|
||||
|
||||
if (!ci_otg_is_fsm_mode(ci)) {
|
||||
/* only update vbus status for peripheral */
|
||||
if (ci->role == CI_ROLE_GADGET)
|
||||
ci_handle_vbus_change(ci);
|
||||
|
||||
ret = ci_role_start(ci, ci->role);
|
||||
if (ret) {
|
||||
dev_err(dev, "can't start %s role\n",
|
||||
@ -788,9 +817,19 @@ static int ci_hdrc_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
goto stop;
|
||||
|
||||
if (ci->supports_runtime_pm) {
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, 2000);
|
||||
pm_runtime_mark_last_busy(ci->dev);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
}
|
||||
|
||||
if (ci_otg_is_fsm_mode(ci))
|
||||
ci_hdrc_otg_fsm_start(ci);
|
||||
|
||||
device_set_wakeup_capable(&pdev->dev, true);
|
||||
|
||||
ret = dbg_create_files(ci);
|
||||
if (!ret)
|
||||
return 0;
|
||||
@ -807,6 +846,12 @@ static int ci_hdrc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ci_hdrc *ci = platform_get_drvdata(pdev);
|
||||
|
||||
if (ci->supports_runtime_pm) {
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
}
|
||||
|
||||
dbg_remove_files(ci);
|
||||
ci_role_destroy(ci);
|
||||
ci_hdrc_enter_lpm(ci, true);
|
||||
@ -815,13 +860,41 @@ static int ci_hdrc_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
#ifdef CONFIG_PM
|
||||
/* Prepare wakeup by SRP before suspend */
|
||||
static void ci_otg_fsm_suspend_for_srp(struct ci_hdrc *ci)
|
||||
{
|
||||
if ((ci->fsm.otg->state == OTG_STATE_A_IDLE) &&
|
||||
!hw_read_otgsc(ci, OTGSC_ID)) {
|
||||
hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_PP,
|
||||
PORTSC_PP);
|
||||
hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_WKCN,
|
||||
PORTSC_WKCN);
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle SRP when wakeup by data pulse */
|
||||
static void ci_otg_fsm_wakeup_by_srp(struct ci_hdrc *ci)
|
||||
{
|
||||
if ((ci->fsm.otg->state == OTG_STATE_A_IDLE) &&
|
||||
(ci->fsm.a_bus_drop == 1) && (ci->fsm.a_bus_req == 0)) {
|
||||
if (!hw_read_otgsc(ci, OTGSC_ID)) {
|
||||
ci->fsm.a_srp_det = 1;
|
||||
ci->fsm.a_bus_drop = 0;
|
||||
} else {
|
||||
ci->fsm.id = 1;
|
||||
}
|
||||
ci_otg_queue_work(ci);
|
||||
}
|
||||
}
|
||||
|
||||
static void ci_controller_suspend(struct ci_hdrc *ci)
|
||||
{
|
||||
disable_irq(ci->irq);
|
||||
ci_hdrc_enter_lpm(ci, true);
|
||||
|
||||
if (ci->usb_phy)
|
||||
usb_phy_set_suspend(ci->usb_phy, 1);
|
||||
usb_phy_set_suspend(ci->usb_phy, 1);
|
||||
ci->in_lpm = true;
|
||||
enable_irq(ci->irq);
|
||||
}
|
||||
|
||||
static int ci_controller_resume(struct device *dev)
|
||||
@ -830,23 +903,59 @@ static int ci_controller_resume(struct device *dev)
|
||||
|
||||
dev_dbg(dev, "at %s\n", __func__);
|
||||
|
||||
ci_hdrc_enter_lpm(ci, false);
|
||||
if (!ci->in_lpm) {
|
||||
WARN_ON(1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ci_hdrc_enter_lpm(ci, false);
|
||||
if (ci->usb_phy) {
|
||||
usb_phy_set_suspend(ci->usb_phy, 0);
|
||||
usb_phy_set_wakeup(ci->usb_phy, false);
|
||||
hw_wait_phy_stable();
|
||||
}
|
||||
|
||||
ci->in_lpm = false;
|
||||
if (ci->wakeup_int) {
|
||||
ci->wakeup_int = false;
|
||||
pm_runtime_mark_last_busy(ci->dev);
|
||||
pm_runtime_put_autosuspend(ci->dev);
|
||||
enable_irq(ci->irq);
|
||||
if (ci_otg_is_fsm_mode(ci))
|
||||
ci_otg_fsm_wakeup_by_srp(ci);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int ci_suspend(struct device *dev)
|
||||
{
|
||||
struct ci_hdrc *ci = dev_get_drvdata(dev);
|
||||
|
||||
if (ci->wq)
|
||||
flush_workqueue(ci->wq);
|
||||
/*
|
||||
* Controller needs to be active during suspend, otherwise the core
|
||||
* may run resume when the parent is at suspend if other driver's
|
||||
* suspend fails, it occurs before parent's suspend has not started,
|
||||
* but the core suspend has finished.
|
||||
*/
|
||||
if (ci->in_lpm)
|
||||
pm_runtime_resume(dev);
|
||||
|
||||
if (ci->in_lpm) {
|
||||
WARN_ON(1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (device_may_wakeup(dev)) {
|
||||
if (ci_otg_is_fsm_mode(ci))
|
||||
ci_otg_fsm_suspend_for_srp(ci);
|
||||
|
||||
usb_phy_set_wakeup(ci->usb_phy, true);
|
||||
enable_irq_wake(ci->irq);
|
||||
}
|
||||
|
||||
ci_controller_suspend(ci);
|
||||
|
||||
@ -855,13 +964,57 @@ static int ci_suspend(struct device *dev)
|
||||
|
||||
static int ci_resume(struct device *dev)
|
||||
{
|
||||
return ci_controller_resume(dev);
|
||||
struct ci_hdrc *ci = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
disable_irq_wake(ci->irq);
|
||||
|
||||
ret = ci_controller_resume(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (ci->supports_runtime_pm) {
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static int ci_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct ci_hdrc *ci = dev_get_drvdata(dev);
|
||||
|
||||
dev_dbg(dev, "at %s\n", __func__);
|
||||
|
||||
if (ci->in_lpm) {
|
||||
WARN_ON(1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ci_otg_is_fsm_mode(ci))
|
||||
ci_otg_fsm_suspend_for_srp(ci);
|
||||
|
||||
usb_phy_set_wakeup(ci->usb_phy, true);
|
||||
ci_controller_suspend(ci);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ci_runtime_resume(struct device *dev)
|
||||
{
|
||||
return ci_controller_resume(dev);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
static const struct dev_pm_ops ci_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(ci_suspend, ci_resume)
|
||||
SET_RUNTIME_PM_OPS(ci_runtime_suspend, ci_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver ci_hdrc_driver = {
|
||||
.probe = ci_hdrc_probe,
|
||||
.remove = ci_hdrc_remove,
|
||||
|
@ -336,8 +336,8 @@ static int ci_registers_show(struct seq_file *s, void *unused)
|
||||
struct ci_hdrc *ci = s->private;
|
||||
u32 tmp_reg;
|
||||
|
||||
if (!ci)
|
||||
return 0;
|
||||
if (!ci || ci->in_lpm)
|
||||
return -EPERM;
|
||||
|
||||
/* ------ Registers ----- */
|
||||
tmp_reg = hw_read_intr_enable(ci);
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "host.h"
|
||||
|
||||
static struct hc_driver __read_mostly ci_ehci_hc_driver;
|
||||
static int (*orig_bus_suspend)(struct usb_hcd *hcd);
|
||||
|
||||
struct ehci_ci_priv {
|
||||
struct regulator *reg_vbus;
|
||||
@ -43,11 +44,10 @@ static int ehci_ci_portpower(struct usb_hcd *hcd, int portnum, bool enable)
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
struct ehci_ci_priv *priv = (struct ehci_ci_priv *)ehci->priv;
|
||||
struct device *dev = hcd->self.controller;
|
||||
struct ci_hdrc *ci = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
int port = HCS_N_PORTS(ehci->hcs_params);
|
||||
|
||||
if (priv->reg_vbus && !ci_otg_is_fsm_mode(ci)) {
|
||||
if (priv->reg_vbus) {
|
||||
if (port > 1) {
|
||||
dev_warn(dev,
|
||||
"Not support multi-port regulator control\n");
|
||||
@ -113,12 +113,23 @@ static int host_start(struct ci_hdrc *ci)
|
||||
priv = (struct ehci_ci_priv *)ehci->priv;
|
||||
priv->reg_vbus = NULL;
|
||||
|
||||
if (ci->platdata->reg_vbus)
|
||||
priv->reg_vbus = ci->platdata->reg_vbus;
|
||||
if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci)) {
|
||||
if (ci->platdata->flags & CI_HDRC_TURN_VBUS_EARLY_ON) {
|
||||
ret = regulator_enable(ci->platdata->reg_vbus);
|
||||
if (ret) {
|
||||
dev_err(ci->dev,
|
||||
"Failed to enable vbus regulator, ret=%d\n",
|
||||
ret);
|
||||
goto put_hcd;
|
||||
}
|
||||
} else {
|
||||
priv->reg_vbus = ci->platdata->reg_vbus;
|
||||
}
|
||||
}
|
||||
|
||||
ret = usb_add_hcd(hcd, 0, 0);
|
||||
if (ret) {
|
||||
goto put_hcd;
|
||||
goto disable_reg;
|
||||
} else {
|
||||
struct usb_otg *otg = &ci->otg;
|
||||
|
||||
@ -133,8 +144,15 @@ static int host_start(struct ci_hdrc *ci)
|
||||
if (ci->platdata->flags & CI_HDRC_DISABLE_STREAMING)
|
||||
hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS);
|
||||
|
||||
if (ci->platdata->flags & CI_HDRC_FORCE_FULLSPEED)
|
||||
hw_write(ci, OP_PORTSC, PORTSC_PFSC, PORTSC_PFSC);
|
||||
|
||||
return ret;
|
||||
|
||||
disable_reg:
|
||||
if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci) &&
|
||||
(ci->platdata->flags & CI_HDRC_TURN_VBUS_EARLY_ON))
|
||||
regulator_disable(ci->platdata->reg_vbus);
|
||||
put_hcd:
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
@ -148,6 +166,9 @@ static void host_stop(struct ci_hdrc *ci)
|
||||
if (hcd) {
|
||||
usb_remove_hcd(hcd);
|
||||
usb_put_hcd(hcd);
|
||||
if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci) &&
|
||||
(ci->platdata->flags & CI_HDRC_TURN_VBUS_EARLY_ON))
|
||||
regulator_disable(ci->platdata->reg_vbus);
|
||||
}
|
||||
}
|
||||
|
||||
@ -158,6 +179,47 @@ void ci_hdrc_host_destroy(struct ci_hdrc *ci)
|
||||
host_stop(ci);
|
||||
}
|
||||
|
||||
static int ci_ehci_bus_suspend(struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
int port;
|
||||
u32 tmp;
|
||||
|
||||
int ret = orig_bus_suspend(hcd);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
port = HCS_N_PORTS(ehci->hcs_params);
|
||||
while (port--) {
|
||||
u32 __iomem *reg = &ehci->regs->port_status[port];
|
||||
u32 portsc = ehci_readl(ehci, reg);
|
||||
|
||||
if (portsc & PORT_CONNECT) {
|
||||
/*
|
||||
* For chipidea, the resume signal will be ended
|
||||
* automatically, so for remote wakeup case, the
|
||||
* usbcmd.rs may not be set before the resume has
|
||||
* ended if other resume paths consumes too much
|
||||
* time (~24ms), in that case, the SOF will not
|
||||
* send out within 3ms after resume ends, then the
|
||||
* high speed device will enter full speed mode.
|
||||
*/
|
||||
|
||||
tmp = ehci_readl(ehci, &ehci->regs->command);
|
||||
tmp |= CMD_RUN;
|
||||
ehci_writel(ehci, tmp, &ehci->regs->command);
|
||||
/*
|
||||
* It needs a short delay between set RS bit and PHCD.
|
||||
*/
|
||||
usleep_range(150, 200);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ci_hdrc_host_init(struct ci_hdrc *ci)
|
||||
{
|
||||
struct ci_role_driver *rdrv;
|
||||
@ -176,6 +238,8 @@ int ci_hdrc_host_init(struct ci_hdrc *ci)
|
||||
ci->roles[CI_ROLE_HOST] = rdrv;
|
||||
|
||||
ehci_init_driver(&ci_ehci_hc_driver, &ehci_ci_overrides);
|
||||
orig_bus_suspend = ci_ehci_hc_driver.bus_suspend;
|
||||
ci_ehci_hc_driver.bus_suspend = ci_ehci_bus_suspend;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -96,6 +96,7 @@ static void ci_otg_work(struct work_struct *work)
|
||||
return;
|
||||
}
|
||||
|
||||
pm_runtime_get_sync(ci->dev);
|
||||
if (ci->id_event) {
|
||||
ci->id_event = false;
|
||||
ci_handle_id_switch(ci);
|
||||
@ -104,6 +105,7 @@ static void ci_otg_work(struct work_struct *work)
|
||||
ci_handle_vbus_change(ci);
|
||||
} else
|
||||
dev_err(ci->dev, "unexpected event occurs at %s\n", __func__);
|
||||
pm_runtime_put_sync(ci->dev);
|
||||
|
||||
enable_irq(ci->irq);
|
||||
}
|
||||
|
@ -30,22 +30,6 @@
|
||||
#include "otg.h"
|
||||
#include "otg_fsm.h"
|
||||
|
||||
static struct ci_otg_fsm_timer *otg_timer_initializer
|
||||
(struct ci_hdrc *ci, void (*function)(void *, unsigned long),
|
||||
unsigned long expires, unsigned long data)
|
||||
{
|
||||
struct ci_otg_fsm_timer *timer;
|
||||
|
||||
timer = devm_kzalloc(ci->dev, sizeof(struct ci_otg_fsm_timer),
|
||||
GFP_KERNEL);
|
||||
if (!timer)
|
||||
return NULL;
|
||||
timer->function = function;
|
||||
timer->expires = expires;
|
||||
timer->data = data;
|
||||
return timer;
|
||||
}
|
||||
|
||||
/* Add for otg: interact with user space app */
|
||||
static ssize_t
|
||||
get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
@ -203,230 +187,228 @@ static struct attribute_group inputs_attr_group = {
|
||||
.attrs = inputs_attrs,
|
||||
};
|
||||
|
||||
/*
|
||||
* Keep this list in the same order as timers indexed
|
||||
* by enum otg_fsm_timer in include/linux/usb/otg-fsm.h
|
||||
*/
|
||||
static unsigned otg_timer_ms[] = {
|
||||
TA_WAIT_VRISE,
|
||||
TA_WAIT_VFALL,
|
||||
TA_WAIT_BCON,
|
||||
TA_AIDL_BDIS,
|
||||
TB_ASE0_BRST,
|
||||
TA_BIDL_ADIS,
|
||||
TB_SE0_SRP,
|
||||
TB_SRP_FAIL,
|
||||
0,
|
||||
TB_DATA_PLS,
|
||||
TB_SSEND_SRP,
|
||||
};
|
||||
|
||||
/*
|
||||
* Add timer to active timer list
|
||||
*/
|
||||
static void ci_otg_add_timer(struct ci_hdrc *ci, enum ci_otg_fsm_timer_index t)
|
||||
static void ci_otg_add_timer(struct ci_hdrc *ci, enum otg_fsm_timer t)
|
||||
{
|
||||
struct ci_otg_fsm_timer *tmp_timer;
|
||||
struct ci_otg_fsm_timer *timer = ci->fsm_timer->timer_list[t];
|
||||
struct list_head *active_timers = &ci->fsm_timer->active_timers;
|
||||
unsigned long flags, timer_sec, timer_nsec;
|
||||
|
||||
if (t >= NUM_CI_OTG_FSM_TIMERS)
|
||||
if (t >= NUM_OTG_FSM_TIMERS)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Check if the timer is already in the active list,
|
||||
* if so update timer count
|
||||
*/
|
||||
list_for_each_entry(tmp_timer, active_timers, list)
|
||||
if (tmp_timer == timer) {
|
||||
timer->count = timer->expires;
|
||||
return;
|
||||
}
|
||||
|
||||
timer->count = timer->expires;
|
||||
list_add_tail(&timer->list, active_timers);
|
||||
|
||||
/* Enable 1ms irq */
|
||||
if (!(hw_read_otgsc(ci, OTGSC_1MSIE)))
|
||||
hw_write_otgsc(ci, OTGSC_1MSIE, OTGSC_1MSIE);
|
||||
spin_lock_irqsave(&ci->lock, flags);
|
||||
timer_sec = otg_timer_ms[t] / MSEC_PER_SEC;
|
||||
timer_nsec = (otg_timer_ms[t] % MSEC_PER_SEC) * NSEC_PER_MSEC;
|
||||
ci->hr_timeouts[t] = ktime_add(ktime_get(),
|
||||
ktime_set(timer_sec, timer_nsec));
|
||||
ci->enabled_otg_timer_bits |= (1 << t);
|
||||
if ((ci->next_otg_timer == NUM_OTG_FSM_TIMERS) ||
|
||||
(ci->hr_timeouts[ci->next_otg_timer].tv64 >
|
||||
ci->hr_timeouts[t].tv64)) {
|
||||
ci->next_otg_timer = t;
|
||||
hrtimer_start_range_ns(&ci->otg_fsm_hrtimer,
|
||||
ci->hr_timeouts[t], NSEC_PER_MSEC,
|
||||
HRTIMER_MODE_ABS);
|
||||
}
|
||||
spin_unlock_irqrestore(&ci->lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove timer from active timer list
|
||||
*/
|
||||
static void ci_otg_del_timer(struct ci_hdrc *ci, enum ci_otg_fsm_timer_index t)
|
||||
static void ci_otg_del_timer(struct ci_hdrc *ci, enum otg_fsm_timer t)
|
||||
{
|
||||
struct ci_otg_fsm_timer *tmp_timer, *del_tmp;
|
||||
struct ci_otg_fsm_timer *timer = ci->fsm_timer->timer_list[t];
|
||||
struct list_head *active_timers = &ci->fsm_timer->active_timers;
|
||||
unsigned long flags, enabled_timer_bits;
|
||||
enum otg_fsm_timer cur_timer, next_timer = NUM_OTG_FSM_TIMERS;
|
||||
|
||||
if (t >= NUM_CI_OTG_FSM_TIMERS)
|
||||
if ((t >= NUM_OTG_FSM_TIMERS) ||
|
||||
!(ci->enabled_otg_timer_bits & (1 << t)))
|
||||
return;
|
||||
|
||||
list_for_each_entry_safe(tmp_timer, del_tmp, active_timers, list)
|
||||
if (tmp_timer == timer)
|
||||
list_del(&timer->list);
|
||||
|
||||
/* Disable 1ms irq if there is no any active timer */
|
||||
if (list_empty(active_timers))
|
||||
hw_write_otgsc(ci, OTGSC_1MSIE, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reduce timer count by 1, and find timeout conditions.
|
||||
* Called by otg 1ms timer interrupt
|
||||
*/
|
||||
static inline int ci_otg_tick_timer(struct ci_hdrc *ci)
|
||||
{
|
||||
struct ci_otg_fsm_timer *tmp_timer, *del_tmp;
|
||||
struct list_head *active_timers = &ci->fsm_timer->active_timers;
|
||||
int expired = 0;
|
||||
|
||||
list_for_each_entry_safe(tmp_timer, del_tmp, active_timers, list) {
|
||||
tmp_timer->count--;
|
||||
/* check if timer expires */
|
||||
if (!tmp_timer->count) {
|
||||
list_del(&tmp_timer->list);
|
||||
tmp_timer->function(ci, tmp_timer->data);
|
||||
expired = 1;
|
||||
spin_lock_irqsave(&ci->lock, flags);
|
||||
ci->enabled_otg_timer_bits &= ~(1 << t);
|
||||
if (ci->next_otg_timer == t) {
|
||||
if (ci->enabled_otg_timer_bits == 0) {
|
||||
/* No enabled timers after delete it */
|
||||
hrtimer_cancel(&ci->otg_fsm_hrtimer);
|
||||
ci->next_otg_timer = NUM_OTG_FSM_TIMERS;
|
||||
} else {
|
||||
/* Find the next timer */
|
||||
enabled_timer_bits = ci->enabled_otg_timer_bits;
|
||||
for_each_set_bit(cur_timer, &enabled_timer_bits,
|
||||
NUM_OTG_FSM_TIMERS) {
|
||||
if ((next_timer == NUM_OTG_FSM_TIMERS) ||
|
||||
(ci->hr_timeouts[next_timer].tv64 <
|
||||
ci->hr_timeouts[cur_timer].tv64))
|
||||
next_timer = cur_timer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* disable 1ms irq if there is no any timer active */
|
||||
if ((expired == 1) && list_empty(active_timers))
|
||||
hw_write_otgsc(ci, OTGSC_1MSIE, 0);
|
||||
|
||||
return expired;
|
||||
}
|
||||
|
||||
/* The timeout callback function to set time out bit */
|
||||
static void set_tmout(void *ptr, unsigned long indicator)
|
||||
{
|
||||
*(int *)indicator = 1;
|
||||
}
|
||||
|
||||
static void set_tmout_and_fsm(void *ptr, unsigned long indicator)
|
||||
{
|
||||
struct ci_hdrc *ci = (struct ci_hdrc *)ptr;
|
||||
|
||||
set_tmout(ci, indicator);
|
||||
|
||||
ci_otg_queue_work(ci);
|
||||
}
|
||||
|
||||
static void a_wait_vfall_tmout_func(void *ptr, unsigned long indicator)
|
||||
{
|
||||
struct ci_hdrc *ci = (struct ci_hdrc *)ptr;
|
||||
|
||||
set_tmout(ci, indicator);
|
||||
/* Disable port power */
|
||||
hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_PP, 0);
|
||||
/* Clear existing DP irq */
|
||||
hw_write_otgsc(ci, OTGSC_DPIS, OTGSC_DPIS);
|
||||
/* Enable data pulse irq */
|
||||
hw_write_otgsc(ci, OTGSC_DPIE, OTGSC_DPIE);
|
||||
ci_otg_queue_work(ci);
|
||||
}
|
||||
|
||||
static void b_ase0_brst_tmout_func(void *ptr, unsigned long indicator)
|
||||
{
|
||||
struct ci_hdrc *ci = (struct ci_hdrc *)ptr;
|
||||
|
||||
set_tmout(ci, indicator);
|
||||
if (!hw_read_otgsc(ci, OTGSC_BSV))
|
||||
ci->fsm.b_sess_vld = 0;
|
||||
|
||||
ci_otg_queue_work(ci);
|
||||
}
|
||||
|
||||
static void b_ssend_srp_tmout_func(void *ptr, unsigned long indicator)
|
||||
{
|
||||
struct ci_hdrc *ci = (struct ci_hdrc *)ptr;
|
||||
|
||||
set_tmout(ci, indicator);
|
||||
|
||||
/* only vbus fall below B_sess_vld in b_idle state */
|
||||
if (ci->fsm.otg->state == OTG_STATE_B_IDLE)
|
||||
ci_otg_queue_work(ci);
|
||||
}
|
||||
|
||||
static void b_sess_vld_tmout_func(void *ptr, unsigned long indicator)
|
||||
{
|
||||
struct ci_hdrc *ci = (struct ci_hdrc *)ptr;
|
||||
|
||||
/* Check if A detached */
|
||||
if (!(hw_read_otgsc(ci, OTGSC_BSV))) {
|
||||
ci->fsm.b_sess_vld = 0;
|
||||
ci_otg_add_timer(ci, B_SSEND_SRP);
|
||||
ci_otg_queue_work(ci);
|
||||
if (next_timer != NUM_OTG_FSM_TIMERS) {
|
||||
ci->next_otg_timer = next_timer;
|
||||
hrtimer_start_range_ns(&ci->otg_fsm_hrtimer,
|
||||
ci->hr_timeouts[next_timer], NSEC_PER_MSEC,
|
||||
HRTIMER_MODE_ABS);
|
||||
}
|
||||
spin_unlock_irqrestore(&ci->lock, flags);
|
||||
}
|
||||
|
||||
static void b_data_pulse_end(void *ptr, unsigned long indicator)
|
||||
/* OTG FSM timer handlers */
|
||||
static int a_wait_vrise_tmout(struct ci_hdrc *ci)
|
||||
{
|
||||
struct ci_hdrc *ci = (struct ci_hdrc *)ptr;
|
||||
ci->fsm.a_wait_vrise_tmout = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int a_wait_vfall_tmout(struct ci_hdrc *ci)
|
||||
{
|
||||
ci->fsm.a_wait_vfall_tmout = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int a_wait_bcon_tmout(struct ci_hdrc *ci)
|
||||
{
|
||||
ci->fsm.a_wait_bcon_tmout = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int a_aidl_bdis_tmout(struct ci_hdrc *ci)
|
||||
{
|
||||
ci->fsm.a_aidl_bdis_tmout = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int b_ase0_brst_tmout(struct ci_hdrc *ci)
|
||||
{
|
||||
ci->fsm.b_ase0_brst_tmout = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int a_bidl_adis_tmout(struct ci_hdrc *ci)
|
||||
{
|
||||
ci->fsm.a_bidl_adis_tmout = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int b_se0_srp_tmout(struct ci_hdrc *ci)
|
||||
{
|
||||
ci->fsm.b_se0_srp = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int b_srp_fail_tmout(struct ci_hdrc *ci)
|
||||
{
|
||||
ci->fsm.b_srp_done = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int b_data_pls_tmout(struct ci_hdrc *ci)
|
||||
{
|
||||
ci->fsm.b_srp_done = 1;
|
||||
ci->fsm.b_bus_req = 0;
|
||||
if (ci->fsm.power_up)
|
||||
ci->fsm.power_up = 0;
|
||||
|
||||
hw_write_otgsc(ci, OTGSC_HABA, 0);
|
||||
pm_runtime_put(ci->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ci_otg_queue_work(ci);
|
||||
static int b_ssend_srp_tmout(struct ci_hdrc *ci)
|
||||
{
|
||||
ci->fsm.b_ssend_srp = 1;
|
||||
/* only vbus fall below B_sess_vld in b_idle state */
|
||||
if (ci->fsm.otg->state == OTG_STATE_B_IDLE)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Keep this list in the same order as timers indexed
|
||||
* by enum otg_fsm_timer in include/linux/usb/otg-fsm.h
|
||||
*/
|
||||
static int (*otg_timer_handlers[])(struct ci_hdrc *) = {
|
||||
a_wait_vrise_tmout, /* A_WAIT_VRISE */
|
||||
a_wait_vfall_tmout, /* A_WAIT_VFALL */
|
||||
a_wait_bcon_tmout, /* A_WAIT_BCON */
|
||||
a_aidl_bdis_tmout, /* A_AIDL_BDIS */
|
||||
b_ase0_brst_tmout, /* B_ASE0_BRST */
|
||||
a_bidl_adis_tmout, /* A_BIDL_ADIS */
|
||||
b_se0_srp_tmout, /* B_SE0_SRP */
|
||||
b_srp_fail_tmout, /* B_SRP_FAIL */
|
||||
NULL, /* A_WAIT_ENUM */
|
||||
b_data_pls_tmout, /* B_DATA_PLS */
|
||||
b_ssend_srp_tmout, /* B_SSEND_SRP */
|
||||
};
|
||||
|
||||
/*
|
||||
* Enable the next nearest enabled timer if have
|
||||
*/
|
||||
static enum hrtimer_restart ci_otg_hrtimer_func(struct hrtimer *t)
|
||||
{
|
||||
struct ci_hdrc *ci = container_of(t, struct ci_hdrc, otg_fsm_hrtimer);
|
||||
ktime_t now, *timeout;
|
||||
unsigned long enabled_timer_bits;
|
||||
unsigned long flags;
|
||||
enum otg_fsm_timer cur_timer, next_timer = NUM_OTG_FSM_TIMERS;
|
||||
int ret = -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&ci->lock, flags);
|
||||
enabled_timer_bits = ci->enabled_otg_timer_bits;
|
||||
ci->next_otg_timer = NUM_OTG_FSM_TIMERS;
|
||||
|
||||
now = ktime_get();
|
||||
for_each_set_bit(cur_timer, &enabled_timer_bits, NUM_OTG_FSM_TIMERS) {
|
||||
if (now.tv64 >= ci->hr_timeouts[cur_timer].tv64) {
|
||||
ci->enabled_otg_timer_bits &= ~(1 << cur_timer);
|
||||
if (otg_timer_handlers[cur_timer])
|
||||
ret = otg_timer_handlers[cur_timer](ci);
|
||||
} else {
|
||||
if ((next_timer == NUM_OTG_FSM_TIMERS) ||
|
||||
(ci->hr_timeouts[cur_timer].tv64 <
|
||||
ci->hr_timeouts[next_timer].tv64))
|
||||
next_timer = cur_timer;
|
||||
}
|
||||
}
|
||||
/* Enable the next nearest timer */
|
||||
if (next_timer < NUM_OTG_FSM_TIMERS) {
|
||||
timeout = &ci->hr_timeouts[next_timer];
|
||||
hrtimer_start_range_ns(&ci->otg_fsm_hrtimer, *timeout,
|
||||
NSEC_PER_MSEC, HRTIMER_MODE_ABS);
|
||||
ci->next_otg_timer = next_timer;
|
||||
}
|
||||
spin_unlock_irqrestore(&ci->lock, flags);
|
||||
|
||||
if (!ret)
|
||||
ci_otg_queue_work(ci);
|
||||
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
/* Initialize timers */
|
||||
static int ci_otg_init_timers(struct ci_hdrc *ci)
|
||||
{
|
||||
struct otg_fsm *fsm = &ci->fsm;
|
||||
|
||||
/* FSM used timers */
|
||||
ci->fsm_timer->timer_list[A_WAIT_VRISE] =
|
||||
otg_timer_initializer(ci, &set_tmout_and_fsm, TA_WAIT_VRISE,
|
||||
(unsigned long)&fsm->a_wait_vrise_tmout);
|
||||
if (ci->fsm_timer->timer_list[A_WAIT_VRISE] == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ci->fsm_timer->timer_list[A_WAIT_VFALL] =
|
||||
otg_timer_initializer(ci, &a_wait_vfall_tmout_func,
|
||||
TA_WAIT_VFALL, (unsigned long)&fsm->a_wait_vfall_tmout);
|
||||
if (ci->fsm_timer->timer_list[A_WAIT_VFALL] == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ci->fsm_timer->timer_list[A_WAIT_BCON] =
|
||||
otg_timer_initializer(ci, &set_tmout_and_fsm, TA_WAIT_BCON,
|
||||
(unsigned long)&fsm->a_wait_bcon_tmout);
|
||||
if (ci->fsm_timer->timer_list[A_WAIT_BCON] == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ci->fsm_timer->timer_list[A_AIDL_BDIS] =
|
||||
otg_timer_initializer(ci, &set_tmout_and_fsm, TA_AIDL_BDIS,
|
||||
(unsigned long)&fsm->a_aidl_bdis_tmout);
|
||||
if (ci->fsm_timer->timer_list[A_AIDL_BDIS] == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ci->fsm_timer->timer_list[A_BIDL_ADIS] =
|
||||
otg_timer_initializer(ci, &set_tmout_and_fsm, TA_BIDL_ADIS,
|
||||
(unsigned long)&fsm->a_bidl_adis_tmout);
|
||||
if (ci->fsm_timer->timer_list[A_BIDL_ADIS] == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ci->fsm_timer->timer_list[B_ASE0_BRST] =
|
||||
otg_timer_initializer(ci, &b_ase0_brst_tmout_func, TB_ASE0_BRST,
|
||||
(unsigned long)&fsm->b_ase0_brst_tmout);
|
||||
if (ci->fsm_timer->timer_list[B_ASE0_BRST] == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ci->fsm_timer->timer_list[B_SE0_SRP] =
|
||||
otg_timer_initializer(ci, &set_tmout_and_fsm, TB_SE0_SRP,
|
||||
(unsigned long)&fsm->b_se0_srp);
|
||||
if (ci->fsm_timer->timer_list[B_SE0_SRP] == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ci->fsm_timer->timer_list[B_SSEND_SRP] =
|
||||
otg_timer_initializer(ci, &b_ssend_srp_tmout_func, TB_SSEND_SRP,
|
||||
(unsigned long)&fsm->b_ssend_srp);
|
||||
if (ci->fsm_timer->timer_list[B_SSEND_SRP] == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ci->fsm_timer->timer_list[B_SRP_FAIL] =
|
||||
otg_timer_initializer(ci, &set_tmout, TB_SRP_FAIL,
|
||||
(unsigned long)&fsm->b_srp_done);
|
||||
if (ci->fsm_timer->timer_list[B_SRP_FAIL] == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ci->fsm_timer->timer_list[B_DATA_PLS] =
|
||||
otg_timer_initializer(ci, &b_data_pulse_end, TB_DATA_PLS, 0);
|
||||
if (ci->fsm_timer->timer_list[B_DATA_PLS] == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ci->fsm_timer->timer_list[B_SESS_VLD] = otg_timer_initializer(ci,
|
||||
&b_sess_vld_tmout_func, TB_SESS_VLD, 0);
|
||||
if (ci->fsm_timer->timer_list[B_SESS_VLD] == NULL)
|
||||
return -ENOMEM;
|
||||
hrtimer_init(&ci->otg_fsm_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
|
||||
ci->otg_fsm_hrtimer.function = ci_otg_hrtimer_func;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -530,6 +512,7 @@ static void ci_otg_start_pulse(struct otg_fsm *fsm)
|
||||
/* Hardware Assistant Data pulse */
|
||||
hw_write_otgsc(ci, OTGSC_HADP, OTGSC_HADP);
|
||||
|
||||
pm_runtime_get(ci->dev);
|
||||
ci_otg_add_timer(ci, B_DATA_PLS);
|
||||
}
|
||||
|
||||
@ -585,6 +568,7 @@ int ci_otg_fsm_work(struct ci_hdrc *ci)
|
||||
ci->fsm.otg->state < OTG_STATE_A_IDLE)
|
||||
return 0;
|
||||
|
||||
pm_runtime_get_sync(ci->dev);
|
||||
if (otg_statemachine(&ci->fsm)) {
|
||||
if (ci->fsm.otg->state == OTG_STATE_A_IDLE) {
|
||||
/*
|
||||
@ -596,8 +580,15 @@ int ci_otg_fsm_work(struct ci_hdrc *ci)
|
||||
* a_idle to a_wait_vrise when power up
|
||||
*/
|
||||
if ((ci->fsm.id) || (ci->id_event) ||
|
||||
(ci->fsm.power_up))
|
||||
(ci->fsm.power_up)) {
|
||||
ci_otg_queue_work(ci);
|
||||
} else {
|
||||
/* Enable data pulse irq */
|
||||
hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS |
|
||||
PORTSC_PP, 0);
|
||||
hw_write_otgsc(ci, OTGSC_DPIS, OTGSC_DPIS);
|
||||
hw_write_otgsc(ci, OTGSC_DPIE, OTGSC_DPIE);
|
||||
}
|
||||
if (ci->id_event)
|
||||
ci->id_event = false;
|
||||
} else if (ci->fsm.otg->state == OTG_STATE_B_IDLE) {
|
||||
@ -609,8 +600,13 @@ int ci_otg_fsm_work(struct ci_hdrc *ci)
|
||||
*/
|
||||
ci_otg_queue_work(ci);
|
||||
}
|
||||
} else if (ci->fsm.otg->state == OTG_STATE_A_HOST) {
|
||||
pm_runtime_mark_last_busy(ci->dev);
|
||||
pm_runtime_put_autosuspend(ci->dev);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
pm_runtime_put_sync(ci->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -655,7 +651,6 @@ static void ci_otg_fsm_event(struct ci_hdrc *ci)
|
||||
fsm->a_conn = 0;
|
||||
fsm->b_bus_req = 0;
|
||||
ci_otg_queue_work(ci);
|
||||
ci_otg_add_timer(ci, B_SESS_VLD);
|
||||
}
|
||||
break;
|
||||
case OTG_STATE_A_PERIPHERAL:
|
||||
@ -725,11 +720,7 @@ irqreturn_t ci_otg_fsm_irq(struct ci_hdrc *ci)
|
||||
fsm->id = (otgsc & OTGSC_ID) ? 1 : 0;
|
||||
|
||||
if (otg_int_src) {
|
||||
if (otg_int_src & OTGSC_1MSIS) {
|
||||
hw_write_otgsc(ci, OTGSC_1MSIS, OTGSC_1MSIS);
|
||||
retval = ci_otg_tick_timer(ci);
|
||||
return IRQ_HANDLED;
|
||||
} else if (otg_int_src & OTGSC_DPIS) {
|
||||
if (otg_int_src & OTGSC_DPIS) {
|
||||
hw_write_otgsc(ci, OTGSC_DPIS, OTGSC_DPIS);
|
||||
fsm->a_srp_det = 1;
|
||||
fsm->a_bus_drop = 0;
|
||||
@ -793,17 +784,13 @@ int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci)
|
||||
|
||||
mutex_init(&ci->fsm.lock);
|
||||
|
||||
ci->fsm_timer = devm_kzalloc(ci->dev,
|
||||
sizeof(struct ci_otg_fsm_timer_list), GFP_KERNEL);
|
||||
if (!ci->fsm_timer)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&ci->fsm_timer->active_timers);
|
||||
retval = ci_otg_init_timers(ci);
|
||||
if (retval) {
|
||||
dev_err(ci->dev, "Couldn't init OTG timers\n");
|
||||
return retval;
|
||||
}
|
||||
ci->enabled_otg_timer_bits = 0;
|
||||
ci->next_otg_timer = NUM_OTG_FSM_TIMERS;
|
||||
|
||||
retval = sysfs_create_group(&ci->dev->kobj, &inputs_attr_group);
|
||||
if (retval < 0) {
|
||||
|
@ -62,33 +62,6 @@
|
||||
/* SSEND time before SRP */
|
||||
#define TB_SSEND_SRP (1500) /* minimum 1.5 sec, section:5.1.2 */
|
||||
|
||||
#define TB_SESS_VLD (1000)
|
||||
|
||||
enum ci_otg_fsm_timer_index {
|
||||
/*
|
||||
* CI specific timers, start from the end
|
||||
* of standard and auxiliary OTG timers
|
||||
*/
|
||||
B_DATA_PLS = NUM_OTG_FSM_TIMERS,
|
||||
B_SSEND_SRP,
|
||||
B_SESS_VLD,
|
||||
|
||||
NUM_CI_OTG_FSM_TIMERS,
|
||||
};
|
||||
|
||||
struct ci_otg_fsm_timer {
|
||||
unsigned long expires; /* Number of count increase to timeout */
|
||||
unsigned long count; /* Tick counter */
|
||||
void (*function)(void *, unsigned long); /* Timeout function */
|
||||
unsigned long data; /* Data passed to function */
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct ci_otg_fsm_timer_list {
|
||||
struct ci_otg_fsm_timer *timer_list[NUM_CI_OTG_FSM_TIMERS];
|
||||
struct list_head active_timers;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_USB_OTG_FSM
|
||||
|
||||
int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci);
|
||||
|
@ -86,10 +86,8 @@ static int hw_device_state(struct ci_hdrc *ci, u32 dma)
|
||||
/* interrupt, error, port change, reset, sleep/suspend */
|
||||
hw_write(ci, OP_USBINTR, ~0,
|
||||
USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI);
|
||||
hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);
|
||||
} else {
|
||||
hw_write(ci, OP_USBINTR, ~0, 0);
|
||||
hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -522,6 +520,20 @@ static void free_pending_td(struct ci_hw_ep *hwep)
|
||||
kfree(pending);
|
||||
}
|
||||
|
||||
static int reprime_dtd(struct ci_hdrc *ci, struct ci_hw_ep *hwep,
|
||||
struct td_node *node)
|
||||
{
|
||||
hwep->qh.ptr->td.next = node->dma;
|
||||
hwep->qh.ptr->td.token &=
|
||||
cpu_to_le32(~(TD_STATUS_HALTED | TD_STATUS_ACTIVE));
|
||||
|
||||
/* Synchronize before ep prime */
|
||||
wmb();
|
||||
|
||||
return hw_ep_prime(ci, hwep->num, hwep->dir,
|
||||
hwep->type == USB_ENDPOINT_XFER_CONTROL);
|
||||
}
|
||||
|
||||
/**
|
||||
* _hardware_dequeue: handles a request at hardware level
|
||||
* @gadget: gadget
|
||||
@ -535,6 +547,7 @@ static int _hardware_dequeue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq)
|
||||
struct td_node *node, *tmpnode;
|
||||
unsigned remaining_length;
|
||||
unsigned actual = hwreq->req.length;
|
||||
struct ci_hdrc *ci = hwep->ci;
|
||||
|
||||
if (hwreq->req.status != -EALREADY)
|
||||
return -EINVAL;
|
||||
@ -544,6 +557,11 @@ static int _hardware_dequeue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq)
|
||||
list_for_each_entry_safe(node, tmpnode, &hwreq->tds, td) {
|
||||
tmptoken = le32_to_cpu(node->ptr->token);
|
||||
if ((TD_STATUS_ACTIVE & tmptoken) != 0) {
|
||||
int n = hw_ep_bit(hwep->num, hwep->dir);
|
||||
|
||||
if (ci->rev == CI_REVISION_24)
|
||||
if (!hw_read(ci, OP_ENDPTSTAT, BIT(n)))
|
||||
reprime_dtd(ci, hwep, node);
|
||||
hwreq->req.status = -EALREADY;
|
||||
return -EBUSY;
|
||||
}
|
||||
@ -1162,10 +1180,13 @@ static int ep_enable(struct usb_ep *ep,
|
||||
|
||||
/* only internal SW should enable ctrl endpts */
|
||||
|
||||
hwep->ep.desc = desc;
|
||||
|
||||
if (!list_empty(&hwep->qh.queue))
|
||||
if (!list_empty(&hwep->qh.queue)) {
|
||||
dev_warn(hwep->ci->dev, "enabling a non-empty endpoint!\n");
|
||||
spin_unlock_irqrestore(hwep->lock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
hwep->ep.desc = desc;
|
||||
|
||||
hwep->dir = usb_endpoint_dir_in(desc) ? TX : RX;
|
||||
hwep->num = usb_endpoint_num(desc);
|
||||
@ -1485,7 +1506,9 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
|
||||
hw_device_reset(ci);
|
||||
hw_device_state(ci, ci->ep0out->qh.dma);
|
||||
usb_gadget_set_state(_gadget, USB_STATE_POWERED);
|
||||
usb_udc_vbus_handler(_gadget, true);
|
||||
} else {
|
||||
usb_udc_vbus_handler(_gadget, false);
|
||||
if (ci->driver)
|
||||
ci->driver->disconnect(&ci->gadget);
|
||||
hw_device_state(ci, 0);
|
||||
@ -1551,13 +1574,16 @@ static int ci_udc_pullup(struct usb_gadget *_gadget, int is_on)
|
||||
{
|
||||
struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget);
|
||||
|
||||
if (!ci->vbus_active)
|
||||
return -EOPNOTSUPP;
|
||||
/* Data+ pullup controlled by OTG state machine in OTG fsm mode */
|
||||
if (ci_otg_is_fsm_mode(ci))
|
||||
return 0;
|
||||
|
||||
pm_runtime_get_sync(&ci->gadget.dev);
|
||||
if (is_on)
|
||||
hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);
|
||||
else
|
||||
hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
|
||||
pm_runtime_put_sync(&ci->gadget.dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1687,6 +1713,7 @@ static int ci_udc_start(struct usb_gadget *gadget,
|
||||
spin_lock_irqsave(&ci->lock, flags);
|
||||
hw_device_reset(ci);
|
||||
} else {
|
||||
usb_udc_vbus_handler(&ci->gadget, false);
|
||||
pm_runtime_put_sync(&ci->gadget.dev);
|
||||
return retval;
|
||||
}
|
||||
|
@ -11,7 +11,6 @@
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/delay.h>
|
||||
@ -56,6 +55,19 @@
|
||||
#define MX53_USB_PLL_DIV_24_MHZ 0x01
|
||||
|
||||
#define MX6_BM_OVER_CUR_DIS BIT(7)
|
||||
#define MX6_BM_WAKEUP_ENABLE BIT(10)
|
||||
#define MX6_BM_ID_WAKEUP BIT(16)
|
||||
#define MX6_BM_VBUS_WAKEUP BIT(17)
|
||||
#define MX6SX_BM_DPDM_WAKEUP_EN BIT(29)
|
||||
#define MX6_BM_WAKEUP_INTR BIT(31)
|
||||
#define MX6_USB_OTG1_PHY_CTRL 0x18
|
||||
/* For imx6dql, it is host-only controller, for later imx6, it is otg's */
|
||||
#define MX6_USB_OTG2_PHY_CTRL 0x1c
|
||||
#define MX6SX_USB_VBUS_WAKEUP_SOURCE(v) (v << 8)
|
||||
#define MX6SX_USB_VBUS_WAKEUP_SOURCE_VBUS MX6SX_USB_VBUS_WAKEUP_SOURCE(0)
|
||||
#define MX6SX_USB_VBUS_WAKEUP_SOURCE_AVALID MX6SX_USB_VBUS_WAKEUP_SOURCE(1)
|
||||
#define MX6SX_USB_VBUS_WAKEUP_SOURCE_BVALID MX6SX_USB_VBUS_WAKEUP_SOURCE(2)
|
||||
#define MX6SX_USB_VBUS_WAKEUP_SOURCE_SESS_END MX6SX_USB_VBUS_WAKEUP_SOURCE(3)
|
||||
|
||||
#define VF610_OVER_CUR_DIS BIT(7)
|
||||
|
||||
@ -64,12 +76,13 @@ struct usbmisc_ops {
|
||||
int (*init)(struct imx_usbmisc_data *data);
|
||||
/* It's called once after adding a usb device */
|
||||
int (*post)(struct imx_usbmisc_data *data);
|
||||
/* It's called when we need to enable/disable usb wakeup */
|
||||
int (*set_wakeup)(struct imx_usbmisc_data *data, bool enabled);
|
||||
};
|
||||
|
||||
struct imx_usbmisc {
|
||||
void __iomem *base;
|
||||
spinlock_t lock;
|
||||
struct clk *clk;
|
||||
const struct usbmisc_ops *ops;
|
||||
};
|
||||
|
||||
@ -204,6 +217,35 @@ static int usbmisc_imx53_init(struct imx_usbmisc_data *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usbmisc_imx6q_set_wakeup
|
||||
(struct imx_usbmisc_data *data, bool enabled)
|
||||
{
|
||||
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
u32 wakeup_setting = (MX6_BM_WAKEUP_ENABLE |
|
||||
MX6_BM_VBUS_WAKEUP | MX6_BM_ID_WAKEUP);
|
||||
int ret = 0;
|
||||
|
||||
if (data->index > 3)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&usbmisc->lock, flags);
|
||||
val = readl(usbmisc->base + data->index * 4);
|
||||
if (enabled) {
|
||||
val |= wakeup_setting;
|
||||
writel(val, usbmisc->base + data->index * 4);
|
||||
} else {
|
||||
if (val & MX6_BM_WAKEUP_INTR)
|
||||
pr_debug("wakeup int at ci_hdrc.%d\n", data->index);
|
||||
val &= ~wakeup_setting;
|
||||
writel(val, usbmisc->base + data->index * 4);
|
||||
}
|
||||
spin_unlock_irqrestore(&usbmisc->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int usbmisc_imx6q_init(struct imx_usbmisc_data *data)
|
||||
{
|
||||
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
|
||||
@ -221,6 +263,36 @@ static int usbmisc_imx6q_init(struct imx_usbmisc_data *data)
|
||||
spin_unlock_irqrestore(&usbmisc->lock, flags);
|
||||
}
|
||||
|
||||
usbmisc_imx6q_set_wakeup(data, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usbmisc_imx6sx_init(struct imx_usbmisc_data *data)
|
||||
{
|
||||
void __iomem *reg = NULL;
|
||||
unsigned long flags;
|
||||
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
|
||||
u32 val;
|
||||
|
||||
usbmisc_imx6q_init(data);
|
||||
|
||||
if (data->index == 0 || data->index == 1) {
|
||||
reg = usbmisc->base + MX6_USB_OTG1_PHY_CTRL + data->index * 4;
|
||||
spin_lock_irqsave(&usbmisc->lock, flags);
|
||||
/* Set vbus wakeup source as bvalid */
|
||||
val = readl(reg);
|
||||
writel(val | MX6SX_USB_VBUS_WAKEUP_SOURCE_BVALID, reg);
|
||||
/*
|
||||
* Disable dp/dm wakeup in device mode when vbus is
|
||||
* not there.
|
||||
*/
|
||||
val = readl(usbmisc->base + data->index * 4);
|
||||
writel(val & ~MX6SX_BM_DPDM_WAKEUP_EN,
|
||||
usbmisc->base + data->index * 4);
|
||||
spin_unlock_irqrestore(&usbmisc->lock, flags);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -258,6 +330,7 @@ static const struct usbmisc_ops imx53_usbmisc_ops = {
|
||||
};
|
||||
|
||||
static const struct usbmisc_ops imx6q_usbmisc_ops = {
|
||||
.set_wakeup = usbmisc_imx6q_set_wakeup,
|
||||
.init = usbmisc_imx6q_init,
|
||||
};
|
||||
|
||||
@ -265,10 +338,19 @@ static const struct usbmisc_ops vf610_usbmisc_ops = {
|
||||
.init = usbmisc_vf610_init,
|
||||
};
|
||||
|
||||
static const struct usbmisc_ops imx6sx_usbmisc_ops = {
|
||||
.set_wakeup = usbmisc_imx6q_set_wakeup,
|
||||
.init = usbmisc_imx6sx_init,
|
||||
};
|
||||
|
||||
int imx_usbmisc_init(struct imx_usbmisc_data *data)
|
||||
{
|
||||
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
|
||||
struct imx_usbmisc *usbmisc;
|
||||
|
||||
if (!data)
|
||||
return 0;
|
||||
|
||||
usbmisc = dev_get_drvdata(data->dev);
|
||||
if (!usbmisc->ops->init)
|
||||
return 0;
|
||||
return usbmisc->ops->init(data);
|
||||
@ -277,14 +359,32 @@ EXPORT_SYMBOL_GPL(imx_usbmisc_init);
|
||||
|
||||
int imx_usbmisc_init_post(struct imx_usbmisc_data *data)
|
||||
{
|
||||
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
|
||||
struct imx_usbmisc *usbmisc;
|
||||
|
||||
if (!data)
|
||||
return 0;
|
||||
|
||||
usbmisc = dev_get_drvdata(data->dev);
|
||||
if (!usbmisc->ops->post)
|
||||
return 0;
|
||||
return usbmisc->ops->post(data);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(imx_usbmisc_init_post);
|
||||
|
||||
int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *data, bool enabled)
|
||||
{
|
||||
struct imx_usbmisc *usbmisc;
|
||||
|
||||
if (!data)
|
||||
return 0;
|
||||
|
||||
usbmisc = dev_get_drvdata(data->dev);
|
||||
if (!usbmisc->ops->set_wakeup)
|
||||
return 0;
|
||||
return usbmisc->ops->set_wakeup(data, enabled);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(imx_usbmisc_set_wakeup);
|
||||
|
||||
static const struct of_device_id usbmisc_imx_dt_ids[] = {
|
||||
{
|
||||
.compatible = "fsl,imx25-usbmisc",
|
||||
@ -314,6 +414,10 @@ static const struct of_device_id usbmisc_imx_dt_ids[] = {
|
||||
.compatible = "fsl,vf610-usbmisc",
|
||||
.data = &vf610_usbmisc_ops,
|
||||
},
|
||||
{
|
||||
.compatible = "fsl,imx6sx-usbmisc",
|
||||
.data = &imx6sx_usbmisc_ops,
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, usbmisc_imx_dt_ids);
|
||||
@ -322,7 +426,6 @@ static int usbmisc_imx_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct imx_usbmisc *data;
|
||||
int ret;
|
||||
struct of_device_id *tmp_dev;
|
||||
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
||||
@ -336,20 +439,6 @@ static int usbmisc_imx_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(data->base))
|
||||
return PTR_ERR(data->base);
|
||||
|
||||
data->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(data->clk)) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to get clock, err=%ld\n", PTR_ERR(data->clk));
|
||||
return PTR_ERR(data->clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(data->clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"clk_prepare_enable failed, err=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
tmp_dev = (struct of_device_id *)
|
||||
of_match_device(usbmisc_imx_dt_ids, &pdev->dev);
|
||||
data->ops = (const struct usbmisc_ops *)tmp_dev->data;
|
||||
@ -360,8 +449,6 @@ static int usbmisc_imx_probe(struct platform_device *pdev)
|
||||
|
||||
static int usbmisc_imx_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct imx_usbmisc *usbmisc = dev_get_drvdata(&pdev->dev);
|
||||
clk_disable_unprepare(usbmisc->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -360,7 +360,7 @@ static void acm_ctrl_irq(struct urb *urb)
|
||||
}
|
||||
exit:
|
||||
retval = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
if (retval && retval != -EPERM)
|
||||
dev_err(&acm->control->dev, "%s - usb_submit_urb failed: %d\n",
|
||||
__func__, retval);
|
||||
}
|
||||
@ -417,25 +417,33 @@ static void acm_read_bulk_callback(struct urb *urb)
|
||||
struct acm_rb *rb = urb->context;
|
||||
struct acm *acm = rb->instance;
|
||||
unsigned long flags;
|
||||
int status = urb->status;
|
||||
|
||||
dev_vdbg(&acm->data->dev, "%s - urb %d, len %d\n", __func__,
|
||||
rb->index, urb->actual_length);
|
||||
set_bit(rb->index, &acm->read_urbs_free);
|
||||
|
||||
if (!acm->dev) {
|
||||
set_bit(rb->index, &acm->read_urbs_free);
|
||||
dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (urb->status) {
|
||||
if (status) {
|
||||
set_bit(rb->index, &acm->read_urbs_free);
|
||||
dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n",
|
||||
__func__, urb->status);
|
||||
__func__, status);
|
||||
return;
|
||||
}
|
||||
|
||||
usb_mark_last_busy(acm->dev);
|
||||
|
||||
acm_process_read_urb(acm, urb);
|
||||
/*
|
||||
* Unthrottle may run on another CPU which needs to see events
|
||||
* in the same order. Submission has an implict barrier
|
||||
*/
|
||||
smp_mb__before_atomic();
|
||||
set_bit(rb->index, &acm->read_urbs_free);
|
||||
|
||||
/* throttle device if requested by tty */
|
||||
spin_lock_irqsave(&acm->read_lock, flags);
|
||||
@ -454,13 +462,14 @@ static void acm_write_bulk(struct urb *urb)
|
||||
struct acm_wb *wb = urb->context;
|
||||
struct acm *acm = wb->instance;
|
||||
unsigned long flags;
|
||||
int status = urb->status;
|
||||
|
||||
if (urb->status || (urb->actual_length != urb->transfer_buffer_length))
|
||||
if (status || (urb->actual_length != urb->transfer_buffer_length))
|
||||
dev_vdbg(&acm->data->dev, "%s - len %d/%d, status %d\n",
|
||||
__func__,
|
||||
urb->actual_length,
|
||||
urb->transfer_buffer_length,
|
||||
urb->status);
|
||||
status);
|
||||
|
||||
spin_lock_irqsave(&acm->write_lock, flags);
|
||||
acm_write_done(acm, wb);
|
||||
|
@ -245,7 +245,7 @@ static void wdm_int_callback(struct urb *urb)
|
||||
case USB_CDC_NOTIFY_RESPONSE_AVAILABLE:
|
||||
dev_dbg(&desc->intf->dev,
|
||||
"NOTIFY_RESPONSE_AVAILABLE received: index %d len %d",
|
||||
dr->wIndex, dr->wLength);
|
||||
le16_to_cpu(dr->wIndex), le16_to_cpu(dr->wLength));
|
||||
break;
|
||||
|
||||
case USB_CDC_NOTIFY_NETWORK_CONNECTION:
|
||||
@ -262,7 +262,9 @@ static void wdm_int_callback(struct urb *urb)
|
||||
clear_bit(WDM_POLL_RUNNING, &desc->flags);
|
||||
dev_err(&desc->intf->dev,
|
||||
"unknown notification %d received: index %d len %d\n",
|
||||
dr->bNotificationType, dr->wIndex, dr->wLength);
|
||||
dr->bNotificationType,
|
||||
le16_to_cpu(dr->wIndex),
|
||||
le16_to_cpu(dr->wLength));
|
||||
goto exit;
|
||||
}
|
||||
|
||||
@ -339,7 +341,7 @@ static ssize_t wdm_write
|
||||
desc->werr = 0;
|
||||
spin_unlock_irq(&desc->iuspin);
|
||||
if (we < 0)
|
||||
return -EIO;
|
||||
return usb_translate_errors(we);
|
||||
|
||||
buf = kmalloc(count, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
@ -349,30 +351,25 @@ static ssize_t wdm_write
|
||||
|
||||
r = copy_from_user(buf, buffer, count);
|
||||
if (r > 0) {
|
||||
kfree(buf);
|
||||
rv = -EFAULT;
|
||||
goto outnl;
|
||||
goto out_free_mem;
|
||||
}
|
||||
|
||||
/* concurrent writes and disconnect */
|
||||
r = mutex_lock_interruptible(&desc->wlock);
|
||||
rv = -ERESTARTSYS;
|
||||
if (r) {
|
||||
kfree(buf);
|
||||
goto outnl;
|
||||
}
|
||||
if (r)
|
||||
goto out_free_mem;
|
||||
|
||||
if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
|
||||
kfree(buf);
|
||||
rv = -ENODEV;
|
||||
goto outnp;
|
||||
goto out_free_mem_lock;
|
||||
}
|
||||
|
||||
r = usb_autopm_get_interface(desc->intf);
|
||||
if (r < 0) {
|
||||
kfree(buf);
|
||||
rv = usb_translate_errors(r);
|
||||
goto outnp;
|
||||
goto out_free_mem_lock;
|
||||
}
|
||||
|
||||
if (!(file->f_flags & O_NONBLOCK))
|
||||
@ -386,9 +383,8 @@ static ssize_t wdm_write
|
||||
r = -EIO;
|
||||
|
||||
if (r < 0) {
|
||||
kfree(buf);
|
||||
rv = r;
|
||||
goto out;
|
||||
goto out_free_mem_pm;
|
||||
}
|
||||
|
||||
req = desc->orq;
|
||||
@ -408,28 +404,35 @@ static ssize_t wdm_write
|
||||
USB_RECIP_INTERFACE);
|
||||
req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND;
|
||||
req->wValue = 0;
|
||||
req->wIndex = desc->inum;
|
||||
req->wIndex = desc->inum; /* already converted */
|
||||
req->wLength = cpu_to_le16(count);
|
||||
set_bit(WDM_IN_USE, &desc->flags);
|
||||
desc->outbuf = buf;
|
||||
|
||||
rv = usb_submit_urb(desc->command, GFP_KERNEL);
|
||||
if (rv < 0) {
|
||||
kfree(buf);
|
||||
desc->outbuf = NULL;
|
||||
clear_bit(WDM_IN_USE, &desc->flags);
|
||||
dev_err(&desc->intf->dev, "Tx URB error: %d\n", rv);
|
||||
rv = usb_translate_errors(rv);
|
||||
goto out_free_mem_pm;
|
||||
} else {
|
||||
dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d",
|
||||
req->wIndex);
|
||||
le16_to_cpu(req->wIndex));
|
||||
}
|
||||
out:
|
||||
|
||||
usb_autopm_put_interface(desc->intf);
|
||||
outnp:
|
||||
mutex_unlock(&desc->wlock);
|
||||
outnl:
|
||||
return rv < 0 ? rv : count;
|
||||
|
||||
out_free_mem_pm:
|
||||
usb_autopm_put_interface(desc->intf);
|
||||
out_free_mem_lock:
|
||||
mutex_unlock(&desc->wlock);
|
||||
out_free_mem:
|
||||
kfree(buf);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -519,9 +522,9 @@ retry:
|
||||
spin_lock_irq(&desc->iuspin);
|
||||
|
||||
if (desc->rerr) { /* read completed, error happened */
|
||||
rv = usb_translate_errors(desc->rerr);
|
||||
desc->rerr = 0;
|
||||
spin_unlock_irq(&desc->iuspin);
|
||||
rv = -EIO;
|
||||
goto err;
|
||||
}
|
||||
/*
|
||||
@ -820,7 +823,7 @@ static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor
|
||||
desc->irq->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE);
|
||||
desc->irq->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE;
|
||||
desc->irq->wValue = 0;
|
||||
desc->irq->wIndex = desc->inum;
|
||||
desc->irq->wIndex = desc->inum; /* already converted */
|
||||
desc->irq->wLength = cpu_to_le16(desc->wMaxCommand);
|
||||
|
||||
usb_fill_control_urb(
|
||||
|
@ -2408,7 +2408,7 @@ static int usbdev_notify(struct notifier_block *self,
|
||||
}
|
||||
|
||||
static struct notifier_block usbdev_nb = {
|
||||
.notifier_call = usbdev_notify,
|
||||
.notifier_call = usbdev_notify,
|
||||
};
|
||||
|
||||
static struct cdev usb_device_cdev;
|
||||
|
@ -3406,10 +3406,10 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
|
||||
if (status) {
|
||||
dev_dbg(&port_dev->dev, "can't resume, status %d\n", status);
|
||||
} else {
|
||||
/* drive resume for at least 20 msec */
|
||||
/* drive resume for USB_RESUME_TIMEOUT msec */
|
||||
dev_dbg(&udev->dev, "usb %sresume\n",
|
||||
(PMSG_IS_AUTO(msg) ? "auto-" : ""));
|
||||
msleep(25);
|
||||
msleep(USB_RESUME_TIMEOUT);
|
||||
|
||||
/* Virtual root hubs can trigger on GET_PORT_STATUS to
|
||||
* stop resume signaling. Then finish the resume
|
||||
|
@ -49,6 +49,22 @@ 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>
|
||||
*/
|
||||
int usb_disabled(void)
|
||||
{
|
||||
return nousb;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_disabled);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int usb_autosuspend_delay = 2; /* Default delay value,
|
||||
* in seconds */
|
||||
@ -964,22 +980,6 @@ void usb_buffer_unmap_sg(const struct usb_device *dev, int is_in,
|
||||
EXPORT_SYMBOL_GPL(usb_buffer_unmap_sg);
|
||||
#endif
|
||||
|
||||
/* 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>
|
||||
*/
|
||||
int usb_disabled(void)
|
||||
{
|
||||
return nousb;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_disabled);
|
||||
|
||||
/*
|
||||
* Notifications of device and interface registration
|
||||
*/
|
||||
@ -1045,7 +1045,7 @@ static void usb_debugfs_cleanup(void)
|
||||
static int __init usb_init(void)
|
||||
{
|
||||
int retval;
|
||||
if (nousb) {
|
||||
if (usb_disabled()) {
|
||||
pr_info("%s: USB support disabled\n", usbcore_name);
|
||||
return 0;
|
||||
}
|
||||
@ -1102,7 +1102,7 @@ out:
|
||||
static void __exit usb_exit(void)
|
||||
{
|
||||
/* This will matter if shutdown/reboot does exitcalls. */
|
||||
if (nousb)
|
||||
if (usb_disabled())
|
||||
return;
|
||||
|
||||
usb_deregister_device_driver(&usb_generic_driver);
|
||||
|
@ -59,11 +59,13 @@ config USB_DWC2_PLATFORM
|
||||
|
||||
config USB_DWC2_PCI
|
||||
tristate "DWC2 PCI"
|
||||
depends on USB_DWC2_HOST && PCI
|
||||
default USB_DWC2_HOST
|
||||
depends on PCI
|
||||
default n
|
||||
select USB_DWC2_PLATFORM
|
||||
select NOP_USB_XCEIV
|
||||
help
|
||||
The Designware USB2.0 PCI interface module for controllers
|
||||
connected to a PCI bus. This is only used for host mode.
|
||||
connected to a PCI bus.
|
||||
|
||||
config USB_DWC2_DEBUG
|
||||
bool "Enable Debugging Messages"
|
||||
|
@ -19,10 +19,8 @@ endif
|
||||
# mode. The PCI bus interface module will called dwc2_pci.ko and the platform
|
||||
# interface module will be called dwc2_platform.ko.
|
||||
|
||||
ifneq ($(CONFIG_USB_DWC2_PCI),)
|
||||
obj-$(CONFIG_USB_DWC2) += dwc2_pci.o
|
||||
dwc2_pci-y := pci.o
|
||||
endif
|
||||
obj-$(CONFIG_USB_DWC2_PCI) += dwc2_pci.o
|
||||
dwc2_pci-y := pci.o
|
||||
|
||||
obj-$(CONFIG_USB_DWC2_PLATFORM) += dwc2_platform.o
|
||||
dwc2_platform-y := platform.o
|
||||
|
@ -593,6 +593,8 @@ struct dwc2_hsotg {
|
||||
struct dwc2_core_params *core_params;
|
||||
enum usb_otg_state op_state;
|
||||
enum usb_dr_mode dr_mode;
|
||||
unsigned int hcd_enabled:1;
|
||||
unsigned int gadget_enabled:1;
|
||||
|
||||
struct phy *phy;
|
||||
struct usb_phy *uphy;
|
||||
|
@ -257,6 +257,14 @@ static void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg)
|
||||
*/
|
||||
channel->qh = NULL;
|
||||
}
|
||||
/* All channels have been freed, mark them available */
|
||||
if (hsotg->core_params->uframe_sched > 0) {
|
||||
hsotg->available_host_channels =
|
||||
hsotg->core_params->host_channels;
|
||||
} else {
|
||||
hsotg->non_periodic_channels = 0;
|
||||
hsotg->periodic_channels = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1527,7 +1535,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
|
||||
hprt0 |= HPRT0_RES;
|
||||
writel(hprt0, hsotg->regs + HPRT0);
|
||||
hprt0 &= ~HPRT0_SUSP;
|
||||
usleep_range(100000, 150000);
|
||||
msleep(USB_RESUME_TIMEOUT);
|
||||
|
||||
hprt0 &= ~HPRT0_RES;
|
||||
writel(hprt0, hsotg->regs + HPRT0);
|
||||
@ -1608,7 +1616,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
|
||||
dev_dbg(hsotg->dev, "GetHubDescriptor\n");
|
||||
hub_desc = (struct usb_hub_descriptor *)buf;
|
||||
hub_desc->bDescLength = 9;
|
||||
hub_desc->bDescriptorType = 0x29;
|
||||
hub_desc->bDescriptorType = USB_DT_HUB;
|
||||
hub_desc->bNbrPorts = 1;
|
||||
hub_desc->wHubCharacteristics =
|
||||
cpu_to_le16(HUB_CHAR_COMMON_LPSM |
|
||||
|
@ -50,113 +50,97 @@
|
||||
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/usb/ch11.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/usb/usb_phy_generic.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "hcd.h"
|
||||
|
||||
#define PCI_VENDOR_ID_SYNOPSYS 0x16c3
|
||||
#define PCI_PRODUCT_ID_HAPS_HSOTG 0xabc0
|
||||
|
||||
static const char dwc2_driver_name[] = "dwc2";
|
||||
static const char dwc2_driver_name[] = "dwc2-pci";
|
||||
|
||||
static const struct dwc2_core_params dwc2_module_params = {
|
||||
.otg_cap = -1,
|
||||
.otg_ver = -1,
|
||||
.dma_enable = -1,
|
||||
.dma_desc_enable = 0,
|
||||
.speed = -1,
|
||||
.enable_dynamic_fifo = -1,
|
||||
.en_multiple_tx_fifo = -1,
|
||||
.host_rx_fifo_size = 1024,
|
||||
.host_nperio_tx_fifo_size = 256,
|
||||
.host_perio_tx_fifo_size = 1024,
|
||||
.max_transfer_size = 65535,
|
||||
.max_packet_count = 511,
|
||||
.host_channels = -1,
|
||||
.phy_type = -1,
|
||||
.phy_utmi_width = -1,
|
||||
.phy_ulpi_ddr = -1,
|
||||
.phy_ulpi_ext_vbus = -1,
|
||||
.i2c_enable = -1,
|
||||
.ulpi_fs_ls = -1,
|
||||
.host_support_fs_ls_low_power = -1,
|
||||
.host_ls_low_power_phy_clk = -1,
|
||||
.ts_dline = -1,
|
||||
.reload_ctl = -1,
|
||||
.ahbcfg = -1,
|
||||
.uframe_sched = -1,
|
||||
struct dwc2_pci_glue {
|
||||
struct platform_device *dwc2;
|
||||
struct platform_device *phy;
|
||||
};
|
||||
|
||||
/**
|
||||
* dwc2_driver_remove() - Called when the DWC_otg core is unregistered with the
|
||||
* DWC_otg driver
|
||||
*
|
||||
* @dev: Bus device
|
||||
*
|
||||
* This routine is called, for example, when the rmmod command is executed. The
|
||||
* device may or may not be electrically present. If it is present, the driver
|
||||
* stops device processing. Any resources used on behalf of this device are
|
||||
* freed.
|
||||
*/
|
||||
static void dwc2_driver_remove(struct pci_dev *dev)
|
||||
static void dwc2_pci_remove(struct pci_dev *pci)
|
||||
{
|
||||
struct dwc2_hsotg *hsotg = pci_get_drvdata(dev);
|
||||
struct dwc2_pci_glue *glue = pci_get_drvdata(pci);
|
||||
|
||||
dwc2_hcd_remove(hsotg);
|
||||
pci_disable_device(dev);
|
||||
platform_device_unregister(glue->dwc2);
|
||||
usb_phy_generic_unregister(glue->phy);
|
||||
kfree(glue);
|
||||
pci_set_drvdata(pci, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_driver_probe() - Called when the DWC_otg core is bound to the DWC_otg
|
||||
* driver
|
||||
*
|
||||
* @dev: Bus device
|
||||
*
|
||||
* This routine creates the driver components required to control the device
|
||||
* (core, HCD, and PCD) and initializes the device. The driver components are
|
||||
* stored in a dwc2_hsotg structure. A reference to the dwc2_hsotg is saved
|
||||
* in the device private data. This allows the driver to access the dwc2_hsotg
|
||||
* structure on subsequent calls to driver methods for this device.
|
||||
*/
|
||||
static int dwc2_driver_probe(struct pci_dev *dev,
|
||||
const struct pci_device_id *id)
|
||||
static int dwc2_pci_probe(struct pci_dev *pci,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
struct dwc2_hsotg *hsotg;
|
||||
int retval;
|
||||
struct resource res[2];
|
||||
struct platform_device *dwc2;
|
||||
struct platform_device *phy;
|
||||
int ret;
|
||||
struct device *dev = &pci->dev;
|
||||
struct dwc2_pci_glue *glue;
|
||||
|
||||
hsotg = devm_kzalloc(&dev->dev, sizeof(*hsotg), GFP_KERNEL);
|
||||
if (!hsotg)
|
||||
return -ENOMEM;
|
||||
|
||||
hsotg->dev = &dev->dev;
|
||||
hsotg->regs = devm_ioremap_resource(&dev->dev, &dev->resource[0]);
|
||||
if (IS_ERR(hsotg->regs))
|
||||
return PTR_ERR(hsotg->regs);
|
||||
|
||||
dev_dbg(&dev->dev, "mapped PA %08lx to VA %p\n",
|
||||
(unsigned long)pci_resource_start(dev, 0), hsotg->regs);
|
||||
|
||||
if (pci_enable_device(dev) < 0)
|
||||
ret = pcim_enable_device(pci);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable pci device\n");
|
||||
return -ENODEV;
|
||||
|
||||
pci_set_master(dev);
|
||||
|
||||
retval = devm_request_irq(hsotg->dev, dev->irq,
|
||||
dwc2_handle_common_intr, IRQF_SHARED,
|
||||
dev_name(hsotg->dev), hsotg);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
spin_lock_init(&hsotg->lock);
|
||||
retval = dwc2_hcd_init(hsotg, dev->irq, &dwc2_module_params);
|
||||
if (retval) {
|
||||
pci_disable_device(dev);
|
||||
return retval;
|
||||
}
|
||||
|
||||
pci_set_drvdata(dev, hsotg);
|
||||
pci_set_master(pci);
|
||||
|
||||
return retval;
|
||||
dwc2 = platform_device_alloc("dwc2", PLATFORM_DEVID_AUTO);
|
||||
if (!dwc2) {
|
||||
dev_err(dev, "couldn't allocate dwc2 device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res));
|
||||
|
||||
res[0].start = pci_resource_start(pci, 0);
|
||||
res[0].end = pci_resource_end(pci, 0);
|
||||
res[0].name = "dwc2";
|
||||
res[0].flags = IORESOURCE_MEM;
|
||||
|
||||
res[1].start = pci->irq;
|
||||
res[1].name = "dwc2";
|
||||
res[1].flags = IORESOURCE_IRQ;
|
||||
|
||||
ret = platform_device_add_resources(dwc2, res, ARRAY_SIZE(res));
|
||||
if (ret) {
|
||||
dev_err(dev, "couldn't add resources to dwc2 device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dwc2->dev.parent = dev;
|
||||
|
||||
phy = usb_phy_generic_register();
|
||||
if (IS_ERR(phy)) {
|
||||
dev_err(dev, "error registering generic PHY (%ld)\n",
|
||||
PTR_ERR(phy));
|
||||
return PTR_ERR(phy);
|
||||
}
|
||||
|
||||
ret = platform_device_add(dwc2);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register dwc2 device\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
glue = kzalloc(sizeof(*glue), GFP_KERNEL);
|
||||
if (!glue)
|
||||
return -ENOMEM;
|
||||
|
||||
glue->phy = phy;
|
||||
glue->dwc2 = dwc2;
|
||||
pci_set_drvdata(pci, glue);
|
||||
|
||||
return 0;
|
||||
err:
|
||||
usb_phy_generic_unregister(phy);
|
||||
platform_device_put(dwc2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct pci_device_id dwc2_pci_ids[] = {
|
||||
@ -174,8 +158,8 @@ MODULE_DEVICE_TABLE(pci, dwc2_pci_ids);
|
||||
static struct pci_driver dwc2_pci_driver = {
|
||||
.name = dwc2_driver_name,
|
||||
.id_table = dwc2_pci_ids,
|
||||
.probe = dwc2_driver_probe,
|
||||
.remove = dwc2_driver_remove,
|
||||
.probe = dwc2_pci_probe,
|
||||
.remove = dwc2_pci_remove,
|
||||
};
|
||||
|
||||
module_pci_driver(dwc2_pci_driver);
|
||||
|
@ -121,8 +121,10 @@ static int dwc2_driver_remove(struct platform_device *dev)
|
||||
{
|
||||
struct dwc2_hsotg *hsotg = platform_get_drvdata(dev);
|
||||
|
||||
dwc2_hcd_remove(hsotg);
|
||||
s3c_hsotg_remove(hsotg);
|
||||
if (hsotg->hcd_enabled)
|
||||
dwc2_hcd_remove(hsotg);
|
||||
if (hsotg->gadget_enabled)
|
||||
s3c_hsotg_remove(hsotg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -234,12 +236,23 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
||||
|
||||
spin_lock_init(&hsotg->lock);
|
||||
mutex_init(&hsotg->init_mutex);
|
||||
retval = dwc2_gadget_init(hsotg, irq);
|
||||
if (retval)
|
||||
return retval;
|
||||
retval = dwc2_hcd_init(hsotg, irq, params);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
if (hsotg->dr_mode != USB_DR_MODE_HOST) {
|
||||
retval = dwc2_gadget_init(hsotg, irq);
|
||||
if (retval)
|
||||
return retval;
|
||||
hsotg->gadget_enabled = 1;
|
||||
}
|
||||
|
||||
if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) {
|
||||
retval = dwc2_hcd_init(hsotg, irq, params);
|
||||
if (retval) {
|
||||
if (hsotg->gadget_enabled)
|
||||
s3c_hsotg_remove(hsotg);
|
||||
return retval;
|
||||
}
|
||||
hsotg->hcd_enabled = 1;
|
||||
}
|
||||
|
||||
platform_set_drvdata(dev, hsotg);
|
||||
|
||||
|
@ -104,11 +104,4 @@ config USB_DWC3_DEBUG
|
||||
help
|
||||
Say Y here to enable debugging messages on DWC3 Driver.
|
||||
|
||||
config DWC3_HOST_USB3_LPM_ENABLE
|
||||
bool "Enable USB3 LPM Capability"
|
||||
depends on USB_DWC3_HOST=y || USB_DWC3_DUAL_ROLE=y
|
||||
default n
|
||||
help
|
||||
Select this when you want to enable USB3 LPM with dwc3 xhci host.
|
||||
|
||||
endif
|
||||
|
@ -774,17 +774,13 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
* since it will be requested by the xhci-plat driver.
|
||||
*/
|
||||
regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(regs))
|
||||
return PTR_ERR(regs);
|
||||
if (IS_ERR(regs)) {
|
||||
ret = PTR_ERR(regs);
|
||||
goto err0;
|
||||
}
|
||||
|
||||
dwc->regs = regs;
|
||||
dwc->regs_size = resource_size(res);
|
||||
/*
|
||||
* restore res->start back to its original value so that,
|
||||
* in case the probe is deferred, we don't end up getting error in
|
||||
* request the memory region the next time probe is called.
|
||||
*/
|
||||
res->start -= DWC3_GLOBALS_REGS_START;
|
||||
|
||||
/* default to highest possible threshold */
|
||||
lpm_nyet_threshold = 0xff;
|
||||
@ -808,6 +804,8 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
"snps,is-utmi-l1-suspend");
|
||||
of_property_read_u8(node, "snps,hird-threshold",
|
||||
&hird_threshold);
|
||||
dwc->usb3_lpm_capable = of_property_read_bool(node,
|
||||
"snps,usb3_lpm_capable");
|
||||
|
||||
dwc->needs_fifo_resize = of_property_read_bool(node,
|
||||
"tx-fifo-resize");
|
||||
@ -848,6 +846,7 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
hird_threshold = pdata->hird_threshold;
|
||||
|
||||
dwc->needs_fifo_resize = pdata->tx_fifo_resize;
|
||||
dwc->usb3_lpm_capable = pdata->usb3_lpm_capable;
|
||||
dwc->dr_mode = pdata->dr_mode;
|
||||
|
||||
dwc->disable_scramble_quirk = pdata->disable_scramble_quirk;
|
||||
@ -878,7 +877,7 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
|
||||
ret = dwc3_core_get_phy(dwc);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto err0;
|
||||
|
||||
spin_lock_init(&dwc->lock);
|
||||
platform_set_drvdata(pdev, dwc);
|
||||
@ -899,7 +898,7 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to allocate event buffers\n");
|
||||
ret = -ENOMEM;
|
||||
goto err0;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_USB_DWC3_HOST))
|
||||
@ -913,65 +912,81 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
ret = dwc3_core_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize core\n");
|
||||
goto err0;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
usb_phy_set_suspend(dwc->usb2_phy, 0);
|
||||
usb_phy_set_suspend(dwc->usb3_phy, 0);
|
||||
ret = phy_power_on(dwc->usb2_generic_phy);
|
||||
if (ret < 0)
|
||||
goto err1;
|
||||
goto err2;
|
||||
|
||||
ret = phy_power_on(dwc->usb3_generic_phy);
|
||||
if (ret < 0)
|
||||
goto err_usb2phy_power;
|
||||
goto err3;
|
||||
|
||||
ret = dwc3_event_buffers_setup(dwc);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to setup event buffers\n");
|
||||
goto err_usb3phy_power;
|
||||
goto err4;
|
||||
}
|
||||
|
||||
ret = dwc3_core_init_mode(dwc);
|
||||
if (ret)
|
||||
goto err2;
|
||||
goto err5;
|
||||
|
||||
ret = dwc3_debugfs_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize debugfs\n");
|
||||
goto err3;
|
||||
goto err6;
|
||||
}
|
||||
|
||||
pm_runtime_allow(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err3:
|
||||
err6:
|
||||
dwc3_core_exit_mode(dwc);
|
||||
|
||||
err2:
|
||||
err5:
|
||||
dwc3_event_buffers_cleanup(dwc);
|
||||
|
||||
err_usb3phy_power:
|
||||
err4:
|
||||
phy_power_off(dwc->usb3_generic_phy);
|
||||
|
||||
err_usb2phy_power:
|
||||
err3:
|
||||
phy_power_off(dwc->usb2_generic_phy);
|
||||
|
||||
err1:
|
||||
err2:
|
||||
usb_phy_set_suspend(dwc->usb2_phy, 1);
|
||||
usb_phy_set_suspend(dwc->usb3_phy, 1);
|
||||
dwc3_core_exit(dwc);
|
||||
|
||||
err0:
|
||||
err1:
|
||||
dwc3_free_event_buffers(dwc);
|
||||
|
||||
err0:
|
||||
/*
|
||||
* restore res->start back to its original value so that, in case the
|
||||
* probe is deferred, we don't end up getting error in request the
|
||||
* memory region the next time probe is called.
|
||||
*/
|
||||
res->start -= DWC3_GLOBALS_REGS_START;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dwc3_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3 *dwc = platform_get_drvdata(pdev);
|
||||
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
/*
|
||||
* restore res->start back to its original value so that, in case the
|
||||
* probe is deferred, we don't end up getting error in request the
|
||||
* memory region the next time probe is called.
|
||||
*/
|
||||
res->start -= DWC3_GLOBALS_REGS_START;
|
||||
|
||||
dwc3_debugfs_exit(dwc);
|
||||
dwc3_core_exit_mode(dwc);
|
||||
|
@ -689,6 +689,7 @@ struct dwc3_scratchpad_array {
|
||||
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
|
||||
* @start_config_issued: true when StartConfig command has been issued
|
||||
* @three_stage_setup: set if we perform a three phase setup
|
||||
* @usb3_lpm_capable: set if hadrware supports Link Power Management
|
||||
* @disable_scramble_quirk: set if we enable the disable scramble quirk
|
||||
* @u2exit_lfps_quirk: set if we enable u2exit lfps quirk
|
||||
* @u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk
|
||||
@ -812,6 +813,7 @@ struct dwc3 {
|
||||
unsigned setup_packet_pending:1;
|
||||
unsigned start_config_issued:1;
|
||||
unsigned three_stage_setup:1;
|
||||
unsigned usb3_lpm_capable:1;
|
||||
|
||||
unsigned disable_scramble_quirk:1;
|
||||
unsigned u2exit_lfps_quirk:1;
|
||||
|
@ -325,15 +325,6 @@ static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int dwc3_omap_remove_core(struct device *dev, void *c)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
|
||||
of_device_unregister(pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dwc3_omap_enable_irqs(struct dwc3_omap *omap)
|
||||
{
|
||||
u32 reg;
|
||||
@ -600,7 +591,7 @@ static int dwc3_omap_remove(struct platform_device *pdev)
|
||||
if (omap->extcon_id_dev.edev)
|
||||
extcon_unregister_interest(&omap->extcon_id_dev);
|
||||
dwc3_omap_disable_irqs(omap);
|
||||
device_for_each_child(&pdev->dev, NULL, dwc3_omap_remove_core);
|
||||
of_platform_depopulate(omap->dev);
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
|
@ -24,8 +24,6 @@
|
||||
|
||||
#include "platform_data.h"
|
||||
|
||||
/* FIXME define these in <linux/pci_ids.h> */
|
||||
#define PCI_VENDOR_ID_SYNOPSYS 0x16c3
|
||||
#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 0xabcd
|
||||
#define PCI_DEVICE_ID_INTEL_BYT 0x0f37
|
||||
#define PCI_DEVICE_ID_INTEL_MRFLD 0x119e
|
||||
|
@ -1855,32 +1855,27 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
req = next_request(&dep->req_queued);
|
||||
if (!req) {
|
||||
WARN_ON_ONCE(1);
|
||||
return 1;
|
||||
}
|
||||
i = 0;
|
||||
do {
|
||||
req = next_request(&dep->req_queued);
|
||||
if (!req) {
|
||||
WARN_ON_ONCE(1);
|
||||
return 1;
|
||||
}
|
||||
i = 0;
|
||||
do {
|
||||
slot = req->start_slot + i;
|
||||
if ((slot == DWC3_TRB_NUM - 1) &&
|
||||
slot = req->start_slot + i;
|
||||
if ((slot == DWC3_TRB_NUM - 1) &&
|
||||
usb_endpoint_xfer_isoc(dep->endpoint.desc))
|
||||
slot++;
|
||||
slot %= DWC3_TRB_NUM;
|
||||
trb = &dep->trb_pool[slot];
|
||||
|
||||
ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
|
||||
event, status);
|
||||
if (ret)
|
||||
break;
|
||||
}while (++i < req->request.num_mapped_sgs);
|
||||
|
||||
dwc3_gadget_giveback(dep, req, status);
|
||||
slot++;
|
||||
slot %= DWC3_TRB_NUM;
|
||||
trb = &dep->trb_pool[slot];
|
||||
|
||||
ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
|
||||
event, status);
|
||||
if (ret)
|
||||
break;
|
||||
} while (1);
|
||||
} while (++i < req->request.num_mapped_sgs);
|
||||
|
||||
dwc3_gadget_giveback(dep, req, status);
|
||||
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
|
||||
list_empty(&dep->req_queued)) {
|
||||
|
@ -49,9 +49,7 @@ int dwc3_host_init(struct dwc3 *dwc)
|
||||
|
||||
memset(&pdata, 0, sizeof(pdata));
|
||||
|
||||
#ifdef CONFIG_DWC3_HOST_USB3_LPM_ENABLE
|
||||
pdata.usb3_lpm_capable = 1;
|
||||
#endif
|
||||
pdata.usb3_lpm_capable = dwc->usb3_lpm_capable;
|
||||
|
||||
ret = platform_device_add_data(xhci, &pdata, sizeof(pdata));
|
||||
if (ret) {
|
||||
|
@ -24,6 +24,7 @@ struct dwc3_platform_data {
|
||||
enum usb_device_speed maximum_speed;
|
||||
enum usb_dr_mode dr_mode;
|
||||
bool tx_fifo_resize;
|
||||
bool usb3_lpm_capable;
|
||||
|
||||
unsigned is_utmi_l1_suspend:1;
|
||||
u8 hird_threshold;
|
||||
|
@ -196,6 +196,9 @@ config USB_F_MIDI
|
||||
config USB_F_HID
|
||||
tristate
|
||||
|
||||
config USB_F_PRINTER
|
||||
tristate
|
||||
|
||||
choice
|
||||
tristate "USB Gadget Drivers"
|
||||
default USB_ETH
|
||||
@ -434,6 +437,20 @@ config USB_CONFIGFS_F_UVC
|
||||
device. It provides a userspace API to process UVC control requests
|
||||
and stream video data to the host.
|
||||
|
||||
config USB_CONFIGFS_F_PRINTER
|
||||
bool "Printer function"
|
||||
select USB_F_PRINTER
|
||||
depends on USB_CONFIGFS
|
||||
help
|
||||
The Printer function channels data between the USB host and a
|
||||
userspace program driving the print engine. The user space
|
||||
program reads and writes the device file /dev/g_printer<X> to
|
||||
receive or send printer data. It can use ioctl calls to
|
||||
the device file to get or set printer status.
|
||||
|
||||
For more information, see Documentation/usb/gadget_printer.txt
|
||||
which includes sample code for accessing the device file.
|
||||
|
||||
source "drivers/usb/gadget/legacy/Kconfig"
|
||||
|
||||
endchoice
|
||||
|
@ -1161,11 +1161,11 @@ static struct usb_gadget_string_container *copy_gadget_strings(
|
||||
* This function will create a deep copy of usb_gadget_strings and usb_string
|
||||
* and attach it to the cdev. The actual string (usb_string.s) will not be
|
||||
* copied but only a referenced will be made. The struct usb_gadget_strings
|
||||
* array may contain multiple languges and should be NULL terminated.
|
||||
* array may contain multiple languages and should be NULL terminated.
|
||||
* The ->language pointer of each struct usb_gadget_strings has to contain the
|
||||
* same amount of entries.
|
||||
* For instance: sp[0] is en-US, sp[1] is es-ES. It is expected that the first
|
||||
* usb_string entry of es-ES containts the translation of the first usb_string
|
||||
* usb_string entry of es-ES contains the translation of the first usb_string
|
||||
* entry of en-US. Therefore both entries become the same id assign.
|
||||
*/
|
||||
struct usb_string *usb_gstrings_attach(struct usb_composite_dev *cdev,
|
||||
@ -1472,6 +1472,13 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
||||
req->length = 0;
|
||||
gadget->ep0->driver_data = cdev;
|
||||
|
||||
/*
|
||||
* Don't let non-standard requests match any of the cases below
|
||||
* by accident.
|
||||
*/
|
||||
if ((ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
|
||||
goto unknown;
|
||||
|
||||
switch (ctrl->bRequest) {
|
||||
|
||||
/* we handle all standard USB descriptors */
|
||||
@ -1751,6 +1758,10 @@ unknown:
|
||||
* take such requests too, if that's ever needed: to work
|
||||
* in config 0, etc.
|
||||
*/
|
||||
list_for_each_entry(f, &cdev->config->functions, list)
|
||||
if (f->req_match && f->req_match(f, ctrl))
|
||||
goto try_fun_setup;
|
||||
f = NULL;
|
||||
switch (ctrl->bRequestType & USB_RECIP_MASK) {
|
||||
case USB_RECIP_INTERFACE:
|
||||
if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
|
||||
@ -1768,7 +1779,7 @@ unknown:
|
||||
f = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
try_fun_setup:
|
||||
if (f && f->setup)
|
||||
value = f->setup(f, ctrl);
|
||||
else {
|
||||
|
@ -42,3 +42,5 @@ usb_f_midi-y := f_midi.o
|
||||
obj-$(CONFIG_USB_F_MIDI) += usb_f_midi.o
|
||||
usb_f_hid-y := f_hid.o
|
||||
obj-$(CONFIG_USB_F_HID) += usb_f_hid.o
|
||||
usb_f_printer-y := f_printer.o
|
||||
obj-$(CONFIG_USB_F_PRINTER) += usb_f_printer.o
|
||||
|
@ -908,7 +908,6 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
|
||||
/* disable/free request and end point */
|
||||
usb_ep_disable(hidg->in_ep);
|
||||
usb_ep_dequeue(hidg->in_ep, hidg->req);
|
||||
kfree(hidg->req->buf);
|
||||
usb_ep_free_request(hidg->in_ep, hidg->req);
|
||||
|
||||
|
@ -1085,7 +1085,7 @@ static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh)
|
||||
if (!curlun) { /* Unsupported LUNs are okay */
|
||||
common->bad_lun_okay = 1;
|
||||
memset(buf, 0, 36);
|
||||
buf[0] = 0x7f; /* Unsupported, no device-type */
|
||||
buf[0] = TYPE_NO_LUN; /* Unsupported, no device-type */
|
||||
buf[4] = 31; /* Additional length */
|
||||
return 36;
|
||||
}
|
||||
@ -2624,13 +2624,10 @@ static ssize_t file_store(struct device *dev, struct device_attribute *attr,
|
||||
return fsg_store_file(curlun, filesem, buf, count);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(ro);
|
||||
static DEVICE_ATTR_RW(nofua);
|
||||
static DEVICE_ATTR_RW(file);
|
||||
|
||||
static struct device_attribute dev_attr_ro_cdrom = __ATTR_RO(ro);
|
||||
static struct device_attribute dev_attr_file_nonremovable = __ATTR_RO(file);
|
||||
|
||||
/* mode wil be set in fsg_lun_attr_is_visible() */
|
||||
static DEVICE_ATTR(ro, 0, ro_show, ro_store);
|
||||
static DEVICE_ATTR(file, 0, file_show, file_store);
|
||||
|
||||
/****************************** FSG COMMON ******************************/
|
||||
|
||||
@ -2745,40 +2742,10 @@ error_release:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fsg_common_set_num_buffers);
|
||||
|
||||
static inline void fsg_common_remove_sysfs(struct fsg_lun *lun)
|
||||
{
|
||||
device_remove_file(&lun->dev, &dev_attr_nofua);
|
||||
/*
|
||||
* device_remove_file() =>
|
||||
*
|
||||
* here the attr (e.g. dev_attr_ro) is only used to be passed to:
|
||||
*
|
||||
* sysfs_remove_file() =>
|
||||
*
|
||||
* here e.g. both dev_attr_ro_cdrom and dev_attr_ro are in
|
||||
* the same namespace and
|
||||
* from here only attr->name is passed to:
|
||||
*
|
||||
* sysfs_hash_and_remove()
|
||||
*
|
||||
* attr->name is the same for dev_attr_ro_cdrom and
|
||||
* dev_attr_ro
|
||||
* attr->name is the same for dev_attr_file and
|
||||
* dev_attr_file_nonremovable
|
||||
*
|
||||
* so we don't differentiate between removing e.g. dev_attr_ro_cdrom
|
||||
* and dev_attr_ro
|
||||
*/
|
||||
device_remove_file(&lun->dev, &dev_attr_ro);
|
||||
device_remove_file(&lun->dev, &dev_attr_file);
|
||||
}
|
||||
|
||||
void fsg_common_remove_lun(struct fsg_lun *lun, bool sysfs)
|
||||
{
|
||||
if (sysfs) {
|
||||
fsg_common_remove_sysfs(lun);
|
||||
if (sysfs)
|
||||
device_unregister(&lun->dev);
|
||||
}
|
||||
fsg_lun_close(lun);
|
||||
kfree(lun);
|
||||
}
|
||||
@ -2877,42 +2844,36 @@ int fsg_common_set_cdev(struct fsg_common *common,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fsg_common_set_cdev);
|
||||
|
||||
static inline int fsg_common_add_sysfs(struct fsg_common *common,
|
||||
struct fsg_lun *lun)
|
||||
static struct attribute *fsg_lun_dev_attrs[] = {
|
||||
&dev_attr_ro.attr,
|
||||
&dev_attr_file.attr,
|
||||
&dev_attr_nofua.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static umode_t fsg_lun_dev_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr, int idx)
|
||||
{
|
||||
int rc;
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct fsg_lun *lun = fsg_lun_from_dev(dev);
|
||||
|
||||
rc = device_register(&lun->dev);
|
||||
if (rc) {
|
||||
put_device(&lun->dev);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = device_create_file(&lun->dev,
|
||||
lun->cdrom
|
||||
? &dev_attr_ro_cdrom
|
||||
: &dev_attr_ro);
|
||||
if (rc)
|
||||
goto error;
|
||||
rc = device_create_file(&lun->dev,
|
||||
lun->removable
|
||||
? &dev_attr_file
|
||||
: &dev_attr_file_nonremovable);
|
||||
if (rc)
|
||||
goto error;
|
||||
rc = device_create_file(&lun->dev, &dev_attr_nofua);
|
||||
if (rc)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
/* removing nonexistent files is a no-op */
|
||||
fsg_common_remove_sysfs(lun);
|
||||
device_unregister(&lun->dev);
|
||||
return rc;
|
||||
if (attr == &dev_attr_ro.attr)
|
||||
return lun->cdrom ? S_IRUGO : (S_IWUSR | S_IRUGO);
|
||||
if (attr == &dev_attr_file.attr)
|
||||
return lun->removable ? (S_IWUSR | S_IRUGO) : S_IRUGO;
|
||||
return attr->mode;
|
||||
}
|
||||
|
||||
static const struct attribute_group fsg_lun_dev_group = {
|
||||
.attrs = fsg_lun_dev_attrs,
|
||||
.is_visible = fsg_lun_dev_is_visible,
|
||||
};
|
||||
|
||||
static const struct attribute_group *fsg_lun_dev_groups[] = {
|
||||
&fsg_lun_dev_group,
|
||||
NULL
|
||||
};
|
||||
|
||||
int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg,
|
||||
unsigned int id, const char *name,
|
||||
const char **name_pfx)
|
||||
@ -2949,13 +2910,15 @@ int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg,
|
||||
} else {
|
||||
lun->dev.release = fsg_lun_release;
|
||||
lun->dev.parent = &common->gadget->dev;
|
||||
lun->dev.groups = fsg_lun_dev_groups;
|
||||
dev_set_drvdata(&lun->dev, &common->filesem);
|
||||
dev_set_name(&lun->dev, "%s", name);
|
||||
lun->name = dev_name(&lun->dev);
|
||||
|
||||
rc = fsg_common_add_sysfs(common, lun);
|
||||
rc = device_register(&lun->dev);
|
||||
if (rc) {
|
||||
pr_info("failed to register LUN%d: %d\n", id, rc);
|
||||
put_device(&lun->dev);
|
||||
goto error_sysfs;
|
||||
}
|
||||
}
|
||||
@ -2988,10 +2951,8 @@ int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg,
|
||||
return 0;
|
||||
|
||||
error_lun:
|
||||
if (common->sysfs) {
|
||||
fsg_common_remove_sysfs(lun);
|
||||
if (common->sysfs)
|
||||
device_unregister(&lun->dev);
|
||||
}
|
||||
fsg_lun_close(lun);
|
||||
common->luns[id] = NULL;
|
||||
error_sysfs:
|
||||
@ -3077,8 +3038,6 @@ static void fsg_common_release(struct kref *ref)
|
||||
struct fsg_lun *lun = *lun_it;
|
||||
if (!lun)
|
||||
continue;
|
||||
if (common->sysfs)
|
||||
fsg_common_remove_sysfs(lun);
|
||||
fsg_lun_close(lun);
|
||||
if (common->sysfs)
|
||||
device_unregister(&lun->dev);
|
||||
|
1471
drivers/usb/gadget/function/f_printer.c
Normal file
1471
drivers/usb/gadget/function/f_printer.c
Normal file
File diff suppressed because it is too large
Load Diff
37
drivers/usb/gadget/function/u_printer.h
Normal file
37
drivers/usb/gadget/function/u_printer.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* u_printer.h
|
||||
*
|
||||
* Utility definitions for the printer function
|
||||
*
|
||||
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.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 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef U_PRINTER_H
|
||||
#define U_PRINTER_H
|
||||
|
||||
#include <linux/usb/composite.h>
|
||||
|
||||
#define PNP_STRING_LEN 1024
|
||||
|
||||
struct f_printer_opts {
|
||||
struct usb_function_instance func_inst;
|
||||
int minor;
|
||||
char pnp_string[PNP_STRING_LEN];
|
||||
unsigned q_len;
|
||||
|
||||
/*
|
||||
* Protect the data from concurrent access by read/write
|
||||
* and create symlink/remove symlink
|
||||
*/
|
||||
struct mutex lock;
|
||||
int refcnt;
|
||||
};
|
||||
|
||||
#endif /* U_PRINTER_H */
|
@ -912,7 +912,7 @@ static int gs_put_char(struct tty_struct *tty, unsigned char ch)
|
||||
unsigned long flags;
|
||||
int status;
|
||||
|
||||
pr_vdebug("gs_put_char: (%d,%p) char=0x%x, called from %pf\n",
|
||||
pr_vdebug("gs_put_char: (%d,%p) char=0x%x, called from %ps\n",
|
||||
port->port_num, tty, ch, __builtin_return_address(0));
|
||||
|
||||
spin_lock_irqsave(&port->port_lock, flags);
|
||||
|
@ -301,6 +301,7 @@ config USB_MIDI_GADGET
|
||||
config USB_G_PRINTER
|
||||
tristate "Printer Gadget"
|
||||
select USB_LIBCOMPOSITE
|
||||
select USB_F_PRINTER
|
||||
help
|
||||
The Printer Gadget channels data between the USB host and a
|
||||
userspace program driving the print engine. The user space
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -152,7 +152,7 @@ static int regs_dbg_open(struct inode *inode, struct file *file)
|
||||
|
||||
spin_lock_irq(&udc->lock);
|
||||
for (i = 0; i < inode->i_size / 4; i++)
|
||||
data[i] = __raw_readl(udc->regs + i * 4);
|
||||
data[i] = usba_io_readl(udc->regs + i * 4);
|
||||
spin_unlock_irq(&udc->lock);
|
||||
|
||||
file->private_data = data;
|
||||
@ -1249,7 +1249,7 @@ static int handle_ep0_setup(struct usba_udc *udc, struct usba_ep *ep,
|
||||
if (crq->wLength != cpu_to_le16(sizeof(status)))
|
||||
goto stall;
|
||||
ep->state = DATA_STAGE_IN;
|
||||
__raw_writew(status, ep->fifo);
|
||||
usba_io_writew(status, ep->fifo);
|
||||
usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY);
|
||||
break;
|
||||
}
|
||||
@ -1739,7 +1739,72 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t usba_vbus_irq(int irq, void *devid)
|
||||
static int start_clock(struct usba_udc *udc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (udc->clocked)
|
||||
return 0;
|
||||
|
||||
ret = clk_prepare_enable(udc->pclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = clk_prepare_enable(udc->hclk);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(udc->pclk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
udc->clocked = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stop_clock(struct usba_udc *udc)
|
||||
{
|
||||
if (!udc->clocked)
|
||||
return;
|
||||
|
||||
clk_disable_unprepare(udc->hclk);
|
||||
clk_disable_unprepare(udc->pclk);
|
||||
|
||||
udc->clocked = false;
|
||||
}
|
||||
|
||||
static int usba_start(struct usba_udc *udc)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
ret = start_clock(udc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
toggle_bias(udc, 1);
|
||||
usba_writel(udc, CTRL, USBA_ENABLE_MASK);
|
||||
usba_int_enb_set(udc, USBA_END_OF_RESET);
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usba_stop(struct usba_udc *udc)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
udc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
reset_all_endpoints(udc);
|
||||
|
||||
/* This will also disable the DP pullup */
|
||||
toggle_bias(udc, 0);
|
||||
usba_writel(udc, CTRL, USBA_DISABLE_MASK);
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
||||
stop_clock(udc);
|
||||
}
|
||||
|
||||
static irqreturn_t usba_vbus_irq_thread(int irq, void *devid)
|
||||
{
|
||||
struct usba_udc *udc = devid;
|
||||
int vbus;
|
||||
@ -1747,35 +1812,22 @@ static irqreturn_t usba_vbus_irq(int irq, void *devid)
|
||||
/* debounce */
|
||||
udelay(10);
|
||||
|
||||
spin_lock(&udc->lock);
|
||||
|
||||
/* May happen if Vbus pin toggles during probe() */
|
||||
if (!udc->driver)
|
||||
goto out;
|
||||
mutex_lock(&udc->vbus_mutex);
|
||||
|
||||
vbus = vbus_is_present(udc);
|
||||
if (vbus != udc->vbus_prev) {
|
||||
if (vbus) {
|
||||
toggle_bias(udc, 1);
|
||||
usba_writel(udc, CTRL, USBA_ENABLE_MASK);
|
||||
usba_int_enb_set(udc, USBA_END_OF_RESET);
|
||||
usba_start(udc);
|
||||
} else {
|
||||
udc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
reset_all_endpoints(udc);
|
||||
toggle_bias(udc, 0);
|
||||
usba_writel(udc, CTRL, USBA_DISABLE_MASK);
|
||||
if (udc->driver->disconnect) {
|
||||
spin_unlock(&udc->lock);
|
||||
usba_stop(udc);
|
||||
|
||||
if (udc->driver->disconnect)
|
||||
udc->driver->disconnect(&udc->gadget);
|
||||
spin_lock(&udc->lock);
|
||||
}
|
||||
}
|
||||
udc->vbus_prev = vbus;
|
||||
}
|
||||
|
||||
out:
|
||||
spin_unlock(&udc->lock);
|
||||
|
||||
mutex_unlock(&udc->vbus_mutex);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
@ -1787,55 +1839,47 @@ static int atmel_usba_start(struct usb_gadget *gadget,
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
|
||||
udc->devstatus = 1 << USB_DEVICE_SELF_POWERED;
|
||||
udc->driver = driver;
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
||||
ret = clk_prepare_enable(udc->pclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = clk_prepare_enable(udc->hclk);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(udc->pclk);
|
||||
return ret;
|
||||
}
|
||||
mutex_lock(&udc->vbus_mutex);
|
||||
|
||||
udc->vbus_prev = 0;
|
||||
if (gpio_is_valid(udc->vbus_pin))
|
||||
enable_irq(gpio_to_irq(udc->vbus_pin));
|
||||
|
||||
/* If Vbus is present, enable the controller and wait for reset */
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
if (vbus_is_present(udc) && udc->vbus_prev == 0) {
|
||||
toggle_bias(udc, 1);
|
||||
usba_writel(udc, CTRL, USBA_ENABLE_MASK);
|
||||
usba_int_enb_set(udc, USBA_END_OF_RESET);
|
||||
udc->vbus_prev = vbus_is_present(udc);
|
||||
if (udc->vbus_prev) {
|
||||
ret = usba_start(udc);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
||||
mutex_unlock(&udc->vbus_mutex);
|
||||
return 0;
|
||||
|
||||
err:
|
||||
if (gpio_is_valid(udc->vbus_pin))
|
||||
disable_irq(gpio_to_irq(udc->vbus_pin));
|
||||
|
||||
mutex_unlock(&udc->vbus_mutex);
|
||||
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);
|
||||
udc->driver = NULL;
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int atmel_usba_stop(struct usb_gadget *gadget)
|
||||
{
|
||||
struct usba_udc *udc = container_of(gadget, struct usba_udc, gadget);
|
||||
unsigned long flags;
|
||||
|
||||
if (gpio_is_valid(udc->vbus_pin))
|
||||
disable_irq(gpio_to_irq(udc->vbus_pin));
|
||||
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
udc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
reset_all_endpoints(udc);
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
||||
/* This will also disable the DP pullup */
|
||||
toggle_bias(udc, 0);
|
||||
usba_writel(udc, CTRL, USBA_DISABLE_MASK);
|
||||
|
||||
clk_disable_unprepare(udc->hclk);
|
||||
clk_disable_unprepare(udc->pclk);
|
||||
usba_stop(udc);
|
||||
|
||||
udc->driver = NULL;
|
||||
|
||||
@ -2057,6 +2101,7 @@ static int usba_udc_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(hclk);
|
||||
|
||||
spin_lock_init(&udc->lock);
|
||||
mutex_init(&udc->vbus_mutex);
|
||||
udc->pdev = pdev;
|
||||
udc->pclk = pclk;
|
||||
udc->hclk = hclk;
|
||||
@ -2111,17 +2156,17 @@ static int usba_udc_probe(struct platform_device *pdev)
|
||||
|
||||
if (gpio_is_valid(udc->vbus_pin)) {
|
||||
if (!devm_gpio_request(&pdev->dev, udc->vbus_pin, "atmel_usba_udc")) {
|
||||
ret = devm_request_irq(&pdev->dev,
|
||||
gpio_to_irq(udc->vbus_pin),
|
||||
usba_vbus_irq, 0,
|
||||
irq_set_status_flags(gpio_to_irq(udc->vbus_pin),
|
||||
IRQ_NOAUTOEN);
|
||||
ret = devm_request_threaded_irq(&pdev->dev,
|
||||
gpio_to_irq(udc->vbus_pin), NULL,
|
||||
usba_vbus_irq_thread, IRQF_ONESHOT,
|
||||
"atmel_usba_udc", udc);
|
||||
if (ret) {
|
||||
udc->vbus_pin = -ENODEV;
|
||||
dev_warn(&udc->pdev->dev,
|
||||
"failed to request vbus irq; "
|
||||
"assuming always on\n");
|
||||
} else {
|
||||
disable_irq(gpio_to_irq(udc->vbus_pin));
|
||||
}
|
||||
} else {
|
||||
/* gpio_request fail so use -EINVAL for gpio_is_valid */
|
||||
@ -2132,6 +2177,7 @@ static int usba_udc_probe(struct platform_device *pdev)
|
||||
ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
|
||||
if (ret)
|
||||
return ret;
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
|
||||
usba_init_debugfs(udc);
|
||||
for (i = 1; i < udc->num_ep; i++)
|
||||
@ -2147,6 +2193,7 @@ static int __exit usba_udc_remove(struct platform_device *pdev)
|
||||
|
||||
udc = platform_get_drvdata(pdev);
|
||||
|
||||
device_init_wakeup(&pdev->dev, 0);
|
||||
usb_del_gadget_udc(&udc->gadget);
|
||||
|
||||
for (i = 1; i < udc->num_ep; i++)
|
||||
@ -2156,10 +2203,65 @@ static int __exit usba_udc_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int usba_udc_suspend(struct device *dev)
|
||||
{
|
||||
struct usba_udc *udc = dev_get_drvdata(dev);
|
||||
|
||||
/* Not started */
|
||||
if (!udc->driver)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&udc->vbus_mutex);
|
||||
|
||||
if (!device_may_wakeup(dev)) {
|
||||
usba_stop(udc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Device may wake up. We stay clocked if we failed
|
||||
* to request vbus irq, assuming always on.
|
||||
*/
|
||||
if (gpio_is_valid(udc->vbus_pin)) {
|
||||
usba_stop(udc);
|
||||
enable_irq_wake(gpio_to_irq(udc->vbus_pin));
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&udc->vbus_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usba_udc_resume(struct device *dev)
|
||||
{
|
||||
struct usba_udc *udc = dev_get_drvdata(dev);
|
||||
|
||||
/* Not started */
|
||||
if (!udc->driver)
|
||||
return 0;
|
||||
|
||||
if (device_may_wakeup(dev) && gpio_is_valid(udc->vbus_pin))
|
||||
disable_irq_wake(gpio_to_irq(udc->vbus_pin));
|
||||
|
||||
/* If Vbus is present, enable the controller and wait for reset */
|
||||
mutex_lock(&udc->vbus_mutex);
|
||||
udc->vbus_prev = vbus_is_present(udc);
|
||||
if (udc->vbus_prev)
|
||||
usba_start(udc);
|
||||
mutex_unlock(&udc->vbus_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(usba_udc_pm_ops, usba_udc_suspend, usba_udc_resume);
|
||||
|
||||
static struct platform_driver udc_driver = {
|
||||
.remove = __exit_p(usba_udc_remove),
|
||||
.driver = {
|
||||
.name = "atmel_usba_udc",
|
||||
.pm = &usba_udc_pm_ops,
|
||||
.of_match_table = of_match_ptr(atmel_udc_dt_ids),
|
||||
},
|
||||
};
|
||||
|
@ -191,18 +191,28 @@
|
||||
| USBA_BF(name, value))
|
||||
|
||||
/* Register access macros */
|
||||
#ifdef CONFIG_AVR32
|
||||
#define usba_io_readl __raw_readl
|
||||
#define usba_io_writel __raw_writel
|
||||
#define usba_io_writew __raw_writew
|
||||
#else
|
||||
#define usba_io_readl readl_relaxed
|
||||
#define usba_io_writel writel_relaxed
|
||||
#define usba_io_writew writew_relaxed
|
||||
#endif
|
||||
|
||||
#define usba_readl(udc, reg) \
|
||||
__raw_readl((udc)->regs + USBA_##reg)
|
||||
usba_io_readl((udc)->regs + USBA_##reg)
|
||||
#define usba_writel(udc, reg, value) \
|
||||
__raw_writel((value), (udc)->regs + USBA_##reg)
|
||||
usba_io_writel((value), (udc)->regs + USBA_##reg)
|
||||
#define usba_ep_readl(ep, reg) \
|
||||
__raw_readl((ep)->ep_regs + USBA_EPT_##reg)
|
||||
usba_io_readl((ep)->ep_regs + USBA_EPT_##reg)
|
||||
#define usba_ep_writel(ep, reg, value) \
|
||||
__raw_writel((value), (ep)->ep_regs + USBA_EPT_##reg)
|
||||
usba_io_writel((value), (ep)->ep_regs + USBA_EPT_##reg)
|
||||
#define usba_dma_readl(ep, reg) \
|
||||
__raw_readl((ep)->dma_regs + USBA_DMA_##reg)
|
||||
usba_io_readl((ep)->dma_regs + USBA_DMA_##reg)
|
||||
#define usba_dma_writel(ep, reg, value) \
|
||||
__raw_writel((value), (ep)->dma_regs + USBA_DMA_##reg)
|
||||
usba_io_writel((value), (ep)->dma_regs + USBA_DMA_##reg)
|
||||
|
||||
/* Calculate base address for a given endpoint or DMA controller */
|
||||
#define USBA_EPT_BASE(x) (0x100 + (x) * 0x20)
|
||||
@ -313,6 +323,9 @@ struct usba_udc {
|
||||
/* Protect hw registers from concurrent modifications */
|
||||
spinlock_t lock;
|
||||
|
||||
/* Mutex to prevent concurrent start or stop */
|
||||
struct mutex vbus_mutex;
|
||||
|
||||
void __iomem *regs;
|
||||
void __iomem *fifo;
|
||||
|
||||
@ -328,6 +341,7 @@ struct usba_udc {
|
||||
struct clk *hclk;
|
||||
struct usba_ep *usba_ep;
|
||||
bool bias_pulse_needed;
|
||||
bool clocked;
|
||||
|
||||
u16 devstatus;
|
||||
|
||||
|
@ -1923,7 +1923,7 @@ static inline void
|
||||
ss_hub_descriptor(struct usb_hub_descriptor *desc)
|
||||
{
|
||||
memset(desc, 0, sizeof *desc);
|
||||
desc->bDescriptorType = 0x2a;
|
||||
desc->bDescriptorType = USB_DT_SS_HUB;
|
||||
desc->bDescLength = 12;
|
||||
desc->wHubCharacteristics = cpu_to_le16(
|
||||
HUB_CHAR_INDV_PORT_LPSM |
|
||||
@ -1936,7 +1936,7 @@ ss_hub_descriptor(struct usb_hub_descriptor *desc)
|
||||
static inline void hub_descriptor(struct usb_hub_descriptor *desc)
|
||||
{
|
||||
memset(desc, 0, sizeof *desc);
|
||||
desc->bDescriptorType = 0x29;
|
||||
desc->bDescriptorType = USB_DT_HUB;
|
||||
desc->bDescLength = 9;
|
||||
desc->wHubCharacteristics = cpu_to_le16(
|
||||
HUB_CHAR_INDV_PORT_LPSM |
|
||||
@ -2631,7 +2631,7 @@ static int __init init(void)
|
||||
return -EINVAL;
|
||||
|
||||
if (mod_data.num < 1 || mod_data.num > MAX_NUM_UDC) {
|
||||
pr_err("Number of emulated UDC must be in range of 1…%d\n",
|
||||
pr_err("Number of emulated UDC must be in range of 1...%d\n",
|
||||
MAX_NUM_UDC);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -1024,35 +1024,79 @@ static const char proc_node_name [] = "driver/udc";
|
||||
static void dump_intmask(struct seq_file *m, const char *label, u32 mask)
|
||||
{
|
||||
/* int_status is the same format ... */
|
||||
seq_printf(m,
|
||||
"%s %05X =" FOURBITS EIGHTBITS EIGHTBITS "\n",
|
||||
label, mask,
|
||||
(mask & INT_PWRDETECT) ? " power" : "",
|
||||
(mask & INT_SYSERROR) ? " sys" : "",
|
||||
(mask & INT_MSTRDEND) ? " in-dma" : "",
|
||||
(mask & INT_MSTWRTMOUT) ? " wrtmo" : "",
|
||||
seq_printf(m, "%s %05X =" FOURBITS EIGHTBITS EIGHTBITS "\n",
|
||||
label, mask,
|
||||
(mask & INT_PWRDETECT) ? " power" : "",
|
||||
(mask & INT_SYSERROR) ? " sys" : "",
|
||||
(mask & INT_MSTRDEND) ? " in-dma" : "",
|
||||
(mask & INT_MSTWRTMOUT) ? " wrtmo" : "",
|
||||
|
||||
(mask & INT_MSTWREND) ? " out-dma" : "",
|
||||
(mask & INT_MSTWRSET) ? " wrset" : "",
|
||||
(mask & INT_ERR) ? " err" : "",
|
||||
(mask & INT_SOF) ? " sof" : "",
|
||||
(mask & INT_MSTWREND) ? " out-dma" : "",
|
||||
(mask & INT_MSTWRSET) ? " wrset" : "",
|
||||
(mask & INT_ERR) ? " err" : "",
|
||||
(mask & INT_SOF) ? " sof" : "",
|
||||
|
||||
(mask & INT_EP3NAK) ? " ep3nak" : "",
|
||||
(mask & INT_EP2NAK) ? " ep2nak" : "",
|
||||
(mask & INT_EP1NAK) ? " ep1nak" : "",
|
||||
(mask & INT_EP3DATASET) ? " ep3" : "",
|
||||
(mask & INT_EP3NAK) ? " ep3nak" : "",
|
||||
(mask & INT_EP2NAK) ? " ep2nak" : "",
|
||||
(mask & INT_EP1NAK) ? " ep1nak" : "",
|
||||
(mask & INT_EP3DATASET) ? " ep3" : "",
|
||||
|
||||
(mask & INT_EP2DATASET) ? " ep2" : "",
|
||||
(mask & INT_EP1DATASET) ? " ep1" : "",
|
||||
(mask & INT_STATUSNAK) ? " ep0snak" : "",
|
||||
(mask & INT_STATUS) ? " ep0status" : "",
|
||||
(mask & INT_EP2DATASET) ? " ep2" : "",
|
||||
(mask & INT_EP1DATASET) ? " ep1" : "",
|
||||
(mask & INT_STATUSNAK) ? " ep0snak" : "",
|
||||
(mask & INT_STATUS) ? " ep0status" : "",
|
||||
|
||||
(mask & INT_SETUP) ? " setup" : "",
|
||||
(mask & INT_ENDPOINT0) ? " ep0" : "",
|
||||
(mask & INT_USBRESET) ? " reset" : "",
|
||||
(mask & INT_SUSPEND) ? " suspend" : "");
|
||||
(mask & INT_SETUP) ? " setup" : "",
|
||||
(mask & INT_ENDPOINT0) ? " ep0" : "",
|
||||
(mask & INT_USBRESET) ? " reset" : "",
|
||||
(mask & INT_SUSPEND) ? " suspend" : "");
|
||||
}
|
||||
|
||||
static const char *udc_ep_state(enum ep0state state)
|
||||
{
|
||||
switch (state) {
|
||||
case EP0_DISCONNECT:
|
||||
return "ep0_disconnect";
|
||||
case EP0_IDLE:
|
||||
return "ep0_idle";
|
||||
case EP0_IN:
|
||||
return "ep0_in";
|
||||
case EP0_OUT:
|
||||
return "ep0_out";
|
||||
case EP0_STATUS:
|
||||
return "ep0_status";
|
||||
case EP0_STALL:
|
||||
return "ep0_stall";
|
||||
case EP0_SUSPEND:
|
||||
return "ep0_suspend";
|
||||
}
|
||||
|
||||
return "ep0_?";
|
||||
}
|
||||
|
||||
static const char *udc_ep_status(u32 status)
|
||||
{
|
||||
switch (status & EPxSTATUS_EP_MASK) {
|
||||
case EPxSTATUS_EP_READY:
|
||||
return "ready";
|
||||
case EPxSTATUS_EP_DATAIN:
|
||||
return "packet";
|
||||
case EPxSTATUS_EP_FULL:
|
||||
return "full";
|
||||
case EPxSTATUS_EP_TX_ERR: /* host will retry */
|
||||
return "tx_err";
|
||||
case EPxSTATUS_EP_RX_ERR:
|
||||
return "rx_err";
|
||||
case EPxSTATUS_EP_BUSY: /* ep0 only */
|
||||
return "busy";
|
||||
case EPxSTATUS_EP_STALL:
|
||||
return "stall";
|
||||
case EPxSTATUS_EP_INVALID: /* these "can't happen" */
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
return "?";
|
||||
}
|
||||
|
||||
static int udc_proc_read(struct seq_file *m, void *v)
|
||||
{
|
||||
@ -1068,29 +1112,18 @@ static int udc_proc_read(struct seq_file *m, void *v)
|
||||
tmp = readl(®s->power_detect);
|
||||
is_usb_connected = tmp & PW_DETECT;
|
||||
seq_printf(m,
|
||||
"%s - %s\n"
|
||||
"%s version: %s %s\n"
|
||||
"Gadget driver: %s\n"
|
||||
"Host %s, %s\n"
|
||||
"\n",
|
||||
pci_name(dev->pdev), driver_desc,
|
||||
driver_name, DRIVER_VERSION, dmastr(),
|
||||
dev->driver ? dev->driver->driver.name : "(none)",
|
||||
is_usb_connected
|
||||
? ((tmp & PW_PULLUP) ? "full speed" : "powered")
|
||||
: "disconnected",
|
||||
({const char *state;
|
||||
switch(dev->ep0state){
|
||||
case EP0_DISCONNECT: state = "ep0_disconnect"; break;
|
||||
case EP0_IDLE: state = "ep0_idle"; break;
|
||||
case EP0_IN: state = "ep0_in"; break;
|
||||
case EP0_OUT: state = "ep0_out"; break;
|
||||
case EP0_STATUS: state = "ep0_status"; break;
|
||||
case EP0_STALL: state = "ep0_stall"; break;
|
||||
case EP0_SUSPEND: state = "ep0_suspend"; break;
|
||||
default: state = "ep0_?"; break;
|
||||
} state; })
|
||||
);
|
||||
"%s - %s\n"
|
||||
"%s version: %s %s\n"
|
||||
"Gadget driver: %s\n"
|
||||
"Host %s, %s\n"
|
||||
"\n",
|
||||
pci_name(dev->pdev), driver_desc,
|
||||
driver_name, DRIVER_VERSION, dmastr(),
|
||||
dev->driver ? dev->driver->driver.name : "(none)",
|
||||
is_usb_connected
|
||||
? ((tmp & PW_PULLUP) ? "full speed" : "powered")
|
||||
: "disconnected",
|
||||
udc_ep_state(dev->ep0state));
|
||||
|
||||
dump_intmask(m, "int_status", readl(®s->int_status));
|
||||
dump_intmask(m, "int_enable", readl(®s->int_enable));
|
||||
@ -1099,31 +1132,30 @@ static int udc_proc_read(struct seq_file *m, void *v)
|
||||
goto done;
|
||||
|
||||
/* registers for (active) device and ep0 */
|
||||
if (seq_printf(m, "\nirqs %lu\ndataset %02x "
|
||||
"single.bcs %02x.%02x state %x addr %u\n",
|
||||
dev->irqs, readl(®s->DataSet),
|
||||
readl(®s->EPxSingle), readl(®s->EPxBCS),
|
||||
readl(®s->UsbState),
|
||||
readl(®s->address)) < 0)
|
||||
seq_printf(m, "\nirqs %lu\ndataset %02x single.bcs %02x.%02x state %x addr %u\n",
|
||||
dev->irqs, readl(®s->DataSet),
|
||||
readl(®s->EPxSingle), readl(®s->EPxBCS),
|
||||
readl(®s->UsbState),
|
||||
readl(®s->address));
|
||||
if (seq_has_overflowed(m))
|
||||
goto done;
|
||||
|
||||
tmp = readl(®s->dma_master);
|
||||
if (seq_printf(m,
|
||||
"dma %03X =" EIGHTBITS "%s %s\n", tmp,
|
||||
(tmp & MST_EOPB_DIS) ? " eopb-" : "",
|
||||
(tmp & MST_EOPB_ENA) ? " eopb+" : "",
|
||||
(tmp & MST_TIMEOUT_DIS) ? " tmo-" : "",
|
||||
(tmp & MST_TIMEOUT_ENA) ? " tmo+" : "",
|
||||
seq_printf(m, "dma %03X =" EIGHTBITS "%s %s\n",
|
||||
tmp,
|
||||
(tmp & MST_EOPB_DIS) ? " eopb-" : "",
|
||||
(tmp & MST_EOPB_ENA) ? " eopb+" : "",
|
||||
(tmp & MST_TIMEOUT_DIS) ? " tmo-" : "",
|
||||
(tmp & MST_TIMEOUT_ENA) ? " tmo+" : "",
|
||||
|
||||
(tmp & MST_RD_EOPB) ? " eopb" : "",
|
||||
(tmp & MST_RD_RESET) ? " in_reset" : "",
|
||||
(tmp & MST_WR_RESET) ? " out_reset" : "",
|
||||
(tmp & MST_RD_ENA) ? " IN" : "",
|
||||
(tmp & MST_RD_EOPB) ? " eopb" : "",
|
||||
(tmp & MST_RD_RESET) ? " in_reset" : "",
|
||||
(tmp & MST_WR_RESET) ? " out_reset" : "",
|
||||
(tmp & MST_RD_ENA) ? " IN" : "",
|
||||
|
||||
(tmp & MST_WR_ENA) ? " OUT" : "",
|
||||
(tmp & MST_CONNECTION)
|
||||
? "ep1in/ep2out"
|
||||
: "ep1out/ep2in") < 0)
|
||||
(tmp & MST_WR_ENA) ? " OUT" : "",
|
||||
(tmp & MST_CONNECTION) ? "ep1in/ep2out" : "ep1out/ep2in");
|
||||
if (seq_has_overflowed(m))
|
||||
goto done;
|
||||
|
||||
/* dump endpoint queues */
|
||||
@ -1135,44 +1167,23 @@ static int udc_proc_read(struct seq_file *m, void *v)
|
||||
continue;
|
||||
|
||||
tmp = readl(ep->reg_status);
|
||||
if (seq_printf(m,
|
||||
"%s %s max %u %s, irqs %lu, "
|
||||
"status %02x (%s) " FOURBITS "\n",
|
||||
ep->ep.name,
|
||||
ep->is_in ? "in" : "out",
|
||||
ep->ep.maxpacket,
|
||||
ep->dma ? "dma" : "pio",
|
||||
ep->irqs,
|
||||
tmp, ({ char *s;
|
||||
switch (tmp & EPxSTATUS_EP_MASK) {
|
||||
case EPxSTATUS_EP_READY:
|
||||
s = "ready"; break;
|
||||
case EPxSTATUS_EP_DATAIN:
|
||||
s = "packet"; break;
|
||||
case EPxSTATUS_EP_FULL:
|
||||
s = "full"; break;
|
||||
case EPxSTATUS_EP_TX_ERR: // host will retry
|
||||
s = "tx_err"; break;
|
||||
case EPxSTATUS_EP_RX_ERR:
|
||||
s = "rx_err"; break;
|
||||
case EPxSTATUS_EP_BUSY: /* ep0 only */
|
||||
s = "busy"; break;
|
||||
case EPxSTATUS_EP_STALL:
|
||||
s = "stall"; break;
|
||||
case EPxSTATUS_EP_INVALID: // these "can't happen"
|
||||
s = "invalid"; break;
|
||||
default:
|
||||
s = "?"; break;
|
||||
} s; }),
|
||||
(tmp & EPxSTATUS_TOGGLE) ? "data1" : "data0",
|
||||
(tmp & EPxSTATUS_SUSPEND) ? " suspend" : "",
|
||||
(tmp & EPxSTATUS_FIFO_DISABLE) ? " disable" : "",
|
||||
(tmp & EPxSTATUS_STAGE_ERROR) ? " ep0stat" : ""
|
||||
) < 0)
|
||||
seq_printf(m, "%s %s max %u %s, irqs %lu, status %02x (%s) " FOURBITS "\n",
|
||||
ep->ep.name,
|
||||
ep->is_in ? "in" : "out",
|
||||
ep->ep.maxpacket,
|
||||
ep->dma ? "dma" : "pio",
|
||||
ep->irqs,
|
||||
tmp, udc_ep_status(tmp),
|
||||
(tmp & EPxSTATUS_TOGGLE) ? "data1" : "data0",
|
||||
(tmp & EPxSTATUS_SUSPEND) ? " suspend" : "",
|
||||
(tmp & EPxSTATUS_FIFO_DISABLE) ? " disable" : "",
|
||||
(tmp & EPxSTATUS_STAGE_ERROR) ? " ep0stat" : "");
|
||||
if (seq_has_overflowed(m))
|
||||
goto done;
|
||||
|
||||
if (list_empty(&ep->queue)) {
|
||||
if (seq_puts(m, "\t(nothing queued)\n") < 0)
|
||||
seq_puts(m, "\t(nothing queued)\n");
|
||||
if (seq_has_overflowed(m))
|
||||
goto done;
|
||||
continue;
|
||||
}
|
||||
@ -1187,10 +1198,10 @@ static int udc_proc_read(struct seq_file *m, void *v)
|
||||
} else
|
||||
tmp = req->req.actual;
|
||||
|
||||
if (seq_printf(m,
|
||||
"\treq %p len %u/%u buf %p\n",
|
||||
&req->req, tmp, req->req.length,
|
||||
req->req.buf) < 0)
|
||||
seq_printf(m, "\treq %p len %u/%u buf %p\n",
|
||||
&req->req, tmp, req->req.length,
|
||||
req->req.buf);
|
||||
if (seq_has_overflowed(m))
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
@ -1803,23 +1803,14 @@ static int lpc32xx_ep_queue(struct usb_ep *_ep,
|
||||
req = container_of(_req, struct lpc32xx_request, req);
|
||||
ep = container_of(_ep, struct lpc32xx_ep, ep);
|
||||
|
||||
if (!_req || !_req->complete || !_req->buf ||
|
||||
if (!_ep || !_req || !_req->complete || !_req->buf ||
|
||||
!list_empty(&req->queue))
|
||||
return -EINVAL;
|
||||
|
||||
udc = ep->udc;
|
||||
|
||||
if (!_ep) {
|
||||
dev_dbg(udc->dev, "invalid ep\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
if ((!udc) || (!udc->driver) ||
|
||||
(udc->gadget.speed == USB_SPEED_UNKNOWN)) {
|
||||
dev_dbg(udc->dev, "invalid device\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (udc->gadget.speed == USB_SPEED_UNKNOWN)
|
||||
return -EPIPE;
|
||||
|
||||
if (ep->lep) {
|
||||
struct lpc32xx_usbd_dd_gad *dd;
|
||||
|
@ -80,6 +80,13 @@ static const char *const ep_name[] = {
|
||||
"ep-e", "ep-f", "ep-g", "ep-h",
|
||||
};
|
||||
|
||||
/* Endpoint names for usb3380 advance mode */
|
||||
static const char *const ep_name_adv[] = {
|
||||
ep0name,
|
||||
"ep1in", "ep2out", "ep3in", "ep4out",
|
||||
"ep1out", "ep2in", "ep3out", "ep4in",
|
||||
};
|
||||
|
||||
/* mode 0 == ep-{a,b,c,d} 1K fifo each
|
||||
* mode 1 == ep-{a,b} 2K fifo each, ep-{c,d} unavailable
|
||||
* mode 2 == ep-a 2K fifo, ep-{b,c} 1K each, ep-d unavailable
|
||||
@ -138,31 +145,44 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
|
||||
u32 max, tmp;
|
||||
unsigned long flags;
|
||||
static const u32 ep_key[9] = { 1, 0, 1, 0, 1, 1, 0, 1, 0 };
|
||||
int ret = 0;
|
||||
|
||||
ep = container_of(_ep, struct net2280_ep, ep);
|
||||
if (!_ep || !desc || ep->desc || _ep->name == ep0name ||
|
||||
desc->bDescriptorType != USB_DT_ENDPOINT)
|
||||
desc->bDescriptorType != USB_DT_ENDPOINT) {
|
||||
pr_err("%s: failed at line=%d\n", __func__, __LINE__);
|
||||
return -EINVAL;
|
||||
}
|
||||
dev = ep->dev;
|
||||
if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
|
||||
return -ESHUTDOWN;
|
||||
if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
|
||||
ret = -ESHUTDOWN;
|
||||
goto print_err;
|
||||
}
|
||||
|
||||
/* erratum 0119 workaround ties up an endpoint number */
|
||||
if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE)
|
||||
return -EDOM;
|
||||
if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE) {
|
||||
ret = -EDOM;
|
||||
goto print_err;
|
||||
}
|
||||
|
||||
if (dev->quirks & PLX_SUPERSPEED) {
|
||||
if ((desc->bEndpointAddress & 0x0f) >= 0x0c)
|
||||
return -EDOM;
|
||||
if ((desc->bEndpointAddress & 0x0f) >= 0x0c) {
|
||||
ret = -EDOM;
|
||||
goto print_err;
|
||||
}
|
||||
ep->is_in = !!usb_endpoint_dir_in(desc);
|
||||
if (dev->enhanced_mode && ep->is_in && ep_key[ep->num])
|
||||
return -EINVAL;
|
||||
if (dev->enhanced_mode && ep->is_in && ep_key[ep->num]) {
|
||||
ret = -EINVAL;
|
||||
goto print_err;
|
||||
}
|
||||
}
|
||||
|
||||
/* sanity check ep-e/ep-f since their fifos are small */
|
||||
max = usb_endpoint_maxp(desc) & 0x1fff;
|
||||
if (ep->num > 4 && max > 64 && (dev->quirks & PLX_LEGACY))
|
||||
return -ERANGE;
|
||||
if (ep->num > 4 && max > 64 && (dev->quirks & PLX_LEGACY)) {
|
||||
ret = -ERANGE;
|
||||
goto print_err;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
_ep->maxpacket = max & 0x7ff;
|
||||
@ -192,7 +212,8 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
|
||||
(dev->gadget.speed == USB_SPEED_HIGH && max != 512) ||
|
||||
(dev->gadget.speed == USB_SPEED_FULL && max > 64)) {
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
return -ERANGE;
|
||||
ret = -ERANGE;
|
||||
goto print_err;
|
||||
}
|
||||
}
|
||||
ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC);
|
||||
@ -271,7 +292,11 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
|
||||
|
||||
/* pci writes may still be posted */
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
return 0;
|
||||
return ret;
|
||||
|
||||
print_err:
|
||||
dev_err(&ep->dev->pdev->dev, "%s: error=%d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int handshake(u32 __iomem *ptr, u32 mask, u32 done, int usec)
|
||||
@ -426,9 +451,10 @@ static int net2280_disable(struct usb_ep *_ep)
|
||||
unsigned long flags;
|
||||
|
||||
ep = container_of(_ep, struct net2280_ep, ep);
|
||||
if (!_ep || !ep->desc || _ep->name == ep0name)
|
||||
if (!_ep || !ep->desc || _ep->name == ep0name) {
|
||||
pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep);
|
||||
return -EINVAL;
|
||||
|
||||
}
|
||||
spin_lock_irqsave(&ep->dev->lock, flags);
|
||||
nuke(ep);
|
||||
|
||||
@ -458,8 +484,10 @@ static struct usb_request
|
||||
struct net2280_ep *ep;
|
||||
struct net2280_request *req;
|
||||
|
||||
if (!_ep)
|
||||
if (!_ep) {
|
||||
pr_err("%s: Invalid ep\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
ep = container_of(_ep, struct net2280_ep, ep);
|
||||
|
||||
req = kzalloc(sizeof(*req), gfp_flags);
|
||||
@ -491,8 +519,11 @@ static void net2280_free_request(struct usb_ep *_ep, struct usb_request *_req)
|
||||
struct net2280_request *req;
|
||||
|
||||
ep = container_of(_ep, struct net2280_ep, ep);
|
||||
if (!_ep || !_req)
|
||||
if (!_ep || !_req) {
|
||||
dev_err(&ep->dev->pdev->dev, "%s: Inavlid ep=%p or req=%p\n",
|
||||
__func__, _ep, _req);
|
||||
return;
|
||||
}
|
||||
|
||||
req = container_of(_req, struct net2280_request, req);
|
||||
WARN_ON(!list_empty(&req->queue));
|
||||
@ -896,35 +927,44 @@ net2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
|
||||
struct net2280_ep *ep;
|
||||
struct net2280 *dev;
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
/* we always require a cpu-view buffer, so that we can
|
||||
* always use pio (as fallback or whatever).
|
||||
*/
|
||||
ep = container_of(_ep, struct net2280_ep, ep);
|
||||
if (!_ep || (!ep->desc && ep->num != 0)) {
|
||||
pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep);
|
||||
return -EINVAL;
|
||||
}
|
||||
req = container_of(_req, struct net2280_request, req);
|
||||
if (!_req || !_req->complete || !_req->buf ||
|
||||
!list_empty(&req->queue))
|
||||
return -EINVAL;
|
||||
if (_req->length > (~0 & DMA_BYTE_COUNT_MASK))
|
||||
return -EDOM;
|
||||
ep = container_of(_ep, struct net2280_ep, ep);
|
||||
if (!_ep || (!ep->desc && ep->num != 0))
|
||||
return -EINVAL;
|
||||
!list_empty(&req->queue)) {
|
||||
ret = -EINVAL;
|
||||
goto print_err;
|
||||
}
|
||||
if (_req->length > (~0 & DMA_BYTE_COUNT_MASK)) {
|
||||
ret = -EDOM;
|
||||
goto print_err;
|
||||
}
|
||||
dev = ep->dev;
|
||||
if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
|
||||
return -ESHUTDOWN;
|
||||
if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
|
||||
ret = -ESHUTDOWN;
|
||||
goto print_err;
|
||||
}
|
||||
|
||||
/* FIXME implement PIO fallback for ZLPs with DMA */
|
||||
if (ep->dma && _req->length == 0)
|
||||
return -EOPNOTSUPP;
|
||||
if (ep->dma && _req->length == 0) {
|
||||
ret = -EOPNOTSUPP;
|
||||
goto print_err;
|
||||
}
|
||||
|
||||
/* set up dma mapping in case the caller didn't */
|
||||
if (ep->dma) {
|
||||
int ret;
|
||||
|
||||
ret = usb_gadget_map_request(&dev->gadget, _req,
|
||||
ep->is_in);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto print_err;
|
||||
}
|
||||
|
||||
ep_vdbg(dev, "%s queue req %p, len %d buf %p\n",
|
||||
@ -1013,7 +1053,11 @@ done:
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
||||
/* pci writes may still be posted */
|
||||
return 0;
|
||||
return ret;
|
||||
|
||||
print_err:
|
||||
dev_err(&ep->dev->pdev->dev, "%s: error=%d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void
|
||||
@ -1134,8 +1178,11 @@ static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
||||
int stopped;
|
||||
|
||||
ep = container_of(_ep, struct net2280_ep, ep);
|
||||
if (!_ep || (!ep->desc && ep->num != 0) || !_req)
|
||||
if (!_ep || (!ep->desc && ep->num != 0) || !_req) {
|
||||
pr_err("%s: Invalid ep=%p or ep->desc or req=%p\n",
|
||||
__func__, _ep, _req);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&ep->dev->lock, flags);
|
||||
stopped = ep->stopped;
|
||||
@ -1157,6 +1204,8 @@ static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
||||
}
|
||||
if (&req->req != _req) {
|
||||
spin_unlock_irqrestore(&ep->dev->lock, flags);
|
||||
dev_err(&ep->dev->pdev->dev, "%s: Request mismatch\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -1214,20 +1263,28 @@ net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged)
|
||||
int retval = 0;
|
||||
|
||||
ep = container_of(_ep, struct net2280_ep, ep);
|
||||
if (!_ep || (!ep->desc && ep->num != 0))
|
||||
if (!_ep || (!ep->desc && ep->num != 0)) {
|
||||
pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep);
|
||||
return -EINVAL;
|
||||
if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) {
|
||||
retval = -ESHUTDOWN;
|
||||
goto print_err;
|
||||
}
|
||||
if (ep->desc /* not ep0 */ && (ep->desc->bmAttributes & 0x03)
|
||||
== USB_ENDPOINT_XFER_ISOC)
|
||||
return -EINVAL;
|
||||
== USB_ENDPOINT_XFER_ISOC) {
|
||||
retval = -EINVAL;
|
||||
goto print_err;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&ep->dev->lock, flags);
|
||||
if (!list_empty(&ep->queue))
|
||||
if (!list_empty(&ep->queue)) {
|
||||
retval = -EAGAIN;
|
||||
else if (ep->is_in && value && net2280_fifo_status(_ep) != 0)
|
||||
goto print_unlock;
|
||||
} else if (ep->is_in && value && net2280_fifo_status(_ep) != 0) {
|
||||
retval = -EAGAIN;
|
||||
else {
|
||||
goto print_unlock;
|
||||
} else {
|
||||
ep_vdbg(ep->dev, "%s %s %s\n", _ep->name,
|
||||
value ? "set" : "clear",
|
||||
wedged ? "wedge" : "halt");
|
||||
@ -1251,6 +1308,12 @@ net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged)
|
||||
spin_unlock_irqrestore(&ep->dev->lock, flags);
|
||||
|
||||
return retval;
|
||||
|
||||
print_unlock:
|
||||
spin_unlock_irqrestore(&ep->dev->lock, flags);
|
||||
print_err:
|
||||
dev_err(&ep->dev->pdev->dev, "%s: error=%d\n", __func__, retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int net2280_set_halt(struct usb_ep *_ep, int value)
|
||||
@ -1260,8 +1323,10 @@ static int net2280_set_halt(struct usb_ep *_ep, int value)
|
||||
|
||||
static int net2280_set_wedge(struct usb_ep *_ep)
|
||||
{
|
||||
if (!_ep || _ep->name == ep0name)
|
||||
if (!_ep || _ep->name == ep0name) {
|
||||
pr_err("%s: Invalid ep=%p or ep0\n", __func__, _ep);
|
||||
return -EINVAL;
|
||||
}
|
||||
return net2280_set_halt_and_wedge(_ep, 1, 1);
|
||||
}
|
||||
|
||||
@ -1271,14 +1336,22 @@ static int net2280_fifo_status(struct usb_ep *_ep)
|
||||
u32 avail;
|
||||
|
||||
ep = container_of(_ep, struct net2280_ep, ep);
|
||||
if (!_ep || (!ep->desc && ep->num != 0))
|
||||
if (!_ep || (!ep->desc && ep->num != 0)) {
|
||||
pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep);
|
||||
return -ENODEV;
|
||||
if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
|
||||
}
|
||||
if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) {
|
||||
dev_err(&ep->dev->pdev->dev,
|
||||
"%s: Invalid driver=%p or speed=%d\n",
|
||||
__func__, ep->dev->driver, ep->dev->gadget.speed);
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
|
||||
avail = readl(&ep->regs->ep_avail) & (BIT(12) - 1);
|
||||
if (avail > ep->fifo_size)
|
||||
if (avail > ep->fifo_size) {
|
||||
dev_err(&ep->dev->pdev->dev, "%s: Fifo overflow\n", __func__);
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
if (ep->is_in)
|
||||
avail = ep->fifo_size - avail;
|
||||
return avail;
|
||||
@ -1289,10 +1362,16 @@ static void net2280_fifo_flush(struct usb_ep *_ep)
|
||||
struct net2280_ep *ep;
|
||||
|
||||
ep = container_of(_ep, struct net2280_ep, ep);
|
||||
if (!_ep || (!ep->desc && ep->num != 0))
|
||||
if (!_ep || (!ep->desc && ep->num != 0)) {
|
||||
pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep);
|
||||
return;
|
||||
if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
|
||||
}
|
||||
if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) {
|
||||
dev_err(&ep->dev->pdev->dev,
|
||||
"%s: Invalid driver=%p or speed=%d\n",
|
||||
__func__, ep->dev->driver, ep->dev->gadget.speed);
|
||||
return;
|
||||
}
|
||||
|
||||
writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat);
|
||||
(void) readl(&ep->regs->ep_rsp);
|
||||
@ -1977,7 +2056,7 @@ static void usb_reinit_338x(struct net2280 *dev)
|
||||
for (i = 0; i < dev->n_ep; i++) {
|
||||
struct net2280_ep *ep = &dev->ep[i];
|
||||
|
||||
ep->ep.name = ep_name[i];
|
||||
ep->ep.name = dev->enhanced_mode ? ep_name_adv[i] : ep_name[i];
|
||||
ep->dev = dev;
|
||||
ep->num = i;
|
||||
|
||||
@ -1989,11 +2068,9 @@ static void usb_reinit_338x(struct net2280 *dev)
|
||||
ep->regs = (struct net2280_ep_regs __iomem *)
|
||||
(((void __iomem *)&dev->epregs[ne[i]]) +
|
||||
ep_reg_addr[i]);
|
||||
ep->fiforegs = &dev->fiforegs[i];
|
||||
} else {
|
||||
ep->cfg = &dev->epregs[i];
|
||||
ep->regs = &dev->epregs[i];
|
||||
ep->fiforegs = &dev->fiforegs[i];
|
||||
}
|
||||
|
||||
ep->fifo_size = (i != 0) ? 2048 : 512;
|
||||
@ -2186,7 +2263,6 @@ static int net2280_start(struct usb_gadget *_gadget,
|
||||
dev->ep[i].irqs = 0;
|
||||
|
||||
/* hook up the driver ... */
|
||||
dev->softconnect = 1;
|
||||
driver->driver.bus = NULL;
|
||||
dev->driver = driver;
|
||||
|
||||
@ -3052,6 +3128,8 @@ next_endpoints:
|
||||
BIT(PCI_RETRY_ABORT_INTERRUPT))
|
||||
|
||||
static void handle_stat1_irqs(struct net2280 *dev, u32 stat)
|
||||
__releases(dev->lock)
|
||||
__acquires(dev->lock)
|
||||
{
|
||||
struct net2280_ep *ep;
|
||||
u32 tmp, num, mask, scratch;
|
||||
@ -3373,8 +3451,6 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
u32 usbstat;
|
||||
dev->usb_ext = (struct usb338x_usb_ext_regs __iomem *)
|
||||
(base + 0x00b4);
|
||||
dev->fiforegs = (struct usb338x_fifo_regs __iomem *)
|
||||
(base + 0x0500);
|
||||
dev->llregs = (struct usb338x_ll_regs __iomem *)
|
||||
(base + 0x0700);
|
||||
dev->ll_lfps_regs = (struct usb338x_ll_lfps_regs __iomem *)
|
||||
|
@ -96,7 +96,6 @@ struct net2280_ep {
|
||||
struct net2280_ep_regs __iomem *regs;
|
||||
struct net2280_dma_regs __iomem *dma;
|
||||
struct net2280_dma *dummy;
|
||||
struct usb338x_fifo_regs __iomem *fiforegs;
|
||||
dma_addr_t td_dma; /* of dummy */
|
||||
struct net2280 *dev;
|
||||
unsigned long irqs;
|
||||
@ -181,7 +180,6 @@ struct net2280 {
|
||||
struct net2280_dma_regs __iomem *dma;
|
||||
struct net2280_dep_regs __iomem *dep;
|
||||
struct net2280_ep_regs __iomem *epregs;
|
||||
struct usb338x_fifo_regs __iomem *fiforegs;
|
||||
struct usb338x_ll_regs __iomem *llregs;
|
||||
struct usb338x_ll_lfps_regs __iomem *ll_lfps_regs;
|
||||
struct usb338x_ll_tsn_regs __iomem *ll_tsn_regs;
|
||||
|
@ -93,50 +93,46 @@ static void handle_ep(struct pxa_ep *ep);
|
||||
static int state_dbg_show(struct seq_file *s, void *p)
|
||||
{
|
||||
struct pxa_udc *udc = s->private;
|
||||
int pos = 0, ret;
|
||||
u32 tmp;
|
||||
|
||||
ret = -ENODEV;
|
||||
if (!udc->driver)
|
||||
goto out;
|
||||
return -ENODEV;
|
||||
|
||||
/* basic device status */
|
||||
pos += seq_printf(s, DRIVER_DESC "\n"
|
||||
"%s version: %s\nGadget driver: %s\n",
|
||||
driver_name, DRIVER_VERSION,
|
||||
udc->driver ? udc->driver->driver.name : "(none)");
|
||||
seq_printf(s, DRIVER_DESC "\n"
|
||||
"%s version: %s\n"
|
||||
"Gadget driver: %s\n",
|
||||
driver_name, DRIVER_VERSION,
|
||||
udc->driver ? udc->driver->driver.name : "(none)");
|
||||
|
||||
tmp = udc_readl(udc, UDCCR);
|
||||
pos += seq_printf(s,
|
||||
"udccr=0x%0x(%s%s%s%s%s%s%s%s%s%s), "
|
||||
"con=%d,inter=%d,altinter=%d\n", tmp,
|
||||
(tmp & UDCCR_OEN) ? " oen":"",
|
||||
(tmp & UDCCR_AALTHNP) ? " aalthnp":"",
|
||||
(tmp & UDCCR_AHNP) ? " rem" : "",
|
||||
(tmp & UDCCR_BHNP) ? " rstir" : "",
|
||||
(tmp & UDCCR_DWRE) ? " dwre" : "",
|
||||
(tmp & UDCCR_SMAC) ? " smac" : "",
|
||||
(tmp & UDCCR_EMCE) ? " emce" : "",
|
||||
(tmp & UDCCR_UDR) ? " udr" : "",
|
||||
(tmp & UDCCR_UDA) ? " uda" : "",
|
||||
(tmp & UDCCR_UDE) ? " ude" : "",
|
||||
(tmp & UDCCR_ACN) >> UDCCR_ACN_S,
|
||||
(tmp & UDCCR_AIN) >> UDCCR_AIN_S,
|
||||
(tmp & UDCCR_AAISN) >> UDCCR_AAISN_S);
|
||||
seq_printf(s,
|
||||
"udccr=0x%0x(%s%s%s%s%s%s%s%s%s%s), con=%d,inter=%d,altinter=%d\n",
|
||||
tmp,
|
||||
(tmp & UDCCR_OEN) ? " oen":"",
|
||||
(tmp & UDCCR_AALTHNP) ? " aalthnp":"",
|
||||
(tmp & UDCCR_AHNP) ? " rem" : "",
|
||||
(tmp & UDCCR_BHNP) ? " rstir" : "",
|
||||
(tmp & UDCCR_DWRE) ? " dwre" : "",
|
||||
(tmp & UDCCR_SMAC) ? " smac" : "",
|
||||
(tmp & UDCCR_EMCE) ? " emce" : "",
|
||||
(tmp & UDCCR_UDR) ? " udr" : "",
|
||||
(tmp & UDCCR_UDA) ? " uda" : "",
|
||||
(tmp & UDCCR_UDE) ? " ude" : "",
|
||||
(tmp & UDCCR_ACN) >> UDCCR_ACN_S,
|
||||
(tmp & UDCCR_AIN) >> UDCCR_AIN_S,
|
||||
(tmp & UDCCR_AAISN) >> UDCCR_AAISN_S);
|
||||
/* registers for device and ep0 */
|
||||
pos += seq_printf(s, "udcicr0=0x%08x udcicr1=0x%08x\n",
|
||||
udc_readl(udc, UDCICR0), udc_readl(udc, UDCICR1));
|
||||
pos += seq_printf(s, "udcisr0=0x%08x udcisr1=0x%08x\n",
|
||||
udc_readl(udc, UDCISR0), udc_readl(udc, UDCISR1));
|
||||
pos += seq_printf(s, "udcfnr=%d\n", udc_readl(udc, UDCFNR));
|
||||
pos += seq_printf(s, "irqs: reset=%lu, suspend=%lu, resume=%lu, "
|
||||
"reconfig=%lu\n",
|
||||
udc->stats.irqs_reset, udc->stats.irqs_suspend,
|
||||
udc->stats.irqs_resume, udc->stats.irqs_reconfig);
|
||||
seq_printf(s, "udcicr0=0x%08x udcicr1=0x%08x\n",
|
||||
udc_readl(udc, UDCICR0), udc_readl(udc, UDCICR1));
|
||||
seq_printf(s, "udcisr0=0x%08x udcisr1=0x%08x\n",
|
||||
udc_readl(udc, UDCISR0), udc_readl(udc, UDCISR1));
|
||||
seq_printf(s, "udcfnr=%d\n", udc_readl(udc, UDCFNR));
|
||||
seq_printf(s, "irqs: reset=%lu, suspend=%lu, resume=%lu, reconfig=%lu\n",
|
||||
udc->stats.irqs_reset, udc->stats.irqs_suspend,
|
||||
udc->stats.irqs_resume, udc->stats.irqs_reconfig);
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int queues_dbg_show(struct seq_file *s, void *p)
|
||||
@ -144,75 +140,67 @@ static int queues_dbg_show(struct seq_file *s, void *p)
|
||||
struct pxa_udc *udc = s->private;
|
||||
struct pxa_ep *ep;
|
||||
struct pxa27x_request *req;
|
||||
int pos = 0, i, maxpkt, ret;
|
||||
int i, maxpkt;
|
||||
|
||||
ret = -ENODEV;
|
||||
if (!udc->driver)
|
||||
goto out;
|
||||
return -ENODEV;
|
||||
|
||||
/* dump endpoint queues */
|
||||
for (i = 0; i < NR_PXA_ENDPOINTS; i++) {
|
||||
ep = &udc->pxa_ep[i];
|
||||
maxpkt = ep->fifo_size;
|
||||
pos += seq_printf(s, "%-12s max_pkt=%d %s\n",
|
||||
EPNAME(ep), maxpkt, "pio");
|
||||
seq_printf(s, "%-12s max_pkt=%d %s\n",
|
||||
EPNAME(ep), maxpkt, "pio");
|
||||
|
||||
if (list_empty(&ep->queue)) {
|
||||
pos += seq_printf(s, "\t(nothing queued)\n");
|
||||
seq_puts(s, "\t(nothing queued)\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
list_for_each_entry(req, &ep->queue, queue) {
|
||||
pos += seq_printf(s, "\treq %p len %d/%d buf %p\n",
|
||||
&req->req, req->req.actual,
|
||||
req->req.length, req->req.buf);
|
||||
seq_printf(s, "\treq %p len %d/%d buf %p\n",
|
||||
&req->req, req->req.actual,
|
||||
req->req.length, req->req.buf);
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int eps_dbg_show(struct seq_file *s, void *p)
|
||||
{
|
||||
struct pxa_udc *udc = s->private;
|
||||
struct pxa_ep *ep;
|
||||
int pos = 0, i, ret;
|
||||
int i;
|
||||
u32 tmp;
|
||||
|
||||
ret = -ENODEV;
|
||||
if (!udc->driver)
|
||||
goto out;
|
||||
return -ENODEV;
|
||||
|
||||
ep = &udc->pxa_ep[0];
|
||||
tmp = udc_ep_readl(ep, UDCCSR);
|
||||
pos += seq_printf(s, "udccsr0=0x%03x(%s%s%s%s%s%s%s)\n", tmp,
|
||||
(tmp & UDCCSR0_SA) ? " sa" : "",
|
||||
(tmp & UDCCSR0_RNE) ? " rne" : "",
|
||||
(tmp & UDCCSR0_FST) ? " fst" : "",
|
||||
(tmp & UDCCSR0_SST) ? " sst" : "",
|
||||
(tmp & UDCCSR0_DME) ? " dme" : "",
|
||||
(tmp & UDCCSR0_IPR) ? " ipr" : "",
|
||||
(tmp & UDCCSR0_OPC) ? " opc" : "");
|
||||
seq_printf(s, "udccsr0=0x%03x(%s%s%s%s%s%s%s)\n",
|
||||
tmp,
|
||||
(tmp & UDCCSR0_SA) ? " sa" : "",
|
||||
(tmp & UDCCSR0_RNE) ? " rne" : "",
|
||||
(tmp & UDCCSR0_FST) ? " fst" : "",
|
||||
(tmp & UDCCSR0_SST) ? " sst" : "",
|
||||
(tmp & UDCCSR0_DME) ? " dme" : "",
|
||||
(tmp & UDCCSR0_IPR) ? " ipr" : "",
|
||||
(tmp & UDCCSR0_OPC) ? " opc" : "");
|
||||
for (i = 0; i < NR_PXA_ENDPOINTS; i++) {
|
||||
ep = &udc->pxa_ep[i];
|
||||
tmp = i? udc_ep_readl(ep, UDCCR) : udc_readl(udc, UDCCR);
|
||||
pos += seq_printf(s, "%-12s: "
|
||||
"IN %lu(%lu reqs), OUT %lu(%lu reqs), "
|
||||
"irqs=%lu, udccr=0x%08x, udccsr=0x%03x, "
|
||||
"udcbcr=%d\n",
|
||||
EPNAME(ep),
|
||||
ep->stats.in_bytes, ep->stats.in_ops,
|
||||
ep->stats.out_bytes, ep->stats.out_ops,
|
||||
ep->stats.irqs,
|
||||
tmp, udc_ep_readl(ep, UDCCSR),
|
||||
udc_ep_readl(ep, UDCBCR));
|
||||
seq_printf(s, "%-12s: IN %lu(%lu reqs), OUT %lu(%lu reqs), irqs=%lu, udccr=0x%08x, udccsr=0x%03x, udcbcr=%d\n",
|
||||
EPNAME(ep),
|
||||
ep->stats.in_bytes, ep->stats.in_ops,
|
||||
ep->stats.out_bytes, ep->stats.out_ops,
|
||||
ep->stats.irqs,
|
||||
tmp, udc_ep_readl(ep, UDCCSR),
|
||||
udc_ep_readl(ep, UDCBCR));
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int eps_dbg_open(struct inode *inode, struct file *file)
|
||||
@ -2399,7 +2387,7 @@ static struct pxa_udc memory = {
|
||||
};
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
static struct of_device_id udc_pxa_dt_ids[] = {
|
||||
static const struct of_device_id udc_pxa_dt_ids[] = {
|
||||
{ .compatible = "marvell,pxa270-udc" },
|
||||
{}
|
||||
};
|
||||
|
@ -35,6 +35,8 @@
|
||||
* @dev - the child device to the actual controller
|
||||
* @gadget - the gadget. For use by the class code
|
||||
* @list - for use by the udc class driver
|
||||
* @vbus - for udcs who care about vbus status, this value is real vbus status;
|
||||
* for udcs who do not care about vbus status, this value is always true
|
||||
*
|
||||
* This represents the internal data structure which is used by the UDC-class
|
||||
* to hold information about udc driver and gadget together.
|
||||
@ -44,6 +46,7 @@ struct usb_udc {
|
||||
struct usb_gadget *gadget;
|
||||
struct device dev;
|
||||
struct list_head list;
|
||||
bool vbus;
|
||||
};
|
||||
|
||||
static struct class *udc_class;
|
||||
@ -128,21 +131,11 @@ EXPORT_SYMBOL_GPL(usb_gadget_giveback_request);
|
||||
|
||||
static void usb_gadget_state_work(struct work_struct *work)
|
||||
{
|
||||
struct usb_gadget *gadget = work_to_gadget(work);
|
||||
struct usb_udc *udc = NULL;
|
||||
struct usb_gadget *gadget = work_to_gadget(work);
|
||||
struct usb_udc *udc = gadget->udc;
|
||||
|
||||
mutex_lock(&udc_lock);
|
||||
list_for_each_entry(udc, &udc_list, list)
|
||||
if (udc->gadget == gadget)
|
||||
goto found;
|
||||
mutex_unlock(&udc_lock);
|
||||
|
||||
return;
|
||||
|
||||
found:
|
||||
mutex_unlock(&udc_lock);
|
||||
|
||||
sysfs_notify(&udc->dev.kobj, NULL, "state");
|
||||
if (udc)
|
||||
sysfs_notify(&udc->dev.kobj, NULL, "state");
|
||||
}
|
||||
|
||||
void usb_gadget_set_state(struct usb_gadget *gadget,
|
||||
@ -155,6 +148,34 @@ EXPORT_SYMBOL_GPL(usb_gadget_set_state);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static void usb_udc_connect_control(struct usb_udc *udc)
|
||||
{
|
||||
if (udc->vbus)
|
||||
usb_gadget_connect(udc->gadget);
|
||||
else
|
||||
usb_gadget_disconnect(udc->gadget);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_udc_vbus_handler - updates the udc core vbus status, and try to
|
||||
* connect or disconnect gadget
|
||||
* @gadget: The gadget which vbus change occurs
|
||||
* @status: The vbus status
|
||||
*
|
||||
* The udc driver calls it when it wants to connect or disconnect gadget
|
||||
* according to vbus status.
|
||||
*/
|
||||
void usb_udc_vbus_handler(struct usb_gadget *gadget, bool status)
|
||||
{
|
||||
struct usb_udc *udc = gadget->udc;
|
||||
|
||||
if (udc) {
|
||||
udc->vbus = status;
|
||||
usb_udc_connect_control(udc);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_udc_vbus_handler);
|
||||
|
||||
/**
|
||||
* usb_gadget_udc_reset - notifies the udc core that bus reset occurs
|
||||
* @gadget: The gadget which bus reset occurs
|
||||
@ -278,6 +299,7 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
|
||||
goto err3;
|
||||
|
||||
udc->gadget = gadget;
|
||||
gadget->udc = udc;
|
||||
|
||||
mutex_lock(&udc_lock);
|
||||
list_add_tail(&udc->list, &udc_list);
|
||||
@ -287,6 +309,7 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
|
||||
goto err4;
|
||||
|
||||
usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);
|
||||
udc->vbus = true;
|
||||
|
||||
mutex_unlock(&udc_lock);
|
||||
|
||||
@ -348,21 +371,14 @@ static void usb_gadget_remove_driver(struct usb_udc *udc)
|
||||
*/
|
||||
void usb_del_gadget_udc(struct usb_gadget *gadget)
|
||||
{
|
||||
struct usb_udc *udc = NULL;
|
||||
struct usb_udc *udc = gadget->udc;
|
||||
|
||||
mutex_lock(&udc_lock);
|
||||
list_for_each_entry(udc, &udc_list, list)
|
||||
if (udc->gadget == gadget)
|
||||
goto found;
|
||||
if (!udc)
|
||||
return;
|
||||
|
||||
dev_err(gadget->dev.parent, "gadget not registered.\n");
|
||||
mutex_unlock(&udc_lock);
|
||||
|
||||
return;
|
||||
|
||||
found:
|
||||
dev_vdbg(gadget->dev.parent, "unregistering gadget\n");
|
||||
|
||||
mutex_lock(&udc_lock);
|
||||
list_del(&udc->list);
|
||||
mutex_unlock(&udc_lock);
|
||||
|
||||
@ -397,7 +413,7 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri
|
||||
driver->unbind(udc->gadget);
|
||||
goto err1;
|
||||
}
|
||||
usb_gadget_connect(udc->gadget);
|
||||
usb_udc_connect_control(udc);
|
||||
|
||||
kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
|
||||
return 0;
|
||||
|
@ -198,7 +198,7 @@ config USB_EHCI_HCD_AT91
|
||||
|
||||
config USB_EHCI_MSM
|
||||
tristate "Support for Qualcomm QSD/MSM on-chip EHCI USB controller"
|
||||
depends on ARCH_MSM || ARCH_QCOM
|
||||
depends on ARCH_QCOM
|
||||
select USB_EHCI_ROOT_HUB_TT
|
||||
---help---
|
||||
Enables support for the USB Host controller present on the
|
||||
|
@ -792,12 +792,12 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
|
||||
ehci->reset_done[i] == 0))
|
||||
continue;
|
||||
|
||||
/* start 20 msec resume signaling from this port,
|
||||
* and make hub_wq collect PORT_STAT_C_SUSPEND to
|
||||
* stop that signaling. Use 5 ms extra for safety,
|
||||
* like usb_port_resume() does.
|
||||
/* start USB_RESUME_TIMEOUT msec resume signaling from
|
||||
* this port, and make hub_wq collect
|
||||
* PORT_STAT_C_SUSPEND to stop that signaling.
|
||||
*/
|
||||
ehci->reset_done[i] = jiffies + msecs_to_jiffies(25);
|
||||
ehci->reset_done[i] = jiffies +
|
||||
msecs_to_jiffies(USB_RESUME_TIMEOUT);
|
||||
set_bit(i, &ehci->resuming_ports);
|
||||
ehci_dbg (ehci, "port %d remote wakeup\n", i + 1);
|
||||
usb_hcd_start_port_resume(&hcd->self, i);
|
||||
|
@ -471,10 +471,13 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
|
||||
ehci_writel(ehci, temp, &ehci->regs->port_status [i]);
|
||||
}
|
||||
|
||||
/* msleep for 20ms only if code is trying to resume port */
|
||||
/*
|
||||
* msleep for USB_RESUME_TIMEOUT ms only if code is trying to resume
|
||||
* port
|
||||
*/
|
||||
if (resume_needed) {
|
||||
spin_unlock_irq(&ehci->lock);
|
||||
msleep(20);
|
||||
msleep(USB_RESUME_TIMEOUT);
|
||||
spin_lock_irq(&ehci->lock);
|
||||
if (ehci->shutdown)
|
||||
goto shutdown;
|
||||
@ -688,7 +691,7 @@ ehci_hub_descriptor (
|
||||
int ports = HCS_N_PORTS (ehci->hcs_params);
|
||||
u16 temp;
|
||||
|
||||
desc->bDescriptorType = 0x29;
|
||||
desc->bDescriptorType = USB_DT_HUB;
|
||||
desc->bPwrOn2PwrGood = 10; /* ehci 1.0, 2.3.9 says 20ms max */
|
||||
desc->bHubContrCurrent = 0;
|
||||
|
||||
@ -942,7 +945,7 @@ int ehci_hub_control(
|
||||
temp &= ~PORT_WAKE_BITS;
|
||||
ehci_writel(ehci, temp | PORT_RESUME, status_reg);
|
||||
ehci->reset_done[wIndex] = jiffies
|
||||
+ msecs_to_jiffies(20);
|
||||
+ msecs_to_jiffies(USB_RESUME_TIMEOUT);
|
||||
set_bit(wIndex, &ehci->resuming_ports);
|
||||
usb_hcd_start_port_resume(&hcd->self, wIndex);
|
||||
break;
|
||||
|
@ -29,7 +29,13 @@
|
||||
#define wrl(off, val) writel_relaxed((val), hcd->regs + (off))
|
||||
|
||||
#define USB_CMD 0x140
|
||||
#define USB_CMD_RUN BIT(0)
|
||||
#define USB_CMD_RESET BIT(1)
|
||||
#define USB_MODE 0x1a8
|
||||
#define USB_MODE_MASK GENMASK(1, 0)
|
||||
#define USB_MODE_DEVICE 0x2
|
||||
#define USB_MODE_HOST 0x3
|
||||
#define USB_MODE_SDIS BIT(4)
|
||||
#define USB_CAUSE 0x310
|
||||
#define USB_MASK 0x314
|
||||
#define USB_WINDOW_CTRL(i) (0x320 + ((i) << 4))
|
||||
@ -69,8 +75,8 @@ static void orion_usb_phy_v1_setup(struct usb_hcd *hcd)
|
||||
/*
|
||||
* Reset controller
|
||||
*/
|
||||
wrl(USB_CMD, rdl(USB_CMD) | 0x2);
|
||||
while (rdl(USB_CMD) & 0x2);
|
||||
wrl(USB_CMD, rdl(USB_CMD) | USB_CMD_RESET);
|
||||
while (rdl(USB_CMD) & USB_CMD_RESET);
|
||||
|
||||
/*
|
||||
* GL# USB-10: Set IPG for non start of frame packets
|
||||
@ -112,16 +118,16 @@ static void orion_usb_phy_v1_setup(struct usb_hcd *hcd)
|
||||
/*
|
||||
* Stop and reset controller
|
||||
*/
|
||||
wrl(USB_CMD, rdl(USB_CMD) & ~0x1);
|
||||
wrl(USB_CMD, rdl(USB_CMD) | 0x2);
|
||||
while (rdl(USB_CMD) & 0x2);
|
||||
wrl(USB_CMD, rdl(USB_CMD) & ~USB_CMD_RUN);
|
||||
wrl(USB_CMD, rdl(USB_CMD) | USB_CMD_RESET);
|
||||
while (rdl(USB_CMD) & USB_CMD_RESET);
|
||||
|
||||
/*
|
||||
* GL# USB-5 Streaming disable REG_USB_MODE[4]=1
|
||||
* TBD: This need to be done after each reset!
|
||||
* GL# USB-4 Setup USB Host mode
|
||||
*/
|
||||
wrl(USB_MODE, 0x13);
|
||||
wrl(USB_MODE, USB_MODE_SDIS | USB_MODE_HOST);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -30,7 +30,7 @@
|
||||
/* virtual root hub specific descriptor */
|
||||
static u8 root_hub_des[] = {
|
||||
0x09, /* blength */
|
||||
0x29, /* bDescriptorType;hub-descriptor */
|
||||
USB_DT_HUB, /* bDescriptorType;hub-descriptor */
|
||||
0x01, /* bNbrPorts */
|
||||
HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_NO_OCPM, /* wHubCharacteristics */
|
||||
0x00, /* per-port power, no overcurrent */
|
||||
|
@ -1509,7 +1509,7 @@ fotg210_hub_descriptor(
|
||||
int ports = HCS_N_PORTS(fotg210->hcs_params);
|
||||
u16 temp;
|
||||
|
||||
desc->bDescriptorType = 0x29;
|
||||
desc->bDescriptorType = USB_DT_HUB;
|
||||
desc->bPwrOn2PwrGood = 10; /* fotg210 1.0, 2.3.9 says 20ms max */
|
||||
desc->bHubContrCurrent = 0;
|
||||
|
||||
@ -1595,7 +1595,7 @@ static int fotg210_hub_control(
|
||||
/* resume signaling for 20 msec */
|
||||
fotg210_writel(fotg210, temp | PORT_RESUME, status_reg);
|
||||
fotg210->reset_done[wIndex] = jiffies
|
||||
+ msecs_to_jiffies(20);
|
||||
+ msecs_to_jiffies(USB_RESUME_TIMEOUT);
|
||||
break;
|
||||
case USB_PORT_FEAT_C_SUSPEND:
|
||||
clear_bit(wIndex, &fotg210->port_c_suspend);
|
||||
|
@ -1467,7 +1467,7 @@ fusbh200_hub_descriptor (
|
||||
int ports = HCS_N_PORTS (fusbh200->hcs_params);
|
||||
u16 temp;
|
||||
|
||||
desc->bDescriptorType = 0x29;
|
||||
desc->bDescriptorType = USB_DT_HUB;
|
||||
desc->bPwrOn2PwrGood = 10; /* fusbh200 1.0, 2.3.9 says 20ms max */
|
||||
desc->bHubContrCurrent = 0;
|
||||
|
||||
@ -1550,10 +1550,9 @@ static int fusbh200_hub_control (
|
||||
if ((temp & PORT_PE) == 0)
|
||||
goto error;
|
||||
|
||||
/* resume signaling for 20 msec */
|
||||
fusbh200_writel(fusbh200, temp | PORT_RESUME, status_reg);
|
||||
fusbh200->reset_done[wIndex] = jiffies
|
||||
+ msecs_to_jiffies(20);
|
||||
+ msecs_to_jiffies(USB_RESUME_TIMEOUT);
|
||||
break;
|
||||
case USB_PORT_FEAT_C_SUSPEND:
|
||||
clear_bit(wIndex, &fusbh200->port_c_suspend);
|
||||
|
@ -1474,7 +1474,7 @@ static int get_hub_descriptor(struct usb_hcd *hcd,
|
||||
struct usb_hub_descriptor *desc)
|
||||
{
|
||||
struct imx21 *imx21 = hcd_to_imx21(hcd);
|
||||
desc->bDescriptorType = 0x29; /* HUB descriptor */
|
||||
desc->bDescriptorType = USB_DT_HUB; /* HUB descriptor */
|
||||
desc->bHubContrCurrent = 0;
|
||||
|
||||
desc->bNbrPorts = readl(imx21->regs + USBH_ROOTHUBA)
|
||||
|
@ -943,7 +943,7 @@ static void isp116x_hub_descriptor(struct isp116x *isp116x,
|
||||
{
|
||||
u32 reg = isp116x->rhdesca;
|
||||
|
||||
desc->bDescriptorType = 0x29;
|
||||
desc->bDescriptorType = USB_DT_HUB;
|
||||
desc->bDescLength = 9;
|
||||
desc->bHubContrCurrent = 0;
|
||||
desc->bNbrPorts = (u8) (reg & 0x3);
|
||||
@ -1490,7 +1490,7 @@ static int isp116x_bus_resume(struct usb_hcd *hcd)
|
||||
spin_unlock_irq(&isp116x->lock);
|
||||
|
||||
hcd->state = HC_STATE_RESUMING;
|
||||
msleep(20);
|
||||
msleep(USB_RESUME_TIMEOUT);
|
||||
|
||||
/* Go operational */
|
||||
spin_lock_irq(&isp116x->lock);
|
||||
|
@ -1538,7 +1538,7 @@ static void isp1362_hub_descriptor(struct isp1362_hcd *isp1362_hcd,
|
||||
|
||||
DBG(3, "%s: enter\n", __func__);
|
||||
|
||||
desc->bDescriptorType = 0x29;
|
||||
desc->bDescriptorType = USB_DT_HUB;
|
||||
desc->bDescLength = 9;
|
||||
desc->bHubContrCurrent = 0;
|
||||
desc->bNbrPorts = reg & 0x3;
|
||||
|
@ -1659,7 +1659,7 @@ hub_descriptor(struct usb_hub_descriptor *desc)
|
||||
/*
|
||||
* See Table 11-13: Hub Descriptor in USB 2.0 spec.
|
||||
*/
|
||||
desc->bDescriptorType = 0x29; /* hub descriptor */
|
||||
desc->bDescriptorType = USB_DT_HUB; /* hub descriptor */
|
||||
desc->bDescLength = 9;
|
||||
desc->wHubCharacteristics = cpu_to_le16(HUB_CHAR_INDV_PORT_LPSM |
|
||||
HUB_CHAR_COMMON_OCPM);
|
||||
|
@ -39,7 +39,6 @@
|
||||
struct ohci_at91_priv {
|
||||
struct clk *iclk;
|
||||
struct clk *fclk;
|
||||
struct clk *uclk;
|
||||
struct clk *hclk;
|
||||
bool clocked;
|
||||
bool wakeup; /* Saved wake-up state for resume */
|
||||
@ -64,10 +63,8 @@ static void at91_start_clock(struct ohci_at91_priv *ohci_at91)
|
||||
{
|
||||
if (ohci_at91->clocked)
|
||||
return;
|
||||
if (IS_ENABLED(CONFIG_COMMON_CLK)) {
|
||||
clk_set_rate(ohci_at91->uclk, 48000000);
|
||||
clk_prepare_enable(ohci_at91->uclk);
|
||||
}
|
||||
|
||||
clk_set_rate(ohci_at91->fclk, 48000000);
|
||||
clk_prepare_enable(ohci_at91->hclk);
|
||||
clk_prepare_enable(ohci_at91->iclk);
|
||||
clk_prepare_enable(ohci_at91->fclk);
|
||||
@ -78,11 +75,10 @@ static void at91_stop_clock(struct ohci_at91_priv *ohci_at91)
|
||||
{
|
||||
if (!ohci_at91->clocked)
|
||||
return;
|
||||
|
||||
clk_disable_unprepare(ohci_at91->fclk);
|
||||
clk_disable_unprepare(ohci_at91->iclk);
|
||||
clk_disable_unprepare(ohci_at91->hclk);
|
||||
if (IS_ENABLED(CONFIG_COMMON_CLK))
|
||||
clk_disable_unprepare(ohci_at91->uclk);
|
||||
ohci_at91->clocked = false;
|
||||
}
|
||||
|
||||
@ -191,14 +187,6 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver,
|
||||
retval = PTR_ERR(ohci_at91->hclk);
|
||||
goto err;
|
||||
}
|
||||
if (IS_ENABLED(CONFIG_COMMON_CLK)) {
|
||||
ohci_at91->uclk = devm_clk_get(dev, "usb_clk");
|
||||
if (IS_ERR(ohci_at91->uclk)) {
|
||||
dev_err(dev, "failed to get uclk\n");
|
||||
retval = PTR_ERR(ohci_at91->uclk);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
board = hcd->self.controller->platform_data;
|
||||
ohci = hcd_to_ohci(hcd);
|
||||
|
@ -536,7 +536,7 @@ ohci_hub_descriptor (
|
||||
u32 rh = roothub_a (ohci);
|
||||
u16 temp;
|
||||
|
||||
desc->bDescriptorType = 0x29;
|
||||
desc->bDescriptorType = USB_DT_HUB;
|
||||
desc->bPwrOn2PwrGood = (rh & RH_A_POTPGT) >> 24;
|
||||
desc->bHubContrCurrent = 0;
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user