USB/PHY patches for 5.1-rc1

Here is the big USB/PHY driver pull request for 5.1-rc1.
 
 The usual set of gadget driver updates, phy driver updates (you will
 have a merge issue with Kconfig and Makefile), xhci updates, and typec
 additions.  Also included in here are a lot of small cleanups and fixes
 and driver updates where needed.
 
 All of these have been in linux-next for a while with no reported
 issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCXH+hsw8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ynfNwCgqKKg+MxJ9pFjrwfWYOrbk+BBe2UAn2Elp4ia
 8FTdneQfN2J8Hhc6KGXE
 =Kx9I
 -----END PGP SIGNATURE-----

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

Pull USB/PHY updates from Greg KH:
 "Here is the big USB/PHY driver pull request for 5.1-rc1.

  The usual set of gadget driver updates, phy driver updates, xhci
  updates, and typec additions. Also included in here are a lot of small
  cleanups and fixes and driver updates where needed.

  All of these have been in linux-next for a while with no reported
  issues"

* tag 'usb-5.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (167 commits)
  wusb: Remove unnecessary static function ckhdid_printf
  usb: core: make default autosuspend delay configurable
  usb: core: Fix typo in description of "authorized_default"
  usb: chipidea: Refactor USB PHY selection and keep a single PHY
  usb: chipidea: Grab the (legacy) USB PHY by phandle first
  usb: chipidea: imx: set power polarity
  dt-bindings: usb: ci-hdrc-usb2: add property power-active-high
  usb: chipidea: imx: remove unused header files
  usb: chipidea: tegra: Fix missed ci_hdrc_remove_device()
  usb: core: add option of only authorizing internal devices
  usb: typec: tps6598x: handle block writes separately with plain-I2C adapters
  usb: xhci: Fix for Enabling USB ROLE SWITCH QUIRK on INTEL_SUNRISEPOINT_LP_XHCI
  usb: xhci: fix build warning - missing prototype
  usb: xhci: dbc: Fixing typo error.
  usb: xhci: remove unused member 'parent' in xhci_regset struct
  xhci: tegra: Prevent error pointer dereference
  USB: serial: option: add Telit ME910 ECM composition
  usb: core: Replace hardcoded check with inline function from usb.h
  usb: core: skip interfaces disabled in devicetree
  usb: typec: mux: remove redundant check on variable match
  ...
This commit is contained in:
Linus Torvalds 2019-03-06 16:48:27 -08:00
commit f90d64483e
203 changed files with 2952 additions and 1111 deletions

View File

@ -186,7 +186,7 @@ Contact: Lan Tianyu <tianyu.lan@intel.com>
Description: Description:
Some platforms provide usb port connect types through ACPI. Some platforms provide usb port connect types through ACPI.
This attribute is to expose these information to user space. This attribute is to expose these information to user space.
The file will read "hotplug", "wired" and "not used" if the The file will read "hotplug", "hardwired" and "not used" if the
information is available, and "unknown" otherwise. information is available, and "unknown" otherwise.
What: /sys/bus/usb/devices/.../(hub interface)/portX/location What: /sys/bus/usb/devices/.../(hub interface)/portX/location

View File

@ -4718,7 +4718,8 @@
usbcore.authorized_default= usbcore.authorized_default=
[USB] Default USB device authorization: [USB] Default USB device authorization:
(default -1 = authorized except for wireless USB, (default -1 = authorized except for wireless USB,
0 = not authorized, 1 = authorized) 0 = not authorized, 1 = authorized, 2 = authorized
if device connected to internal port)
usbcore.autosuspend= usbcore.autosuspend=
[USB] The autosuspend time delay (in seconds) used [USB] The autosuspend time delay (in seconds) used

View File

@ -147,6 +147,7 @@ required properties:
- compatible: Should be "atmel,<chip>-sfr", "syscon" or - compatible: Should be "atmel,<chip>-sfr", "syscon" or
"atmel,<chip>-sfrbu", "syscon" "atmel,<chip>-sfrbu", "syscon"
<chip> can be "sama5d3", "sama5d4" or "sama5d2". <chip> can be "sama5d3", "sama5d4" or "sama5d2".
It also can be "microchip,sam9x60-sfr", "syscon".
- reg: Should contain registers location and length - reg: Should contain registers location and length
sfr@f0038000 { sfr@f0038000 {

View File

@ -31,28 +31,7 @@ Required subnodes:
- one subnode per DSI device connected on the DSI bus. Each DSI device should - one subnode per DSI device connected on the DSI bus. Each DSI device should
contain a reg property encoding its virtual channel. contain a reg property encoding its virtual channel.
Cadence DPHY
============
Cadence DPHY block.
Required properties:
- compatible: should be set to "cdns,dphy".
- reg: physical base address and length of the DPHY registers.
- clocks: DPHY reference clocks.
- clock-names: must contain "psm" and "pll_ref".
- #phy-cells: must be set to 0.
Example: Example:
dphy0: dphy@fd0e0000{
compatible = "cdns,dphy";
reg = <0x0 0xfd0e0000 0x0 0x1000>;
clocks = <&psm_clk>, <&pll_ref_clk>;
clock-names = "psm", "pll_ref";
#phy-cells = <0>;
};
dsi0: dsi@fd0c0000 { dsi0: dsi@fd0c0000 {
compatible = "cdns,dsi"; compatible = "cdns,dsi";
reg = <0x0 0xfd0c0000 0x0 0x1000>; reg = <0x0 0xfd0c0000 0x0 0x1000>;

View File

@ -0,0 +1,20 @@
Cadence DPHY
============
Cadence DPHY block.
Required properties:
- compatible: should be set to "cdns,dphy".
- reg: physical base address and length of the DPHY registers.
- clocks: DPHY reference clocks.
- clock-names: must contain "psm" and "pll_ref".
- #phy-cells: must be set to 0.
Example:
dphy0: dphy@fd0e0000{
compatible = "cdns,dphy";
reg = <0x0 0xfd0e0000 0x0 0x1000>;
clocks = <&psm_clk>, <&pll_ref_clk>;
clock-names = "psm", "pll_ref";
#phy-cells = <0>;
};

View File

@ -1,16 +1,27 @@
mvebu comphy driver MVEBU comphy drivers
------------------- --------------------
A comphy controller can be found on Marvell Armada 7k/8k on the CP110. It COMPHY controllers can be found on the following Marvell MVEBU SoCs:
provides a number of shared PHYs used by various interfaces (network, sata, * Armada 7k/8k (on the CP110)
usb, PCIe...). * Armada 3700
It provides a number of shared PHYs used by various interfaces (network, SATA,
USB, PCIe...).
Required properties: Required properties:
- compatible: should be "marvell,comphy-cp110" - compatible: should be one of:
- reg: should contain the comphy register location and length. * "marvell,comphy-cp110" for Armada 7k/8k
- marvell,system-controller: should contain a phandle to the * "marvell,comphy-a3700" for Armada 3700
system controller node. - reg: should contain the COMPHY register(s) location(s) and length(s).
* 1 entry for Armada 7k/8k
* 4 entries for Armada 3700 along with the corresponding reg-names
properties, memory areas are:
* Generic COMPHY registers
* Lane 1 (PCIe/GbE)
* Lane 0 (USB3/GbE)
* Lane 2 (SATA/USB3)
- marvell,system-controller: should contain a phandle to the system
controller node (only for Armada 7k/8k)
- #address-cells: should be 1. - #address-cells: should be 1.
- #size-cells: should be 0. - #size-cells: should be 0.
@ -18,11 +29,11 @@ A sub-node is required for each comphy lane provided by the comphy.
Required properties (child nodes): Required properties (child nodes):
- reg: comphy lane number. - reg: COMPHY lane number.
- #phy-cells : from the generic phy bindings, must be 1. Defines the - #phy-cells : from the generic PHY bindings, must be 1. Defines the
input port to use for a given comphy lane. input port to use for a given comphy lane.
Example: Examples:
cpm_comphy: phy@120000 { cpm_comphy: phy@120000 {
compatible = "marvell,comphy-cp110"; compatible = "marvell,comphy-cp110";
@ -41,3 +52,33 @@ Example:
#phy-cells = <1>; #phy-cells = <1>;
}; };
}; };
comphy: phy@18300 {
compatible = "marvell,comphy-a3700";
reg = <0x18300 0x300>,
<0x1F000 0x400>,
<0x5C000 0x400>,
<0xe0178 0x8>;
reg-names = "comphy",
"lane1_pcie_gbe",
"lane0_usb3_gbe",
"lane2_sata_usb3";
#address-cells = <1>;
#size-cells = <0>;
comphy0: phy@0 {
reg = <0>;
#phy-cells = <1>;
};
comphy1: phy@1 {
reg = <1>;
#phy-cells = <1>;
};
comphy2: phy@2 {
reg = <2>;
#phy-cells = <1>;
};
};

View File

@ -0,0 +1,38 @@
MVEBU A3700 UTMI PHY
--------------------
USB2 UTMI+ PHY controllers can be found on the following Marvell MVEBU SoCs:
* Armada 3700
On Armada 3700, there are two USB controllers, one is compatible with the USB2
and USB3 specifications and supports OTG. The other one is USB2 compliant and
only supports host mode. Both of these controllers come with a slightly
different UTMI PHY.
Required Properties:
- compatible: Should be one of:
* "marvell,a3700-utmi-host-phy" for the PHY connected to
the USB2 host-only controller.
* "marvell,a3700-utmi-otg-phy" for the PHY connected to
the USB3 and USB2 OTG capable controller.
- reg: PHY IP register range.
- marvell,usb-misc-reg: handle on the "USB miscellaneous registers" shared
region covering registers related to both the host
controller and the PHY.
- #phy-cells: Standard property (Documentation: phy-bindings.txt) Should be 0.
Example:
usb2_utmi_host_phy: phy@5f000 {
compatible = "marvell,armada-3700-utmi-host-phy";
reg = <0x5f000 0x800>;
marvell,usb-misc-reg = <&usb2_syscon>;
#phy-cells = <0>;
};
usb2_syscon: system-controller@5f800 {
compatible = "marvell,armada-3700-usb2-host-misc", "syscon";
reg = <0x5f800 0x800>;
};

View File

@ -23,6 +23,8 @@ Optional properties:
register files". When set driver will request its register files". When set driver will request its
phandle as one companion-grf for some special SoCs phandle as one companion-grf for some special SoCs
(e.g RV1108). (e.g RV1108).
- extcon : phandle to the extcon device providing the cable state for
the otg phy.
Required nodes : a sub-node is required for each port the phy provides. Required nodes : a sub-node is required for each port the phy provides.
The sub-node name is used to identify host or otg port, The sub-node name is used to identify host or otg port,

View File

@ -9,6 +9,8 @@ Required properties:
"qcom,ipq8074-qmp-pcie-phy" for PCIe phy on IPQ8074 "qcom,ipq8074-qmp-pcie-phy" for PCIe phy on IPQ8074
"qcom,msm8996-qmp-pcie-phy" for 14nm PCIe phy on msm8996, "qcom,msm8996-qmp-pcie-phy" for 14nm PCIe phy on msm8996,
"qcom,msm8996-qmp-usb3-phy" for 14nm USB3 phy on msm8996, "qcom,msm8996-qmp-usb3-phy" for 14nm USB3 phy on msm8996,
"qcom,msm8998-qmp-usb3-phy" for USB3 QMP V3 phy on msm8998,
"qcom,msm8998-qmp-ufs-phy" for UFS QMP phy on msm8998,
"qcom,sdm845-qmp-usb3-phy" for USB3 QMP V3 phy on sdm845, "qcom,sdm845-qmp-usb3-phy" for USB3 QMP V3 phy on sdm845,
"qcom,sdm845-qmp-usb3-uni-phy" for USB3 QMP V3 UNI phy on sdm845, "qcom,sdm845-qmp-usb3-uni-phy" for USB3 QMP V3 UNI phy on sdm845,
"qcom,sdm845-qmp-ufs-phy" for UFS QMP phy on sdm845. "qcom,sdm845-qmp-ufs-phy" for UFS QMP phy on sdm845.
@ -42,6 +44,10 @@ Required properties:
"aux", "cfg_ahb", "ref". "aux", "cfg_ahb", "ref".
For "qcom,msm8996-qmp-usb3-phy" must contain: For "qcom,msm8996-qmp-usb3-phy" must contain:
"aux", "cfg_ahb", "ref". "aux", "cfg_ahb", "ref".
For "qcom,msm8998-qmp-usb3-phy" must contain:
"aux", "cfg_ahb", "ref".
For "qcom,msm8998-qmp-ufs-phy" must contain:
"ref", "ref_aux".
For "qcom,sdm845-qmp-usb3-phy" must contain: For "qcom,sdm845-qmp-usb3-phy" must contain:
"aux", "cfg_ahb", "ref", "com_aux". "aux", "cfg_ahb", "ref", "com_aux".
For "qcom,sdm845-qmp-usb3-uni-phy" must contain: For "qcom,sdm845-qmp-usb3-uni-phy" must contain:
@ -61,6 +67,9 @@ Required properties:
"phy", "common", "cfg". "phy", "common", "cfg".
For "qcom,msm8996-qmp-usb3-phy" must contain For "qcom,msm8996-qmp-usb3-phy" must contain
"phy", "common". "phy", "common".
For "qcom,msm8998-qmp-usb3-phy" must contain
"phy", "common".
For "qcom,msm8998-qmp-ufs-phy": no resets are listed.
For "qcom,sdm845-qmp-usb3-phy" must contain: For "qcom,sdm845-qmp-usb3-phy" must contain:
"phy", "common". "phy", "common".
For "qcom,sdm845-qmp-usb3-uni-phy" must contain: For "qcom,sdm845-qmp-usb3-uni-phy" must contain:

View File

@ -6,6 +6,7 @@ QUSB2 controller supports LS/FS/HS usb connectivity on Qualcomm chipsets.
Required properties: Required properties:
- compatible: compatible list, contains - compatible: compatible list, contains
"qcom,msm8996-qusb2-phy" for 14nm PHY on msm8996, "qcom,msm8996-qusb2-phy" for 14nm PHY on msm8996,
"qcom,msm8998-qusb2-phy" for 10nm PHY on msm8998,
"qcom,sdm845-qusb2-phy" for 10nm PHY on sdm845. "qcom,sdm845-qusb2-phy" for 10nm PHY on sdm845.
- reg: offset and length of the PHY register set. - reg: offset and length of the PHY register set.

View File

@ -5,6 +5,8 @@ This file provides information on what the device node for the R-Car generation
Required properties: Required properties:
- compatible: "renesas,usb2-phy-r8a774a1" if the device is a part of an R8A774A1 - compatible: "renesas,usb2-phy-r8a774a1" if the device is a part of an R8A774A1
SoC.
"renesas,usb2-phy-r8a774c0" if the device is a part of an R8A774C0
SoC. SoC.
"renesas,usb2-phy-r8a7795" if the device is a part of an R8A7795 "renesas,usb2-phy-r8a7795" if the device is a part of an R8A7795
SoC. SoC.

View File

@ -35,6 +35,7 @@ Required properties:
DRA7x DRA7x
Should be "ti,dra7x-usb2-phy2" for the 2nd instance of USB2 PHY Should be "ti,dra7x-usb2-phy2" for the 2nd instance of USB2 PHY
in DRA7x in DRA7x
Should be "ti,am654-usb2" for the USB2 PHYs on AM654.
- reg : Address and length of the register set for the device. - reg : Address and length of the register set for the device.
- #phy-cells: determine the number of cells that should be given in the - #phy-cells: determine the number of cells that should be given in the
phandle while referencing this phy. phandle while referencing this phy.

View File

@ -93,6 +93,7 @@ i.mx specific properties
- over-current-active-low: over current signal polarity is active low. - over-current-active-low: over current signal polarity is active low.
- over-current-active-high: over current signal polarity is active high. - over-current-active-high: over current signal polarity is active high.
It's recommended to specify the over current polarity. It's recommended to specify the over current polarity.
- power-active-high: power signal polarity is active high
- external-vbus-divider: enables off-chip resistor divider for Vbus - external-vbus-divider: enables off-chip resistor divider for Vbus
Example: Example:

View File

@ -0,0 +1,24 @@
Ingenic JZ4740 MUSB driver
Required properties:
- compatible: Must be "ingenic,jz4740-musb"
- reg: Address range of the UDC register set
- interrupts: IRQ number related to the UDC hardware
- interrupt-names: must be "mc"
- clocks: phandle to the "udc" clock
- clock-names: must be "udc"
Example:
udc: usb@13040000 {
compatible = "ingenic,jz4740-musb";
reg = <0x13040000 0x10000>;
interrupt-parent = <&intc>;
interrupts = <24>;
interrupt-names = "mc";
clocks = <&cgu JZ4740_CLK_UDC>;
clock-names = "udc";
};

View File

@ -3,7 +3,9 @@ TI Keystone Soc USB Controller
DWC3 GLUE DWC3 GLUE
Required properties: Required properties:
- compatible: should be "ti,keystone-dwc3". - compatible: should be
"ti,keystone-dwc3" for Keystone 2 SoCs
"ti,am654-dwc3" for AM654 SoC
- #address-cells, #size-cells : should be '1' if the device has sub-nodes - #address-cells, #size-cells : should be '1' if the device has sub-nodes
with 'reg' property. with 'reg' property.
- reg : Address and length of the register set for the USB subsystem on - reg : Address and length of the register set for the USB subsystem on
@ -21,7 +23,7 @@ SoCs only:
- clock-names: Must be "usb". - clock-names: Must be "usb".
The following are mandatory properties for Keystone 2 66AK2G SoCs only: The following are mandatory properties for 66AK2G and AM654:
- power-domains: Should contain a phandle to a PM domain provider node - power-domains: Should contain a phandle to a PM domain provider node
and an args specifier containing the USB device id and an args specifier containing the USB device id

View File

@ -4,6 +4,7 @@ Required properties:
- compatible: Compatible list, contains - compatible: Compatible list, contains
"qcom,dwc3" "qcom,dwc3"
"qcom,msm8996-dwc3" for msm8996 SOC. "qcom,msm8996-dwc3" for msm8996 SOC.
"qcom,msm8998-dwc3" for msm8998 SOC.
"qcom,sdm845-dwc3" for sdm845 SOC. "qcom,sdm845-dwc3" for sdm845 SOC.
- reg: Offset and length of register set for QSCRATCH wrapper - reg: Offset and length of register set for QSCRATCH wrapper
- power-domains: specifies a phandle to PM domain provider node - power-domains: specifies a phandle to PM domain provider node

View File

@ -3,6 +3,7 @@ Renesas Electronics USB3.0 Peripheral driver
Required properties: Required properties:
- compatible: Must contain one of the following: - compatible: Must contain one of the following:
- "renesas,r8a774a1-usb3-peri" - "renesas,r8a774a1-usb3-peri"
- "renesas,r8a774c0-usb3-peri"
- "renesas,r8a7795-usb3-peri" - "renesas,r8a7795-usb3-peri"
- "renesas,r8a7796-usb3-peri" - "renesas,r8a7796-usb3-peri"
- "renesas,r8a77965-usb3-peri" - "renesas,r8a77965-usb3-peri"

View File

@ -7,6 +7,7 @@ Required properties:
- "renesas,usbhs-r8a7744" for r8a7744 (RZ/G1N) compatible device - "renesas,usbhs-r8a7744" for r8a7744 (RZ/G1N) compatible device
- "renesas,usbhs-r8a7745" for r8a7745 (RZ/G1E) compatible device - "renesas,usbhs-r8a7745" for r8a7745 (RZ/G1E) compatible device
- "renesas,usbhs-r8a774a1" for r8a774a1 (RZ/G2M) compatible device - "renesas,usbhs-r8a774a1" for r8a774a1 (RZ/G2M) compatible device
- "renesas,usbhs-r8a774c0" for r8a774c0 (RZ/G2E) compatible device
- "renesas,usbhs-r8a7790" for r8a7790 (R-Car H2) compatible device - "renesas,usbhs-r8a7790" for r8a7790 (R-Car H2) compatible device
- "renesas,usbhs-r8a7791" for r8a7791 (R-Car M2-W) 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-r8a7792" for r8a7792 (R-Car V2H) compatible device

View File

@ -64,6 +64,8 @@ Optional properties :
- power-on-time-ms : Specifies the time it takes from the time the host - power-on-time-ms : Specifies the time it takes from the time the host
initiates the power-on sequence to a port until the port has adequate initiates the power-on sequence to a port until the port has adequate
power. The value is given in ms in a 0 - 510 range (default is 100ms). power. The value is given in ms in a 0 - 510 range (default is 100ms).
- swap-dx-lanes : Specifies the ports which will swap the differential-pair
(D+/D-), default is not-swapped.
Examples: Examples:
usb2512b@2c { usb2512b@2c {
@ -81,4 +83,6 @@ Examples:
manufacturer = "Foo"; manufacturer = "Foo";
product = "Foo-Bar"; product = "Foo-Bar";
serial = "1234567890A"; serial = "1234567890A";
/* correct misplaced usb connectors on port 1,2 */
swap-dx-lanes = <1 2>;
}; };

View File

@ -34,7 +34,9 @@ $ echo 1 > /sys/bus/usb/devices/usbX/authorized_default
By default, Wired USB devices are authorized by default to By default, Wired USB devices are authorized by default to
connect. Wireless USB hosts deauthorize by default all new connected connect. Wireless USB hosts deauthorize by default all new connected
devices (this is so because we need to do an authentication phase devices (this is so because we need to do an authentication phase
before authorizing). before authorizing). Writing "2" to the authorized_default attribute
causes kernel to only authorize by default devices connected to internal
USB ports.
Example system lockdown (lame) Example system lockdown (lame)

View File

@ -9178,6 +9178,14 @@ F: drivers/gpu/drm/armada/
F: include/uapi/drm/armada_drm.h F: include/uapi/drm/armada_drm.h
F: Documentation/devicetree/bindings/display/armada/ F: Documentation/devicetree/bindings/display/armada/
MARVELL ARMADA 3700 PHY DRIVERS
M: Miquel Raynal <miquel.raynal@bootlin.com>
S: Maintained
F: drivers/phy/marvell/phy-mvebu-a3700-comphy.c
F: drivers/phy/marvell/phy-mvebu-a3700-utmi.c
F: Documentation/devicetree/bindings/phy/phy-mvebu-comphy.txt
F: Documentation/devicetree/bindings/phy/phy-mvebu-utmi.txt
MARVELL CRYPTO DRIVER MARVELL CRYPTO DRIVER
M: Boris Brezillon <bbrezillon@kernel.org> M: Boris Brezillon <bbrezillon@kernel.org>
M: Arnaud Ebalard <arno@natisbad.org> M: Arnaud Ebalard <arno@natisbad.org>

View File

@ -7,10 +7,37 @@
*/ */
#include <linux/device.h> #include <linux/device.h>
#include <linux/property.h>
static DEFINE_MUTEX(devcon_lock); static DEFINE_MUTEX(devcon_lock);
static LIST_HEAD(devcon_list); static LIST_HEAD(devcon_list);
typedef void *(*devcon_match_fn_t)(struct device_connection *con, int ep,
void *data);
static void *
fwnode_graph_devcon_match(struct fwnode_handle *fwnode, const char *con_id,
void *data, devcon_match_fn_t match)
{
struct device_connection con = { .id = con_id };
struct fwnode_handle *ep;
void *ret;
fwnode_graph_for_each_endpoint(fwnode, ep) {
con.fwnode = fwnode_graph_get_remote_port_parent(ep);
if (!fwnode_device_is_available(con.fwnode))
continue;
ret = match(&con, -1, data);
fwnode_handle_put(con.fwnode);
if (ret) {
fwnode_handle_put(ep);
return ret;
}
}
return NULL;
}
/** /**
* device_connection_find_match - Find physical connection to a device * device_connection_find_match - Find physical connection to a device
* @dev: Device with the connection * @dev: Device with the connection
@ -23,10 +50,9 @@ static LIST_HEAD(devcon_list);
* caller is expecting to be returned. * caller is expecting to be returned.
*/ */
void *device_connection_find_match(struct device *dev, const char *con_id, void *device_connection_find_match(struct device *dev, const char *con_id,
void *data, void *data, devcon_match_fn_t match)
void *(*match)(struct device_connection *con,
int ep, void *data))
{ {
struct fwnode_handle *fwnode = dev_fwnode(dev);
const char *devname = dev_name(dev); const char *devname = dev_name(dev);
struct device_connection *con; struct device_connection *con;
void *ret = NULL; void *ret = NULL;
@ -35,6 +61,12 @@ void *device_connection_find_match(struct device *dev, const char *con_id,
if (!match) if (!match)
return NULL; return NULL;
if (fwnode) {
ret = fwnode_graph_devcon_match(fwnode, con_id, data, match);
if (ret)
return ret;
}
mutex_lock(&devcon_lock); mutex_lock(&devcon_lock);
list_for_each_entry(con, &devcon_list, list) { list_for_each_entry(con, &devcon_list, list) {
@ -75,12 +107,36 @@ static struct bus_type *generic_match_buses[] = {
NULL, NULL,
}; };
static int device_fwnode_match(struct device *dev, void *fwnode)
{
return dev_fwnode(dev) == fwnode;
}
static void *device_connection_fwnode_match(struct device_connection *con)
{
struct bus_type *bus;
struct device *dev;
for (bus = generic_match_buses[0]; bus; bus++) {
dev = bus_find_device(bus, NULL, (void *)con->fwnode,
device_fwnode_match);
if (dev && !strncmp(dev_name(dev), con->id, strlen(con->id)))
return dev;
put_device(dev);
}
return NULL;
}
/* This tries to find the device from the most common bus types by name. */ /* This tries to find the device from the most common bus types by name. */
static void *generic_match(struct device_connection *con, int ep, void *data) static void *generic_match(struct device_connection *con, int ep, void *data)
{ {
struct bus_type *bus; struct bus_type *bus;
struct device *dev; struct device *dev;
if (con->fwnode)
return device_connection_fwnode_match(con);
for (bus = generic_match_buses[0]; bus; bus++) { for (bus = generic_match_buses[0]; bus; bus++) {
dev = bus_find_device_by_name(bus, NULL, con->endpoint[ep]); dev = bus_find_device_by_name(bus, NULL, con->endpoint[ep]);
if (dev) if (dev)

View File

@ -78,8 +78,8 @@ struct sr_pcie_phy_core {
static const u8 pipemux_table[] = { static const u8 pipemux_table[] = {
/* PIPEMUX = 0, EP 1x16 */ /* PIPEMUX = 0, EP 1x16 */
0x00, 0x00,
/* PIPEMUX = 1, EP 2x8 */ /* PIPEMUX = 1, EP 1x8 + RC 1x8, core 7 */
0x00, 0x80,
/* PIPEMUX = 2, EP 4x4 */ /* PIPEMUX = 2, EP 4x4 */
0x00, 0x00,
/* PIPEMUX = 3, RC 2x8, cores 0, 7 */ /* PIPEMUX = 3, RC 2x8, cores 0, 7 */

View File

@ -1,6 +1,7 @@
# #
# Phy drivers for Cadence PHYs # Phy drivers for Cadence PHYs
# #
config PHY_CADENCE_DP config PHY_CADENCE_DP
tristate "Cadence MHDP DisplayPort PHY driver" tristate "Cadence MHDP DisplayPort PHY driver"
depends on OF depends on OF
@ -9,9 +10,19 @@ config PHY_CADENCE_DP
help help
Support for Cadence MHDP DisplayPort PHY. Support for Cadence MHDP DisplayPort PHY.
config PHY_CADENCE_DPHY
tristate "Cadence D-PHY Support"
depends on HAS_IOMEM && OF
select GENERIC_PHY
select GENERIC_PHY_MIPI_DPHY
help
Choose this option if you have a Cadence D-PHY in your
system. If M is selected, the module will be called
cdns-dphy.
config PHY_CADENCE_SIERRA config PHY_CADENCE_SIERRA
tristate "Cadence Sierra PHY Driver" tristate "Cadence Sierra PHY Driver"
depends on OF && HAS_IOMEM && RESET_CONTROLLER depends on OF && HAS_IOMEM && RESET_CONTROLLER
select GENERIC_PHY select GENERIC_PHY
help help
Enable this to support the Cadence Sierra PHY driver Enable this to support the Cadence Sierra PHY driver

View File

@ -1,2 +1,3 @@
obj-$(CONFIG_PHY_CADENCE_DP) += phy-cadence-dp.o obj-$(CONFIG_PHY_CADENCE_DP) += phy-cadence-dp.o
obj-$(CONFIG_PHY_CADENCE_DPHY) += cdns-dphy.o
obj-$(CONFIG_PHY_CADENCE_SIERRA) += phy-cadence-sierra.o obj-$(CONFIG_PHY_CADENCE_SIERRA) += phy-cadence-sierra.o

View File

@ -0,0 +1,391 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright: 2017-2018 Cadence Design Systems, Inc.
*/
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <linux/phy/phy.h>
#include <linux/phy/phy-mipi-dphy.h>
#define REG_WAKEUP_TIME_NS 800
#define DPHY_PLL_RATE_HZ 108000000
/* DPHY registers */
#define DPHY_PMA_CMN(reg) (reg)
#define DPHY_PMA_LCLK(reg) (0x100 + (reg))
#define DPHY_PMA_LDATA(lane, reg) (0x200 + ((lane) * 0x100) + (reg))
#define DPHY_PMA_RCLK(reg) (0x600 + (reg))
#define DPHY_PMA_RDATA(lane, reg) (0x700 + ((lane) * 0x100) + (reg))
#define DPHY_PCS(reg) (0xb00 + (reg))
#define DPHY_CMN_SSM DPHY_PMA_CMN(0x20)
#define DPHY_CMN_SSM_EN BIT(0)
#define DPHY_CMN_TX_MODE_EN BIT(9)
#define DPHY_CMN_PWM DPHY_PMA_CMN(0x40)
#define DPHY_CMN_PWM_DIV(x) ((x) << 20)
#define DPHY_CMN_PWM_LOW(x) ((x) << 10)
#define DPHY_CMN_PWM_HIGH(x) (x)
#define DPHY_CMN_FBDIV DPHY_PMA_CMN(0x4c)
#define DPHY_CMN_FBDIV_VAL(low, high) (((high) << 11) | ((low) << 22))
#define DPHY_CMN_FBDIV_FROM_REG (BIT(10) | BIT(21))
#define DPHY_CMN_OPIPDIV DPHY_PMA_CMN(0x50)
#define DPHY_CMN_IPDIV_FROM_REG BIT(0)
#define DPHY_CMN_IPDIV(x) ((x) << 1)
#define DPHY_CMN_OPDIV_FROM_REG BIT(6)
#define DPHY_CMN_OPDIV(x) ((x) << 7)
#define DPHY_PSM_CFG DPHY_PCS(0x4)
#define DPHY_PSM_CFG_FROM_REG BIT(0)
#define DPHY_PSM_CLK_DIV(x) ((x) << 1)
#define DSI_HBP_FRAME_OVERHEAD 12
#define DSI_HSA_FRAME_OVERHEAD 14
#define DSI_HFP_FRAME_OVERHEAD 6
#define DSI_HSS_VSS_VSE_FRAME_OVERHEAD 4
#define DSI_BLANKING_FRAME_OVERHEAD 6
#define DSI_NULL_FRAME_OVERHEAD 6
#define DSI_EOT_PKT_SIZE 4
struct cdns_dphy_cfg {
u8 pll_ipdiv;
u8 pll_opdiv;
u16 pll_fbdiv;
unsigned int nlanes;
};
enum cdns_dphy_clk_lane_cfg {
DPHY_CLK_CFG_LEFT_DRIVES_ALL = 0,
DPHY_CLK_CFG_LEFT_DRIVES_RIGHT = 1,
DPHY_CLK_CFG_LEFT_DRIVES_LEFT = 2,
DPHY_CLK_CFG_RIGHT_DRIVES_ALL = 3,
};
struct cdns_dphy;
struct cdns_dphy_ops {
int (*probe)(struct cdns_dphy *dphy);
void (*remove)(struct cdns_dphy *dphy);
void (*set_psm_div)(struct cdns_dphy *dphy, u8 div);
void (*set_clk_lane_cfg)(struct cdns_dphy *dphy,
enum cdns_dphy_clk_lane_cfg cfg);
void (*set_pll_cfg)(struct cdns_dphy *dphy,
const struct cdns_dphy_cfg *cfg);
unsigned long (*get_wakeup_time_ns)(struct cdns_dphy *dphy);
};
struct cdns_dphy {
struct cdns_dphy_cfg cfg;
void __iomem *regs;
struct clk *psm_clk;
struct clk *pll_ref_clk;
const struct cdns_dphy_ops *ops;
struct phy *phy;
};
static int cdns_dsi_get_dphy_pll_cfg(struct cdns_dphy *dphy,
struct cdns_dphy_cfg *cfg,
struct phy_configure_opts_mipi_dphy *opts,
unsigned int *dsi_hfp_ext)
{
unsigned long pll_ref_hz = clk_get_rate(dphy->pll_ref_clk);
u64 dlane_bps;
memset(cfg, 0, sizeof(*cfg));
if (pll_ref_hz < 9600000 || pll_ref_hz >= 150000000)
return -EINVAL;
else if (pll_ref_hz < 19200000)
cfg->pll_ipdiv = 1;
else if (pll_ref_hz < 38400000)
cfg->pll_ipdiv = 2;
else if (pll_ref_hz < 76800000)
cfg->pll_ipdiv = 4;
else
cfg->pll_ipdiv = 8;
dlane_bps = opts->hs_clk_rate;
if (dlane_bps > 2500000000UL || dlane_bps < 160000000UL)
return -EINVAL;
else if (dlane_bps >= 1250000000)
cfg->pll_opdiv = 1;
else if (dlane_bps >= 630000000)
cfg->pll_opdiv = 2;
else if (dlane_bps >= 320000000)
cfg->pll_opdiv = 4;
else if (dlane_bps >= 160000000)
cfg->pll_opdiv = 8;
cfg->pll_fbdiv = DIV_ROUND_UP_ULL(dlane_bps * 2 * cfg->pll_opdiv *
cfg->pll_ipdiv,
pll_ref_hz);
return 0;
}
static int cdns_dphy_setup_psm(struct cdns_dphy *dphy)
{
unsigned long psm_clk_hz = clk_get_rate(dphy->psm_clk);
unsigned long psm_div;
if (!psm_clk_hz || psm_clk_hz > 100000000)
return -EINVAL;
psm_div = DIV_ROUND_CLOSEST(psm_clk_hz, 1000000);
if (dphy->ops->set_psm_div)
dphy->ops->set_psm_div(dphy, psm_div);
return 0;
}
static void cdns_dphy_set_clk_lane_cfg(struct cdns_dphy *dphy,
enum cdns_dphy_clk_lane_cfg cfg)
{
if (dphy->ops->set_clk_lane_cfg)
dphy->ops->set_clk_lane_cfg(dphy, cfg);
}
static void cdns_dphy_set_pll_cfg(struct cdns_dphy *dphy,
const struct cdns_dphy_cfg *cfg)
{
if (dphy->ops->set_pll_cfg)
dphy->ops->set_pll_cfg(dphy, cfg);
}
static unsigned long cdns_dphy_get_wakeup_time_ns(struct cdns_dphy *dphy)
{
return dphy->ops->get_wakeup_time_ns(dphy);
}
static unsigned long cdns_dphy_ref_get_wakeup_time_ns(struct cdns_dphy *dphy)
{
/* Default wakeup time is 800 ns (in a simulated environment). */
return 800;
}
static void cdns_dphy_ref_set_pll_cfg(struct cdns_dphy *dphy,
const struct cdns_dphy_cfg *cfg)
{
u32 fbdiv_low, fbdiv_high;
fbdiv_low = (cfg->pll_fbdiv / 4) - 2;
fbdiv_high = cfg->pll_fbdiv - fbdiv_low - 2;
writel(DPHY_CMN_IPDIV_FROM_REG | DPHY_CMN_OPDIV_FROM_REG |
DPHY_CMN_IPDIV(cfg->pll_ipdiv) |
DPHY_CMN_OPDIV(cfg->pll_opdiv),
dphy->regs + DPHY_CMN_OPIPDIV);
writel(DPHY_CMN_FBDIV_FROM_REG |
DPHY_CMN_FBDIV_VAL(fbdiv_low, fbdiv_high),
dphy->regs + DPHY_CMN_FBDIV);
writel(DPHY_CMN_PWM_HIGH(6) | DPHY_CMN_PWM_LOW(0x101) |
DPHY_CMN_PWM_DIV(0x8),
dphy->regs + DPHY_CMN_PWM);
}
static void cdns_dphy_ref_set_psm_div(struct cdns_dphy *dphy, u8 div)
{
writel(DPHY_PSM_CFG_FROM_REG | DPHY_PSM_CLK_DIV(div),
dphy->regs + DPHY_PSM_CFG);
}
/*
* This is the reference implementation of DPHY hooks. Specific integration of
* this IP may have to re-implement some of them depending on how they decided
* to wire things in the SoC.
*/
static const struct cdns_dphy_ops ref_dphy_ops = {
.get_wakeup_time_ns = cdns_dphy_ref_get_wakeup_time_ns,
.set_pll_cfg = cdns_dphy_ref_set_pll_cfg,
.set_psm_div = cdns_dphy_ref_set_psm_div,
};
static int cdns_dphy_config_from_opts(struct phy *phy,
struct phy_configure_opts_mipi_dphy *opts,
struct cdns_dphy_cfg *cfg)
{
struct cdns_dphy *dphy = phy_get_drvdata(phy);
unsigned int dsi_hfp_ext = 0;
int ret;
ret = phy_mipi_dphy_config_validate(opts);
if (ret)
return ret;
ret = cdns_dsi_get_dphy_pll_cfg(dphy, cfg,
opts, &dsi_hfp_ext);
if (ret)
return ret;
opts->wakeup = cdns_dphy_get_wakeup_time_ns(dphy) / 1000;
return 0;
}
static int cdns_dphy_validate(struct phy *phy, enum phy_mode mode, int submode,
union phy_configure_opts *opts)
{
struct cdns_dphy_cfg cfg = { 0 };
if (mode != PHY_MODE_MIPI_DPHY)
return -EINVAL;
return cdns_dphy_config_from_opts(phy, &opts->mipi_dphy, &cfg);
}
static int cdns_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
{
struct cdns_dphy *dphy = phy_get_drvdata(phy);
struct cdns_dphy_cfg cfg = { 0 };
int ret;
ret = cdns_dphy_config_from_opts(phy, &opts->mipi_dphy, &cfg);
if (ret)
return ret;
/*
* Configure the internal PSM clk divider so that the DPHY has a
* 1MHz clk (or something close).
*/
ret = cdns_dphy_setup_psm(dphy);
if (ret)
return ret;
/*
* Configure attach clk lanes to data lanes: the DPHY has 2 clk lanes
* and 8 data lanes, each clk lane can be attache different set of
* data lanes. The 2 groups are named 'left' and 'right', so here we
* just say that we want the 'left' clk lane to drive the 'left' data
* lanes.
*/
cdns_dphy_set_clk_lane_cfg(dphy, DPHY_CLK_CFG_LEFT_DRIVES_LEFT);
/*
* Configure the DPHY PLL that will be used to generate the TX byte
* clk.
*/
cdns_dphy_set_pll_cfg(dphy, &cfg);
return 0;
}
static int cdns_dphy_power_on(struct phy *phy)
{
struct cdns_dphy *dphy = phy_get_drvdata(phy);
clk_prepare_enable(dphy->psm_clk);
clk_prepare_enable(dphy->pll_ref_clk);
/* Start TX state machine. */
writel(DPHY_CMN_SSM_EN | DPHY_CMN_TX_MODE_EN,
dphy->regs + DPHY_CMN_SSM);
return 0;
}
static int cdns_dphy_power_off(struct phy *phy)
{
struct cdns_dphy *dphy = phy_get_drvdata(phy);
clk_disable_unprepare(dphy->pll_ref_clk);
clk_disable_unprepare(dphy->psm_clk);
return 0;
}
static const struct phy_ops cdns_dphy_ops = {
.configure = cdns_dphy_configure,
.validate = cdns_dphy_validate,
.power_on = cdns_dphy_power_on,
.power_off = cdns_dphy_power_off,
};
static int cdns_dphy_probe(struct platform_device *pdev)
{
struct phy_provider *phy_provider;
struct cdns_dphy *dphy;
struct resource *res;
int ret;
dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
if (!dphy)
return -ENOMEM;
dev_set_drvdata(&pdev->dev, dphy);
dphy->ops = of_device_get_match_data(&pdev->dev);
if (!dphy->ops)
return -EINVAL;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dphy->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(dphy->regs))
return PTR_ERR(dphy->regs);
dphy->psm_clk = devm_clk_get(&pdev->dev, "psm");
if (IS_ERR(dphy->psm_clk))
return PTR_ERR(dphy->psm_clk);
dphy->pll_ref_clk = devm_clk_get(&pdev->dev, "pll_ref");
if (IS_ERR(dphy->pll_ref_clk))
return PTR_ERR(dphy->pll_ref_clk);
if (dphy->ops->probe) {
ret = dphy->ops->probe(dphy);
if (ret)
return ret;
}
dphy->phy = devm_phy_create(&pdev->dev, NULL, &cdns_dphy_ops);
if (IS_ERR(dphy->phy)) {
dev_err(&pdev->dev, "failed to create PHY\n");
if (dphy->ops->remove)
dphy->ops->remove(dphy);
return PTR_ERR(dphy->phy);
}
phy_set_drvdata(dphy->phy, dphy);
phy_provider = devm_of_phy_provider_register(&pdev->dev,
of_phy_simple_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
}
static int cdns_dphy_remove(struct platform_device *pdev)
{
struct cdns_dphy *dphy = dev_get_drvdata(&pdev->dev);
if (dphy->ops->remove)
dphy->ops->remove(dphy);
return 0;
}
static const struct of_device_id cdns_dphy_of_match[] = {
{ .compatible = "cdns,dphy", .data = &ref_dphy_ops },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, cdns_dphy_of_match);
static struct platform_driver cdns_dphy_platform_driver = {
.probe = cdns_dphy_probe,
.remove = cdns_dphy_remove,
.driver = {
.name = "cdns-mipi-dphy",
.of_match_table = cdns_dphy_of_match,
},
};
module_platform_driver(cdns_dphy_platform_driver);
MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>");
MODULE_DESCRIPTION("Cadence MIPI D-PHY Driver");
MODULE_LICENSE("GPL");

View File

@ -2,4 +2,4 @@ config PHY_FSL_IMX8MQ_USB
tristate "Freescale i.MX8M USB3 PHY" tristate "Freescale i.MX8M USB3 PHY"
depends on OF && HAS_IOMEM depends on OF && HAS_IOMEM
select GENERIC_PHY select GENERIC_PHY
default SOC_IMX8MQ default ARCH_MXC && ARM64

View File

@ -21,6 +21,27 @@ config PHY_BERLIN_USB
help help
Enable this to support the USB PHY on Marvell Berlin SoCs. Enable this to support the USB PHY on Marvell Berlin SoCs.
config PHY_MVEBU_A3700_COMPHY
tristate "Marvell A3700 comphy driver"
depends on ARCH_MVEBU || COMPILE_TEST
depends on OF
depends on HAVE_ARM_SMCCC
default y
select GENERIC_PHY
help
This driver allows to control the comphy, a hardware block providing
shared serdes PHYs on Marvell Armada 3700. Its serdes lanes can be
used by various controllers: Ethernet, SATA, USB3, PCIe.
config PHY_MVEBU_A3700_UTMI
tristate "Marvell A3700 UTMI driver"
depends on ARCH_MVEBU || COMPILE_TEST
depends on OF
default y
select GENERIC_PHY
help
Enable this to support Marvell A3700 UTMI PHY driver.
config PHY_MVEBU_A38X_COMPHY config PHY_MVEBU_A38X_COMPHY
tristate "Marvell Armada 38x comphy driver" tristate "Marvell Armada 38x comphy driver"
depends on ARCH_MVEBU || COMPILE_TEST depends on ARCH_MVEBU || COMPILE_TEST

View File

@ -2,6 +2,8 @@
obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o
obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o
obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o
obj-$(CONFIG_PHY_MVEBU_A3700_COMPHY) += phy-mvebu-a3700-comphy.o
obj-$(CONFIG_PHY_MVEBU_A3700_UTMI) += phy-mvebu-a3700-utmi.o
obj-$(CONFIG_PHY_MVEBU_A38X_COMPHY) += phy-armada38x-comphy.o obj-$(CONFIG_PHY_MVEBU_A38X_COMPHY) += phy-armada38x-comphy.o
obj-$(CONFIG_PHY_MVEBU_CP110_COMPHY) += phy-mvebu-cp110-comphy.o obj-$(CONFIG_PHY_MVEBU_CP110_COMPHY) += phy-mvebu-cp110-comphy.o
obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o

View File

@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0+
/* /*
* USB cluster support for Armada 375 platform. * USB cluster support for Armada 375 platform.
* *
@ -5,10 +6,6 @@
* *
* Gregory CLEMENT <gregory.clement@free-electrons.com> * Gregory CLEMENT <gregory.clement@free-electrons.com>
* *
* This file is licensed under the terms of the GNU General Public
* License version 2 or later. This program is licensed "as is"
* without any warranty of any kind, whether express or implied.
*
* Armada 375 comes with an USB2 host and device controller and an * Armada 375 comes with an USB2 host and device controller and an
* USB3 controller. The USB cluster control register allows to manage * USB3 controller. The USB cluster control register allows to manage
* common features of both USB controllers. * common features of both USB controllers.
@ -18,7 +15,6 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/phy/phy.h> #include <linux/phy/phy.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
@ -142,7 +138,6 @@ static const struct of_device_id of_usb_cluster_table[] = {
{ .compatible = "marvell,armada-375-usb-cluster", }, { .compatible = "marvell,armada-375-usb-cluster", },
{ /* end of list */ }, { /* end of list */ },
}; };
MODULE_DEVICE_TABLE(of, of_usb_cluster_table);
static struct platform_driver armada375_usb_phy_driver = { static struct platform_driver armada375_usb_phy_driver = {
.probe = armada375_usb_phy_probe, .probe = armada375_usb_phy_probe,
@ -151,8 +146,4 @@ static struct platform_driver armada375_usb_phy_driver = {
.name = "armada-375-usb-cluster", .name = "armada-375-usb-cluster",
} }
}; };
module_platform_driver(armada375_usb_phy_driver); builtin_platform_driver(armada375_usb_phy_driver);
MODULE_DESCRIPTION("Armada 375 USB cluster driver");
MODULE_AUTHOR("Gregory CLEMENT <gregory.clement@free-electrons.com>");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,318 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2018 Marvell
*
* Authors:
* Evan Wang <xswang@marvell.com>
* Miquèl Raynal <miquel.raynal@bootlin.com>
*
* Structure inspired from phy-mvebu-cp110-comphy.c written by Antoine Tenart.
* SMC call initial support done by Grzegorz Jaszczyk.
*/
#include <linux/arm-smccc.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/phy.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#define MVEBU_A3700_COMPHY_LANES 3
#define MVEBU_A3700_COMPHY_PORTS 2
/* COMPHY Fast SMC function identifiers */
#define COMPHY_SIP_POWER_ON 0x82000001
#define COMPHY_SIP_POWER_OFF 0x82000002
#define COMPHY_SIP_PLL_LOCK 0x82000003
#define COMPHY_FW_MODE_SATA 0x1
#define COMPHY_FW_MODE_SGMII 0x2
#define COMPHY_FW_MODE_HS_SGMII 0x3
#define COMPHY_FW_MODE_USB3H 0x4
#define COMPHY_FW_MODE_USB3D 0x5
#define COMPHY_FW_MODE_PCIE 0x6
#define COMPHY_FW_MODE_RXAUI 0x7
#define COMPHY_FW_MODE_XFI 0x8
#define COMPHY_FW_MODE_SFI 0x9
#define COMPHY_FW_MODE_USB3 0xa
#define COMPHY_FW_SPEED_1_25G 0 /* SGMII 1G */
#define COMPHY_FW_SPEED_2_5G 1
#define COMPHY_FW_SPEED_3_125G 2 /* SGMII 2.5G */
#define COMPHY_FW_SPEED_5G 3
#define COMPHY_FW_SPEED_5_15625G 4 /* XFI 5G */
#define COMPHY_FW_SPEED_6G 5
#define COMPHY_FW_SPEED_10_3125G 6 /* XFI 10G */
#define COMPHY_FW_SPEED_MAX 0x3F
#define COMPHY_FW_MODE(mode) ((mode) << 12)
#define COMPHY_FW_NET(mode, idx, speed) (COMPHY_FW_MODE(mode) | \
((idx) << 8) | \
((speed) << 2))
#define COMPHY_FW_PCIE(mode, idx, speed, width) (COMPHY_FW_NET(mode, idx, speed) | \
((width) << 18))
struct mvebu_a3700_comphy_conf {
unsigned int lane;
enum phy_mode mode;
int submode;
unsigned int port;
u32 fw_mode;
};
#define MVEBU_A3700_COMPHY_CONF(_lane, _mode, _smode, _port, _fw) \
{ \
.lane = _lane, \
.mode = _mode, \
.submode = _smode, \
.port = _port, \
.fw_mode = _fw, \
}
#define MVEBU_A3700_COMPHY_CONF_GEN(_lane, _mode, _port, _fw) \
MVEBU_A3700_COMPHY_CONF(_lane, _mode, PHY_INTERFACE_MODE_NA, _port, _fw)
#define MVEBU_A3700_COMPHY_CONF_ETH(_lane, _smode, _port, _fw) \
MVEBU_A3700_COMPHY_CONF(_lane, PHY_MODE_ETHERNET, _smode, _port, _fw)
static const struct mvebu_a3700_comphy_conf mvebu_a3700_comphy_modes[] = {
/* lane 0 */
MVEBU_A3700_COMPHY_CONF_GEN(0, PHY_MODE_USB_HOST_SS, 0,
COMPHY_FW_MODE_USB3H),
MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_SGMII, 1,
COMPHY_FW_MODE_SGMII),
MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_2500BASEX, 1,
COMPHY_FW_MODE_HS_SGMII),
/* lane 1 */
MVEBU_A3700_COMPHY_CONF_GEN(1, PHY_MODE_PCIE, 0,
COMPHY_FW_MODE_PCIE),
MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_SGMII, 0,
COMPHY_FW_MODE_SGMII),
MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_2500BASEX, 0,
COMPHY_FW_MODE_HS_SGMII),
/* lane 2 */
MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_SATA, 0,
COMPHY_FW_MODE_SATA),
MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_USB_HOST_SS, 0,
COMPHY_FW_MODE_USB3H),
};
struct mvebu_a3700_comphy_lane {
struct device *dev;
unsigned int id;
enum phy_mode mode;
int submode;
int port;
};
static int mvebu_a3700_comphy_smc(unsigned long function, unsigned long lane,
unsigned long mode)
{
struct arm_smccc_res res;
arm_smccc_smc(function, lane, mode, 0, 0, 0, 0, 0, &res);
return res.a0;
}
static int mvebu_a3700_comphy_get_fw_mode(int lane, int port,
enum phy_mode mode,
int submode)
{
int i, n = ARRAY_SIZE(mvebu_a3700_comphy_modes);
/* Unused PHY mux value is 0x0 */
if (mode == PHY_MODE_INVALID)
return -EINVAL;
for (i = 0; i < n; i++) {
if (mvebu_a3700_comphy_modes[i].lane == lane &&
mvebu_a3700_comphy_modes[i].port == port &&
mvebu_a3700_comphy_modes[i].mode == mode &&
mvebu_a3700_comphy_modes[i].submode == submode)
break;
}
if (i == n)
return -EINVAL;
return mvebu_a3700_comphy_modes[i].fw_mode;
}
static int mvebu_a3700_comphy_set_mode(struct phy *phy, enum phy_mode mode,
int submode)
{
struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
int fw_mode;
if (submode == PHY_INTERFACE_MODE_1000BASEX)
submode = PHY_INTERFACE_MODE_SGMII;
fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, lane->port, mode,
submode);
if (fw_mode < 0) {
dev_err(lane->dev, "invalid COMPHY mode\n");
return fw_mode;
}
/* Just remember the mode, ->power_on() will do the real setup */
lane->mode = mode;
lane->submode = submode;
return 0;
}
static int mvebu_a3700_comphy_power_on(struct phy *phy)
{
struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
u32 fw_param;
int fw_mode;
fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, lane->port,
lane->mode, lane->submode);
if (fw_mode < 0) {
dev_err(lane->dev, "invalid COMPHY mode\n");
return fw_mode;
}
switch (lane->mode) {
case PHY_MODE_USB_HOST_SS:
dev_dbg(lane->dev, "set lane %d to USB3 host mode\n", lane->id);
fw_param = COMPHY_FW_MODE(fw_mode);
break;
case PHY_MODE_SATA:
dev_dbg(lane->dev, "set lane %d to SATA mode\n", lane->id);
fw_param = COMPHY_FW_MODE(fw_mode);
break;
case PHY_MODE_ETHERNET:
switch (lane->submode) {
case PHY_INTERFACE_MODE_SGMII:
dev_dbg(lane->dev, "set lane %d to SGMII mode\n",
lane->id);
fw_param = COMPHY_FW_NET(fw_mode, lane->port,
COMPHY_FW_SPEED_1_25G);
break;
case PHY_INTERFACE_MODE_2500BASEX:
dev_dbg(lane->dev, "set lane %d to HS SGMII mode\n",
lane->id);
fw_param = COMPHY_FW_NET(fw_mode, lane->port,
COMPHY_FW_SPEED_3_125G);
break;
default:
dev_err(lane->dev, "unsupported PHY submode (%d)\n",
lane->submode);
return -ENOTSUPP;
}
break;
case PHY_MODE_PCIE:
dev_dbg(lane->dev, "set lane %d to PCIe mode\n", lane->id);
fw_param = COMPHY_FW_PCIE(fw_mode, lane->port,
COMPHY_FW_SPEED_5G,
phy->attrs.bus_width);
break;
default:
dev_err(lane->dev, "unsupported PHY mode (%d)\n", lane->mode);
return -ENOTSUPP;
}
return mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_ON, lane->id, fw_param);
}
static int mvebu_a3700_comphy_power_off(struct phy *phy)
{
struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
return mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_OFF, lane->id, 0);
}
static const struct phy_ops mvebu_a3700_comphy_ops = {
.power_on = mvebu_a3700_comphy_power_on,
.power_off = mvebu_a3700_comphy_power_off,
.set_mode = mvebu_a3700_comphy_set_mode,
.owner = THIS_MODULE,
};
static struct phy *mvebu_a3700_comphy_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct mvebu_a3700_comphy_lane *lane;
struct phy *phy;
if (WARN_ON(args->args[0] >= MVEBU_A3700_COMPHY_PORTS))
return ERR_PTR(-EINVAL);
phy = of_phy_simple_xlate(dev, args);
if (IS_ERR(phy))
return phy;
lane = phy_get_drvdata(phy);
lane->port = args->args[0];
return phy;
}
static int mvebu_a3700_comphy_probe(struct platform_device *pdev)
{
struct phy_provider *provider;
struct device_node *child;
for_each_available_child_of_node(pdev->dev.of_node, child) {
struct mvebu_a3700_comphy_lane *lane;
struct phy *phy;
int ret;
u32 lane_id;
ret = of_property_read_u32(child, "reg", &lane_id);
if (ret < 0) {
dev_err(&pdev->dev, "missing 'reg' property (%d)\n",
ret);
continue;
}
if (lane_id >= MVEBU_A3700_COMPHY_LANES) {
dev_err(&pdev->dev, "invalid 'reg' property\n");
continue;
}
lane = devm_kzalloc(&pdev->dev, sizeof(*lane), GFP_KERNEL);
if (!lane)
return -ENOMEM;
phy = devm_phy_create(&pdev->dev, child,
&mvebu_a3700_comphy_ops);
if (IS_ERR(phy))
return PTR_ERR(phy);
lane->dev = &pdev->dev;
lane->mode = PHY_MODE_INVALID;
lane->submode = PHY_INTERFACE_MODE_NA;
lane->id = lane_id;
lane->port = -1;
phy_set_drvdata(phy, lane);
}
provider = devm_of_phy_provider_register(&pdev->dev,
mvebu_a3700_comphy_xlate);
return PTR_ERR_OR_ZERO(provider);
}
static const struct of_device_id mvebu_a3700_comphy_of_match_table[] = {
{ .compatible = "marvell,comphy-a3700" },
{ },
};
MODULE_DEVICE_TABLE(of, mvebu_a3700_comphy_of_match_table);
static struct platform_driver mvebu_a3700_comphy_driver = {
.probe = mvebu_a3700_comphy_probe,
.driver = {
.name = "mvebu-a3700-comphy",
.of_match_table = mvebu_a3700_comphy_of_match_table,
},
};
module_platform_driver(mvebu_a3700_comphy_driver);
MODULE_AUTHOR("Miquèl Raynal <miquel.raynal@bootlin.com>");
MODULE_DESCRIPTION("Common PHY driver for A3700");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,278 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2018 Marvell
*
* Authors:
* Igal Liberman <igall@marvell.com>
* Miquèl Raynal <miquel.raynal@bootlin.com>
*
* Marvell A3700 UTMI PHY driver
*/
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
/* Armada 3700 UTMI PHY registers */
#define USB2_PHY_PLL_CTRL_REG0 0x0
#define PLL_REF_DIV_OFF 0
#define PLL_REF_DIV_MASK GENMASK(6, 0)
#define PLL_REF_DIV_5 5
#define PLL_FB_DIV_OFF 16
#define PLL_FB_DIV_MASK GENMASK(24, 16)
#define PLL_FB_DIV_96 96
#define PLL_SEL_LPFR_OFF 28
#define PLL_SEL_LPFR_MASK GENMASK(29, 28)
#define PLL_READY BIT(31)
#define USB2_PHY_CAL_CTRL 0x8
#define PHY_PLLCAL_DONE BIT(31)
#define PHY_IMPCAL_DONE BIT(23)
#define USB2_RX_CHAN_CTRL1 0x18
#define USB2PHY_SQCAL_DONE BIT(31)
#define USB2_PHY_OTG_CTRL 0x34
#define PHY_PU_OTG BIT(4)
#define USB2_PHY_CHRGR_DETECT 0x38
#define PHY_CDP_EN BIT(2)
#define PHY_DCP_EN BIT(3)
#define PHY_PD_EN BIT(4)
#define PHY_PU_CHRG_DTC BIT(5)
#define PHY_CDP_DM_AUTO BIT(7)
#define PHY_ENSWITCH_DP BIT(12)
#define PHY_ENSWITCH_DM BIT(13)
/* Armada 3700 USB miscellaneous registers */
#define USB2_PHY_CTRL(usb32) (usb32 ? 0x20 : 0x4)
#define RB_USB2PHY_PU BIT(0)
#define USB2_DP_PULLDN_DEV_MODE BIT(5)
#define USB2_DM_PULLDN_DEV_MODE BIT(6)
#define RB_USB2PHY_SUSPM(usb32) (usb32 ? BIT(14) : BIT(7))
#define PLL_LOCK_DELAY_US 10000
#define PLL_LOCK_TIMEOUT_US 1000000
/**
* struct mvebu_a3700_utmi_caps - PHY capabilities
*
* @usb32: Flag indicating which PHY is in use (impacts the register map):
* - The UTMI PHY wired to the USB3/USB2 controller (otg)
* - The UTMI PHY wired to the USB2 controller (host only)
* @ops: PHY operations
*/
struct mvebu_a3700_utmi_caps {
int usb32;
const struct phy_ops *ops;
};
/**
* struct mvebu_a3700_utmi - PHY driver data
*
* @regs: PHY registers
* @usb_mis: Regmap with USB miscellaneous registers including PHY ones
* @caps: PHY capabilities
* @phy: PHY handle
*/
struct mvebu_a3700_utmi {
void __iomem *regs;
struct regmap *usb_misc;
const struct mvebu_a3700_utmi_caps *caps;
struct phy *phy;
};
static int mvebu_a3700_utmi_phy_power_on(struct phy *phy)
{
struct mvebu_a3700_utmi *utmi = phy_get_drvdata(phy);
struct device *dev = &phy->dev;
int usb32 = utmi->caps->usb32;
int ret = 0;
u32 reg;
/*
* Setup PLL. 40MHz clock used to be the default, being 25MHz now.
* See "PLL Settings for Typical REFCLK" table.
*/
reg = readl(utmi->regs + USB2_PHY_PLL_CTRL_REG0);
reg &= ~(PLL_REF_DIV_MASK | PLL_FB_DIV_MASK | PLL_SEL_LPFR_MASK);
reg |= (PLL_REF_DIV_5 << PLL_REF_DIV_OFF) |
(PLL_FB_DIV_96 << PLL_FB_DIV_OFF);
writel(reg, utmi->regs + USB2_PHY_PLL_CTRL_REG0);
/* Enable PHY pull up and disable USB2 suspend */
regmap_update_bits(utmi->usb_misc, USB2_PHY_CTRL(usb32),
RB_USB2PHY_SUSPM(usb32) | RB_USB2PHY_PU,
RB_USB2PHY_SUSPM(usb32) | RB_USB2PHY_PU);
if (usb32) {
/* Power up OTG module */
reg = readl(utmi->regs + USB2_PHY_OTG_CTRL);
reg |= PHY_PU_OTG;
writel(reg, utmi->regs + USB2_PHY_OTG_CTRL);
/* Disable PHY charger detection */
reg = readl(utmi->regs + USB2_PHY_CHRGR_DETECT);
reg &= ~(PHY_CDP_EN | PHY_DCP_EN | PHY_PD_EN | PHY_PU_CHRG_DTC |
PHY_CDP_DM_AUTO | PHY_ENSWITCH_DP | PHY_ENSWITCH_DM);
writel(reg, utmi->regs + USB2_PHY_CHRGR_DETECT);
/* Disable PHY DP/DM pull-down (used for device mode) */
regmap_update_bits(utmi->usb_misc, USB2_PHY_CTRL(usb32),
USB2_DP_PULLDN_DEV_MODE |
USB2_DM_PULLDN_DEV_MODE, 0);
}
/* Wait for PLL calibration */
ret = readl_poll_timeout(utmi->regs + USB2_PHY_CAL_CTRL, reg,
reg & PHY_PLLCAL_DONE,
PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US);
if (ret) {
dev_err(dev, "Failed to end USB2 PLL calibration\n");
return ret;
}
/* Wait for impedance calibration */
ret = readl_poll_timeout(utmi->regs + USB2_PHY_CAL_CTRL, reg,
reg & PHY_IMPCAL_DONE,
PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US);
if (ret) {
dev_err(dev, "Failed to end USB2 impedance calibration\n");
return ret;
}
/* Wait for squelch calibration */
ret = readl_poll_timeout(utmi->regs + USB2_RX_CHAN_CTRL1, reg,
reg & USB2PHY_SQCAL_DONE,
PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US);
if (ret) {
dev_err(dev, "Failed to end USB2 unknown calibration\n");
return ret;
}
/* Wait for PLL to be locked */
ret = readl_poll_timeout(utmi->regs + USB2_PHY_PLL_CTRL_REG0, reg,
reg & PLL_READY,
PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US);
if (ret)
dev_err(dev, "Failed to lock USB2 PLL\n");
return ret;
}
static int mvebu_a3700_utmi_phy_power_off(struct phy *phy)
{
struct mvebu_a3700_utmi *utmi = phy_get_drvdata(phy);
int usb32 = utmi->caps->usb32;
u32 reg;
/* Disable PHY pull-up and enable USB2 suspend */
reg = readl(utmi->regs + USB2_PHY_CTRL(usb32));
reg &= ~(RB_USB2PHY_PU | RB_USB2PHY_SUSPM(usb32));
writel(reg, utmi->regs + USB2_PHY_CTRL(usb32));
/* Power down OTG module */
if (usb32) {
reg = readl(utmi->regs + USB2_PHY_OTG_CTRL);
reg &= ~PHY_PU_OTG;
writel(reg, utmi->regs + USB2_PHY_OTG_CTRL);
}
return 0;
}
static const struct phy_ops mvebu_a3700_utmi_phy_ops = {
.power_on = mvebu_a3700_utmi_phy_power_on,
.power_off = mvebu_a3700_utmi_phy_power_off,
.owner = THIS_MODULE,
};
static const struct mvebu_a3700_utmi_caps mvebu_a3700_utmi_otg_phy_caps = {
.usb32 = true,
.ops = &mvebu_a3700_utmi_phy_ops,
};
static const struct mvebu_a3700_utmi_caps mvebu_a3700_utmi_host_phy_caps = {
.usb32 = false,
.ops = &mvebu_a3700_utmi_phy_ops,
};
static const struct of_device_id mvebu_a3700_utmi_of_match[] = {
{
.compatible = "marvell,a3700-utmi-otg-phy",
.data = &mvebu_a3700_utmi_otg_phy_caps,
},
{
.compatible = "marvell,a3700-utmi-host-phy",
.data = &mvebu_a3700_utmi_host_phy_caps,
},
{},
};
MODULE_DEVICE_TABLE(of, mvebu_a3700_utmi_of_match);
static int mvebu_a3700_utmi_phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mvebu_a3700_utmi *utmi;
struct phy_provider *provider;
struct resource *res;
utmi = devm_kzalloc(dev, sizeof(*utmi), GFP_KERNEL);
if (!utmi)
return -ENOMEM;
/* Get UTMI memory region */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "Missing UTMI PHY memory resource\n");
return -ENODEV;
}
utmi->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(utmi->regs))
return PTR_ERR(utmi->regs);
/* Get miscellaneous Host/PHY region */
utmi->usb_misc = syscon_regmap_lookup_by_phandle(dev->of_node,
"marvell,usb-misc-reg");
if (IS_ERR(utmi->usb_misc)) {
dev_err(dev,
"Missing USB misc purpose system controller\n");
return PTR_ERR(utmi->usb_misc);
}
/* Retrieve PHY capabilities */
utmi->caps = of_device_get_match_data(dev);
/* Instantiate the PHY */
utmi->phy = devm_phy_create(dev, NULL, utmi->caps->ops);
if (IS_ERR(utmi->phy)) {
dev_err(dev, "Failed to create the UTMI PHY\n");
return PTR_ERR(utmi->phy);
}
phy_set_drvdata(utmi->phy, utmi);
/* Ensure the PHY is powered off */
utmi->caps->ops->power_off(utmi->phy);
provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
return PTR_ERR_OR_ZERO(provider);
}
static struct platform_driver mvebu_a3700_utmi_driver = {
.probe = mvebu_a3700_utmi_phy_probe,
.driver = {
.name = "mvebu-a3700-utmi-phy",
.owner = THIS_MODULE,
.of_match_table = mvebu_a3700_utmi_of_match,
},
};
module_platform_driver(mvebu_a3700_utmi_driver);
MODULE_AUTHOR("Igal Liberman <igall@marvell.com>");
MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>");
MODULE_DESCRIPTION("Marvell EBU A3700 UTMI PHY driver");
MODULE_LICENSE("GPL v2");

View File

@ -580,8 +580,6 @@ static struct phy *mvebu_comphy_xlate(struct device *dev,
return phy; return phy;
lane = phy_get_drvdata(phy); lane = phy_get_drvdata(phy);
if (lane->port >= 0)
return ERR_PTR(-EBUSY);
lane->port = args->args[0]; lane->port = args->args[0];
return phy; return phy;

View File

@ -10,7 +10,7 @@
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/init.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/phy/phy.h> #include <linux/phy/phy.h>
#include <linux/io.h> #include <linux/io.h>
@ -122,7 +122,6 @@ static const struct of_device_id phy_mvebu_sata_of_match[] = {
{ .compatible = "marvell,mvebu-sata-phy" }, { .compatible = "marvell,mvebu-sata-phy" },
{ }, { },
}; };
MODULE_DEVICE_TABLE(of, phy_mvebu_sata_of_match);
static struct platform_driver phy_mvebu_sata_driver = { static struct platform_driver phy_mvebu_sata_driver = {
.probe = phy_mvebu_sata_probe, .probe = phy_mvebu_sata_probe,
@ -131,8 +130,4 @@ static struct platform_driver phy_mvebu_sata_driver = {
.of_match_table = phy_mvebu_sata_of_match, .of_match_table = phy_mvebu_sata_of_match,
} }
}; };
module_platform_driver(phy_mvebu_sata_driver); builtin_platform_driver(phy_mvebu_sata_driver);
MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>");
MODULE_DESCRIPTION("Marvell MVEBU SATA PHY driver");
MODULE_LICENSE("GPL v2");

View File

@ -65,12 +65,12 @@ int phy_mipi_dphy_get_default_config(unsigned long pixel_clock,
*/ */
cfg->hs_trail = max(4 * 8 * ui, 60000 + 4 * 4 * ui); cfg->hs_trail = max(4 * 8 * ui, 60000 + 4 * 4 * ui);
cfg->init = 100000000; cfg->init = 100;
cfg->lpx = 60000; cfg->lpx = 60000;
cfg->ta_get = 5 * cfg->lpx; cfg->ta_get = 5 * cfg->lpx;
cfg->ta_go = 4 * cfg->lpx; cfg->ta_go = 4 * cfg->lpx;
cfg->ta_sure = 2 * cfg->lpx; cfg->ta_sure = 2 * cfg->lpx;
cfg->wakeup = 1000000000; cfg->wakeup = 1000;
cfg->hs_clk_rate = hs_clk_rate; cfg->hs_clk_rate = hs_clk_rate;
cfg->lanes = lanes; cfg->lanes = lanes;
@ -143,7 +143,7 @@ int phy_mipi_dphy_config_validate(struct phy_configure_opts_mipi_dphy *cfg)
if (cfg->hs_trail < max(8 * ui, 60000 + 4 * ui)) if (cfg->hs_trail < max(8 * ui, 60000 + 4 * ui))
return -EINVAL; return -EINVAL;
if (cfg->init < 100000000) if (cfg->init < 100)
return -EINVAL; return -EINVAL;
if (cfg->lpx < 50000) if (cfg->lpx < 50000)
@ -158,7 +158,7 @@ int phy_mipi_dphy_config_validate(struct phy_configure_opts_mipi_dphy *cfg)
if (cfg->ta_sure < cfg->lpx || cfg->ta_sure > (2 * cfg->lpx)) if (cfg->ta_sure < cfg->lpx || cfg->ta_sure > (2 * cfg->lpx))
return -EINVAL; return -EINVAL;
if (cfg->wakeup < 1000000000) if (cfg->wakeup < 1000)
return -EINVAL; return -EINVAL;
return 0; return 0;

View File

@ -1112,14 +1112,4 @@ static int __init phy_core_init(void)
return 0; return 0;
} }
module_init(phy_core_init); device_initcall(phy_core_init);
static void __exit phy_core_exit(void)
{
class_destroy(phy_class);
}
module_exit(phy_core_exit);
MODULE_DESCRIPTION("Generic PHY Framework");
MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
MODULE_LICENSE("GPL v2");

View File

@ -687,6 +687,116 @@ static const struct qmp_phy_init_tbl sdm845_ufsphy_pcs_tbl[] = {
QMP_PHY_INIT_CFG(QPHY_V3_PCS_MULTI_LANE_CTRL1, 0x02), QMP_PHY_INIT_CFG(QPHY_V3_PCS_MULTI_LANE_CTRL1, 0x02),
}; };
static const struct qmp_phy_init_tbl msm8998_usb3_serdes_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x04),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x14),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x06),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL2, 0x08),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x80),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0xab),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0xea),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x02),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xc9),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x34),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x15),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x04),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_CFG, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_BG_TIMER, 0x0a),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_INITVAL, 0x80),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_MODE, 0x01),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_EN_CENTER, 0x01),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER1, 0x31),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER2, 0x01),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER1, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER2, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE1, 0x85),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE2, 0x07),
};
static const struct qmp_phy_init_tbl msm8998_usb3_tx_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10),
QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12),
QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x16),
QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x00),
};
static const struct qmp_phy_init_tbl msm8998_usb3_rx_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4e),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x18),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x07),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x43),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x1c),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x75),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_PI_CONTROLS, 0x80),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FO_GAIN, 0x0a),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_GAIN, 0x06),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_ENABLES, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_VGA_CAL_CNTRL2, 0x03),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_MODE_00, 0x05),
};
static const struct qmp_phy_init_tbl msm8998_usb3_pcs_tbl[] = {
QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x40),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG1, 0xd1),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG2, 0x1f),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG3, 0x47),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_POWER_STATE_CONFIG2, 0x1b),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V0, 0x9f),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V1, 0x9f),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V2, 0xb7),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V3, 0x4e),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V4, 0x65),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_LS, 0x6b),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x15),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0d),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TX_LARGE_AMP_DRV_LVL, 0x15),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V1, 0x0d),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V2, 0x15),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V2, 0x0d),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V3, 0x15),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V3, 0x0d),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V4, 0x15),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V4, 0x0d),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_LS, 0x15),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_LS, 0x0d),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_RATE_SLEW_CNTRL, 0x02),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TSYNC_RSYNC_TIME, 0x44),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L, 0x40),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H, 0x00),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0x8a),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME, 0x75),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK, 0x86),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_RUN_TIME, 0x13),
};
/* struct qmp_phy_cfg - per-PHY initialization config */ /* struct qmp_phy_cfg - per-PHY initialization config */
struct qmp_phy_cfg { struct qmp_phy_cfg {
/* phy-type - PCIE/UFS/USB */ /* phy-type - PCIE/UFS/USB */
@ -1036,6 +1146,33 @@ static const struct qmp_phy_cfg sdm845_ufsphy_cfg = {
.no_pcs_sw_reset = true, .no_pcs_sw_reset = true,
}; };
static const struct qmp_phy_cfg msm8998_usb3phy_cfg = {
.type = PHY_TYPE_USB3,
.nlanes = 1,
.serdes_tbl = msm8998_usb3_serdes_tbl,
.serdes_tbl_num = ARRAY_SIZE(msm8998_usb3_serdes_tbl),
.tx_tbl = msm8998_usb3_tx_tbl,
.tx_tbl_num = ARRAY_SIZE(msm8998_usb3_tx_tbl),
.rx_tbl = msm8998_usb3_rx_tbl,
.rx_tbl_num = ARRAY_SIZE(msm8998_usb3_rx_tbl),
.pcs_tbl = msm8998_usb3_pcs_tbl,
.pcs_tbl_num = ARRAY_SIZE(msm8998_usb3_pcs_tbl),
.clk_list = msm8996_phy_clk_l,
.num_clks = ARRAY_SIZE(msm8996_phy_clk_l),
.reset_list = msm8996_usb3phy_reset_l,
.num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
.vreg_list = qmp_phy_vreg_l,
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
.regs = qmp_v3_usb3phy_regs_layout,
.start_ctrl = SERDES_START | PCS_START,
.pwrdn_ctrl = SW_PWRDN,
.mask_pcs_ready = PHYSTATUS,
.is_dual_lane_phy = true,
};
static void qcom_qmp_phy_configure(void __iomem *base, static void qcom_qmp_phy_configure(void __iomem *base,
const unsigned int *regs, const unsigned int *regs,
const struct qmp_phy_init_tbl tbl[], const struct qmp_phy_init_tbl tbl[],
@ -1735,6 +1872,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = {
}, { }, {
.compatible = "qcom,msm8996-qmp-usb3-phy", .compatible = "qcom,msm8996-qmp-usb3-phy",
.data = &msm8996_usb3phy_cfg, .data = &msm8996_usb3phy_cfg,
}, {
.compatible = "qcom,msm8998-qmp-ufs-phy",
.data = &sdm845_ufsphy_cfg,
}, { }, {
.compatible = "qcom,ipq8074-qmp-pcie-phy", .compatible = "qcom,ipq8074-qmp-pcie-phy",
.data = &ipq8074_pciephy_cfg, .data = &ipq8074_pciephy_cfg,
@ -1747,6 +1887,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = {
}, { }, {
.compatible = "qcom,sdm845-qmp-ufs-phy", .compatible = "qcom,sdm845-qmp-ufs-phy",
.data = &sdm845_ufsphy_cfg, .data = &sdm845_ufsphy_cfg,
}, {
.compatible = "qcom,msm8998-qmp-usb3-phy",
.data = &msm8998_usb3phy_cfg,
}, },
{ }, { },
}; };

View File

@ -174,6 +174,7 @@
#define QSERDES_V3_COM_DIV_FRAC_START1_MODE1 0x0c4 #define QSERDES_V3_COM_DIV_FRAC_START1_MODE1 0x0c4
#define QSERDES_V3_COM_DIV_FRAC_START2_MODE1 0x0c8 #define QSERDES_V3_COM_DIV_FRAC_START2_MODE1 0x0c8
#define QSERDES_V3_COM_DIV_FRAC_START3_MODE1 0x0cc #define QSERDES_V3_COM_DIV_FRAC_START3_MODE1 0x0cc
#define QSERDES_V3_COM_INTEGLOOP_INITVAL 0x0d0
#define QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0 0x0d8 #define QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0 0x0d8
#define QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0 0x0dc #define QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0 0x0dc
#define QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE1 0x0e0 #define QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE1 0x0e0
@ -201,6 +202,7 @@
#define QSERDES_V3_COM_DEBUG_BUS2 0x170 #define QSERDES_V3_COM_DEBUG_BUS2 0x170
#define QSERDES_V3_COM_DEBUG_BUS3 0x174 #define QSERDES_V3_COM_DEBUG_BUS3 0x174
#define QSERDES_V3_COM_DEBUG_BUS_SEL 0x178 #define QSERDES_V3_COM_DEBUG_BUS_SEL 0x178
#define QSERDES_V3_COM_CMN_MODE 0x184
/* Only for QMP V3 PHY - TX registers */ /* Only for QMP V3 PHY - TX registers */
#define QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX 0x044 #define QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX 0x044
@ -211,6 +213,7 @@
#define QSERDES_V3_TX_RCV_DETECT_LVL_2 0x0a4 #define QSERDES_V3_TX_RCV_DETECT_LVL_2 0x0a4
/* Only for QMP V3 PHY - RX registers */ /* Only for QMP V3 PHY - RX registers */
#define QSERDES_V3_RX_UCDR_FO_GAIN 0x008
#define QSERDES_V3_RX_UCDR_SO_GAIN_HALF 0x00c #define QSERDES_V3_RX_UCDR_SO_GAIN_HALF 0x00c
#define QSERDES_V3_RX_UCDR_SO_GAIN 0x014 #define QSERDES_V3_RX_UCDR_SO_GAIN 0x014
#define QSERDES_V3_RX_UCDR_SVS_SO_GAIN_HALF 0x024 #define QSERDES_V3_RX_UCDR_SVS_SO_GAIN_HALF 0x024
@ -219,6 +222,7 @@
#define QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN 0x030 #define QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN 0x030
#define QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE 0x034 #define QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE 0x034
#define QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW 0x03c #define QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW 0x03c
#define QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_HIGH 0x040
#define QSERDES_V3_RX_UCDR_PI_CONTROLS 0x044 #define QSERDES_V3_RX_UCDR_PI_CONTROLS 0x044
#define QSERDES_V3_RX_RX_TERM_BW 0x07c #define QSERDES_V3_RX_RX_TERM_BW 0x07c
#define QSERDES_V3_RX_VGA_CAL_CNTRL1 0x0bc #define QSERDES_V3_RX_VGA_CAL_CNTRL1 0x0bc

View File

@ -152,6 +152,31 @@ static const struct qusb2_phy_init_tbl msm8996_init_tbl[] = {
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_PWR_CTRL, 0x00), QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_PWR_CTRL, 0x00),
}; };
static const unsigned int msm8998_regs_layout[] = {
[QUSB2PHY_PLL_CORE_INPUT_OVERRIDE] = 0xa8,
[QUSB2PHY_PLL_STATUS] = 0x1a0,
[QUSB2PHY_PORT_TUNE1] = 0x23c,
[QUSB2PHY_PORT_TUNE2] = 0x240,
[QUSB2PHY_PORT_TUNE3] = 0x244,
[QUSB2PHY_PORT_TUNE4] = 0x248,
[QUSB2PHY_PORT_TEST1] = 0x24c,
[QUSB2PHY_PORT_TEST2] = 0x250,
[QUSB2PHY_PORT_POWERDOWN] = 0x210,
[QUSB2PHY_INTR_CTRL] = 0x22c,
};
static const struct qusb2_phy_init_tbl msm8998_init_tbl[] = {
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_ANALOG_CONTROLS_TWO, 0x13),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_CLOCK_INVERTERS, 0x7c),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_CMODE, 0x80),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_LOCK_DELAY, 0x0a),
QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE1, 0xa5),
QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE2, 0x09),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_DIGITAL_TIMERS_TWO, 0x19),
};
static const unsigned int sdm845_regs_layout[] = { static const unsigned int sdm845_regs_layout[] = {
[QUSB2PHY_PLL_CORE_INPUT_OVERRIDE] = 0xa8, [QUSB2PHY_PLL_CORE_INPUT_OVERRIDE] = 0xa8,
[QUSB2PHY_PLL_STATUS] = 0x1a0, [QUSB2PHY_PLL_STATUS] = 0x1a0,
@ -221,6 +246,18 @@ static const struct qusb2_phy_cfg msm8996_phy_cfg = {
.autoresume_en = BIT(3), .autoresume_en = BIT(3),
}; };
static const struct qusb2_phy_cfg msm8998_phy_cfg = {
.tbl = msm8998_init_tbl,
.tbl_num = ARRAY_SIZE(msm8998_init_tbl),
.regs = msm8998_regs_layout,
.disable_ctrl = POWER_DOWN,
.mask_core_ready = CORE_READY_STATUS,
.has_pll_override = true,
.autoresume_en = BIT(0),
.update_tune1_with_efuse = true,
};
static const struct qusb2_phy_cfg sdm845_phy_cfg = { static const struct qusb2_phy_cfg sdm845_phy_cfg = {
.tbl = sdm845_init_tbl, .tbl = sdm845_init_tbl,
.tbl_num = ARRAY_SIZE(sdm845_init_tbl), .tbl_num = ARRAY_SIZE(sdm845_init_tbl),
@ -733,6 +770,9 @@ static const struct of_device_id qusb2_phy_of_match_table[] = {
{ {
.compatible = "qcom,msm8996-qusb2-phy", .compatible = "qcom,msm8996-qusb2-phy",
.data = &msm8996_phy_cfg, .data = &msm8996_phy_cfg,
}, {
.compatible = "qcom,msm8998-qusb2-phy",
.data = &msm8998_phy_cfg,
}, { }, {
.compatible = "qcom,sdm845-qusb2-phy", .compatible = "qcom,sdm845-qusb2-phy",
.data = &sdm845_phy_cfg, .data = &sdm845_phy_cfg,

View File

@ -23,24 +23,7 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/iopoll.h>
#define readl_poll_timeout(addr, val, cond, sleep_us, timeout_us) \
({ \
ktime_t timeout = ktime_add_us(ktime_get(), timeout_us); \
might_sleep_if(timeout_us); \
for (;;) { \
(val) = readl(addr); \
if (cond) \
break; \
if (timeout_us && ktime_compare(ktime_get(), timeout) > 0) { \
(val) = readl(addr); \
break; \
} \
if (sleep_us) \
usleep_range(DIV_ROUND_UP(sleep_us, 4), sleep_us); \
} \
(cond) ? 0 : -ETIMEDOUT; \
})
#define UFS_QCOM_PHY_CAL_ENTRY(reg, val) \ #define UFS_QCOM_PHY_CAL_ENTRY(reg, val) \
{ \ { \

View File

@ -55,16 +55,16 @@ enum rockchip_usb2phy_host_state {
}; };
/** /**
* Different states involved in USB charger detection. * enum usb_chg_state - Different states involved in USB charger detection.
* USB_CHG_STATE_UNDEFINED USB charger is not connected or detection * @USB_CHG_STATE_UNDEFINED: USB charger is not connected or detection
* process is not yet started. * process is not yet started.
* USB_CHG_STATE_WAIT_FOR_DCD Waiting for Data pins contact. * @USB_CHG_STATE_WAIT_FOR_DCD: Waiting for Data pins contact.
* USB_CHG_STATE_DCD_DONE Data pin contact is detected. * @USB_CHG_STATE_DCD_DONE: Data pin contact is detected.
* USB_CHG_STATE_PRIMARY_DONE Primary detection is completed (Detects * @USB_CHG_STATE_PRIMARY_DONE: Primary detection is completed (Detects
* between SDP and DCP/CDP). * between SDP and DCP/CDP).
* USB_CHG_STATE_SECONDARY_DONE Secondary detection is completed (Detects * @USB_CHG_STATE_SECONDARY_DONE: Secondary detection is completed (Detects
* between DCP and CDP). * between DCP and CDP).
* USB_CHG_STATE_DETECTED USB charger type is determined. * @USB_CHG_STATE_DETECTED: USB charger type is determined.
*/ */
enum usb_chg_state { enum usb_chg_state {
USB_CHG_STATE_UNDEFINED = 0, USB_CHG_STATE_UNDEFINED = 0,
@ -94,7 +94,7 @@ struct usb2phy_reg {
}; };
/** /**
* struct rockchip_chg_det_reg: usb charger detect registers * struct rockchip_chg_det_reg - usb charger detect registers
* @cp_det: charging port detected successfully. * @cp_det: charging port detected successfully.
* @dcp_det: dedicated charging port detected successfully. * @dcp_det: dedicated charging port detected successfully.
* @dp_det: assert data pin connect successfully. * @dp_det: assert data pin connect successfully.
@ -120,7 +120,7 @@ struct rockchip_chg_det_reg {
}; };
/** /**
* struct rockchip_usb2phy_port_cfg: usb-phy port configuration. * struct rockchip_usb2phy_port_cfg - usb-phy port configuration.
* @phy_sus: phy suspend register. * @phy_sus: phy suspend register.
* @bvalid_det_en: vbus valid rise detection enable register. * @bvalid_det_en: vbus valid rise detection enable register.
* @bvalid_det_st: vbus valid rise detection status register. * @bvalid_det_st: vbus valid rise detection status register.
@ -148,10 +148,11 @@ struct rockchip_usb2phy_port_cfg {
}; };
/** /**
* struct rockchip_usb2phy_cfg: usb-phy configuration. * struct rockchip_usb2phy_cfg - usb-phy configuration.
* @reg: the address offset of grf for usb-phy config. * @reg: the address offset of grf for usb-phy config.
* @num_ports: specify how many ports that the phy has. * @num_ports: specify how many ports that the phy has.
* @clkout_ctl: keep on/turn off output clk of phy. * @clkout_ctl: keep on/turn off output clk of phy.
* @port_cfgs: usb-phy port configurations.
* @chg_det: charger detection registers. * @chg_det: charger detection registers.
*/ */
struct rockchip_usb2phy_cfg { struct rockchip_usb2phy_cfg {
@ -163,12 +164,10 @@ struct rockchip_usb2phy_cfg {
}; };
/** /**
* struct rockchip_usb2phy_port: usb-phy port data. * struct rockchip_usb2phy_port - usb-phy port data.
* @phy: generic phy.
* @port_id: flag for otg port or host port. * @port_id: flag for otg port or host port.
* @suspended: phy suspended flag. * @suspended: phy suspended flag.
* @utmi_avalid: utmi avalid status usage flag.
* true - use avalid to get vbus status
* flase - use bvalid to get vbus status
* @vbus_attached: otg device vbus status. * @vbus_attached: otg device vbus status.
* @bvalid_irq: IRQ number assigned for vbus valid rise detection. * @bvalid_irq: IRQ number assigned for vbus valid rise detection.
* @ls_irq: IRQ number assigned for linestate detection. * @ls_irq: IRQ number assigned for linestate detection.
@ -178,7 +177,7 @@ struct rockchip_usb2phy_cfg {
* @chg_work: charge detect work. * @chg_work: charge detect work.
* @otg_sm_work: OTG state machine work. * @otg_sm_work: OTG state machine work.
* @sm_work: HOST state machine work. * @sm_work: HOST state machine work.
* @phy_cfg: port register configuration, assigned by driver data. * @port_cfg: port register configuration, assigned by driver data.
* @event_nb: hold event notification callback. * @event_nb: hold event notification callback.
* @state: define OTG enumeration states before device reset. * @state: define OTG enumeration states before device reset.
* @mode: the dr_mode of the controller. * @mode: the dr_mode of the controller.
@ -187,7 +186,6 @@ struct rockchip_usb2phy_port {
struct phy *phy; struct phy *phy;
unsigned int port_id; unsigned int port_id;
bool suspended; bool suspended;
bool utmi_avalid;
bool vbus_attached; bool vbus_attached;
int bvalid_irq; int bvalid_irq;
int ls_irq; int ls_irq;
@ -203,12 +201,13 @@ struct rockchip_usb2phy_port {
}; };
/** /**
* struct rockchip_usb2phy: usb2.0 phy driver data. * struct rockchip_usb2phy - usb2.0 phy driver data.
* @dev: pointer to device.
* @grf: General Register Files regmap. * @grf: General Register Files regmap.
* @usbgrf: USB General Register Files regmap. * @usbgrf: USB General Register Files regmap.
* @clk: clock struct of phy input clk. * @clk: clock struct of phy input clk.
* @clk480m: clock struct of phy output clk. * @clk480m: clock struct of phy output clk.
* @clk_hw: clock struct of phy output clk management. * @clk480m_hw: clock struct of phy output clk management.
* @chg_state: states involved in USB charger detection. * @chg_state: states involved in USB charger detection.
* @chg_type: USB charger types. * @chg_type: USB charger types.
* @dcd_retries: The retry count used to track Data contact * @dcd_retries: The retry count used to track Data contact
@ -542,12 +541,8 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work)
unsigned long delay; unsigned long delay;
bool vbus_attach, sch_work, notify_charger; bool vbus_attach, sch_work, notify_charger;
if (rport->utmi_avalid) vbus_attach = property_enabled(rphy->grf,
vbus_attach = property_enabled(rphy->grf, &rport->port_cfg->utmi_bvalid);
&rport->port_cfg->utmi_avalid);
else
vbus_attach = property_enabled(rphy->grf,
&rport->port_cfg->utmi_bvalid);
sch_work = false; sch_work = false;
notify_charger = false; notify_charger = false;
@ -1021,9 +1016,6 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy,
INIT_DELAYED_WORK(&rport->chg_work, rockchip_chg_detect_work); INIT_DELAYED_WORK(&rport->chg_work, rockchip_chg_detect_work);
INIT_DELAYED_WORK(&rport->otg_sm_work, rockchip_usb2phy_otg_sm_work); INIT_DELAYED_WORK(&rport->otg_sm_work, rockchip_usb2phy_otg_sm_work);
rport->utmi_avalid =
of_property_read_bool(child_np, "rockchip,utmi-avalid");
/* /*
* Some SoCs use one interrupt with otg-id/otg-bvalid/linestate * Some SoCs use one interrupt with otg-id/otg-bvalid/linestate
* interrupts muxed together, so probe the otg-mux interrupt first, * interrupts muxed together, so probe the otg-mux interrupt first,

View File

@ -33,12 +33,11 @@ config OMAP_CONTROL_PHY
config OMAP_USB2 config OMAP_USB2
tristate "OMAP USB2 PHY Driver" tristate "OMAP USB2 PHY Driver"
depends on ARCH_OMAP2PLUS depends on ARCH_OMAP2PLUS || ARCH_K3
depends on USB_SUPPORT depends on USB_SUPPORT
select GENERIC_PHY select GENERIC_PHY
select USB_PHY select USB_PHY
select OMAP_CONTROL_PHY select OMAP_CONTROL_PHY if ARCH_OMAP2PLUS
depends on OMAP_OCP2SCP
help help
Enable this to support the transceiver that is part of SOC. This Enable this to support the transceiver that is part of SOC. This
driver takes care of all the PHY functionality apart from comparator. driver takes care of all the PHY functionality apart from comparator.
@ -50,7 +49,6 @@ config TI_PIPE3
depends on ARCH_OMAP2PLUS || COMPILE_TEST depends on ARCH_OMAP2PLUS || COMPILE_TEST
select GENERIC_PHY select GENERIC_PHY
select OMAP_CONTROL_PHY select OMAP_CONTROL_PHY
depends on OMAP_OCP2SCP
help help
Enable this to support the PIPE3 PHY that is part of TI SOCs. This Enable this to support the PIPE3 PHY that is part of TI SOCs. This
driver takes care of all the PHY functionality apart from comparator. driver takes care of all the PHY functionality apart from comparator.

View File

@ -36,6 +36,10 @@
#define USB2PHY_DISCON_BYP_LATCH (1 << 31) #define USB2PHY_DISCON_BYP_LATCH (1 << 31)
#define USB2PHY_ANA_CONFIG1 0x4c #define USB2PHY_ANA_CONFIG1 0x4c
#define AM654_USB2_OTG_PD BIT(8)
#define AM654_USB2_VBUS_DET_EN BIT(5)
#define AM654_USB2_VBUSVALID_DET_EN BIT(4)
/** /**
* omap_usb2_set_comparator - links the comparator present in the sytem with * omap_usb2_set_comparator - links the comparator present in the sytem with
* this phy * this phy
@ -135,9 +139,9 @@ static int omap_usb_power_on(struct phy *x)
static int omap_usb2_disable_clocks(struct omap_usb *phy) static int omap_usb2_disable_clocks(struct omap_usb *phy)
{ {
clk_disable(phy->wkupclk); clk_disable_unprepare(phy->wkupclk);
if (!IS_ERR(phy->optclk)) if (!IS_ERR(phy->optclk))
clk_disable(phy->optclk); clk_disable_unprepare(phy->optclk);
return 0; return 0;
} }
@ -146,14 +150,14 @@ static int omap_usb2_enable_clocks(struct omap_usb *phy)
{ {
int ret; int ret;
ret = clk_enable(phy->wkupclk); ret = clk_prepare_enable(phy->wkupclk);
if (ret < 0) { if (ret < 0) {
dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret); dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret);
goto err0; goto err0;
} }
if (!IS_ERR(phy->optclk)) { if (!IS_ERR(phy->optclk)) {
ret = clk_enable(phy->optclk); ret = clk_prepare_enable(phy->optclk);
if (ret < 0) { if (ret < 0) {
dev_err(phy->dev, "Failed to enable optclk %d\n", ret); dev_err(phy->dev, "Failed to enable optclk %d\n", ret);
goto err1; goto err1;
@ -245,6 +249,15 @@ static const struct usb_phy_data am437x_usb2_data = {
.power_off = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD, .power_off = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD,
}; };
static const struct usb_phy_data am654_usb2_data = {
.label = "am654_usb2",
.flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT,
.mask = AM654_USB2_OTG_PD | AM654_USB2_VBUS_DET_EN |
AM654_USB2_VBUSVALID_DET_EN,
.power_on = AM654_USB2_VBUS_DET_EN | AM654_USB2_VBUSVALID_DET_EN,
.power_off = AM654_USB2_OTG_PD,
};
static const struct of_device_id omap_usb2_id_table[] = { static const struct of_device_id omap_usb2_id_table[] = {
{ {
.compatible = "ti,omap-usb2", .compatible = "ti,omap-usb2",
@ -266,6 +279,10 @@ static const struct of_device_id omap_usb2_id_table[] = {
.compatible = "ti,am437x-usb2", .compatible = "ti,am437x-usb2",
.data = &am437x_usb2_data, .data = &am437x_usb2_data,
}, },
{
.compatible = "ti,am654-usb2",
.data = &am654_usb2_data,
},
{}, {},
}; };
MODULE_DEVICE_TABLE(of, omap_usb2_id_table); MODULE_DEVICE_TABLE(of, omap_usb2_id_table);
@ -346,13 +363,52 @@ static int omap_usb2_probe(struct platform_device *pdev)
} }
} }
otg->set_host = omap_usb_set_host;
otg->set_peripheral = omap_usb_set_peripheral; phy->wkupclk = devm_clk_get(phy->dev, "wkupclk");
if (IS_ERR(phy->wkupclk)) {
if (PTR_ERR(phy->wkupclk) == -EPROBE_DEFER)
return -EPROBE_DEFER;
dev_warn(&pdev->dev, "unable to get wkupclk %ld, trying old name\n",
PTR_ERR(phy->wkupclk));
phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k");
if (IS_ERR(phy->wkupclk)) {
if (PTR_ERR(phy->wkupclk) != -EPROBE_DEFER)
dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n");
return PTR_ERR(phy->wkupclk);
} else {
dev_warn(&pdev->dev,
"found usb_phy_cm_clk32k, please fix DTS\n");
}
}
phy->optclk = devm_clk_get(phy->dev, "refclk");
if (IS_ERR(phy->optclk)) {
if (PTR_ERR(phy->optclk) == -EPROBE_DEFER)
return -EPROBE_DEFER;
dev_dbg(&pdev->dev, "unable to get refclk, trying old name\n");
phy->optclk = devm_clk_get(phy->dev, "usb_otg_ss_refclk960m");
if (IS_ERR(phy->optclk)) {
if (PTR_ERR(phy->optclk) != -EPROBE_DEFER) {
dev_dbg(&pdev->dev,
"unable to get usb_otg_ss_refclk960m\n");
}
} else {
dev_warn(&pdev->dev,
"found usb_otg_ss_refclk960m, please fix DTS\n");
}
}
otg->set_host = omap_usb_set_host;
otg->set_peripheral = omap_usb_set_peripheral;
if (phy_data->flags & OMAP_USB2_HAS_SET_VBUS) if (phy_data->flags & OMAP_USB2_HAS_SET_VBUS)
otg->set_vbus = omap_usb_set_vbus; otg->set_vbus = omap_usb_set_vbus;
if (phy_data->flags & OMAP_USB2_HAS_START_SRP) if (phy_data->flags & OMAP_USB2_HAS_START_SRP)
otg->start_srp = omap_usb_start_srp; otg->start_srp = omap_usb_start_srp;
otg->usb_phy = &phy->phy; otg->usb_phy = &phy->phy;
platform_set_drvdata(pdev, phy); platform_set_drvdata(pdev, phy);
pm_runtime_enable(phy->dev); pm_runtime_enable(phy->dev);
@ -367,42 +423,12 @@ static int omap_usb2_probe(struct platform_device *pdev)
omap_usb_power_off(generic_phy); omap_usb_power_off(generic_phy);
phy_provider = devm_of_phy_provider_register(phy->dev, phy_provider = devm_of_phy_provider_register(phy->dev,
of_phy_simple_xlate); of_phy_simple_xlate);
if (IS_ERR(phy_provider)) { if (IS_ERR(phy_provider)) {
pm_runtime_disable(phy->dev); pm_runtime_disable(phy->dev);
return PTR_ERR(phy_provider); return PTR_ERR(phy_provider);
} }
phy->wkupclk = devm_clk_get(phy->dev, "wkupclk");
if (IS_ERR(phy->wkupclk)) {
dev_warn(&pdev->dev, "unable to get wkupclk, trying old name\n");
phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k");
if (IS_ERR(phy->wkupclk)) {
dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n");
pm_runtime_disable(phy->dev);
return PTR_ERR(phy->wkupclk);
} else {
dev_warn(&pdev->dev,
"found usb_phy_cm_clk32k, please fix DTS\n");
}
}
clk_prepare(phy->wkupclk);
phy->optclk = devm_clk_get(phy->dev, "refclk");
if (IS_ERR(phy->optclk)) {
dev_dbg(&pdev->dev, "unable to get refclk, trying old name\n");
phy->optclk = devm_clk_get(phy->dev, "usb_otg_ss_refclk960m");
if (IS_ERR(phy->optclk)) {
dev_dbg(&pdev->dev,
"unable to get usb_otg_ss_refclk960m\n");
} else {
dev_warn(&pdev->dev,
"found usb_otg_ss_refclk960m, please fix DTS\n");
}
}
if (!IS_ERR(phy->optclk))
clk_prepare(phy->optclk);
usb_add_phy_dev(&phy->phy); usb_add_phy_dev(&phy->phy);
@ -413,9 +439,6 @@ static int omap_usb2_remove(struct platform_device *pdev)
{ {
struct omap_usb *phy = platform_get_drvdata(pdev); struct omap_usb *phy = platform_get_drvdata(pdev);
clk_unprepare(phy->wkupclk);
if (!IS_ERR(phy->optclk))
clk_unprepare(phy->optclk);
usb_remove_phy(&phy->phy); usb_remove_phy(&phy->phy);
pm_runtime_disable(phy->dev); pm_runtime_disable(phy->dev);

View File

@ -32,7 +32,7 @@ struct cht_int33fe_data {
struct i2c_client *fusb302; struct i2c_client *fusb302;
struct i2c_client *pi3usb30532; struct i2c_client *pi3usb30532;
/* Contain a list-head must be per device */ /* Contain a list-head must be per device */
struct device_connection connections[5]; struct device_connection connections[4];
}; };
/* /*
@ -174,16 +174,13 @@ static int cht_int33fe_probe(struct platform_device *pdev)
data->connections[0].endpoint[0] = "port0"; data->connections[0].endpoint[0] = "port0";
data->connections[0].endpoint[1] = "i2c-pi3usb30532"; data->connections[0].endpoint[1] = "i2c-pi3usb30532";
data->connections[0].id = "typec-switch"; data->connections[0].id = "orientation-switch";
data->connections[1].endpoint[0] = "port0"; data->connections[1].endpoint[0] = "port0";
data->connections[1].endpoint[1] = "i2c-pi3usb30532"; data->connections[1].endpoint[1] = "i2c-pi3usb30532";
data->connections[1].id = "typec-mux"; data->connections[1].id = "mode-switch";
data->connections[2].endpoint[0] = "port0"; data->connections[2].endpoint[0] = "i2c-fusb302";
data->connections[2].endpoint[1] = "i2c-pi3usb30532"; data->connections[2].endpoint[1] = "intel_xhci_usb_sw-role-switch";
data->connections[2].id = "idff01m01"; data->connections[2].id = "usb-role-switch";
data->connections[3].endpoint[0] = "i2c-fusb302";
data->connections[3].endpoint[1] = "intel_xhci_usb_sw-role-switch";
data->connections[3].id = "usb-role-switch";
device_connections_add(data->connections); device_connections_add(data->connections);

View File

@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
# #
# USB device configuration # USB device configuration
# #

View File

@ -1,54 +0,0 @@
To understand all the Linux-USB framework, you'll use these resources:
* This source code. This is necessarily an evolving work, and
includes kerneldoc that should help you get a current overview.
("make pdfdocs", and then look at "usb.pdf" for host side and
"gadget.pdf" for peripheral side.) Also, Documentation/usb has
more information.
* The USB 2.0 specification (from www.usb.org), with supplements
such as those for USB OTG and the various device classes.
The USB specification has a good overview chapter, and USB
peripherals conform to the widely known "Chapter 9".
* Chip specifications for USB controllers. Examples include
host controllers (on PCs, servers, and more); peripheral
controllers (in devices with Linux firmware, like printers or
cell phones); and hard-wired peripherals like Ethernet adapters.
* Specifications for other protocols implemented by USB peripheral
functions. Some are vendor-specific; others are vendor-neutral
but just standardized outside of the www.usb.org team.
Here is a list of what each subdirectory here is, and what is contained in
them.
core/ - This is for the core USB host code, including the
usbfs files and the hub class driver ("hub_wq").
host/ - This is for USB host controller drivers. This
includes UHCI, OHCI, EHCI, and others that might
be used with more specialized "embedded" systems.
gadget/ - This is for USB peripheral controller drivers and
the various gadget drivers which talk to them.
Individual USB driver directories. A new driver should be added to the
first subdirectory in the list below that it fits into.
image/ - This is for still image drivers, like scanners or
digital cameras.
../input/ - This is for any driver that uses the input subsystem,
like keyboard, mice, touchscreens, tablets, etc.
../media/ - This is for multimedia drivers, like video cameras,
radios, and any other drivers that talk to the v4l
subsystem.
../net/ - This is for network drivers.
serial/ - This is for USB to serial drivers.
storage/ - This is for USB mass-storage drivers.
class/ - This is for all USB device drivers that do not fit
into any of the above categories, and work for a range
of USB Class specified devices.
misc/ - This is for all USB device drivers that do not fit
into any of the above categories.

View File

@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
# #
# USB/ATM DSL configuration # USB/ATM DSL configuration
# #

View File

@ -1,3 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
config USB_CHIPIDEA config USB_CHIPIDEA
tristate "ChipIdea Highspeed Dual Role Controller" tristate "ChipIdea Highspeed Dual Role Controller"
depends on ((USB_EHCI_HCD && USB_GADGET) || (USB_EHCI_HCD && !USB_GADGET) || (!USB_EHCI_HCD && USB_GADGET)) && HAS_DMA depends on ((USB_EHCI_HCD && USB_GADGET) || (USB_EHCI_HCD && !USB_GADGET) || (!USB_EHCI_HCD && USB_GADGET)) && HAS_DMA

View File

@ -7,10 +7,8 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/dma-mapping.h>
#include <linux/usb/chipidea.h> #include <linux/usb/chipidea.h>
#include <linux/usb/of.h> #include <linux/usb/of.h>
#include <linux/clk.h> #include <linux/clk.h>
@ -152,8 +150,8 @@ static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev)
dev_warn(dev, "No over current polarity defined\n"); dev_warn(dev, "No over current polarity defined\n");
} }
if (of_find_property(np, "external-vbus-divider", NULL)) data->pwr_pol = of_property_read_bool(np, "power-active-high");
data->evdo = 1; data->evdo = of_property_read_bool(np, "external-vbus-divider");
if (of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI) if (of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI)
data->ulpi = 1; data->ulpi = 1;

View File

@ -18,6 +18,7 @@ struct imx_usbmisc_data {
/* true if dt specifies polarity */ /* true if dt specifies polarity */
unsigned int oc_pol_configured:1; unsigned int oc_pol_configured:1;
unsigned int pwr_pol:1; /* power polarity */
unsigned int evdo:1; /* set external vbus divider option */ unsigned int evdo:1; /* set external vbus divider option */
unsigned int ulpi:1; /* connected to an ULPI phy */ unsigned int ulpi:1; /* connected to an ULPI phy */
unsigned int hsic:1; /* HSIC controlller */ unsigned int hsic:1; /* HSIC controlller */

View File

@ -130,6 +130,7 @@ static int tegra_udc_remove(struct platform_device *pdev)
{ {
struct tegra_udc *udc = platform_get_drvdata(pdev); struct tegra_udc *udc = platform_get_drvdata(pdev);
ci_hdrc_remove_device(udc->dev);
usb_phy_set_suspend(udc->phy, 1); usb_phy_set_suspend(udc->phy, 1);
clk_disable_unprepare(udc->clk); clk_disable_unprepare(udc->clk);

View File

@ -954,25 +954,47 @@ static int ci_hdrc_probe(struct platform_device *pdev)
} else if (ci->platdata->usb_phy) { } else if (ci->platdata->usb_phy) {
ci->usb_phy = ci->platdata->usb_phy; ci->usb_phy = ci->platdata->usb_phy;
} else { } else {
/* Look for a generic PHY first */
ci->phy = devm_phy_get(dev->parent, "usb-phy"); ci->phy = devm_phy_get(dev->parent, "usb-phy");
ci->usb_phy = devm_usb_get_phy(dev->parent, USB_PHY_TYPE_USB2);
/* if both generic PHY and USB PHY layers aren't enabled */ if (PTR_ERR(ci->phy) == -EPROBE_DEFER) {
if (PTR_ERR(ci->phy) == -ENOSYS && ret = -EPROBE_DEFER;
PTR_ERR(ci->usb_phy) == -ENXIO) { goto ulpi_exit;
} else if (IS_ERR(ci->phy)) {
ci->phy = NULL;
}
/* Look for a legacy USB PHY from device-tree next */
if (!ci->phy) {
ci->usb_phy = devm_usb_get_phy_by_phandle(dev->parent,
"phys", 0);
if (PTR_ERR(ci->usb_phy) == -EPROBE_DEFER) {
ret = -EPROBE_DEFER;
goto ulpi_exit;
} else if (IS_ERR(ci->usb_phy)) {
ci->usb_phy = NULL;
}
}
/* Look for any registered legacy USB PHY as last resort */
if (!ci->phy && !ci->usb_phy) {
ci->usb_phy = devm_usb_get_phy(dev->parent,
USB_PHY_TYPE_USB2);
if (PTR_ERR(ci->usb_phy) == -EPROBE_DEFER) {
ret = -EPROBE_DEFER;
goto ulpi_exit;
} else if (IS_ERR(ci->usb_phy)) {
ci->usb_phy = NULL;
}
}
/* No USB PHY was found in the end */
if (!ci->phy && !ci->usb_phy) {
ret = -ENXIO; ret = -ENXIO;
goto ulpi_exit; goto ulpi_exit;
} }
if (IS_ERR(ci->phy) && IS_ERR(ci->usb_phy)) {
ret = -EPROBE_DEFER;
goto ulpi_exit;
}
if (IS_ERR(ci->phy))
ci->phy = NULL;
else if (IS_ERR(ci->usb_phy))
ci->usb_phy = NULL;
} }
ret = ci_usb_phy_init(ci); ret = ci_usb_phy_init(ci);

View File

@ -63,6 +63,7 @@
#define MX6_BM_NON_BURST_SETTING BIT(1) #define MX6_BM_NON_BURST_SETTING BIT(1)
#define MX6_BM_OVER_CUR_DIS BIT(7) #define MX6_BM_OVER_CUR_DIS BIT(7)
#define MX6_BM_OVER_CUR_POLARITY BIT(8) #define MX6_BM_OVER_CUR_POLARITY BIT(8)
#define MX6_BM_PWR_POLARITY BIT(9)
#define MX6_BM_WAKEUP_ENABLE BIT(10) #define MX6_BM_WAKEUP_ENABLE BIT(10)
#define MX6_BM_UTMI_ON_CLOCK BIT(13) #define MX6_BM_UTMI_ON_CLOCK BIT(13)
#define MX6_BM_ID_WAKEUP BIT(16) #define MX6_BM_ID_WAKEUP BIT(16)
@ -383,6 +384,9 @@ static int usbmisc_imx6q_init(struct imx_usbmisc_data *data)
else if (data->oc_pol_configured) else if (data->oc_pol_configured)
reg &= ~MX6_BM_OVER_CUR_POLARITY; reg &= ~MX6_BM_OVER_CUR_POLARITY;
} }
/* If the polarity is not set keep it as setup by the bootlader */
if (data->pwr_pol == 1)
reg |= MX6_BM_PWR_POLARITY;
writel(reg, usbmisc->base + data->index * 4); writel(reg, usbmisc->base + data->index * 4);
/* SoC non-burst setting */ /* SoC non-burst setting */
@ -585,6 +589,9 @@ static int usbmisc_imx7d_init(struct imx_usbmisc_data *data)
else if (data->oc_pol_configured) else if (data->oc_pol_configured)
reg &= ~MX6_BM_OVER_CUR_POLARITY; reg &= ~MX6_BM_OVER_CUR_POLARITY;
} }
/* If the polarity is not set keep it as setup by the bootlader */
if (data->pwr_pol == 1)
reg |= MX6_BM_PWR_POLARITY;
writel(reg, usbmisc->base); writel(reg, usbmisc->base);
reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2); reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);

View File

@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
# #
# USB Class driver configuration # USB Class driver configuration
# #

View File

@ -1099,7 +1099,7 @@ static int wdm_post_reset(struct usb_interface *intf)
rv = recover_from_urb_loss(desc); rv = recover_from_urb_loss(desc);
mutex_unlock(&desc->wlock); mutex_unlock(&desc->wlock);
mutex_unlock(&desc->rlock); mutex_unlock(&desc->rlock);
return 0; return rv;
} }
static struct usb_driver wdm_driver = { static struct usb_driver wdm_driver = {

View File

@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
# #
# USB Core configuration # USB Core configuration
# #
@ -90,3 +91,15 @@ config USB_LEDS_TRIGGER_USBPORT
This driver allows LEDs to be controlled by USB events. Enabling this This driver allows LEDs to be controlled by USB events. Enabling this
trigger allows specifying list of USB ports that should turn on LED trigger allows specifying list of USB ports that should turn on LED
when some USB device gets connected. when some USB device gets connected.
config USB_AUTOSUSPEND_DELAY
int "Default autosuspend delay"
depends on USB
default 2
help
The default autosuspend delay in seconds. Can be overridden
with the usbcore.autosuspend command line or module parameter.
The default value Linux has always had is 2 seconds. Change
this value if you want a different delay and cannot modify
the command line or module parameter.

View File

@ -552,7 +552,7 @@ static int usb_parse_configuration(struct usb_device *dev, int cfgidx,
unsigned char *buffer2; unsigned char *buffer2;
int size2; int size2;
struct usb_descriptor_header *header; struct usb_descriptor_header *header;
int len, retval; int retval;
u8 inums[USB_MAXINTERFACES], nalts[USB_MAXINTERFACES]; u8 inums[USB_MAXINTERFACES], nalts[USB_MAXINTERFACES];
unsigned iad_num = 0; unsigned iad_num = 0;
@ -707,8 +707,8 @@ static int usb_parse_configuration(struct usb_device *dev, int cfgidx,
nalts[i] = j = USB_MAXALTSETTING; nalts[i] = j = USB_MAXALTSETTING;
} }
len = sizeof(*intfc) + sizeof(struct usb_host_interface) * j; intfc = kzalloc(struct_size(intfc, altsetting, j), GFP_KERNEL);
config->intf_cache[i] = intfc = kzalloc(len, GFP_KERNEL); config->intf_cache[i] = intfc;
if (!intfc) if (!intfc)
return -ENOMEM; return -ENOMEM;
kref_init(&intfc->ref); kref_init(&intfc->ref);
@ -800,13 +800,11 @@ int usb_get_configuration(struct usb_device *dev)
{ {
struct device *ddev = &dev->dev; struct device *ddev = &dev->dev;
int ncfg = dev->descriptor.bNumConfigurations; int ncfg = dev->descriptor.bNumConfigurations;
int result = 0; int result = -ENOMEM;
unsigned int cfgno, length; unsigned int cfgno, length;
unsigned char *bigbuffer; unsigned char *bigbuffer;
struct usb_config_descriptor *desc; struct usb_config_descriptor *desc;
cfgno = 0;
result = -ENOMEM;
if (ncfg > USB_MAXCONFIG) { if (ncfg > USB_MAXCONFIG) {
dev_warn(ddev, "too many configurations: %d, " dev_warn(ddev, "too many configurations: %d, "
"using maximum allowed: %d\n", ncfg, USB_MAXCONFIG); "using maximum allowed: %d\n", ncfg, USB_MAXCONFIG);
@ -832,8 +830,7 @@ int usb_get_configuration(struct usb_device *dev)
if (!desc) if (!desc)
goto err2; goto err2;
result = 0; for (cfgno = 0; cfgno < ncfg; cfgno++) {
for (; cfgno < ncfg; cfgno++) {
/* We grab just the first descriptor so we know how long /* We grab just the first descriptor so we know how long
* the whole configuration is */ * the whole configuration is */
result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,
@ -889,7 +886,6 @@ int usb_get_configuration(struct usb_device *dev)
goto err; goto err;
} }
} }
result = 0;
err: err:
kfree(desc); kfree(desc);

View File

@ -604,7 +604,7 @@ static void async_completed(struct urb *urb)
snoop(&urb->dev->dev, "urb complete\n"); snoop(&urb->dev->dev, "urb complete\n");
snoop_urb(urb->dev, as->userurb, urb->pipe, urb->actual_length, snoop_urb(urb->dev, as->userurb, urb->pipe, urb->actual_length,
as->status, COMPLETE, NULL, 0); as->status, COMPLETE, NULL, 0);
if ((urb->transfer_flags & URB_DIR_MASK) == URB_DIR_IN) if (usb_urb_dir_in(urb))
snoop_urb_data(urb, urb->actual_length); snoop_urb_data(urb, urb->actual_length);
if (as->status < 0 && as->bulk_addr && as->status != -ECONNRESET && if (as->status < 0 && as->bulk_addr && as->status != -ECONNRESET &&
@ -1564,12 +1564,10 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
} }
for (totlen = u = 0; u < number_of_packets; u++) { for (totlen = u = 0; u < number_of_packets; u++) {
/* /*
* arbitrary limit need for USB 3.0 * arbitrary limit need for USB 3.1 Gen2
* bMaxBurst (0~15 allowed, 1~16 packets) * sizemax: 96 DPs at SSP, 96 * 1024 = 98304
* bmAttributes (bit 1:0, mult 0~2, 1~3 packets)
* sizemax: 1024 * 16 * 3 = 49152
*/ */
if (isopkt[u].length > 49152) { if (isopkt[u].length > 98304) {
ret = -EINVAL; ret = -EINVAL;
goto error; goto error;
} }

View File

@ -1896,14 +1896,11 @@ int usb_runtime_idle(struct device *dev)
return -EBUSY; return -EBUSY;
} }
int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable) static int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable)
{ {
struct usb_hcd *hcd = bus_to_hcd(udev->bus); struct usb_hcd *hcd = bus_to_hcd(udev->bus);
int ret = -EPERM; int ret = -EPERM;
if (enable && !udev->usb2_hw_lpm_allowed)
return 0;
if (hcd->driver->set_usb2_hw_lpm) { if (hcd->driver->set_usb2_hw_lpm) {
ret = hcd->driver->set_usb2_hw_lpm(hcd, udev, enable); ret = hcd->driver->set_usb2_hw_lpm(hcd, udev, enable);
if (!ret) if (!ret)
@ -1913,6 +1910,24 @@ int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable)
return ret; return ret;
} }
int usb_enable_usb2_hardware_lpm(struct usb_device *udev)
{
if (!udev->usb2_hw_lpm_capable ||
!udev->usb2_hw_lpm_allowed ||
udev->usb2_hw_lpm_enabled)
return 0;
return usb_set_usb2_hardware_lpm(udev, 1);
}
int usb_disable_usb2_hardware_lpm(struct usb_device *udev)
{
if (!udev->usb2_hw_lpm_enabled)
return 0;
return usb_set_usb2_hardware_lpm(udev, 0);
}
#endif /* CONFIG_PM */ #endif /* CONFIG_PM */
struct bus_type usb_bus_type = { struct bus_type usb_bus_type = {

View File

@ -118,6 +118,31 @@ int usb_choose_configuration(struct usb_device *udev)
continue; continue;
} }
/*
* Select first configuration as default for audio so that
* devices that don't comply with UAC3 protocol are supported.
* But, still iterate through other configurations and
* select UAC3 compliant config if present.
*/
if (desc && is_audio(desc)) {
/* Always prefer the first found UAC3 config */
if (is_uac3_config(desc)) {
best = c;
break;
}
/* If there is no UAC3 config, prefer the first config */
else if (i == 0)
best = c;
/* Unconditional continue, because the rest of the code
* in the loop is irrelevant for audio devices, and
* because it can reassign best, which for audio devices
* we don't want.
*/
continue;
}
/* When the first config's first interface is one of Microsoft's /* When the first config's first interface is one of Microsoft's
* pet nonstandard Ethernet-over-USB protocols, ignore it unless * pet nonstandard Ethernet-over-USB protocols, ignore it unless
* this kernel has enabled the necessary host side driver. * this kernel has enabled the necessary host side driver.
@ -132,25 +157,6 @@ int usb_choose_configuration(struct usb_device *udev)
#endif #endif
} }
/*
* Select first configuration as default for audio so that
* devices that don't comply with UAC3 protocol are supported.
* But, still iterate through other configurations and
* select UAC3 compliant config if present.
*/
if (i == 0 && num_configs > 1 && desc && is_audio(desc)) {
best = c;
continue;
}
if (i > 0 && desc && is_audio(desc)) {
if (is_uac3_config(desc)) {
best = c;
break;
}
continue;
}
/* From the remaining configs, choose the first one whose /* From the remaining configs, choose the first one whose
* first interface is for a non-vendor-specific class. * first interface is for a non-vendor-specific class.
* Reason: Linux is more likely to have a class driver * Reason: Linux is more likely to have a class driver

View File

@ -373,13 +373,19 @@ static const u8 ss_rh_config_descriptor[] = {
* -1 is authorized for all devices except wireless (old behaviour) * -1 is authorized for all devices except wireless (old behaviour)
* 0 is unauthorized for all devices * 0 is unauthorized for all devices
* 1 is authorized for all devices * 1 is authorized for all devices
* 2 is authorized for internal devices
*/ */
static int authorized_default = -1; #define USB_AUTHORIZE_WIRED -1
#define USB_AUTHORIZE_NONE 0
#define USB_AUTHORIZE_ALL 1
#define USB_AUTHORIZE_INTERNAL 2
static int authorized_default = USB_AUTHORIZE_WIRED;
module_param(authorized_default, int, S_IRUGO|S_IWUSR); module_param(authorized_default, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(authorized_default, MODULE_PARM_DESC(authorized_default,
"Default USB device authorization: 0 is not authorized, 1 is " "Default USB device authorization: 0 is not authorized, 1 is "
"authorized, -1 is authorized except for wireless USB (default, " "authorized, 2 is authorized for internal devices, -1 is "
"old behaviour"); "authorized except for wireless USB (default, old behaviour)");
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
/** /**
@ -884,7 +890,7 @@ static ssize_t authorized_default_show(struct device *dev,
struct usb_hcd *hcd; struct usb_hcd *hcd;
hcd = bus_to_hcd(usb_bus); hcd = bus_to_hcd(usb_bus);
return snprintf(buf, PAGE_SIZE, "%u\n", !!HCD_DEV_AUTHORIZED(hcd)); return snprintf(buf, PAGE_SIZE, "%u\n", hcd->dev_policy);
} }
static ssize_t authorized_default_store(struct device *dev, static ssize_t authorized_default_store(struct device *dev,
@ -900,11 +906,8 @@ static ssize_t authorized_default_store(struct device *dev,
hcd = bus_to_hcd(usb_bus); hcd = bus_to_hcd(usb_bus);
result = sscanf(buf, "%u\n", &val); result = sscanf(buf, "%u\n", &val);
if (result == 1) { if (result == 1) {
if (val) hcd->dev_policy = val <= USB_DEVICE_AUTHORIZE_INTERNAL ?
set_bit(HCD_FLAG_DEV_AUTHORIZED, &hcd->flags); val : USB_DEVICE_AUTHORIZE_ALL;
else
clear_bit(HCD_FLAG_DEV_AUTHORIZED, &hcd->flags);
result = size; result = size;
} else { } else {
result = -EINVAL; result = -EINVAL;
@ -2736,6 +2739,11 @@ int usb_add_hcd(struct usb_hcd *hcd,
if (retval) if (retval)
return retval; return retval;
retval = usb_phy_roothub_set_mode(hcd->phy_roothub,
PHY_MODE_USB_HOST_SS);
if (retval)
goto err_usb_phy_roothub_power_on;
retval = usb_phy_roothub_power_on(hcd->phy_roothub); retval = usb_phy_roothub_power_on(hcd->phy_roothub);
if (retval) if (retval)
goto err_usb_phy_roothub_power_on; goto err_usb_phy_roothub_power_on;
@ -2743,18 +2751,26 @@ int usb_add_hcd(struct usb_hcd *hcd,
dev_info(hcd->self.controller, "%s\n", hcd->product_desc); dev_info(hcd->self.controller, "%s\n", hcd->product_desc);
/* Keep old behaviour if authorized_default is not in [0, 1]. */ switch (authorized_default) {
if (authorized_default < 0 || authorized_default > 1) { case USB_AUTHORIZE_NONE:
if (hcd->wireless) hcd->dev_policy = USB_DEVICE_AUTHORIZE_NONE;
clear_bit(HCD_FLAG_DEV_AUTHORIZED, &hcd->flags); break;
else
set_bit(HCD_FLAG_DEV_AUTHORIZED, &hcd->flags); case USB_AUTHORIZE_ALL:
} else { hcd->dev_policy = USB_DEVICE_AUTHORIZE_ALL;
if (authorized_default) break;
set_bit(HCD_FLAG_DEV_AUTHORIZED, &hcd->flags);
else case USB_AUTHORIZE_INTERNAL:
clear_bit(HCD_FLAG_DEV_AUTHORIZED, &hcd->flags); hcd->dev_policy = USB_DEVICE_AUTHORIZE_INTERNAL;
break;
case USB_AUTHORIZE_WIRED:
default:
hcd->dev_policy = hcd->wireless ?
USB_DEVICE_AUTHORIZE_NONE : USB_DEVICE_AUTHORIZE_ALL;
break;
} }
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
/* per default all interfaces are authorized */ /* per default all interfaces are authorized */

View File

@ -108,6 +108,8 @@ EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);
static void hub_release(struct kref *kref); static void hub_release(struct kref *kref);
static int usb_reset_and_verify_device(struct usb_device *udev); static int usb_reset_and_verify_device(struct usb_device *udev);
static int hub_port_disable(struct usb_hub *hub, int port1, int set_state); static int hub_port_disable(struct usb_hub *hub, int port1, int set_state);
static bool hub_port_warm_reset_required(struct usb_hub *hub, int port1,
u16 portstatus);
static inline char *portspeed(struct usb_hub *hub, int portstatus) static inline char *portspeed(struct usb_hub *hub, int portstatus)
{ {
@ -607,6 +609,36 @@ static int hub_port_status(struct usb_hub *hub, int port1,
status, change, NULL); status, change, NULL);
} }
static void hub_resubmit_irq_urb(struct usb_hub *hub)
{
unsigned long flags;
int status;
spin_lock_irqsave(&hub->irq_urb_lock, flags);
if (hub->quiescing) {
spin_unlock_irqrestore(&hub->irq_urb_lock, flags);
return;
}
status = usb_submit_urb(hub->urb, GFP_ATOMIC);
if (status && status != -ENODEV && status != -EPERM &&
status != -ESHUTDOWN) {
dev_err(hub->intfdev, "resubmit --> %d\n", status);
mod_timer(&hub->irq_urb_retry, jiffies + HZ);
}
spin_unlock_irqrestore(&hub->irq_urb_lock, flags);
}
static void hub_retry_irq_urb(struct timer_list *t)
{
struct usb_hub *hub = from_timer(hub, t, irq_urb_retry);
hub_resubmit_irq_urb(hub);
}
static void kick_hub_wq(struct usb_hub *hub) static void kick_hub_wq(struct usb_hub *hub)
{ {
struct usb_interface *intf; struct usb_interface *intf;
@ -709,12 +741,7 @@ static void hub_irq(struct urb *urb)
kick_hub_wq(hub); kick_hub_wq(hub);
resubmit: resubmit:
if (hub->quiescing) hub_resubmit_irq_urb(hub);
return;
status = usb_submit_urb(hub->urb, GFP_ATOMIC);
if (status != 0 && status != -ENODEV && status != -EPERM)
dev_err(hub->intfdev, "resubmit --> %d\n", status);
} }
/* USB 2.0 spec Section 11.24.2.3 */ /* USB 2.0 spec Section 11.24.2.3 */
@ -1112,6 +1139,11 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
USB_PORT_FEAT_ENABLE); USB_PORT_FEAT_ENABLE);
} }
/* Make sure a warm-reset request is handled by port_event */
if (type == HUB_RESUME &&
hub_port_warm_reset_required(hub, port1, portstatus))
set_bit(port1, hub->event_bits);
/* /*
* Add debounce if USB3 link is in polling/link training state. * Add debounce if USB3 link is in polling/link training state.
* Link will automatically transition to Enabled state after * Link will automatically transition to Enabled state after
@ -1264,10 +1296,13 @@ enum hub_quiescing_type {
static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type) static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type)
{ {
struct usb_device *hdev = hub->hdev; struct usb_device *hdev = hub->hdev;
unsigned long flags;
int i; int i;
/* hub_wq and related activity won't re-trigger */ /* hub_wq and related activity won't re-trigger */
spin_lock_irqsave(&hub->irq_urb_lock, flags);
hub->quiescing = 1; hub->quiescing = 1;
spin_unlock_irqrestore(&hub->irq_urb_lock, flags);
if (type != HUB_SUSPEND) { if (type != HUB_SUSPEND) {
/* Disconnect all the children */ /* Disconnect all the children */
@ -1278,6 +1313,7 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type)
} }
/* Stop hub_wq and related activity */ /* Stop hub_wq and related activity */
del_timer_sync(&hub->irq_urb_retry);
usb_kill_urb(hub->urb); usb_kill_urb(hub->urb);
if (hub->has_indicators) if (hub->has_indicators)
cancel_delayed_work_sync(&hub->leds); cancel_delayed_work_sync(&hub->leds);
@ -1810,6 +1846,8 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
INIT_DELAYED_WORK(&hub->leds, led_work); INIT_DELAYED_WORK(&hub->leds, led_work);
INIT_DELAYED_WORK(&hub->init_work, NULL); INIT_DELAYED_WORK(&hub->init_work, NULL);
INIT_WORK(&hub->events, hub_event); INIT_WORK(&hub->events, hub_event);
spin_lock_init(&hub->irq_urb_lock);
timer_setup(&hub->irq_urb_retry, hub_retry_irq_urb, 0);
usb_get_intf(intf); usb_get_intf(intf);
usb_get_dev(hdev); usb_get_dev(hdev);
@ -3220,8 +3258,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
} }
/* disable USB2 hardware LPM */ /* disable USB2 hardware LPM */
if (udev->usb2_hw_lpm_enabled == 1) usb_disable_usb2_hardware_lpm(udev);
usb_set_usb2_hardware_lpm(udev, 0);
if (usb_disable_ltm(udev)) { if (usb_disable_ltm(udev)) {
dev_err(&udev->dev, "Failed to disable LTM before suspend\n"); dev_err(&udev->dev, "Failed to disable LTM before suspend\n");
@ -3259,8 +3296,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
usb_enable_ltm(udev); usb_enable_ltm(udev);
err_ltm: err_ltm:
/* Try to enable USB2 hardware LPM again */ /* Try to enable USB2 hardware LPM again */
if (udev->usb2_hw_lpm_capable == 1) usb_enable_usb2_hardware_lpm(udev);
usb_set_usb2_hardware_lpm(udev, 1);
if (udev->do_remote_wakeup) if (udev->do_remote_wakeup)
(void) usb_disable_remote_wakeup(udev); (void) usb_disable_remote_wakeup(udev);
@ -3543,8 +3579,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
hub_port_logical_disconnect(hub, port1); hub_port_logical_disconnect(hub, port1);
} else { } else {
/* Try to enable USB2 hardware LPM */ /* Try to enable USB2 hardware LPM */
if (udev->usb2_hw_lpm_capable == 1) usb_enable_usb2_hardware_lpm(udev);
usb_set_usb2_hardware_lpm(udev, 1);
/* Try to enable USB3 LTM */ /* Try to enable USB3 LTM */
usb_enable_ltm(udev); usb_enable_ltm(udev);
@ -4435,7 +4470,7 @@ static void hub_set_initial_usb2_lpm_policy(struct usb_device *udev)
if ((udev->bos->ext_cap->bmAttributes & cpu_to_le32(USB_BESL_SUPPORT)) || if ((udev->bos->ext_cap->bmAttributes & cpu_to_le32(USB_BESL_SUPPORT)) ||
connect_type == USB_PORT_CONNECT_TYPE_HARD_WIRED) { connect_type == USB_PORT_CONNECT_TYPE_HARD_WIRED) {
udev->usb2_hw_lpm_allowed = 1; udev->usb2_hw_lpm_allowed = 1;
usb_set_usb2_hardware_lpm(udev, 1); usb_enable_usb2_hardware_lpm(udev);
} }
} }
@ -5649,8 +5684,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
/* Disable USB2 hardware LPM. /* Disable USB2 hardware LPM.
* It will be re-enabled by the enumeration process. * It will be re-enabled by the enumeration process.
*/ */
if (udev->usb2_hw_lpm_enabled == 1) usb_disable_usb2_hardware_lpm(udev);
usb_set_usb2_hardware_lpm(udev, 0);
/* Disable LPM while we reset the device and reinstall the alt settings. /* Disable LPM while we reset the device and reinstall the alt settings.
* Device-initiated LPM, and system exit latency settings are cleared * Device-initiated LPM, and system exit latency settings are cleared
@ -5753,7 +5787,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
done: done:
/* Now that the alt settings are re-installed, enable LTM and LPM. */ /* Now that the alt settings are re-installed, enable LTM and LPM. */
usb_set_usb2_hardware_lpm(udev, 1); usb_enable_usb2_hardware_lpm(udev);
usb_unlocked_enable_lpm(udev); usb_unlocked_enable_lpm(udev);
usb_enable_ltm(udev); usb_enable_ltm(udev);
usb_release_bos_descriptor(udev); usb_release_bos_descriptor(udev);

View File

@ -69,6 +69,8 @@ struct usb_hub {
struct delayed_work leds; struct delayed_work leds;
struct delayed_work init_work; struct delayed_work init_work;
struct work_struct events; struct work_struct events;
spinlock_t irq_urb_lock;
struct timer_list irq_urb_retry;
struct usb_port **ports; struct usb_port **ports;
}; };

View File

@ -1243,8 +1243,7 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0)
dev->actconfig->interface[i] = NULL; dev->actconfig->interface[i] = NULL;
} }
if (dev->usb2_hw_lpm_enabled == 1) usb_disable_usb2_hardware_lpm(dev);
usb_set_usb2_hardware_lpm(dev, 0);
usb_unlocked_disable_lpm(dev); usb_unlocked_disable_lpm(dev);
usb_disable_ltm(dev); usb_disable_ltm(dev);
@ -2007,6 +2006,13 @@ free_interfaces:
for (i = 0; i < nintf; ++i) { for (i = 0; i < nintf; ++i) {
struct usb_interface *intf = cp->interface[i]; struct usb_interface *intf = cp->interface[i];
if (intf->dev.of_node &&
!of_device_is_available(intf->dev.of_node)) {
dev_info(&dev->dev, "skipping disabled interface %d\n",
intf->cur_altsetting->desc.bInterfaceNumber);
continue;
}
dev_dbg(&dev->dev, dev_dbg(&dev->dev,
"adding %s (config #%d, interface %d)\n", "adding %s (config #%d, interface %d)\n",
dev_name(&intf->dev), configuration, dev_name(&intf->dev), configuration,

View File

@ -123,6 +123,34 @@ int usb_phy_roothub_exit(struct usb_phy_roothub *phy_roothub)
} }
EXPORT_SYMBOL_GPL(usb_phy_roothub_exit); EXPORT_SYMBOL_GPL(usb_phy_roothub_exit);
int usb_phy_roothub_set_mode(struct usb_phy_roothub *phy_roothub,
enum phy_mode mode)
{
struct usb_phy_roothub *roothub_entry;
struct list_head *head;
int err;
if (!phy_roothub)
return 0;
head = &phy_roothub->list;
list_for_each_entry(roothub_entry, head, list) {
err = phy_set_mode(roothub_entry->phy, mode);
if (err)
goto err_out;
}
return 0;
err_out:
list_for_each_entry_continue_reverse(roothub_entry, head, list)
phy_power_off(roothub_entry->phy);
return err;
}
EXPORT_SYMBOL_GPL(usb_phy_roothub_set_mode);
int usb_phy_roothub_power_on(struct usb_phy_roothub *phy_roothub) int usb_phy_roothub_power_on(struct usb_phy_roothub *phy_roothub)
{ {
struct usb_phy_roothub *roothub_entry; struct usb_phy_roothub *roothub_entry;

View File

@ -16,6 +16,8 @@ struct usb_phy_roothub *usb_phy_roothub_alloc(struct device *dev);
int usb_phy_roothub_init(struct usb_phy_roothub *phy_roothub); int usb_phy_roothub_init(struct usb_phy_roothub *phy_roothub);
int usb_phy_roothub_exit(struct usb_phy_roothub *phy_roothub); int usb_phy_roothub_exit(struct usb_phy_roothub *phy_roothub);
int usb_phy_roothub_set_mode(struct usb_phy_roothub *phy_roothub,
enum phy_mode mode);
int usb_phy_roothub_power_on(struct usb_phy_roothub *phy_roothub); int usb_phy_roothub_power_on(struct usb_phy_roothub *phy_roothub);
void usb_phy_roothub_power_off(struct usb_phy_roothub *phy_roothub); void usb_phy_roothub_power_off(struct usb_phy_roothub *phy_roothub);

View File

@ -528,7 +528,10 @@ static ssize_t usb2_hardware_lpm_store(struct device *dev,
if (!ret) { if (!ret) {
udev->usb2_hw_lpm_allowed = value; udev->usb2_hw_lpm_allowed = value;
ret = usb_set_usb2_hardware_lpm(udev, value); if (value)
ret = usb_enable_usb2_hardware_lpm(udev);
else
ret = usb_disable_usb2_hardware_lpm(udev);
} }
usb_unlock_device(udev); usb_unlock_device(udev);

View File

@ -70,9 +70,8 @@ struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
{ {
struct urb *urb; struct urb *urb;
urb = kmalloc(sizeof(struct urb) + urb = kmalloc(struct_size(urb, iso_frame_desc, iso_packets),
iso_packets * sizeof(struct usb_iso_packet_descriptor), mem_flags);
mem_flags);
if (!urb) if (!urb)
return NULL; return NULL;
usb_init_urb(urb); usb_init_urb(urb);

View File

@ -46,8 +46,7 @@
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include "usb.h" #include "hub.h"
const char *usbcore_name = "usbcore"; const char *usbcore_name = "usbcore";
@ -65,8 +64,8 @@ int usb_disabled(void)
EXPORT_SYMBOL_GPL(usb_disabled); EXPORT_SYMBOL_GPL(usb_disabled);
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int usb_autosuspend_delay = 2; /* Default delay value, /* Default delay value, in seconds */
* in seconds */ static int usb_autosuspend_delay = CONFIG_USB_AUTOSUSPEND_DELAY;
module_param_named(autosuspend, usb_autosuspend_delay, int, 0644); module_param_named(autosuspend, usb_autosuspend_delay, int, 0644);
MODULE_PARM_DESC(autosuspend, "default autosuspend delay"); MODULE_PARM_DESC(autosuspend, "default autosuspend delay");
@ -536,6 +535,27 @@ static unsigned usb_bus_is_wusb(struct usb_bus *bus)
return hcd->wireless; return hcd->wireless;
} }
static bool usb_dev_authorized(struct usb_device *dev, struct usb_hcd *hcd)
{
struct usb_hub *hub;
if (!dev->parent)
return true; /* Root hub always ok [and always wired] */
switch (hcd->dev_policy) {
case USB_DEVICE_AUTHORIZE_NONE:
default:
return false;
case USB_DEVICE_AUTHORIZE_ALL:
return true;
case USB_DEVICE_AUTHORIZE_INTERNAL:
hub = usb_hub_to_struct_hub(dev->parent);
return hub->ports[dev->portnum - 1]->connect_type ==
USB_PORT_CONNECT_TYPE_HARD_WIRED;
}
}
/** /**
* usb_alloc_dev - usb device constructor (usbcore-internal) * usb_alloc_dev - usb device constructor (usbcore-internal)
@ -663,12 +683,11 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
dev->connect_time = jiffies; dev->connect_time = jiffies;
dev->active_duration = -jiffies; dev->active_duration = -jiffies;
#endif #endif
if (root_hub) /* Root hub always ok [and always wired] */
dev->authorized = 1; dev->authorized = usb_dev_authorized(dev, usb_hcd);
else { if (!root_hub)
dev->authorized = !!HCD_DEV_AUTHORIZED(usb_hcd);
dev->wusb = usb_bus_is_wusb(bus) ? 1 : 0; dev->wusb = usb_bus_is_wusb(bus) ? 1 : 0;
}
return dev; return dev;
} }
EXPORT_SYMBOL_GPL(usb_alloc_dev); EXPORT_SYMBOL_GPL(usb_alloc_dev);

View File

@ -92,7 +92,8 @@ extern int usb_remote_wakeup(struct usb_device *dev);
extern int usb_runtime_suspend(struct device *dev); extern int usb_runtime_suspend(struct device *dev);
extern int usb_runtime_resume(struct device *dev); extern int usb_runtime_resume(struct device *dev);
extern int usb_runtime_idle(struct device *dev); extern int usb_runtime_idle(struct device *dev);
extern int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable); extern int usb_enable_usb2_hardware_lpm(struct usb_device *udev);
extern int usb_disable_usb2_hardware_lpm(struct usb_device *udev);
#else #else
@ -112,7 +113,12 @@ static inline int usb_autoresume_device(struct usb_device *udev)
return 0; return 0;
} }
static inline int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable) static inline int usb_enable_usb2_hardware_lpm(struct usb_device *udev)
{
return 0;
}
static inline int usb_disable_usb2_hardware_lpm(struct usb_device *udev)
{ {
return 0; return 0;
} }

View File

@ -1,3 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
config USB_DWC2 config USB_DWC2
tristate "DesignWare USB2 DRD Core Support" tristate "DesignWare USB2 DRD Core Support"
depends on HAS_DMA depends on HAS_DMA

View File

@ -768,22 +768,13 @@ static u32 dwc2_gadget_get_desc_params(struct dwc2_hsotg_ep *hs_ep, u32 *mask)
return desc_size; return desc_size;
} }
/* static void dwc2_gadget_fill_nonisoc_xfer_ddma_one(struct dwc2_hsotg_ep *hs_ep,
* dwc2_gadget_config_nonisoc_xfer_ddma - prepare non ISOC DMA desc chain. struct dwc2_dma_desc **desc,
* @hs_ep: The endpoint
* @dma_buff: DMA address to use
* @len: Length of the transfer
*
* This function will iterate over descriptor chain and fill its entries
* with corresponding information based on transfer data.
*/
static void dwc2_gadget_config_nonisoc_xfer_ddma(struct dwc2_hsotg_ep *hs_ep,
dma_addr_t dma_buff, dma_addr_t dma_buff,
unsigned int len) unsigned int len,
bool true_last)
{ {
struct dwc2_hsotg *hsotg = hs_ep->parent;
int dir_in = hs_ep->dir_in; int dir_in = hs_ep->dir_in;
struct dwc2_dma_desc *desc = hs_ep->desc_list;
u32 mps = hs_ep->ep.maxpacket; u32 mps = hs_ep->ep.maxpacket;
u32 maxsize = 0; u32 maxsize = 0;
u32 offset = 0; u32 offset = 0;
@ -798,41 +789,79 @@ static void dwc2_gadget_config_nonisoc_xfer_ddma(struct dwc2_hsotg_ep *hs_ep,
hs_ep->desc_count = 1; hs_ep->desc_count = 1;
for (i = 0; i < hs_ep->desc_count; ++i) { for (i = 0; i < hs_ep->desc_count; ++i) {
desc->status = 0; (*desc)->status = 0;
desc->status |= (DEV_DMA_BUFF_STS_HBUSY (*desc)->status |= (DEV_DMA_BUFF_STS_HBUSY
<< DEV_DMA_BUFF_STS_SHIFT); << DEV_DMA_BUFF_STS_SHIFT);
if (len > maxsize) { if (len > maxsize) {
if (!hs_ep->index && !dir_in) if (!hs_ep->index && !dir_in)
desc->status |= (DEV_DMA_L | DEV_DMA_IOC); (*desc)->status |= (DEV_DMA_L | DEV_DMA_IOC);
desc->status |= (maxsize << (*desc)->status |=
DEV_DMA_NBYTES_SHIFT & mask); maxsize << DEV_DMA_NBYTES_SHIFT & mask;
desc->buf = dma_buff + offset; (*desc)->buf = dma_buff + offset;
len -= maxsize; len -= maxsize;
offset += maxsize; offset += maxsize;
} else { } else {
desc->status |= (DEV_DMA_L | DEV_DMA_IOC); if (true_last)
(*desc)->status |= (DEV_DMA_L | DEV_DMA_IOC);
if (dir_in) if (dir_in)
desc->status |= (len % mps) ? DEV_DMA_SHORT : (*desc)->status |= (len % mps) ? DEV_DMA_SHORT :
((hs_ep->send_zlp) ? DEV_DMA_SHORT : 0); ((hs_ep->send_zlp && true_last) ?
if (len > maxsize) DEV_DMA_SHORT : 0);
dev_err(hsotg->dev, "wrong len %d\n", len);
desc->status |= (*desc)->status |=
len << DEV_DMA_NBYTES_SHIFT & mask; len << DEV_DMA_NBYTES_SHIFT & mask;
desc->buf = dma_buff + offset; (*desc)->buf = dma_buff + offset;
} }
desc->status &= ~DEV_DMA_BUFF_STS_MASK; (*desc)->status &= ~DEV_DMA_BUFF_STS_MASK;
desc->status |= (DEV_DMA_BUFF_STS_HREADY (*desc)->status |= (DEV_DMA_BUFF_STS_HREADY
<< DEV_DMA_BUFF_STS_SHIFT); << DEV_DMA_BUFF_STS_SHIFT);
desc++; (*desc)++;
} }
} }
/*
* dwc2_gadget_config_nonisoc_xfer_ddma - prepare non ISOC DMA desc chain.
* @hs_ep: The endpoint
* @ureq: Request to transfer
* @offset: offset in bytes
* @len: Length of the transfer
*
* This function will iterate over descriptor chain and fill its entries
* with corresponding information based on transfer data.
*/
static void dwc2_gadget_config_nonisoc_xfer_ddma(struct dwc2_hsotg_ep *hs_ep,
struct usb_request *ureq,
unsigned int offset,
unsigned int len)
{
struct dwc2_dma_desc *desc = hs_ep->desc_list;
struct scatterlist *sg;
int i;
u8 desc_count = 0;
/* non-DMA sg buffer */
if (!ureq->num_sgs) {
dwc2_gadget_fill_nonisoc_xfer_ddma_one(hs_ep, &desc,
ureq->dma + offset, len, true);
return;
}
/* DMA sg buffer */
for_each_sg(ureq->sg, sg, ureq->num_sgs, i) {
dwc2_gadget_fill_nonisoc_xfer_ddma_one(hs_ep, &desc,
sg_dma_address(sg) + sg->offset, sg_dma_len(sg),
sg_is_last(sg));
desc_count += hs_ep->desc_count;
}
hs_ep->desc_count = desc_count;
}
/* /*
* dwc2_gadget_fill_isoc_desc - fills next isochronous descriptor in chain. * dwc2_gadget_fill_isoc_desc - fills next isochronous descriptor in chain.
* @hs_ep: The isochronous endpoint. * @hs_ep: The isochronous endpoint.
@ -944,7 +973,13 @@ static void dwc2_gadget_start_isoc_ddma(struct dwc2_hsotg_ep *hs_ep)
hs_ep->next_desc = 0; hs_ep->next_desc = 0;
list_for_each_entry_safe(hs_req, treq, &hs_ep->queue, queue) { list_for_each_entry_safe(hs_req, treq, &hs_ep->queue, queue) {
ret = dwc2_gadget_fill_isoc_desc(hs_ep, hs_req->req.dma, dma_addr_t dma_addr = hs_req->req.dma;
if (hs_req->req.num_sgs) {
WARN_ON(hs_req->req.num_sgs > 1);
dma_addr = sg_dma_address(hs_req->req.sg);
}
ret = dwc2_gadget_fill_isoc_desc(hs_ep, dma_addr,
hs_req->req.length); hs_req->req.length);
if (ret) if (ret)
break; break;
@ -1100,7 +1135,7 @@ static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg,
offset = ureq->actual; offset = ureq->actual;
/* Fill DDMA chain entries */ /* Fill DDMA chain entries */
dwc2_gadget_config_nonisoc_xfer_ddma(hs_ep, ureq->dma + offset, dwc2_gadget_config_nonisoc_xfer_ddma(hs_ep, ureq, offset,
length); length);
/* write descriptor chain address to control register */ /* write descriptor chain address to control register */
@ -1399,7 +1434,13 @@ static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
*/ */
if (using_desc_dma(hs) && hs_ep->isochronous) { if (using_desc_dma(hs) && hs_ep->isochronous) {
if (hs_ep->target_frame != TARGET_FRAME_INITIAL) { if (hs_ep->target_frame != TARGET_FRAME_INITIAL) {
dwc2_gadget_fill_isoc_desc(hs_ep, hs_req->req.dma, dma_addr_t dma_addr = hs_req->req.dma;
if (hs_req->req.num_sgs) {
WARN_ON(hs_req->req.num_sgs > 1);
dma_addr = sg_dma_address(hs_req->req.sg);
}
dwc2_gadget_fill_isoc_desc(hs_ep, dma_addr,
hs_req->req.length); hs_req->req.length);
} }
return 0; return 0;
@ -1987,13 +2028,12 @@ static void dwc2_hsotg_program_zlp(struct dwc2_hsotg *hsotg,
dev_dbg(hsotg->dev, "Receiving zero-length packet on ep%d\n", dev_dbg(hsotg->dev, "Receiving zero-length packet on ep%d\n",
index); index);
if (using_desc_dma(hsotg)) { if (using_desc_dma(hsotg)) {
/* Not specific buffer needed for ep0 ZLP */
dma_addr_t dma = hs_ep->desc_list_dma;
if (!index) if (!index)
dwc2_gadget_set_ep0_desc_chain(hsotg, hs_ep); dwc2_gadget_set_ep0_desc_chain(hsotg, hs_ep);
dwc2_gadget_config_nonisoc_xfer_ddma(hs_ep, dma, 0); /* Not specific buffer needed for ep0 ZLP */
dwc2_gadget_fill_nonisoc_xfer_ddma_one(hs_ep, &hs_ep->desc_list,
hs_ep->desc_list_dma, 0, true);
} else { } else {
dwc2_writel(hsotg, DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) | dwc2_writel(hsotg, DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) |
DXEPTSIZ_XFERSIZE(0), DXEPTSIZ_XFERSIZE(0),
@ -4005,6 +4045,7 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep,
ret = -ENOMEM; ret = -ENOMEM;
goto error1; goto error1;
} }
epctrl &= ~(DXEPCTL_TXFNUM_LIMIT << DXEPCTL_TXFNUM_SHIFT);
hsotg->fifo_map |= 1 << fifo_index; hsotg->fifo_map |= 1 << fifo_index;
epctrl |= DXEPCTL_TXFNUM(fifo_index); epctrl |= DXEPCTL_TXFNUM(fifo_index);
hs_ep->fifo_index = fifo_index; hs_ep->fifo_index = fifo_index;
@ -4385,6 +4426,7 @@ static int dwc2_hsotg_udc_start(struct usb_gadget *gadget,
hsotg->enabled = 0; hsotg->enabled = 0;
spin_unlock_irqrestore(&hsotg->lock, flags); spin_unlock_irqrestore(&hsotg->lock, flags);
gadget->sg_supported = using_desc_dma(hsotg);
dev_info(hsotg->dev, "bound driver %s\n", driver->driver.name); dev_info(hsotg->dev, "bound driver %s\n", driver->driver.name);
return 0; return 0;

View File

@ -3981,10 +3981,8 @@ static struct dwc2_hcd_urb *dwc2_hcd_urb_alloc(struct dwc2_hsotg *hsotg,
gfp_t mem_flags) gfp_t mem_flags)
{ {
struct dwc2_hcd_urb *urb; struct dwc2_hcd_urb *urb;
u32 size = sizeof(*urb) + iso_desc_count *
sizeof(struct dwc2_hcd_iso_packet_desc);
urb = kzalloc(size, mem_flags); urb = kzalloc(struct_size(urb, iso_descs, iso_desc_count), mem_flags);
if (urb) if (urb)
urb->packet_count = iso_desc_count; urb->packet_count = iso_desc_count;
return urb; return urb;

View File

@ -1,3 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
config USB_DWC3 config USB_DWC3
tristate "DesignWare USB3 DRD Core Support" tristate "DesignWare USB3 DRD Core Support"
depends on (USB || USB_GADGET) && HAS_DMA depends on (USB || USB_GADGET) && HAS_DMA
@ -86,11 +88,11 @@ config USB_DWC3_HAPS
platform, please say 'Y' or 'M' here. platform, please say 'Y' or 'M' here.
config USB_DWC3_KEYSTONE config USB_DWC3_KEYSTONE
tristate "Texas Instruments Keystone2 Platforms" tristate "Texas Instruments Keystone2/AM654 Platforms"
depends on ARCH_KEYSTONE || COMPILE_TEST depends on ARCH_KEYSTONE || ARCH_K3 || COMPILE_TEST
default USB_DWC3 default USB_DWC3
help help
Support of USB2/3 functionality in TI Keystone2 platforms. Support of USB2/3 functionality in TI Keystone2 and AM654 platforms.
Say 'Y' or 'M' here if you have one such device Say 'Y' or 'M' here if you have one such device
config USB_DWC3_OF_SIMPLE config USB_DWC3_OF_SIMPLE

View File

@ -692,7 +692,6 @@ struct dwc3_ep {
#define DWC3_EP_WEDGE BIT(2) #define DWC3_EP_WEDGE BIT(2)
#define DWC3_EP_TRANSFER_STARTED BIT(3) #define DWC3_EP_TRANSFER_STARTED BIT(3)
#define DWC3_EP_PENDING_REQUEST BIT(5) #define DWC3_EP_PENDING_REQUEST BIT(5)
#define DWC3_EP_END_TRANSFER_PENDING BIT(7)
/* This last one is specific to EP0 */ /* This last one is specific to EP0 */
#define DWC3_EP0_DIR_IN BIT(31) #define DWC3_EP0_DIR_IN BIT(31)
@ -863,6 +862,7 @@ struct dwc3_hwparams {
* @num_pending_sgs: counter to pending sgs * @num_pending_sgs: counter to pending sgs
* @num_queued_sgs: counter to the number of sgs which already got queued * @num_queued_sgs: counter to the number of sgs which already got queued
* @remaining: amount of data remaining * @remaining: amount of data remaining
* @status: internal dwc3 request status tracking
* @epnum: endpoint number to which this request refers * @epnum: endpoint number to which this request refers
* @trb: pointer to struct dwc3_trb * @trb: pointer to struct dwc3_trb
* @trb_dma: DMA address of @trb * @trb_dma: DMA address of @trb
@ -871,7 +871,6 @@ struct dwc3_hwparams {
* or unaligned OUT) * or unaligned OUT)
* @direction: IN or OUT direction flag * @direction: IN or OUT direction flag
* @mapped: true when request has been dma-mapped * @mapped: true when request has been dma-mapped
* @started: request is started
*/ */
struct dwc3_request { struct dwc3_request {
struct usb_request request; struct usb_request request;
@ -883,6 +882,14 @@ struct dwc3_request {
unsigned num_pending_sgs; unsigned num_pending_sgs;
unsigned int num_queued_sgs; unsigned int num_queued_sgs;
unsigned remaining; unsigned remaining;
unsigned int status;
#define DWC3_REQUEST_STATUS_QUEUED 0
#define DWC3_REQUEST_STATUS_STARTED 1
#define DWC3_REQUEST_STATUS_CANCELLED 2
#define DWC3_REQUEST_STATUS_COMPLETED 3
#define DWC3_REQUEST_STATUS_UNKNOWN -1
u8 epnum; u8 epnum;
struct dwc3_trb *trb; struct dwc3_trb *trb;
dma_addr_t trb_dma; dma_addr_t trb_dma;
@ -892,7 +899,6 @@ struct dwc3_request {
unsigned needs_extra_trb:1; unsigned needs_extra_trb:1;
unsigned direction:1; unsigned direction:1;
unsigned mapped:1; unsigned mapped:1;
unsigned started:1;
}; };
/* /*

View File

@ -193,65 +193,69 @@ static inline const char *dwc3_ep0_state_string(enum dwc3_ep0_state state)
* dwc3_gadget_event_string - returns event name * dwc3_gadget_event_string - returns event name
* @event: the event code * @event: the event code
*/ */
static inline const char * static inline const char *dwc3_gadget_event_string(char *str, size_t size,
dwc3_gadget_event_string(char *str, const struct dwc3_event_devt *event) const struct dwc3_event_devt *event)
{ {
enum dwc3_link_state state = event->event_info & DWC3_LINK_STATE_MASK; enum dwc3_link_state state = event->event_info & DWC3_LINK_STATE_MASK;
switch (event->type) { switch (event->type) {
case DWC3_DEVICE_EVENT_DISCONNECT: case DWC3_DEVICE_EVENT_DISCONNECT:
sprintf(str, "Disconnect: [%s]", snprintf(str, size, "Disconnect: [%s]",
dwc3_gadget_link_string(state)); dwc3_gadget_link_string(state));
break; break;
case DWC3_DEVICE_EVENT_RESET: case DWC3_DEVICE_EVENT_RESET:
sprintf(str, "Reset [%s]", dwc3_gadget_link_string(state)); snprintf(str, size, "Reset [%s]",
dwc3_gadget_link_string(state));
break; break;
case DWC3_DEVICE_EVENT_CONNECT_DONE: case DWC3_DEVICE_EVENT_CONNECT_DONE:
sprintf(str, "Connection Done [%s]", snprintf(str, size, "Connection Done [%s]",
dwc3_gadget_link_string(state)); dwc3_gadget_link_string(state));
break; break;
case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE: case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
sprintf(str, "Link Change [%s]", snprintf(str, size, "Link Change [%s]",
dwc3_gadget_link_string(state)); dwc3_gadget_link_string(state));
break; break;
case DWC3_DEVICE_EVENT_WAKEUP: case DWC3_DEVICE_EVENT_WAKEUP:
sprintf(str, "WakeUp [%s]", dwc3_gadget_link_string(state)); snprintf(str, size, "WakeUp [%s]",
dwc3_gadget_link_string(state));
break; break;
case DWC3_DEVICE_EVENT_EOPF: case DWC3_DEVICE_EVENT_EOPF:
sprintf(str, "End-Of-Frame [%s]", snprintf(str, size, "End-Of-Frame [%s]",
dwc3_gadget_link_string(state)); dwc3_gadget_link_string(state));
break; break;
case DWC3_DEVICE_EVENT_SOF: case DWC3_DEVICE_EVENT_SOF:
sprintf(str, "Start-Of-Frame [%s]", snprintf(str, size, "Start-Of-Frame [%s]",
dwc3_gadget_link_string(state)); dwc3_gadget_link_string(state));
break; break;
case DWC3_DEVICE_EVENT_ERRATIC_ERROR: case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
sprintf(str, "Erratic Error [%s]", snprintf(str, size, "Erratic Error [%s]",
dwc3_gadget_link_string(state)); dwc3_gadget_link_string(state));
break; break;
case DWC3_DEVICE_EVENT_CMD_CMPL: case DWC3_DEVICE_EVENT_CMD_CMPL:
sprintf(str, "Command Complete [%s]", snprintf(str, size, "Command Complete [%s]",
dwc3_gadget_link_string(state)); dwc3_gadget_link_string(state));
break; break;
case DWC3_DEVICE_EVENT_OVERFLOW: case DWC3_DEVICE_EVENT_OVERFLOW:
sprintf(str, "Overflow [%s]", dwc3_gadget_link_string(state)); snprintf(str, size, "Overflow [%s]",
dwc3_gadget_link_string(state));
break; break;
default: default:
sprintf(str, "UNKNOWN"); snprintf(str, size, "UNKNOWN");
} }
return str; return str;
} }
static inline void dwc3_decode_get_status(__u8 t, __u16 i, __u16 l, char *str) static inline void dwc3_decode_get_status(__u8 t, __u16 i, __u16 l, char *str,
size_t size)
{ {
switch (t & USB_RECIP_MASK) { switch (t & USB_RECIP_MASK) {
case USB_RECIP_INTERFACE: case USB_RECIP_INTERFACE:
sprintf(str, "Get Interface Status(Intf = %d, Length = %d)", snprintf(str, size, "Get Interface Status(Intf = %d, Length = %d)",
i, l); i, l);
break; break;
case USB_RECIP_ENDPOINT: case USB_RECIP_ENDPOINT:
sprintf(str, "Get Endpoint Status(ep%d%s)", snprintf(str, size, "Get Endpoint Status(ep%d%s)",
i & ~USB_DIR_IN, i & ~USB_DIR_IN,
i & USB_DIR_IN ? "in" : "out"); i & USB_DIR_IN ? "in" : "out");
break; break;
@ -259,11 +263,11 @@ static inline void dwc3_decode_get_status(__u8 t, __u16 i, __u16 l, char *str)
} }
static inline void dwc3_decode_set_clear_feature(__u8 t, __u8 b, __u16 v, static inline void dwc3_decode_set_clear_feature(__u8 t, __u8 b, __u16 v,
__u16 i, char *str) __u16 i, char *str, size_t size)
{ {
switch (t & USB_RECIP_MASK) { switch (t & USB_RECIP_MASK) {
case USB_RECIP_DEVICE: case USB_RECIP_DEVICE:
sprintf(str, "%s Device Feature(%s%s)", snprintf(str, size, "%s Device Feature(%s%s)",
b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set", b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
({char *s; ({char *s;
switch (v) { switch (v) {
@ -311,13 +315,13 @@ static inline void dwc3_decode_set_clear_feature(__u8 t, __u8 b, __u16 v,
} s; }) : ""); } s; }) : "");
break; break;
case USB_RECIP_INTERFACE: case USB_RECIP_INTERFACE:
sprintf(str, "%s Interface Feature(%s)", snprintf(str, size, "%s Interface Feature(%s)",
b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set", b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
v == USB_INTRF_FUNC_SUSPEND ? v == USB_INTRF_FUNC_SUSPEND ?
"Function Suspend" : "UNKNOWN"); "Function Suspend" : "UNKNOWN");
break; break;
case USB_RECIP_ENDPOINT: case USB_RECIP_ENDPOINT:
sprintf(str, "%s Endpoint Feature(%s ep%d%s)", snprintf(str, size, "%s Endpoint Feature(%s ep%d%s)",
b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set", b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
v == USB_ENDPOINT_HALT ? "Halt" : "UNKNOWN", v == USB_ENDPOINT_HALT ? "Halt" : "UNKNOWN",
i & ~USB_DIR_IN, i & ~USB_DIR_IN,
@ -326,15 +330,15 @@ static inline void dwc3_decode_set_clear_feature(__u8 t, __u8 b, __u16 v,
} }
} }
static inline void dwc3_decode_set_address(__u16 v, char *str) static inline void dwc3_decode_set_address(__u16 v, char *str, size_t size)
{ {
sprintf(str, "Set Address(Addr = %02x)", v); snprintf(str, size, "Set Address(Addr = %02x)", v);
} }
static inline void dwc3_decode_get_set_descriptor(__u8 t, __u8 b, __u16 v, static inline void dwc3_decode_get_set_descriptor(__u8 t, __u8 b, __u16 v,
__u16 i, __u16 l, char *str) __u16 i, __u16 l, char *str, size_t size)
{ {
sprintf(str, "%s %s Descriptor(Index = %d, Length = %d)", snprintf(str, size, "%s %s Descriptor(Index = %d, Length = %d)",
b == USB_REQ_GET_DESCRIPTOR ? "Get" : "Set", b == USB_REQ_GET_DESCRIPTOR ? "Get" : "Set",
({ char *s; ({ char *s;
switch (v >> 8) { switch (v >> 8) {
@ -393,87 +397,92 @@ static inline void dwc3_decode_get_set_descriptor(__u8 t, __u8 b, __u16 v,
} }
static inline void dwc3_decode_get_configuration(__u16 l, char *str) static inline void dwc3_decode_get_configuration(__u16 l, char *str,
size_t size)
{ {
sprintf(str, "Get Configuration(Length = %d)", l); snprintf(str, size, "Get Configuration(Length = %d)", l);
} }
static inline void dwc3_decode_set_configuration(__u8 v, char *str) static inline void dwc3_decode_set_configuration(__u8 v, char *str, size_t size)
{ {
sprintf(str, "Set Configuration(Config = %d)", v); snprintf(str, size, "Set Configuration(Config = %d)", v);
} }
static inline void dwc3_decode_get_intf(__u16 i, __u16 l, char *str) static inline void dwc3_decode_get_intf(__u16 i, __u16 l, char *str,
size_t size)
{ {
sprintf(str, "Get Interface(Intf = %d, Length = %d)", i, l); snprintf(str, size, "Get Interface(Intf = %d, Length = %d)", i, l);
} }
static inline void dwc3_decode_set_intf(__u8 v, __u16 i, char *str) static inline void dwc3_decode_set_intf(__u8 v, __u16 i, char *str, size_t size)
{ {
sprintf(str, "Set Interface(Intf = %d, Alt.Setting = %d)", i, v); snprintf(str, size, "Set Interface(Intf = %d, Alt.Setting = %d)", i, v);
} }
static inline void dwc3_decode_synch_frame(__u16 i, __u16 l, char *str) static inline void dwc3_decode_synch_frame(__u16 i, __u16 l, char *str,
size_t size)
{ {
sprintf(str, "Synch Frame(Endpoint = %d, Length = %d)", i, l); snprintf(str, size, "Synch Frame(Endpoint = %d, Length = %d)", i, l);
} }
static inline void dwc3_decode_set_sel(__u16 l, char *str) static inline void dwc3_decode_set_sel(__u16 l, char *str, size_t size)
{ {
sprintf(str, "Set SEL(Length = %d)", l); snprintf(str, size, "Set SEL(Length = %d)", l);
} }
static inline void dwc3_decode_set_isoch_delay(__u8 v, char *str) static inline void dwc3_decode_set_isoch_delay(__u8 v, char *str, size_t size)
{ {
sprintf(str, "Set Isochronous Delay(Delay = %d ns)", v); snprintf(str, size, "Set Isochronous Delay(Delay = %d ns)", v);
} }
/** /**
* dwc3_decode_ctrl - returns a string represetion of ctrl request * dwc3_decode_ctrl - returns a string represetion of ctrl request
*/ */
static inline const char *dwc3_decode_ctrl(char *str, __u8 bRequestType, static inline const char *dwc3_decode_ctrl(char *str, size_t size,
__u8 bRequest, __u16 wValue, __u16 wIndex, __u16 wLength) __u8 bRequestType, __u8 bRequest, __u16 wValue, __u16 wIndex,
__u16 wLength)
{ {
switch (bRequest) { switch (bRequest) {
case USB_REQ_GET_STATUS: case USB_REQ_GET_STATUS:
dwc3_decode_get_status(bRequestType, wIndex, wLength, str); dwc3_decode_get_status(bRequestType, wIndex, wLength, str,
size);
break; break;
case USB_REQ_CLEAR_FEATURE: case USB_REQ_CLEAR_FEATURE:
case USB_REQ_SET_FEATURE: case USB_REQ_SET_FEATURE:
dwc3_decode_set_clear_feature(bRequestType, bRequest, wValue, dwc3_decode_set_clear_feature(bRequestType, bRequest, wValue,
wIndex, str); wIndex, str, size);
break; break;
case USB_REQ_SET_ADDRESS: case USB_REQ_SET_ADDRESS:
dwc3_decode_set_address(wValue, str); dwc3_decode_set_address(wValue, str, size);
break; break;
case USB_REQ_GET_DESCRIPTOR: case USB_REQ_GET_DESCRIPTOR:
case USB_REQ_SET_DESCRIPTOR: case USB_REQ_SET_DESCRIPTOR:
dwc3_decode_get_set_descriptor(bRequestType, bRequest, wValue, dwc3_decode_get_set_descriptor(bRequestType, bRequest, wValue,
wIndex, wLength, str); wIndex, wLength, str, size);
break; break;
case USB_REQ_GET_CONFIGURATION: case USB_REQ_GET_CONFIGURATION:
dwc3_decode_get_configuration(wLength, str); dwc3_decode_get_configuration(wLength, str, size);
break; break;
case USB_REQ_SET_CONFIGURATION: case USB_REQ_SET_CONFIGURATION:
dwc3_decode_set_configuration(wValue, str); dwc3_decode_set_configuration(wValue, str, size);
break; break;
case USB_REQ_GET_INTERFACE: case USB_REQ_GET_INTERFACE:
dwc3_decode_get_intf(wIndex, wLength, str); dwc3_decode_get_intf(wIndex, wLength, str, size);
break; break;
case USB_REQ_SET_INTERFACE: case USB_REQ_SET_INTERFACE:
dwc3_decode_set_intf(wValue, wIndex, str); dwc3_decode_set_intf(wValue, wIndex, str, size);
break; break;
case USB_REQ_SYNCH_FRAME: case USB_REQ_SYNCH_FRAME:
dwc3_decode_synch_frame(wIndex, wLength, str); dwc3_decode_synch_frame(wIndex, wLength, str, size);
break; break;
case USB_REQ_SET_SEL: case USB_REQ_SET_SEL:
dwc3_decode_set_sel(wLength, str); dwc3_decode_set_sel(wLength, str, size);
break; break;
case USB_REQ_SET_ISOCH_DELAY: case USB_REQ_SET_ISOCH_DELAY:
dwc3_decode_set_isoch_delay(wValue, str); dwc3_decode_set_isoch_delay(wValue, str, size);
break; break;
default: default:
sprintf(str, "%02x %02x %02x %02x %02x %02x %02x %02x", snprintf(str, size, "%02x %02x %02x %02x %02x %02x %02x %02x",
bRequestType, bRequest, bRequestType, bRequest,
cpu_to_le16(wValue) & 0xff, cpu_to_le16(wValue) & 0xff,
cpu_to_le16(wValue) >> 8, cpu_to_le16(wValue) >> 8,
@ -490,16 +499,15 @@ static inline const char *dwc3_decode_ctrl(char *str, __u8 bRequestType,
* dwc3_ep_event_string - returns event name * dwc3_ep_event_string - returns event name
* @event: then event code * @event: then event code
*/ */
static inline const char * static inline const char *dwc3_ep_event_string(char *str, size_t size,
dwc3_ep_event_string(char *str, const struct dwc3_event_depevt *event, const struct dwc3_event_depevt *event, u32 ep0state)
u32 ep0state)
{ {
u8 epnum = event->endpoint_number; u8 epnum = event->endpoint_number;
size_t len; size_t len;
int status; int status;
int ret; int ret;
ret = sprintf(str, "ep%d%s: ", epnum >> 1, ret = snprintf(str, size, "ep%d%s: ", epnum >> 1,
(epnum & 1) ? "in" : "out"); (epnum & 1) ? "in" : "out");
if (ret < 0) if (ret < 0)
return "UNKNOWN"; return "UNKNOWN";
@ -509,7 +517,7 @@ dwc3_ep_event_string(char *str, const struct dwc3_event_depevt *event,
switch (event->endpoint_event) { switch (event->endpoint_event) {
case DWC3_DEPEVT_XFERCOMPLETE: case DWC3_DEPEVT_XFERCOMPLETE:
len = strlen(str); len = strlen(str);
sprintf(str + len, "Transfer Complete (%c%c%c)", snprintf(str + len, size - len, "Transfer Complete (%c%c%c)",
status & DEPEVT_STATUS_SHORT ? 'S' : 's', status & DEPEVT_STATUS_SHORT ? 'S' : 's',
status & DEPEVT_STATUS_IOC ? 'I' : 'i', status & DEPEVT_STATUS_IOC ? 'I' : 'i',
status & DEPEVT_STATUS_LST ? 'L' : 'l'); status & DEPEVT_STATUS_LST ? 'L' : 'l');
@ -517,12 +525,13 @@ dwc3_ep_event_string(char *str, const struct dwc3_event_depevt *event,
len = strlen(str); len = strlen(str);
if (epnum <= 1) if (epnum <= 1)
sprintf(str + len, " [%s]", dwc3_ep0_state_string(ep0state)); snprintf(str + len, size - len, " [%s]",
dwc3_ep0_state_string(ep0state));
break; break;
case DWC3_DEPEVT_XFERINPROGRESS: case DWC3_DEPEVT_XFERINPROGRESS:
len = strlen(str); len = strlen(str);
sprintf(str + len, "Transfer In Progress [%d] (%c%c%c)", snprintf(str + len, size - len, "Transfer In Progress [%d] (%c%c%c)",
event->parameters, event->parameters,
status & DEPEVT_STATUS_SHORT ? 'S' : 's', status & DEPEVT_STATUS_SHORT ? 'S' : 's',
status & DEPEVT_STATUS_IOC ? 'I' : 'i', status & DEPEVT_STATUS_IOC ? 'I' : 'i',
@ -531,47 +540,51 @@ dwc3_ep_event_string(char *str, const struct dwc3_event_depevt *event,
case DWC3_DEPEVT_XFERNOTREADY: case DWC3_DEPEVT_XFERNOTREADY:
len = strlen(str); len = strlen(str);
sprintf(str + len, "Transfer Not Ready [%d]%s", snprintf(str + len, size - len, "Transfer Not Ready [%d]%s",
event->parameters, event->parameters,
status & DEPEVT_STATUS_TRANSFER_ACTIVE ? status & DEPEVT_STATUS_TRANSFER_ACTIVE ?
" (Active)" : " (Not Active)"); " (Active)" : " (Not Active)");
len = strlen(str);
/* Control Endpoints */ /* Control Endpoints */
if (epnum <= 1) { if (epnum <= 1) {
int phase = DEPEVT_STATUS_CONTROL_PHASE(event->status); int phase = DEPEVT_STATUS_CONTROL_PHASE(event->status);
switch (phase) { switch (phase) {
case DEPEVT_STATUS_CONTROL_DATA: case DEPEVT_STATUS_CONTROL_DATA:
strcat(str, " [Data Phase]"); snprintf(str + ret, size - ret,
" [Data Phase]");
break; break;
case DEPEVT_STATUS_CONTROL_STATUS: case DEPEVT_STATUS_CONTROL_STATUS:
strcat(str, " [Status Phase]"); snprintf(str + ret, size - ret,
" [Status Phase]");
} }
} }
break; break;
case DWC3_DEPEVT_RXTXFIFOEVT: case DWC3_DEPEVT_RXTXFIFOEVT:
strcat(str, "FIFO"); snprintf(str + ret, size - ret, "FIFO");
break; break;
case DWC3_DEPEVT_STREAMEVT: case DWC3_DEPEVT_STREAMEVT:
status = event->status; status = event->status;
switch (status) { switch (status) {
case DEPEVT_STREAMEVT_FOUND: case DEPEVT_STREAMEVT_FOUND:
sprintf(str + ret, " Stream %d Found", snprintf(str + ret, size - ret, " Stream %d Found",
event->parameters); event->parameters);
break; break;
case DEPEVT_STREAMEVT_NOTFOUND: case DEPEVT_STREAMEVT_NOTFOUND:
default: default:
strcat(str, " Stream Not Found"); snprintf(str + ret, size - ret, " Stream Not Found");
break; break;
} }
break; break;
case DWC3_DEPEVT_EPCMDCMPLT: case DWC3_DEPEVT_EPCMDCMPLT:
strcat(str, "Endpoint Command Complete"); snprintf(str + ret, size - ret, "Endpoint Command Complete");
break; break;
default: default:
sprintf(str, "UNKNOWN"); snprintf(str, size, "UNKNOWN");
} }
return str; return str;
@ -611,14 +624,15 @@ static inline const char *dwc3_gadget_event_type_string(u8 event)
} }
} }
static inline const char *dwc3_decode_event(char *str, u32 event, u32 ep0state) static inline const char *dwc3_decode_event(char *str, size_t size, u32 event,
u32 ep0state)
{ {
const union dwc3_event evt = (union dwc3_event) event; const union dwc3_event evt = (union dwc3_event) event;
if (evt.type.is_devspec) if (evt.type.is_devspec)
return dwc3_gadget_event_string(str, &evt.devt); return dwc3_gadget_event_string(str, size, &evt.devt);
else else
return dwc3_ep_event_string(str, &evt.depevt, ep0state); return dwc3_ep_event_string(str, size, &evt.depevt, ep0state);
} }
static inline const char *dwc3_ep_cmd_status_string(int status) static inline const char *dwc3_ep_cmd_status_string(int status)

View File

@ -457,8 +457,13 @@ static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc)
* This device property is for kernel internal use only and * This device property is for kernel internal use only and
* is expected to be set by the glue code. * is expected to be set by the glue code.
*/ */
if (device_property_read_string(dev, "linux,extcon-name", &name) == 0) if (device_property_read_string(dev, "linux,extcon-name", &name) == 0) {
return extcon_get_extcon_dev(name); edev = extcon_get_extcon_dev(name);
if (!edev)
return ERR_PTR(-EPROBE_DEFER);
return edev;
}
np_phy = of_parse_phandle(dev->of_node, "phys", 0); np_phy = of_parse_phandle(dev->of_node, "phys", 0);
np_conn = of_graph_get_remote_node(np_phy, -1, -1); np_conn = of_graph_get_remote_node(np_phy, -1, -1);

View File

@ -106,6 +106,15 @@ static const struct pci_device_id dwc3_haps_id_table[] = {
{ {
PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS,
PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3), PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3),
/*
* i.MX6QP and i.MX7D platform use a PCIe controller with the
* same VID and PID as this USB controller. The system may
* incorrectly match this driver to that PCIe controller. To
* workaround this, specifically use class type USB to prevent
* incorrect driver matching.
*/
.class = (PCI_CLASS_SERIAL_USB << 8),
.class_mask = 0xffff00,
}, },
{ {
PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS,

View File

@ -106,6 +106,10 @@ static int kdwc3_probe(struct platform_device *pdev)
goto err_irq; goto err_irq;
} }
/* IRQ processing not required currently for AM65 */
if (of_device_is_compatible(node, "ti,am654-dwc3"))
goto skip_irq;
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
if (irq < 0) { if (irq < 0) {
dev_err(&pdev->dev, "missing irq\n"); dev_err(&pdev->dev, "missing irq\n");
@ -123,6 +127,7 @@ static int kdwc3_probe(struct platform_device *pdev)
kdwc3_enable_irqs(kdwc); kdwc3_enable_irqs(kdwc);
skip_irq:
error = of_platform_populate(node, NULL, NULL, dev); error = of_platform_populate(node, NULL, NULL, dev);
if (error) { if (error) {
dev_err(&pdev->dev, "failed to create dwc3 core\n"); dev_err(&pdev->dev, "failed to create dwc3 core\n");
@ -152,8 +157,11 @@ static int kdwc3_remove_core(struct device *dev, void *c)
static int kdwc3_remove(struct platform_device *pdev) static int kdwc3_remove(struct platform_device *pdev)
{ {
struct dwc3_keystone *kdwc = platform_get_drvdata(pdev); struct dwc3_keystone *kdwc = platform_get_drvdata(pdev);
struct device_node *node = pdev->dev.of_node;
if (!of_device_is_compatible(node, "ti,am654-dwc3"))
kdwc3_disable_irqs(kdwc);
kdwc3_disable_irqs(kdwc);
device_for_each_child(&pdev->dev, NULL, kdwc3_remove_core); device_for_each_child(&pdev->dev, NULL, kdwc3_remove_core);
pm_runtime_put_sync(kdwc->dev); pm_runtime_put_sync(kdwc->dev);
pm_runtime_disable(kdwc->dev); pm_runtime_disable(kdwc->dev);
@ -165,6 +173,7 @@ static int kdwc3_remove(struct platform_device *pdev)
static const struct of_device_id kdwc3_of_match[] = { static const struct of_device_id kdwc3_of_match[] = {
{ .compatible = "ti,keystone-dwc3", }, { .compatible = "ti,keystone-dwc3", },
{ .compatible = "ti,am654-dwc3" },
{}, {},
}; };
MODULE_DEVICE_TABLE(of, kdwc3_of_match); MODULE_DEVICE_TABLE(of, kdwc3_of_match);

View File

@ -595,6 +595,7 @@ static const struct dev_pm_ops dwc3_qcom_dev_pm_ops = {
static const struct of_device_id dwc3_qcom_of_match[] = { static const struct of_device_id dwc3_qcom_of_match[] = {
{ .compatible = "qcom,dwc3" }, { .compatible = "qcom,dwc3" },
{ .compatible = "qcom,msm8996-dwc3" }, { .compatible = "qcom,msm8996-dwc3" },
{ .compatible = "qcom,msm8998-dwc3" },
{ .compatible = "qcom,sdm845-dwc3" }, { .compatible = "qcom,sdm845-dwc3" },
{ } { }
}; };

View File

@ -174,7 +174,6 @@ static void dwc3_gadget_del_and_unmap_request(struct dwc3_ep *dep,
{ {
struct dwc3 *dwc = dep->dwc; struct dwc3 *dwc = dep->dwc;
req->started = false;
list_del(&req->list); list_del(&req->list);
req->remaining = 0; req->remaining = 0;
req->needs_extra_trb = false; req->needs_extra_trb = false;
@ -209,6 +208,7 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
struct dwc3 *dwc = dep->dwc; struct dwc3 *dwc = dep->dwc;
dwc3_gadget_del_and_unmap_request(dep, req, status); dwc3_gadget_del_and_unmap_request(dep, req, status);
req->status = DWC3_REQUEST_STATUS_COMPLETED;
spin_unlock(&dwc->lock); spin_unlock(&dwc->lock);
usb_gadget_giveback_request(&dep->endpoint, &req->request); usb_gadget_giveback_request(&dep->endpoint, &req->request);
@ -384,19 +384,9 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
trace_dwc3_gadget_ep_cmd(dep, cmd, params, cmd_status); trace_dwc3_gadget_ep_cmd(dep, cmd, params, cmd_status);
if (ret == 0) { if (ret == 0 && DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
switch (DWC3_DEPCMD_CMD(cmd)) { dep->flags |= DWC3_EP_TRANSFER_STARTED;
case DWC3_DEPCMD_STARTTRANSFER: dwc3_gadget_ep_get_transfer_index(dep);
dep->flags |= DWC3_EP_TRANSFER_STARTED;
dwc3_gadget_ep_get_transfer_index(dep);
break;
case DWC3_DEPCMD_ENDTRANSFER:
dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
break;
default:
/* nothing */
break;
}
} }
if (saved_config) { if (saved_config) {
@ -642,7 +632,6 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
dep->type = usb_endpoint_type(desc); dep->type = usb_endpoint_type(desc);
dep->flags |= DWC3_EP_ENABLED; dep->flags |= DWC3_EP_ENABLED;
dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING;
reg = dwc3_readl(dwc->regs, DWC3_DALEPENA); reg = dwc3_readl(dwc->regs, DWC3_DALEPENA);
reg |= DWC3_DALEPENA_EP(dep->number); reg |= DWC3_DALEPENA_EP(dep->number);
@ -698,12 +687,13 @@ out:
return 0; return 0;
} }
static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force); static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
bool interrupt);
static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep) static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
{ {
struct dwc3_request *req; struct dwc3_request *req;
dwc3_stop_active_transfer(dep, true); dwc3_stop_active_transfer(dep, true, false);
/* - giveback all requests to gadget driver */ /* - giveback all requests to gadget driver */
while (!list_empty(&dep->started_list)) { while (!list_empty(&dep->started_list)) {
@ -748,7 +738,7 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
dep->stream_capable = false; dep->stream_capable = false;
dep->type = 0; dep->type = 0;
dep->flags &= DWC3_EP_END_TRANSFER_PENDING; dep->flags = 0;
/* Clear out the ep descriptors for non-ep0 */ /* Clear out the ep descriptors for non-ep0 */
if (dep->number > 1) { if (dep->number > 1) {
@ -847,6 +837,7 @@ static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep,
req->direction = dep->direction; req->direction = dep->direction;
req->epnum = dep->number; req->epnum = dep->number;
req->dep = dep; req->dep = dep;
req->status = DWC3_REQUEST_STATUS_UNKNOWN;
trace_dwc3_alloc_request(req); trace_dwc3_alloc_request(req);
@ -1360,7 +1351,7 @@ static int dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep)
* to wait for the next XferNotReady to test the command again * to wait for the next XferNotReady to test the command again
*/ */
if (cmd_status == 0) { if (cmd_status == 0) {
dwc3_stop_active_transfer(dep, true); dwc3_stop_active_transfer(dep, true, true);
return 0; return 0;
} }
} }
@ -1435,6 +1426,11 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
&req->request, req->dep->name)) &req->request, req->dep->name))
return -EINVAL; return -EINVAL;
if (WARN(req->status < DWC3_REQUEST_STATUS_COMPLETED,
"%s: request %pK already in flight\n",
dep->name, &req->request))
return -EINVAL;
pm_runtime_get(dwc->dev); pm_runtime_get(dwc->dev);
req->request.actual = 0; req->request.actual = 0;
@ -1443,6 +1439,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
trace_dwc3_ep_queue(req); trace_dwc3_ep_queue(req);
list_add_tail(&req->list, &dep->pending_list); list_add_tail(&req->list, &dep->pending_list);
req->status = DWC3_REQUEST_STATUS_QUEUED;
/* /*
* NOTICE: Isochronous endpoints should NEVER be prestarted. We must * NOTICE: Isochronous endpoints should NEVER be prestarted. We must
@ -1506,6 +1503,8 @@ static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, struct dwc3_request *r
trb->ctrl &= ~DWC3_TRB_CTRL_HWO; trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
dwc3_ep_inc_deq(dep); dwc3_ep_inc_deq(dep);
} }
req->num_trbs = 0;
} }
static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep) static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep)
@ -1547,13 +1546,16 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
} }
if (r == req) { if (r == req) {
/* wait until it is processed */ /* wait until it is processed */
dwc3_stop_active_transfer(dep, true); dwc3_stop_active_transfer(dep, true, true);
if (!r->trb) if (!r->trb)
goto out0; goto out0;
dwc3_gadget_move_cancelled_request(req); dwc3_gadget_move_cancelled_request(req);
goto out0; if (dep->flags & DWC3_EP_TRANSFER_STARTED)
goto out0;
else
goto out1;
} }
dev_err(dwc->dev, "request %pK was not queued to %s\n", dev_err(dwc->dev, "request %pK was not queued to %s\n",
request, ep->name); request, ep->name);
@ -1561,6 +1563,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
goto out0; goto out0;
} }
out1:
dwc3_gadget_giveback(dep, req, -ECONNRESET); dwc3_gadget_giveback(dep, req, -ECONNRESET);
out0: out0:
@ -2500,7 +2503,7 @@ static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
dwc3_gadget_ep_cleanup_completed_requests(dep, event, status); dwc3_gadget_ep_cleanup_completed_requests(dep, event, status);
if (stop) { if (stop) {
dwc3_stop_active_transfer(dep, true); dwc3_stop_active_transfer(dep, true, true);
dep->flags = DWC3_EP_ENABLED; dep->flags = DWC3_EP_ENABLED;
} }
@ -2547,7 +2550,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
dep = dwc->eps[epnum]; dep = dwc->eps[epnum];
if (!(dep->flags & DWC3_EP_ENABLED)) { if (!(dep->flags & DWC3_EP_ENABLED)) {
if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING)) if (!(dep->flags & DWC3_EP_TRANSFER_STARTED))
return; return;
/* Handle only EPCMDCMPLT when EP disabled */ /* Handle only EPCMDCMPLT when EP disabled */
@ -2571,7 +2574,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
cmd = DEPEVT_PARAMETER_CMD(event->parameters); cmd = DEPEVT_PARAMETER_CMD(event->parameters);
if (cmd == DWC3_DEPCMD_ENDTRANSFER) { if (cmd == DWC3_DEPCMD_ENDTRANSFER) {
dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING; dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
dwc3_gadget_ep_cleanup_cancelled_requests(dep); dwc3_gadget_ep_cleanup_cancelled_requests(dep);
} }
break; break;
@ -2621,15 +2624,15 @@ static void dwc3_reset_gadget(struct dwc3 *dwc)
} }
} }
static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force) static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
bool interrupt)
{ {
struct dwc3 *dwc = dep->dwc; struct dwc3 *dwc = dep->dwc;
struct dwc3_gadget_ep_cmd_params params; struct dwc3_gadget_ep_cmd_params params;
u32 cmd; u32 cmd;
int ret; int ret;
if ((dep->flags & DWC3_EP_END_TRANSFER_PENDING) || if (!(dep->flags & DWC3_EP_TRANSFER_STARTED))
!dep->resource_index)
return; return;
/* /*
@ -2665,17 +2668,15 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force)
cmd = DWC3_DEPCMD_ENDTRANSFER; cmd = DWC3_DEPCMD_ENDTRANSFER;
cmd |= force ? DWC3_DEPCMD_HIPRI_FORCERM : 0; cmd |= force ? DWC3_DEPCMD_HIPRI_FORCERM : 0;
cmd |= DWC3_DEPCMD_CMDIOC; cmd |= interrupt ? DWC3_DEPCMD_CMDIOC : 0;
cmd |= DWC3_DEPCMD_PARAM(dep->resource_index); cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
memset(&params, 0, sizeof(params)); memset(&params, 0, sizeof(params));
ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params); ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
WARN_ON_ONCE(ret); WARN_ON_ONCE(ret);
dep->resource_index = 0; dep->resource_index = 0;
if (dwc3_is_usb31(dwc) || dwc->revision < DWC3_REVISION_310A) { if (dwc3_is_usb31(dwc) || dwc->revision < DWC3_REVISION_310A)
dep->flags |= DWC3_EP_END_TRANSFER_PENDING;
udelay(100); udelay(100);
}
} }
static void dwc3_clear_stall_all_ep(struct dwc3 *dwc) static void dwc3_clear_stall_all_ep(struct dwc3 *dwc)
@ -3339,6 +3340,8 @@ int dwc3_gadget_init(struct dwc3 *dwc)
goto err4; goto err4;
} }
dwc3_gadget_set_speed(&dwc->gadget, dwc->maximum_speed);
return 0; return 0;
err4: err4:

View File

@ -75,7 +75,7 @@ static inline void dwc3_gadget_move_started_request(struct dwc3_request *req)
{ {
struct dwc3_ep *dep = req->dep; struct dwc3_ep *dep = req->dep;
req->started = true; req->status = DWC3_REQUEST_STATUS_STARTED;
list_move_tail(&req->list, &dep->started_list); list_move_tail(&req->list, &dep->started_list);
} }
@ -90,7 +90,7 @@ static inline void dwc3_gadget_move_cancelled_request(struct dwc3_request *req)
{ {
struct dwc3_ep *dep = req->dep; struct dwc3_ep *dep = req->dep;
req->started = false; req->status = DWC3_REQUEST_STATUS_CANCELLED;
list_move_tail(&req->list, &dep->cancelled_list); list_move_tail(&req->list, &dep->cancelled_list);
} }

View File

@ -59,8 +59,8 @@ DECLARE_EVENT_CLASS(dwc3_log_event,
__entry->ep0state = dwc->ep0state; __entry->ep0state = dwc->ep0state;
), ),
TP_printk("event (%08x): %s", __entry->event, TP_printk("event (%08x): %s", __entry->event,
dwc3_decode_event(__get_str(str), __entry->event, dwc3_decode_event(__get_str(str), DWC3_MSG_MAX,
__entry->ep0state)) __entry->event, __entry->ep0state))
); );
DEFINE_EVENT(dwc3_log_event, dwc3_event, DEFINE_EVENT(dwc3_log_event, dwc3_event,
@ -86,7 +86,8 @@ DECLARE_EVENT_CLASS(dwc3_log_ctrl,
__entry->wIndex = le16_to_cpu(ctrl->wIndex); __entry->wIndex = le16_to_cpu(ctrl->wIndex);
__entry->wLength = le16_to_cpu(ctrl->wLength); __entry->wLength = le16_to_cpu(ctrl->wLength);
), ),
TP_printk("%s", dwc3_decode_ctrl(__get_str(str), __entry->bRequestType, TP_printk("%s", dwc3_decode_ctrl(__get_str(str), DWC3_MSG_MAX,
__entry->bRequestType,
__entry->bRequest, __entry->wValue, __entry->bRequest, __entry->wValue,
__entry->wIndex, __entry->wLength) __entry->wIndex, __entry->wLength)
) )
@ -305,7 +306,7 @@ DECLARE_EVENT_CLASS(dwc3_log_ep,
__entry->trb_enqueue = dep->trb_enqueue; __entry->trb_enqueue = dep->trb_enqueue;
__entry->trb_dequeue = dep->trb_dequeue; __entry->trb_dequeue = dep->trb_dequeue;
), ),
TP_printk("%s: mps %d/%d streams %d burst %d ring %d/%d flags %c:%c%c%c%c:%c:%c", TP_printk("%s: mps %d/%d streams %d burst %d ring %d/%d flags %c:%c%c%c%c:%c",
__get_str(name), __entry->maxpacket, __get_str(name), __entry->maxpacket,
__entry->maxpacket_limit, __entry->max_streams, __entry->maxpacket_limit, __entry->max_streams,
__entry->maxburst, __entry->trb_enqueue, __entry->maxburst, __entry->trb_enqueue,
@ -315,7 +316,6 @@ DECLARE_EVENT_CLASS(dwc3_log_ep,
__entry->flags & DWC3_EP_WEDGE ? 'W' : 'w', __entry->flags & DWC3_EP_WEDGE ? 'W' : 'w',
__entry->flags & DWC3_EP_TRANSFER_STARTED ? 'B' : 'b', __entry->flags & DWC3_EP_TRANSFER_STARTED ? 'B' : 'b',
__entry->flags & DWC3_EP_PENDING_REQUEST ? 'P' : 'p', __entry->flags & DWC3_EP_PENDING_REQUEST ? 'P' : 'p',
__entry->flags & DWC3_EP_END_TRANSFER_PENDING ? 'E' : 'e',
__entry->direction ? '<' : '>' __entry->direction ? '<' : '>'
) )
); );

View File

@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
# #
# USB Gadget support on a system involves # USB Gadget support on a system involves
# (a) a peripheral controller, and # (a) a peripheral controller, and

View File

@ -67,9 +67,6 @@ struct usb_ep *usb_ep_autoconfig_ss(
) )
{ {
struct usb_ep *ep; struct usb_ep *ep;
u8 type;
type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
if (gadget->ops->match_ep) { if (gadget->ops->match_ep) {
ep = gadget->ops->match_ep(gadget, desc, ep_comp); ep = gadget->ops->match_ep(gadget, desc, ep_comp);
@ -109,16 +106,6 @@ found_ep:
desc->bEndpointAddress |= gadget->out_epnum; desc->bEndpointAddress |= gadget->out_epnum;
} }
/* report (variable) full speed bulk maxpacket */
if ((type == USB_ENDPOINT_XFER_BULK) && !ep_comp) {
int size = ep->maxpacket_limit;
/* min() doesn't work on bitfields with gcc-3.5 */
if (size > 64)
size = 64;
desc->wMaxPacketSize = cpu_to_le16(size);
}
ep->address = desc->bEndpointAddress; ep->address = desc->bEndpointAddress;
ep->desc = NULL; ep->desc = NULL;
ep->comp_desc = NULL; ep->comp_desc = NULL;
@ -152,9 +139,10 @@ EXPORT_SYMBOL_GPL(usb_ep_autoconfig_ss);
* *
* On success, this returns an claimed usb_ep, and modifies the endpoint * On success, this returns an claimed usb_ep, and modifies the endpoint
* descriptor bEndpointAddress. For bulk endpoints, the wMaxPacket value * descriptor bEndpointAddress. For bulk endpoints, the wMaxPacket value
* is initialized as if the endpoint were used at full speed. To prevent * is initialized as if the endpoint were used at full speed. Because of
* the endpoint from being returned by a later autoconfig call, claims it * that the users must consider adjusting the autoconfigured descriptor.
* by assigning ep->claimed to true. * To prevent the endpoint from being returned by a later autoconfig call,
* claims it by assigning ep->claimed to true.
* *
* On failure, this returns a null endpoint descriptor. * On failure, this returns a null endpoint descriptor.
*/ */
@ -163,7 +151,26 @@ struct usb_ep *usb_ep_autoconfig(
struct usb_endpoint_descriptor *desc struct usb_endpoint_descriptor *desc
) )
{ {
return usb_ep_autoconfig_ss(gadget, desc, NULL); struct usb_ep *ep;
u8 type;
ep = usb_ep_autoconfig_ss(gadget, desc, NULL);
if (!ep)
return NULL;
type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
/* report (variable) full speed bulk maxpacket */
if (type == USB_ENDPOINT_XFER_BULK) {
int size = ep->maxpacket_limit;
/* min() doesn't work on bitfields with gcc-3.5 */
if (size > 64)
size = 64;
desc->wMaxPacketSize = cpu_to_le16(size);
}
return ep;
} }
EXPORT_SYMBOL_GPL(usb_ep_autoconfig); EXPORT_SYMBOL_GPL(usb_ep_autoconfig);

View File

@ -1082,6 +1082,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
* condition with req->complete callback. * condition with req->complete callback.
*/ */
usb_ep_dequeue(ep->ep, req); usb_ep_dequeue(ep->ep, req);
wait_for_completion(&done);
interrupted = ep->status < 0; interrupted = ep->status < 0;
} }
@ -2843,12 +2844,18 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
struct usb_request *req; struct usb_request *req;
struct usb_ep *ep; struct usb_ep *ep;
u8 bEndpointAddress; u8 bEndpointAddress;
u16 wMaxPacketSize;
/* /*
* We back up bEndpointAddress because autoconfig overwrites * We back up bEndpointAddress because autoconfig overwrites
* it with physical endpoint address. * it with physical endpoint address.
*/ */
bEndpointAddress = ds->bEndpointAddress; bEndpointAddress = ds->bEndpointAddress;
/*
* We back up wMaxPacketSize because autoconfig treats
* endpoint descriptors as if they were full speed.
*/
wMaxPacketSize = ds->wMaxPacketSize;
pr_vdebug("autoconfig\n"); pr_vdebug("autoconfig\n");
ep = usb_ep_autoconfig(func->gadget, ds); ep = usb_ep_autoconfig(func->gadget, ds);
if (unlikely(!ep)) if (unlikely(!ep))
@ -2869,6 +2876,11 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
*/ */
if (func->ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR) if (func->ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR)
ds->bEndpointAddress = bEndpointAddress; ds->bEndpointAddress = bEndpointAddress;
/*
* Restore wMaxPacketSize which was potentially
* overwritten by autoconfig.
*/
ds->wMaxPacketSize = wMaxPacketSize;
} }
ffs_dump_mem(": Rewritten ep desc", ds, ds->bLength); ffs_dump_mem(": Rewritten ep desc", ds, ds->bLength);

View File

@ -459,10 +459,10 @@ static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
} else if (intf == uac1->as_in_intf) { } else if (intf == uac1->as_in_intf) {
uac1->as_in_alt = alt; uac1->as_in_alt = alt;
if (alt) if (alt)
ret = u_audio_start_playback(&uac1->g_audio); ret = u_audio_start_playback(&uac1->g_audio);
else else
u_audio_stop_playback(&uac1->g_audio); u_audio_stop_playback(&uac1->g_audio);
} else { } else {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return -EINVAL; return -EINVAL;
@ -568,6 +568,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
goto fail; goto fail;
as_out_interface_alt_0_desc.bInterfaceNumber = status; as_out_interface_alt_0_desc.bInterfaceNumber = status;
as_out_interface_alt_1_desc.bInterfaceNumber = status; as_out_interface_alt_1_desc.bInterfaceNumber = status;
ac_header_desc.baInterfaceNr[0] = status;
uac1->as_out_intf = status; uac1->as_out_intf = status;
uac1->as_out_alt = 0; uac1->as_out_alt = 0;
@ -576,6 +577,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
goto fail; goto fail;
as_in_interface_alt_0_desc.bInterfaceNumber = status; as_in_interface_alt_0_desc.bInterfaceNumber = status;
as_in_interface_alt_1_desc.bInterfaceNumber = status; as_in_interface_alt_1_desc.bInterfaceNumber = status;
ac_header_desc.baInterfaceNr[1] = status;
uac1->as_in_intf = status; uac1->as_in_intf = status;
uac1->as_in_alt = 0; uac1->as_in_alt = 0;

View File

@ -7,7 +7,7 @@
* Copyright (c) 2013 Samsung Electronics Co., Ltd. * Copyright (c) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com * http://www.samsung.com
* *
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> * Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com>
*/ */
#ifndef U_ECM_H #ifndef U_ECM_H

View File

@ -7,7 +7,7 @@
* Copyright (c) 2013 Samsung Electronics Co., Ltd. * Copyright (c) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com * http://www.samsung.com
* *
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> * Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com>
*/ */
#ifndef U_EEM_H #ifndef U_EEM_H

View File

@ -7,7 +7,7 @@
* Copyright (c) 2013 Samsung Electronics Co., Ltd. * Copyright (c) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com * http://www.samsung.com
* *
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> * Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com>
*/ */
#ifndef __U_ETHER_CONFIGFS_H #ifndef __U_ETHER_CONFIGFS_H

View File

@ -7,7 +7,7 @@
* Copyright (c) 2013 Samsung Electronics Co., Ltd. * Copyright (c) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com * http://www.samsung.com
* *
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> * Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com>
*/ */
#ifndef U_FFS_H #ifndef U_FFS_H

View File

@ -7,7 +7,7 @@
* Copyright (c) 2013 Samsung Electronics Co., Ltd. * Copyright (c) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com * http://www.samsung.com
* *
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> * Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com>
*/ */
#ifndef U_GETHER_H #ifndef U_GETHER_H

View File

@ -7,7 +7,7 @@
* Copyright (c) 2014 Samsung Electronics Co., Ltd. * Copyright (c) 2014 Samsung Electronics Co., Ltd.
* http://www.samsung.com * http://www.samsung.com
* *
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> * Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com>
*/ */
#ifndef U_HID_H #ifndef U_HID_H

View File

@ -7,7 +7,7 @@
* Copyright (c) 2014 Samsung Electronics Co., Ltd. * Copyright (c) 2014 Samsung Electronics Co., Ltd.
* http://www.samsung.com * http://www.samsung.com
* *
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> * Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com>
*/ */
#ifndef U_MIDI_H #ifndef U_MIDI_H

View File

@ -7,7 +7,7 @@
* Copyright (c) 2013 Samsung Electronics Co., Ltd. * Copyright (c) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com * http://www.samsung.com
* *
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> * Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com>
*/ */
#ifndef U_NCM_H #ifndef U_NCM_H

View File

@ -7,7 +7,7 @@
* Copyright (c) 2015 Samsung Electronics Co., Ltd. * Copyright (c) 2015 Samsung Electronics Co., Ltd.
* http://www.samsung.com * http://www.samsung.com
* *
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> * Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com>
*/ */
#ifndef U_PRINTER_H #ifndef U_PRINTER_H

View File

@ -7,7 +7,7 @@
* Copyright (c) 2013 Samsung Electronics Co., Ltd. * Copyright (c) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com * http://www.samsung.com
* *
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> * Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com>
*/ */
#ifndef U_RNDIS_H #ifndef U_RNDIS_H

View File

@ -16,7 +16,6 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/tty.h> #include <linux/tty.h>
@ -26,6 +25,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/console.h> #include <linux/console.h>
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/workqueue.h>
#include <linux/kfifo.h> #include <linux/kfifo.h>
#include "u_serial.h" #include "u_serial.h"
@ -110,7 +110,7 @@ struct gs_port {
int read_allocated; int read_allocated;
struct list_head read_queue; struct list_head read_queue;
unsigned n_read; unsigned n_read;
struct tasklet_struct push; struct delayed_work push;
struct list_head write_pool; struct list_head write_pool;
int write_started; int write_started;
@ -352,9 +352,10 @@ __acquires(&port->port_lock)
* So QUEUE_SIZE packets plus however many the FIFO holds (usually two) * So QUEUE_SIZE packets plus however many the FIFO holds (usually two)
* can be buffered before the TTY layer's buffers (currently 64 KB). * can be buffered before the TTY layer's buffers (currently 64 KB).
*/ */
static void gs_rx_push(unsigned long _port) static void gs_rx_push(struct work_struct *work)
{ {
struct gs_port *port = (void *)_port; struct delayed_work *w = to_delayed_work(work);
struct gs_port *port = container_of(w, struct gs_port, push);
struct tty_struct *tty; struct tty_struct *tty;
struct list_head *queue = &port->read_queue; struct list_head *queue = &port->read_queue;
bool disconnect = false; bool disconnect = false;
@ -429,21 +430,13 @@ static void gs_rx_push(unsigned long _port)
/* We want our data queue to become empty ASAP, keeping data /* We want our data queue to become empty ASAP, keeping data
* in the tty and ldisc (not here). If we couldn't push any * in the tty and ldisc (not here). If we couldn't push any
* this time around, there may be trouble unless there's an * this time around, RX may be starved, so wait until next jiffy.
* implicit tty_unthrottle() call on its way...
* *
* REVISIT we should probably add a timer to keep the tasklet * We may leave non-empty queue only when there is a tty, and
* from starving ... but it's not clear that case ever happens. * either it is throttled or there is no more room in flip buffer.
*/ */
if (!list_empty(queue) && tty) { if (!list_empty(queue) && !tty_throttled(tty))
if (!tty_throttled(tty)) { schedule_delayed_work(&port->push, 1);
if (do_push)
tasklet_schedule(&port->push);
else
pr_warn("ttyGS%d: RX not scheduled?\n",
port->port_num);
}
}
/* If we're still connected, refill the USB RX queue. */ /* If we're still connected, refill the USB RX queue. */
if (!disconnect && port->port_usb) if (!disconnect && port->port_usb)
@ -459,7 +452,7 @@ static void gs_read_complete(struct usb_ep *ep, struct usb_request *req)
/* Queue all received data until the tty layer is ready for it. */ /* Queue all received data until the tty layer is ready for it. */
spin_lock(&port->port_lock); spin_lock(&port->port_lock);
list_add_tail(&req->list, &port->read_queue); list_add_tail(&req->list, &port->read_queue);
tasklet_schedule(&port->push); schedule_delayed_work(&port->push, 0);
spin_unlock(&port->port_lock); spin_unlock(&port->port_lock);
} }
@ -854,8 +847,8 @@ static void gs_unthrottle(struct tty_struct *tty)
* rts/cts, or other handshaking with the host, but if the * rts/cts, or other handshaking with the host, but if the
* read queue backs up enough we'll be NAKing OUT packets. * read queue backs up enough we'll be NAKing OUT packets.
*/ */
tasklet_schedule(&port->push);
pr_vdebug("ttyGS%d: unthrottle\n", port->port_num); pr_vdebug("ttyGS%d: unthrottle\n", port->port_num);
schedule_delayed_work(&port->push, 0);
} }
spin_unlock_irqrestore(&port->port_lock, flags); spin_unlock_irqrestore(&port->port_lock, flags);
} }
@ -1159,7 +1152,7 @@ gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding)
init_waitqueue_head(&port->drain_wait); init_waitqueue_head(&port->drain_wait);
init_waitqueue_head(&port->close_wait); init_waitqueue_head(&port->close_wait);
tasklet_init(&port->push, gs_rx_push, (unsigned long) port); INIT_DELAYED_WORK(&port->push, gs_rx_push);
INIT_LIST_HEAD(&port->read_pool); INIT_LIST_HEAD(&port->read_pool);
INIT_LIST_HEAD(&port->read_queue); INIT_LIST_HEAD(&port->read_queue);
@ -1186,7 +1179,7 @@ static int gs_closed(struct gs_port *port)
static void gserial_free_port(struct gs_port *port) static void gserial_free_port(struct gs_port *port)
{ {
tasklet_kill(&port->push); cancel_delayed_work_sync(&port->push);
/* wait for old opens to finish */ /* wait for old opens to finish */
wait_event(port->close_wait, gs_closed(port)); wait_event(port->close_wait, gs_closed(port));
WARN_ON(port->port_usb != NULL); WARN_ON(port->port_usb != NULL);

View File

@ -7,7 +7,7 @@
* Copyright (c) 2014 Samsung Electronics Co., Ltd. * Copyright (c) 2014 Samsung Electronics Co., Ltd.
* http://www.samsung.com * http://www.samsung.com
* *
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> * Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com>
*/ */
#ifndef U_UAC2_H #ifndef U_UAC2_H

View File

@ -7,7 +7,7 @@
* Copyright (c) 2013-2014 Samsung Electronics Co., Ltd. * Copyright (c) 2013-2014 Samsung Electronics Co., Ltd.
* http://www.samsung.com * http://www.samsung.com
* *
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> * Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com>
*/ */
#ifndef U_UVC_H #ifndef U_UVC_H

Some files were not shown because too many files have changed in this diff Show More