Commit 237f38c3 authored by Linus Torvalds's avatar Linus Torvalds

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

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

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

* tag 'usb-4.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (191 commits)
  MAINTAINERS: change my email address
  USB: usbmon: remove assignment from IS_ERR argument
  USB: mxu11x0: drop redundant function name from error messages
  USB: mxu11x0: fix debug-message typos
  USB: mxu11x0: rename usb-serial driver
  USB: mxu11x0: fix modem-control handling on B0-transitions
  USB: mxu11x0: fix memory leak on firmware download
  USB: mxu11x0: fix memory leak in port-probe error path
  USB: serial: add Moxa UPORT 11x0 driver
  USB: cp210x: add ID for ELV Marble Sound Board 1
  usb: chipidea: otg: use usb autosuspend to suspend bus for HNP
  usb: chipidea: host: set host to be null after hcd is freed
  usb: chipidea: removing of_find_property
  usb: chipidea: implement platform shutdown callback
  usb: chipidea: clean up CONFIG_USB_CHIPIDEA_DEBUG reference
  usb: chipidea: delete static debug support
  usb: chipidea: support debugfs without CONFIG_USB_CHIPIDEA_DEBUG
  usb: chipidea: udc: improve error handling on _hardware_enqueue
  usb: chipidea: udc: _ep_queue and _hw_queue cleanup
  usb: dwc3: of-simple: fix build warning on !PM
  ...
