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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
+10
-2
@@ -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 = {
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
+13
-20
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
+5
-18
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
+169
-16
@@ -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);
|
||||
}
|
||||
|
||||
+206
-219
@@ -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);
|
||||
|
||||
+25
-22
@@ -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
|
||||
|
||||
+18
-18
@@ -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;
|
||||
|
||||
+10
-2
@@ -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 |
|
||||
|
||||
+77
-93
@@ -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
|
||||
|
||||
+37
-22
@@ -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
|
||||
|
||||
+16
-21
@@ -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);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
+48
-1191
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;
|
||||
}
|
||||
|
||||
+116
-105
@@ -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
Reference in New Issue
Block a user