提交 42e3a58b 编写于 作者: L Linus Torvalds

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
  ...
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
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 ...@@ -20,8 +20,8 @@ Required nodes : A sub-node is required for each channel the controller
Required properties (port (child) node): Required properties (port (child) node):
- #phy-cells : Should be 1 (See second example) - #phy-cells : Should be 1 (See second example)
Cell after port phandle is device type from: Cell after port phandle is device type from:
- MIPHY_TYPE_SATA - PHY_TYPE_SATA
- MIPHY_TYPE_PCI - PHY_TYPE_PCI
- reg : Address and length of register sets for each device in - reg : Address and length of register sets for each device in
"reg-names" "reg-names"
- reg-names : The names of the register addresses corresponding to the - 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. ...@@ -68,10 +68,10 @@ property, containing a phandle to the phy port node and a device type.
Example: Example:
#include <dt-bindings/phy/phy-miphy365x.h> #include <dt-bindings/phy/phy.h>
sata0: sata@fe380000 { sata0: sata@fe380000 {
... ...
phys = <&phy_port0 MIPHY_TYPE_SATA>; phys = <&phy_port0 PHY_TYPE_SATA>;
... ...
}; };
...@@ -128,6 +128,7 @@ Required properties: ...@@ -128,6 +128,7 @@ Required properties:
- compatible : Should be set to one of the following supported values: - compatible : Should be set to one of the following supported values:
- "samsung,exynos5250-usbdrd-phy" - for exynos5250 SoC, - "samsung,exynos5250-usbdrd-phy" - for exynos5250 SoC,
- "samsung,exynos5420-usbdrd-phy" - for exynos5420 SoC. - "samsung,exynos5420-usbdrd-phy" - for exynos5420 SoC.
- "samsung,exynos5433-usbdrd-phy" - for exynos5433 SoC.
- "samsung,exynos7-usbdrd-phy" - for exynos7 SoC. - "samsung,exynos7-usbdrd-phy" - for exynos7 SoC.
- reg : Register offset and length of USB DRD PHY register set; - reg : Register offset and length of USB DRD PHY register set;
- clocks: Clock IDs array as required by the controller - clocks: Clock IDs array as required by the controller
...@@ -139,7 +140,7 @@ Required properties: ...@@ -139,7 +140,7 @@ Required properties:
PHY operations, associated by phy name. It is used to PHY operations, associated by phy name. It is used to
determine bit values for clock settings register. determine bit values for clock settings register.
For Exynos5420 this is given as 'sclk_usbphy30' in CMU. 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: gate clocks available:
- phy_pipe: for PIPE3 phy - phy_pipe: for PIPE3 phy
- phy_utmi: for UTMI+ phy - phy_utmi: for UTMI+ phy
......
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: ...@@ -14,6 +14,7 @@ Optional properties:
- phys: from the *Generic PHY* bindings - phys: from the *Generic PHY* bindings
- phy-names: from the *Generic PHY* bindings - phy-names: from the *Generic PHY* bindings
- tx-fifo-resize: determines if the FIFO *has* to be reallocated. - 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. - snps,disable_scramble_quirk: true when SW should disable data scrambling.
Only really useful for FPGA builds. Only really useful for FPGA builds.
- snps,has-lpm-erratum: true when DWC3 was configured with LPM Erratum enabled - snps,has-lpm-erratum: true when DWC3 was configured with LPM Erratum enabled
......
...@@ -15,7 +15,10 @@ Optional properties: ...@@ -15,7 +15,10 @@ Optional properties:
- phys: phandle + phy specifier pair - phys: phandle + phy specifier pair
- phy-names: must be "usb" - phy-names: must be "usb"
- dmas: Must contain a list of references to DMA specifiers. - 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: Example:
usbhs: usb@e6590000 { usbhs: usb@e6590000 {
......
...@@ -5,6 +5,7 @@ Required properties: ...@@ -5,6 +5,7 @@ Required properties:
- compatible: Should be one of below: - compatible: Should be one of below:
"fsl,imx6q-usbmisc" for imx6q "fsl,imx6q-usbmisc" for imx6q
"fsl,vf610-usbmisc" for Vybrid vf610 "fsl,vf610-usbmisc" for Vybrid vf610
"fsl,imx6sx-usbmisc" for imx6sx
- reg: Should contain registers location and length - reg: Should contain registers location and length
Examples: Examples:
......
...@@ -69,3 +69,24 @@ cat /sys/kernel/debug/ci_hdrc.0/registers ...@@ -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 "On-The-Go and Embedded Host Supplement to the USB Revision 2.0 Specification
July 27, 2012 Revision 2.0 version 1.1a" 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. ...@@ -19,6 +19,7 @@ provided by gadgets.
16. UAC1 function 16. UAC1 function
17. UAC2 function 17. UAC2 function
18. UVC function 18. UVC function
19. PRINTER function
1. ACM function 1. ACM function
...@@ -726,3 +727,49 @@ with these patches: ...@@ -726,3 +727,49 @@ with these patches:
http://www.spinics.net/lists/linux-usb/msg99220.html http://www.spinics.net/lists/linux-usb/msg99220.html
host: luvcview -f yuv 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.
...@@ -1468,6 +1468,8 @@ F: drivers/clocksource/arm_global_timer.c ...@@ -1468,6 +1468,8 @@ F: drivers/clocksource/arm_global_timer.c
F: drivers/i2c/busses/i2c-st.c F: drivers/i2c/busses/i2c-st.c
F: drivers/media/rc/st_rc.c F: drivers/media/rc/st_rc.c
F: drivers/mmc/host/sdhci-st.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-stih407-usb.c
F: drivers/phy/phy-stih41x-usb.c F: drivers/phy/phy-stih41x-usb.c
F: drivers/pinctrl/pinctrl-st.c F: drivers/pinctrl/pinctrl-st.c
...@@ -2518,7 +2520,7 @@ F: Documentation/zh_CN/ ...@@ -2518,7 +2520,7 @@ F: Documentation/zh_CN/
CHIPIDEA USB HIGH SPEED DUAL ROLE CONTROLLER CHIPIDEA USB HIGH SPEED DUAL ROLE CONTROLLER
M: Peter Chen <Peter.Chen@freescale.com> 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 L: linux-usb@vger.kernel.org
S: Maintained S: Maintained
F: drivers/usb/chipidea/ F: drivers/usb/chipidea/
...@@ -10132,6 +10134,12 @@ S: Maintained ...@@ -10132,6 +10134,12 @@ S: Maintained
F: drivers/net/usb/cdc_*.c F: drivers/net/usb/cdc_*.c
F: include/uapi/linux/usb/cdc.h 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 USB CYPRESS C67X00 DRIVER
M: Peter Korsgaard <jacmet@sunsite.dk> M: Peter Korsgaard <jacmet@sunsite.dk>
L: linux-usb@vger.kernel.org L: linux-usb@vger.kernel.org
...@@ -10212,7 +10220,7 @@ F: drivers/usb/host/ohci* ...@@ -10212,7 +10220,7 @@ F: drivers/usb/host/ohci*
USB OTG FSM (Finite State Machine) USB OTG FSM (Finite State Machine)
M: Peter Chen <Peter.Chen@freescale.com> 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 L: linux-usb@vger.kernel.org
S: Maintained S: Maintained
F: drivers/usb/common/usb-otg-fsm.c F: drivers/usb/common/usb-otg-fsm.c
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
#include "stih416-clock.dtsi" #include "stih416-clock.dtsi"
#include "stih416-pinctrl.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/interrupt-controller/arm-gic.h>
#include <dt-bindings/reset-controller/stih416-resets.h> #include <dt-bindings/reset-controller/stih416-resets.h>
/ { / {
...@@ -306,7 +306,7 @@ ...@@ -306,7 +306,7 @@
reg = <0xfe380000 0x1000>; reg = <0xfe380000 0x1000>;
interrupts = <GIC_SPI 157 IRQ_TYPE_NONE>; interrupts = <GIC_SPI 157 IRQ_TYPE_NONE>;
interrupt-names = "hostc"; interrupt-names = "hostc";
phys = <&phy_port0 MIPHY_TYPE_SATA>; phys = <&phy_port0 PHY_TYPE_SATA>;
phy-names = "sata-phy"; phy-names = "sata-phy";
resets = <&powerdown STIH416_SATA0_POWERDOWN>, resets = <&powerdown STIH416_SATA0_POWERDOWN>,
<&softreset STIH416_SATA0_SOFTRESET>; <&softreset STIH416_SATA0_SOFTRESET>;
......
...@@ -35,6 +35,13 @@ config ARMADA375_USBCLUSTER_PHY ...@@ -35,6 +35,13 @@ config ARMADA375_USBCLUSTER_PHY
depends on OF depends on OF
select GENERIC_PHY 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 config PHY_EXYNOS_MIPI_VIDEO
tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver" tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver"
depends on HAS_IOMEM depends on HAS_IOMEM
...@@ -174,6 +181,17 @@ config PHY_SUN4I_USB ...@@ -174,6 +181,17 @@ config PHY_SUN4I_USB
This driver controls the entire USB PHY block, both the USB OTG This driver controls the entire USB PHY block, both the USB OTG
parts, as well as the 2 regular USB 2 host PHYs. 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 config PHY_SAMSUNG_USB2
tristate "Samsung USB 2.0 PHY driver" tristate "Samsung USB 2.0 PHY driver"
depends on HAS_IOMEM depends on HAS_IOMEM
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
obj-$(CONFIG_GENERIC_PHY) += phy-core.o obj-$(CONFIG_GENERIC_PHY) += phy-core.o
obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o
obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.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_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o
obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o
obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o
...@@ -20,6 +21,7 @@ obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.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_EXYNOS5250_SATA) += phy-exynos5250-sata.o
obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o
obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.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 obj-$(CONFIG_PHY_SAMSUNG_USB2) += phy-exynos-usb2.o
phy-exynos-usb2-y += phy-samsung-usb2.o phy-exynos-usb2-y += phy-samsung-usb2.o
phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4210_USB2) += phy-exynos4210-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) ...@@ -218,7 +218,7 @@ static int phy_berlin_sata_probe(struct platform_device *pdev)
if (priv->nphys == 0) if (priv->nphys == 0)
return -ENODEV; return -ENODEV;
priv->phys = devm_kzalloc(dev, priv->nphys * sizeof(*priv->phys), priv->phys = devm_kcalloc(dev, priv->nphys, sizeof(*priv->phys),
GFP_KERNEL); GFP_KERNEL);
if (!priv->phys) if (!priv->phys)
return -ENOMEM; return -ENOMEM;
......
...@@ -103,9 +103,6 @@ ...@@ -103,9 +103,6 @@
#define MODE_TEST_EN BIT(11) #define MODE_TEST_EN BIT(11)
#define ANA_TEST_DC_CTRL(x) ((x) << 12) #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[] = { static const u32 phy_berlin_pll_dividers[] = {
/* Berlin 2 */ /* Berlin 2 */
CLK_REF_DIV(0xc) | FEEDBACK_CLK_DIV(0x54), CLK_REF_DIV(0xc) | FEEDBACK_CLK_DIV(0x54),
...@@ -115,14 +112,13 @@ static const u32 phy_berlin_pll_dividers[] = { ...@@ -115,14 +112,13 @@ static const u32 phy_berlin_pll_dividers[] = {
struct phy_berlin_usb_priv { struct phy_berlin_usb_priv {
void __iomem *base; void __iomem *base;
struct phy *phy;
struct reset_control *rst_ctrl; struct reset_control *rst_ctrl;
u32 pll_divider; u32 pll_divider;
}; };
static int phy_berlin_usb_power_on(struct phy *phy) 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); reset_control_reset(priv->rst_ctrl);
...@@ -175,6 +171,7 @@ static int phy_berlin_usb_probe(struct platform_device *pdev) ...@@ -175,6 +171,7 @@ static int phy_berlin_usb_probe(struct platform_device *pdev)
of_match_device(phy_berlin_sata_of_match, &pdev->dev); of_match_device(phy_berlin_sata_of_match, &pdev->dev);
struct phy_berlin_usb_priv *priv; struct phy_berlin_usb_priv *priv;
struct resource *res; struct resource *res;
struct phy *phy;
struct phy_provider *phy_provider; struct phy_provider *phy_provider;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
...@@ -192,20 +189,18 @@ static int phy_berlin_usb_probe(struct platform_device *pdev) ...@@ -192,20 +189,18 @@ static int phy_berlin_usb_probe(struct platform_device *pdev)
priv->pll_divider = *((u32 *)match->data); priv->pll_divider = *((u32 *)match->data);
priv->phy = devm_phy_create(&pdev->dev, NULL, &phy_berlin_usb_ops); phy = devm_phy_create(&pdev->dev, NULL, &phy_berlin_usb_ops);
if (IS_ERR(priv->phy)) { if (IS_ERR(phy)) {
dev_err(&pdev->dev, "failed to create PHY\n"); dev_err(&pdev->dev, "failed to create PHY\n");
return PTR_ERR(priv->phy); return PTR_ERR(phy);
} }
platform_set_drvdata(pdev, priv); platform_set_drvdata(pdev, priv);
phy_set_drvdata(phy, priv);
phy_provider = phy_provider =
devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate);
if (IS_ERR(phy_provider)) return PTR_ERR_OR_ZERO(phy_provider);
return PTR_ERR(phy_provider);
return 0;
} }
static struct platform_driver phy_berlin_usb_driver = { static struct platform_driver phy_berlin_usb_driver = {
......
/*
* 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 = { ...@@ -624,6 +624,13 @@ static const struct exynos5_usbdrd_phy_drvdata exynos5250_usbdrd_phy = {
.has_common_clk_gate = true, .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 = { static const struct exynos5_usbdrd_phy_drvdata exynos7_usbdrd_phy = {
.phy_cfg = phy_cfg_exynos5, .phy_cfg = phy_cfg_exynos5,
.pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL,
...@@ -637,6 +644,9 @@ static const struct of_device_id exynos5_usbdrd_phy_of_match[] = { ...@@ -637,6 +644,9 @@ static const struct of_device_id exynos5_usbdrd_phy_of_match[] = {
}, { }, {
.compatible = "samsung,exynos5420-usbdrd-phy", .compatible = "samsung,exynos5420-usbdrd-phy",
.data = &exynos5420_usbdrd_phy .data = &exynos5420_usbdrd_phy
}, {
.compatible = "samsung,exynos5433-usbdrd-phy",
.data = &exynos5433_usbdrd_phy
}, { }, {
.compatible = "samsung,exynos7-usbdrd-phy", .compatible = "samsung,exynos7-usbdrd-phy",
.data = &exynos7_usbdrd_phy .data = &exynos7_usbdrd_phy
......
...@@ -1259,10 +1259,7 @@ static int miphy28lp_probe(struct platform_device *pdev) ...@@ -1259,10 +1259,7 @@ static int miphy28lp_probe(struct platform_device *pdev)
} }
provider = devm_of_phy_provider_register(&pdev->dev, miphy28lp_xlate); provider = devm_of_phy_provider_register(&pdev->dev, miphy28lp_xlate);
if (IS_ERR(provider)) return PTR_ERR_OR_ZERO(provider);
return PTR_ERR(provider);
return 0;
} }
static const struct of_device_id miphy28lp_of_match[] = { static const struct of_device_id miphy28lp_of_match[] = {
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
#include <linux/mfd/syscon.h> #include <linux/mfd/syscon.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <dt-bindings/phy/phy-miphy365x.h> #include <dt-bindings/phy/phy.h>
#define HFC_TIMEOUT 100 #define HFC_TIMEOUT 100
...@@ -177,7 +177,7 @@ static u8 rx_tx_spd[] = { ...@@ -177,7 +177,7 @@ static u8 rx_tx_spd[] = {
static int miphy365x_set_path(struct miphy365x_phy *miphy_phy, static int miphy365x_set_path(struct miphy365x_phy *miphy_phy,
struct miphy365x_dev *miphy_dev) 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, return regmap_update_bits(miphy_dev->regmap,
miphy_phy->ctrlreg, miphy_phy->ctrlreg,
...@@ -431,7 +431,7 @@ static int miphy365x_init(struct phy *phy) ...@@ -431,7 +431,7 @@ static int miphy365x_init(struct phy *phy)
} }
/* Initialise Miphy for PCIe or SATA */ /* 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); ret = miphy365x_init_pcie_port(miphy_phy, miphy_dev);
else else
ret = miphy365x_init_sata_port(miphy_phy, miphy_dev); 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, ...@@ -455,8 +455,8 @@ int miphy365x_get_addr(struct device *dev, struct miphy365x_phy *miphy_phy,
return ret; return ret;
} }
if (!((!strncmp(name, "sata", 4) && type == MIPHY_TYPE_SATA) || if (!((!strncmp(name, "sata", 4) && type == PHY_TYPE_SATA) ||
(!strncmp(name, "pcie", 4) && type == MIPHY_TYPE_PCIE))) (!strncmp(name, "pcie", 4) && type == PHY_TYPE_PCIE)))
return 0; return 0;
miphy_phy->base = of_iomap(phynode, index); miphy_phy->base = of_iomap(phynode, index);
...@@ -499,8 +499,8 @@ static struct phy *miphy365x_xlate(struct device *dev, ...@@ -499,8 +499,8 @@ static struct phy *miphy365x_xlate(struct device *dev,
miphy_phy->type = args->args[0]; miphy_phy->type = args->args[0];
if (!(miphy_phy->type == MIPHY_TYPE_SATA || if (!(miphy_phy->type == PHY_TYPE_SATA ||
miphy_phy->type == MIPHY_TYPE_PCIE)) { miphy_phy->type == PHY_TYPE_PCIE)) {
dev_err(dev, "Unsupported device type: %d\n", miphy_phy->type); dev_err(dev, "Unsupported device type: %d\n", miphy_phy->type);
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
......
...@@ -216,7 +216,6 @@ void omap_control_usb_set_mode(struct device *dev, ...@@ -216,7 +216,6 @@ void omap_control_usb_set_mode(struct device *dev,
return; return;
ctrl_phy = dev_get_drvdata(dev); ctrl_phy = dev_get_drvdata(dev);
if (!ctrl_phy) { if (!ctrl_phy) {
dev_err(dev, "Invalid control phy device\n"); dev_err(dev, "Invalid control phy device\n");
return; return;
...@@ -241,8 +240,6 @@ void omap_control_usb_set_mode(struct device *dev, ...@@ -241,8 +240,6 @@ void omap_control_usb_set_mode(struct device *dev,
} }
EXPORT_SYMBOL_GPL(omap_control_usb_set_mode); 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 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 usb2_data = OMAP_CTRL_TYPE_USB2;
static const enum omap_control_phy_type pipe3_data = OMAP_CTRL_TYPE_PIPE3; 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[] = { ...@@ -278,8 +275,6 @@ static const struct of_device_id omap_control_phy_id_table[] = {
{}, {},
}; };
MODULE_DEVICE_TABLE(of, 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) static int omap_control_phy_probe(struct platform_device *pdev)
{ {
...@@ -287,8 +282,7 @@ 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; const struct of_device_id *of_id;
struct omap_control_phy *control_phy; struct omap_control_phy *control_phy;
of_id = of_match_device(of_match_ptr(omap_control_phy_id_table), of_id = of_match_device(omap_control_phy_id_table, &pdev->dev);
&pdev->dev);
if (!of_id) if (!of_id)
return -EINVAL; return -EINVAL;
...@@ -344,7 +338,7 @@ static struct platform_driver omap_control_phy_driver = { ...@@ -344,7 +338,7 @@ static struct platform_driver omap_control_phy_driver = {
.probe = omap_control_phy_probe, .probe = omap_control_phy_probe,
.driver = { .driver = {
.name = "omap-control-phy", .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 = { ...@@ -144,7 +144,6 @@ static struct phy_ops ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
#ifdef CONFIG_OF
static const struct usb_phy_data omap_usb2_data = { static const struct usb_phy_data omap_usb2_data = {
.label = "omap_usb2", .label = "omap_usb2",
.flags = OMAP_USB2_HAS_START_SRP | OMAP_USB2_HAS_SET_VBUS, .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[] = { ...@@ -185,7 +184,6 @@ static const struct of_device_id omap_usb2_id_table[] = {
{}, {},
}; };
MODULE_DEVICE_TABLE(of, omap_usb2_id_table); MODULE_DEVICE_TABLE(of, omap_usb2_id_table);
#endif
static int omap_usb2_probe(struct platform_device *pdev) static int omap_usb2_probe(struct platform_device *pdev)
{ {
...@@ -200,7 +198,7 @@ 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; const struct of_device_id *of_id;
struct usb_phy_data *phy_data; 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) if (!of_id)
return -EINVAL; return -EINVAL;
...@@ -378,7 +376,7 @@ static struct platform_driver omap_usb2_driver = { ...@@ -378,7 +376,7 @@ static struct platform_driver omap_usb2_driver = {
.driver = { .driver = {
.name = "omap-usb2", .name = "omap-usb2",
.pm = DEV_PM_OPS, .pm = DEV_PM_OPS,
.of_match_table = of_match_ptr(omap_usb2_id_table), .of_match_table = omap_usb2_id_table,
}, },
}; };
......
...@@ -73,6 +73,7 @@ int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, ...@@ -73,6 +73,7 @@ int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy,
out: out:
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(ufs_qcom_phy_calibrate);
struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev, struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev,
struct ufs_qcom_phy *common_cfg, struct ufs_qcom_phy *common_cfg,
...@@ -101,6 +102,7 @@ struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev, ...@@ -101,6 +102,7 @@ struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev,
if (IS_ERR(generic_phy)) { if (IS_ERR(generic_phy)) {
err = PTR_ERR(generic_phy); err = PTR_ERR(generic_phy);
dev_err(dev, "%s: failed to create phy %d\n", __func__, err); dev_err(dev, "%s: failed to create phy %d\n", __func__, err);
generic_phy = NULL;
goto out; goto out;
} }
...@@ -110,6 +112,7 @@ struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev, ...@@ -110,6 +112,7 @@ struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev,
out: out:
return generic_phy; return generic_phy;
} }
EXPORT_SYMBOL_GPL(ufs_qcom_phy_generic_probe);
/* /*
* This assumes the embedded phy structure inside generic_phy is of type * 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) ...@@ -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); return (struct ufs_qcom_phy *)phy_get_drvdata(generic_phy);
} }
EXPORT_SYMBOL_GPL(get_ufs_qcom_phy);
static static
int ufs_qcom_phy_base_init(struct platform_device *pdev, int ufs_qcom_phy_base_init(struct platform_device *pdev,
...@@ -131,40 +135,23 @@ 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; int err = 0;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_mem"); 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); phy_common->mmio = devm_ioremap_resource(dev, res);
if (IS_ERR((void const *)phy_common->mmio)) { if (IS_ERR((void const *)phy_common->mmio)) {
err = PTR_ERR((void const *)phy_common->mmio); err = PTR_ERR((void const *)phy_common->mmio);
phy_common->mmio = NULL; phy_common->mmio = NULL;
dev_err(dev, "%s: ioremap for phy_mem resource failed %d\n", dev_err(dev, "%s: ioremap for phy_mem resource failed %d\n",
__func__, err); __func__, err);
goto out; return err;
} }
/* "dev_ref_clk_ctrl_mem" is optional resource */ /* "dev_ref_clk_ctrl_mem" is optional resource */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"dev_ref_clk_ctrl_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); phy_common->dev_ref_clk_ctrl_mmio = devm_ioremap_resource(dev, res);
if (IS_ERR((void const *)phy_common->dev_ref_clk_ctrl_mmio)) { if (IS_ERR((void const *)phy_common->dev_ref_clk_ctrl_mmio))
err = PTR_ERR((void const *)phy_common->dev_ref_clk_ctrl_mmio);
phy_common->dev_ref_clk_ctrl_mmio = NULL; 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 0;
return err;
} }
static int __ufs_qcom_phy_clk_get(struct phy *phy, static int __ufs_qcom_phy_clk_get(struct phy *phy,
...@@ -228,6 +215,7 @@ ufs_qcom_phy_init_clks(struct phy *generic_phy, ...@@ -228,6 +215,7 @@ ufs_qcom_phy_init_clks(struct phy *generic_phy,
out: out:
return err; return err;
} }
EXPORT_SYMBOL_GPL(ufs_qcom_phy_init_clks);
int int
ufs_qcom_phy_init_vregulators(struct phy *generic_phy, ufs_qcom_phy_init_vregulators(struct phy *generic_phy,
...@@ -252,6 +240,7 @@ ufs_qcom_phy_init_vregulators(struct phy *generic_phy, ...@@ -252,6 +240,7 @@ ufs_qcom_phy_init_vregulators(struct phy *generic_phy,
out: out:
return err; return err;
} }
EXPORT_SYMBOL_GPL(ufs_qcom_phy_init_vregulators);
static int __ufs_qcom_phy_init_vreg(struct phy *phy, static int __ufs_qcom_phy_init_vreg(struct phy *phy,
struct ufs_qcom_phy_vreg *vreg, const char *name, bool optional) struct ufs_qcom_phy_vreg *vreg, const char *name, bool optional)
...@@ -647,6 +636,7 @@ int ufs_qcom_phy_remove(struct phy *generic_phy, ...@@ -647,6 +636,7 @@ int ufs_qcom_phy_remove(struct phy *generic_phy,
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(ufs_qcom_phy_remove);
int ufs_qcom_phy_exit(struct phy *generic_phy) int ufs_qcom_phy_exit(struct phy *generic_phy)
{ {
...@@ -657,6 +647,7 @@ int ufs_qcom_phy_exit(struct phy *generic_phy) ...@@ -657,6 +647,7 @@ int ufs_qcom_phy_exit(struct phy *generic_phy)
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(ufs_qcom_phy_exit);
int ufs_qcom_phy_is_pcs_ready(struct phy *generic_phy) int ufs_qcom_phy_is_pcs_ready(struct phy *generic_phy)
{ {
...@@ -725,6 +716,7 @@ int ufs_qcom_phy_power_on(struct phy *generic_phy) ...@@ -725,6 +716,7 @@ int ufs_qcom_phy_power_on(struct phy *generic_phy)
out: out:
return err; return err;
} }
EXPORT_SYMBOL_GPL(ufs_qcom_phy_power_on);
int ufs_qcom_phy_power_off(struct phy *generic_phy) int ufs_qcom_phy_power_off(struct phy *generic_phy)
{ {
...@@ -743,3 +735,4 @@ 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; return 0;
} }
EXPORT_SYMBOL_GPL(ufs_qcom_phy_power_off);
...@@ -37,10 +37,14 @@ static int samsung_usb2_phy_power_on(struct phy *phy) ...@@ -37,10 +37,14 @@ static int samsung_usb2_phy_power_on(struct phy *phy)
spin_lock(&drv->lock); spin_lock(&drv->lock);
ret = inst->cfg->power_on(inst); ret = inst->cfg->power_on(inst);
spin_unlock(&drv->lock); spin_unlock(&drv->lock);
if (ret)
goto err_power_on;
} }
return 0; return 0;
err_power_on:
clk_disable_unprepare(drv->ref_clk);
err_instance_clk: err_instance_clk:
clk_disable_unprepare(drv->clk); clk_disable_unprepare(drv->clk);
err_main_clk: err_main_clk:
...@@ -51,7 +55,7 @@ static int samsung_usb2_phy_power_off(struct phy *phy) ...@@ -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_instance *inst = phy_get_drvdata(phy);
struct samsung_usb2_phy_driver *drv = inst->drv; 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", dev_dbg(drv->dev, "Request to power_off \"%s\" usb phy\n",
inst->cfg->label); inst->cfg->label);
...@@ -59,10 +63,12 @@ static int samsung_usb2_phy_power_off(struct phy *phy) ...@@ -59,10 +63,12 @@ static int samsung_usb2_phy_power_off(struct phy *phy)
spin_lock(&drv->lock); spin_lock(&drv->lock);
ret = inst->cfg->power_off(inst); ret = inst->cfg->power_off(inst);
spin_unlock(&drv->lock); spin_unlock(&drv->lock);
if (ret)
return ret;
} }
clk_disable_unprepare(drv->ref_clk); clk_disable_unprepare(drv->ref_clk);
clk_disable_unprepare(drv->clk); clk_disable_unprepare(drv->clk);
return ret; return 0;
} }
static struct phy_ops samsung_usb2_phy_ops = { static struct phy_ops samsung_usb2_phy_ops = {
......
...@@ -192,14 +192,14 @@ static struct phy *spear1310_miphy_xlate(struct device *dev, ...@@ -192,14 +192,14 @@ static struct phy *spear1310_miphy_xlate(struct device *dev,
if (args->args_count < 1) { if (args->args_count < 1) {
dev_err(dev, "DT did not pass correct no of args\n"); dev_err(dev, "DT did not pass correct no of args\n");
return NULL; return ERR_PTR(-ENODEV);
} }
priv->mode = args->args[0]; priv->mode = args->args[0];
if (priv->mode != SATA && priv->mode != PCIE) { if (priv->mode != SATA && priv->mode != PCIE) {
dev_err(dev, "DT did not pass correct phy mode\n"); dev_err(dev, "DT did not pass correct phy mode\n");
return NULL; return ERR_PTR(-ENODEV);
} }
return priv->phy; return priv->phy;
......
...@@ -229,14 +229,14 @@ static struct phy *spear1340_miphy_xlate(struct device *dev, ...@@ -229,14 +229,14 @@ static struct phy *spear1340_miphy_xlate(struct device *dev,
if (args->args_count < 1) { if (args->args_count < 1) {
dev_err(dev, "DT did not pass correct no of args\n"); dev_err(dev, "DT did not pass correct no of args\n");
return NULL; return ERR_PTR(-ENODEV);
} }
priv->mode = args->args[0]; priv->mode = args->args[0];
if (priv->mode != SATA && priv->mode != PCIE) { if (priv->mode != SATA && priv->mode != PCIE) {
dev_err(dev, "DT did not pass correct phy mode\n"); dev_err(dev, "DT did not pass correct phy mode\n");
return NULL; return ERR_PTR(-ENODEV);
} }
return priv->phy; return priv->phy;
......
...@@ -87,8 +87,12 @@ static int stih41x_usb_phy_power_on(struct phy *phy) ...@@ -87,8 +87,12 @@ static int stih41x_usb_phy_power_on(struct phy *phy)
return ret; return ret;
} }
return regmap_update_bits(phy_dev->regmap, phy_dev->cfg->syscfg, ret = regmap_update_bits(phy_dev->regmap, phy_dev->cfg->syscfg,
phy_dev->cfg->oscok, phy_dev->cfg->oscok); 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) static int stih41x_usb_phy_power_off(struct phy *phy)
......
/*
* 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 = { ...@@ -287,9 +287,7 @@ static struct phy_ops ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
#ifdef CONFIG_OF
static const struct of_device_id ti_pipe3_id_table[]; static const struct of_device_id ti_pipe3_id_table[];
#endif
static int ti_pipe3_probe(struct platform_device *pdev) static int ti_pipe3_probe(struct platform_device *pdev)
{ {
...@@ -311,8 +309,7 @@ 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); spin_lock_init(&phy->lock);
if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie")) { if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
match = of_match_device(of_match_ptr(ti_pipe3_id_table), match = of_match_device(ti_pipe3_id_table, &pdev->dev);
&pdev->dev);
if (!match) if (!match)
return -EINVAL; return -EINVAL;
...@@ -570,7 +567,6 @@ static const struct dev_pm_ops ti_pipe3_pm_ops = { ...@@ -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) SET_SYSTEM_SLEEP_PM_OPS(ti_pipe3_suspend, ti_pipe3_resume)
}; };
#ifdef CONFIG_OF
static const struct of_device_id ti_pipe3_id_table[] = { static const struct of_device_id ti_pipe3_id_table[] = {
{ {
.compatible = "ti,phy-usb3", .compatible = "ti,phy-usb3",
...@@ -590,7 +586,6 @@ static const struct of_device_id ti_pipe3_id_table[] = { ...@@ -590,7 +586,6 @@ static const struct of_device_id ti_pipe3_id_table[] = {
{} {}
}; };
MODULE_DEVICE_TABLE(of, ti_pipe3_id_table); MODULE_DEVICE_TABLE(of, ti_pipe3_id_table);
#endif
static struct platform_driver ti_pipe3_driver = { static struct platform_driver ti_pipe3_driver = {
.probe = ti_pipe3_probe, .probe = ti_pipe3_probe,
...@@ -598,7 +593,7 @@ static struct platform_driver ti_pipe3_driver = { ...@@ -598,7 +593,7 @@ static struct platform_driver ti_pipe3_driver = {
.driver = { .driver = {
.name = "ti-pipe3", .name = "ti-pipe3",
.pm = &ti_pipe3_pm_ops, .pm = &ti_pipe3_pm_ops,
.of_match_table = of_match_ptr(ti_pipe3_id_table), .of_match_table = ti_pipe3_id_table,
}, },
}; };
......
...@@ -1657,7 +1657,6 @@ static int xgene_phy_probe(struct platform_device *pdev) ...@@ -1657,7 +1657,6 @@ static int xgene_phy_probe(struct platform_device *pdev)
struct phy_provider *phy_provider; struct phy_provider *phy_provider;
struct xgene_phy_ctx *ctx; struct xgene_phy_ctx *ctx;
struct resource *res; struct resource *res;
int rc = 0;
u32 default_spd[] = DEFAULT_SATA_SPD_SEL; u32 default_spd[] = DEFAULT_SATA_SPD_SEL;
u32 default_txboost_gain[] = DEFAULT_SATA_TXBOOST_GAIN; u32 default_txboost_gain[] = DEFAULT_SATA_TXBOOST_GAIN;
u32 default_txeye_direction[] = DEFAULT_SATA_TXEYEDIRECTION; u32 default_txeye_direction[] = DEFAULT_SATA_TXEYEDIRECTION;
...@@ -1676,10 +1675,8 @@ static int xgene_phy_probe(struct platform_device *pdev) ...@@ -1676,10 +1675,8 @@ static int xgene_phy_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ctx->sds_base = devm_ioremap_resource(&pdev->dev, res); ctx->sds_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(ctx->sds_base)) { if (IS_ERR(ctx->sds_base))
rc = PTR_ERR(ctx->sds_base); return PTR_ERR(ctx->sds_base);
goto error;
}
/* Retrieve optional clock */ /* Retrieve optional clock */
ctx->clk = clk_get(&pdev->dev, NULL); ctx->clk = clk_get(&pdev->dev, NULL);
...@@ -1709,22 +1706,12 @@ static int xgene_phy_probe(struct platform_device *pdev) ...@@ -1709,22 +1706,12 @@ static int xgene_phy_probe(struct platform_device *pdev)
ctx->phy = devm_phy_create(ctx->dev, NULL, &xgene_phy_ops); ctx->phy = devm_phy_create(ctx->dev, NULL, &xgene_phy_ops);
if (IS_ERR(ctx->phy)) { if (IS_ERR(ctx->phy)) {
dev_dbg(&pdev->dev, "Failed to create PHY\n"); dev_dbg(&pdev->dev, "Failed to create PHY\n");
rc = PTR_ERR(ctx->phy); return PTR_ERR(ctx->phy);
goto error;
} }
phy_set_drvdata(ctx->phy, ctx); phy_set_drvdata(ctx->phy, ctx);
phy_provider = devm_of_phy_provider_register(ctx->dev, phy_provider = devm_of_phy_provider_register(ctx->dev, xgene_phy_xlate);
xgene_phy_xlate); return PTR_ERR_OR_ZERO(phy_provider);
if (IS_ERR(phy_provider)) {
rc = PTR_ERR(phy_provider);
goto error;
}
return 0;
error:
return rc;
} }
static const struct of_device_id xgene_phy_of_match[] = { static const struct of_device_id xgene_phy_of_match[] = {
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
# Object files in subdirectories # Object files in subdirectories
obj-$(CONFIG_USB) += core/ obj-$(CONFIG_USB) += core/
obj-$(CONFIG_USB_SUPPORT) += phy/
obj-$(CONFIG_USB_DWC3) += dwc3/ obj-$(CONFIG_USB_DWC3) += dwc3/
obj-$(CONFIG_USB_DWC2) += dwc2/ obj-$(CONFIG_USB_DWC2) += dwc2/
...@@ -48,7 +49,6 @@ obj-$(CONFIG_USB_MICROTEK) += image/ ...@@ -48,7 +49,6 @@ obj-$(CONFIG_USB_MICROTEK) += image/
obj-$(CONFIG_USB_SERIAL) += serial/ obj-$(CONFIG_USB_SERIAL) += serial/
obj-$(CONFIG_USB) += misc/ obj-$(CONFIG_USB) += misc/
obj-$(CONFIG_USB_SUPPORT) += phy/
obj-$(CONFIG_EARLY_PRINTK_DBGP) += early/ obj-$(CONFIG_EARLY_PRINTK_DBGP) += early/
obj-$(CONFIG_USB_ATM) += atm/ obj-$(CONFIG_USB_ATM) += atm/
......
...@@ -952,7 +952,7 @@ static void uea_load_page_e1(struct work_struct *work) ...@@ -952,7 +952,7 @@ static void uea_load_page_e1(struct work_struct *work)
int i; int i;
/* reload firmware when reboot start and it's loaded already */ /* 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); release_firmware(sc->dsp_firm);
sc->dsp_firm = NULL; sc->dsp_firm = NULL;
} }
...@@ -1074,7 +1074,7 @@ static void uea_load_page_e4(struct work_struct *work) ...@@ -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); uea_dbg(INS_TO_USBDEV(sc), "sending DSP page %u\n", pageno);
/* reload firmware when reboot start and it's loaded already */ /* reload firmware when reboot start and it's loaded already */
if (pageno == 0 && sc->dsp_firm) { if (pageno == 0) {
release_firmware(sc->dsp_firm); release_firmware(sc->dsp_firm);
sc->dsp_firm = NULL; sc->dsp_firm = NULL;
} }
......
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
static __u8 c67x00_hub_des[] = { static __u8 c67x00_hub_des[] = {
0x09, /* __u8 bLength; */ 0x09, /* __u8 bLength; */
0x29, /* __u8 bDescriptorType; Hub-descriptor */ USB_DT_HUB, /* __u8 bDescriptorType; Hub-descriptor */
0x02, /* __u8 bNbrPorts; */ 0x02, /* __u8 bNbrPorts; */
0x00, /* __u16 wHubCharacteristics; */ 0x00, /* __u16 wHubCharacteristics; */
0x00, /* (per-port OC, no power switching) */ 0x00, /* (per-port OC, no power switching) */
......
...@@ -10,6 +10,17 @@ config USB_CHIPIDEA ...@@ -10,6 +10,17 @@ config USB_CHIPIDEA
if 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 config USB_CHIPIDEA_UDC
bool "ChipIdea device controller" bool "ChipIdea device controller"
depends on USB_GADGET depends on USB_GADGET
......
...@@ -14,11 +14,6 @@ obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_usb2.o ...@@ -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_msm.o
obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_zevio.o obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_zevio.o
# PCI doesn't provide stubs, need to check obj-$(CONFIG_USB_CHIPIDEA_PCI) += ci_hdrc_pci.o
ifneq ($(CONFIG_PCI),)
obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_pci.o
endif
ifneq ($(CONFIG_OF),) obj-$(CONFIG_USB_CHIPIDEA_OF) += usbmisc_imx.o ci_hdrc_imx.o
obj-$(CONFIG_USB_CHIPIDEA) += usbmisc_imx.o ci_hdrc_imx.o
endif
...@@ -15,6 +15,16 @@ ...@@ -15,6 +15,16 @@
#include <linux/usb/ehci_def.h> #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 */ /* HCCPARAMS */
#define HCCPARAMS_LEN BIT(17) #define HCCPARAMS_LEN BIT(17)
...@@ -53,6 +63,7 @@ ...@@ -53,6 +63,7 @@
#define PORTSC_HSP BIT(9) #define PORTSC_HSP BIT(9)
#define PORTSC_PP BIT(12) #define PORTSC_PP BIT(12)
#define PORTSC_PTC (0x0FUL << 16) #define PORTSC_PTC (0x0FUL << 16)
#define PORTSC_WKCN BIT(20)
#define PORTSC_PHCD(d) ((d) ? BIT(22) : BIT(23)) #define PORTSC_PHCD(d) ((d) ? BIT(22) : BIT(23))
/* PTS and PTW for non lpm version only */ /* PTS and PTW for non lpm version only */
#define PORTSC_PFSC BIT(24) #define PORTSC_PFSC BIT(24)
......
...@@ -29,6 +29,15 @@ ...@@ -29,6 +29,15 @@
/****************************************************************************** /******************************************************************************
* REGISTERS * 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 */ /* register indices */
enum ci_hw_regs { enum ci_hw_regs {
CAP_CAPLENGTH, CAP_CAPLENGTH,
...@@ -97,6 +106,18 @@ enum ci_role { ...@@ -97,6 +106,18 @@ enum ci_role {
CI_ROLE_END, 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 * struct ci_role_driver - host/gadget role driver
* @start: start this role * @start: start this role
...@@ -141,7 +162,10 @@ struct hw_bank { ...@@ -141,7 +162,10 @@ struct hw_bank {
* @role: current role * @role: current role
* @is_otg: if the device is otg-capable * @is_otg: if the device is otg-capable
* @fsm: otg finite state machine * @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 * @work: work for role changing
* @wq: workqueue thread * @wq: workqueue thread
* @qh_pool: allocation pool for queue heads * @qh_pool: allocation pool for queue heads
...@@ -169,6 +193,10 @@ struct hw_bank { ...@@ -169,6 +193,10 @@ struct hw_bank {
* @b_sess_valid_event: indicates there is a vbus event, and handled * @b_sess_valid_event: indicates there is a vbus event, and handled
* at ci_otg_work * at ci_otg_work
* @imx28_write_fix: Freescale imx28 needs swp instruction for writing * @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 ci_hdrc {
struct device *dev; struct device *dev;
...@@ -180,7 +208,10 @@ struct ci_hdrc { ...@@ -180,7 +208,10 @@ struct ci_hdrc {
bool is_otg; bool is_otg;
struct usb_otg otg; struct usb_otg otg;
struct otg_fsm fsm; 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 work_struct work;
struct workqueue_struct *wq; struct workqueue_struct *wq;
...@@ -211,6 +242,10 @@ struct ci_hdrc { ...@@ -211,6 +242,10 @@ struct ci_hdrc {
bool id_event; bool id_event;
bool b_sess_valid_event; bool b_sess_valid_event;
bool imx28_write_fix; 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) 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) ...@@ -247,6 +282,36 @@ static inline void ci_role_stop(struct ci_hdrc *ci)
ci->roles[role]->stop(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 * hw_read: reads from a hw register
* @ci: the controller * @ci: the controller
......
...@@ -23,22 +23,40 @@ ...@@ -23,22 +23,40 @@
#include "ci.h" #include "ci.h"
#include "ci_hdrc_imx.h" #include "ci_hdrc_imx.h"
#define CI_HDRC_IMX_IMX28_WRITE_FIX BIT(0)
struct ci_hdrc_imx_platform_flag { struct ci_hdrc_imx_platform_flag {
unsigned int flags; 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 imx27_usb_data = {
}; };
static const struct ci_hdrc_imx_platform_flag imx28_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[] = { static const struct of_device_id ci_hdrc_imx_dt_ids[] = {
{ .compatible = "fsl,imx28-usb", .data = &imx28_usb_data}, { .compatible = "fsl,imx28-usb", .data = &imx28_usb_data},
{ .compatible = "fsl,imx27-usb", .data = &imx27_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 */ } { /* sentinel */ }
}; };
MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids); MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids);
...@@ -48,6 +66,8 @@ struct ci_hdrc_imx_data { ...@@ -48,6 +66,8 @@ struct ci_hdrc_imx_data {
struct platform_device *ci_pdev; struct platform_device *ci_pdev;
struct clk *clk; struct clk *clk;
struct imx_usbmisc_data *usbmisc_data; struct imx_usbmisc_data *usbmisc_data;
bool supports_runtime_pm;
bool in_lpm;
}; };
/* Common functions shared by usbmisc drivers */ /* Common functions shared by usbmisc drivers */
...@@ -145,21 +165,18 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) ...@@ -145,21 +165,18 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
} }
pdata.usb_phy = data->phy; pdata.usb_phy = data->phy;
pdata.flags |= imx_platform_flag->flags;
if (imx_platform_flag->flags & CI_HDRC_IMX_IMX28_WRITE_FIX) if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM)
pdata.flags |= CI_HDRC_IMX28_WRITE_FIX; data->supports_runtime_pm = true;
ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
if (ret) if (ret)
goto err_clk; goto err_clk;
if (data->usbmisc_data) { ret = imx_usbmisc_init(data->usbmisc_data);
ret = imx_usbmisc_init(data->usbmisc_data); if (ret) {
if (ret) { dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", ret);
dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", goto err_clk;
ret);
goto err_clk;
}
} }
data->ci_pdev = ci_hdrc_add_device(&pdev->dev, data->ci_pdev = ci_hdrc_add_device(&pdev->dev,
...@@ -173,19 +190,20 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) ...@@ -173,19 +190,20 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
goto err_clk; goto err_clk;
} }
if (data->usbmisc_data) { ret = imx_usbmisc_init_post(data->usbmisc_data);
ret = imx_usbmisc_init_post(data->usbmisc_data); if (ret) {
if (ret) { dev_err(&pdev->dev, "usbmisc post failed, ret=%d\n", ret);
dev_err(&pdev->dev, "usbmisc post failed, ret=%d\n", goto disable_device;
ret);
goto disable_device;
}
} }
platform_set_drvdata(pdev, data); platform_set_drvdata(pdev, data);
pm_runtime_no_callbacks(&pdev->dev); if (data->supports_runtime_pm) {
pm_runtime_enable(&pdev->dev); pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
}
device_set_wakeup_capable(&pdev->dev, true);
return 0; return 0;
...@@ -200,14 +218,18 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev) ...@@ -200,14 +218,18 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev)
{ {
struct ci_hdrc_imx_data *data = platform_get_drvdata(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); ci_hdrc_remove_device(data->ci_pdev);
clk_disable_unprepare(data->clk); clk_disable_unprepare(data->clk);
return 0; return 0;
} }
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM
static int imx_controller_suspend(struct device *dev) static int imx_controller_suspend(struct device *dev)
{ {
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
...@@ -215,6 +237,7 @@ static int imx_controller_suspend(struct device *dev) ...@@ -215,6 +237,7 @@ static int imx_controller_suspend(struct device *dev)
dev_dbg(dev, "at %s\n", __func__); dev_dbg(dev, "at %s\n", __func__);
clk_disable_unprepare(data->clk); clk_disable_unprepare(data->clk);
data->in_lpm = true;
return 0; return 0;
} }
...@@ -222,25 +245,103 @@ static int imx_controller_suspend(struct device *dev) ...@@ -222,25 +245,103 @@ static int imx_controller_suspend(struct device *dev)
static int imx_controller_resume(struct device *dev) static int imx_controller_resume(struct device *dev)
{ {
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
int ret = 0;
dev_dbg(dev, "at %s\n", __func__); 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) 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); return imx_controller_suspend(dev);
} }
static int ci_hdrc_imx_resume(struct device *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 */ #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 = { 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_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 = { static struct platform_driver ci_hdrc_imx_driver = {
.probe = ci_hdrc_imx_probe, .probe = ci_hdrc_imx_probe,
......
...@@ -22,5 +22,6 @@ struct imx_usbmisc_data { ...@@ -22,5 +22,6 @@ struct imx_usbmisc_data {
int imx_usbmisc_init(struct imx_usbmisc_data *); int imx_usbmisc_init(struct imx_usbmisc_data *);
int imx_usbmisc_init_post(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 */ #endif /* __DRIVER_USB_CHIPIDEA_CI_HDRC_IMX_H */
...@@ -16,10 +16,16 @@ ...@@ -16,10 +16,16 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/usb/gadget.h> #include <linux/usb/gadget.h>
#include <linux/usb/chipidea.h> #include <linux/usb/chipidea.h>
#include <linux/usb/usb_phy_generic.h>
/* driver name */ /* driver name */
#define UDC_DRIVER_NAME "ci_hdrc_pci" #define UDC_DRIVER_NAME "ci_hdrc_pci"
struct ci_hdrc_pci {
struct platform_device *ci;
struct platform_device *phy;
};
/****************************************************************************** /******************************************************************************
* PCI block * PCI block
*****************************************************************************/ *****************************************************************************/
...@@ -52,7 +58,7 @@ static int ci_hdrc_pci_probe(struct pci_dev *pdev, ...@@ -52,7 +58,7 @@ static int ci_hdrc_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id) const struct pci_device_id *id)
{ {
struct ci_hdrc_platform_data *platdata = (void *)id->driver_data; struct ci_hdrc_platform_data *platdata = (void *)id->driver_data;
struct platform_device *plat_ci; struct ci_hdrc_pci *ci;
struct resource res[3]; struct resource res[3];
int retval = 0, nres = 2; int retval = 0, nres = 2;
...@@ -61,6 +67,10 @@ static int ci_hdrc_pci_probe(struct pci_dev *pdev, ...@@ -61,6 +67,10 @@ static int ci_hdrc_pci_probe(struct pci_dev *pdev,
return -ENODEV; return -ENODEV;
} }
ci = devm_kzalloc(&pdev->dev, sizeof(*ci), GFP_KERNEL);
if (!ci)
return -ENOMEM;
retval = pcim_enable_device(pdev); retval = pcim_enable_device(pdev);
if (retval) if (retval)
return retval; return retval;
...@@ -73,6 +83,11 @@ static int ci_hdrc_pci_probe(struct pci_dev *pdev, ...@@ -73,6 +83,11 @@ static int ci_hdrc_pci_probe(struct pci_dev *pdev,
pci_set_master(pdev); pci_set_master(pdev);
pci_try_set_mwi(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)); memset(res, 0, sizeof(res));
res[0].start = pci_resource_start(pdev, 0); res[0].start = pci_resource_start(pdev, 0);
res[0].end = pci_resource_end(pdev, 0); res[0].end = pci_resource_end(pdev, 0);
...@@ -80,13 +95,14 @@ static int ci_hdrc_pci_probe(struct pci_dev *pdev, ...@@ -80,13 +95,14 @@ static int ci_hdrc_pci_probe(struct pci_dev *pdev,
res[1].start = pdev->irq; res[1].start = pdev->irq;
res[1].flags = IORESOURCE_IRQ; res[1].flags = IORESOURCE_IRQ;
plat_ci = ci_hdrc_add_device(&pdev->dev, res, nres, platdata); ci->ci = ci_hdrc_add_device(&pdev->dev, res, nres, platdata);
if (IS_ERR(plat_ci)) { if (IS_ERR(ci->ci)) {
dev_err(&pdev->dev, "ci_hdrc_add_device failed!\n"); 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; return 0;
} }
...@@ -101,9 +117,10 @@ static int ci_hdrc_pci_probe(struct pci_dev *pdev, ...@@ -101,9 +117,10 @@ static int ci_hdrc_pci_probe(struct pci_dev *pdev,
*/ */
static void ci_hdrc_pci_remove(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 @@ ...@@ -18,7 +18,7 @@
static struct ci_hdrc_platform_data ci_hdrc_zevio_platdata = { static struct ci_hdrc_platform_data ci_hdrc_zevio_platdata = {
.name = "ci_hdrc_zevio", .name = "ci_hdrc_zevio",
.flags = CI_HDRC_REGS_SHARED, .flags = CI_HDRC_REGS_SHARED | CI_HDRC_FORCE_FULLSPEED,
.capoffset = DEF_CAPOFFSET, .capoffset = DEF_CAPOFFSET,
}; };
......
...@@ -137,6 +137,22 @@ static int hw_alloc_regmap(struct ci_hdrc *ci, bool is_lpm) ...@@ -137,6 +137,22 @@ static int hw_alloc_regmap(struct ci_hdrc *ci, bool is_lpm)
return 0; 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 * hw_read_intr_enable: returns interrupt enable register
* *
...@@ -251,8 +267,11 @@ static int hw_device_init(struct ci_hdrc *ci, void __iomem *base) ...@@ -251,8 +267,11 @@ static int hw_device_init(struct ci_hdrc *ci, void __iomem *base)
/* Clear all interrupts status bits*/ /* Clear all interrupts status bits*/
hw_write(ci, OP_USBSTS, 0xffffffff, 0xffffffff); hw_write(ci, OP_USBSTS, 0xffffffff, 0xffffffff);
dev_dbg(ci->dev, "ChipIdea HDRC found, lpm: %d; cap: %p op: %p\n", ci->rev = ci_get_revision(ci);
ci->hw_bank.lpm, ci->hw_bank.cap, ci->hw_bank.op);
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 ? */ /* setup lock mode ? */
...@@ -491,6 +510,13 @@ static irqreturn_t ci_irq(int irq, void *data) ...@@ -491,6 +510,13 @@ static irqreturn_t ci_irq(int irq, void *data)
irqreturn_t ret = IRQ_NONE; irqreturn_t ret = IRQ_NONE;
u32 otgsc = 0; 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) { if (ci->is_otg) {
otgsc = hw_read_otgsc(ci, ~0); otgsc = hw_read_otgsc(ci, ~0);
if (ci_otg_is_fsm_mode(ci)) { if (ci_otg_is_fsm_mode(ci)) {
...@@ -642,8 +668,12 @@ static void ci_get_otg_capable(struct ci_hdrc *ci) ...@@ -642,8 +668,12 @@ static void ci_get_otg_capable(struct ci_hdrc *ci)
ci->is_otg = (hw_read(ci, CAP_DCCPARAMS, ci->is_otg = (hw_read(ci, CAP_DCCPARAMS,
DCCPARAMS_DC | DCCPARAMS_HC) DCCPARAMS_DC | DCCPARAMS_HC)
== (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"); 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) static int ci_hdrc_probe(struct platform_device *pdev)
...@@ -673,6 +703,8 @@ 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->platdata = dev_get_platdata(dev);
ci->imx28_write_fix = !!(ci->platdata->flags & ci->imx28_write_fix = !!(ci->platdata->flags &
CI_HDRC_IMX28_WRITE_FIX); CI_HDRC_IMX28_WRITE_FIX);
ci->supports_runtime_pm = !!(ci->platdata->flags &
CI_HDRC_SUPPORTS_RUNTIME_PM);
ret = hw_device_init(ci, base); ret = hw_device_init(ci, base);
if (ret < 0) { if (ret < 0) {
...@@ -740,9 +772,6 @@ static int ci_hdrc_probe(struct platform_device *pdev) ...@@ -740,9 +772,6 @@ static int ci_hdrc_probe(struct platform_device *pdev)
} }
if (ci->is_otg && ci->roles[CI_ROLE_GADGET]) { 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); ret = ci_hdrc_otg_init(ci);
if (ret) { if (ret) {
dev_err(dev, "init otg fails, ret = %d\n", ret); dev_err(dev, "init otg fails, ret = %d\n", ret);
...@@ -769,11 +798,11 @@ static int ci_hdrc_probe(struct platform_device *pdev) ...@@ -769,11 +798,11 @@ static int ci_hdrc_probe(struct platform_device *pdev)
: CI_ROLE_GADGET; : 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)) { 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); ret = ci_role_start(ci, ci->role);
if (ret) { if (ret) {
dev_err(dev, "can't start %s role\n", dev_err(dev, "can't start %s role\n",
...@@ -788,9 +817,19 @@ static int ci_hdrc_probe(struct platform_device *pdev) ...@@ -788,9 +817,19 @@ static int ci_hdrc_probe(struct platform_device *pdev)
if (ret) if (ret)
goto stop; 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)) if (ci_otg_is_fsm_mode(ci))
ci_hdrc_otg_fsm_start(ci); ci_hdrc_otg_fsm_start(ci);
device_set_wakeup_capable(&pdev->dev, true);
ret = dbg_create_files(ci); ret = dbg_create_files(ci);
if (!ret) if (!ret)
return 0; return 0;
...@@ -807,6 +846,12 @@ static int ci_hdrc_remove(struct platform_device *pdev) ...@@ -807,6 +846,12 @@ static int ci_hdrc_remove(struct platform_device *pdev)
{ {
struct ci_hdrc *ci = platform_get_drvdata(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); dbg_remove_files(ci);
ci_role_destroy(ci); ci_role_destroy(ci);
ci_hdrc_enter_lpm(ci, true); ci_hdrc_enter_lpm(ci, true);
...@@ -815,13 +860,41 @@ static int ci_hdrc_remove(struct platform_device *pdev) ...@@ -815,13 +860,41 @@ static int ci_hdrc_remove(struct platform_device *pdev)
return 0; 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) static void ci_controller_suspend(struct ci_hdrc *ci)
{ {
disable_irq(ci->irq);
ci_hdrc_enter_lpm(ci, true); ci_hdrc_enter_lpm(ci, true);
usb_phy_set_suspend(ci->usb_phy, 1);
if (ci->usb_phy) ci->in_lpm = true;
usb_phy_set_suspend(ci->usb_phy, 1); enable_irq(ci->irq);
} }
static int ci_controller_resume(struct device *dev) static int ci_controller_resume(struct device *dev)
...@@ -830,23 +903,59 @@ 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__); 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) { if (ci->usb_phy) {
usb_phy_set_suspend(ci->usb_phy, 0); usb_phy_set_suspend(ci->usb_phy, 0);
usb_phy_set_wakeup(ci->usb_phy, false); usb_phy_set_wakeup(ci->usb_phy, false);
hw_wait_phy_stable(); 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; return 0;
} }
#ifdef CONFIG_PM_SLEEP
static int ci_suspend(struct device *dev) static int ci_suspend(struct device *dev)
{ {
struct ci_hdrc *ci = dev_get_drvdata(dev); struct ci_hdrc *ci = dev_get_drvdata(dev);
if (ci->wq) if (ci->wq)
flush_workqueue(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); ci_controller_suspend(ci);
...@@ -855,13 +964,57 @@ static int ci_suspend(struct device *dev) ...@@ -855,13 +964,57 @@ static int ci_suspend(struct device *dev)
static int ci_resume(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 */ #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 = { static const struct dev_pm_ops ci_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(ci_suspend, ci_resume) 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 = { static struct platform_driver ci_hdrc_driver = {
.probe = ci_hdrc_probe, .probe = ci_hdrc_probe,
.remove = ci_hdrc_remove, .remove = ci_hdrc_remove,
......
...@@ -336,8 +336,8 @@ static int ci_registers_show(struct seq_file *s, void *unused) ...@@ -336,8 +336,8 @@ static int ci_registers_show(struct seq_file *s, void *unused)
struct ci_hdrc *ci = s->private; struct ci_hdrc *ci = s->private;
u32 tmp_reg; u32 tmp_reg;
if (!ci) if (!ci || ci->in_lpm)
return 0; return -EPERM;
/* ------ Registers ----- */ /* ------ Registers ----- */
tmp_reg = hw_read_intr_enable(ci); tmp_reg = hw_read_intr_enable(ci);
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include "host.h" #include "host.h"
static struct hc_driver __read_mostly ci_ehci_hc_driver; static struct hc_driver __read_mostly ci_ehci_hc_driver;
static int (*orig_bus_suspend)(struct usb_hcd *hcd);
struct ehci_ci_priv { struct ehci_ci_priv {
struct regulator *reg_vbus; struct regulator *reg_vbus;
...@@ -43,11 +44,10 @@ static int ehci_ci_portpower(struct usb_hcd *hcd, int portnum, bool enable) ...@@ -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_hcd *ehci = hcd_to_ehci(hcd);
struct ehci_ci_priv *priv = (struct ehci_ci_priv *)ehci->priv; struct ehci_ci_priv *priv = (struct ehci_ci_priv *)ehci->priv;
struct device *dev = hcd->self.controller; struct device *dev = hcd->self.controller;
struct ci_hdrc *ci = dev_get_drvdata(dev);
int ret = 0; int ret = 0;
int port = HCS_N_PORTS(ehci->hcs_params); 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) { if (port > 1) {
dev_warn(dev, dev_warn(dev,
"Not support multi-port regulator control\n"); "Not support multi-port regulator control\n");
...@@ -113,12 +113,23 @@ static int host_start(struct ci_hdrc *ci) ...@@ -113,12 +113,23 @@ static int host_start(struct ci_hdrc *ci)
priv = (struct ehci_ci_priv *)ehci->priv; priv = (struct ehci_ci_priv *)ehci->priv;
priv->reg_vbus = NULL; priv->reg_vbus = NULL;
if (ci->platdata->reg_vbus) if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci)) {
priv->reg_vbus = ci->platdata->reg_vbus; 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); ret = usb_add_hcd(hcd, 0, 0);
if (ret) { if (ret) {
goto put_hcd; goto disable_reg;
} else { } else {
struct usb_otg *otg = &ci->otg; struct usb_otg *otg = &ci->otg;
...@@ -133,8 +144,15 @@ static int host_start(struct ci_hdrc *ci) ...@@ -133,8 +144,15 @@ static int host_start(struct ci_hdrc *ci)
if (ci->platdata->flags & CI_HDRC_DISABLE_STREAMING) if (ci->platdata->flags & CI_HDRC_DISABLE_STREAMING)
hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS); 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; 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: put_hcd:
usb_put_hcd(hcd); usb_put_hcd(hcd);
...@@ -148,6 +166,9 @@ static void host_stop(struct ci_hdrc *ci) ...@@ -148,6 +166,9 @@ static void host_stop(struct ci_hdrc *ci)
if (hcd) { if (hcd) {
usb_remove_hcd(hcd); usb_remove_hcd(hcd);
usb_put_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) ...@@ -158,6 +179,47 @@ void ci_hdrc_host_destroy(struct ci_hdrc *ci)
host_stop(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) int ci_hdrc_host_init(struct ci_hdrc *ci)
{ {
struct ci_role_driver *rdrv; struct ci_role_driver *rdrv;
...@@ -176,6 +238,8 @@ int ci_hdrc_host_init(struct ci_hdrc *ci) ...@@ -176,6 +238,8 @@ int ci_hdrc_host_init(struct ci_hdrc *ci)
ci->roles[CI_ROLE_HOST] = rdrv; ci->roles[CI_ROLE_HOST] = rdrv;
ehci_init_driver(&ci_ehci_hc_driver, &ehci_ci_overrides); 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; return 0;
} }
...@@ -96,6 +96,7 @@ static void ci_otg_work(struct work_struct *work) ...@@ -96,6 +96,7 @@ static void ci_otg_work(struct work_struct *work)
return; return;
} }
pm_runtime_get_sync(ci->dev);
if (ci->id_event) { if (ci->id_event) {
ci->id_event = false; ci->id_event = false;
ci_handle_id_switch(ci); ci_handle_id_switch(ci);
...@@ -104,6 +105,7 @@ static void ci_otg_work(struct work_struct *work) ...@@ -104,6 +105,7 @@ static void ci_otg_work(struct work_struct *work)
ci_handle_vbus_change(ci); ci_handle_vbus_change(ci);
} else } else
dev_err(ci->dev, "unexpected event occurs at %s\n", __func__); dev_err(ci->dev, "unexpected event occurs at %s\n", __func__);
pm_runtime_put_sync(ci->dev);
enable_irq(ci->irq); enable_irq(ci->irq);
} }
......
...@@ -30,22 +30,6 @@ ...@@ -30,22 +30,6 @@
#include "otg.h" #include "otg.h"
#include "otg_fsm.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 */ /* Add for otg: interact with user space app */
static ssize_t static ssize_t
get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf) get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf)
...@@ -203,230 +187,228 @@ static struct attribute_group inputs_attr_group = { ...@@ -203,230 +187,228 @@ static struct attribute_group inputs_attr_group = {
.attrs = inputs_attrs, .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 * 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; unsigned long flags, timer_sec, timer_nsec;
struct ci_otg_fsm_timer *timer = ci->fsm_timer->timer_list[t];
struct list_head *active_timers = &ci->fsm_timer->active_timers;
if (t >= NUM_CI_OTG_FSM_TIMERS) if (t >= NUM_OTG_FSM_TIMERS)
return; return;
/* spin_lock_irqsave(&ci->lock, flags);
* Check if the timer is already in the active list, timer_sec = otg_timer_ms[t] / MSEC_PER_SEC;
* if so update timer count timer_nsec = (otg_timer_ms[t] % MSEC_PER_SEC) * NSEC_PER_MSEC;
*/ ci->hr_timeouts[t] = ktime_add(ktime_get(),
list_for_each_entry(tmp_timer, active_timers, list) ktime_set(timer_sec, timer_nsec));
if (tmp_timer == timer) { ci->enabled_otg_timer_bits |= (1 << t);
timer->count = timer->expires; if ((ci->next_otg_timer == NUM_OTG_FSM_TIMERS) ||
return; (ci->hr_timeouts[ci->next_otg_timer].tv64 >
} ci->hr_timeouts[t].tv64)) {
ci->next_otg_timer = t;
timer->count = timer->expires; hrtimer_start_range_ns(&ci->otg_fsm_hrtimer,
list_add_tail(&timer->list, active_timers); ci->hr_timeouts[t], NSEC_PER_MSEC,
HRTIMER_MODE_ABS);
/* Enable 1ms irq */ }
if (!(hw_read_otgsc(ci, OTGSC_1MSIE))) spin_unlock_irqrestore(&ci->lock, flags);
hw_write_otgsc(ci, OTGSC_1MSIE, OTGSC_1MSIE);
} }
/* /*
* Remove timer from active timer list * 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; unsigned long flags, enabled_timer_bits;
struct ci_otg_fsm_timer *timer = ci->fsm_timer->timer_list[t]; enum otg_fsm_timer cur_timer, next_timer = NUM_OTG_FSM_TIMERS;
struct list_head *active_timers = &ci->fsm_timer->active_timers;
if (t >= NUM_CI_OTG_FSM_TIMERS) if ((t >= NUM_OTG_FSM_TIMERS) ||
!(ci->enabled_otg_timer_bits & (1 << t)))
return; return;
list_for_each_entry_safe(tmp_timer, del_tmp, active_timers, list) spin_lock_irqsave(&ci->lock, flags);
if (tmp_timer == timer) ci->enabled_otg_timer_bits &= ~(1 << t);
list_del(&timer->list); if (ci->next_otg_timer == t) {
if (ci->enabled_otg_timer_bits == 0) {
/* Disable 1ms irq if there is no any active timer */ /* No enabled timers after delete it */
if (list_empty(active_timers)) hrtimer_cancel(&ci->otg_fsm_hrtimer);
hw_write_otgsc(ci, OTGSC_1MSIE, 0); ci->next_otg_timer = NUM_OTG_FSM_TIMERS;
} } else {
/* Find the next timer */
/* enabled_timer_bits = ci->enabled_otg_timer_bits;
* Reduce timer count by 1, and find timeout conditions. for_each_set_bit(cur_timer, &enabled_timer_bits,
* Called by otg 1ms timer interrupt NUM_OTG_FSM_TIMERS) {
*/ if ((next_timer == NUM_OTG_FSM_TIMERS) ||
static inline int ci_otg_tick_timer(struct ci_hdrc *ci) (ci->hr_timeouts[next_timer].tv64 <
{ ci->hr_timeouts[cur_timer].tv64))
struct ci_otg_fsm_timer *tmp_timer, *del_tmp; next_timer = cur_timer;
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;
} }
} }
if (next_timer != NUM_OTG_FSM_TIMERS) {
/* disable 1ms irq if there is no any timer active */ ci->next_otg_timer = next_timer;
if ((expired == 1) && list_empty(active_timers)) hrtimer_start_range_ns(&ci->otg_fsm_hrtimer,
hw_write_otgsc(ci, OTGSC_1MSIE, 0); ci->hr_timeouts[next_timer], NSEC_PER_MSEC,
HRTIMER_MODE_ABS);
return expired; }
spin_unlock_irqrestore(&ci->lock, flags);
} }
/* The timeout callback function to set time out bit */ /* OTG FSM timer handlers */
static void set_tmout(void *ptr, unsigned long indicator) static int a_wait_vrise_tmout(struct ci_hdrc *ci)
{ {
*(int *)indicator = 1; ci->fsm.a_wait_vrise_tmout = 1;
return 0;
} }
static void set_tmout_and_fsm(void *ptr, unsigned long indicator) static int a_wait_vfall_tmout(struct ci_hdrc *ci)
{ {
struct ci_hdrc *ci = (struct ci_hdrc *)ptr; ci->fsm.a_wait_vfall_tmout = 1;
return 0;
set_tmout(ci, indicator);
ci_otg_queue_work(ci);
} }
static void a_wait_vfall_tmout_func(void *ptr, unsigned long indicator) static int a_wait_bcon_tmout(struct ci_hdrc *ci)
{ {
struct ci_hdrc *ci = (struct ci_hdrc *)ptr; ci->fsm.a_wait_bcon_tmout = 1;
return 0;
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) static int a_aidl_bdis_tmout(struct ci_hdrc *ci)
{ {
struct ci_hdrc *ci = (struct ci_hdrc *)ptr; ci->fsm.a_aidl_bdis_tmout = 1;
return 0;
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) static int b_ase0_brst_tmout(struct ci_hdrc *ci)
{ {
struct ci_hdrc *ci = (struct ci_hdrc *)ptr; ci->fsm.b_ase0_brst_tmout = 1;
return 0;
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) static int a_bidl_adis_tmout(struct ci_hdrc *ci)
{ {
struct ci_hdrc *ci = (struct ci_hdrc *)ptr; ci->fsm.a_bidl_adis_tmout = 1;
return 0;
}
/* Check if A detached */ static int b_se0_srp_tmout(struct ci_hdrc *ci)
if (!(hw_read_otgsc(ci, OTGSC_BSV))) { {
ci->fsm.b_sess_vld = 0; ci->fsm.b_se0_srp = 1;
ci_otg_add_timer(ci, B_SSEND_SRP); return 0;
ci_otg_queue_work(ci);
}
} }
static void b_data_pulse_end(void *ptr, unsigned long indicator) static int b_srp_fail_tmout(struct ci_hdrc *ci)
{ {
struct ci_hdrc *ci = (struct ci_hdrc *)ptr; 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_srp_done = 1;
ci->fsm.b_bus_req = 0; ci->fsm.b_bus_req = 0;
if (ci->fsm.power_up) if (ci->fsm.power_up)
ci->fsm.power_up = 0; ci->fsm.power_up = 0;
hw_write_otgsc(ci, OTGSC_HABA, 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 */ /* Initialize timers */
static int ci_otg_init_timers(struct ci_hdrc *ci) static int ci_otg_init_timers(struct ci_hdrc *ci)
{ {
struct otg_fsm *fsm = &ci->fsm; hrtimer_init(&ci->otg_fsm_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
ci->otg_fsm_hrtimer.function = ci_otg_hrtimer_func;
/* 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;
return 0; return 0;
} }
...@@ -530,6 +512,7 @@ static void ci_otg_start_pulse(struct otg_fsm *fsm) ...@@ -530,6 +512,7 @@ static void ci_otg_start_pulse(struct otg_fsm *fsm)
/* Hardware Assistant Data pulse */ /* Hardware Assistant Data pulse */
hw_write_otgsc(ci, OTGSC_HADP, OTGSC_HADP); hw_write_otgsc(ci, OTGSC_HADP, OTGSC_HADP);
pm_runtime_get(ci->dev);
ci_otg_add_timer(ci, B_DATA_PLS); ci_otg_add_timer(ci, B_DATA_PLS);
} }
...@@ -585,6 +568,7 @@ int ci_otg_fsm_work(struct ci_hdrc *ci) ...@@ -585,6 +568,7 @@ int ci_otg_fsm_work(struct ci_hdrc *ci)
ci->fsm.otg->state < OTG_STATE_A_IDLE) ci->fsm.otg->state < OTG_STATE_A_IDLE)
return 0; return 0;
pm_runtime_get_sync(ci->dev);
if (otg_statemachine(&ci->fsm)) { if (otg_statemachine(&ci->fsm)) {
if (ci->fsm.otg->state == OTG_STATE_A_IDLE) { if (ci->fsm.otg->state == OTG_STATE_A_IDLE) {
/* /*
...@@ -596,8 +580,15 @@ int ci_otg_fsm_work(struct ci_hdrc *ci) ...@@ -596,8 +580,15 @@ int ci_otg_fsm_work(struct ci_hdrc *ci)
* a_idle to a_wait_vrise when power up * a_idle to a_wait_vrise when power up
*/ */
if ((ci->fsm.id) || (ci->id_event) || if ((ci->fsm.id) || (ci->id_event) ||
(ci->fsm.power_up)) (ci->fsm.power_up)) {
ci_otg_queue_work(ci); 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) if (ci->id_event)
ci->id_event = false; ci->id_event = false;
} else if (ci->fsm.otg->state == OTG_STATE_B_IDLE) { } else if (ci->fsm.otg->state == OTG_STATE_B_IDLE) {
...@@ -609,8 +600,13 @@ int ci_otg_fsm_work(struct ci_hdrc *ci) ...@@ -609,8 +600,13 @@ int ci_otg_fsm_work(struct ci_hdrc *ci)
*/ */
ci_otg_queue_work(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; return 0;
} }
...@@ -655,7 +651,6 @@ static void ci_otg_fsm_event(struct ci_hdrc *ci) ...@@ -655,7 +651,6 @@ static void ci_otg_fsm_event(struct ci_hdrc *ci)
fsm->a_conn = 0; fsm->a_conn = 0;
fsm->b_bus_req = 0; fsm->b_bus_req = 0;
ci_otg_queue_work(ci); ci_otg_queue_work(ci);
ci_otg_add_timer(ci, B_SESS_VLD);
} }
break; break;
case OTG_STATE_A_PERIPHERAL: case OTG_STATE_A_PERIPHERAL:
...@@ -725,11 +720,7 @@ irqreturn_t ci_otg_fsm_irq(struct ci_hdrc *ci) ...@@ -725,11 +720,7 @@ irqreturn_t ci_otg_fsm_irq(struct ci_hdrc *ci)
fsm->id = (otgsc & OTGSC_ID) ? 1 : 0; fsm->id = (otgsc & OTGSC_ID) ? 1 : 0;
if (otg_int_src) { if (otg_int_src) {
if (otg_int_src & OTGSC_1MSIS) { if (otg_int_src & OTGSC_DPIS) {
hw_write_otgsc(ci, OTGSC_1MSIS, OTGSC_1MSIS);
retval = ci_otg_tick_timer(ci);
return IRQ_HANDLED;
} else if (otg_int_src & OTGSC_DPIS) {
hw_write_otgsc(ci, OTGSC_DPIS, OTGSC_DPIS); hw_write_otgsc(ci, OTGSC_DPIS, OTGSC_DPIS);
fsm->a_srp_det = 1; fsm->a_srp_det = 1;
fsm->a_bus_drop = 0; fsm->a_bus_drop = 0;
...@@ -793,17 +784,13 @@ int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci) ...@@ -793,17 +784,13 @@ int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci)
mutex_init(&ci->fsm.lock); 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); retval = ci_otg_init_timers(ci);
if (retval) { if (retval) {
dev_err(ci->dev, "Couldn't init OTG timers\n"); dev_err(ci->dev, "Couldn't init OTG timers\n");
return retval; 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); retval = sysfs_create_group(&ci->dev->kobj, &inputs_attr_group);
if (retval < 0) { if (retval < 0) {
......
...@@ -62,33 +62,6 @@ ...@@ -62,33 +62,6 @@
/* SSEND time before SRP */ /* SSEND time before SRP */
#define TB_SSEND_SRP (1500) /* minimum 1.5 sec, section:5.1.2 */ #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 #ifdef CONFIG_USB_OTG_FSM
int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci); 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) ...@@ -86,10 +86,8 @@ static int hw_device_state(struct ci_hdrc *ci, u32 dma)
/* interrupt, error, port change, reset, sleep/suspend */ /* interrupt, error, port change, reset, sleep/suspend */
hw_write(ci, OP_USBINTR, ~0, hw_write(ci, OP_USBINTR, ~0,
USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI); USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI);
hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);
} else { } else {
hw_write(ci, OP_USBINTR, ~0, 0); hw_write(ci, OP_USBINTR, ~0, 0);
hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
} }
return 0; return 0;
} }
...@@ -522,6 +520,20 @@ static void free_pending_td(struct ci_hw_ep *hwep) ...@@ -522,6 +520,20 @@ static void free_pending_td(struct ci_hw_ep *hwep)
kfree(pending); 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 * _hardware_dequeue: handles a request at hardware level
* @gadget: gadget * @gadget: gadget
...@@ -535,6 +547,7 @@ static int _hardware_dequeue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) ...@@ -535,6 +547,7 @@ static int _hardware_dequeue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq)
struct td_node *node, *tmpnode; struct td_node *node, *tmpnode;
unsigned remaining_length; unsigned remaining_length;
unsigned actual = hwreq->req.length; unsigned actual = hwreq->req.length;
struct ci_hdrc *ci = hwep->ci;
if (hwreq->req.status != -EALREADY) if (hwreq->req.status != -EALREADY)
return -EINVAL; return -EINVAL;
...@@ -544,6 +557,11 @@ static int _hardware_dequeue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) ...@@ -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) { list_for_each_entry_safe(node, tmpnode, &hwreq->tds, td) {
tmptoken = le32_to_cpu(node->ptr->token); tmptoken = le32_to_cpu(node->ptr->token);
if ((TD_STATUS_ACTIVE & tmptoken) != 0) { 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; hwreq->req.status = -EALREADY;
return -EBUSY; return -EBUSY;
} }
...@@ -1162,10 +1180,13 @@ static int ep_enable(struct usb_ep *ep, ...@@ -1162,10 +1180,13 @@ static int ep_enable(struct usb_ep *ep,
/* only internal SW should enable ctrl endpts */ /* 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"); 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->dir = usb_endpoint_dir_in(desc) ? TX : RX;
hwep->num = usb_endpoint_num(desc); hwep->num = usb_endpoint_num(desc);
...@@ -1485,7 +1506,9 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active) ...@@ -1485,7 +1506,9 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
hw_device_reset(ci); hw_device_reset(ci);
hw_device_state(ci, ci->ep0out->qh.dma); hw_device_state(ci, ci->ep0out->qh.dma);
usb_gadget_set_state(_gadget, USB_STATE_POWERED); usb_gadget_set_state(_gadget, USB_STATE_POWERED);
usb_udc_vbus_handler(_gadget, true);
} else { } else {
usb_udc_vbus_handler(_gadget, false);
if (ci->driver) if (ci->driver)
ci->driver->disconnect(&ci->gadget); ci->driver->disconnect(&ci->gadget);
hw_device_state(ci, 0); hw_device_state(ci, 0);
...@@ -1551,13 +1574,16 @@ static int ci_udc_pullup(struct usb_gadget *_gadget, int is_on) ...@@ -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); struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget);
if (!ci->vbus_active) /* Data+ pullup controlled by OTG state machine in OTG fsm mode */
return -EOPNOTSUPP; if (ci_otg_is_fsm_mode(ci))
return 0;
pm_runtime_get_sync(&ci->gadget.dev);
if (is_on) if (is_on)
hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS); hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);
else else
hw_write(ci, OP_USBCMD, USBCMD_RS, 0); hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
pm_runtime_put_sync(&ci->gadget.dev);
return 0; return 0;
} }
...@@ -1687,6 +1713,7 @@ static int ci_udc_start(struct usb_gadget *gadget, ...@@ -1687,6 +1713,7 @@ static int ci_udc_start(struct usb_gadget *gadget,
spin_lock_irqsave(&ci->lock, flags); spin_lock_irqsave(&ci->lock, flags);
hw_device_reset(ci); hw_device_reset(ci);
} else { } else {
usb_udc_vbus_handler(&ci->gadget, false);
pm_runtime_put_sync(&ci->gadget.dev); pm_runtime_put_sync(&ci->gadget.dev);
return retval; return retval;
} }
......
...@@ -11,7 +11,6 @@ ...@@ -11,7 +11,6 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/clk.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/delay.h> #include <linux/delay.h>
...@@ -56,6 +55,19 @@ ...@@ -56,6 +55,19 @@
#define MX53_USB_PLL_DIV_24_MHZ 0x01 #define MX53_USB_PLL_DIV_24_MHZ 0x01
#define MX6_BM_OVER_CUR_DIS BIT(7) #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) #define VF610_OVER_CUR_DIS BIT(7)
...@@ -64,12 +76,13 @@ struct usbmisc_ops { ...@@ -64,12 +76,13 @@ struct usbmisc_ops {
int (*init)(struct imx_usbmisc_data *data); int (*init)(struct imx_usbmisc_data *data);
/* It's called once after adding a usb device */ /* It's called once after adding a usb device */
int (*post)(struct imx_usbmisc_data *data); 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 { struct imx_usbmisc {
void __iomem *base; void __iomem *base;
spinlock_t lock; spinlock_t lock;
struct clk *clk;
const struct usbmisc_ops *ops; const struct usbmisc_ops *ops;
}; };
...@@ -204,6 +217,35 @@ static int usbmisc_imx53_init(struct imx_usbmisc_data *data) ...@@ -204,6 +217,35 @@ static int usbmisc_imx53_init(struct imx_usbmisc_data *data)
return 0; 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) static int usbmisc_imx6q_init(struct imx_usbmisc_data *data)
{ {
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
...@@ -221,6 +263,36 @@ static int usbmisc_imx6q_init(struct imx_usbmisc_data *data) ...@@ -221,6 +263,36 @@ static int usbmisc_imx6q_init(struct imx_usbmisc_data *data)
spin_unlock_irqrestore(&usbmisc->lock, flags); 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; return 0;
} }
...@@ -258,6 +330,7 @@ static const struct usbmisc_ops imx53_usbmisc_ops = { ...@@ -258,6 +330,7 @@ static const struct usbmisc_ops imx53_usbmisc_ops = {
}; };
static const struct usbmisc_ops imx6q_usbmisc_ops = { static const struct usbmisc_ops imx6q_usbmisc_ops = {
.set_wakeup = usbmisc_imx6q_set_wakeup,
.init = usbmisc_imx6q_init, .init = usbmisc_imx6q_init,
}; };
...@@ -265,10 +338,19 @@ static const struct usbmisc_ops vf610_usbmisc_ops = { ...@@ -265,10 +338,19 @@ static const struct usbmisc_ops vf610_usbmisc_ops = {
.init = usbmisc_vf610_init, .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) 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) if (!usbmisc->ops->init)
return 0; return 0;
return usbmisc->ops->init(data); return usbmisc->ops->init(data);
...@@ -277,14 +359,32 @@ EXPORT_SYMBOL_GPL(imx_usbmisc_init); ...@@ -277,14 +359,32 @@ EXPORT_SYMBOL_GPL(imx_usbmisc_init);
int imx_usbmisc_init_post(struct imx_usbmisc_data *data) 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) if (!usbmisc->ops->post)
return 0; return 0;
return usbmisc->ops->post(data); return usbmisc->ops->post(data);
} }
EXPORT_SYMBOL_GPL(imx_usbmisc_init_post); 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[] = { static const struct of_device_id usbmisc_imx_dt_ids[] = {
{ {
.compatible = "fsl,imx25-usbmisc", .compatible = "fsl,imx25-usbmisc",
...@@ -314,6 +414,10 @@ static const struct of_device_id usbmisc_imx_dt_ids[] = { ...@@ -314,6 +414,10 @@ static const struct of_device_id usbmisc_imx_dt_ids[] = {
.compatible = "fsl,vf610-usbmisc", .compatible = "fsl,vf610-usbmisc",
.data = &vf610_usbmisc_ops, .data = &vf610_usbmisc_ops,
}, },
{
.compatible = "fsl,imx6sx-usbmisc",
.data = &imx6sx_usbmisc_ops,
},
{ /* sentinel */ } { /* sentinel */ }
}; };
MODULE_DEVICE_TABLE(of, usbmisc_imx_dt_ids); MODULE_DEVICE_TABLE(of, usbmisc_imx_dt_ids);
...@@ -322,7 +426,6 @@ static int usbmisc_imx_probe(struct platform_device *pdev) ...@@ -322,7 +426,6 @@ static int usbmisc_imx_probe(struct platform_device *pdev)
{ {
struct resource *res; struct resource *res;
struct imx_usbmisc *data; struct imx_usbmisc *data;
int ret;
struct of_device_id *tmp_dev; struct of_device_id *tmp_dev;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
...@@ -336,20 +439,6 @@ static int usbmisc_imx_probe(struct platform_device *pdev) ...@@ -336,20 +439,6 @@ static int usbmisc_imx_probe(struct platform_device *pdev)
if (IS_ERR(data->base)) if (IS_ERR(data->base))
return PTR_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 *) tmp_dev = (struct of_device_id *)
of_match_device(usbmisc_imx_dt_ids, &pdev->dev); of_match_device(usbmisc_imx_dt_ids, &pdev->dev);
data->ops = (const struct usbmisc_ops *)tmp_dev->data; data->ops = (const struct usbmisc_ops *)tmp_dev->data;
...@@ -360,8 +449,6 @@ static int usbmisc_imx_probe(struct platform_device *pdev) ...@@ -360,8 +449,6 @@ static int usbmisc_imx_probe(struct platform_device *pdev)
static int usbmisc_imx_remove(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; return 0;
} }
......
...@@ -360,7 +360,7 @@ static void acm_ctrl_irq(struct urb *urb) ...@@ -360,7 +360,7 @@ static void acm_ctrl_irq(struct urb *urb)
} }
exit: exit:
retval = usb_submit_urb(urb, GFP_ATOMIC); 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", dev_err(&acm->control->dev, "%s - usb_submit_urb failed: %d\n",
__func__, retval); __func__, retval);
} }
...@@ -417,25 +417,33 @@ static void acm_read_bulk_callback(struct urb *urb) ...@@ -417,25 +417,33 @@ static void acm_read_bulk_callback(struct urb *urb)
struct acm_rb *rb = urb->context; struct acm_rb *rb = urb->context;
struct acm *acm = rb->instance; struct acm *acm = rb->instance;
unsigned long flags; unsigned long flags;
int status = urb->status;
dev_vdbg(&acm->data->dev, "%s - urb %d, len %d\n", __func__, dev_vdbg(&acm->data->dev, "%s - urb %d, len %d\n", __func__,
rb->index, urb->actual_length); rb->index, urb->actual_length);
set_bit(rb->index, &acm->read_urbs_free);
if (!acm->dev) { if (!acm->dev) {
set_bit(rb->index, &acm->read_urbs_free);
dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__); dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__);
return; 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", dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n",
__func__, urb->status); __func__, status);
return; return;
} }
usb_mark_last_busy(acm->dev); usb_mark_last_busy(acm->dev);
acm_process_read_urb(acm, urb); 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 */ /* throttle device if requested by tty */
spin_lock_irqsave(&acm->read_lock, flags); spin_lock_irqsave(&acm->read_lock, flags);
...@@ -454,13 +462,14 @@ static void acm_write_bulk(struct urb *urb) ...@@ -454,13 +462,14 @@ static void acm_write_bulk(struct urb *urb)
struct acm_wb *wb = urb->context; struct acm_wb *wb = urb->context;
struct acm *acm = wb->instance; struct acm *acm = wb->instance;
unsigned long flags; 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", dev_vdbg(&acm->data->dev, "%s - len %d/%d, status %d\n",
__func__, __func__,
urb->actual_length, urb->actual_length,
urb->transfer_buffer_length, urb->transfer_buffer_length,
urb->status); status);
spin_lock_irqsave(&acm->write_lock, flags); spin_lock_irqsave(&acm->write_lock, flags);
acm_write_done(acm, wb); acm_write_done(acm, wb);
......
...@@ -245,7 +245,7 @@ static void wdm_int_callback(struct urb *urb) ...@@ -245,7 +245,7 @@ static void wdm_int_callback(struct urb *urb)
case USB_CDC_NOTIFY_RESPONSE_AVAILABLE: case USB_CDC_NOTIFY_RESPONSE_AVAILABLE:
dev_dbg(&desc->intf->dev, dev_dbg(&desc->intf->dev,
"NOTIFY_RESPONSE_AVAILABLE received: index %d len %d", "NOTIFY_RESPONSE_AVAILABLE received: index %d len %d",
dr->wIndex, dr->wLength); le16_to_cpu(dr->wIndex), le16_to_cpu(dr->wLength));
break; break;
case USB_CDC_NOTIFY_NETWORK_CONNECTION: case USB_CDC_NOTIFY_NETWORK_CONNECTION:
...@@ -262,7 +262,9 @@ static void wdm_int_callback(struct urb *urb) ...@@ -262,7 +262,9 @@ static void wdm_int_callback(struct urb *urb)
clear_bit(WDM_POLL_RUNNING, &desc->flags); clear_bit(WDM_POLL_RUNNING, &desc->flags);
dev_err(&desc->intf->dev, dev_err(&desc->intf->dev,
"unknown notification %d received: index %d len %d\n", "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; goto exit;
} }
...@@ -339,7 +341,7 @@ static ssize_t wdm_write ...@@ -339,7 +341,7 @@ static ssize_t wdm_write
desc->werr = 0; desc->werr = 0;
spin_unlock_irq(&desc->iuspin); spin_unlock_irq(&desc->iuspin);
if (we < 0) if (we < 0)
return -EIO; return usb_translate_errors(we);
buf = kmalloc(count, GFP_KERNEL); buf = kmalloc(count, GFP_KERNEL);
if (!buf) { if (!buf) {
...@@ -349,30 +351,25 @@ static ssize_t wdm_write ...@@ -349,30 +351,25 @@ static ssize_t wdm_write
r = copy_from_user(buf, buffer, count); r = copy_from_user(buf, buffer, count);
if (r > 0) { if (r > 0) {
kfree(buf);
rv = -EFAULT; rv = -EFAULT;
goto outnl; goto out_free_mem;
} }
/* concurrent writes and disconnect */ /* concurrent writes and disconnect */
r = mutex_lock_interruptible(&desc->wlock); r = mutex_lock_interruptible(&desc->wlock);
rv = -ERESTARTSYS; rv = -ERESTARTSYS;
if (r) { if (r)
kfree(buf); goto out_free_mem;
goto outnl;
}
if (test_bit(WDM_DISCONNECTING, &desc->flags)) { if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
kfree(buf);
rv = -ENODEV; rv = -ENODEV;
goto outnp; goto out_free_mem_lock;
} }
r = usb_autopm_get_interface(desc->intf); r = usb_autopm_get_interface(desc->intf);
if (r < 0) { if (r < 0) {
kfree(buf);
rv = usb_translate_errors(r); rv = usb_translate_errors(r);
goto outnp; goto out_free_mem_lock;
} }
if (!(file->f_flags & O_NONBLOCK)) if (!(file->f_flags & O_NONBLOCK))
...@@ -386,9 +383,8 @@ static ssize_t wdm_write ...@@ -386,9 +383,8 @@ static ssize_t wdm_write
r = -EIO; r = -EIO;
if (r < 0) { if (r < 0) {
kfree(buf);
rv = r; rv = r;
goto out; goto out_free_mem_pm;
} }
req = desc->orq; req = desc->orq;
...@@ -408,28 +404,35 @@ static ssize_t wdm_write ...@@ -408,28 +404,35 @@ static ssize_t wdm_write
USB_RECIP_INTERFACE); USB_RECIP_INTERFACE);
req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND; req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND;
req->wValue = 0; req->wValue = 0;
req->wIndex = desc->inum; req->wIndex = desc->inum; /* already converted */
req->wLength = cpu_to_le16(count); req->wLength = cpu_to_le16(count);
set_bit(WDM_IN_USE, &desc->flags); set_bit(WDM_IN_USE, &desc->flags);
desc->outbuf = buf; desc->outbuf = buf;
rv = usb_submit_urb(desc->command, GFP_KERNEL); rv = usb_submit_urb(desc->command, GFP_KERNEL);
if (rv < 0) { if (rv < 0) {
kfree(buf);
desc->outbuf = NULL; desc->outbuf = NULL;
clear_bit(WDM_IN_USE, &desc->flags); clear_bit(WDM_IN_USE, &desc->flags);
dev_err(&desc->intf->dev, "Tx URB error: %d\n", rv); dev_err(&desc->intf->dev, "Tx URB error: %d\n", rv);
rv = usb_translate_errors(rv); rv = usb_translate_errors(rv);
goto out_free_mem_pm;
} else { } else {
dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d", 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); usb_autopm_put_interface(desc->intf);
outnp:
mutex_unlock(&desc->wlock); mutex_unlock(&desc->wlock);
outnl: outnl:
return rv < 0 ? rv : count; 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 @@ static ssize_t wdm_read ...@@ -519,9 +522,9 @@ static ssize_t wdm_read
spin_lock_irq(&desc->iuspin); spin_lock_irq(&desc->iuspin);
if (desc->rerr) { /* read completed, error happened */ if (desc->rerr) { /* read completed, error happened */
rv = usb_translate_errors(desc->rerr);
desc->rerr = 0; desc->rerr = 0;
spin_unlock_irq(&desc->iuspin); spin_unlock_irq(&desc->iuspin);
rv = -EIO;
goto err; goto err;
} }
/* /*
...@@ -820,7 +823,7 @@ static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor ...@@ -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->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE);
desc->irq->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE; desc->irq->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE;
desc->irq->wValue = 0; desc->irq->wValue = 0;
desc->irq->wIndex = desc->inum; desc->irq->wIndex = desc->inum; /* already converted */
desc->irq->wLength = cpu_to_le16(desc->wMaxCommand); desc->irq->wLength = cpu_to_le16(desc->wMaxCommand);
usb_fill_control_urb( usb_fill_control_urb(
......
...@@ -2408,7 +2408,7 @@ static int usbdev_notify(struct notifier_block *self, ...@@ -2408,7 +2408,7 @@ static int usbdev_notify(struct notifier_block *self,
} }
static struct notifier_block usbdev_nb = { static struct notifier_block usbdev_nb = {
.notifier_call = usbdev_notify, .notifier_call = usbdev_notify,
}; };
static struct cdev usb_device_cdev; static struct cdev usb_device_cdev;
......
...@@ -3406,10 +3406,10 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) ...@@ -3406,10 +3406,10 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
if (status) { if (status) {
dev_dbg(&port_dev->dev, "can't resume, status %d\n", status); dev_dbg(&port_dev->dev, "can't resume, status %d\n", status);
} else { } else {
/* drive resume for at least 20 msec */ /* drive resume for USB_RESUME_TIMEOUT msec */
dev_dbg(&udev->dev, "usb %sresume\n", dev_dbg(&udev->dev, "usb %sresume\n",
(PMSG_IS_AUTO(msg) ? "auto-" : "")); (PMSG_IS_AUTO(msg) ? "auto-" : ""));
msleep(25); msleep(USB_RESUME_TIMEOUT);
/* Virtual root hubs can trigger on GET_PORT_STATUS to /* Virtual root hubs can trigger on GET_PORT_STATUS to
* stop resume signaling. Then finish the resume * stop resume signaling. Then finish the resume
......
...@@ -49,6 +49,22 @@ const char *usbcore_name = "usbcore"; ...@@ -49,6 +49,22 @@ const char *usbcore_name = "usbcore";
static bool nousb; /* Disable USB when built into kernel image */ 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 #ifdef CONFIG_PM
static int usb_autosuspend_delay = 2; /* Default delay value, static int usb_autosuspend_delay = 2; /* Default delay value,
* in seconds */ * in seconds */
...@@ -964,22 +980,6 @@ void usb_buffer_unmap_sg(const struct usb_device *dev, int is_in, ...@@ -964,22 +980,6 @@ void usb_buffer_unmap_sg(const struct usb_device *dev, int is_in,
EXPORT_SYMBOL_GPL(usb_buffer_unmap_sg); EXPORT_SYMBOL_GPL(usb_buffer_unmap_sg);
#endif #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 * Notifications of device and interface registration
*/ */
...@@ -1045,7 +1045,7 @@ static void usb_debugfs_cleanup(void) ...@@ -1045,7 +1045,7 @@ static void usb_debugfs_cleanup(void)
static int __init usb_init(void) static int __init usb_init(void)
{ {
int retval; int retval;
if (nousb) { if (usb_disabled()) {
pr_info("%s: USB support disabled\n", usbcore_name); pr_info("%s: USB support disabled\n", usbcore_name);
return 0; return 0;
} }
...@@ -1102,7 +1102,7 @@ static int __init usb_init(void) ...@@ -1102,7 +1102,7 @@ static int __init usb_init(void)
static void __exit usb_exit(void) static void __exit usb_exit(void)
{ {
/* This will matter if shutdown/reboot does exitcalls. */ /* This will matter if shutdown/reboot does exitcalls. */
if (nousb) if (usb_disabled())
return; return;
usb_deregister_device_driver(&usb_generic_driver); usb_deregister_device_driver(&usb_generic_driver);
......
...@@ -59,11 +59,13 @@ config USB_DWC2_PLATFORM ...@@ -59,11 +59,13 @@ config USB_DWC2_PLATFORM
config USB_DWC2_PCI config USB_DWC2_PCI
tristate "DWC2 PCI" tristate "DWC2 PCI"
depends on USB_DWC2_HOST && PCI depends on PCI
default USB_DWC2_HOST default n
select USB_DWC2_PLATFORM
select NOP_USB_XCEIV
help help
The Designware USB2.0 PCI interface module for controllers 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 config USB_DWC2_DEBUG
bool "Enable Debugging Messages" bool "Enable Debugging Messages"
......
...@@ -19,10 +19,8 @@ endif ...@@ -19,10 +19,8 @@ endif
# mode. The PCI bus interface module will called dwc2_pci.ko and the platform # mode. The PCI bus interface module will called dwc2_pci.ko and the platform
# interface module will be called dwc2_platform.ko. # interface module will be called dwc2_platform.ko.
ifneq ($(CONFIG_USB_DWC2_PCI),) obj-$(CONFIG_USB_DWC2_PCI) += dwc2_pci.o
obj-$(CONFIG_USB_DWC2) += dwc2_pci.o dwc2_pci-y := pci.o
dwc2_pci-y := pci.o
endif
obj-$(CONFIG_USB_DWC2_PLATFORM) += dwc2_platform.o obj-$(CONFIG_USB_DWC2_PLATFORM) += dwc2_platform.o
dwc2_platform-y := platform.o dwc2_platform-y := platform.o
...@@ -593,6 +593,8 @@ struct dwc2_hsotg { ...@@ -593,6 +593,8 @@ struct dwc2_hsotg {
struct dwc2_core_params *core_params; struct dwc2_core_params *core_params;
enum usb_otg_state op_state; enum usb_otg_state op_state;
enum usb_dr_mode dr_mode; enum usb_dr_mode dr_mode;
unsigned int hcd_enabled:1;
unsigned int gadget_enabled:1;
struct phy *phy; struct phy *phy;
struct usb_phy *uphy; struct usb_phy *uphy;
......
...@@ -257,6 +257,14 @@ static void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg) ...@@ -257,6 +257,14 @@ static void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg)
*/ */
channel->qh = NULL; 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, ...@@ -1527,7 +1535,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
hprt0 |= HPRT0_RES; hprt0 |= HPRT0_RES;
writel(hprt0, hsotg->regs + HPRT0); writel(hprt0, hsotg->regs + HPRT0);
hprt0 &= ~HPRT0_SUSP; hprt0 &= ~HPRT0_SUSP;
usleep_range(100000, 150000); msleep(USB_RESUME_TIMEOUT);
hprt0 &= ~HPRT0_RES; hprt0 &= ~HPRT0_RES;
writel(hprt0, hsotg->regs + HPRT0); writel(hprt0, hsotg->regs + HPRT0);
...@@ -1608,7 +1616,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, ...@@ -1608,7 +1616,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
dev_dbg(hsotg->dev, "GetHubDescriptor\n"); dev_dbg(hsotg->dev, "GetHubDescriptor\n");
hub_desc = (struct usb_hub_descriptor *)buf; hub_desc = (struct usb_hub_descriptor *)buf;
hub_desc->bDescLength = 9; hub_desc->bDescLength = 9;
hub_desc->bDescriptorType = 0x29; hub_desc->bDescriptorType = USB_DT_HUB;
hub_desc->bNbrPorts = 1; hub_desc->bNbrPorts = 1;
hub_desc->wHubCharacteristics = hub_desc->wHubCharacteristics =
cpu_to_le16(HUB_CHAR_COMMON_LPSM | cpu_to_le16(HUB_CHAR_COMMON_LPSM |
......
...@@ -50,113 +50,97 @@ ...@@ -50,113 +50,97 @@
#include <linux/usb/hcd.h> #include <linux/usb/hcd.h>
#include <linux/usb/ch11.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 #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 = { struct dwc2_pci_glue {
.otg_cap = -1, struct platform_device *dwc2;
.otg_ver = -1, struct platform_device *phy;
.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,
}; };
/** static void dwc2_pci_remove(struct pci_dev *pci)
* 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)
{ {
struct dwc2_hsotg *hsotg = pci_get_drvdata(dev); struct dwc2_pci_glue *glue = pci_get_drvdata(pci);
dwc2_hcd_remove(hsotg); platform_device_unregister(glue->dwc2);
pci_disable_device(dev); usb_phy_generic_unregister(glue->phy);
kfree(glue);
pci_set_drvdata(pci, NULL);
} }
/** static int dwc2_pci_probe(struct pci_dev *pci,
* dwc2_driver_probe() - Called when the DWC_otg core is bound to the DWC_otg const struct pci_device_id *id)
* 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)
{ {
struct dwc2_hsotg *hsotg; struct resource res[2];
int retval; struct platform_device *dwc2;
struct platform_device *phy;
int ret;
struct device *dev = &pci->dev;
struct dwc2_pci_glue *glue;
ret = pcim_enable_device(pci);
if (ret) {
dev_err(dev, "failed to enable pci device\n");
return -ENODEV;
}
pci_set_master(pci);
hsotg = devm_kzalloc(&dev->dev, sizeof(*hsotg), GFP_KERNEL); dwc2 = platform_device_alloc("dwc2", PLATFORM_DEVID_AUTO);
if (!hsotg) if (!dwc2) {
dev_err(dev, "couldn't allocate dwc2 device\n");
return -ENOMEM; return -ENOMEM;
}
hsotg->dev = &dev->dev; memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res));
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", res[0].start = pci_resource_start(pci, 0);
(unsigned long)pci_resource_start(dev, 0), hsotg->regs); res[0].end = pci_resource_end(pci, 0);
res[0].name = "dwc2";
res[0].flags = IORESOURCE_MEM;
if (pci_enable_device(dev) < 0) res[1].start = pci->irq;
return -ENODEV; res[1].name = "dwc2";
res[1].flags = IORESOURCE_IRQ;
pci_set_master(dev); 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;
}
retval = devm_request_irq(hsotg->dev, dev->irq, dwc2->dev.parent = dev;
dwc2_handle_common_intr, IRQF_SHARED,
dev_name(hsotg->dev), hsotg);
if (retval)
return retval;
spin_lock_init(&hsotg->lock); phy = usb_phy_generic_register();
retval = dwc2_hcd_init(hsotg, dev->irq, &dwc2_module_params); if (IS_ERR(phy)) {
if (retval) { dev_err(dev, "error registering generic PHY (%ld)\n",
pci_disable_device(dev); PTR_ERR(phy));
return retval; return PTR_ERR(phy);
} }
pci_set_drvdata(dev, hsotg); 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 retval; return 0;
err:
usb_phy_generic_unregister(phy);
platform_device_put(dwc2);
return ret;
} }
static const struct pci_device_id dwc2_pci_ids[] = { static const struct pci_device_id dwc2_pci_ids[] = {
...@@ -174,8 +158,8 @@ MODULE_DEVICE_TABLE(pci, dwc2_pci_ids); ...@@ -174,8 +158,8 @@ MODULE_DEVICE_TABLE(pci, dwc2_pci_ids);
static struct pci_driver dwc2_pci_driver = { static struct pci_driver dwc2_pci_driver = {
.name = dwc2_driver_name, .name = dwc2_driver_name,
.id_table = dwc2_pci_ids, .id_table = dwc2_pci_ids,
.probe = dwc2_driver_probe, .probe = dwc2_pci_probe,
.remove = dwc2_driver_remove, .remove = dwc2_pci_remove,
}; };
module_pci_driver(dwc2_pci_driver); module_pci_driver(dwc2_pci_driver);
......
...@@ -121,8 +121,10 @@ static int dwc2_driver_remove(struct platform_device *dev) ...@@ -121,8 +121,10 @@ static int dwc2_driver_remove(struct platform_device *dev)
{ {
struct dwc2_hsotg *hsotg = platform_get_drvdata(dev); struct dwc2_hsotg *hsotg = platform_get_drvdata(dev);
dwc2_hcd_remove(hsotg); if (hsotg->hcd_enabled)
s3c_hsotg_remove(hsotg); dwc2_hcd_remove(hsotg);
if (hsotg->gadget_enabled)
s3c_hsotg_remove(hsotg);
return 0; return 0;
} }
...@@ -234,12 +236,23 @@ static int dwc2_driver_probe(struct platform_device *dev) ...@@ -234,12 +236,23 @@ static int dwc2_driver_probe(struct platform_device *dev)
spin_lock_init(&hsotg->lock); spin_lock_init(&hsotg->lock);
mutex_init(&hsotg->init_mutex); mutex_init(&hsotg->init_mutex);
retval = dwc2_gadget_init(hsotg, irq);
if (retval) if (hsotg->dr_mode != USB_DR_MODE_HOST) {
return retval; retval = dwc2_gadget_init(hsotg, irq);
retval = dwc2_hcd_init(hsotg, irq, params); if (retval)
if (retval) return 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); platform_set_drvdata(dev, hsotg);
......
...@@ -104,11 +104,4 @@ config USB_DWC3_DEBUG ...@@ -104,11 +104,4 @@ config USB_DWC3_DEBUG
help help
Say Y here to enable debugging messages on DWC3 Driver. 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 endif
...@@ -774,17 +774,13 @@ static int dwc3_probe(struct platform_device *pdev) ...@@ -774,17 +774,13 @@ static int dwc3_probe(struct platform_device *pdev)
* since it will be requested by the xhci-plat driver. * since it will be requested by the xhci-plat driver.
*/ */
regs = devm_ioremap_resource(dev, res); regs = devm_ioremap_resource(dev, res);
if (IS_ERR(regs)) if (IS_ERR(regs)) {
return PTR_ERR(regs); ret = PTR_ERR(regs);
goto err0;
}
dwc->regs = regs; dwc->regs = regs;
dwc->regs_size = resource_size(res); 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 */ /* default to highest possible threshold */
lpm_nyet_threshold = 0xff; lpm_nyet_threshold = 0xff;
...@@ -808,6 +804,8 @@ static int dwc3_probe(struct platform_device *pdev) ...@@ -808,6 +804,8 @@ static int dwc3_probe(struct platform_device *pdev)
"snps,is-utmi-l1-suspend"); "snps,is-utmi-l1-suspend");
of_property_read_u8(node, "snps,hird-threshold", of_property_read_u8(node, "snps,hird-threshold",
&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, dwc->needs_fifo_resize = of_property_read_bool(node,
"tx-fifo-resize"); "tx-fifo-resize");
...@@ -848,6 +846,7 @@ static int dwc3_probe(struct platform_device *pdev) ...@@ -848,6 +846,7 @@ static int dwc3_probe(struct platform_device *pdev)
hird_threshold = pdata->hird_threshold; hird_threshold = pdata->hird_threshold;
dwc->needs_fifo_resize = pdata->tx_fifo_resize; dwc->needs_fifo_resize = pdata->tx_fifo_resize;
dwc->usb3_lpm_capable = pdata->usb3_lpm_capable;
dwc->dr_mode = pdata->dr_mode; dwc->dr_mode = pdata->dr_mode;
dwc->disable_scramble_quirk = pdata->disable_scramble_quirk; dwc->disable_scramble_quirk = pdata->disable_scramble_quirk;
...@@ -878,7 +877,7 @@ static int dwc3_probe(struct platform_device *pdev) ...@@ -878,7 +877,7 @@ static int dwc3_probe(struct platform_device *pdev)
ret = dwc3_core_get_phy(dwc); ret = dwc3_core_get_phy(dwc);
if (ret) if (ret)
return ret; goto err0;
spin_lock_init(&dwc->lock); spin_lock_init(&dwc->lock);
platform_set_drvdata(pdev, dwc); platform_set_drvdata(pdev, dwc);
...@@ -899,7 +898,7 @@ static int dwc3_probe(struct platform_device *pdev) ...@@ -899,7 +898,7 @@ static int dwc3_probe(struct platform_device *pdev)
if (ret) { if (ret) {
dev_err(dwc->dev, "failed to allocate event buffers\n"); dev_err(dwc->dev, "failed to allocate event buffers\n");
ret = -ENOMEM; ret = -ENOMEM;
goto err0; goto err1;
} }
if (IS_ENABLED(CONFIG_USB_DWC3_HOST)) if (IS_ENABLED(CONFIG_USB_DWC3_HOST))
...@@ -913,65 +912,81 @@ static int dwc3_probe(struct platform_device *pdev) ...@@ -913,65 +912,81 @@ static int dwc3_probe(struct platform_device *pdev)
ret = dwc3_core_init(dwc); ret = dwc3_core_init(dwc);
if (ret) { if (ret) {
dev_err(dev, "failed to initialize core\n"); 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->usb2_phy, 0);
usb_phy_set_suspend(dwc->usb3_phy, 0); usb_phy_set_suspend(dwc->usb3_phy, 0);
ret = phy_power_on(dwc->usb2_generic_phy); ret = phy_power_on(dwc->usb2_generic_phy);
if (ret < 0) if (ret < 0)
goto err1; goto err2;
ret = phy_power_on(dwc->usb3_generic_phy); ret = phy_power_on(dwc->usb3_generic_phy);
if (ret < 0) if (ret < 0)
goto err_usb2phy_power; goto err3;
ret = dwc3_event_buffers_setup(dwc); ret = dwc3_event_buffers_setup(dwc);
if (ret) { if (ret) {
dev_err(dwc->dev, "failed to setup event buffers\n"); dev_err(dwc->dev, "failed to setup event buffers\n");
goto err_usb3phy_power; goto err4;
} }
ret = dwc3_core_init_mode(dwc); ret = dwc3_core_init_mode(dwc);
if (ret) if (ret)
goto err2; goto err5;
ret = dwc3_debugfs_init(dwc); ret = dwc3_debugfs_init(dwc);
if (ret) { if (ret) {
dev_err(dev, "failed to initialize debugfs\n"); dev_err(dev, "failed to initialize debugfs\n");
goto err3; goto err6;
} }
pm_runtime_allow(dev); pm_runtime_allow(dev);
return 0; return 0;
err3: err6:
dwc3_core_exit_mode(dwc); dwc3_core_exit_mode(dwc);
err2: err5:
dwc3_event_buffers_cleanup(dwc); dwc3_event_buffers_cleanup(dwc);
err_usb3phy_power: err4:
phy_power_off(dwc->usb3_generic_phy); phy_power_off(dwc->usb3_generic_phy);
err_usb2phy_power: err3:
phy_power_off(dwc->usb2_generic_phy); phy_power_off(dwc->usb2_generic_phy);
err1: err2:
usb_phy_set_suspend(dwc->usb2_phy, 1); usb_phy_set_suspend(dwc->usb2_phy, 1);
usb_phy_set_suspend(dwc->usb3_phy, 1); usb_phy_set_suspend(dwc->usb3_phy, 1);
dwc3_core_exit(dwc); dwc3_core_exit(dwc);
err0: err1:
dwc3_free_event_buffers(dwc); 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; return ret;
} }
static int dwc3_remove(struct platform_device *pdev) static int dwc3_remove(struct platform_device *pdev)
{ {
struct dwc3 *dwc = platform_get_drvdata(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_debugfs_exit(dwc);
dwc3_core_exit_mode(dwc); dwc3_core_exit_mode(dwc);
......
...@@ -689,6 +689,7 @@ struct dwc3_scratchpad_array { ...@@ -689,6 +689,7 @@ struct dwc3_scratchpad_array {
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround * @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
* @start_config_issued: true when StartConfig command has been issued * @start_config_issued: true when StartConfig command has been issued
* @three_stage_setup: set if we perform a three phase setup * @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 * @disable_scramble_quirk: set if we enable the disable scramble quirk
* @u2exit_lfps_quirk: set if we enable u2exit lfps 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 * @u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk
...@@ -812,6 +813,7 @@ struct dwc3 { ...@@ -812,6 +813,7 @@ struct dwc3 {
unsigned setup_packet_pending:1; unsigned setup_packet_pending:1;
unsigned start_config_issued:1; unsigned start_config_issued:1;
unsigned three_stage_setup:1; unsigned three_stage_setup:1;
unsigned usb3_lpm_capable:1;
unsigned disable_scramble_quirk:1; unsigned disable_scramble_quirk:1;
unsigned u2exit_lfps_quirk:1; unsigned u2exit_lfps_quirk:1;
......
...@@ -325,15 +325,6 @@ static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap) ...@@ -325,15 +325,6 @@ static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
return IRQ_HANDLED; 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) static void dwc3_omap_enable_irqs(struct dwc3_omap *omap)
{ {
u32 reg; u32 reg;
...@@ -600,7 +591,7 @@ static int dwc3_omap_remove(struct platform_device *pdev) ...@@ -600,7 +591,7 @@ static int dwc3_omap_remove(struct platform_device *pdev)
if (omap->extcon_id_dev.edev) if (omap->extcon_id_dev.edev)
extcon_unregister_interest(&omap->extcon_id_dev); extcon_unregister_interest(&omap->extcon_id_dev);
dwc3_omap_disable_irqs(omap); 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_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
......
...@@ -24,8 +24,6 @@ ...@@ -24,8 +24,6 @@
#include "platform_data.h" #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_SYNOPSYS_HAPSUSB3 0xabcd
#define PCI_DEVICE_ID_INTEL_BYT 0x0f37 #define PCI_DEVICE_ID_INTEL_BYT 0x0f37
#define PCI_DEVICE_ID_INTEL_MRFLD 0x119e #define PCI_DEVICE_ID_INTEL_MRFLD 0x119e
......
...@@ -1855,32 +1855,27 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, ...@@ -1855,32 +1855,27 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
unsigned int i; unsigned int i;
int ret; int ret;
req = next_request(&dep->req_queued);
if (!req) {
WARN_ON_ONCE(1);
return 1;
}
i = 0;
do { do {
req = next_request(&dep->req_queued); slot = req->start_slot + i;
if (!req) { if ((slot == DWC3_TRB_NUM - 1) &&
WARN_ON_ONCE(1);
return 1;
}
i = 0;
do {
slot = req->start_slot + i;
if ((slot == DWC3_TRB_NUM - 1) &&
usb_endpoint_xfer_isoc(dep->endpoint.desc)) usb_endpoint_xfer_isoc(dep->endpoint.desc))
slot++; slot++;
slot %= DWC3_TRB_NUM; slot %= DWC3_TRB_NUM;
trb = &dep->trb_pool[slot]; 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);
ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
event, status);
if (ret) if (ret)
break; break;
} while (1); } while (++i < req->request.num_mapped_sgs);
dwc3_gadget_giveback(dep, req, status);
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
list_empty(&dep->req_queued)) { list_empty(&dep->req_queued)) {
......
...@@ -49,9 +49,7 @@ int dwc3_host_init(struct dwc3 *dwc) ...@@ -49,9 +49,7 @@ int dwc3_host_init(struct dwc3 *dwc)
memset(&pdata, 0, sizeof(pdata)); memset(&pdata, 0, sizeof(pdata));
#ifdef CONFIG_DWC3_HOST_USB3_LPM_ENABLE pdata.usb3_lpm_capable = dwc->usb3_lpm_capable;
pdata.usb3_lpm_capable = 1;
#endif
ret = platform_device_add_data(xhci, &pdata, sizeof(pdata)); ret = platform_device_add_data(xhci, &pdata, sizeof(pdata));
if (ret) { if (ret) {
......
...@@ -24,6 +24,7 @@ struct dwc3_platform_data { ...@@ -24,6 +24,7 @@ struct dwc3_platform_data {
enum usb_device_speed maximum_speed; enum usb_device_speed maximum_speed;
enum usb_dr_mode dr_mode; enum usb_dr_mode dr_mode;
bool tx_fifo_resize; bool tx_fifo_resize;
bool usb3_lpm_capable;
unsigned is_utmi_l1_suspend:1; unsigned is_utmi_l1_suspend:1;
u8 hird_threshold; u8 hird_threshold;
......
...@@ -196,6 +196,9 @@ config USB_F_MIDI ...@@ -196,6 +196,9 @@ config USB_F_MIDI
config USB_F_HID config USB_F_HID
tristate tristate
config USB_F_PRINTER
tristate
choice choice
tristate "USB Gadget Drivers" tristate "USB Gadget Drivers"
default USB_ETH default USB_ETH
...@@ -434,6 +437,20 @@ config USB_CONFIGFS_F_UVC ...@@ -434,6 +437,20 @@ config USB_CONFIGFS_F_UVC
device. It provides a userspace API to process UVC control requests device. It provides a userspace API to process UVC control requests
and stream video data to the host. 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" source "drivers/usb/gadget/legacy/Kconfig"
endchoice endchoice
......
...@@ -1161,11 +1161,11 @@ static struct usb_gadget_string_container *copy_gadget_strings( ...@@ -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 * 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 * 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 * 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 * The ->language pointer of each struct usb_gadget_strings has to contain the
* same amount of entries. * same amount of entries.
* For instance: sp[0] is en-US, sp[1] is es-ES. It is expected that the first * 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. * entry of en-US. Therefore both entries become the same id assign.
*/ */
struct usb_string *usb_gstrings_attach(struct usb_composite_dev *cdev, 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) ...@@ -1472,6 +1472,13 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
req->length = 0; req->length = 0;
gadget->ep0->driver_data = cdev; 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) { switch (ctrl->bRequest) {
/* we handle all standard USB descriptors */ /* we handle all standard USB descriptors */
...@@ -1751,6 +1758,10 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) ...@@ -1751,6 +1758,10 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
* take such requests too, if that's ever needed: to work * take such requests too, if that's ever needed: to work
* in config 0, etc. * 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) { switch (ctrl->bRequestType & USB_RECIP_MASK) {
case USB_RECIP_INTERFACE: case USB_RECIP_INTERFACE:
if (!cdev->config || intf >= MAX_CONFIG_INTERFACES) if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
...@@ -1768,7 +1779,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) ...@@ -1768,7 +1779,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
f = NULL; f = NULL;
break; break;
} }
try_fun_setup:
if (f && f->setup) if (f && f->setup)
value = f->setup(f, ctrl); value = f->setup(f, ctrl);
else { else {
......
...@@ -42,3 +42,5 @@ usb_f_midi-y := f_midi.o ...@@ -42,3 +42,5 @@ usb_f_midi-y := f_midi.o
obj-$(CONFIG_USB_F_MIDI) += usb_f_midi.o obj-$(CONFIG_USB_F_MIDI) += usb_f_midi.o
usb_f_hid-y := f_hid.o usb_f_hid-y := f_hid.o
obj-$(CONFIG_USB_F_HID) += usb_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) ...@@ -908,7 +908,6 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
/* disable/free request and end point */ /* disable/free request and end point */
usb_ep_disable(hidg->in_ep); usb_ep_disable(hidg->in_ep);
usb_ep_dequeue(hidg->in_ep, hidg->req);
kfree(hidg->req->buf); kfree(hidg->req->buf);
usb_ep_free_request(hidg->in_ep, hidg->req); 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) ...@@ -1085,7 +1085,7 @@ static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh)
if (!curlun) { /* Unsupported LUNs are okay */ if (!curlun) { /* Unsupported LUNs are okay */
common->bad_lun_okay = 1; common->bad_lun_okay = 1;
memset(buf, 0, 36); 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 */ buf[4] = 31; /* Additional length */
return 36; return 36;
} }
...@@ -2624,13 +2624,10 @@ static ssize_t file_store(struct device *dev, struct device_attribute *attr, ...@@ -2624,13 +2624,10 @@ static ssize_t file_store(struct device *dev, struct device_attribute *attr,
return fsg_store_file(curlun, filesem, buf, count); return fsg_store_file(curlun, filesem, buf, count);
} }
static DEVICE_ATTR_RW(ro);
static DEVICE_ATTR_RW(nofua); static DEVICE_ATTR_RW(nofua);
static DEVICE_ATTR_RW(file); /* mode wil be set in fsg_lun_attr_is_visible() */
static DEVICE_ATTR(ro, 0, ro_show, ro_store);
static struct device_attribute dev_attr_ro_cdrom = __ATTR_RO(ro); static DEVICE_ATTR(file, 0, file_show, file_store);
static struct device_attribute dev_attr_file_nonremovable = __ATTR_RO(file);
/****************************** FSG COMMON ******************************/ /****************************** FSG COMMON ******************************/
...@@ -2745,40 +2742,10 @@ int fsg_common_set_num_buffers(struct fsg_common *common, unsigned int n) ...@@ -2745,40 +2742,10 @@ int fsg_common_set_num_buffers(struct fsg_common *common, unsigned int n)
} }
EXPORT_SYMBOL_GPL(fsg_common_set_num_buffers); 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) void fsg_common_remove_lun(struct fsg_lun *lun, bool sysfs)
{ {
if (sysfs) { if (sysfs)
fsg_common_remove_sysfs(lun);
device_unregister(&lun->dev); device_unregister(&lun->dev);
}
fsg_lun_close(lun); fsg_lun_close(lun);
kfree(lun); kfree(lun);
} }
...@@ -2877,41 +2844,35 @@ int fsg_common_set_cdev(struct fsg_common *common, ...@@ -2877,41 +2844,35 @@ int fsg_common_set_cdev(struct fsg_common *common,
} }
EXPORT_SYMBOL_GPL(fsg_common_set_cdev); EXPORT_SYMBOL_GPL(fsg_common_set_cdev);
static inline int fsg_common_add_sysfs(struct fsg_common *common, static struct attribute *fsg_lun_dev_attrs[] = {
struct fsg_lun *lun) &dev_attr_ro.attr,
{ &dev_attr_file.attr,
int rc; &dev_attr_nofua.attr,
NULL
};
rc = device_register(&lun->dev); static umode_t fsg_lun_dev_is_visible(struct kobject *kobj,
if (rc) { struct attribute *attr, int idx)
put_device(&lun->dev); {
return rc; struct device *dev = kobj_to_dev(kobj);
} struct fsg_lun *lun = fsg_lun_from_dev(dev);
rc = device_create_file(&lun->dev, if (attr == &dev_attr_ro.attr)
lun->cdrom return lun->cdrom ? S_IRUGO : (S_IWUSR | S_IRUGO);
? &dev_attr_ro_cdrom if (attr == &dev_attr_file.attr)
: &dev_attr_ro); return lun->removable ? (S_IWUSR | S_IRUGO) : S_IRUGO;
if (rc) return attr->mode;
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; static const struct attribute_group fsg_lun_dev_group = {
.attrs = fsg_lun_dev_attrs,
.is_visible = fsg_lun_dev_is_visible,
};
error: static const struct attribute_group *fsg_lun_dev_groups[] = {
/* removing nonexistent files is a no-op */ &fsg_lun_dev_group,
fsg_common_remove_sysfs(lun); NULL
device_unregister(&lun->dev); };
return rc;
}
int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg, int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg,
unsigned int id, const char *name, unsigned int id, const char *name,
...@@ -2949,13 +2910,15 @@ int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg, ...@@ -2949,13 +2910,15 @@ int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg,
} else { } else {
lun->dev.release = fsg_lun_release; lun->dev.release = fsg_lun_release;
lun->dev.parent = &common->gadget->dev; lun->dev.parent = &common->gadget->dev;
lun->dev.groups = fsg_lun_dev_groups;
dev_set_drvdata(&lun->dev, &common->filesem); dev_set_drvdata(&lun->dev, &common->filesem);
dev_set_name(&lun->dev, "%s", name); dev_set_name(&lun->dev, "%s", name);
lun->name = dev_name(&lun->dev); lun->name = dev_name(&lun->dev);
rc = fsg_common_add_sysfs(common, lun); rc = device_register(&lun->dev);
if (rc) { if (rc) {
pr_info("failed to register LUN%d: %d\n", id, rc); pr_info("failed to register LUN%d: %d\n", id, rc);
put_device(&lun->dev);
goto error_sysfs; goto error_sysfs;
} }
} }
...@@ -2988,10 +2951,8 @@ int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg, ...@@ -2988,10 +2951,8 @@ int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg,
return 0; return 0;
error_lun: error_lun:
if (common->sysfs) { if (common->sysfs)
fsg_common_remove_sysfs(lun);
device_unregister(&lun->dev); device_unregister(&lun->dev);
}
fsg_lun_close(lun); fsg_lun_close(lun);
common->luns[id] = NULL; common->luns[id] = NULL;
error_sysfs: error_sysfs:
...@@ -3077,8 +3038,6 @@ static void fsg_common_release(struct kref *ref) ...@@ -3077,8 +3038,6 @@ static void fsg_common_release(struct kref *ref)
struct fsg_lun *lun = *lun_it; struct fsg_lun *lun = *lun_it;
if (!lun) if (!lun)
continue; continue;
if (common->sysfs)
fsg_common_remove_sysfs(lun);
fsg_lun_close(lun); fsg_lun_close(lun);
if (common->sysfs) if (common->sysfs)
device_unregister(&lun->dev); device_unregister(&lun->dev);
......
此差异已折叠。
/*
* 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) ...@@ -912,7 +912,7 @@ static int gs_put_char(struct tty_struct *tty, unsigned char ch)
unsigned long flags; unsigned long flags;
int status; 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)); port->port_num, tty, ch, __builtin_return_address(0));
spin_lock_irqsave(&port->port_lock, flags); spin_lock_irqsave(&port->port_lock, flags);
......
...@@ -301,6 +301,7 @@ config USB_MIDI_GADGET ...@@ -301,6 +301,7 @@ config USB_MIDI_GADGET
config USB_G_PRINTER config USB_G_PRINTER
tristate "Printer Gadget" tristate "Printer Gadget"
select USB_LIBCOMPOSITE select USB_LIBCOMPOSITE
select USB_F_PRINTER
help help
The Printer Gadget channels data between the USB host and a The Printer Gadget channels data between the USB host and a
userspace program driving the print engine. The user space userspace program driving the print engine. The user space
......
此差异已折叠。
...@@ -152,7 +152,7 @@ static int regs_dbg_open(struct inode *inode, struct file *file) ...@@ -152,7 +152,7 @@ static int regs_dbg_open(struct inode *inode, struct file *file)
spin_lock_irq(&udc->lock); spin_lock_irq(&udc->lock);
for (i = 0; i < inode->i_size / 4; i++) 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); spin_unlock_irq(&udc->lock);
file->private_data = data; file->private_data = data;
...@@ -1249,7 +1249,7 @@ static int handle_ep0_setup(struct usba_udc *udc, struct usba_ep *ep, ...@@ -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))) if (crq->wLength != cpu_to_le16(sizeof(status)))
goto stall; goto stall;
ep->state = DATA_STAGE_IN; 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); usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY);
break; break;
} }
...@@ -1739,7 +1739,72 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) ...@@ -1739,7 +1739,72 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
return IRQ_HANDLED; 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; struct usba_udc *udc = devid;
int vbus; int vbus;
...@@ -1747,35 +1812,22 @@ static irqreturn_t usba_vbus_irq(int irq, void *devid) ...@@ -1747,35 +1812,22 @@ static irqreturn_t usba_vbus_irq(int irq, void *devid)
/* debounce */ /* debounce */
udelay(10); udelay(10);
spin_lock(&udc->lock); mutex_lock(&udc->vbus_mutex);
/* May happen if Vbus pin toggles during probe() */
if (!udc->driver)
goto out;
vbus = vbus_is_present(udc); vbus = vbus_is_present(udc);
if (vbus != udc->vbus_prev) { if (vbus != udc->vbus_prev) {
if (vbus) { if (vbus) {
toggle_bias(udc, 1); usba_start(udc);
usba_writel(udc, CTRL, USBA_ENABLE_MASK);
usba_int_enb_set(udc, USBA_END_OF_RESET);
} else { } else {
udc->gadget.speed = USB_SPEED_UNKNOWN; usba_stop(udc);
reset_all_endpoints(udc);
toggle_bias(udc, 0); if (udc->driver->disconnect)
usba_writel(udc, CTRL, USBA_DISABLE_MASK);
if (udc->driver->disconnect) {
spin_unlock(&udc->lock);
udc->driver->disconnect(&udc->gadget); udc->driver->disconnect(&udc->gadget);
spin_lock(&udc->lock);
}
} }
udc->vbus_prev = vbus; udc->vbus_prev = vbus;
} }
out: mutex_unlock(&udc->vbus_mutex);
spin_unlock(&udc->lock);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -1787,55 +1839,47 @@ static int atmel_usba_start(struct usb_gadget *gadget, ...@@ -1787,55 +1839,47 @@ static int atmel_usba_start(struct usb_gadget *gadget,
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&udc->lock, flags); spin_lock_irqsave(&udc->lock, flags);
udc->devstatus = 1 << USB_DEVICE_SELF_POWERED; udc->devstatus = 1 << USB_DEVICE_SELF_POWERED;
udc->driver = driver; udc->driver = driver;
spin_unlock_irqrestore(&udc->lock, flags); spin_unlock_irqrestore(&udc->lock, flags);
ret = clk_prepare_enable(udc->pclk); mutex_lock(&udc->vbus_mutex);
if (ret)
return ret;
ret = clk_prepare_enable(udc->hclk);
if (ret) {
clk_disable_unprepare(udc->pclk);
return ret;
}
udc->vbus_prev = 0;
if (gpio_is_valid(udc->vbus_pin)) if (gpio_is_valid(udc->vbus_pin))
enable_irq(gpio_to_irq(udc->vbus_pin)); enable_irq(gpio_to_irq(udc->vbus_pin));
/* If Vbus is present, enable the controller and wait for reset */ /* If Vbus is present, enable the controller and wait for reset */
spin_lock_irqsave(&udc->lock, flags); udc->vbus_prev = vbus_is_present(udc);
if (vbus_is_present(udc) && udc->vbus_prev == 0) { if (udc->vbus_prev) {
toggle_bias(udc, 1); ret = usba_start(udc);
usba_writel(udc, CTRL, USBA_ENABLE_MASK); if (ret)
usba_int_enb_set(udc, USBA_END_OF_RESET); goto err;
} }
spin_unlock_irqrestore(&udc->lock, flags);
mutex_unlock(&udc->vbus_mutex);
return 0; 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) static int atmel_usba_stop(struct usb_gadget *gadget)
{ {
struct usba_udc *udc = container_of(gadget, struct usba_udc, gadget); struct usba_udc *udc = container_of(gadget, struct usba_udc, gadget);
unsigned long flags;
if (gpio_is_valid(udc->vbus_pin)) if (gpio_is_valid(udc->vbus_pin))
disable_irq(gpio_to_irq(udc->vbus_pin)); disable_irq(gpio_to_irq(udc->vbus_pin));
spin_lock_irqsave(&udc->lock, flags); usba_stop(udc);
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);
udc->driver = NULL; udc->driver = NULL;
...@@ -2057,6 +2101,7 @@ static int usba_udc_probe(struct platform_device *pdev) ...@@ -2057,6 +2101,7 @@ static int usba_udc_probe(struct platform_device *pdev)
return PTR_ERR(hclk); return PTR_ERR(hclk);
spin_lock_init(&udc->lock); spin_lock_init(&udc->lock);
mutex_init(&udc->vbus_mutex);
udc->pdev = pdev; udc->pdev = pdev;
udc->pclk = pclk; udc->pclk = pclk;
udc->hclk = hclk; udc->hclk = hclk;
...@@ -2111,17 +2156,17 @@ static int usba_udc_probe(struct platform_device *pdev) ...@@ -2111,17 +2156,17 @@ static int usba_udc_probe(struct platform_device *pdev)
if (gpio_is_valid(udc->vbus_pin)) { if (gpio_is_valid(udc->vbus_pin)) {
if (!devm_gpio_request(&pdev->dev, udc->vbus_pin, "atmel_usba_udc")) { if (!devm_gpio_request(&pdev->dev, udc->vbus_pin, "atmel_usba_udc")) {
ret = devm_request_irq(&pdev->dev, irq_set_status_flags(gpio_to_irq(udc->vbus_pin),
gpio_to_irq(udc->vbus_pin), IRQ_NOAUTOEN);
usba_vbus_irq, 0, ret = devm_request_threaded_irq(&pdev->dev,
gpio_to_irq(udc->vbus_pin), NULL,
usba_vbus_irq_thread, IRQF_ONESHOT,
"atmel_usba_udc", udc); "atmel_usba_udc", udc);
if (ret) { if (ret) {
udc->vbus_pin = -ENODEV; udc->vbus_pin = -ENODEV;
dev_warn(&udc->pdev->dev, dev_warn(&udc->pdev->dev,
"failed to request vbus irq; " "failed to request vbus irq; "
"assuming always on\n"); "assuming always on\n");
} else {
disable_irq(gpio_to_irq(udc->vbus_pin));
} }
} else { } else {
/* gpio_request fail so use -EINVAL for gpio_is_valid */ /* gpio_request fail so use -EINVAL for gpio_is_valid */
...@@ -2132,6 +2177,7 @@ static int usba_udc_probe(struct platform_device *pdev) ...@@ -2132,6 +2177,7 @@ static int usba_udc_probe(struct platform_device *pdev)
ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget); ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
if (ret) if (ret)
return ret; return ret;
device_init_wakeup(&pdev->dev, 1);
usba_init_debugfs(udc); usba_init_debugfs(udc);
for (i = 1; i < udc->num_ep; i++) for (i = 1; i < udc->num_ep; i++)
...@@ -2147,6 +2193,7 @@ static int __exit usba_udc_remove(struct platform_device *pdev) ...@@ -2147,6 +2193,7 @@ static int __exit usba_udc_remove(struct platform_device *pdev)
udc = platform_get_drvdata(pdev); udc = platform_get_drvdata(pdev);
device_init_wakeup(&pdev->dev, 0);
usb_del_gadget_udc(&udc->gadget); usb_del_gadget_udc(&udc->gadget);
for (i = 1; i < udc->num_ep; i++) for (i = 1; i < udc->num_ep; i++)
...@@ -2156,10 +2203,65 @@ static int __exit usba_udc_remove(struct platform_device *pdev) ...@@ -2156,10 +2203,65 @@ static int __exit usba_udc_remove(struct platform_device *pdev)
return 0; 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 = { static struct platform_driver udc_driver = {
.remove = __exit_p(usba_udc_remove), .remove = __exit_p(usba_udc_remove),
.driver = { .driver = {
.name = "atmel_usba_udc", .name = "atmel_usba_udc",
.pm = &usba_udc_pm_ops,
.of_match_table = of_match_ptr(atmel_udc_dt_ids), .of_match_table = of_match_ptr(atmel_udc_dt_ids),
}, },
}; };
......
...@@ -191,18 +191,28 @@ ...@@ -191,18 +191,28 @@
| USBA_BF(name, value)) | USBA_BF(name, value))
/* Register access macros */ /* 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) \ #define usba_readl(udc, reg) \
__raw_readl((udc)->regs + USBA_##reg) usba_io_readl((udc)->regs + USBA_##reg)
#define usba_writel(udc, reg, value) \ #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) \ #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) \ #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) \ #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) \ #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 */ /* Calculate base address for a given endpoint or DMA controller */
#define USBA_EPT_BASE(x) (0x100 + (x) * 0x20) #define USBA_EPT_BASE(x) (0x100 + (x) * 0x20)
...@@ -313,6 +323,9 @@ struct usba_udc { ...@@ -313,6 +323,9 @@ struct usba_udc {
/* Protect hw registers from concurrent modifications */ /* Protect hw registers from concurrent modifications */
spinlock_t lock; spinlock_t lock;
/* Mutex to prevent concurrent start or stop */
struct mutex vbus_mutex;
void __iomem *regs; void __iomem *regs;
void __iomem *fifo; void __iomem *fifo;
...@@ -328,6 +341,7 @@ struct usba_udc { ...@@ -328,6 +341,7 @@ struct usba_udc {
struct clk *hclk; struct clk *hclk;
struct usba_ep *usba_ep; struct usba_ep *usba_ep;
bool bias_pulse_needed; bool bias_pulse_needed;
bool clocked;
u16 devstatus; u16 devstatus;
......
...@@ -1923,7 +1923,7 @@ static inline void ...@@ -1923,7 +1923,7 @@ static inline void
ss_hub_descriptor(struct usb_hub_descriptor *desc) ss_hub_descriptor(struct usb_hub_descriptor *desc)
{ {
memset(desc, 0, sizeof *desc); memset(desc, 0, sizeof *desc);
desc->bDescriptorType = 0x2a; desc->bDescriptorType = USB_DT_SS_HUB;
desc->bDescLength = 12; desc->bDescLength = 12;
desc->wHubCharacteristics = cpu_to_le16( desc->wHubCharacteristics = cpu_to_le16(
HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_INDV_PORT_LPSM |
...@@ -1936,7 +1936,7 @@ ss_hub_descriptor(struct usb_hub_descriptor *desc) ...@@ -1936,7 +1936,7 @@ ss_hub_descriptor(struct usb_hub_descriptor *desc)
static inline void hub_descriptor(struct usb_hub_descriptor *desc) static inline void hub_descriptor(struct usb_hub_descriptor *desc)
{ {
memset(desc, 0, sizeof *desc); memset(desc, 0, sizeof *desc);
desc->bDescriptorType = 0x29; desc->bDescriptorType = USB_DT_HUB;
desc->bDescLength = 9; desc->bDescLength = 9;
desc->wHubCharacteristics = cpu_to_le16( desc->wHubCharacteristics = cpu_to_le16(
HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_INDV_PORT_LPSM |
...@@ -2631,7 +2631,7 @@ static int __init init(void) ...@@ -2631,7 +2631,7 @@ static int __init init(void)
return -EINVAL; return -EINVAL;
if (mod_data.num < 1 || mod_data.num > MAX_NUM_UDC) { 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); MAX_NUM_UDC);
return -EINVAL; return -EINVAL;
} }
......
...@@ -1024,35 +1024,79 @@ static const char proc_node_name [] = "driver/udc"; ...@@ -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) static void dump_intmask(struct seq_file *m, const char *label, u32 mask)
{ {
/* int_status is the same format ... */ /* int_status is the same format ... */
seq_printf(m, seq_printf(m, "%s %05X =" FOURBITS EIGHTBITS EIGHTBITS "\n",
"%s %05X =" FOURBITS EIGHTBITS EIGHTBITS "\n", label, mask,
label, mask, (mask & INT_PWRDETECT) ? " power" : "",
(mask & INT_PWRDETECT) ? " power" : "", (mask & INT_SYSERROR) ? " sys" : "",
(mask & INT_SYSERROR) ? " sys" : "", (mask & INT_MSTRDEND) ? " in-dma" : "",
(mask & INT_MSTRDEND) ? " in-dma" : "", (mask & INT_MSTWRTMOUT) ? " wrtmo" : "",
(mask & INT_MSTWRTMOUT) ? " wrtmo" : "",
(mask & INT_MSTWREND) ? " out-dma" : "",
(mask & INT_MSTWREND) ? " out-dma" : "", (mask & INT_MSTWRSET) ? " wrset" : "",
(mask & INT_MSTWRSET) ? " wrset" : "", (mask & INT_ERR) ? " err" : "",
(mask & INT_ERR) ? " err" : "", (mask & INT_SOF) ? " sof" : "",
(mask & INT_SOF) ? " sof" : "",
(mask & INT_EP3NAK) ? " ep3nak" : "",
(mask & INT_EP3NAK) ? " ep3nak" : "", (mask & INT_EP2NAK) ? " ep2nak" : "",
(mask & INT_EP2NAK) ? " ep2nak" : "", (mask & INT_EP1NAK) ? " ep1nak" : "",
(mask & INT_EP1NAK) ? " ep1nak" : "", (mask & INT_EP3DATASET) ? " ep3" : "",
(mask & INT_EP3DATASET) ? " ep3" : "",
(mask & INT_EP2DATASET) ? " ep2" : "",
(mask & INT_EP2DATASET) ? " ep2" : "", (mask & INT_EP1DATASET) ? " ep1" : "",
(mask & INT_EP1DATASET) ? " ep1" : "", (mask & INT_STATUSNAK) ? " ep0snak" : "",
(mask & INT_STATUSNAK) ? " ep0snak" : "", (mask & INT_STATUS) ? " ep0status" : "",
(mask & INT_STATUS) ? " ep0status" : "",
(mask & INT_SETUP) ? " setup" : "",
(mask & INT_SETUP) ? " setup" : "", (mask & INT_ENDPOINT0) ? " ep0" : "",
(mask & INT_ENDPOINT0) ? " ep0" : "", (mask & INT_USBRESET) ? " reset" : "",
(mask & INT_USBRESET) ? " reset" : "", (mask & INT_SUSPEND) ? " suspend" : "");
(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) 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) ...@@ -1068,29 +1112,18 @@ static int udc_proc_read(struct seq_file *m, void *v)
tmp = readl(&regs->power_detect); tmp = readl(&regs->power_detect);
is_usb_connected = tmp & PW_DETECT; is_usb_connected = tmp & PW_DETECT;
seq_printf(m, seq_printf(m,
"%s - %s\n" "%s - %s\n"
"%s version: %s %s\n" "%s version: %s %s\n"
"Gadget driver: %s\n" "Gadget driver: %s\n"
"Host %s, %s\n" "Host %s, %s\n"
"\n", "\n",
pci_name(dev->pdev), driver_desc, pci_name(dev->pdev), driver_desc,
driver_name, DRIVER_VERSION, dmastr(), driver_name, DRIVER_VERSION, dmastr(),
dev->driver ? dev->driver->driver.name : "(none)", dev->driver ? dev->driver->driver.name : "(none)",
is_usb_connected is_usb_connected
? ((tmp & PW_PULLUP) ? "full speed" : "powered") ? ((tmp & PW_PULLUP) ? "full speed" : "powered")
: "disconnected", : "disconnected",
({const char *state; udc_ep_state(dev->ep0state));
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; })
);
dump_intmask(m, "int_status", readl(&regs->int_status)); dump_intmask(m, "int_status", readl(&regs->int_status));
dump_intmask(m, "int_enable", readl(&regs->int_enable)); dump_intmask(m, "int_enable", readl(&regs->int_enable));
...@@ -1099,31 +1132,30 @@ static int udc_proc_read(struct seq_file *m, void *v) ...@@ -1099,31 +1132,30 @@ static int udc_proc_read(struct seq_file *m, void *v)
goto done; goto done;
/* registers for (active) device and ep0 */ /* registers for (active) device and ep0 */
if (seq_printf(m, "\nirqs %lu\ndataset %02x " seq_printf(m, "\nirqs %lu\ndataset %02x single.bcs %02x.%02x state %x addr %u\n",
"single.bcs %02x.%02x state %x addr %u\n", dev->irqs, readl(&regs->DataSet),
dev->irqs, readl(&regs->DataSet), readl(&regs->EPxSingle), readl(&regs->EPxBCS),
readl(&regs->EPxSingle), readl(&regs->EPxBCS), readl(&regs->UsbState),
readl(&regs->UsbState), readl(&regs->address));
readl(&regs->address)) < 0) if (seq_has_overflowed(m))
goto done; goto done;
tmp = readl(&regs->dma_master); tmp = readl(&regs->dma_master);
if (seq_printf(m, seq_printf(m, "dma %03X =" EIGHTBITS "%s %s\n",
"dma %03X =" EIGHTBITS "%s %s\n", tmp, tmp,
(tmp & MST_EOPB_DIS) ? " eopb-" : "", (tmp & MST_EOPB_DIS) ? " eopb-" : "",
(tmp & MST_EOPB_ENA) ? " eopb+" : "", (tmp & MST_EOPB_ENA) ? " eopb+" : "",
(tmp & MST_TIMEOUT_DIS) ? " tmo-" : "", (tmp & MST_TIMEOUT_DIS) ? " tmo-" : "",
(tmp & MST_TIMEOUT_ENA) ? " tmo+" : "", (tmp & MST_TIMEOUT_ENA) ? " tmo+" : "",
(tmp & MST_RD_EOPB) ? " eopb" : "", (tmp & MST_RD_EOPB) ? " eopb" : "",
(tmp & MST_RD_RESET) ? " in_reset" : "", (tmp & MST_RD_RESET) ? " in_reset" : "",
(tmp & MST_WR_RESET) ? " out_reset" : "", (tmp & MST_WR_RESET) ? " out_reset" : "",
(tmp & MST_RD_ENA) ? " IN" : "", (tmp & MST_RD_ENA) ? " IN" : "",
(tmp & MST_WR_ENA) ? " OUT" : "", (tmp & MST_WR_ENA) ? " OUT" : "",
(tmp & MST_CONNECTION) (tmp & MST_CONNECTION) ? "ep1in/ep2out" : "ep1out/ep2in");
? "ep1in/ep2out" if (seq_has_overflowed(m))
: "ep1out/ep2in") < 0)
goto done; goto done;
/* dump endpoint queues */ /* dump endpoint queues */
...@@ -1135,44 +1167,23 @@ static int udc_proc_read(struct seq_file *m, void *v) ...@@ -1135,44 +1167,23 @@ static int udc_proc_read(struct seq_file *m, void *v)
continue; continue;
tmp = readl(ep->reg_status); tmp = readl(ep->reg_status);
if (seq_printf(m, seq_printf(m, "%s %s max %u %s, irqs %lu, status %02x (%s) " FOURBITS "\n",
"%s %s max %u %s, irqs %lu, " ep->ep.name,
"status %02x (%s) " FOURBITS "\n", ep->is_in ? "in" : "out",
ep->ep.name, ep->ep.maxpacket,
ep->is_in ? "in" : "out", ep->dma ? "dma" : "pio",
ep->ep.maxpacket, ep->irqs,
ep->dma ? "dma" : "pio", tmp, udc_ep_status(tmp),
ep->irqs, (tmp & EPxSTATUS_TOGGLE) ? "data1" : "data0",
tmp, ({ char *s; (tmp & EPxSTATUS_SUSPEND) ? " suspend" : "",
switch (tmp & EPxSTATUS_EP_MASK) { (tmp & EPxSTATUS_FIFO_DISABLE) ? " disable" : "",
case EPxSTATUS_EP_READY: (tmp & EPxSTATUS_STAGE_ERROR) ? " ep0stat" : "");
s = "ready"; break; if (seq_has_overflowed(m))
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)
goto done; goto done;
if (list_empty(&ep->queue)) { 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; goto done;
continue; continue;
} }
...@@ -1187,10 +1198,10 @@ static int udc_proc_read(struct seq_file *m, void *v) ...@@ -1187,10 +1198,10 @@ static int udc_proc_read(struct seq_file *m, void *v)
} else } else
tmp = req->req.actual; tmp = req->req.actual;
if (seq_printf(m, seq_printf(m, "\treq %p len %u/%u buf %p\n",
"\treq %p len %u/%u buf %p\n", &req->req, tmp, req->req.length,
&req->req, tmp, req->req.length, req->req.buf);
req->req.buf) < 0) if (seq_has_overflowed(m))
goto done; goto done;
} }
} }
......
...@@ -1803,23 +1803,14 @@ static int lpc32xx_ep_queue(struct usb_ep *_ep, ...@@ -1803,23 +1803,14 @@ static int lpc32xx_ep_queue(struct usb_ep *_ep,
req = container_of(_req, struct lpc32xx_request, req); req = container_of(_req, struct lpc32xx_request, req);
ep = container_of(_ep, struct lpc32xx_ep, ep); 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)) !list_empty(&req->queue))
return -EINVAL; return -EINVAL;
udc = ep->udc; udc = ep->udc;
if (!_ep) { if (udc->gadget.speed == USB_SPEED_UNKNOWN)
dev_dbg(udc->dev, "invalid ep\n"); return -EPIPE;
return -EINVAL;
}
if ((!udc) || (!udc->driver) ||
(udc->gadget.speed == USB_SPEED_UNKNOWN)) {
dev_dbg(udc->dev, "invalid device\n");
return -EINVAL;
}
if (ep->lep) { if (ep->lep) {
struct lpc32xx_usbd_dd_gad *dd; struct lpc32xx_usbd_dd_gad *dd;
......
此差异已折叠。
...@@ -96,7 +96,6 @@ struct net2280_ep { ...@@ -96,7 +96,6 @@ struct net2280_ep {
struct net2280_ep_regs __iomem *regs; struct net2280_ep_regs __iomem *regs;
struct net2280_dma_regs __iomem *dma; struct net2280_dma_regs __iomem *dma;
struct net2280_dma *dummy; struct net2280_dma *dummy;
struct usb338x_fifo_regs __iomem *fiforegs;
dma_addr_t td_dma; /* of dummy */ dma_addr_t td_dma; /* of dummy */
struct net2280 *dev; struct net2280 *dev;
unsigned long irqs; unsigned long irqs;
...@@ -181,7 +180,6 @@ struct net2280 { ...@@ -181,7 +180,6 @@ struct net2280 {
struct net2280_dma_regs __iomem *dma; struct net2280_dma_regs __iomem *dma;
struct net2280_dep_regs __iomem *dep; struct net2280_dep_regs __iomem *dep;
struct net2280_ep_regs __iomem *epregs; struct net2280_ep_regs __iomem *epregs;
struct usb338x_fifo_regs __iomem *fiforegs;
struct usb338x_ll_regs __iomem *llregs; struct usb338x_ll_regs __iomem *llregs;
struct usb338x_ll_lfps_regs __iomem *ll_lfps_regs; struct usb338x_ll_lfps_regs __iomem *ll_lfps_regs;
struct usb338x_ll_tsn_regs __iomem *ll_tsn_regs; struct usb338x_ll_tsn_regs __iomem *ll_tsn_regs;
......
此差异已折叠。
此差异已折叠。
...@@ -198,7 +198,7 @@ config USB_EHCI_HCD_AT91 ...@@ -198,7 +198,7 @@ config USB_EHCI_HCD_AT91
config USB_EHCI_MSM config USB_EHCI_MSM
tristate "Support for Qualcomm QSD/MSM on-chip EHCI USB controller" 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 select USB_EHCI_ROOT_HUB_TT
---help--- ---help---
Enables support for the USB Host controller present on the Enables support for the USB Host controller present on the
......
...@@ -792,12 +792,12 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) ...@@ -792,12 +792,12 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
ehci->reset_done[i] == 0)) ehci->reset_done[i] == 0))
continue; continue;
/* start 20 msec resume signaling from this port, /* start USB_RESUME_TIMEOUT msec resume signaling from
* and make hub_wq collect PORT_STAT_C_SUSPEND to * this port, and make hub_wq collect
* stop that signaling. Use 5 ms extra for safety, * PORT_STAT_C_SUSPEND to stop that signaling.
* like usb_port_resume() does.
*/ */
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); set_bit(i, &ehci->resuming_ports);
ehci_dbg (ehci, "port %d remote wakeup\n", i + 1); ehci_dbg (ehci, "port %d remote wakeup\n", i + 1);
usb_hcd_start_port_resume(&hcd->self, i); usb_hcd_start_port_resume(&hcd->self, i);
......
...@@ -471,10 +471,13 @@ static int ehci_bus_resume (struct usb_hcd *hcd) ...@@ -471,10 +471,13 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
ehci_writel(ehci, temp, &ehci->regs->port_status [i]); 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) { if (resume_needed) {
spin_unlock_irq(&ehci->lock); spin_unlock_irq(&ehci->lock);
msleep(20); msleep(USB_RESUME_TIMEOUT);
spin_lock_irq(&ehci->lock); spin_lock_irq(&ehci->lock);
if (ehci->shutdown) if (ehci->shutdown)
goto shutdown; goto shutdown;
...@@ -688,7 +691,7 @@ ehci_hub_descriptor ( ...@@ -688,7 +691,7 @@ ehci_hub_descriptor (
int ports = HCS_N_PORTS (ehci->hcs_params); int ports = HCS_N_PORTS (ehci->hcs_params);
u16 temp; u16 temp;
desc->bDescriptorType = 0x29; desc->bDescriptorType = USB_DT_HUB;
desc->bPwrOn2PwrGood = 10; /* ehci 1.0, 2.3.9 says 20ms max */ desc->bPwrOn2PwrGood = 10; /* ehci 1.0, 2.3.9 says 20ms max */
desc->bHubContrCurrent = 0; desc->bHubContrCurrent = 0;
...@@ -942,7 +945,7 @@ int ehci_hub_control( ...@@ -942,7 +945,7 @@ int ehci_hub_control(
temp &= ~PORT_WAKE_BITS; temp &= ~PORT_WAKE_BITS;
ehci_writel(ehci, temp | PORT_RESUME, status_reg); ehci_writel(ehci, temp | PORT_RESUME, status_reg);
ehci->reset_done[wIndex] = jiffies ehci->reset_done[wIndex] = jiffies
+ msecs_to_jiffies(20); + msecs_to_jiffies(USB_RESUME_TIMEOUT);
set_bit(wIndex, &ehci->resuming_ports); set_bit(wIndex, &ehci->resuming_ports);
usb_hcd_start_port_resume(&hcd->self, wIndex); usb_hcd_start_port_resume(&hcd->self, wIndex);
break; break;
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册