parents 67990608 60d77b3d
......@@ -10,3 +10,5 @@ Description:
isoc_mult - 0..2 (hs/ss only)
isoc_maxburst - 0..15 (ss only)
buflen - buffer length
bulk_qlen - depth of queue for bulk
iso_qlen - depth of queue for iso
......@@ -134,19 +134,21 @@ Description:
enabled for the device. Developer can write y/Y/1 or n/N/0 to
the file to enable/disable the feature.
What: /sys/bus/usb/devices/.../power/usb3_hardware_lpm
Date: June 2015
What: /sys/bus/usb/devices/.../power/usb3_hardware_lpm_u1
/sys/bus/usb/devices/.../power/usb3_hardware_lpm_u2
Date: November 2015
Contact: Kevin Strasser <kevin.strasser@linux.intel.com>
Lu Baolu <baolu.lu@linux.intel.com>
Description:
If CONFIG_PM is set and a USB 3.0 lpm-capable device is plugged
in to a xHCI host which supports link PM, it will check if U1
and U2 exit latencies have been set in the BOS descriptor; if
the check is is passed and the host supports USB3 hardware LPM,
the check is passed and the host supports USB3 hardware LPM,
USB3 hardware LPM will be enabled for the device and the USB
device directory will contain a file named
power/usb3_hardware_lpm. The file holds a string value (enable
or disable) indicating whether or not USB3 hardware LPM is
enabled for the device.
device directory will contain two files named
power/usb3_hardware_lpm_u1 and power/usb3_hardware_lpm_u2. These
files hold a string value (enable or disable) indicating whether
or not USB3 hardware LPM U1 or U2 is enabled for the device.
What: /sys/bus/usb/devices/.../removable
Date: February 2012
......@@ -187,6 +189,17 @@ Description:
The file will read "hotplug", "wired" and "not used" if the
information is available, and "unknown" otherwise.
What: /sys/bus/usb/devices/.../(hub interface)/portX/usb3_lpm_permit
Date: November 2015
Contact: Lu Baolu <baolu.lu@linux.intel.com>
Description:
Some USB3.0 devices are not friendly to USB3 LPM. usb3_lpm_permit
attribute allows enabling/disabling usb3 lpm of a port. It takes
effect both before and after a usb device is enumerated. Supported
values are "0" if both u1 and u2 are NOT permitted, "u1" if only u1
is permitted, "u2" if only u2 is permitted, "u1_u2" if both u1 and
u2 are permitted.
What: /sys/bus/usb/devices/.../power/usb2_lpm_l1_timeout
Date: May 2013
Contact: Mathias Nyman <mathias.nyman@linux.intel.com>
......
......@@ -2,6 +2,7 @@
Required properties:
- compatible: should be one or more of
"brcm,bcm7425-sata-phy"
"brcm,bcm7445-sata-phy"
"brcm,phy-sata3"
- address-cells: should be 1
......
Hisilicon hi6220 usb PHY
-----------------------
Required properties:
- compatible: should be "hisilicon,hi6220-usb-phy"
- #phy-cells: must be 0
- hisilicon,peripheral-syscon: phandle of syscon used to control phy.
Refer to phy/phy-bindings.txt for the generic PHY binding properties
Example:
usb_phy: usbphy {
compatible = "hisilicon,hi6220-usb-phy";
#phy-cells = <0>;
phy-supply = <&fixed_5v_hub>;
hisilicon,peripheral-syscon = <&sys_ctrl>;
};
* Renesas R-Car generation 3 USB 2.0 PHY
This file provides information on what the device node for the R-Car generation
3 USB 2.0 PHY contains.
Required properties:
- compatible: "renesas,usb2-phy-r8a7795" if the device is a part of an R8A7795
SoC.
- reg: offset and length of the partial USB 2.0 Host register block.
- reg-names: must be "usb2_host".
- clocks: clock phandle and specifier pair(s).
- #phy-cells: see phy-bindings.txt in the same directory, must be <0>.
Optional properties:
To use a USB channel where USB 2.0 Host and HSUSB (USB 2.0 Peripheral) are
combined, the device tree node should set HSUSB properties to reg and reg-names
properties. This is because HSUSB has registers to select USB 2.0 host or
peripheral at that channel:
- reg: offset and length of the partial HSUSB register block.
- reg-names: must be "hsusb".
- interrupts: interrupt specifier for the PHY.
Example (R-Car H3):
usb-phy@ee080200 {
compatible = "renesas,usb2-phy-r8a7795";
reg = <0 0xee080200 0 0x700>, <0 0xe6590100 0 0x100>;
reg-names = "usb2_host", "hsusb";
interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&mstp7_clks R8A7795_CLK_EHCI0>,
<&mstp7_clks R8A7795_CLK_HSUSB>;
};
usb-phy@ee0a0200 {
compatible = "renesas,usb2-phy-r8a7795";
reg = <0 0xee0a0200 0 0x700>;
reg-names = "usb2_host";
clocks = <&mstp7_clks R8A7795_CLK_EHCI0>;
};
ROCKCHIP USB2 PHY
Required properties:
- compatible: rockchip,rk3288-usb-phy
- compatible: matching the soc type, one of
"rockchip,rk3066a-usb-phy"
"rockchip,rk3188-usb-phy"
"rockchip,rk3288-usb-phy"
- rockchip,grf : phandle to the syscon managing the "general
register files"
- #address-cells: should be 1
......@@ -21,6 +24,7 @@ required properties:
Optional Properties:
- clocks : phandle + clock specifier for the phy clocks
- clock-names: string, clock name, must be "phyclk"
- #clock-cells: for users of the phy-pll, should be 0
Example:
......
......@@ -9,6 +9,7 @@ Required properties:
* allwinner,sun7i-a20-usb-phy
* allwinner,sun8i-a23-usb-phy
* allwinner,sun8i-a33-usb-phy
* allwinner,sun8i-h3-usb-phy
- reg : a list of offset + length pairs
- reg-names :
* "phy_ctrl"
......
......@@ -31,6 +31,8 @@ OMAP USB2 PHY
Required properties:
- compatible: Should be "ti,omap-usb2"
Should be "ti,dra7x-usb2-phy2" for the 2nd instance of USB2 PHY
in DRA7x
- reg : Address and length of the register set for the device.
- #phy-cells: determine the number of cells that should be given in the
phandle while referencing this phy.
......@@ -40,10 +42,14 @@ Required properties:
* "wkupclk" - wakeup clock.
* "refclk" - reference clock (optional).
Optional properties:
Deprecated properties:
- ctrl-module : phandle of the control module used by PHY driver to power on
the PHY.
Recommended properies:
- syscon-phy-power : phandle/offset pair. Phandle to the system control
module and the register offset to power on/off the PHY.
This is usually a subnode of ocp2scp to which it is connected.
usb2phy@4a0ad080 {
......@@ -77,14 +83,22 @@ Required properties:
* "div-clk" - apll clock
Optional properties:
- ctrl-module : phandle of the control module used by PHY driver to power on
the PHY.
- id: If there are multiple instance of the same type, in order to
differentiate between each instance "id" can be used (e.g., multi-lane PCIe
PHY). If "id" is not provided, it is set to default value of '1'.
- syscon-pllreset: Handle to system control region that contains the
CTRL_CORE_SMA_SW_0 register and register offset to the CTRL_CORE_SMA_SW_0
register that contains the SATA_PLL_SOFT_RESET bit. Only valid for sata_phy.
- syscon-pcs : phandle/offset pair. Phandle to the system control module and the
register offset to write the PCS delay value.
Deprecated properties:
- ctrl-module : phandle of the control module used by PHY driver to power on
the PHY.
Recommended properies:
- syscon-phy-power : phandle/offset pair. Phandle to the system control
module and the register offset to power on/off the PHY.
This is usually a subnode of ocp2scp to which it is connected.
......
......@@ -4,6 +4,7 @@ Platform DesignWare HS OTG USB 2.0 controller
Required properties:
- compatible : One of:
- brcm,bcm2835-usb: The DWC2 USB controller instance in the BCM2835 SoC.
- hisilicon,hi6220-usb: The DWC2 USB controller instance in the hi6220 SoC.
- rockchip,rk3066-usb: The DWC2 USB controller instance in the rk3066 Soc;
- "rockchip,rk3188-usb", "rockchip,rk3066-usb", "snps,dwc2": for rk3188 Soc;
- "rockchip,rk3288-usb", "rockchip,rk3066-usb", "snps,dwc2": for rk3288 Soc;
......
Xilinx SuperSpeed DWC3 USB SoC controller
Required properties:
- compatible: Should contain "xlnx,zynqmp-dwc3"
- clocks: A list of phandles for the clocks listed in clock-names
- clock-names: Should contain the following:
"bus_clk" Master/Core clock, have to be >= 125 MHz for SS
operation and >= 60MHz for HS operation
"ref_clk" Clock source to core during PHY power down
Required child node:
A child node must exist to represent the core DWC3 IP block. The name of
the node is not important. The content of the node is defined in dwc3.txt.
Example device node:
usb@0 {
#address-cells = <0x2>;
#size-cells = <0x1>;
status = "okay";
compatible = "xlnx,zynqmp-dwc3";
clock-names = "bus_clk" "ref_clk";
clocks = <&clk125>, <&clk125>;
ranges;
dwc3@fe200000 {
compatible = "snps,dwc3";
reg = <0x0 0xfe200000 0x40000>;
interrupts = <0x0 0x41 0x4>;
dr_mode = "host";
};
};
MT8173 xHCI
The device node for Mediatek SOC USB3.0 host controller
Required properties:
- compatible : should contain "mediatek,mt8173-xhci"
- reg : specifies physical base address and size of the registers,
the first one for MAC, the second for IPPC
- interrupts : interrupt used by the controller
- power-domains : a phandle to USB power domain node to control USB's
mtcmos
- vusb33-supply : regulator of USB avdd3.3v
- clocks : a list of phandle + clock-specifier pairs, one for each
entry in clock-names
- clock-names : must contain
"sys_ck": for clock of xHCI MAC
"wakeup_deb_p0": for USB wakeup debounce clock of port0
"wakeup_deb_p1": for USB wakeup debounce clock of port1
- phys : a list of phandle + phy specifier pairs
Optional properties:
- mediatek,wakeup-src : 1: ip sleep wakeup mode; 2: line state wakeup
mode;
- mediatek,syscon-wakeup : phandle to syscon used to access USB wakeup
control register, it depends on "mediatek,wakeup-src".
- vbus-supply : reference to the VBUS regulator;
- usb3-lpm-capable : supports USB3.0 LPM
Example:
usb30: usb@11270000 {
compatible = "mediatek,mt8173-xhci";
reg = <0 0x11270000 0 0x1000>,
<0 0x11280700 0 0x0100>;
interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_LOW>;
power-domains = <&scpsys MT8173_POWER_DOMAIN_USB>;
clocks = <&topckgen CLK_TOP_USB30_SEL>,
<&pericfg CLK_PERI_USB0>,
<&pericfg CLK_PERI_USB1>;
clock-names = "sys_ck",
"wakeup_deb_p0",
"wakeup_deb_p1";
phys = <&phy_port0 PHY_TYPE_USB3>,
<&phy_port1 PHY_TYPE_USB2>;
vusb33-supply = <&mt6397_vusb_reg>;
vbus-supply = <&usb_p1_vbus>;
usb3-lpm-capable;
mediatek,syscon-wakeup = <&pericfg>;
mediatek,wakeup-src = <1>;
};
Renesas Electronics USB3.0 Peripheral driver
Required properties:
- compatible: Must contain one of the following:
- "renesas,r8a7795-usb3-peri"
- reg: Base address and length of the register for the USB3.0 Peripheral
- interrupts: Interrupt specifier for the USB3.0 Peripheral
- clocks: clock phandle and specifier pair
Example:
usb3_peri0: usb@ee020000 {
compatible = "renesas,r8a7795-usb3-peri";
reg = <0 0xee020000 0 0x400>;
interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 328>;
};
usb3_peri1: usb@ee060000 {
compatible = "renesas,r8a7795-usb3-peri";
reg = <0 0xee060000 0 0x400>;
interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 327>;
};
Renesas Electronics USBHS driver
Required properties:
- compatible: Must contain one of the following:
- "renesas,usbhs-r8a7790"
- "renesas,usbhs-r8a7791"
- "renesas,usbhs-r8a7794"
- "renesas,usbhs-r8a7795"
- compatible: Must contain one or more of the following:
- "renesas,usbhs-r8a7790" for r8a7790 (R-Car H2) compatible device
- "renesas,usbhs-r8a7791" for r8a7791 (R-Car M2-W) compatible device
- "renesas,usbhs-r8a7792" for r8a7792 (R-Car V2H) compatible device
- "renesas,usbhs-r8a7793" for r8a7793 (R-Car M2-N) compatible device
- "renesas,usbhs-r8a7794" for r8a7794 (R-Car E2) compatible device
- "renesas,usbhs-r8a7795" for r8a7795 (R-Car H3) compatible device
- "renesas,rcar-gen2-usbhs" for R-Car Gen2 compatible device
- "renesas,rcar-gen3-usbhs" for R-Car Gen3 compatible device
When compatible with the generic version, nodes must list the
SoC-specific version corresponding to the platform first followed
by the generic version.
- reg: Base address and length of the register for the USBHS
- interrupts: Interrupt specifier for the USBHS
- clocks: A list of phandle + clock specifier pairs
......@@ -22,7 +32,7 @@ Optional properties:
Example:
usbhs: usb@e6590000 {
compatible = "renesas,usbhs-r8a7790";
compatible = "renesas,usbhs-r8a7790", "renesas,rcar-gen2-usbhs";
reg = <0 0xe6590000 0 0x100>;
interrupts = <0 107 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&mstp7_clks R8A7790_CLK_HSUSB>;
......
......@@ -3,8 +3,8 @@ USB xHCI controllers
Required properties:
- compatible: should be one of "generic-xhci",
"marvell,armada-375-xhci", "marvell,armada-380-xhci",
"renesas,xhci-r8a7790", "renesas,xhci-r8a7791" (deprecated:
"xhci-platform").
"renesas,xhci-r8a7790", "renesas,xhci-r8a7791", "renesas,xhci-r8a7793",
"renesas,xhci-r8a7795" (deprecated: "xhci-platform").
- reg: should contain address and length of the standard XHCI
register set for the device.
- interrupts: one XHCI interrupt should be described here.
......
......@@ -2584,8 +2584,6 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
notsc [BUGS=X86-32] Disable Time Stamp Counter
nousb [USB] Disable the USB subsystem
nowatchdog [KNL] Disable both lockup detectors, i.e.
soft-lockup and NMI watchdog (hard-lockup).
......@@ -3900,6 +3898,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
usbcore.usbfs_snoop=
[USB] Set to log all usbfs traffic (default 0 = off).
usbcore.usbfs_snoop_max=
[USB] Maximum number of bytes to snoop in each URB
(default = 65536).
usbcore.blinkenlights=
[USB] Set to cycle leds on hubs (default 0 = off).
......@@ -3920,6 +3922,8 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
USB_REQ_GET_DESCRIPTOR request in milliseconds
(default 5000 = 5.0 seconds).
usbcore.nousb [USB] Disable the USB subsystem
usbhid.mousepoll=
[USBHID] The interval which mice are to be polled at.
......
......@@ -7,8 +7,8 @@ with 2 Freescale i.MX6Q sabre SD boards.
---------------------------------------
Select CONFIG_USB_OTG_FSM, rebuild kernel Image and modules.
If you want to check some internal variables for otg fsm,
select CONFIG_USB_CHIPIDEA_DEBUG, there are 2 files which
can show otg fsm variables and some controller registers value:
mount debugfs, there are 2 files which can show otg fsm
variables and some controller registers value:
cat /sys/kernel/debug/ci_hdrc.0/otg
cat /sys/kernel/debug/ci_hdrc.0/registers
......
......@@ -434,7 +434,7 @@ On host: serialc -v <vendorID> -p <productID> -i<interface#> -a1 -s1024 \
where seriald and serialc are Felipe's utilities found here:
https://git.gitorious.org/usb/usb-tools.git master
https://github.com/felipebalbi/usb-tools.git master
12. PHONET function
===================
......@@ -579,6 +579,8 @@ The SOURCESINK function provides these attributes in its function directory:
isoc_mult - 0..2 (hs/ss only)
isoc_maxburst - 0..15 (ss only)
bulk_buflen - buffer length
bulk_qlen - depth of queue for bulk
iso_qlen - depth of queue for iso
Testing the SOURCESINK function
-------------------------------
......
......@@ -537,17 +537,18 @@ relevant attribute files are usb2_hardware_lpm and usb3_hardware_lpm.
can write y/Y/1 or n/N/0 to the file to enable/disable
USB2 hardware LPM manually. This is for test purpose mainly.
power/usb3_hardware_lpm
power/usb3_hardware_lpm_u1
power/usb3_hardware_lpm_u2
When a USB 3.0 lpm-capable device is plugged in to a
xHCI host which supports link PM, it will check if U1
and U2 exit latencies have been set in the BOS
descriptor; if the check is is passed and the host
supports USB3 hardware LPM, USB3 hardware LPM will be
enabled for the device and this file will be created.
The file holds a string value (enable or disable)
indicating whether or not USB3 hardware LPM is
enabled for the device.
enabled for the device and these files will be created.
The files hold a string value (enable or disable)
indicating whether or not USB3 hardware LPM U1 or U2
is enabled for the device.
USB Port Power Control
----------------------
......
......@@ -2767,7 +2767,7 @@ S: Maintained
F: Documentation/zh_CN/
CHIPIDEA USB HIGH SPEED DUAL ROLE CONTROLLER
M: Peter Chen <Peter.Chen@freescale.com>
M: Peter Chen <Peter.Chen@nxp.com>
T: git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git
L: linux-usb@vger.kernel.org
S: Maintained
......@@ -8992,6 +8992,12 @@ L: linux-sh@vger.kernel.org
F: drivers/net/ethernet/renesas/
F: include/linux/sh_eth.h
RENESAS USB2 PHY DRIVER
M: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
L: linux-sh@vger.kernel.org
S: Maintained
F: drivers/phy/phy-rcar-gen3-usb2.c
RESET CONTROLLER FRAMEWORK
M: Philipp Zabel <p.zabel@pengutronix.de>
S: Maintained
......@@ -11188,7 +11194,7 @@ F: Documentation/usb/ohci.txt
F: drivers/usb/host/ohci*
USB OTG FSM (Finite State Machine)
M: Peter Chen <Peter.Chen@freescale.com>
M: Peter Chen <Peter.Chen@nxp.com>
T: git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git
L: linux-usb@vger.kernel.org
S: Maintained
......
......@@ -13,6 +13,7 @@
*/
/dts-v1/;
#include <dt-bindings/gpio/gpio.h>
#include "mt8173.dtsi"
/ {
......@@ -32,6 +33,15 @@ memory@40000000 {
};
chosen { };
usb_p1_vbus: regulator@0 {
compatible = "regulator-fixed";
regulator-name = "usb_vbus";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
gpio = <&pio 130 GPIO_ACTIVE_HIGH>;
enable-active-high;
};
};
&i2c1 {
......@@ -408,3 +418,9 @@ &spi {
&uart0 {
status = "okay";
};
&usb30 {
vusb33-supply = <&mt6397_vusb_reg>;
vbus-supply = <&usb_p1_vbus>;
mediatek,wakeup-src = <1>;
};
......@@ -14,6 +14,7 @@
#include <dt-bindings/clock/mt8173-clk.h>
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/phy/phy.h>
#include <dt-bindings/power/mt8173-power.h>
#include <dt-bindings/reset-controller/mt8173-resets.h>
#include "mt8173-pinfunc.h"
......@@ -510,6 +511,47 @@ mmc3: mmc@11260000 {
status = "disabled";
};
usb30: usb@11270000 {
compatible = "mediatek,mt8173-xhci";
reg = <0 0x11270000 0 0x1000>,
<0 0x11280700 0 0x0100>;
interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_LOW>;
power-domains = <&scpsys MT8173_POWER_DOMAIN_USB>;
clocks = <&topckgen CLK_TOP_USB30_SEL>,
<&pericfg CLK_PERI_USB0>,
<&pericfg CLK_PERI_USB1>;
clock-names = "sys_ck",
"wakeup_deb_p0",
"wakeup_deb_p1";
phys = <&phy_port0 PHY_TYPE_USB3>,
<&phy_port1 PHY_TYPE_USB2>;
mediatek,syscon-wakeup = <&pericfg>;
status = "okay";
};
u3phy: usb-phy@11290000 {
compatible = "mediatek,mt8173-u3phy";
reg = <0 0x11290000 0 0x800>;
clocks = <&apmixedsys CLK_APMIXED_REF2USB_TX>;
clock-names = "u3phya_ref";
#address-cells = <2>;
#size-cells = <2>;
ranges;
status = "okay";
phy_port0: port@11290800 {
reg = <0 0x11290800 0 0x800>;
#phy-cells = <1>;
status = "okay";
};
phy_port1: port@11291000 {
reg = <0 0x11291000 0 0x800>;
#phy-cells = <1>;
status = "okay";
};
};
mmsys: clock-controller@14000000 {
compatible = "mediatek,mt8173-mmsys", "syscon";
reg = <0 0x14000000 0 0x1000>;
......
......@@ -118,6 +118,13 @@ config PHY_RCAR_GEN2
help
Support for USB PHY found on Renesas R-Car generation 2 SoCs.
config PHY_RCAR_GEN3_USB2
tristate "Renesas R-Car generation 3 USB 2.0 PHY driver"
depends on OF && ARCH_SHMOBILE
select GENERIC_PHY
help
Support for USB 2.0 PHY found on Renesas R-Car generation 3 SoCs.
config OMAP_CONTROL_PHY
tristate "OMAP CONTROL PHY Driver"
depends on ARCH_OMAP2PLUS || COMPILE_TEST
......@@ -215,6 +222,15 @@ config PHY_MT65XX_USB3
for mt65xx SoCs. it supports two usb2.0 ports and
one usb3.0 port.
config PHY_HI6220_USB
tristate "hi6220 USB PHY support"
select GENERIC_PHY
select MFD_SYSCON
help
Enable this to support the HISILICON HI6220 USB PHY.
To compile this driver as a module, choose M here.
config PHY_SUN4I_USB
tristate "Allwinner sunxi SoC USB PHY driver"
depends on ARCH_SUNXI && HAS_IOMEM && OF
......@@ -374,11 +390,11 @@ config PHY_TUSB1210
config PHY_BRCMSTB_SATA
tristate "Broadcom STB SATA PHY driver"
depends on ARCH_BRCMSTB
depends on ARCH_BRCMSTB || BMIPS_GENERIC
depends on OF
select GENERIC_PHY
help
Enable this to support the SATA3 PHY on 28nm Broadcom STB SoCs.
Enable this to support the SATA3 PHY on 28nm or 40nm Broadcom STB SoCs.
Likely useful only with CONFIG_SATA_BRCMSTB enabled.
config PHY_CYGNUS_PCIE
......
......@@ -17,12 +17,14 @@ obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o
obj-$(CONFIG_PHY_MIPHY28LP) += phy-miphy28lp.o
obj-$(CONFIG_PHY_MIPHY365X) += phy-miphy365x.o
obj-$(CONFIG_PHY_RCAR_GEN2) += phy-rcar-gen2.o
obj-$(CONFIG_PHY_RCAR_GEN3_USB2) += phy-rcar-gen3-usb2.o
obj-$(CONFIG_OMAP_CONTROL_PHY) += phy-omap-control.o
obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o
obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
obj-$(CONFIG_PHY_EXYNOS5250_SATA) += phy-exynos5250-sata.o
obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o
obj-$(CONFIG_PHY_HI6220_USB) += phy-hi6220-usb.o
obj-$(CONFIG_PHY_MT65XX_USB3) += phy-mt65xx-usb3.o
obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o
obj-$(CONFIG_PHY_SUN9I_USB) += phy-sun9i-usb.o
......
......@@ -9,11 +9,9 @@
* warranty of any kind, whether express or implied.
*/
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
......@@ -195,7 +193,6 @@ static int phy_berlin_usb_probe(struct platform_device *pdev)
return PTR_ERR(phy);
}
platform_set_drvdata(pdev, priv);
phy_set_drvdata(phy, priv);
phy_provider =
......
......@@ -26,13 +26,21 @@
#define SATA_MDIO_BANK_OFFSET 0x23c
#define SATA_MDIO_REG_OFFSET(ofs) ((ofs) * 4)
#define SATA_MDIO_REG_SPACE_SIZE 0x1000
#define SATA_MDIO_REG_LENGTH 0x1f00
#define MAX_PORTS 2
/* Register offset between PHYs in PCB space */
#define SATA_MDIO_REG_SPACE_SIZE 0x1000
#define SATA_MDIO_REG_28NM_SPACE_SIZE 0x1000
/* The older SATA PHY registers duplicated per port registers within the map,
* rather than having a separate map per port.
*/
#define SATA_MDIO_REG_40NM_SPACE_SIZE 0x10
enum brcm_sata_phy_version {
BRCM_SATA_PHY_28NM,
BRCM_SATA_PHY_40NM,
};
struct brcm_sata_port {
int portnum;
......@@ -44,11 +52,12 @@ struct brcm_sata_port {
struct brcm_sata_phy {
struct device *dev;
void __iomem *phy_base;
enum brcm_sata_phy_version version;
struct brcm_sata_port phys[MAX_PORTS];
};
enum sata_mdio_phy_regs_28nm {
enum sata_mdio_phy_regs {
PLL_REG_BANK_0 = 0x50,
PLL_REG_BANK_0_PLLCONTROL_0 = 0x81,
......@@ -66,8 +75,16 @@ enum sata_mdio_phy_regs_28nm {
static inline void __iomem *brcm_sata_phy_base(struct brcm_sata_port *port)
{
struct brcm_sata_phy *priv = port->phy_priv;
u32 offset = 0;
if (priv->version == BRCM_SATA_PHY_28NM)
offset = SATA_MDIO_REG_28NM_SPACE_SIZE;
else if (priv->version == BRCM_SATA_PHY_40NM)
offset = SATA_MDIO_REG_40NM_SPACE_SIZE;
else
dev_err(priv->dev, "invalid phy version\n");
return priv->phy_base + (port->portnum * SATA_MDIO_REG_SPACE_SIZE);
return priv->phy_base + (port->portnum * offset);
}
static void brcm_sata_mdio_wr(void __iomem *addr, u32 bank, u32 ofs,
......@@ -86,7 +103,7 @@ static void brcm_sata_mdio_wr(void __iomem *addr, u32 bank, u32 ofs,
#define FMAX_VAL_DEFAULT 0x3df
#define FMAX_VAL_SSC 0x83
static void brcm_sata_cfg_ssc_28nm(struct brcm_sata_port *port)
static void brcm_sata_cfg_ssc(struct brcm_sata_port *port)
{
void __iomem *base = brcm_sata_phy_base(port);
struct brcm_sata_phy *priv = port->phy_priv;
......@@ -117,18 +134,21 @@ static int brcm_sata_phy_init(struct phy *phy)
{
struct brcm_sata_port *port = phy_get_drvdata(phy);
brcm_sata_cfg_ssc_28nm(port);
brcm_sata_cfg_ssc(port);
return 0;
}
static const struct phy_ops phy_ops_28nm = {
static const struct phy_ops phy_ops = {
.init = brcm_sata_phy_init,
.owner = THIS_MODULE,
};
static const struct of_device_id brcm_sata_phy_of_match[] = {
{ .compatible = "brcm,bcm7445-sata-phy" },
{ .compatible = "brcm,bcm7445-sata-phy",
.data = (void *)BRCM_SATA_PHY_28NM },
{ .compatible = "brcm,bcm7425-sata-phy",
.data = (void *)BRCM_SATA_PHY_40NM },
{},
};
MODULE_DEVICE_TABLE(of, brcm_sata_phy_of_match);
......@@ -137,6 +157,7 @@ static int brcm_sata_phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *dn = dev->of_node, *child;
const struct of_device_id *of_id;
struct brcm_sata_phy *priv;
struct resource *res;
struct phy_provider *provider;
......@@ -156,6 +177,12 @@ static int brcm_sata_phy_probe(struct platform_device *pdev)
if (IS_ERR(priv->phy_base))
return PTR_ERR(priv->phy_base);
of_id = of_match_node(brcm_sata_phy_of_match, dn);
if (of_id)
priv->version = (enum brcm_sata_phy_version)of_id->data;
else
priv->version = BRCM_SATA_PHY_28NM;
for_each_available_child_of_node(dn, child) {
unsigned int id;
struct brcm_sata_port *port;
......@@ -181,7 +208,7 @@ static int brcm_sata_phy_probe(struct platform_device *pdev)
port = &priv->phys[id];
port->portnum = id;
port->phy_priv = priv;
port->phy = devm_phy_create(dev, child, &phy_ops_28nm);
port->phy = devm_phy_create(dev, child, &phy_ops);
port->ssc_en = of_property_read_bool(child, "brcm,enable-ssc");
if (IS_ERR(port->phy)) {
dev_err(dev, "failed to create PHY\n");
......
/*
* Copyright (c) 2015 Linaro Ltd.
* Copyright (c) 2015 Hisilicon Limited.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/phy/phy.h>
#include <linux/regmap.h>
#define SC_PERIPH_CTRL4 0x00c
#define CTRL4_PICO_SIDDQ BIT(6)
#define CTRL4_PICO_OGDISABLE BIT(8)
#define CTRL4_PICO_VBUSVLDEXT BIT(10)
#define CTRL4_PICO_VBUSVLDEXTSEL BIT(11)
#define CTRL4_OTG_PHY_SEL BIT(21)
#define SC_PERIPH_CTRL5 0x010
#define CTRL5_USBOTG_RES_SEL BIT(3)
#define CTRL5_PICOPHY_ACAENB BIT(4)
#define CTRL5_PICOPHY_BC_MODE BIT(5)
#define CTRL5_PICOPHY_CHRGSEL BIT(6)
#define CTRL5_PICOPHY_VDATSRCEND BIT(7)
#define CTRL5_PICOPHY_VDATDETENB BIT(8)
#define CTRL5_PICOPHY_DCDENB BIT(9)
#define CTRL5_PICOPHY_IDDIG BIT(10)
#define SC_PERIPH_CTRL8 0x018
#define SC_PERIPH_RSTEN0 0x300
#define SC_PERIPH_RSTDIS0 0x304
#define RST0_USBOTG_BUS BIT(4)
#define RST0_POR_PICOPHY BIT(5)
#define RST0_USBOTG BIT(6)
#define RST0_USBOTG_32K BIT(7)
#define EYE_PATTERN_PARA 0x7053348c
struct hi6220_priv {
struct regmap *reg;
struct device *dev;
};
static void hi6220_phy_init(struct hi6220_priv *priv)
{
struct regmap *reg = priv->reg;
u32 val, mask;
val = RST0_USBOTG_BUS | RST0_POR_PICOPHY |
RST0_USBOTG | RST0_USBOTG_32K;
mask = val;
regmap_update_bits(reg, SC_PERIPH_RSTEN0, mask, val);
regmap_update_bits(reg, SC_PERIPH_RSTDIS0, mask, val);
}
static int hi6220_phy_setup(struct hi6220_priv *priv, bool on)
{
struct regmap *reg = priv->reg;
u32 val, mask;
int ret;
if (on) {
val = CTRL5_USBOTG_RES_SEL | CTRL5_PICOPHY_ACAENB;
mask = val | CTRL5_PICOPHY_BC_MODE;
ret = regmap_update_bits(reg, SC_PERIPH_CTRL5, mask, val);
if (ret)
goto out;
val = CTRL4_PICO_VBUSVLDEXT | CTRL4_PICO_VBUSVLDEXTSEL |
CTRL4_OTG_PHY_SEL;
mask = val | CTRL4_PICO_SIDDQ | CTRL4_PICO_OGDISABLE;
ret = regmap_update_bits(reg, SC_PERIPH_CTRL4, mask, val);
if (ret)
goto out;
ret = regmap_write(reg, SC_PERIPH_CTRL8, EYE_PATTERN_PARA);
if (ret)
goto out;
} else {
val = CTRL4_PICO_SIDDQ;
mask = val;
ret = regmap_update_bits(reg, SC_PERIPH_CTRL4, mask, val);
if (ret)
goto out;
}
return 0;
out:
dev_err(priv->dev, "failed to setup phy ret: %d\n", ret);
return ret;
}
static int hi6220_phy_start(struct phy *phy)
{
struct hi6220_priv *priv = phy_get_drvdata(phy);
return hi6220_phy_setup(priv, true);
}
static int hi6220_phy_exit(struct phy *phy)
{
struct hi6220_priv *priv = phy_get_drvdata(phy);
return hi6220_phy_setup(priv, false);
}
static struct phy_ops hi6220_phy_ops = {
.init = hi6220_phy_start,
.exit = hi6220_phy_exit,
.owner = THIS_MODULE,
};
static int hi6220_phy_probe(struct platform_device *pdev)
{
struct phy_provider *phy_provider;
struct device *dev = &pdev->dev;
struct phy *phy;
struct hi6220_priv *priv;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = dev;
priv->reg = syscon_regmap_lookup_by_phandle(dev->of_node,
"hisilicon,peripheral-syscon");
if (IS_ERR(priv->reg)) {
dev_err(dev, "no hisilicon,peripheral-syscon\n");
return PTR_ERR(priv->reg);
}
hi6220_phy_init(priv);
phy = devm_phy_create(dev, NULL, &hi6220_phy_ops);
if (IS_ERR(phy))
return PTR_ERR(phy);
phy_set_drvdata(phy, priv);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
}
static const struct of_device_id hi6220_phy_of_match[] = {
{.compatible = "hisilicon,hi6220-usb-phy",},
{ },
};
MODULE_DEVICE_TABLE(of, hi6220_phy_of_match);
static struct platform_driver hi6220_phy_driver = {
.probe = hi6220_phy_probe,
.driver = {
.name = "hi6220-usb-phy",
.of_match_table = hi6220_phy_of_match,
}
};
module_platform_driver(hi6220_phy_driver);
MODULE_DESCRIPTION("HISILICON HI6220 USB PHY driver");
MODULE_ALIAS("platform:hi6220-usb-phy");
MODULE_LICENSE("GPL");
......@@ -17,6 +17,7 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/phy/phy.h>
......@@ -27,6 +28,7 @@
* relative to USB3_SIF2_BASE base address
*/
#define SSUSB_SIFSLV_SPLLC 0x0000
#define SSUSB_SIFSLV_U2FREQ 0x0100
/* offsets of sub-segment in each port registers */
#define SSUSB_SIFSLV_U2PHY_COM_BASE 0x0000
......@@ -41,6 +43,7 @@
#define PA2_RG_SIF_U2PLL_FORCE_EN BIT(18)
#define U3P_USBPHYACR5 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0014)
#define PA5_RG_U2_HSTX_SRCAL_EN BIT(15)
#define PA5_RG_U2_HSTX_SRCTRL GENMASK(14, 12)
#define PA5_RG_U2_HSTX_SRCTRL_VAL(x) ((0x7 & (x)) << 12)
#define PA5_RG_U2_HS_100U_U3_EN BIT(11)
......@@ -49,6 +52,8 @@
#define PA6_RG_U2_ISO_EN BIT(31)
#define PA6_RG_U2_BC11_SW_EN BIT(23)
#define PA6_RG_U2_OTG_VBUSCMP_EN BIT(20)
#define PA6_RG_U2_SQTH GENMASK(3, 0)
#define PA6_RG_U2_SQTH_VAL(x) (0xf & (x))
#define U3P_U2PHYACR4 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0020)
#define P2C_RG_USB20_GPIO_CTL BIT(9)
......@@ -111,6 +116,24 @@
#define XC3_RG_U3_XTAL_RX_PWD BIT(9)
#define XC3_RG_U3_FRC_XTAL_RX_PWD BIT(8)
#define U3P_U2FREQ_FMCR0 (SSUSB_SIFSLV_U2FREQ + 0x00)
#define P2F_RG_MONCLK_SEL GENMASK(27, 26)
#define P2F_RG_MONCLK_SEL_VAL(x) ((0x3 & (x)) << 26)
#define P2F_RG_FREQDET_EN BIT(24)
#define P2F_RG_CYCLECNT GENMASK(23, 0)
#define P2F_RG_CYCLECNT_VAL(x) ((P2F_RG_CYCLECNT) & (x))
#define U3P_U2FREQ_VALUE (SSUSB_SIFSLV_U2FREQ + 0x0c)
#define U3P_U2FREQ_FMMONR1 (SSUSB_SIFSLV_U2FREQ + 0x10)
#define P2F_USB_FM_VALID BIT(0)
#define P2F_RG_FRCK_EN BIT(8)
#define U3P_REF_CLK 26 /* MHZ */
#define U3P_SLEW_RATE_COEF 28
#define U3P_SR_COEF_DIVISOR 1000
#define U3P_FM_DET_CYCLE_CNT 1024
struct mt65xx_phy_instance {
struct phy *phy;
void __iomem *port_base;
......@@ -126,6 +149,77 @@ struct mt65xx_u3phy {
int nphys;
};
static void hs_slew_rate_calibrate(struct mt65xx_u3phy *u3phy,
struct mt65xx_phy_instance *instance)
{
void __iomem *sif_base = u3phy->sif_base;
int calibration_val;
int fm_out;
u32 tmp;
/* enable USB ring oscillator */
tmp = readl(instance->port_base + U3P_USBPHYACR5);
tmp |= PA5_RG_U2_HSTX_SRCAL_EN;
writel(tmp, instance->port_base + U3P_USBPHYACR5);
udelay(1);
/*enable free run clock */
tmp = readl(sif_base + U3P_U2FREQ_FMMONR1);
tmp |= P2F_RG_FRCK_EN;
writel(tmp, sif_base + U3P_U2FREQ_FMMONR1);
/* set cycle count as 1024, and select u2 channel */
tmp = readl(sif_base + U3P_U2FREQ_FMCR0);
tmp &= ~(P2F_RG_CYCLECNT | P2F_RG_MONCLK_SEL);
tmp |= P2F_RG_CYCLECNT_VAL(U3P_FM_DET_CYCLE_CNT);
tmp |= P2F_RG_MONCLK_SEL_VAL(instance->index);
writel(tmp, sif_base + U3P_U2FREQ_FMCR0);
/* enable frequency meter */
tmp = readl(sif_base + U3P_U2FREQ_FMCR0);
tmp |= P2F_RG_FREQDET_EN;
writel(tmp, sif_base + U3P_U2FREQ_FMCR0);
/* ignore return value */
readl_poll_timeout(sif_base + U3P_U2FREQ_FMMONR1, tmp,
(tmp & P2F_USB_FM_VALID), 10, 200);
fm_out = readl(sif_base + U3P_U2FREQ_VALUE);
/* disable frequency meter */
tmp = readl(sif_base + U3P_U2FREQ_FMCR0);
tmp &= ~P2F_RG_FREQDET_EN;
writel(tmp, sif_base + U3P_U2FREQ_FMCR0);
/*disable free run clock */
tmp = readl(sif_base + U3P_U2FREQ_FMMONR1);
tmp &= ~P2F_RG_FRCK_EN;
writel(tmp, sif_base + U3P_U2FREQ_FMMONR1);
if (fm_out) {
/* ( 1024 / FM_OUT ) x reference clock frequency x 0.028 */
tmp = U3P_FM_DET_CYCLE_CNT * U3P_REF_CLK * U3P_SLEW_RATE_COEF;
tmp /= fm_out;
calibration_val = DIV_ROUND_CLOSEST(tmp, U3P_SR_COEF_DIVISOR);
} else {
/* if FM detection fail, set default value */
calibration_val = 4;
}
dev_dbg(u3phy->dev, "phy:%d, fm_out:%d, calib:%d\n",
instance->index, fm_out, calibration_val);
/* set HS slew rate */
tmp = readl(instance->port_base + U3P_USBPHYACR5);
tmp &= ~PA5_RG_U2_HSTX_SRCTRL;
tmp |= PA5_RG_U2_HSTX_SRCTRL_VAL(calibration_val);
writel(tmp, instance->port_base + U3P_USBPHYACR5);
/* disable USB ring oscillator */
tmp = readl(instance->port_base + U3P_USBPHYACR5);
tmp &= ~PA5_RG_U2_HSTX_SRCAL_EN;
writel(tmp, instance->port_base + U3P_USBPHYACR5);
}
static void phy_instance_init(struct mt65xx_u3phy *u3phy,
struct mt65xx_phy_instance *instance)
{
......@@ -165,9 +259,10 @@ static void phy_instance_init(struct mt65xx_u3phy *u3phy,
writel(tmp, port_base + U3P_U2PHYDTM0);
}
/* DP/DM BC1.1 path Disable */
tmp = readl(port_base + U3P_USBPHYACR6);
tmp &= ~PA6_RG_U2_BC11_SW_EN;
tmp &= ~PA6_RG_U2_BC11_SW_EN; /* DP/DM BC1.1 path Disable */
tmp &= ~PA6_RG_U2_SQTH;
tmp |= PA6_RG_U2_SQTH_VAL(2);
writel(tmp, port_base + U3P_USBPHYACR6);
tmp = readl(port_base + U3P_U3PHYA_DA_REG0);
......@@ -223,9 +318,9 @@ static void phy_instance_power_on(struct mt65xx_u3phy *u3phy,
tmp |= XC3_RG_U3_XTAL_RX_PWD | XC3_RG_U3_FRC_XTAL_RX_PWD;
writel(tmp, u3phy->sif_base + U3P_XTALCTL3);
/* [mt8173]disable Change 100uA current from SSUSB */
/* [mt8173]switch 100uA current to SSUSB */
tmp = readl(port_base + U3P_USBPHYACR5);
tmp &= ~PA5_RG_U2_HS_100U_U3_EN;
tmp |= PA5_RG_U2_HS_100U_U3_EN;
writel(tmp, port_base + U3P_USBPHYACR5);
}
......@@ -270,7 +365,7 @@ static void phy_instance_power_off(struct mt65xx_u3phy *u3phy,
writel(tmp, port_base + U3P_USBPHYACR6);
if (!index) {
/* (also disable)Change 100uA current switch to USB2.0 */
/* switch 100uA current back to USB2.0 */
tmp = readl(port_base + U3P_USBPHYACR5);
tmp &= ~PA5_RG_U2_HS_100U_U3_EN;
writel(tmp, port_base + U3P_USBPHYACR5);
......@@ -340,6 +435,7 @@ static int mt65xx_phy_power_on(struct phy *phy)
struct mt65xx_u3phy *u3phy = dev_get_drvdata(phy->dev.parent);
phy_instance_power_on(u3phy, instance);
hs_slew_rate_calibrate(u3phy, instance);
return 0;
}
......
......@@ -29,6 +29,8 @@
#include <linux/delay.h>
#include <linux/phy/omap_control_phy.h>
#include <linux/phy/phy.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/of_platform.h>
#define USB2PHY_DISCON_BYP_LATCH (1 << 31)
......@@ -97,22 +99,38 @@ static int omap_usb_set_peripheral(struct usb_otg *otg,
return 0;
}
static int omap_usb_phy_power(struct omap_usb *phy, int on)
{
u32 val;
int ret;
if (!phy->syscon_phy_power) {
omap_control_phy_power(phy->control_dev, on);
return 0;
}
if (on)
val = phy->power_on;
else
val = phy->power_off;
ret = regmap_update_bits(phy->syscon_phy_power, phy->power_reg,
phy->mask, val);
return ret;
}
static int omap_usb_power_off(struct phy *x)
{
struct omap_usb *phy = phy_get_drvdata(x);
omap_control_phy_power(phy->control_dev, 0);
return 0;
return omap_usb_phy_power(phy, false);
}
static int omap_usb_power_on(struct phy *x)
{
struct omap_usb *phy = phy_get_drvdata(x);
omap_control_phy_power(phy->control_dev, 1);
return 0;
return omap_usb_phy_power(phy, true);
}
static int omap_usb_init(struct phy *x)
......@@ -147,21 +165,38 @@ static const struct phy_ops ops = {
static const struct usb_phy_data omap_usb2_data = {
.label = "omap_usb2",
.flags = OMAP_USB2_HAS_START_SRP | OMAP_USB2_HAS_SET_VBUS,
.mask = OMAP_DEV_PHY_PD,
.power_off = OMAP_DEV_PHY_PD,
};
static const struct usb_phy_data omap5_usb2_data = {
.label = "omap5_usb2",
.flags = 0,
.mask = OMAP_DEV_PHY_PD,
.power_off = OMAP_DEV_PHY_PD,
};
static const struct usb_phy_data dra7x_usb2_data = {
.label = "dra7x_usb2",
.flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT,
.mask = OMAP_DEV_PHY_PD,
.power_off = OMAP_DEV_PHY_PD,
};
static const struct usb_phy_data dra7x_usb2_phy2_data = {
.label = "dra7x_usb2_phy2",
.flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT,
.mask = OMAP_USB2_PHY_PD,
.power_off = OMAP_USB2_PHY_PD,
};
static const struct usb_phy_data am437x_usb2_data = {
.label = "am437x_usb2",
.flags = 0,
.mask = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD |
AM437X_USB2_OTGVDET_EN | AM437X_USB2_OTGSESSEND_EN,
.power_on = AM437X_USB2_OTGVDET_EN | AM437X_USB2_OTGSESSEND_EN,
.power_off = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD,
};
static const struct of_device_id omap_usb2_id_table[] = {
......@@ -177,6 +212,10 @@ static const struct of_device_id omap_usb2_id_table[] = {
.compatible = "ti,dra7x-usb2",
.data = &dra7x_usb2_data,
},
{
.compatible = "ti,dra7x-usb2-phy2",
.data = &dra7x_usb2_phy2_data,
},
{
.compatible = "ti,am437x-usb2",
.data = &am437x_usb2_data,
......@@ -219,6 +258,9 @@ static int omap_usb2_probe(struct platform_device *pdev)
phy->phy.label = phy_data->label;
phy->phy.otg = otg;
phy->phy.type = USB_PHY_TYPE_USB2;
phy->mask = phy_data->mask;
phy->power_on = phy_data->power_on;
phy->power_off = phy_data->power_off;
if (phy_data->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) {
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
......@@ -228,21 +270,36 @@ static int omap_usb2_probe(struct platform_device *pdev)
phy->flags |= OMAP_USB2_CALIBRATE_FALSE_DISCONNECT;
}
control_node = of_parse_phandle(node, "ctrl-module", 0);
if (!control_node) {
dev_err(&pdev->dev, "Failed to get control device phandle\n");
return -EINVAL;
}
phy->syscon_phy_power = syscon_regmap_lookup_by_phandle(node,
"syscon-phy-power");
if (IS_ERR(phy->syscon_phy_power)) {
dev_dbg(&pdev->dev,
"can't get syscon-phy-power, using control device\n");
phy->syscon_phy_power = NULL;
control_node = of_parse_phandle(node, "ctrl-module", 0);
if (!control_node) {
dev_err(&pdev->dev,
"Failed to get control device phandle\n");
return -EINVAL;
}
control_pdev = of_find_device_by_node(control_node);
if (!control_pdev) {
dev_err(&pdev->dev, "Failed to get control device\n");
return -EINVAL;
control_pdev = of_find_device_by_node(control_node);
if (!control_pdev) {
dev_err(&pdev->dev, "Failed to get control device\n");
return -EINVAL;
}
phy->control_dev = &control_pdev->dev;
} else {
if (of_property_read_u32_index(node,
"syscon-phy-power", 1,
&phy->power_reg)) {
dev_err(&pdev->dev,
"couldn't get power reg. offset\n");
return -EINVAL;
}
}
phy->control_dev = &control_pdev->dev;
omap_control_phy_power(phy->control_dev, 0);
otg->set_host = omap_usb_set_host;
otg->set_peripheral = omap_usb_set_peripheral;
if (phy_data->flags & OMAP_USB2_HAS_SET_VBUS)
......@@ -261,6 +318,7 @@ static int omap_usb2_probe(struct platform_device *pdev)
}
phy_set_drvdata(generic_phy, phy);
omap_usb_power_off(generic_phy);
phy_provider = devm_of_phy_provider_register(phy->dev,
of_phy_simple_xlate);
......
This diff is collapsed.
......@@ -15,12 +15,14 @@
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
......@@ -36,120 +38,281 @@
#define SIDDQ_ON BIT(13)
#define SIDDQ_OFF (0 << 13)
struct rockchip_usb_phys {
int reg;
const char *pll_name;
};
struct rockchip_usb_phy_pdata {
struct rockchip_usb_phys *phys;
};
struct rockchip_usb_phy_base {
struct device *dev;
struct regmap *reg_base;
const struct rockchip_usb_phy_pdata *pdata;
};
struct rockchip_usb_phy {
struct rockchip_usb_phy_base *base;
struct device_node *np;
unsigned int reg_offset;
struct regmap *reg_base;
struct clk *clk;
struct clk *clk480m;
struct clk_hw clk480m_hw;
struct phy *phy;
};
static int rockchip_usb_phy_power(struct rockchip_usb_phy *phy,
bool siddq)
{
return regmap_write(phy->reg_base, phy->reg_offset,
return regmap_write(phy->base->reg_base, phy->reg_offset,
SIDDQ_WRITE_ENA | (siddq ? SIDDQ_ON : SIDDQ_OFF));
}
static int rockchip_usb_phy_power_off(struct phy *_phy)
static unsigned long rockchip_usb_phy480m_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
int ret = 0;
return 480000000;
}
static void rockchip_usb_phy480m_disable(struct clk_hw *hw)
{
struct rockchip_usb_phy *phy = container_of(hw,
struct rockchip_usb_phy,
clk480m_hw);
/* Power down usb phy analog blocks by set siddq 1 */
ret = rockchip_usb_phy_power(phy, 1);
if (ret)
return ret;
rockchip_usb_phy_power(phy, 1);
}
clk_disable_unprepare(phy->clk);
static int rockchip_usb_phy480m_enable(struct clk_hw *hw)
{
struct rockchip_usb_phy *phy = container_of(hw,
struct rockchip_usb_phy,
clk480m_hw);
return 0;
/* Power up usb phy analog blocks by set siddq 0 */
return rockchip_usb_phy_power(phy, 0);
}
static int rockchip_usb_phy_power_on(struct phy *_phy)
static int rockchip_usb_phy480m_is_enabled(struct clk_hw *hw)
{
struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
int ret = 0;
struct rockchip_usb_phy *phy = container_of(hw,
struct rockchip_usb_phy,
clk480m_hw);
int ret;
u32 val;
ret = clk_prepare_enable(phy->clk);
if (ret)
ret = regmap_read(phy->base->reg_base, phy->reg_offset, &val);
if (ret < 0)
return ret;
/* Power up usb phy analog blocks by set siddq 0 */
ret = rockchip_usb_phy_power(phy, 0);
if (ret) {
clk_disable_unprepare(phy->clk);
return ret;
}
return (val & SIDDQ_ON) ? 0 : 1;
}
static const struct clk_ops rockchip_usb_phy480m_ops = {
.enable = rockchip_usb_phy480m_enable,
.disable = rockchip_usb_phy480m_disable,
.is_enabled = rockchip_usb_phy480m_is_enabled,
.recalc_rate = rockchip_usb_phy480m_recalc_rate,
};
static int rockchip_usb_phy_power_off(struct phy *_phy)
{
struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
clk_disable_unprepare(phy->clk480m);
return 0;
}
static int rockchip_usb_phy_power_on(struct phy *_phy)
{
struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
return clk_prepare_enable(phy->clk480m);
}
static const struct phy_ops ops = {
.power_on = rockchip_usb_phy_power_on,
.power_off = rockchip_usb_phy_power_off,
.owner = THIS_MODULE,
};
static int rockchip_usb_phy_probe(struct platform_device *pdev)
static void rockchip_usb_phy_action(void *data)
{
struct rockchip_usb_phy *rk_phy = data;
of_clk_del_provider(rk_phy->np);
clk_unregister(rk_phy->clk480m);
if (rk_phy->clk)
clk_put(rk_phy->clk);
}
static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base,
struct device_node *child)
{
struct device *dev = &pdev->dev;
struct rockchip_usb_phy *rk_phy;
struct phy_provider *phy_provider;
struct device_node *child;
struct regmap *grf;
unsigned int reg_offset;
int err;
const char *clk_name;
struct clk_init_data init;
int err, i;
grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
if (IS_ERR(grf)) {
dev_err(&pdev->dev, "Missing rockchip,grf property\n");
return PTR_ERR(grf);
rk_phy = devm_kzalloc(base->dev, sizeof(*rk_phy), GFP_KERNEL);
if (!rk_phy)
return -ENOMEM;
rk_phy->base = base;
rk_phy->np = child;
if (of_property_read_u32(child, "reg", &reg_offset)) {
dev_err(base->dev, "missing reg property in node %s\n",
child->name);
return -EINVAL;
}
for_each_available_child_of_node(dev->of_node, child) {
rk_phy = devm_kzalloc(dev, sizeof(*rk_phy), GFP_KERNEL);
if (!rk_phy) {
err = -ENOMEM;
goto put_child;
}
rk_phy->reg_offset = reg_offset;
rk_phy->clk = of_clk_get_by_name(child, "phyclk");
if (IS_ERR(rk_phy->clk))
rk_phy->clk = NULL;
if (of_property_read_u32(child, "reg", &reg_offset)) {
dev_err(dev, "missing reg property in node %s\n",
child->name);
err = -EINVAL;
goto put_child;
i = 0;
init.name = NULL;
while (base->pdata->phys[i].reg) {
if (base->pdata->phys[i].reg == reg_offset) {
init.name = base->pdata->phys[i].pll_name;
break;
}
i++;
}
rk_phy->reg_offset = reg_offset;
rk_phy->reg_base = grf;
if (!init.name) {
dev_err(base->dev, "phy data not found\n");
return -EINVAL;
}
rk_phy->clk = of_clk_get_by_name(child, "phyclk");
if (IS_ERR(rk_phy->clk))
rk_phy->clk = NULL;
if (rk_phy->clk) {
clk_name = __clk_get_name(rk_phy->clk);
init.flags = 0;
init.parent_names = &clk_name;
init.num_parents = 1;
} else {
init.flags = CLK_IS_ROOT;
init.parent_names = NULL;
init.num_parents = 0;
}
rk_phy->phy = devm_phy_create(dev, child, &ops);
if (IS_ERR(rk_phy->phy)) {
dev_err(dev, "failed to create PHY\n");
err = PTR_ERR(rk_phy->phy);
goto put_child;
}
phy_set_drvdata(rk_phy->phy, rk_phy);
init.ops = &rockchip_usb_phy480m_ops;
rk_phy->clk480m_hw.init = &init;
/* only power up usb phy when it use, so disable it when init*/
err = rockchip_usb_phy_power(rk_phy, 1);
if (err)
goto put_child;
rk_phy->clk480m = clk_register(base->dev, &rk_phy->clk480m_hw);
if (IS_ERR(rk_phy->clk480m)) {
err = PTR_ERR(rk_phy->clk480m);
goto err_clk;
}
err = of_clk_add_provider(child, of_clk_src_simple_get,
rk_phy->clk480m);
if (err < 0)
goto err_clk_prov;
err = devm_add_action(base->dev, rockchip_usb_phy_action, rk_phy);
if (err)
goto err_devm_action;
rk_phy->phy = devm_phy_create(base->dev, child, &ops);
if (IS_ERR(rk_phy->phy)) {
dev_err(base->dev, "failed to create PHY\n");
return PTR_ERR(rk_phy->phy);
}
phy_set_drvdata(rk_phy->phy, rk_phy);
/* only power up usb phy when it use, so disable it when init*/
return rockchip_usb_phy_power(rk_phy, 1);
err_devm_action:
of_clk_del_provider(child);
err_clk_prov:
clk_unregister(rk_phy->clk480m);
err_clk:
if (rk_phy->clk)
clk_put(rk_phy->clk);
return err;
}
static const struct rockchip_usb_phy_pdata rk3066a_pdata = {
.phys = (struct rockchip_usb_phys[]){
{ .reg = 0x17c, .pll_name = "sclk_otgphy0_480m" },
{ .reg = 0x188, .pll_name = "sclk_otgphy1_480m" },
{ /* sentinel */ }
},
};
static const struct rockchip_usb_phy_pdata rk3188_pdata = {
.phys = (struct rockchip_usb_phys[]){
{ .reg = 0x10c, .pll_name = "sclk_otgphy0_480m" },
{ .reg = 0x11c, .pll_name = "sclk_otgphy1_480m" },
{ /* sentinel */ }
},
};
static const struct rockchip_usb_phy_pdata rk3288_pdata = {
.phys = (struct rockchip_usb_phys[]){
{ .reg = 0x320, .pll_name = "sclk_otgphy0_480m" },
{ .reg = 0x334, .pll_name = "sclk_otgphy1_480m" },
{ .reg = 0x348, .pll_name = "sclk_otgphy2_480m" },
{ /* sentinel */ }
},
};
static int rockchip_usb_phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct rockchip_usb_phy_base *phy_base;
struct phy_provider *phy_provider;
const struct of_device_id *match;
struct device_node *child;
int err;
phy_base = devm_kzalloc(dev, sizeof(*phy_base), GFP_KERNEL);
if (!phy_base)
return -ENOMEM;
match = of_match_device(dev->driver->of_match_table, dev);
if (!match || !match->data) {
dev_err(dev, "missing phy data\n");
return -EINVAL;
}
phy_base->pdata = match->data;
phy_base->dev = dev;
phy_base->reg_base = syscon_regmap_lookup_by_phandle(dev->of_node,
"rockchip,grf");
if (IS_ERR(phy_base->reg_base)) {
dev_err(&pdev->dev, "Missing rockchip,grf property\n");
return PTR_ERR(phy_base->reg_base);
}
for_each_available_child_of_node(dev->of_node, child) {
err = rockchip_usb_phy_init(phy_base, child);
if (err) {
of_node_put(child);
return err;
}
}
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
put_child:
of_node_put(child);
return err;
}
static const struct of_device_id rockchip_usb_phy_dt_ids[] = {
{ .compatible = "rockchip,rk3288-usb-phy" },
{ .compatible = "rockchip,rk3066a-usb-phy", .data = &rk3066a_pdata },
{ .compatible = "rockchip,rk3188-usb-phy", .data = &rk3188_pdata },
{ .compatible = "rockchip,rk3288-usb-phy", .data = &rk3288_pdata },
{}
};
......
......@@ -32,6 +32,7 @@
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/phy/phy.h>
#include <linux/phy/phy-sun4i-usb.h>
......@@ -46,6 +47,9 @@
#define REG_PHYBIST 0x08
#define REG_PHYTUNE 0x0c
#define REG_PHYCTL_A33 0x10
#define REG_PHY_UNK_H3 0x20
#define REG_PMU_UNK_H3 0x10
#define PHYCTL_DATA BIT(7)
......@@ -79,7 +83,7 @@
#define PHY_DISCON_TH_SEL 0x2a
#define PHY_SQUELCH_DETECT 0x3c
#define MAX_PHYS 3
#define MAX_PHYS 4
/*
* Note do not raise the debounce time, we must report Vusb high within 100ms
......@@ -88,12 +92,24 @@
#define DEBOUNCE_TIME msecs_to_jiffies(50)
#define POLL_TIME msecs_to_jiffies(250)
enum sun4i_usb_phy_type {
sun4i_a10_phy,
sun8i_a33_phy,
sun8i_h3_phy,
};
struct sun4i_usb_phy_cfg {
int num_phys;
enum sun4i_usb_phy_type type;
u32 disc_thresh;
u8 phyctl_offset;
bool dedicated_clocks;
};
struct sun4i_usb_phy_data {
void __iomem *base;
const struct sun4i_usb_phy_cfg *cfg;
struct mutex mutex;
int num_phys;
u32 disc_thresh;
bool has_a33_phyctl;
struct sun4i_usb_phy {
struct phy *phy;
void __iomem *pmu;
......@@ -159,17 +175,14 @@ static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
{
struct sun4i_usb_phy_data *phy_data = to_sun4i_usb_phy_data(phy);
u32 temp, usbc_bit = BIT(phy->index * 2);
void *phyctl;
void *phyctl = phy_data->base + phy_data->cfg->phyctl_offset;
int i;
mutex_lock(&phy_data->mutex);
if (phy_data->has_a33_phyctl) {
phyctl = phy_data->base + REG_PHYCTL_A33;
if (phy_data->cfg->type == sun8i_a33_phy) {
/* A33 needs us to set phyctl to 0 explicitly */
writel(0, phyctl);
} else {
phyctl = phy_data->base + REG_PHYCTL_A10;
}
for (i = 0; i < len; i++) {
......@@ -230,6 +243,7 @@ static int sun4i_usb_phy_init(struct phy *_phy)
struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
int ret;
u32 val;
ret = clk_prepare_enable(phy->clk);
if (ret)
......@@ -241,15 +255,26 @@ static int sun4i_usb_phy_init(struct phy *_phy)
return ret;
}
/* Enable USB 45 Ohm resistor calibration */
if (phy->index == 0)
sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, 0x01, 1);
if (data->cfg->type == sun8i_h3_phy) {
if (phy->index == 0) {
val = readl(data->base + REG_PHY_UNK_H3);
writel(val & ~1, data->base + REG_PHY_UNK_H3);
}
/* Adjust PHY's magnitude and rate */
sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5);
val = readl(phy->pmu + REG_PMU_UNK_H3);
writel(val & ~2, phy->pmu + REG_PMU_UNK_H3);
} else {
/* Enable USB 45 Ohm resistor calibration */
if (phy->index == 0)
sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, 0x01, 1);
/* Disconnect threshold adjustment */
sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL, data->disc_thresh, 2);
/* Adjust PHY's magnitude and rate */
sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5);
/* Disconnect threshold adjustment */
sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL,
data->cfg->disc_thresh, 2);
}
sun4i_usb_phy_passby(phy, 1);
......@@ -476,7 +501,7 @@ static struct phy *sun4i_usb_phy_xlate(struct device *dev,
{
struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
if (args->args[0] >= data->num_phys)
if (args->args[0] >= data->cfg->num_phys)
return ERR_PTR(-ENODEV);
return data->phys[args->args[0]].phy;
......@@ -511,7 +536,6 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct phy_provider *phy_provider;
bool dedicated_clocks;
struct resource *res;
int i, ret;
......@@ -522,29 +546,9 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
mutex_init(&data->mutex);
INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan);
dev_set_drvdata(dev, data);
if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy") ||
of_device_is_compatible(np, "allwinner,sun8i-a23-usb-phy") ||
of_device_is_compatible(np, "allwinner,sun8i-a33-usb-phy"))
data->num_phys = 2;
else
data->num_phys = 3;
if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy") ||
of_device_is_compatible(np, "allwinner,sun7i-a20-usb-phy"))
data->disc_thresh = 2;
else
data->disc_thresh = 3;
if (of_device_is_compatible(np, "allwinner,sun6i-a31-usb-phy") ||
of_device_is_compatible(np, "allwinner,sun8i-a23-usb-phy") ||
of_device_is_compatible(np, "allwinner,sun8i-a33-usb-phy"))
dedicated_clocks = true;
else
dedicated_clocks = false;
if (of_device_is_compatible(np, "allwinner,sun8i-a33-usb-phy"))
data->has_a33_phyctl = true;
data->cfg = of_device_get_match_data(dev);
if (!data->cfg)
return -EINVAL;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_ctrl");
data->base = devm_ioremap_resource(dev, res);
......@@ -590,7 +594,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
}
}
for (i = 0; i < data->num_phys; i++) {
for (i = 0; i < data->cfg->num_phys; i++) {
struct sun4i_usb_phy *phy = data->phys + i;
char name[16];
......@@ -602,7 +606,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
phy->vbus = NULL;
}
if (dedicated_clocks)
if (data->cfg->dedicated_clocks)
snprintf(name, sizeof(name), "usb%d_phy", i);
else
strlcpy(name, "usb_phy", sizeof(name));
......@@ -689,13 +693,69 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
return 0;
}
static const struct sun4i_usb_phy_cfg sun4i_a10_cfg = {
.num_phys = 3,
.type = sun4i_a10_phy,
.disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A10,
.dedicated_clocks = false,
};
static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = {
.num_phys = 2,
.type = sun4i_a10_phy,
.disc_thresh = 2,
.phyctl_offset = REG_PHYCTL_A10,
.dedicated_clocks = false,
};
static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = {
.num_phys = 3,
.type = sun4i_a10_phy,
.disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A10,
.dedicated_clocks = true,
};
static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = {
.num_phys = 3,
.type = sun4i_a10_phy,
.disc_thresh = 2,
.phyctl_offset = REG_PHYCTL_A10,
.dedicated_clocks = false,
};
static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = {
.num_phys = 2,
.type = sun4i_a10_phy,
.disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A10,
.dedicated_clocks = true,
};
static const struct sun4i_usb_phy_cfg sun8i_a33_cfg = {
.num_phys = 2,
.type = sun8i_a33_phy,
.disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A33,
.dedicated_clocks = true,
};
static const struct sun4i_usb_phy_cfg sun8i_h3_cfg = {
.num_phys = 4,
.type = sun8i_h3_phy,
.disc_thresh = 3,
.dedicated_clocks = true,
};
static const struct of_device_id sun4i_usb_phy_of_match[] = {
{ .compatible = "allwinner,sun4i-a10-usb-phy" },
{ .compatible = "allwinner,sun5i-a13-usb-phy" },
{ .compatible = "allwinner,sun6i-a31-usb-phy" },
{ .compatible = "allwinner,sun7i-a20-usb-phy" },
{ .compatible = "allwinner,sun8i-a23-usb-phy" },
{ .compatible = "allwinner,sun8i-a33-usb-phy" },
{ .compatible = "allwinner,sun4i-a10-usb-phy", .data = &sun4i_a10_cfg },
{ .compatible = "allwinner,sun5i-a13-usb-phy", .data = &sun5i_a13_cfg },
{ .compatible = "allwinner,sun6i-a31-usb-phy", .data = &sun6i_a31_cfg },
{ .compatible = "allwinner,sun7i-a20-usb-phy", .data = &sun7i_a20_cfg },
{ .compatible = "allwinner,sun8i-a23-usb-phy", .data = &sun8i_a23_cfg },
{ .compatible = "allwinner,sun8i-a33-usb-phy", .data = &sun8i_a33_cfg },
{ .compatible = "allwinner,sun8i-h3-usb-phy", .data = &sun8i_h3_cfg },
{ },
};
MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
......
This diff is collapsed.
......@@ -34,7 +34,7 @@
#include <linux/usb/otg.h>
#include <linux/phy/phy.h>
#include <linux/pm_runtime.h>
#include <linux/usb/musb-omap.h>
#include <linux/usb/musb.h>
#include <linux/usb/ulpi.h>
#include <linux/i2c/twl.h>
#include <linux/regulator/consumer.h>
......@@ -148,10 +148,10 @@
* If VBUS is valid or ID is ground, then we know a
* cable is present and we need to be runtime-enabled
*/
static inline bool cable_present(enum omap_musb_vbus_id_status stat)
static inline bool cable_present(enum musb_vbus_id_status stat)
{
return stat == OMAP_MUSB_VBUS_VALID ||
stat == OMAP_MUSB_ID_GROUND;
return stat == MUSB_VBUS_VALID ||
stat == MUSB_ID_GROUND;
}
struct twl4030_usb {
......@@ -170,7 +170,7 @@ struct twl4030_usb {
enum twl4030_usb_mode usb_mode;
int irq;
enum omap_musb_vbus_id_status linkstat;
enum musb_vbus_id_status linkstat;
bool vbus_supplied;
struct delayed_work id_workaround_work;
......@@ -276,11 +276,11 @@ static bool twl4030_is_driving_vbus(struct twl4030_usb *twl)
return (ret & (ULPI_OTG_DRVVBUS | ULPI_OTG_CHRGVBUS)) ? true : false;
}
static enum omap_musb_vbus_id_status
static enum musb_vbus_id_status
twl4030_usb_linkstat(struct twl4030_usb *twl)
{
int status;
enum omap_musb_vbus_id_status linkstat = OMAP_MUSB_UNKNOWN;
enum musb_vbus_id_status linkstat = MUSB_UNKNOWN;
twl->vbus_supplied = false;
......@@ -306,14 +306,14 @@ static enum omap_musb_vbus_id_status
}
if (status & BIT(2))
linkstat = OMAP_MUSB_ID_GROUND;
linkstat = MUSB_ID_GROUND;
else if (status & BIT(7))
linkstat = OMAP_MUSB_VBUS_VALID;
linkstat = MUSB_VBUS_VALID;
else
linkstat = OMAP_MUSB_VBUS_OFF;
linkstat = MUSB_VBUS_OFF;
} else {
if (twl->linkstat != OMAP_MUSB_UNKNOWN)
linkstat = OMAP_MUSB_VBUS_OFF;
if (twl->linkstat != MUSB_UNKNOWN)
linkstat = MUSB_VBUS_OFF;
}
dev_dbg(twl->dev, "HW_CONDITIONS 0x%02x/%d; link %d\n",
......@@ -535,7 +535,7 @@ static DEVICE_ATTR(vbus, 0444, twl4030_usb_vbus_show, NULL);
static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
{
struct twl4030_usb *twl = _twl;
enum omap_musb_vbus_id_status status;
enum musb_vbus_id_status status;
bool status_changed = false;
status = twl4030_usb_linkstat(twl);
......@@ -567,11 +567,11 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
pm_runtime_mark_last_busy(twl->dev);
pm_runtime_put_autosuspend(twl->dev);
}
omap_musb_mailbox(status);
musb_mailbox(status);
}
/* don't schedule during sleep - irq works right then */
if (status == OMAP_MUSB_ID_GROUND && pm_runtime_active(twl->dev)) {
if (status == MUSB_ID_GROUND && pm_runtime_active(twl->dev)) {
cancel_delayed_work(&twl->id_workaround_work);
schedule_delayed_work(&twl->id_workaround_work, HZ);
}
......@@ -670,7 +670,7 @@ static int twl4030_usb_probe(struct platform_device *pdev)
twl->dev = &pdev->dev;
twl->irq = platform_get_irq(pdev, 0);
twl->vbus_supplied = false;
twl->linkstat = OMAP_MUSB_UNKNOWN;
twl->linkstat = MUSB_UNKNOWN;
twl->phy.dev = twl->dev;
twl->phy.label = "twl4030";
......
......@@ -37,9 +37,4 @@ config USB_CHIPIDEA_HOST
Say Y here to enable host controller functionality of the
ChipIdea driver.
config USB_CHIPIDEA_DEBUG
bool "ChipIdea driver debug"
help
Say Y here to enable debugging output of the ChipIdea driver.
endif
ccflags-$(CONFIG_USB_CHIPIDEA_DEBUG) := -DDEBUG
obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc.o
ci_hdrc-y := core.o otg.o
ci_hdrc-y := core.o otg.o debug.o
ci_hdrc-$(CONFIG_USB_CHIPIDEA_UDC) += udc.o
ci_hdrc-$(CONFIG_USB_CHIPIDEA_HOST) += host.o
ci_hdrc-$(CONFIG_USB_CHIPIDEA_DEBUG) += debug.o
ci_hdrc-$(CONFIG_USB_OTG_FSM) += otg_fsm.o
# Glue/Bridge layers go here
......
......@@ -433,4 +433,7 @@ int hw_wait_reg(struct ci_hdrc *ci, enum ci_hw_regs reg, u32 mask,
void ci_platform_configure(struct ci_hdrc *ci);
int dbg_create_files(struct ci_hdrc *ci);
void dbg_remove_files(struct ci_hdrc *ci);
#endif /* __DRIVERS_USB_CHIPIDEA_CI_H */
......@@ -345,6 +345,11 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev)
return 0;
}
static void ci_hdrc_imx_shutdown(struct platform_device *pdev)
{
ci_hdrc_imx_remove(pdev);
}
#ifdef CONFIG_PM
static int imx_controller_suspend(struct device *dev)
{
......@@ -462,6 +467,7 @@ static const struct dev_pm_ops ci_hdrc_imx_pm_ops = {
static struct platform_driver ci_hdrc_imx_driver = {
.probe = ci_hdrc_imx_probe,
.remove = ci_hdrc_imx_remove,
.shutdown = ci_hdrc_imx_shutdown,
.driver = {
.name = "imx_usb",
.of_match_table = ci_hdrc_imx_dt_ids,
......
......@@ -25,7 +25,8 @@ static void ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event)
case CI_HDRC_CONTROLLER_RESET_EVENT:
dev_dbg(dev, "CI_HDRC_CONTROLLER_RESET_EVENT received\n");
writel(0, USB_AHBBURST);
writel(0, USB_AHBMODE);
/* use AHB transactor, allow posted data writes */
writel(0x8, USB_AHBMODE);
usb_phy_init(ci->usb_phy);
break;
case CI_HDRC_CONTROLLER_STOPPED_EVENT:
......
......@@ -23,7 +23,6 @@
* - BUS: bus glue code, bus abstraction layer
*
* Compile Options
* - CONFIG_USB_CHIPIDEA_DEBUG: enable debug facilities
* - STALL_IN: non-empty bulk-in pipes cannot be halted
* if defined mass storage compliance succeeds but with warnings
* => case 4: Hi > Dn
......@@ -71,7 +70,6 @@
#include "udc.h"
#include "bits.h"
#include "host.h"
#include "debug.h"
#include "otg.h"
#include "otg_fsm.h"
......@@ -688,52 +686,39 @@ static int ci_get_platdata(struct device *dev,
if (usb_get_maximum_speed(dev) == USB_SPEED_FULL)
platdata->flags |= CI_HDRC_FORCE_FULLSPEED;
if (of_find_property(dev->of_node, "phy-clkgate-delay-us", NULL))
of_property_read_u32(dev->of_node, "phy-clkgate-delay-us",
of_property_read_u32(dev->of_node, "phy-clkgate-delay-us",
&platdata->phy_clkgate_delay_us);
platdata->itc_setting = 1;
if (of_find_property(dev->of_node, "itc-setting", NULL)) {
ret = of_property_read_u32(dev->of_node, "itc-setting",
&platdata->itc_setting);
if (ret) {
dev_err(dev,
"failed to get itc-setting\n");
return ret;
}
}
if (of_find_property(dev->of_node, "ahb-burst-config", NULL)) {
ret = of_property_read_u32(dev->of_node, "ahb-burst-config",
&platdata->ahb_burst_config);
if (ret) {
dev_err(dev,
"failed to get ahb-burst-config\n");
return ret;
}
of_property_read_u32(dev->of_node, "itc-setting",
&platdata->itc_setting);
ret = of_property_read_u32(dev->of_node, "ahb-burst-config",
&platdata->ahb_burst_config);
if (!ret) {
platdata->flags |= CI_HDRC_OVERRIDE_AHB_BURST;
} else if (ret != -EINVAL) {
dev_err(dev, "failed to get ahb-burst-config\n");
return ret;
}
if (of_find_property(dev->of_node, "tx-burst-size-dword", NULL)) {
ret = of_property_read_u32(dev->of_node, "tx-burst-size-dword",
&platdata->tx_burst_size);
if (ret) {
dev_err(dev,
"failed to get tx-burst-size-dword\n");
return ret;
}
ret = of_property_read_u32(dev->of_node, "tx-burst-size-dword",
&platdata->tx_burst_size);
if (!ret) {
platdata->flags |= CI_HDRC_OVERRIDE_TX_BURST;
} else if (ret != -EINVAL) {
dev_err(dev, "failed to get tx-burst-size-dword\n");
return ret;
}
if (of_find_property(dev->of_node, "rx-burst-size-dword", NULL)) {
ret = of_property_read_u32(dev->of_node, "rx-burst-size-dword",
&platdata->rx_burst_size);
if (ret) {
dev_err(dev,
"failed to get rx-burst-size-dword\n");
return ret;
}
ret = of_property_read_u32(dev->of_node, "rx-burst-size-dword",
&platdata->rx_burst_size);
if (!ret) {
platdata->flags |= CI_HDRC_OVERRIDE_RX_BURST;
} else if (ret != -EINVAL) {
dev_err(dev, "failed to get rx-burst-size-dword\n");
return ret;
}
ext_id = ERR_PTR(-ENODEV);
......
......@@ -15,7 +15,6 @@
#include "ci.h"
#include "udc.h"
#include "bits.h"
#include "debug.h"
#include "otg.h"
/**
......
/*
* debug.h - ChipIdea USB driver debug interfaces
*
* Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved.
*
* Author: David Lopo
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __DRIVERS_USB_CHIPIDEA_DEBUG_H
#define __DRIVERS_USB_CHIPIDEA_DEBUG_H
#ifdef CONFIG_USB_CHIPIDEA_DEBUG
int dbg_create_files(struct ci_hdrc *ci);
void dbg_remove_files(struct ci_hdrc *ci);
#else
static inline int dbg_create_files(struct ci_hdrc *ci)
{
return 0;
}
static inline void dbg_remove_files(struct ci_hdrc *ci)
{
}
#endif
#endif /* __DRIVERS_USB_CHIPIDEA_DEBUG_H */
......@@ -190,6 +190,8 @@ static void host_stop(struct ci_hdrc *ci)
(ci->platdata->flags & CI_HDRC_TURN_VBUS_EARLY_ON))
regulator_disable(ci->platdata->reg_vbus);
}
ci->hcd = NULL;
ci->otg.host = NULL;
}
......
......@@ -485,20 +485,30 @@ static void ci_otg_loc_conn(struct otg_fsm *fsm, int on)
/*
* Generate SOF by host.
* This is controlled through suspend/resume the port.
* In host mode, controller will automatically send SOF.
* Suspend will block the data on the port.
*
* This is controlled through usbcore by usb autosuspend,
* so the usb device class driver need support autosuspend,
* otherwise the bus suspend will not happen.
*/
static void ci_otg_loc_sof(struct otg_fsm *fsm, int on)
{
struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
struct usb_device *udev;
if (on)
hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_FPR,
PORTSC_FPR);
else
hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_SUSP,
PORTSC_SUSP);
if (!fsm->otg->host)
return;
udev = usb_hub_find_child(fsm->otg->host->root_hub, 1);
if (!udev)
return;
if (on) {
usb_disable_autosuspend(udev);
} else {
pm_runtime_set_autosuspend_delay(&udev->dev, 0);
usb_enable_autosuspend(udev);
}
}
/*
......
......@@ -26,7 +26,6 @@
#include "ci.h"
#include "udc.h"
#include "bits.h"
#include "debug.h"
#include "otg.h"
#include "otg_fsm.h"
......@@ -349,14 +348,13 @@ static int add_td_to_list(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq,
if (node == NULL)
return -ENOMEM;
node->ptr = dma_pool_alloc(hwep->td_pool, GFP_ATOMIC,
node->ptr = dma_pool_zalloc(hwep->td_pool, GFP_ATOMIC,
&node->dma);
if (node->ptr == NULL) {
kfree(node);
return -ENOMEM;
}
memset(node->ptr, 0, sizeof(struct ci_hw_td));
node->ptr->token = cpu_to_le32(length << __ffs(TD_TOTAL_BYTES));
node->ptr->token &= cpu_to_le32(TD_TOTAL_BYTES);
node->ptr->token |= cpu_to_le32(TD_STATUS_ACTIVE);
......@@ -404,9 +402,9 @@ static inline u8 _usb_addr(struct ci_hw_ep *ep)
}
/**
* _hardware_queue: configures a request at hardware level
* @gadget: gadget
* _hardware_enqueue: configures a request at hardware level
* @hwep: endpoint
* @hwreq: request
*
* This function returns an error code
*/
......@@ -435,19 +433,28 @@ static int _hardware_enqueue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq)
if (hwreq->req.dma % PAGE_SIZE)
pages--;
if (rest == 0)
add_td_to_list(hwep, hwreq, 0);
if (rest == 0) {
ret = add_td_to_list(hwep, hwreq, 0);
if (ret < 0)
goto done;
}
while (rest > 0) {
unsigned count = min(hwreq->req.length - hwreq->req.actual,
(unsigned)(pages * CI_HDRC_PAGE_SIZE));
add_td_to_list(hwep, hwreq, count);
ret = add_td_to_list(hwep, hwreq, count);
if (ret < 0)
goto done;
rest -= count;
}
if (hwreq->req.zero && hwreq->req.length && hwep->dir == TX
&& (hwreq->req.length % hwep->ep.maxpacket == 0))
add_td_to_list(hwep, hwreq, 0);
&& (hwreq->req.length % hwep->ep.maxpacket == 0)) {
ret = add_td_to_list(hwep, hwreq, 0);
if (ret < 0)
goto done;
}
firstnode = list_first_entry(&hwreq->tds, struct td_node, td);
......@@ -788,8 +795,12 @@ static void isr_get_status_complete(struct usb_ep *ep, struct usb_request *req)
/**
* _ep_queue: queues (submits) an I/O request to an endpoint
* @ep: endpoint
* @req: request
* @gfp_flags: GFP flags (not used)
*
* Caller must hold lock
* This function returns an error code
*/
static int _ep_queue(struct usb_ep *ep, struct usb_request *req,
gfp_t __maybe_unused gfp_flags)
......
......@@ -17,6 +17,7 @@
#include <linux/usb/ch9.h>
#include <linux/usb/of.h>
#include <linux/usb/otg.h>
#include <linux/of_platform.h>
const char *usb_otg_state_string(enum usb_otg_state state)
{
......@@ -106,24 +107,71 @@ static const char *const usb_dr_modes[] = {
[USB_DR_MODE_OTG] = "otg",
};
static enum usb_dr_mode usb_get_dr_mode_from_string(const char *str)
{
int i;
for (i = 0; i < ARRAY_SIZE(usb_dr_modes); i++)
if (!strcmp(usb_dr_modes[i], str))
return i;
return USB_DR_MODE_UNKNOWN;
}
enum usb_dr_mode usb_get_dr_mode(struct device *dev)
{
const char *dr_mode;
int err, i;
int err;
err = device_property_read_string(dev, "dr_mode", &dr_mode);
if (err < 0)
return USB_DR_MODE_UNKNOWN;
for (i = 0; i < ARRAY_SIZE(usb_dr_modes); i++)
if (!strcmp(dr_mode, usb_dr_modes[i]))
return i;
return USB_DR_MODE_UNKNOWN;
return usb_get_dr_mode_from_string(dr_mode);
}
EXPORT_SYMBOL_GPL(usb_get_dr_mode);
#ifdef CONFIG_OF
/**
* of_usb_get_dr_mode_by_phy - Get dual role mode for the controller device
* which is associated with the given phy device_node
* @np: Pointer to the given phy device_node
*
* In dts a usb controller associates with phy devices. The function gets
* the string from property 'dr_mode' of the controller associated with the
* given phy device node, and returns the correspondig enum usb_dr_mode.
*/
enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *phy_np)
{
struct device_node *controller = NULL;
struct device_node *phy;
const char *dr_mode;
int index;
int err;
do {
controller = of_find_node_with_property(controller, "phys");
index = 0;
do {
phy = of_parse_phandle(controller, "phys", index);
of_node_put(phy);
if (phy == phy_np)
goto finish;
index++;
} while (phy);
} while (controller);
finish:
err = of_property_read_string(controller, "dr_mode", &dr_mode);
of_node_put(controller);
if (err < 0)
return USB_DR_MODE_UNKNOWN;
return usb_get_dr_mode_from_string(dr_mode);
}
EXPORT_SYMBOL_GPL(of_usb_get_dr_mode_by_phy);
/**
* of_usb_host_tpl_support - to get if Targeted Peripheral List is supported
* for given targeted hosts (non-PC hosts)
......
......@@ -100,6 +100,11 @@ static bool usbfs_snoop;
module_param(usbfs_snoop, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(usbfs_snoop, "true to log all usbfs traffic");
static unsigned usbfs_snoop_max = 65536;
module_param(usbfs_snoop_max, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(usbfs_snoop_max,
"maximum number of bytes to print while snooping");
#define snoop(dev, format, arg...) \
do { \
if (usbfs_snoop) \
......@@ -368,6 +373,7 @@ static void snoop_urb(struct usb_device *udev,
ep, t, d, length, timeout_or_status);
}
data_len = min(data_len, usbfs_snoop_max);
if (data && data_len > 0) {
print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_NONE, 32, 1,
data, data_len, 1);
......@@ -378,7 +384,8 @@ static void snoop_urb_data(struct urb *urb, unsigned len)
{
int i, size;
if (!usbfs_snoop)
len = min(len, usbfs_snoop_max);
if (!usbfs_snoop || len == 0)
return;
if (urb->num_sgs == 0) {
......@@ -1685,8 +1692,12 @@ static struct async *reap_as(struct usb_dev_state *ps)
static int proc_reapurb(struct usb_dev_state *ps, void __user *arg)
{
struct async *as = reap_as(ps);
if (as) {
int retval = processcompl(as, (void __user * __user *)arg);
int retval;
snoop(&ps->dev->dev, "reap %p\n", as->userurb);
retval = processcompl(as, (void __user * __user *)arg);
free_async(as);
return retval;
}
......@@ -1702,6 +1713,7 @@ static int proc_reapurbnonblock(struct usb_dev_state *ps, void __user *arg)
as = async_getcompleted(ps);
if (as) {
snoop(&ps->dev->dev, "reap %p\n", as->userurb);
retval = processcompl(as, (void __user * __user *)arg);
free_async(as);
} else {
......@@ -1828,8 +1840,12 @@ static int processcompl_compat(struct async *as, void __user * __user *arg)
static int proc_reapurb_compat(struct usb_dev_state *ps, void __user *arg)
{
struct async *as = reap_as(ps);
if (as) {
int retval = processcompl_compat(as, (void __user * __user *)arg);
int retval;
snoop(&ps->dev->dev, "reap %p\n", as->userurb);
retval = processcompl_compat(as, (void __user * __user *)arg);
free_async(as);
return retval;
}
......@@ -1845,6 +1861,7 @@ static int proc_reapurbnonblock_compat(struct usb_dev_state *ps, void __user *ar
as = async_getcompleted(ps);
if (as) {
snoop(&ps->dev->dev, "reap %p\n", as->userurb);
retval = processcompl_compat(as, (void __user * __user *)arg);
free_async(as);
} else {
......@@ -2249,7 +2266,7 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
#endif
case USBDEVFS_DISCARDURB:
snoop(&dev->dev, "%s: DISCARDURB\n", __func__);
snoop(&dev->dev, "%s: DISCARDURB %p\n", __func__, p);
ret = proc_unlinkurb(ps, p);
break;
......
......@@ -3000,7 +3000,7 @@ EXPORT_SYMBOL_GPL(usb_hcd_platform_shutdown);
#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)
struct usb_mon_operations *mon_ops;
const struct usb_mon_operations *mon_ops;
/*
* The registration is unlocked.
......@@ -3010,7 +3010,7 @@ struct usb_mon_operations *mon_ops;
* symbols from usbcore, usbcore gets referenced and cannot be unloaded first.
*/
int usb_mon_register (struct usb_mon_operations *ops)
int usb_mon_register(const struct usb_mon_operations *ops)
{
if (mon_ops)
......
......@@ -3324,7 +3324,7 @@ static int finish_port_resume(struct usb_device *udev)
/*
* There are some SS USB devices which take longer time for link training.
* XHCI specs 4.19.4 says that when Link training is successful, port
* sets CSC bit to 1. So if SW reads port status before successful link
* sets CCS bit to 1. So if SW reads port status before successful link
* training, then it will not find device to be present.
* USB Analyzer log with such buggy devices show that in some cases
* device switch on the RX termination after long delay of host enabling
......@@ -3335,14 +3335,17 @@ static int finish_port_resume(struct usb_device *udev)
* routine implements a 2000 ms timeout for link training. If in a case
* link trains before timeout, loop will exit earlier.
*
* There are also some 2.0 hard drive based devices and 3.0 thumb
* drives that, when plugged into a 2.0 only port, take a long
* time to set CCS after VBUS enable.
*
* FIXME: If a device was connected before suspend, but was removed
* while system was asleep, then the loop in the following routine will
* only exit at timeout.
*
* This routine should only be called when persist is enabled for a SS
* device.
* This routine should only be called when persist is enabled.
*/
static int wait_for_ss_port_enable(struct usb_device *udev,
static int wait_for_connected(struct usb_device *udev,
struct usb_hub *hub, int *port1,
u16 *portchange, u16 *portstatus)
{
......@@ -3355,6 +3358,7 @@ static int wait_for_ss_port_enable(struct usb_device *udev,
delay_ms += 20;
status = hub_port_status(hub, *port1, portstatus, portchange);
}
dev_dbg(&udev->dev, "Waited %dms for CONNECT\n", delay_ms);
return status;
}
......@@ -3454,8 +3458,8 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
}
}
if (udev->persist_enabled && hub_is_superspeed(hub->hdev))
status = wait_for_ss_port_enable(udev, hub, &port1, &portchange,
if (udev->persist_enabled)
status = wait_for_connected(udev, hub, &port1, &portchange,
&portstatus);
status = check_port_resume_type(udev,
......@@ -3895,17 +3899,30 @@ static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
return;
}
if (usb_set_lpm_timeout(udev, state, timeout))
if (usb_set_lpm_timeout(udev, state, timeout)) {
/* If we can't set the parent hub U1/U2 timeout,
* device-initiated LPM won't be allowed either, so let the xHCI
* host know that this link state won't be enabled.
*/
hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state);
} else {
/* Only a configured device will accept the Set Feature
* U1/U2_ENABLE
*/
if (udev->actconfig)
usb_set_device_initiated_lpm(udev, state, true);
/* Only a configured device will accept the Set Feature U1/U2_ENABLE */
else if (udev->actconfig)
usb_set_device_initiated_lpm(udev, state, true);
/* As soon as usb_set_lpm_timeout(timeout) returns 0, the
* hub-initiated LPM is enabled. Thus, LPM is enabled no
* matter the result of usb_set_device_initiated_lpm().
* The only difference is whether device is able to initiate
* LPM.
*/
if (state == USB3_LPM_U1)
udev->usb3_lpm_u1_enabled = 1;
else if (state == USB3_LPM_U2)
udev->usb3_lpm_u2_enabled = 1;
}
}
/*
......@@ -3945,6 +3962,18 @@ static int usb_disable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
dev_warn(&udev->dev, "Could not disable xHCI %s timeout, "
"bus schedule bandwidth may be impacted.\n",
usb3_lpm_names[state]);
/* As soon as usb_set_lpm_timeout(0) return 0, hub initiated LPM
* is disabled. Hub will disallows link to enter U1/U2 as well,
* even device is initiating LPM. Hence LPM is disabled if hub LPM
* timeout set to 0, no matter device-initiated LPM is disabled or
* not.
*/
if (state == USB3_LPM_U1)
udev->usb3_lpm_u1_enabled = 0;
else if (state == USB3_LPM_U2)
udev->usb3_lpm_u2_enabled = 0;
return 0;
}
......@@ -3979,8 +4008,6 @@ int usb_disable_lpm(struct usb_device *udev)
if (usb_disable_link_state(hcd, udev, USB3_LPM_U2))
goto enable_lpm;
udev->usb3_lpm_enabled = 0;
return 0;
enable_lpm:
......@@ -4017,6 +4044,8 @@ EXPORT_SYMBOL_GPL(usb_unlocked_disable_lpm);
void usb_enable_lpm(struct usb_device *udev)
{
struct usb_hcd *hcd;
struct usb_hub *hub;
struct usb_port *port_dev;
if (!udev || !udev->parent ||
udev->speed != USB_SPEED_SUPER ||
......@@ -4036,10 +4065,17 @@ void usb_enable_lpm(struct usb_device *udev)
if (udev->lpm_disable_count > 0)
return;
usb_enable_link_state(hcd, udev, USB3_LPM_U1);
usb_enable_link_state(hcd, udev, USB3_LPM_U2);
hub = usb_hub_to_struct_hub(udev->parent);
if (!hub)
return;
port_dev = hub->ports[udev->portnum - 1];
if (port_dev->usb3_lpm_u1_permit)
usb_enable_link_state(hcd, udev, USB3_LPM_U1);
udev->usb3_lpm_enabled = 1;
if (port_dev->usb3_lpm_u2_permit)
usb_enable_link_state(hcd, udev, USB3_LPM_U2);
}
EXPORT_SYMBOL_GPL(usb_enable_lpm);
......
......@@ -92,6 +92,8 @@ struct usb_hub {
* @status_lock: synchronize port_event() vs usb_port_{suspend|resume}
* @portnum: port index num based one
* @is_superspeed cache super-speed status
* @usb3_lpm_u1_permit: whether USB3 U1 LPM is permitted.
* @usb3_lpm_u2_permit: whether USB3 U2 LPM is permitted.
*/
struct usb_port {
struct usb_device *child;
......@@ -104,6 +106,8 @@ struct usb_port {
struct mutex status_lock;
u8 portnum;
unsigned int is_superspeed:1;
unsigned int usb3_lpm_u1_permit:1;
unsigned int usb3_lpm_u2_permit:1;
};
#define to_usb_port(_dev) \
......@@ -155,4 +159,3 @@ static inline int hub_port_debounce_be_stable(struct usb_hub *hub,
{
return hub_port_debounce(hub, port1, false);
}
......@@ -50,6 +50,72 @@ static ssize_t connect_type_show(struct device *dev,
}
static DEVICE_ATTR_RO(connect_type);
static ssize_t usb3_lpm_permit_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct usb_port *port_dev = to_usb_port(dev);
const char *p;
if (port_dev->usb3_lpm_u1_permit) {
if (port_dev->usb3_lpm_u2_permit)
p = "u1_u2";
else
p = "u1";
} else {
if (port_dev->usb3_lpm_u2_permit)
p = "u2";
else
p = "0";
}
return sprintf(buf, "%s\n", p);
}
static ssize_t usb3_lpm_permit_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct usb_port *port_dev = to_usb_port(dev);
struct usb_device *udev = port_dev->child;
struct usb_hcd *hcd;
if (!strncmp(buf, "u1_u2", 5)) {
port_dev->usb3_lpm_u1_permit = 1;
port_dev->usb3_lpm_u2_permit = 1;
} else if (!strncmp(buf, "u1", 2)) {
port_dev->usb3_lpm_u1_permit = 1;
port_dev->usb3_lpm_u2_permit = 0;
} else if (!strncmp(buf, "u2", 2)) {
port_dev->usb3_lpm_u1_permit = 0;
port_dev->usb3_lpm_u2_permit = 1;
} else if (!strncmp(buf, "0", 1)) {
port_dev->usb3_lpm_u1_permit = 0;
port_dev->usb3_lpm_u2_permit = 0;
} else
return -EINVAL;
/* If device is connected to the port, disable or enable lpm
* to make new u1 u2 setting take effect immediately.
*/
if (udev) {
hcd = bus_to_hcd(udev->bus);
if (!hcd)
return -EINVAL;
usb_lock_device(udev);
mutex_lock(hcd->bandwidth_mutex);
if (!usb_disable_lpm(udev))
usb_enable_lpm(udev);
mutex_unlock(hcd->bandwidth_mutex);
usb_unlock_device(udev);
}
return count;
}
static DEVICE_ATTR_RW(usb3_lpm_permit);
static struct attribute *port_dev_attrs[] = {
&dev_attr_connect_type.attr,
NULL,
......@@ -64,6 +130,21 @@ static const struct attribute_group *port_dev_group[] = {
NULL,
};
static struct attribute *port_dev_usb3_attrs[] = {
&dev_attr_usb3_lpm_permit.attr,
NULL,
};
static struct attribute_group port_dev_usb3_attr_grp = {
.attrs = port_dev_usb3_attrs,
};
static const struct attribute_group *port_dev_usb3_group[] = {
&port_dev_attr_grp,
&port_dev_usb3_attr_grp,
NULL,
};
static void usb_port_device_release(struct device *dev)
{
struct usb_port *port_dev = to_usb_port(dev);
......@@ -401,6 +482,7 @@ static void find_and_link_peer(struct usb_hub *hub, int port1)
int usb_hub_create_port_device(struct usb_hub *hub, int port1)
{
struct usb_port *port_dev;
struct usb_device *hdev = hub->hdev;
int retval;
port_dev = kzalloc(sizeof(*port_dev), GFP_KERNEL);
......@@ -417,7 +499,12 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
port_dev->portnum = port1;
set_bit(port1, hub->power_bits);
port_dev->dev.parent = hub->intfdev;
port_dev->dev.groups = port_dev_group;
if (hub_is_superspeed(hdev)) {
port_dev->usb3_lpm_u1_permit = 1;
port_dev->usb3_lpm_u2_permit = 1;
port_dev->dev.groups = port_dev_usb3_group;
} else
port_dev->dev.groups = port_dev_group;
port_dev->dev.type = &usb_port_device_type;
port_dev->dev.driver = &usb_port_driver;
if (hub_is_superspeed(hub->hdev))
......
......@@ -531,7 +531,7 @@ static ssize_t usb2_lpm_besl_store(struct device *dev,
}
static DEVICE_ATTR_RW(usb2_lpm_besl);
static ssize_t usb3_hardware_lpm_show(struct device *dev,
static ssize_t usb3_hardware_lpm_u1_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct usb_device *udev = to_usb_device(dev);
......@@ -539,7 +539,7 @@ static ssize_t usb3_hardware_lpm_show(struct device *dev,
usb_lock_device(udev);
if (udev->usb3_lpm_enabled)
if (udev->usb3_lpm_u1_enabled)
p = "enabled";
else
p = "disabled";
......@@ -548,7 +548,26 @@ static ssize_t usb3_hardware_lpm_show(struct device *dev,
return sprintf(buf, "%s\n", p);
}
static DEVICE_ATTR_RO(usb3_hardware_lpm);
static DEVICE_ATTR_RO(usb3_hardware_lpm_u1);
static ssize_t usb3_hardware_lpm_u2_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct usb_device *udev = to_usb_device(dev);
const char *p;
usb_lock_device(udev);
if (udev->usb3_lpm_u2_enabled)
p = "enabled";
else
p = "disabled";
usb_unlock_device(udev);
return sprintf(buf, "%s\n", p);
}
static DEVICE_ATTR_RO(usb3_hardware_lpm_u2);
static struct attribute *usb2_hardware_lpm_attr[] = {
&dev_attr_usb2_hardware_lpm.attr,
......@@ -562,7 +581,8 @@ static struct attribute_group usb2_hardware_lpm_attr_group = {
};
static struct attribute *usb3_hardware_lpm_attr[] = {
&dev_attr_usb3_hardware_lpm.attr,
&dev_attr_usb3_hardware_lpm_u1.attr,
&dev_attr_usb3_hardware_lpm_u2.attr,
NULL,
};
static struct attribute_group usb3_hardware_lpm_attr_group = {
......@@ -592,7 +612,8 @@ static int add_power_attributes(struct device *dev)
if (udev->usb2_hw_lpm_capable == 1)
rc = sysfs_merge_group(&dev->kobj,
&usb2_hardware_lpm_attr_group);
if (udev->lpm_capable == 1)
if (udev->speed == USB_SPEED_SUPER &&
udev->lpm_capable == 1)
rc = sysfs_merge_group(&dev->kobj,
&usb3_hardware_lpm_attr_group);
}
......
......@@ -49,12 +49,7 @@ const char *usbcore_name = "usbcore";
static bool nousb; /* Disable USB when built into kernel image */
/* To disable USB, kernel command line is 'nousb' not 'usbcore.nousb' */
#ifdef MODULE
module_param(nousb, bool, 0444);
#else
core_param(nousb, nousb, bool, 0444);
#endif
/*
* for external read access to <nousb>
......
This diff is collapsed.
......@@ -246,6 +246,13 @@ enum dwc2_ep0_state {
* value for this if none is specified.
* 0 - Address DMA
* 1 - Descriptor DMA (default, if available)
* @dma_desc_fs_enable: When DMA mode is enabled, specifies whether to use
* address DMA mode or descriptor DMA mode for accessing
* the data FIFOs in Full Speed mode only. The driver
* will automatically detect the value for this if none is
* specified.
* 0 - Address DMA
* 1 - Descriptor DMA in FS (default, if available)
* @speed: Specifies the maximum speed of operation in host and
* device mode. The actual speed depends on the speed of
* the attached device and the value of phy_type.
......@@ -375,6 +382,7 @@ struct dwc2_core_params {
int otg_ver;
int dma_enable;
int dma_desc_enable;
int dma_desc_fs_enable;
int speed;
int enable_dynamic_fifo;
int en_multiple_tx_fifo;
......@@ -451,15 +459,18 @@ struct dwc2_core_params {
* 1 - 16 bits
* 2 - 8 or 16 bits
* @snpsid: Value from SNPSID register
* @dev_ep_dirs: Direction of device endpoints (GHWCFG1)
*/
struct dwc2_hw_params {
unsigned op_mode:3;
unsigned arch:2;
unsigned dma_desc_enable:1;
unsigned dma_desc_fs_enable:1;
unsigned enable_dynamic_fifo:1;
unsigned en_multiple_tx_fifo:1;
unsigned host_rx_fifo_size:16;
unsigned host_nperio_tx_fifo_size:16;
unsigned dev_nperio_tx_fifo_size:16;
unsigned host_perio_tx_fifo_size:16;
unsigned nperio_tx_q_depth:3;
unsigned host_perio_tx_q_depth:3;
......@@ -476,6 +487,7 @@ struct dwc2_hw_params {
unsigned power_optimized:1;
unsigned utmi_phy_data_width:2;
u32 snpsid;
u32 dev_ep_dirs;
};
/* Size of control and EP0 buffers */
......@@ -676,6 +688,9 @@ struct dwc2_hregs_backup {
* @otg_port: OTG port number
* @frame_list: Frame list
* @frame_list_dma: Frame list DMA address
* @frame_list_sz: Frame list size
* @desc_gen_cache: Kmem cache for generic descriptors
* @desc_hsisoc_cache: Kmem cache for hs isochronous descriptors
*
* These are for peripheral mode:
*
......@@ -770,6 +785,7 @@ struct dwc2_hsotg {
u16 frame_number;
u16 periodic_qh_count;
bool bus_suspended;
bool new_connection;
#ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
#define FRAME_NUM_ARRAY_SIZE 1000
......@@ -794,6 +810,9 @@ struct dwc2_hsotg {
u8 otg_port;
u32 *frame_list;
dma_addr_t frame_list_dma;
u32 frame_list_sz;
struct kmem_cache *desc_gen_cache;
struct kmem_cache *desc_hsisoc_cache;
#ifdef DEBUG
u32 frrem_samples;
......@@ -864,10 +883,14 @@ enum dwc2_halt_status {
* The following functions support initialization of the core driver component
* and the DWC_otg controller
*/
extern int dwc2_core_reset(struct dwc2_hsotg *hsotg);
extern int dwc2_core_reset_and_force_dr_mode(struct dwc2_hsotg *hsotg);
extern void dwc2_core_host_init(struct dwc2_hsotg *hsotg);
extern int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg);
extern int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore);
void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg);
/*
* Host core Functions.
* The following functions support managing the DWC_otg controller in host
......@@ -901,7 +924,7 @@ extern void dwc2_read_packet(struct dwc2_hsotg *hsotg, u8 *dest, u16 bytes);
extern void dwc2_flush_tx_fifo(struct dwc2_hsotg *hsotg, const int num);
extern void dwc2_flush_rx_fifo(struct dwc2_hsotg *hsotg);
extern int dwc2_core_init(struct dwc2_hsotg *hsotg, bool select_phy, int irq);
extern int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup);
extern void dwc2_enable_global_interrupts(struct dwc2_hsotg *hcd);
extern void dwc2_disable_global_interrupts(struct dwc2_hsotg *hcd);
......@@ -941,6 +964,16 @@ extern void dwc2_set_param_dma_enable(struct dwc2_hsotg *hsotg, int val);
*/
extern void dwc2_set_param_dma_desc_enable(struct dwc2_hsotg *hsotg, int val);
/*
* When DMA mode is enabled specifies whether to use
* address DMA or DMA Descritor mode with full speed devices
* for accessing the data FIFOs in host mode.
* 0 - address DMA
* 1 - FS DMA Descriptor(default, if available)
*/
extern void dwc2_set_param_dma_desc_fs_enable(struct dwc2_hsotg *hsotg,
int val);
/*
* Specifies the maximum speed of operation in host and device mode.
* The actual speed depends on the speed of the attached device and
......@@ -1109,6 +1142,31 @@ extern int dwc2_get_hwparams(struct dwc2_hsotg *hsotg);
extern int dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg);
extern int dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg);
/*
* The following functions check the controller's OTG operation mode
* capability (GHWCFG2.OTG_MODE).
*
* These functions can be used before the internal hsotg->hw_params
* are read in and cached so they always read directly from the
* GHWCFG2 register.
*/
unsigned dwc2_op_mode(struct dwc2_hsotg *hsotg);
bool dwc2_hw_is_otg(struct dwc2_hsotg *hsotg);
bool dwc2_hw_is_host(struct dwc2_hsotg *hsotg);
bool dwc2_hw_is_device(struct dwc2_hsotg *hsotg);
/*
* Returns the mode of operation, host or device
*/
static inline int dwc2_is_host_mode(struct dwc2_hsotg *hsotg)
{
return (dwc2_readl(hsotg->regs + GINTSTS) & GINTSTS_CURMODE_HOST) != 0;
}
static inline int dwc2_is_device_mode(struct dwc2_hsotg *hsotg)
{
return (dwc2_readl(hsotg->regs + GINTSTS) & GINTSTS_CURMODE_HOST) == 0;
}
/*
* Dump core registers and SPRAM
*/
......@@ -1154,12 +1212,14 @@ static inline int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg,
#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
extern int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg);
extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg);
extern void dwc2_hcd_connect(struct dwc2_hsotg *hsotg);
extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force);
extern void dwc2_hcd_start(struct dwc2_hsotg *hsotg);
#else
static inline int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg)
{ return 0; }
static inline void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg) {}
static inline void dwc2_hcd_connect(struct dwc2_hsotg *hsotg) {}
static inline void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force) {}
static inline void dwc2_hcd_start(struct dwc2_hsotg *hsotg) {}
static inline void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) {}
static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
......
......@@ -86,9 +86,6 @@ static void dwc2_handle_usb_port_intr(struct dwc2_hsotg *hsotg)
hprt0 &= ~HPRT0_ENA;
dwc2_writel(hprt0, hsotg->regs + HPRT0);
}
/* Clear interrupt */
dwc2_writel(GINTSTS_PRTINT, hsotg->regs + GINTSTS);
}
/**
......@@ -98,11 +95,11 @@ static void dwc2_handle_usb_port_intr(struct dwc2_hsotg *hsotg)
*/
static void dwc2_handle_mode_mismatch_intr(struct dwc2_hsotg *hsotg)
{
dev_warn(hsotg->dev, "Mode Mismatch Interrupt: currently in %s mode\n",
dwc2_is_host_mode(hsotg) ? "Host" : "Device");
/* Clear interrupt */
dwc2_writel(GINTSTS_MODEMIS, hsotg->regs + GINTSTS);
dev_warn(hsotg->dev, "Mode Mismatch Interrupt: currently in %s mode\n",
dwc2_is_host_mode(hsotg) ? "Host" : "Device");
}
/**
......@@ -239,7 +236,7 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
dev_dbg(hsotg->dev, "a_suspend->a_peripheral (%d)\n",
hsotg->op_state);
spin_unlock(&hsotg->lock);
dwc2_hcd_disconnect(hsotg);
dwc2_hcd_disconnect(hsotg, false);
spin_lock(&hsotg->lock);
hsotg->op_state = OTG_STATE_A_PERIPHERAL;
} else {
......@@ -276,9 +273,13 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
*/
static void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg)
{
u32 gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
u32 gintmsk;
/* Clear interrupt */
dwc2_writel(GINTSTS_CONIDSTSCHNG, hsotg->regs + GINTSTS);
/* Need to disable SOF interrupt immediately */
gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
gintmsk &= ~GINTSTS_SOF;
dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
......@@ -295,9 +296,6 @@ static void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg)
queue_work(hsotg->wq_otg, &hsotg->wf_otg);
spin_lock(&hsotg->lock);
}
/* Clear interrupt */
dwc2_writel(GINTSTS_CONIDSTSCHNG, hsotg->regs + GINTSTS);
}
/**
......@@ -315,12 +313,12 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
{
int ret;
dev_dbg(hsotg->dev, "Session request interrupt - lx_state=%d\n",
hsotg->lx_state);
/* Clear interrupt */
dwc2_writel(GINTSTS_SESSREQINT, hsotg->regs + GINTSTS);
dev_dbg(hsotg->dev, "Session request interrupt - lx_state=%d\n",
hsotg->lx_state);
if (dwc2_is_device_mode(hsotg)) {
if (hsotg->lx_state == DWC2_L2) {
ret = dwc2_exit_hibernation(hsotg, true);
......@@ -347,6 +345,10 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
{
int ret;
/* Clear interrupt */
dwc2_writel(GINTSTS_WKUPINT, hsotg->regs + GINTSTS);
dev_dbg(hsotg->dev, "++Resume or Remote Wakeup Detected Interrupt++\n");
dev_dbg(hsotg->dev, "%s lxstate = %d\n", __func__, hsotg->lx_state);
......@@ -368,10 +370,9 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
/* Change to L0 state */
hsotg->lx_state = DWC2_L0;
} else {
if (hsotg->core_params->hibernation) {
dwc2_writel(GINTSTS_WKUPINT, hsotg->regs + GINTSTS);
if (hsotg->core_params->hibernation)
return;
}
if (hsotg->lx_state != DWC2_L1) {
u32 pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
......@@ -385,9 +386,6 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
hsotg->lx_state = DWC2_L0;
}
}
/* Clear interrupt */
dwc2_writel(GINTSTS_WKUPINT, hsotg->regs + GINTSTS);
}
/*
......@@ -396,14 +394,14 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
*/
static void dwc2_handle_disconnect_intr(struct dwc2_hsotg *hsotg)
{
dwc2_writel(GINTSTS_DISCONNINT, hsotg->regs + GINTSTS);
dev_dbg(hsotg->dev, "++Disconnect Detected Interrupt++ (%s) %s\n",
dwc2_is_host_mode(hsotg) ? "Host" : "Device",
dwc2_op_state_str(hsotg));
if (hsotg->op_state == OTG_STATE_A_HOST)
dwc2_hcd_disconnect(hsotg);
dwc2_writel(GINTSTS_DISCONNINT, hsotg->regs + GINTSTS);
dwc2_hcd_disconnect(hsotg, false);
}
/*
......@@ -419,6 +417,9 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
u32 dsts;
int ret;
/* Clear interrupt */
dwc2_writel(GINTSTS_USBSUSP, hsotg->regs + GINTSTS);
dev_dbg(hsotg->dev, "USB SUSPEND\n");
if (dwc2_is_device_mode(hsotg)) {
......@@ -437,7 +438,7 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
if (!dwc2_is_device_connected(hsotg)) {
dev_dbg(hsotg->dev,
"ignore suspend request before enumeration\n");
goto clear_int;
return;
}
ret = dwc2_enter_hibernation(hsotg);
......@@ -476,10 +477,6 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
hsotg->op_state = OTG_STATE_A_HOST;
}
}
clear_int:
/* Clear interrupt */
dwc2_writel(GINTSTS_USBSUSP, hsotg->regs + GINTSTS);
}
#define GINTMSK_COMMON (GINTSTS_WKUPINT | GINTSTS_SESSREQINT | \
......
......@@ -2095,7 +2095,7 @@ static void dwc2_hsotg_irq_enumdone(struct dwc2_hsotg *hsotg)
*/
/* catch both EnumSpd_FS and EnumSpd_FS48 */
switch (dsts & DSTS_ENUMSPD_MASK) {
switch ((dsts & DSTS_ENUMSPD_MASK) >> DSTS_ENUMSPD_SHIFT) {
case DSTS_ENUMSPD_FS:
case DSTS_ENUMSPD_FS48:
hsotg->gadget.speed = USB_SPEED_FULL;
......@@ -2243,54 +2243,6 @@ static void dwc2_hsotg_irq_fifoempty(struct dwc2_hsotg *hsotg, bool periodic)
GINTSTS_PTXFEMP | \
GINTSTS_RXFLVL)
/**
* dwc2_hsotg_corereset - issue softreset to the core
* @hsotg: The device state
*
* Issue a soft reset to the core, and await the core finishing it.
*/
static int dwc2_hsotg_corereset(struct dwc2_hsotg *hsotg)
{
int timeout;
u32 grstctl;
dev_dbg(hsotg->dev, "resetting core\n");
/* issue soft reset */
dwc2_writel(GRSTCTL_CSFTRST, hsotg->regs + GRSTCTL);
timeout = 10000;
do {
grstctl = dwc2_readl(hsotg->regs + GRSTCTL);
} while ((grstctl & GRSTCTL_CSFTRST) && timeout-- > 0);
if (grstctl & GRSTCTL_CSFTRST) {
dev_err(hsotg->dev, "Failed to get CSftRst asserted\n");
return -EINVAL;
}
timeout = 10000;
while (1) {
u32 grstctl = dwc2_readl(hsotg->regs + GRSTCTL);
if (timeout-- < 0) {
dev_info(hsotg->dev,
"%s: reset failed, GRSTCTL=%08x\n",
__func__, grstctl);
return -ETIMEDOUT;
}
if (!(grstctl & GRSTCTL_AHBIDLE))
continue;
break; /* reset done */
}
dev_dbg(hsotg->dev, "reset successful\n");
return 0;
}
/**
* dwc2_hsotg_core_init - issue softreset to the core
* @hsotg: The device state
......@@ -2307,7 +2259,7 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
kill_all_requests(hsotg, hsotg->eps_out[0], -ECONNRESET);
if (!is_usb_reset)
if (dwc2_hsotg_corereset(hsotg))
if (dwc2_core_reset(hsotg))
return;
/*
......@@ -2585,7 +2537,7 @@ static irqreturn_t dwc2_hsotg_irq(int irq, void *pw)
if (gintsts & GINTSTS_GOUTNAKEFF) {
dev_info(hsotg->dev, "GOUTNakEff triggered\n");
dwc2_writel(DCTL_CGOUTNAK, hsotg->regs + DCTL);
__orr32(hsotg->regs + DCTL, DCTL_CGOUTNAK);
dwc2_hsotg_dump(hsotg);
}
......@@ -2593,7 +2545,7 @@ static irqreturn_t dwc2_hsotg_irq(int irq, void *pw)
if (gintsts & GINTSTS_GINNAKEFF) {
dev_info(hsotg->dev, "GINNakEff triggered\n");
dwc2_writel(DCTL_CGNPINNAK, hsotg->regs + DCTL);
__orr32(hsotg->regs + DCTL, DCTL_CGNPINNAK);
dwc2_hsotg_dump(hsotg);
}
......@@ -2911,15 +2863,15 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
"%s: timeout DIEPINT.NAKEFF\n", __func__);
} else {
/* Clear any pending nak effect interrupt */
dwc2_writel(GINTSTS_GINNAKEFF, hsotg->regs + GINTSTS);
dwc2_writel(GINTSTS_GOUTNAKEFF, hsotg->regs + GINTSTS);
__orr32(hsotg->regs + DCTL, DCTL_SGNPINNAK);
__orr32(hsotg->regs + DCTL, DCTL_SGOUTNAK);
/* Wait for global nak to take effect */
if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS,
GINTSTS_GINNAKEFF, 100))
GINTSTS_GOUTNAKEFF, 100))
dev_warn(hsotg->dev,
"%s: timeout GINTSTS.GINNAKEFF\n", __func__);
"%s: timeout GINTSTS.GOUTNAKEFF\n", __func__);
}
/* Disable ep */
......@@ -2944,7 +2896,7 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
/* TODO: Flush shared tx fifo */
} else {
/* Remove global NAKs */
__bic32(hsotg->regs + DCTL, DCTL_SGNPINNAK);
__bic32(hsotg->regs + DCTL, DCTL_SGOUTNAK);
}
}
......@@ -3403,8 +3355,8 @@ static int dwc2_hsotg_hw_cfg(struct dwc2_hsotg *hsotg)
/* check hardware configuration */
cfg = dwc2_readl(hsotg->regs + GHWCFG2);
hsotg->num_of_eps = (cfg >> GHWCFG2_NUM_DEV_EP_SHIFT) & 0xF;
hsotg->num_of_eps = hsotg->hw_params.num_dev_ep;
/* Add ep0 */
hsotg->num_of_eps++;
......@@ -3415,7 +3367,7 @@ static int dwc2_hsotg_hw_cfg(struct dwc2_hsotg *hsotg)
/* Same dwc2_hsotg_ep is used in both directions for ep0 */
hsotg->eps_out[0] = hsotg->eps_in[0];
cfg = dwc2_readl(hsotg->regs + GHWCFG1);
cfg = hsotg->hw_params.dev_ep_dirs;
for (i = 1, cfg >>= 2; i < hsotg->num_of_eps; i++, cfg >>= 2) {
ep_type = cfg & 3;
/* Direction in or both */
......@@ -3434,11 +3386,8 @@ static int dwc2_hsotg_hw_cfg(struct dwc2_hsotg *hsotg)
}
}
cfg = dwc2_readl(hsotg->regs + GHWCFG3);
hsotg->fifo_mem = (cfg >> GHWCFG3_DFIFO_DEPTH_SHIFT);
cfg = dwc2_readl(hsotg->regs + GHWCFG4);
hsotg->dedicated_fifos = (cfg >> GHWCFG4_DED_FIFO_SHIFT) & 1;
hsotg->fifo_mem = hsotg->hw_params.total_fifo_size;
hsotg->dedicated_fifos = hsotg->hw_params.en_multiple_tx_fifo;
dev_info(hsotg->dev, "EPs: %d, %s fifos, %d entries in SPRAM\n",
hsotg->num_of_eps,
......@@ -3563,6 +3512,17 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
memcpy(&hsotg->g_tx_fifo_sz[1], p_tx_fifo, sizeof(p_tx_fifo));
/* Device tree specific probe */
dwc2_hsotg_of_probe(hsotg);
/* Check against largest possible value. */
if (hsotg->g_np_g_tx_fifo_sz >
hsotg->hw_params.dev_nperio_tx_fifo_size) {
dev_warn(dev, "Specified GNPTXFDEP=%d > %d\n",
hsotg->g_np_g_tx_fifo_sz,
hsotg->hw_params.dev_nperio_tx_fifo_size);
hsotg->g_np_g_tx_fifo_sz =
hsotg->hw_params.dev_nperio_tx_fifo_size;
}
/* Dump fifo information */
dev_dbg(dev, "NonPeriodic TXFIFO size: %d\n",
hsotg->g_np_g_tx_fifo_sz);
......@@ -3579,31 +3539,12 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
else if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)
hsotg->op_state = OTG_STATE_B_PERIPHERAL;
/*
* Force Device mode before initialization.
* This allows correctly configuring fifo for device mode.
*/
__bic32(hsotg->regs + GUSBCFG, GUSBCFG_FORCEHOSTMODE);
__orr32(hsotg->regs + GUSBCFG, GUSBCFG_FORCEDEVMODE);
/*
* According to Synopsys databook, this sleep is needed for the force
* device mode to take effect.
*/
msleep(25);
dwc2_hsotg_corereset(hsotg);
ret = dwc2_hsotg_hw_cfg(hsotg);
if (ret) {
dev_err(hsotg->dev, "Hardware configuration failed: %d\n", ret);
return ret;
}
dwc2_hsotg_init(hsotg);
/* Switch back to default configuration */
__bic32(hsotg->regs + GUSBCFG, GUSBCFG_FORCEDEVMODE);
hsotg->ctrl_buff = devm_kzalloc(hsotg->dev,
DWC2_CTRL_BUFF_SIZE, GFP_KERNEL);
if (!hsotg->ctrl_buff) {
......
......@@ -267,16 +267,34 @@ static void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg)
}
}
/**
* dwc2_hcd_connect() - Handles connect of the HCD
*
* @hsotg: Pointer to struct dwc2_hsotg
*
* Must be called with interrupt disabled and spinlock held
*/
void dwc2_hcd_connect(struct dwc2_hsotg *hsotg)
{
if (hsotg->lx_state != DWC2_L0)
usb_hcd_resume_root_hub(hsotg->priv);
hsotg->flags.b.port_connect_status_change = 1;
hsotg->flags.b.port_connect_status = 1;
}
/**
* dwc2_hcd_disconnect() - Handles disconnect of the HCD
*
* @hsotg: Pointer to struct dwc2_hsotg
* @force: If true, we won't try to reconnect even if we see device connected.
*
* Must be called with interrupt disabled and spinlock held
*/
void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg)
void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force)
{
u32 intr;
u32 hprt0;
/* Set status flags for the hub driver */
hsotg->flags.b.port_connect_status_change = 1;
......@@ -315,6 +333,24 @@ void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg)
dwc2_hcd_cleanup_channels(hsotg);
dwc2_host_disconnect(hsotg);
/*
* Add an extra check here to see if we're actually connected but
* we don't have a detection interrupt pending. This can happen if:
* 1. hardware sees connect
* 2. hardware sees disconnect
* 3. hardware sees connect
* 4. dwc2_port_intr() - clears connect interrupt
* 5. dwc2_handle_common_intr() - calls here
*
* Without the extra check here we will end calling disconnect
* and won't get any future interrupts to handle the connect.
*/
if (!force) {
hprt0 = dwc2_readl(hsotg->regs + HPRT0);
if (!(hprt0 & HPRT0_CONNDET) && (hprt0 & HPRT0_CONNSTS))
dwc2_hcd_connect(hsotg);
}
}
/**
......@@ -881,8 +917,10 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
*/
chan->multi_count = dwc2_hb_mult(qh->maxp);
if (hsotg->core_params->dma_desc_enable > 0)
if (hsotg->core_params->dma_desc_enable > 0) {
chan->desc_list_addr = qh->desc_list_dma;
chan->desc_list_sz = qh->desc_list_sz;
}
dwc2_hc_init(hsotg, chan);
chan->qh = qh;
......@@ -1382,7 +1420,7 @@ static void dwc2_conn_id_status_change(struct work_struct *work)
dev_err(hsotg->dev,
"Connection id status change timed out\n");
hsotg->op_state = OTG_STATE_B_PERIPHERAL;
dwc2_core_init(hsotg, false, -1);
dwc2_core_init(hsotg, false);
dwc2_enable_global_interrupts(hsotg);
spin_lock_irqsave(&hsotg->lock, flags);
dwc2_hsotg_core_init_disconnected(hsotg, false);
......@@ -1405,7 +1443,7 @@ static void dwc2_conn_id_status_change(struct work_struct *work)
hsotg->op_state = OTG_STATE_A_HOST;
/* Initialize the Core for Host mode */
dwc2_core_init(hsotg, false, -1);
dwc2_core_init(hsotg, false);
dwc2_enable_global_interrupts(hsotg);
dwc2_hcd_start(hsotg);
}
......@@ -1734,6 +1772,28 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
port_status |= USB_PORT_STAT_TEST;
/* USB_PORT_FEAT_INDICATOR unsupported always 0 */
if (hsotg->core_params->dma_desc_fs_enable) {
/*
* Enable descriptor DMA only if a full speed
* device is connected.
*/
if (hsotg->new_connection &&
((port_status &
(USB_PORT_STAT_CONNECTION |
USB_PORT_STAT_HIGH_SPEED |
USB_PORT_STAT_LOW_SPEED)) ==
USB_PORT_STAT_CONNECTION)) {
u32 hcfg;
dev_info(hsotg->dev, "Enabling descriptor DMA mode\n");
hsotg->core_params->dma_desc_enable = 1;
hcfg = dwc2_readl(hsotg->regs + HCFG);
hcfg |= HCFG_DESCDMA;
dwc2_writel(hcfg, hsotg->regs + HCFG);
hsotg->new_connection = false;
}
}
dev_vdbg(hsotg->dev, "port_status=%08x\n", port_status);
*(__le32 *)buf = cpu_to_le32(port_status);
break;
......@@ -2298,13 +2358,19 @@ static void dwc2_hcd_reset_func(struct work_struct *work)
{
struct dwc2_hsotg *hsotg = container_of(work, struct dwc2_hsotg,
reset_work.work);
unsigned long flags;
u32 hprt0;
dev_dbg(hsotg->dev, "USB RESET function called\n");
spin_lock_irqsave(&hsotg->lock, flags);
hprt0 = dwc2_read_hprt0(hsotg);
hprt0 &= ~HPRT0_RST;
dwc2_writel(hprt0, hsotg->regs + HPRT0);
hsotg->flags.b.port_reset_change = 1;
spin_unlock_irqrestore(&hsotg->lock, flags);
}
/*
......@@ -2366,7 +2432,7 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd)
spin_lock_irqsave(&hsotg->lock, flags);
/* Ensure hcd is disconnected */
dwc2_hcd_disconnect(hsotg);
dwc2_hcd_disconnect(hsotg, true);
dwc2_hcd_stop(hsotg);
hsotg->lx_state = DWC2_L3;
hcd->state = HC_STATE_HALT;
......@@ -3054,7 +3120,7 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
dwc2_disable_global_interrupts(hsotg);
/* Initialize the DWC_otg core, and select the Phy type */
retval = dwc2_core_init(hsotg, true, irq);
retval = dwc2_core_init(hsotg, true);
if (retval)
goto error2;
......@@ -3122,6 +3188,47 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
if (!hsotg->status_buf)
goto error3;
/*
* Create kmem caches to handle descriptor buffers in descriptor
* DMA mode.
* Alignment must be set to 512 bytes.
*/
if (hsotg->core_params->dma_desc_enable ||
hsotg->core_params->dma_desc_fs_enable) {
hsotg->desc_gen_cache = kmem_cache_create("dwc2-gen-desc",
sizeof(struct dwc2_hcd_dma_desc) *
MAX_DMA_DESC_NUM_GENERIC, 512, SLAB_CACHE_DMA,
NULL);
if (!hsotg->desc_gen_cache) {
dev_err(hsotg->dev,
"unable to create dwc2 generic desc cache\n");
/*
* Disable descriptor dma mode since it will not be
* usable.
*/
hsotg->core_params->dma_desc_enable = 0;
hsotg->core_params->dma_desc_fs_enable = 0;
}
hsotg->desc_hsisoc_cache = kmem_cache_create("dwc2-hsisoc-desc",
sizeof(struct dwc2_hcd_dma_desc) *
MAX_DMA_DESC_NUM_HS_ISOC, 512, 0, NULL);
if (!hsotg->desc_hsisoc_cache) {
dev_err(hsotg->dev,
"unable to create dwc2 hs isoc desc cache\n");
kmem_cache_destroy(hsotg->desc_gen_cache);
/*
* Disable descriptor dma mode since it will not be
* usable.
*/
hsotg->core_params->dma_desc_enable = 0;
hsotg->core_params->dma_desc_fs_enable = 0;
}
}
hsotg->otg_port = 1;
hsotg->frame_list = NULL;
hsotg->frame_list_dma = 0;
......@@ -3145,7 +3252,7 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
*/
retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (retval < 0)
goto error3;
goto error4;
device_wakeup_enable(hcd->self.controller);
......@@ -3155,6 +3262,9 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
return 0;
error4:
kmem_cache_destroy(hsotg->desc_gen_cache);
kmem_cache_destroy(hsotg->desc_hsisoc_cache);
error3:
dwc2_hcd_release(hsotg);
error2:
......@@ -3195,6 +3305,10 @@ void dwc2_hcd_remove(struct dwc2_hsotg *hsotg)
usb_remove_hcd(hcd);
hsotg->priv = NULL;
kmem_cache_destroy(hsotg->desc_gen_cache);
kmem_cache_destroy(hsotg->desc_hsisoc_cache);
dwc2_hcd_release(hsotg);
usb_put_hcd(hcd);
......
......@@ -107,6 +107,7 @@ struct dwc2_qh;
* @qh: QH for the transfer being processed by this channel
* @hc_list_entry: For linking to list of host channels
* @desc_list_addr: Current QH's descriptor list DMA address
* @desc_list_sz: Current QH's descriptor list size
*
* This structure represents the state of a single host channel when acting in
* host mode. It contains the data items needed to transfer packets to an
......@@ -159,6 +160,7 @@ struct dwc2_host_chan {
struct dwc2_qh *qh;
struct list_head hc_list_entry;
dma_addr_t desc_list_addr;
u32 desc_list_sz;
};
struct dwc2_hcd_pipe_info {
......@@ -251,6 +253,7 @@ enum dwc2_transaction_type {
* schedule
* @desc_list: List of transfer descriptors
* @desc_list_dma: Physical address of desc_list
* @desc_list_sz: Size of descriptors list
* @n_bytes: Xfer Bytes array. Each element corresponds to a transfer
* descriptor and indicates original XferSize value for the
* descriptor
......@@ -284,6 +287,7 @@ struct dwc2_qh {
struct list_head qh_list_entry;
struct dwc2_hcd_dma_desc *desc_list;
dma_addr_t desc_list_dma;
u32 desc_list_sz;
u32 *n_bytes;
unsigned tt_buffer_dirty:1;
};
......@@ -340,6 +344,8 @@ struct dwc2_qtd {
u8 isoc_split_pos;
u16 isoc_frame_index;
u16 isoc_split_offset;
u16 isoc_td_last;
u16 isoc_td_first;
u32 ssplit_out_xfer_count;
u8 error_count;
u8 n_desc;
......@@ -377,18 +383,6 @@ static inline void disable_hc_int(struct dwc2_hsotg *hsotg, int chnum, u32 intr)
dwc2_writel(mask, hsotg->regs + HCINTMSK(chnum));
}
/*
* Returns the mode of operation, host or device
*/
static inline int dwc2_is_host_mode(struct dwc2_hsotg *hsotg)
{
return (dwc2_readl(hsotg->regs + GINTSTS) & GINTSTS_CURMODE_HOST) != 0;
}
static inline int dwc2_is_device_mode(struct dwc2_hsotg *hsotg)
{
return (dwc2_readl(hsotg->regs + GINTSTS) & GINTSTS_CURMODE_HOST) == 0;
}
/*
* Reads HPRT0 in preparation to modify. It keeps the WC bits 0 so that if they
* are read as 1, they won't clear when written back.
......@@ -534,6 +528,19 @@ static inline bool dbg_perio(void) { return false; }
/* Packet size for any kind of endpoint descriptor */
#define dwc2_max_packet(wmaxpacketsize) ((wmaxpacketsize) & 0x07ff)
/*
* Returns true if frame1 index is greater than frame2 index. The comparison
* is done modulo FRLISTEN_64_SIZE. This accounts for the rollover of the
* frame number when the max index frame number is reached.
*/
static inline bool dwc2_frame_idx_num_gt(u16 fr_idx1, u16 fr_idx2)
{
u16 diff = fr_idx1 - fr_idx2;
u16 sign = diff & (FRLISTEN_64_SIZE >> 1);
return diff && !sign;
}
/*
* Returns true if frame1 is less than or equal to frame2. The comparison is
* done modulo HFNUM_MAX_FRNUM. This accounts for the rollover of the
......
This diff is collapsed.
......@@ -122,6 +122,9 @@ static void dwc2_sof_intr(struct dwc2_hsotg *hsotg)
struct dwc2_qh *qh;
enum dwc2_transaction_type tr_type;
/* Clear interrupt */
dwc2_writel(GINTSTS_SOF, hsotg->regs + GINTSTS);
#ifdef DEBUG_SOF
dev_vdbg(hsotg->dev, "--Start of Frame Interrupt--\n");
#endif
......@@ -146,9 +149,6 @@ static void dwc2_sof_intr(struct dwc2_hsotg *hsotg)
tr_type = dwc2_hcd_select_transactions(hsotg);
if (tr_type != DWC2_TRANSACTION_NONE)
dwc2_hcd_queue_transactions(hsotg, tr_type);
/* Clear interrupt */
dwc2_writel(GINTSTS_SOF, hsotg->regs + GINTSTS);
}
/*
......@@ -312,6 +312,7 @@ static void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0,
if (do_reset) {
*hprt0_modify |= HPRT0_RST;
dwc2_writel(*hprt0_modify, hsotg->regs + HPRT0);
queue_delayed_work(hsotg->wq_otg, &hsotg->reset_work,
msecs_to_jiffies(60));
} else {
......@@ -347,15 +348,12 @@ static void dwc2_port_intr(struct dwc2_hsotg *hsotg)
* Set flag and clear if detected
*/
if (hprt0 & HPRT0_CONNDET) {
dwc2_writel(hprt0_modify | HPRT0_CONNDET, hsotg->regs + HPRT0);
dev_vdbg(hsotg->dev,
"--Port Interrupt HPRT0=0x%08x Port Connect Detected--\n",
hprt0);
if (hsotg->lx_state != DWC2_L0)
usb_hcd_resume_root_hub(hsotg->priv);
hsotg->flags.b.port_connect_status_change = 1;
hsotg->flags.b.port_connect_status = 1;
hprt0_modify |= HPRT0_CONNDET;
dwc2_hcd_connect(hsotg);
/*
* The Hub driver asserts a reset when it sees port connect
......@@ -368,27 +366,36 @@ static void dwc2_port_intr(struct dwc2_hsotg *hsotg)
* Clear if detected - Set internal flag if disabled
*/
if (hprt0 & HPRT0_ENACHG) {
dwc2_writel(hprt0_modify | HPRT0_ENACHG, hsotg->regs + HPRT0);
dev_vdbg(hsotg->dev,
" --Port Interrupt HPRT0=0x%08x Port Enable Changed (now %d)--\n",
hprt0, !!(hprt0 & HPRT0_ENA));
hprt0_modify |= HPRT0_ENACHG;
if (hprt0 & HPRT0_ENA)
if (hprt0 & HPRT0_ENA) {
hsotg->new_connection = true;
dwc2_hprt0_enable(hsotg, hprt0, &hprt0_modify);
else
} else {
hsotg->flags.b.port_enable_change = 1;
if (hsotg->core_params->dma_desc_fs_enable) {
u32 hcfg;
hsotg->core_params->dma_desc_enable = 0;
hsotg->new_connection = false;
hcfg = dwc2_readl(hsotg->regs + HCFG);
hcfg &= ~HCFG_DESCDMA;
dwc2_writel(hcfg, hsotg->regs + HCFG);
}
}
}
/* Overcurrent Change Interrupt */
if (hprt0 & HPRT0_OVRCURRCHG) {
dwc2_writel(hprt0_modify | HPRT0_OVRCURRCHG,
hsotg->regs + HPRT0);
dev_vdbg(hsotg->dev,
" --Port Interrupt HPRT0=0x%08x Port Overcurrent Changed--\n",
hprt0);
hsotg->flags.b.port_over_current_change = 1;
hprt0_modify |= HPRT0_OVRCURRCHG;
}
/* Clear Port Interrupts */
dwc2_writel(hprt0_modify, hsotg->regs + HPRT0);
}
/*
......
......@@ -232,7 +232,7 @@ struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
*/
void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
{
if (hsotg->core_params->dma_desc_enable > 0) {
if (qh->desc_list) {
dwc2_hcd_qh_free_ddma(hsotg, qh);
} else {
/* kfree(NULL) is safe */
......
......@@ -769,10 +769,6 @@
#define TSIZ_XFERSIZE_SHIFT 0
#define HCDMA(_ch) HSOTG_REG(0x0514 + 0x20 * (_ch))
#define HCDMA_DMA_ADDR_MASK (0x1fffff << 11)
#define HCDMA_DMA_ADDR_SHIFT 11
#define HCDMA_CTD_MASK (0xff << 3)
#define HCDMA_CTD_SHIFT 3
#define HCDMAB(_ch) HSOTG_REG(0x051c + 0x20 * (_ch))
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment