mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-08 15:04:45 +00:00
USB/PHY patches for 4.21-rc1
Here is the big set of USB and PHY driver patches for 4.21-rc1. All of the usual bits are in here: - loads of USB gadget driver updates and additions - new device ids - phy driver updates - xhci reworks and new features - typec updates Full details are in the shortlog. All of these have been in linux-next for a long time with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCXCYxNA8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+yl4pQCfaQBjMxPpp6TVcHANZ/O+zE3NH/wAoL11p3IB KUq8v9pmcHO8sW5TWOJw =iYGf -----END PGP SIGNATURE----- Merge tag 'usb-4.21-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb Pull USB/PHY updates from Greg KH: "Here is the big set of USB and PHY driver patches for 4.21-rc1. All of the usual bits are in here: - loads of USB gadget driver updates and additions - new device ids - phy driver updates - xhci reworks and new features - typec updates Full details are in the shortlog. All of these have been in linux-next for a long time with no reported issues" * tag 'usb-4.21-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (142 commits) USB: serial: option: add Fibocom NL678 series cdc-acm: fix abnormal DATA RX issue for Mediatek Preloader. usb: r8a66597: Fix a possible concurrency use-after-free bug in r8a66597_endpoint_disable() usb: typec: tcpm: Extend the matching rules on PPS APDO selection usb: typec: Improve Alt Mode documentation usb: musb: dsps: fix runtime pm for peripheral mode usb: musb: dsps: fix otg state machine USB: serial: pl2303: add ids for Hewlett-Packard HP POS pole displays usb: renesas_usbhs: add support for RZ/G2E usb: ehci-omap: Fix deferred probe for phy handling usb: roles: Add a description for the class to Kconfig usb: renesas_usbhs: mark PM functions as __maybe_unused usb: core: Remove unnecessary memset() usb: host: isp1362-hcd: convert to DEFINE_SHOW_ATTRIBUTE phy: qcom-qmp: Expose provided clocks to DT dt-bindings: phy-qcom-qmp: Move #clock-cells to child phy: qcom-qmp: Utilize fully-specified DT registers dt-bindings: phy-qcom-qmp: Fix register underspecification phy: ti: fix semicolon.cocci warnings phy: dphy: Add configuration helpers ...
This commit is contained in:
commit
c0ea81b4d3
@ -14,6 +14,8 @@ Optional properties:
|
||||
- label: symbolic name for the connector,
|
||||
- type: size of the connector, should be specified in case of USB-A, USB-B
|
||||
non-fullsize connectors: "mini", "micro".
|
||||
- self-powered: Set this property if the usb device that has its own power
|
||||
source.
|
||||
|
||||
Optional properties for usb-c-connector:
|
||||
- power-role: should be one of "source", "sink" or "dual"(DRP) if typec
|
||||
|
@ -22,7 +22,8 @@ Required properties:
|
||||
- cpsw-phy-sel : Specifies the phandle to the CPSW phy mode selection
|
||||
device. See also cpsw-phy-sel.txt for it's binding.
|
||||
Note that in legacy cases cpsw-phy-sel may be
|
||||
a child device instead of a phandle.
|
||||
a child device instead of a phandle
|
||||
(DEPRECATED, use phys property instead).
|
||||
|
||||
Optional properties:
|
||||
- ti,hwmods : Must be "cpgmac0"
|
||||
@ -44,6 +45,7 @@ Optional properties:
|
||||
Slave Properties:
|
||||
Required properties:
|
||||
- phy-mode : See ethernet.txt file in the same directory
|
||||
- phys : phandle on phy-gmii-sel PHY (see phy/ti-phy-gmii-sel.txt)
|
||||
|
||||
Optional properties:
|
||||
- dual_emac_res_vlan : Specifies VID to be used to segregate the ports
|
||||
@ -85,12 +87,14 @@ Examples:
|
||||
phy-mode = "rgmii-txid";
|
||||
/* Filled in by U-Boot */
|
||||
mac-address = [ 00 00 00 00 00 00 ];
|
||||
phys = <&phy_gmii_sel 1 0>;
|
||||
};
|
||||
cpsw_emac1: slave@1 {
|
||||
phy_id = <&davinci_mdio>, <1>;
|
||||
phy-mode = "rgmii-txid";
|
||||
/* Filled in by U-Boot */
|
||||
mac-address = [ 00 00 00 00 00 00 ];
|
||||
phys = <&phy_gmii_sel 2 0>;
|
||||
};
|
||||
};
|
||||
|
||||
@ -114,11 +118,13 @@ Examples:
|
||||
phy-mode = "rgmii-txid";
|
||||
/* Filled in by U-Boot */
|
||||
mac-address = [ 00 00 00 00 00 00 ];
|
||||
phys = <&phy_gmii_sel 1 0>;
|
||||
};
|
||||
cpsw_emac1: slave@1 {
|
||||
phy_id = <&davinci_mdio>, <1>;
|
||||
phy-mode = "rgmii-txid";
|
||||
/* Filled in by U-Boot */
|
||||
mac-address = [ 00 00 00 00 00 00 ];
|
||||
phys = <&phy_gmii_sel 2 0>;
|
||||
};
|
||||
};
|
||||
|
17
Documentation/devicetree/bindings/phy/fsl,imx8mq-usb-phy.txt
Normal file
17
Documentation/devicetree/bindings/phy/fsl,imx8mq-usb-phy.txt
Normal file
@ -0,0 +1,17 @@
|
||||
* Freescale i.MX8MQ USB3 PHY binding
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "fsl,imx8mq-usb-phy"
|
||||
- #phys-cells: must be 0 (see phy-bindings.txt in this directory)
|
||||
- reg: The base address and length of the registers
|
||||
- clocks: phandles to the clocks for each clock listed in clock-names
|
||||
- clock-names: must contain "phy"
|
||||
|
||||
Example:
|
||||
usb3_phy0: phy@381f0040 {
|
||||
compatible = "fsl,imx8mq-usb-phy";
|
||||
reg = <0x381f0040 0x40>;
|
||||
clocks = <&clk IMX8MQ_CLK_USB1_PHY_ROOT>;
|
||||
clock-names = "phy";
|
||||
#phy-cells = <0>;
|
||||
};
|
@ -25,10 +25,6 @@ Required properties:
|
||||
- For all others:
|
||||
- The reg-names property shouldn't be defined.
|
||||
|
||||
- #clock-cells: must be 1
|
||||
- Phy pll outputs a bunch of clocks for Tx, Rx and Pipe
|
||||
interface (for pipe based PHYs). These clock are then gate-controlled
|
||||
by gcc.
|
||||
- #address-cells: must be 1
|
||||
- #size-cells: must be 1
|
||||
- ranges: must be present
|
||||
@ -82,27 +78,33 @@ Required nodes:
|
||||
- Each device node of QMP phy is required to have as many child nodes as
|
||||
the number of lanes the PHY has.
|
||||
|
||||
Required properties for child node:
|
||||
Required properties for child nodes of PCIe PHYs (one child per lane):
|
||||
- reg: list of offset and length pairs of register sets for PHY blocks -
|
||||
- index 0: tx
|
||||
- index 1: rx
|
||||
- index 2: pcs
|
||||
- index 3: pcs_misc (optional)
|
||||
|
||||
tx, rx, pcs, and pcs_misc (optional).
|
||||
- #phy-cells: must be 0
|
||||
|
||||
Required properties child node of pcie and usb3 qmp phys:
|
||||
Required properties for a single "lanes" child node of non-PCIe PHYs:
|
||||
- reg: list of offset and length pairs of register sets for PHY blocks
|
||||
For 1-lane devices:
|
||||
tx, rx, pcs, and (optionally) pcs_misc
|
||||
For 2-lane devices:
|
||||
tx0, rx0, pcs, tx1, rx1, and (optionally) pcs_misc
|
||||
- #phy-cells: must be 0
|
||||
|
||||
Required properties for child node of PCIe and USB3 qmp phys:
|
||||
- clocks: a list of phandles and clock-specifier pairs,
|
||||
one for each entry in clock-names.
|
||||
- clock-names: Must contain following:
|
||||
"pipe<lane-number>" for pipe clock specific to each lane.
|
||||
- clock-output-names: Name of the PHY clock that will be the parent for
|
||||
the above pipe clock.
|
||||
|
||||
For "qcom,ipq8074-qmp-pcie-phy":
|
||||
- "pcie20_phy0_pipe_clk" Pipe Clock parent
|
||||
(or)
|
||||
"pcie20_phy1_pipe_clk"
|
||||
- #clock-cells: must be 0
|
||||
- Phy pll outputs pipe clocks for pipe based PHYs. These clocks are then
|
||||
gate-controlled by the gcc.
|
||||
|
||||
Required properties for child node of PHYs with lane reset, AKA:
|
||||
"qcom,msm8996-qmp-pcie-phy"
|
||||
@ -115,7 +117,6 @@ Example:
|
||||
phy@34000 {
|
||||
compatible = "qcom,msm8996-qmp-pcie-phy";
|
||||
reg = <0x34000 0x488>;
|
||||
#clock-cells = <1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
@ -137,6 +138,7 @@ Example:
|
||||
reg = <0x35000 0x130>,
|
||||
<0x35200 0x200>,
|
||||
<0x35400 0x1dc>;
|
||||
#clock-cells = <0>;
|
||||
#phy-cells = <0>;
|
||||
|
||||
clocks = <&gcc GCC_PCIE_0_PIPE_CLK>;
|
||||
@ -150,3 +152,54 @@ Example:
|
||||
...
|
||||
...
|
||||
};
|
||||
|
||||
phy@88eb000 {
|
||||
compatible = "qcom,sdm845-qmp-usb3-uni-phy";
|
||||
reg = <0x88eb000 0x18c>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
|
||||
clocks = <&gcc GCC_USB3_SEC_PHY_AUX_CLK>,
|
||||
<&gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>,
|
||||
<&gcc GCC_USB3_SEC_CLKREF_CLK>,
|
||||
<&gcc GCC_USB3_SEC_PHY_COM_AUX_CLK>;
|
||||
clock-names = "aux", "cfg_ahb", "ref", "com_aux";
|
||||
|
||||
resets = <&gcc GCC_USB3PHY_PHY_SEC_BCR>,
|
||||
<&gcc GCC_USB3_PHY_SEC_BCR>;
|
||||
reset-names = "phy", "common";
|
||||
|
||||
lane@88eb200 {
|
||||
reg = <0x88eb200 0x128>,
|
||||
<0x88eb400 0x1fc>,
|
||||
<0x88eb800 0x218>,
|
||||
<0x88eb600 0x70>;
|
||||
#clock-cells = <0>;
|
||||
#phy-cells = <0>;
|
||||
clocks = <&gcc GCC_USB3_SEC_PHY_PIPE_CLK>;
|
||||
clock-names = "pipe0";
|
||||
clock-output-names = "usb3_uni_phy_pipe_clk_src";
|
||||
};
|
||||
};
|
||||
|
||||
phy@1d87000 {
|
||||
compatible = "qcom,sdm845-qmp-ufs-phy";
|
||||
reg = <0x1d87000 0x18c>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
clock-names = "ref",
|
||||
"ref_aux";
|
||||
clocks = <&gcc GCC_UFS_MEM_CLKREF_CLK>,
|
||||
<&gcc GCC_UFS_PHY_PHY_AUX_CLK>;
|
||||
|
||||
lanes@1d87400 {
|
||||
reg = <0x1d87400 0x108>,
|
||||
<0x1d87600 0x1e0>,
|
||||
<0x1d87c00 0x1dc>,
|
||||
<0x1d87800 0x108>,
|
||||
<0x1d87a00 0x1e0>;
|
||||
#phy-cells = <0>;
|
||||
};
|
||||
};
|
||||
|
@ -14,13 +14,14 @@ Required properties:
|
||||
* allwinner,sun8i-r40-usb-phy
|
||||
* allwinner,sun8i-v3s-usb-phy
|
||||
* allwinner,sun50i-a64-usb-phy
|
||||
* allwinner,sun50i-h6-usb-phy
|
||||
- reg : a list of offset + length pairs
|
||||
- reg-names :
|
||||
* "phy_ctrl"
|
||||
* "pmu0" for H3, V3s and A64
|
||||
* "pmu0" for H3, V3s, A64 or H6
|
||||
* "pmu1"
|
||||
* "pmu2" for sun4i, sun6i, sun7i, sun8i-a83t or sun8i-h3
|
||||
* "pmu3" for sun8i-h3
|
||||
* "pmu3" for sun8i-h3 or sun50i-h6
|
||||
- #phy-cells : from the generic phy bindings, must be 1
|
||||
- clocks : phandle + clock specifier for the phy clocks
|
||||
- clock-names :
|
||||
@ -29,12 +30,13 @@ Required properties:
|
||||
* "usb0_phy", "usb1_phy" for sun8i
|
||||
* "usb0_phy", "usb1_phy", "usb2_phy" and "usb2_hsic_12M" for sun8i-a83t
|
||||
* "usb0_phy", "usb1_phy", "usb2_phy" and "usb3_phy" for sun8i-h3
|
||||
* "usb0_phy" and "usb3_phy" for sun50i-h6
|
||||
- resets : a list of phandle + reset specifier pairs
|
||||
- reset-names :
|
||||
* "usb0_reset"
|
||||
* "usb1_reset"
|
||||
* "usb2_reset" for sun4i, sun6i, sun7i, sun8i-a83t or sun8i-h3
|
||||
* "usb3_reset" for sun8i-h3
|
||||
* "usb3_reset" for sun8i-h3 and sun50i-h6
|
||||
|
||||
Optional properties:
|
||||
- usb0_id_det-gpios : gpio phandle for reading the otg id pin value
|
||||
|
68
Documentation/devicetree/bindings/phy/ti-phy-gmii-sel.txt
Normal file
68
Documentation/devicetree/bindings/phy/ti-phy-gmii-sel.txt
Normal file
@ -0,0 +1,68 @@
|
||||
CPSW Port's Interface Mode Selection PHY Tree Bindings
|
||||
-----------------------------------------------
|
||||
|
||||
TI am335x/am437x/dra7(am5)/dm814x CPSW3G Ethernet Subsystem supports
|
||||
two 10/100/1000 Ethernet ports with selectable G/MII, RMII, and RGMII interfaces.
|
||||
The interface mode is selected by configuring the MII mode selection register(s)
|
||||
(GMII_SEL) in the System Control Module chapter (SCM). GMII_SEL register(s) and
|
||||
bit fields placement in SCM are different between SoCs while fields meaning
|
||||
is the same.
|
||||
+--------------+
|
||||
+-------------------------------+ |SCM |
|
||||
| CPSW | | +---------+ |
|
||||
| +--------------------------------+gmii_sel | |
|
||||
| | | | +---------+ |
|
||||
| +----v---+ +--------+ | +--------------+
|
||||
| |Port 1..<--+-->GMII/MII<------->
|
||||
| | | | | | |
|
||||
| +--------+ | +--------+ |
|
||||
| | |
|
||||
| | +--------+ |
|
||||
| | | RMII <------->
|
||||
| +--> | |
|
||||
| | +--------+ |
|
||||
| | |
|
||||
| | +--------+ |
|
||||
| | | RGMII <------->
|
||||
| +--> | |
|
||||
| +--------+ |
|
||||
+-------------------------------+
|
||||
|
||||
CPSW Port's Interface Mode Selection PHY describes MII interface mode between
|
||||
CPSW Port and Ethernet PHY which depends on Eth PHY and board configuration.
|
||||
|
||||
CPSW Port's Interface Mode Selection PHY device should defined as child device
|
||||
of SCM node (scm_conf) and can be attached to each CPSW port node using standard
|
||||
PHY bindings (See phy/phy-bindings.txt).
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "ti,am3352-phy-gmii-sel" for am335x platform
|
||||
"ti,dra7xx-phy-gmii-sel" for dra7xx/am57xx platform
|
||||
"ti,am43xx-phy-gmii-sel" for am43xx platform
|
||||
"ti,dm814-phy-gmii-sel" for dm814x platform
|
||||
- reg : Address and length of the register set for the device
|
||||
- #phy-cells : must be 2.
|
||||
cell 1 - CPSW port number (starting from 1)
|
||||
cell 2 - RMII refclk mode
|
||||
|
||||
Examples:
|
||||
phy_gmii_sel: phy-gmii-sel {
|
||||
compatible = "ti,am3352-phy-gmii-sel";
|
||||
reg = <0x650 0x4>;
|
||||
#phy-cells = <2>;
|
||||
};
|
||||
|
||||
mac: ethernet@4a100000 {
|
||||
compatible = "ti,am335x-cpsw","ti,cpsw";
|
||||
...
|
||||
|
||||
cpsw_emac0: slave@4a100200 {
|
||||
...
|
||||
phys = <&phy_gmii_sel 1 1>;
|
||||
};
|
||||
|
||||
cpsw_emac1: slave@4a100300 {
|
||||
...
|
||||
phys = <&phy_gmii_sel 2 1>;
|
||||
};
|
||||
};
|
@ -80,15 +80,19 @@ Optional properties:
|
||||
controller. It's expected that a mux state of 0 indicates device mode and a
|
||||
mux state of 1 indicates host mode.
|
||||
- mux-control-names: Shall be "usb_switch" if mux-controls is specified.
|
||||
- pinctrl-names: Names for optional pin modes in "default", "host", "device"
|
||||
- pinctrl-names: Names for optional pin modes in "default", "host", "device".
|
||||
In case of HSIC-mode, "idle" and "active" pin modes are mandatory. In this
|
||||
case, the "idle" state needs to pull down the data and strobe pin
|
||||
and the "active" state needs to pull up the strobe pin.
|
||||
- pinctrl-n: alternate pin modes
|
||||
|
||||
i.mx specific properties
|
||||
- fsl,usbmisc: phandler of non-core register device, with one
|
||||
argument that indicate usb controller index
|
||||
- disable-over-current: disable over current detect
|
||||
- over-current-active-high: over current signal polarity is high active,
|
||||
typically over current signal polarity is low active.
|
||||
- over-current-active-low: over current signal polarity is active low.
|
||||
- over-current-active-high: over current signal polarity is active high.
|
||||
It's recommended to specify the over current polarity.
|
||||
- external-vbus-divider: enables off-chip resistor divider for Vbus
|
||||
|
||||
Example:
|
||||
@ -111,3 +115,29 @@ Example:
|
||||
mux-controls = <&usb_switch>;
|
||||
mux-control-names = "usb_switch";
|
||||
};
|
||||
|
||||
Example for HSIC:
|
||||
|
||||
usb@2184400 {
|
||||
compatible = "fsl,imx6q-usb", "fsl,imx27-usb";
|
||||
reg = <0x02184400 0x200>;
|
||||
interrupts = <0 41 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clks IMX6QDL_CLK_USBOH3>;
|
||||
fsl,usbphy = <&usbphynop1>;
|
||||
fsl,usbmisc = <&usbmisc 2>;
|
||||
phy_type = "hsic";
|
||||
dr_mode = "host";
|
||||
ahb-burst-config = <0x0>;
|
||||
tx-burst-size-dword = <0x10>;
|
||||
rx-burst-size-dword = <0x10>;
|
||||
pinctrl-names = "idle", "active";
|
||||
pinctrl-0 = <&pinctrl_usbh2_idle>;
|
||||
pinctrl-1 = <&pinctrl_usbh2_active>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
usbnet: smsc@1 {
|
||||
compatible = "usb424,9730";
|
||||
reg = <1>;
|
||||
};
|
||||
};
|
||||
|
@ -37,7 +37,11 @@ Optional properties:
|
||||
- phy-names: from the *Generic PHY* bindings; supported names are "usb2-phy"
|
||||
or "usb3-phy".
|
||||
- resets: a single pair of phandle and reset specifier
|
||||
- snps,usb2-lpm-disable: indicate if we don't want to enable USB2 HW LPM
|
||||
- snps,usb3_lpm_capable: determines if platform is USB3 LPM capable
|
||||
- snps,dis-start-transfer-quirk: when set, disable isoc START TRANSFER command
|
||||
failure SW work-around for DWC_usb31 version 1.70a-ea06
|
||||
and prior.
|
||||
- snps,disable_scramble_quirk: true when SW should disable data scrambling.
|
||||
Only really useful for FPGA builds.
|
||||
- snps,has-lpm-erratum: true when DWC3 was configured with LPM Erratum enabled
|
||||
|
@ -19,6 +19,7 @@ Linux USB API
|
||||
dwc3
|
||||
writing_musb_glue_layer
|
||||
typec
|
||||
typec_bus
|
||||
usb3-debug-port
|
||||
|
||||
.. only:: subproject and html
|
||||
|
@ -1,3 +1,4 @@
|
||||
.. _typec:
|
||||
|
||||
USB Type-C connector class
|
||||
==========================
|
||||
|
@ -13,10 +13,10 @@ every alternate mode, so every alternate mode will need a custom driver.
|
||||
USB Type-C bus allows binding a driver to the discovered partner alternate
|
||||
modes by using the SVID and the mode number.
|
||||
|
||||
USB Type-C Connector Class provides a device for every alternate mode a port
|
||||
supports, and separate device for every alternate mode the partner supports.
|
||||
The drivers for the alternate modes are bound to the partner alternate mode
|
||||
devices, and the port alternate mode devices must be handled by the port
|
||||
:ref:`USB Type-C Connector Class <typec>` provides a device for every alternate
|
||||
mode a port supports, and separate device for every alternate mode the partner
|
||||
supports. The drivers for the alternate modes are bound to the partner alternate
|
||||
mode devices, and the port alternate mode devices must be handled by the port
|
||||
drivers.
|
||||
|
||||
When a new partner alternate mode device is registered, it is linked to the
|
||||
@ -46,7 +46,7 @@ enter any modes on their own.
|
||||
``->vdm`` is the most important callback in the operation callbacks vector. It
|
||||
will be used to deliver all the SVID specific commands from the partner to the
|
||||
alternate mode driver, and vice versa in case of port drivers. The drivers send
|
||||
the SVID specific commands to each other using :c:func:`typec_altmode_vmd()`.
|
||||
the SVID specific commands to each other using :c:func:`typec_altmode_vdm()`.
|
||||
|
||||
If the communication with the partner using the SVID specific commands results
|
||||
in need to reconfigure the pins on the connector, the alternate mode driver
|
||||
@ -67,15 +67,15 @@ Type-C Specification, and also put the connector back to ``TYPEC_STATE_USB``
|
||||
after the mode has been exited.
|
||||
|
||||
An example of working definitions for SVID specific pin configurations would
|
||||
look like this:
|
||||
look like this::
|
||||
|
||||
enum {
|
||||
ALTMODEX_CONF_A = TYPEC_STATE_MODAL,
|
||||
ALTMODEX_CONF_B,
|
||||
...
|
||||
};
|
||||
enum {
|
||||
ALTMODEX_CONF_A = TYPEC_STATE_MODAL,
|
||||
ALTMODEX_CONF_B,
|
||||
...
|
||||
};
|
||||
|
||||
Helper macro ``TYPEC_MODAL_STATE()`` can also be used:
|
||||
Helper macro ``TYPEC_MODAL_STATE()`` can also be used::
|
||||
|
||||
#define ALTMODEX_CONF_A = TYPEC_MODAL_STATE(0);
|
||||
#define ALTMODEX_CONF_B = TYPEC_MODAL_STATE(1);
|
||||
|
@ -119,5 +119,5 @@ If a deauthorized interface will be authorized so the driver probing must
|
||||
be triggered manually by writing INTERFACE to /sys/bus/usb/drivers_probe
|
||||
|
||||
For drivers that need multiple interfaces all needed interfaces should be
|
||||
authroized first. After that the drivers should be probed.
|
||||
authorized first. After that the drivers should be probed.
|
||||
This avoids side effects.
|
||||
|
@ -6310,6 +6310,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/kishon/linux-phy.git
|
||||
S: Supported
|
||||
F: drivers/phy/
|
||||
F: include/linux/phy/
|
||||
F: Documentation/devicetree/bindings/phy/
|
||||
|
||||
GENERIC PINCTRL I2C DEMULTIPLEXER DRIVER
|
||||
M: Wolfram Sang <wsa+renesas@sang-engineering.com>
|
||||
|
@ -1165,28 +1165,13 @@ static void mvpp22_gop_setup_irq(struct mvpp2_port *port)
|
||||
*/
|
||||
static int mvpp22_comphy_init(struct mvpp2_port *port)
|
||||
{
|
||||
enum phy_mode mode;
|
||||
int ret;
|
||||
|
||||
if (!port->comphy)
|
||||
return 0;
|
||||
|
||||
switch (port->phy_interface) {
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
case PHY_INTERFACE_MODE_1000BASEX:
|
||||
mode = PHY_MODE_SGMII;
|
||||
break;
|
||||
case PHY_INTERFACE_MODE_2500BASEX:
|
||||
mode = PHY_MODE_2500SGMII;
|
||||
break;
|
||||
case PHY_INTERFACE_MODE_10GKR:
|
||||
mode = PHY_MODE_10GKR;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = phy_set_mode(port->comphy, mode);
|
||||
ret = phy_set_mode_ext(port->comphy, PHY_MODE_ETHERNET,
|
||||
port->phy_interface);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -471,7 +471,6 @@ static int ocelot_port_open(struct net_device *dev)
|
||||
{
|
||||
struct ocelot_port *port = netdev_priv(dev);
|
||||
struct ocelot *ocelot = port->ocelot;
|
||||
enum phy_mode phy_mode;
|
||||
int err;
|
||||
|
||||
/* Enable receiving frames on the port, and activate auto-learning of
|
||||
@ -483,12 +482,8 @@ static int ocelot_port_open(struct net_device *dev)
|
||||
ANA_PORT_PORT_CFG, port->chip_port);
|
||||
|
||||
if (port->serdes) {
|
||||
if (port->phy_mode == PHY_INTERFACE_MODE_SGMII)
|
||||
phy_mode = PHY_MODE_SGMII;
|
||||
else
|
||||
phy_mode = PHY_MODE_QSGMII;
|
||||
|
||||
err = phy_set_mode(port->serdes, phy_mode);
|
||||
err = phy_set_mode_ext(port->serdes, PHY_MODE_ETHERNET,
|
||||
port->phy_mode);
|
||||
if (err) {
|
||||
netdev_err(dev, "Could not set mode of SerDes\n");
|
||||
return err;
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/net_tstamp.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
@ -387,6 +388,7 @@ struct cpsw_slave_data {
|
||||
int phy_if;
|
||||
u8 mac_addr[ETH_ALEN];
|
||||
u16 dual_emac_res_vlan; /* Reserved VLAN for DualEMAC */
|
||||
struct phy *ifphy;
|
||||
};
|
||||
|
||||
struct cpsw_platform_data {
|
||||
@ -1624,7 +1626,12 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv)
|
||||
phy_start(slave->phy);
|
||||
|
||||
/* Configure GMII_SEL register */
|
||||
cpsw_phy_sel(cpsw->dev, slave->phy->interface, slave->slave_num);
|
||||
if (!IS_ERR(slave->data->ifphy))
|
||||
phy_set_mode_ext(slave->data->ifphy, PHY_MODE_ETHERNET,
|
||||
slave->data->phy_if);
|
||||
else
|
||||
cpsw_phy_sel(cpsw->dev, slave->phy->interface,
|
||||
slave->slave_num);
|
||||
}
|
||||
|
||||
static inline void cpsw_add_default_vlan(struct cpsw_priv *priv)
|
||||
@ -3274,6 +3281,16 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
|
||||
if (!of_node_name_eq(slave_node, "slave"))
|
||||
continue;
|
||||
|
||||
slave_data->ifphy = devm_of_phy_get(&pdev->dev, slave_node,
|
||||
NULL);
|
||||
if (!IS_ENABLED(CONFIG_TI_CPSW_PHY_SEL) &&
|
||||
IS_ERR(slave_data->ifphy)) {
|
||||
ret = PTR_ERR(slave_data->ifphy);
|
||||
dev_err(&pdev->dev,
|
||||
"%d: Error retrieving port phy: %d\n", i, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
slave_data->phy_node = of_parse_phandle(slave_node,
|
||||
"phy-handle", 0);
|
||||
parp = of_get_property(slave_node, "phy_id", &lenp);
|
||||
|
@ -15,6 +15,14 @@ config GENERIC_PHY
|
||||
phy users can obtain reference to the PHY. All the users of this
|
||||
framework should select this config.
|
||||
|
||||
config GENERIC_PHY_MIPI_DPHY
|
||||
bool
|
||||
help
|
||||
Generic MIPI D-PHY support.
|
||||
|
||||
Provides a number of helpers a core functions for MIPI D-PHY
|
||||
drivers to us.
|
||||
|
||||
config PHY_LPC18XX_USB_OTG
|
||||
tristate "NXP LPC18xx/43xx SoC USB OTG PHY driver"
|
||||
depends on OF && (ARCH_LPC18XX || COMPILE_TEST)
|
||||
@ -44,6 +52,7 @@ source "drivers/phy/allwinner/Kconfig"
|
||||
source "drivers/phy/amlogic/Kconfig"
|
||||
source "drivers/phy/broadcom/Kconfig"
|
||||
source "drivers/phy/cadence/Kconfig"
|
||||
source "drivers/phy/freescale/Kconfig"
|
||||
source "drivers/phy/hisilicon/Kconfig"
|
||||
source "drivers/phy/lantiq/Kconfig"
|
||||
source "drivers/phy/marvell/Kconfig"
|
||||
|
@ -4,6 +4,7 @@
|
||||
#
|
||||
|
||||
obj-$(CONFIG_GENERIC_PHY) += phy-core.o
|
||||
obj-$(CONFIG_GENERIC_PHY_MIPI_DPHY) += phy-core-mipi-dphy.o
|
||||
obj-$(CONFIG_PHY_LPC18XX_USB_OTG) += phy-lpc18xx-usb-otg.o
|
||||
obj-$(CONFIG_PHY_XGENE) += phy-xgene.o
|
||||
obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o
|
||||
@ -16,6 +17,7 @@ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
|
||||
obj-$(CONFIG_ARCH_TEGRA) += tegra/
|
||||
obj-y += broadcom/ \
|
||||
cadence/ \
|
||||
freescale/ \
|
||||
hisilicon/ \
|
||||
marvell/ \
|
||||
motorola/ \
|
||||
|
@ -115,6 +115,7 @@ enum sun4i_usb_phy_type {
|
||||
sun8i_r40_phy,
|
||||
sun8i_v3s_phy,
|
||||
sun50i_a64_phy,
|
||||
sun50i_h6_phy,
|
||||
};
|
||||
|
||||
struct sun4i_usb_phy_cfg {
|
||||
@ -126,6 +127,7 @@ struct sun4i_usb_phy_cfg {
|
||||
bool dedicated_clocks;
|
||||
bool enable_pmu_unk1;
|
||||
bool phy0_dual_route;
|
||||
int missing_phys;
|
||||
};
|
||||
|
||||
struct sun4i_usb_phy_data {
|
||||
@ -294,7 +296,8 @@ static int sun4i_usb_phy_init(struct phy *_phy)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (data->cfg->type == sun8i_a83t_phy) {
|
||||
if (data->cfg->type == sun8i_a83t_phy ||
|
||||
data->cfg->type == sun50i_h6_phy) {
|
||||
if (phy->index == 0) {
|
||||
val = readl(data->base + data->cfg->phyctl_offset);
|
||||
val |= PHY_CTL_VBUSVLDEXT;
|
||||
@ -343,7 +346,8 @@ static int sun4i_usb_phy_exit(struct phy *_phy)
|
||||
struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
|
||||
|
||||
if (phy->index == 0) {
|
||||
if (data->cfg->type == sun8i_a83t_phy) {
|
||||
if (data->cfg->type == sun8i_a83t_phy ||
|
||||
data->cfg->type == sun50i_h6_phy) {
|
||||
void __iomem *phyctl = data->base +
|
||||
data->cfg->phyctl_offset;
|
||||
|
||||
@ -474,7 +478,8 @@ static int sun4i_usb_phy_power_off(struct phy *_phy)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sun4i_usb_phy_set_mode(struct phy *_phy, enum phy_mode mode)
|
||||
static int sun4i_usb_phy_set_mode(struct phy *_phy,
|
||||
enum phy_mode mode, int submode)
|
||||
{
|
||||
struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
|
||||
struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
|
||||
@ -646,6 +651,9 @@ static struct phy *sun4i_usb_phy_xlate(struct device *dev,
|
||||
if (args->args[0] >= data->cfg->num_phys)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
if (data->cfg->missing_phys & BIT(args->args[0]))
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
return data->phys[args->args[0]].phy;
|
||||
}
|
||||
|
||||
@ -741,6 +749,9 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
|
||||
struct sun4i_usb_phy *phy = data->phys + i;
|
||||
char name[16];
|
||||
|
||||
if (data->cfg->missing_phys & BIT(i))
|
||||
continue;
|
||||
|
||||
snprintf(name, sizeof(name), "usb%d_vbus", i);
|
||||
phy->vbus = devm_regulator_get_optional(dev, name);
|
||||
if (IS_ERR(phy->vbus)) {
|
||||
@ -952,6 +963,17 @@ static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = {
|
||||
.phy0_dual_route = true,
|
||||
};
|
||||
|
||||
static const struct sun4i_usb_phy_cfg sun50i_h6_cfg = {
|
||||
.num_phys = 4,
|
||||
.type = sun50i_h6_phy,
|
||||
.disc_thresh = 3,
|
||||
.phyctl_offset = REG_PHYCTL_A33,
|
||||
.dedicated_clocks = true,
|
||||
.enable_pmu_unk1 = true,
|
||||
.phy0_dual_route = true,
|
||||
.missing_phys = BIT(1) | BIT(2),
|
||||
};
|
||||
|
||||
static const struct of_device_id sun4i_usb_phy_of_match[] = {
|
||||
{ .compatible = "allwinner,sun4i-a10-usb-phy", .data = &sun4i_a10_cfg },
|
||||
{ .compatible = "allwinner,sun5i-a13-usb-phy", .data = &sun5i_a13_cfg },
|
||||
@ -965,6 +987,7 @@ static const struct of_device_id sun4i_usb_phy_of_match[] = {
|
||||
{ .compatible = "allwinner,sun8i-v3s-usb-phy", .data = &sun8i_v3s_cfg },
|
||||
{ .compatible = "allwinner,sun50i-a64-usb-phy",
|
||||
.data = &sun50i_a64_cfg},
|
||||
{ .compatible = "allwinner,sun50i-h6-usb-phy", .data = &sun50i_h6_cfg },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
|
||||
|
@ -152,7 +152,8 @@ static int phy_meson_gxl_usb2_reset(struct phy *phy)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int phy_meson_gxl_usb2_set_mode(struct phy *phy, enum phy_mode mode)
|
||||
static int phy_meson_gxl_usb2_set_mode(struct phy *phy,
|
||||
enum phy_mode mode, int submode)
|
||||
{
|
||||
struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
|
||||
|
||||
@ -209,7 +210,7 @@ static int phy_meson_gxl_usb2_power_on(struct phy *phy)
|
||||
/* power on the PHY by taking it out of reset mode */
|
||||
regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET, 0);
|
||||
|
||||
ret = phy_meson_gxl_usb2_set_mode(phy, priv->mode);
|
||||
ret = phy_meson_gxl_usb2_set_mode(phy, priv->mode, 0);
|
||||
if (ret) {
|
||||
phy_meson_gxl_usb2_power_off(phy);
|
||||
|
||||
|
@ -119,7 +119,8 @@ static int phy_meson_gxl_usb3_power_off(struct phy *phy)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int phy_meson_gxl_usb3_set_mode(struct phy *phy, enum phy_mode mode)
|
||||
static int phy_meson_gxl_usb3_set_mode(struct phy *phy,
|
||||
enum phy_mode mode, int submode)
|
||||
{
|
||||
struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
|
||||
|
||||
@ -164,7 +165,7 @@ static int phy_meson_gxl_usb3_init(struct phy *phy)
|
||||
if (ret)
|
||||
goto err_disable_clk_phy;
|
||||
|
||||
ret = phy_meson_gxl_usb3_set_mode(phy, priv->mode);
|
||||
ret = phy_meson_gxl_usb3_set_mode(phy, priv->mode, 0);
|
||||
if (ret)
|
||||
goto err_disable_clk_peripheral;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Phy driver for Cadence MHDP DisplayPort controller
|
||||
# Phy drivers for Cadence PHYs
|
||||
#
|
||||
config PHY_CADENCE_DP
|
||||
tristate "Cadence MHDP DisplayPort PHY driver"
|
||||
@ -8,3 +8,10 @@ config PHY_CADENCE_DP
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Support for Cadence MHDP DisplayPort PHY.
|
||||
|
||||
config PHY_CADENCE_SIERRA
|
||||
tristate "Cadence Sierra PHY Driver"
|
||||
depends on OF && HAS_IOMEM && RESET_CONTROLLER
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Enable this to support the Cadence Sierra PHY driver
|
@ -1 +1,2 @@
|
||||
obj-$(CONFIG_PHY_CADENCE_DP) += phy-cadence-dp.o
|
||||
obj-$(CONFIG_PHY_CADENCE_SIERRA) += phy-cadence-sierra.o
|
||||
|
395
drivers/phy/cadence/phy-cadence-sierra.c
Normal file
395
drivers/phy/cadence/phy-cadence-sierra.c
Normal file
@ -0,0 +1,395 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Cadence Sierra PHY Driver
|
||||
*
|
||||
* Copyright (c) 2018 Cadence Design Systems
|
||||
* Author: Alan Douglas <adouglas@cadence.com>
|
||||
*
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <dt-bindings/phy/phy.h>
|
||||
|
||||
/* PHY register offsets */
|
||||
#define SIERRA_PHY_PLL_CFG (0xc00e << 2)
|
||||
#define SIERRA_DET_STANDEC_A (0x4000 << 2)
|
||||
#define SIERRA_DET_STANDEC_B (0x4001 << 2)
|
||||
#define SIERRA_DET_STANDEC_C (0x4002 << 2)
|
||||
#define SIERRA_DET_STANDEC_D (0x4003 << 2)
|
||||
#define SIERRA_DET_STANDEC_E (0x4004 << 2)
|
||||
#define SIERRA_PSM_LANECAL (0x4008 << 2)
|
||||
#define SIERRA_PSM_DIAG (0x4015 << 2)
|
||||
#define SIERRA_PSC_TX_A0 (0x4028 << 2)
|
||||
#define SIERRA_PSC_TX_A1 (0x4029 << 2)
|
||||
#define SIERRA_PSC_TX_A2 (0x402A << 2)
|
||||
#define SIERRA_PSC_TX_A3 (0x402B << 2)
|
||||
#define SIERRA_PSC_RX_A0 (0x4030 << 2)
|
||||
#define SIERRA_PSC_RX_A1 (0x4031 << 2)
|
||||
#define SIERRA_PSC_RX_A2 (0x4032 << 2)
|
||||
#define SIERRA_PSC_RX_A3 (0x4033 << 2)
|
||||
#define SIERRA_PLLCTRL_SUBRATE (0x403A << 2)
|
||||
#define SIERRA_PLLCTRL_GEN_D (0x403E << 2)
|
||||
#define SIERRA_DRVCTRL_ATTEN (0x406A << 2)
|
||||
#define SIERRA_CLKPATHCTRL_TMR (0x4081 << 2)
|
||||
#define SIERRA_RX_CREQ_FLTR_A_MODE1 (0x4087 << 2)
|
||||
#define SIERRA_RX_CREQ_FLTR_A_MODE0 (0x4088 << 2)
|
||||
#define SIERRA_CREQ_CCLKDET_MODE01 (0x408E << 2)
|
||||
#define SIERRA_RX_CTLE_MAINTENANCE (0x4091 << 2)
|
||||
#define SIERRA_CREQ_FSMCLK_SEL (0x4092 << 2)
|
||||
#define SIERRA_CTLELUT_CTRL (0x4098 << 2)
|
||||
#define SIERRA_DFE_ECMP_RATESEL (0x40C0 << 2)
|
||||
#define SIERRA_DFE_SMP_RATESEL (0x40C1 << 2)
|
||||
#define SIERRA_DEQ_VGATUNE_CTRL (0x40E1 << 2)
|
||||
#define SIERRA_TMRVAL_MODE3 (0x416E << 2)
|
||||
#define SIERRA_TMRVAL_MODE2 (0x416F << 2)
|
||||
#define SIERRA_TMRVAL_MODE1 (0x4170 << 2)
|
||||
#define SIERRA_TMRVAL_MODE0 (0x4171 << 2)
|
||||
#define SIERRA_PICNT_MODE1 (0x4174 << 2)
|
||||
#define SIERRA_CPI_OUTBUF_RATESEL (0x417C << 2)
|
||||
#define SIERRA_LFPSFILT_NS (0x418A << 2)
|
||||
#define SIERRA_LFPSFILT_RD (0x418B << 2)
|
||||
#define SIERRA_LFPSFILT_MP (0x418C << 2)
|
||||
#define SIERRA_SDFILT_H2L_A (0x4191 << 2)
|
||||
|
||||
#define SIERRA_MACRO_ID 0x00007364
|
||||
#define SIERRA_MAX_LANES 4
|
||||
|
||||
struct cdns_sierra_inst {
|
||||
struct phy *phy;
|
||||
u32 phy_type;
|
||||
u32 num_lanes;
|
||||
u32 mlane;
|
||||
struct reset_control *lnk_rst;
|
||||
};
|
||||
|
||||
struct cdns_reg_pairs {
|
||||
u16 val;
|
||||
u32 off;
|
||||
};
|
||||
|
||||
struct cdns_sierra_data {
|
||||
u32 id_value;
|
||||
u32 pcie_regs;
|
||||
u32 usb_regs;
|
||||
struct cdns_reg_pairs *pcie_vals;
|
||||
struct cdns_reg_pairs *usb_vals;
|
||||
};
|
||||
|
||||
struct cdns_sierra_phy {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
struct cdns_sierra_data *init_data;
|
||||
struct cdns_sierra_inst phys[SIERRA_MAX_LANES];
|
||||
struct reset_control *phy_rst;
|
||||
struct reset_control *apb_rst;
|
||||
struct clk *clk;
|
||||
int nsubnodes;
|
||||
bool autoconf;
|
||||
};
|
||||
|
||||
static void cdns_sierra_phy_init(struct phy *gphy)
|
||||
{
|
||||
struct cdns_sierra_inst *ins = phy_get_drvdata(gphy);
|
||||
struct cdns_sierra_phy *phy = dev_get_drvdata(gphy->dev.parent);
|
||||
int i, j;
|
||||
struct cdns_reg_pairs *vals;
|
||||
u32 num_regs;
|
||||
|
||||
if (ins->phy_type == PHY_TYPE_PCIE) {
|
||||
num_regs = phy->init_data->pcie_regs;
|
||||
vals = phy->init_data->pcie_vals;
|
||||
} else if (ins->phy_type == PHY_TYPE_USB3) {
|
||||
num_regs = phy->init_data->usb_regs;
|
||||
vals = phy->init_data->usb_vals;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < ins->num_lanes; i++)
|
||||
for (j = 0; j < num_regs ; j++)
|
||||
writel(vals[j].val, phy->base +
|
||||
vals[j].off + (i + ins->mlane) * 0x800);
|
||||
}
|
||||
|
||||
static int cdns_sierra_phy_on(struct phy *gphy)
|
||||
{
|
||||
struct cdns_sierra_inst *ins = phy_get_drvdata(gphy);
|
||||
|
||||
/* Take the PHY lane group out of reset */
|
||||
return reset_control_deassert(ins->lnk_rst);
|
||||
}
|
||||
|
||||
static int cdns_sierra_phy_off(struct phy *gphy)
|
||||
{
|
||||
struct cdns_sierra_inst *ins = phy_get_drvdata(gphy);
|
||||
|
||||
return reset_control_assert(ins->lnk_rst);
|
||||
}
|
||||
|
||||
static const struct phy_ops ops = {
|
||||
.power_on = cdns_sierra_phy_on,
|
||||
.power_off = cdns_sierra_phy_off,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int cdns_sierra_get_optional(struct cdns_sierra_inst *inst,
|
||||
struct device_node *child)
|
||||
{
|
||||
if (of_property_read_u32(child, "reg", &inst->mlane))
|
||||
return -EINVAL;
|
||||
|
||||
if (of_property_read_u32(child, "cdns,num-lanes", &inst->num_lanes))
|
||||
return -EINVAL;
|
||||
|
||||
if (of_property_read_u32(child, "cdns,phy-type", &inst->phy_type))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id cdns_sierra_id_table[];
|
||||
|
||||
static int cdns_sierra_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct cdns_sierra_phy *sp;
|
||||
struct phy_provider *phy_provider;
|
||||
struct device *dev = &pdev->dev;
|
||||
const struct of_device_id *match;
|
||||
struct resource *res;
|
||||
int i, ret, node = 0;
|
||||
struct device_node *dn = dev->of_node, *child;
|
||||
|
||||
if (of_get_child_count(dn) == 0)
|
||||
return -ENODEV;
|
||||
|
||||
sp = devm_kzalloc(dev, sizeof(*sp), GFP_KERNEL);
|
||||
if (!sp)
|
||||
return -ENOMEM;
|
||||
dev_set_drvdata(dev, sp);
|
||||
sp->dev = dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
sp->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(sp->base)) {
|
||||
dev_err(dev, "missing \"reg\"\n");
|
||||
return PTR_ERR(sp->base);
|
||||
}
|
||||
|
||||
/* Get init data for this PHY */
|
||||
match = of_match_device(cdns_sierra_id_table, dev);
|
||||
if (!match)
|
||||
return -EINVAL;
|
||||
sp->init_data = (struct cdns_sierra_data *)match->data;
|
||||
|
||||
platform_set_drvdata(pdev, sp);
|
||||
|
||||
sp->clk = devm_clk_get(dev, "phy_clk");
|
||||
if (IS_ERR(sp->clk)) {
|
||||
dev_err(dev, "failed to get clock phy_clk\n");
|
||||
return PTR_ERR(sp->clk);
|
||||
}
|
||||
|
||||
sp->phy_rst = devm_reset_control_get(dev, "sierra_reset");
|
||||
if (IS_ERR(sp->phy_rst)) {
|
||||
dev_err(dev, "failed to get reset\n");
|
||||
return PTR_ERR(sp->phy_rst);
|
||||
}
|
||||
|
||||
sp->apb_rst = devm_reset_control_get(dev, "sierra_apb");
|
||||
if (IS_ERR(sp->apb_rst)) {
|
||||
dev_err(dev, "failed to get apb reset\n");
|
||||
return PTR_ERR(sp->apb_rst);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(sp->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Enable APB */
|
||||
reset_control_deassert(sp->apb_rst);
|
||||
|
||||
/* Check that PHY is present */
|
||||
if (sp->init_data->id_value != readl(sp->base)) {
|
||||
ret = -EINVAL;
|
||||
goto clk_disable;
|
||||
}
|
||||
|
||||
sp->autoconf = of_property_read_bool(dn, "cdns,autoconf");
|
||||
|
||||
for_each_available_child_of_node(dn, child) {
|
||||
struct phy *gphy;
|
||||
|
||||
sp->phys[node].lnk_rst =
|
||||
of_reset_control_get_exclusive_by_index(child, 0);
|
||||
|
||||
if (IS_ERR(sp->phys[node].lnk_rst)) {
|
||||
dev_err(dev, "failed to get reset %s\n",
|
||||
child->full_name);
|
||||
ret = PTR_ERR(sp->phys[node].lnk_rst);
|
||||
goto put_child2;
|
||||
}
|
||||
|
||||
if (!sp->autoconf) {
|
||||
ret = cdns_sierra_get_optional(&sp->phys[node], child);
|
||||
if (ret) {
|
||||
dev_err(dev, "missing property in node %s\n",
|
||||
child->name);
|
||||
goto put_child;
|
||||
}
|
||||
}
|
||||
|
||||
gphy = devm_phy_create(dev, child, &ops);
|
||||
|
||||
if (IS_ERR(gphy)) {
|
||||
ret = PTR_ERR(gphy);
|
||||
goto put_child;
|
||||
}
|
||||
sp->phys[node].phy = gphy;
|
||||
phy_set_drvdata(gphy, &sp->phys[node]);
|
||||
|
||||
/* Initialise the PHY registers, unless auto configured */
|
||||
if (!sp->autoconf)
|
||||
cdns_sierra_phy_init(gphy);
|
||||
|
||||
node++;
|
||||
}
|
||||
sp->nsubnodes = node;
|
||||
|
||||
/* If more than one subnode, configure the PHY as multilink */
|
||||
if (!sp->autoconf && sp->nsubnodes > 1)
|
||||
writel(2, sp->base + SIERRA_PHY_PLL_CFG);
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
reset_control_deassert(sp->phy_rst);
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
|
||||
put_child:
|
||||
node++;
|
||||
put_child2:
|
||||
for (i = 0; i < node; i++)
|
||||
reset_control_put(sp->phys[i].lnk_rst);
|
||||
of_node_put(child);
|
||||
clk_disable:
|
||||
clk_disable_unprepare(sp->clk);
|
||||
reset_control_assert(sp->apb_rst);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cdns_sierra_phy_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct cdns_sierra_phy *phy = dev_get_drvdata(pdev->dev.parent);
|
||||
int i;
|
||||
|
||||
reset_control_assert(phy->phy_rst);
|
||||
reset_control_assert(phy->apb_rst);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
/*
|
||||
* The device level resets will be put automatically.
|
||||
* Need to put the subnode resets here though.
|
||||
*/
|
||||
for (i = 0; i < phy->nsubnodes; i++) {
|
||||
reset_control_assert(phy->phys[i].lnk_rst);
|
||||
reset_control_put(phy->phys[i].lnk_rst);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct cdns_reg_pairs cdns_usb_regs[] = {
|
||||
/*
|
||||
* Write USB configuration parameters to the PHY.
|
||||
* These values are specific to this specific hardware
|
||||
* configuration.
|
||||
*/
|
||||
{0xFE0A, SIERRA_DET_STANDEC_A},
|
||||
{0x000F, SIERRA_DET_STANDEC_B},
|
||||
{0x55A5, SIERRA_DET_STANDEC_C},
|
||||
{0x69AD, SIERRA_DET_STANDEC_D},
|
||||
{0x0241, SIERRA_DET_STANDEC_E},
|
||||
{0x0110, SIERRA_PSM_LANECAL},
|
||||
{0xCF00, SIERRA_PSM_DIAG},
|
||||
{0x001F, SIERRA_PSC_TX_A0},
|
||||
{0x0007, SIERRA_PSC_TX_A1},
|
||||
{0x0003, SIERRA_PSC_TX_A2},
|
||||
{0x0003, SIERRA_PSC_TX_A3},
|
||||
{0x0FFF, SIERRA_PSC_RX_A0},
|
||||
{0x0003, SIERRA_PSC_RX_A1},
|
||||
{0x0003, SIERRA_PSC_RX_A2},
|
||||
{0x0001, SIERRA_PSC_RX_A3},
|
||||
{0x0001, SIERRA_PLLCTRL_SUBRATE},
|
||||
{0x0406, SIERRA_PLLCTRL_GEN_D},
|
||||
{0x0000, SIERRA_DRVCTRL_ATTEN},
|
||||
{0x823E, SIERRA_CLKPATHCTRL_TMR},
|
||||
{0x078F, SIERRA_RX_CREQ_FLTR_A_MODE1},
|
||||
{0x078F, SIERRA_RX_CREQ_FLTR_A_MODE0},
|
||||
{0x7B3C, SIERRA_CREQ_CCLKDET_MODE01},
|
||||
{0x023C, SIERRA_RX_CTLE_MAINTENANCE},
|
||||
{0x3232, SIERRA_CREQ_FSMCLK_SEL},
|
||||
{0x8452, SIERRA_CTLELUT_CTRL},
|
||||
{0x4121, SIERRA_DFE_ECMP_RATESEL},
|
||||
{0x4121, SIERRA_DFE_SMP_RATESEL},
|
||||
{0x9999, SIERRA_DEQ_VGATUNE_CTRL},
|
||||
{0x0330, SIERRA_TMRVAL_MODE0},
|
||||
{0x01FF, SIERRA_PICNT_MODE1},
|
||||
{0x0009, SIERRA_CPI_OUTBUF_RATESEL},
|
||||
{0x000F, SIERRA_LFPSFILT_NS},
|
||||
{0x0009, SIERRA_LFPSFILT_RD},
|
||||
{0x0001, SIERRA_LFPSFILT_MP},
|
||||
{0x8013, SIERRA_SDFILT_H2L_A},
|
||||
{0x0400, SIERRA_TMRVAL_MODE1},
|
||||
};
|
||||
|
||||
static struct cdns_reg_pairs cdns_pcie_regs[] = {
|
||||
/*
|
||||
* Write PCIe configuration parameters to the PHY.
|
||||
* These values are specific to this specific hardware
|
||||
* configuration.
|
||||
*/
|
||||
{0x891f, SIERRA_DET_STANDEC_D},
|
||||
{0x0053, SIERRA_DET_STANDEC_E},
|
||||
{0x0400, SIERRA_TMRVAL_MODE2},
|
||||
{0x0200, SIERRA_TMRVAL_MODE3},
|
||||
};
|
||||
|
||||
static const struct cdns_sierra_data cdns_map_sierra = {
|
||||
SIERRA_MACRO_ID,
|
||||
ARRAY_SIZE(cdns_pcie_regs),
|
||||
ARRAY_SIZE(cdns_usb_regs),
|
||||
cdns_pcie_regs,
|
||||
cdns_usb_regs
|
||||
};
|
||||
|
||||
static const struct of_device_id cdns_sierra_id_table[] = {
|
||||
{
|
||||
.compatible = "cdns,sierra-phy-t0",
|
||||
.data = &cdns_map_sierra,
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, cdns_sierra_id_table);
|
||||
|
||||
static struct platform_driver cdns_sierra_driver = {
|
||||
.probe = cdns_sierra_phy_probe,
|
||||
.remove = cdns_sierra_phy_remove,
|
||||
.driver = {
|
||||
.name = "cdns-sierra-phy",
|
||||
.of_match_table = cdns_sierra_id_table,
|
||||
},
|
||||
};
|
||||
module_platform_driver(cdns_sierra_driver);
|
||||
|
||||
MODULE_ALIAS("platform:cdns_sierra");
|
||||
MODULE_AUTHOR("Cadence Design Systems");
|
||||
MODULE_DESCRIPTION("CDNS sierra phy driver");
|
||||
MODULE_LICENSE("GPL v2");
|
5
drivers/phy/freescale/Kconfig
Normal file
5
drivers/phy/freescale/Kconfig
Normal file
@ -0,0 +1,5 @@
|
||||
config PHY_FSL_IMX8MQ_USB
|
||||
tristate "Freescale i.MX8M USB3 PHY"
|
||||
depends on OF && HAS_IOMEM
|
||||
select GENERIC_PHY
|
||||
default SOC_IMX8MQ
|
1
drivers/phy/freescale/Makefile
Normal file
1
drivers/phy/freescale/Makefile
Normal file
@ -0,0 +1 @@
|
||||
obj-$(CONFIG_PHY_FSL_IMX8MQ_USB) += phy-fsl-imx8mq-usb.o
|
127
drivers/phy/freescale/phy-fsl-imx8mq-usb.c
Normal file
127
drivers/phy/freescale/phy-fsl-imx8mq-usb.c
Normal file
@ -0,0 +1,127 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* Copyright (c) 2017 NXP. */
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define PHY_CTRL0 0x0
|
||||
#define PHY_CTRL0_REF_SSP_EN BIT(2)
|
||||
|
||||
#define PHY_CTRL1 0x4
|
||||
#define PHY_CTRL1_RESET BIT(0)
|
||||
#define PHY_CTRL1_COMMONONN BIT(1)
|
||||
#define PHY_CTRL1_ATERESET BIT(3)
|
||||
#define PHY_CTRL1_VDATSRCENB0 BIT(19)
|
||||
#define PHY_CTRL1_VDATDETENB0 BIT(20)
|
||||
|
||||
#define PHY_CTRL2 0x8
|
||||
#define PHY_CTRL2_TXENABLEN0 BIT(8)
|
||||
|
||||
struct imx8mq_usb_phy {
|
||||
struct phy *phy;
|
||||
struct clk *clk;
|
||||
void __iomem *base;
|
||||
};
|
||||
|
||||
static int imx8mq_usb_phy_init(struct phy *phy)
|
||||
{
|
||||
struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy);
|
||||
u32 value;
|
||||
|
||||
value = readl(imx_phy->base + PHY_CTRL1);
|
||||
value &= ~(PHY_CTRL1_VDATSRCENB0 | PHY_CTRL1_VDATDETENB0 |
|
||||
PHY_CTRL1_COMMONONN);
|
||||
value |= PHY_CTRL1_RESET | PHY_CTRL1_ATERESET;
|
||||
writel(value, imx_phy->base + PHY_CTRL1);
|
||||
|
||||
value = readl(imx_phy->base + PHY_CTRL0);
|
||||
value |= PHY_CTRL0_REF_SSP_EN;
|
||||
writel(value, imx_phy->base + PHY_CTRL0);
|
||||
|
||||
value = readl(imx_phy->base + PHY_CTRL2);
|
||||
value |= PHY_CTRL2_TXENABLEN0;
|
||||
writel(value, imx_phy->base + PHY_CTRL2);
|
||||
|
||||
value = readl(imx_phy->base + PHY_CTRL1);
|
||||
value &= ~(PHY_CTRL1_RESET | PHY_CTRL1_ATERESET);
|
||||
writel(value, imx_phy->base + PHY_CTRL1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx8mq_phy_power_on(struct phy *phy)
|
||||
{
|
||||
struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy);
|
||||
|
||||
return clk_prepare_enable(imx_phy->clk);
|
||||
}
|
||||
|
||||
static int imx8mq_phy_power_off(struct phy *phy)
|
||||
{
|
||||
struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy);
|
||||
|
||||
clk_disable_unprepare(imx_phy->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct phy_ops imx8mq_usb_phy_ops = {
|
||||
.init = imx8mq_usb_phy_init,
|
||||
.power_on = imx8mq_phy_power_on,
|
||||
.power_off = imx8mq_phy_power_off,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int imx8mq_usb_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct phy_provider *phy_provider;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct imx8mq_usb_phy *imx_phy;
|
||||
struct resource *res;
|
||||
|
||||
imx_phy = devm_kzalloc(dev, sizeof(*imx_phy), GFP_KERNEL);
|
||||
if (!imx_phy)
|
||||
return -ENOMEM;
|
||||
|
||||
imx_phy->clk = devm_clk_get(dev, "phy");
|
||||
if (IS_ERR(imx_phy->clk)) {
|
||||
dev_err(dev, "failed to get imx8mq usb phy clock\n");
|
||||
return PTR_ERR(imx_phy->clk);
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
imx_phy->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(imx_phy->base))
|
||||
return PTR_ERR(imx_phy->base);
|
||||
|
||||
imx_phy->phy = devm_phy_create(dev, NULL, &imx8mq_usb_phy_ops);
|
||||
if (IS_ERR(imx_phy->phy))
|
||||
return PTR_ERR(imx_phy->phy);
|
||||
|
||||
phy_set_drvdata(imx_phy->phy, imx_phy);
|
||||
|
||||
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
}
|
||||
|
||||
static const struct of_device_id imx8mq_usb_phy_of_match[] = {
|
||||
{.compatible = "fsl,imx8mq-usb-phy",},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx8mq_usb_phy_of_match);
|
||||
|
||||
static struct platform_driver imx8mq_usb_phy_driver = {
|
||||
.probe = imx8mq_usb_phy_probe,
|
||||
.driver = {
|
||||
.name = "imx8mq-usb-phy",
|
||||
.of_match_table = imx8mq_usb_phy_of_match,
|
||||
}
|
||||
};
|
||||
module_platform_driver(imx8mq_usb_phy_driver);
|
||||
|
||||
MODULE_DESCRIPTION("FSL IMX8MQ USB PHY driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -9,6 +9,7 @@
|
||||
#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>
|
||||
#include <linux/regmap.h>
|
||||
@ -114,43 +115,45 @@
|
||||
#define MVEBU_COMPHY_LANES 6
|
||||
#define MVEBU_COMPHY_PORTS 3
|
||||
|
||||
struct mvebu_comhy_conf {
|
||||
struct mvebu_comphy_conf {
|
||||
enum phy_mode mode;
|
||||
int submode;
|
||||
unsigned lane;
|
||||
unsigned port;
|
||||
u32 mux;
|
||||
};
|
||||
|
||||
#define MVEBU_COMPHY_CONF(_lane, _port, _mode, _mux) \
|
||||
#define MVEBU_COMPHY_CONF(_lane, _port, _submode, _mux) \
|
||||
{ \
|
||||
.lane = _lane, \
|
||||
.port = _port, \
|
||||
.mode = _mode, \
|
||||
.mode = PHY_MODE_ETHERNET, \
|
||||
.submode = _submode, \
|
||||
.mux = _mux, \
|
||||
}
|
||||
|
||||
static const struct mvebu_comhy_conf mvebu_comphy_cp110_modes[] = {
|
||||
static const struct mvebu_comphy_conf mvebu_comphy_cp110_modes[] = {
|
||||
/* lane 0 */
|
||||
MVEBU_COMPHY_CONF(0, 1, PHY_MODE_SGMII, 0x1),
|
||||
MVEBU_COMPHY_CONF(0, 1, PHY_MODE_2500SGMII, 0x1),
|
||||
MVEBU_COMPHY_CONF(0, 1, PHY_INTERFACE_MODE_SGMII, 0x1),
|
||||
MVEBU_COMPHY_CONF(0, 1, PHY_INTERFACE_MODE_2500BASEX, 0x1),
|
||||
/* lane 1 */
|
||||
MVEBU_COMPHY_CONF(1, 2, PHY_MODE_SGMII, 0x1),
|
||||
MVEBU_COMPHY_CONF(1, 2, PHY_MODE_2500SGMII, 0x1),
|
||||
MVEBU_COMPHY_CONF(1, 2, PHY_INTERFACE_MODE_SGMII, 0x1),
|
||||
MVEBU_COMPHY_CONF(1, 2, PHY_INTERFACE_MODE_2500BASEX, 0x1),
|
||||
/* lane 2 */
|
||||
MVEBU_COMPHY_CONF(2, 0, PHY_MODE_SGMII, 0x1),
|
||||
MVEBU_COMPHY_CONF(2, 0, PHY_MODE_2500SGMII, 0x1),
|
||||
MVEBU_COMPHY_CONF(2, 0, PHY_MODE_10GKR, 0x1),
|
||||
MVEBU_COMPHY_CONF(2, 0, PHY_INTERFACE_MODE_SGMII, 0x1),
|
||||
MVEBU_COMPHY_CONF(2, 0, PHY_INTERFACE_MODE_2500BASEX, 0x1),
|
||||
MVEBU_COMPHY_CONF(2, 0, PHY_INTERFACE_MODE_10GKR, 0x1),
|
||||
/* lane 3 */
|
||||
MVEBU_COMPHY_CONF(3, 1, PHY_MODE_SGMII, 0x2),
|
||||
MVEBU_COMPHY_CONF(3, 1, PHY_MODE_2500SGMII, 0x2),
|
||||
MVEBU_COMPHY_CONF(3, 1, PHY_INTERFACE_MODE_SGMII, 0x2),
|
||||
MVEBU_COMPHY_CONF(3, 1, PHY_INTERFACE_MODE_2500BASEX, 0x2),
|
||||
/* lane 4 */
|
||||
MVEBU_COMPHY_CONF(4, 0, PHY_MODE_SGMII, 0x2),
|
||||
MVEBU_COMPHY_CONF(4, 0, PHY_MODE_2500SGMII, 0x2),
|
||||
MVEBU_COMPHY_CONF(4, 0, PHY_MODE_10GKR, 0x2),
|
||||
MVEBU_COMPHY_CONF(4, 1, PHY_MODE_SGMII, 0x1),
|
||||
MVEBU_COMPHY_CONF(4, 0, PHY_INTERFACE_MODE_SGMII, 0x2),
|
||||
MVEBU_COMPHY_CONF(4, 0, PHY_INTERFACE_MODE_2500BASEX, 0x2),
|
||||
MVEBU_COMPHY_CONF(4, 0, PHY_INTERFACE_MODE_10GKR, 0x2),
|
||||
MVEBU_COMPHY_CONF(4, 1, PHY_INTERFACE_MODE_SGMII, 0x1),
|
||||
/* lane 5 */
|
||||
MVEBU_COMPHY_CONF(5, 2, PHY_MODE_SGMII, 0x1),
|
||||
MVEBU_COMPHY_CONF(5, 2, PHY_MODE_2500SGMII, 0x1),
|
||||
MVEBU_COMPHY_CONF(5, 2, PHY_INTERFACE_MODE_SGMII, 0x1),
|
||||
MVEBU_COMPHY_CONF(5, 2, PHY_INTERFACE_MODE_2500BASEX, 0x1),
|
||||
};
|
||||
|
||||
struct mvebu_comphy_priv {
|
||||
@ -163,10 +166,12 @@ struct mvebu_comphy_lane {
|
||||
struct mvebu_comphy_priv *priv;
|
||||
unsigned id;
|
||||
enum phy_mode mode;
|
||||
int submode;
|
||||
int port;
|
||||
};
|
||||
|
||||
static int mvebu_comphy_get_mux(int lane, int port, enum phy_mode mode)
|
||||
static int mvebu_comphy_get_mux(int lane, int port,
|
||||
enum phy_mode mode, int submode)
|
||||
{
|
||||
int i, n = ARRAY_SIZE(mvebu_comphy_cp110_modes);
|
||||
|
||||
@ -177,7 +182,8 @@ static int mvebu_comphy_get_mux(int lane, int port, enum phy_mode mode)
|
||||
for (i = 0; i < n; i++) {
|
||||
if (mvebu_comphy_cp110_modes[i].lane == lane &&
|
||||
mvebu_comphy_cp110_modes[i].port == port &&
|
||||
mvebu_comphy_cp110_modes[i].mode == mode)
|
||||
mvebu_comphy_cp110_modes[i].mode == mode &&
|
||||
mvebu_comphy_cp110_modes[i].submode == submode)
|
||||
break;
|
||||
}
|
||||
|
||||
@ -187,8 +193,7 @@ static int mvebu_comphy_get_mux(int lane, int port, enum phy_mode mode)
|
||||
return mvebu_comphy_cp110_modes[i].mux;
|
||||
}
|
||||
|
||||
static void mvebu_comphy_ethernet_init_reset(struct mvebu_comphy_lane *lane,
|
||||
enum phy_mode mode)
|
||||
static void mvebu_comphy_ethernet_init_reset(struct mvebu_comphy_lane *lane)
|
||||
{
|
||||
struct mvebu_comphy_priv *priv = lane->priv;
|
||||
u32 val;
|
||||
@ -206,14 +211,14 @@ static void mvebu_comphy_ethernet_init_reset(struct mvebu_comphy_lane *lane,
|
||||
MVEBU_COMPHY_SERDES_CFG0_HALF_BUS |
|
||||
MVEBU_COMPHY_SERDES_CFG0_GEN_RX(0xf) |
|
||||
MVEBU_COMPHY_SERDES_CFG0_GEN_TX(0xf));
|
||||
if (mode == PHY_MODE_10GKR)
|
||||
if (lane->submode == PHY_INTERFACE_MODE_10GKR)
|
||||
val |= MVEBU_COMPHY_SERDES_CFG0_GEN_RX(0xe) |
|
||||
MVEBU_COMPHY_SERDES_CFG0_GEN_TX(0xe);
|
||||
else if (mode == PHY_MODE_2500SGMII)
|
||||
else if (lane->submode == PHY_INTERFACE_MODE_2500BASEX)
|
||||
val |= MVEBU_COMPHY_SERDES_CFG0_GEN_RX(0x8) |
|
||||
MVEBU_COMPHY_SERDES_CFG0_GEN_TX(0x8) |
|
||||
MVEBU_COMPHY_SERDES_CFG0_HALF_BUS;
|
||||
else if (mode == PHY_MODE_SGMII)
|
||||
else if (lane->submode == PHY_INTERFACE_MODE_SGMII)
|
||||
val |= MVEBU_COMPHY_SERDES_CFG0_GEN_RX(0x6) |
|
||||
MVEBU_COMPHY_SERDES_CFG0_GEN_TX(0x6) |
|
||||
MVEBU_COMPHY_SERDES_CFG0_HALF_BUS;
|
||||
@ -243,7 +248,7 @@ static void mvebu_comphy_ethernet_init_reset(struct mvebu_comphy_lane *lane,
|
||||
/* refclk selection */
|
||||
val = readl(priv->base + MVEBU_COMPHY_MISC_CTRL0(lane->id));
|
||||
val &= ~MVEBU_COMPHY_MISC_CTRL0_REFCLK_SEL;
|
||||
if (mode == PHY_MODE_10GKR)
|
||||
if (lane->submode == PHY_INTERFACE_MODE_10GKR)
|
||||
val |= MVEBU_COMPHY_MISC_CTRL0_ICP_FORCE;
|
||||
writel(val, priv->base + MVEBU_COMPHY_MISC_CTRL0(lane->id));
|
||||
|
||||
@ -261,8 +266,7 @@ static void mvebu_comphy_ethernet_init_reset(struct mvebu_comphy_lane *lane,
|
||||
writel(val, priv->base + MVEBU_COMPHY_LOOPBACK(lane->id));
|
||||
}
|
||||
|
||||
static int mvebu_comphy_init_plls(struct mvebu_comphy_lane *lane,
|
||||
enum phy_mode mode)
|
||||
static int mvebu_comphy_init_plls(struct mvebu_comphy_lane *lane)
|
||||
{
|
||||
struct mvebu_comphy_priv *priv = lane->priv;
|
||||
u32 val;
|
||||
@ -303,13 +307,13 @@ static int mvebu_comphy_init_plls(struct mvebu_comphy_lane *lane,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mvebu_comphy_set_mode_sgmii(struct phy *phy, enum phy_mode mode)
|
||||
static int mvebu_comphy_set_mode_sgmii(struct phy *phy)
|
||||
{
|
||||
struct mvebu_comphy_lane *lane = phy_get_drvdata(phy);
|
||||
struct mvebu_comphy_priv *priv = lane->priv;
|
||||
u32 val;
|
||||
|
||||
mvebu_comphy_ethernet_init_reset(lane, mode);
|
||||
mvebu_comphy_ethernet_init_reset(lane);
|
||||
|
||||
val = readl(priv->base + MVEBU_COMPHY_RX_CTRL1(lane->id));
|
||||
val &= ~MVEBU_COMPHY_RX_CTRL1_CLK8T_EN;
|
||||
@ -330,7 +334,7 @@ static int mvebu_comphy_set_mode_sgmii(struct phy *phy, enum phy_mode mode)
|
||||
val |= MVEBU_COMPHY_GEN1_S0_TX_EMPH(0x1);
|
||||
writel(val, priv->base + MVEBU_COMPHY_GEN1_S0(lane->id));
|
||||
|
||||
return mvebu_comphy_init_plls(lane, PHY_MODE_SGMII);
|
||||
return mvebu_comphy_init_plls(lane);
|
||||
}
|
||||
|
||||
static int mvebu_comphy_set_mode_10gkr(struct phy *phy)
|
||||
@ -339,7 +343,7 @@ static int mvebu_comphy_set_mode_10gkr(struct phy *phy)
|
||||
struct mvebu_comphy_priv *priv = lane->priv;
|
||||
u32 val;
|
||||
|
||||
mvebu_comphy_ethernet_init_reset(lane, PHY_MODE_10GKR);
|
||||
mvebu_comphy_ethernet_init_reset(lane);
|
||||
|
||||
val = readl(priv->base + MVEBU_COMPHY_RX_CTRL1(lane->id));
|
||||
val |= MVEBU_COMPHY_RX_CTRL1_RXCLK2X_SEL |
|
||||
@ -469,7 +473,7 @@ static int mvebu_comphy_set_mode_10gkr(struct phy *phy)
|
||||
val |= MVEBU_COMPHY_EXT_SELV_RX_SAMPL(0x1a);
|
||||
writel(val, priv->base + MVEBU_COMPHY_EXT_SELV(lane->id));
|
||||
|
||||
return mvebu_comphy_init_plls(lane, PHY_MODE_10GKR);
|
||||
return mvebu_comphy_init_plls(lane);
|
||||
}
|
||||
|
||||
static int mvebu_comphy_power_on(struct phy *phy)
|
||||
@ -479,7 +483,8 @@ static int mvebu_comphy_power_on(struct phy *phy)
|
||||
int ret, mux;
|
||||
u32 val;
|
||||
|
||||
mux = mvebu_comphy_get_mux(lane->id, lane->port, lane->mode);
|
||||
mux = mvebu_comphy_get_mux(lane->id, lane->port,
|
||||
lane->mode, lane->submode);
|
||||
if (mux < 0)
|
||||
return -ENOTSUPP;
|
||||
|
||||
@ -492,12 +497,12 @@ static int mvebu_comphy_power_on(struct phy *phy)
|
||||
val |= mux << MVEBU_COMPHY_SELECTOR_PHY(lane->id);
|
||||
regmap_write(priv->regmap, MVEBU_COMPHY_SELECTOR, val);
|
||||
|
||||
switch (lane->mode) {
|
||||
case PHY_MODE_SGMII:
|
||||
case PHY_MODE_2500SGMII:
|
||||
ret = mvebu_comphy_set_mode_sgmii(phy, lane->mode);
|
||||
switch (lane->submode) {
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
case PHY_INTERFACE_MODE_2500BASEX:
|
||||
ret = mvebu_comphy_set_mode_sgmii(phy);
|
||||
break;
|
||||
case PHY_MODE_10GKR:
|
||||
case PHY_INTERFACE_MODE_10GKR:
|
||||
ret = mvebu_comphy_set_mode_10gkr(phy);
|
||||
break;
|
||||
default:
|
||||
@ -512,14 +517,22 @@ static int mvebu_comphy_power_on(struct phy *phy)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mvebu_comphy_set_mode(struct phy *phy, enum phy_mode mode)
|
||||
static int mvebu_comphy_set_mode(struct phy *phy,
|
||||
enum phy_mode mode, int submode)
|
||||
{
|
||||
struct mvebu_comphy_lane *lane = phy_get_drvdata(phy);
|
||||
|
||||
if (mvebu_comphy_get_mux(lane->id, lane->port, mode) < 0)
|
||||
if (mode != PHY_MODE_ETHERNET)
|
||||
return -EINVAL;
|
||||
|
||||
if (submode == PHY_INTERFACE_MODE_1000BASEX)
|
||||
submode = PHY_INTERFACE_MODE_SGMII;
|
||||
|
||||
if (mvebu_comphy_get_mux(lane->id, lane->port, mode, submode) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
lane->mode = mode;
|
||||
lane->submode = submode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -971,7 +971,7 @@ static int mtk_phy_exit(struct phy *phy)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_phy_set_mode(struct phy *phy, enum phy_mode mode)
|
||||
static int mtk_phy_set_mode(struct phy *phy, enum phy_mode mode, int submode)
|
||||
{
|
||||
struct mtk_phy_instance *instance = phy_get_drvdata(phy);
|
||||
struct mtk_tphy *tphy = dev_get_drvdata(phy->dev.parent);
|
||||
|
@ -426,7 +426,7 @@ static int mtk_phy_exit(struct phy *phy)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_phy_set_mode(struct phy *phy, enum phy_mode mode)
|
||||
static int mtk_phy_set_mode(struct phy *phy, enum phy_mode mode, int submode)
|
||||
{
|
||||
struct xsphy_instance *inst = phy_get_drvdata(phy);
|
||||
struct mtk_xsphy *xsphy = dev_get_drvdata(phy->dev.parent);
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
|
||||
#define PHY_MDM6600_PHY_DELAY_MS 4000 /* PHY enable 2.2s to 3.5s */
|
||||
#define PHY_MDM6600_ENABLED_DELAY_MS 8000 /* 8s more total for MDM6600 */
|
||||
@ -120,12 +121,22 @@ static int phy_mdm6600_power_on(struct phy *x)
|
||||
{
|
||||
struct phy_mdm6600 *ddata = phy_get_drvdata(x);
|
||||
struct gpio_desc *enable_gpio = ddata->ctrl_gpios[PHY_MDM6600_ENABLE];
|
||||
int error;
|
||||
|
||||
if (!ddata->enabled)
|
||||
return -ENODEV;
|
||||
|
||||
error = pinctrl_pm_select_default_state(ddata->dev);
|
||||
if (error)
|
||||
dev_warn(ddata->dev, "%s: error with default_state: %i\n",
|
||||
__func__, error);
|
||||
|
||||
gpiod_set_value_cansleep(enable_gpio, 1);
|
||||
|
||||
/* Allow aggressive PM for USB, it's only needed for n_gsm port */
|
||||
if (pm_runtime_enabled(&x->dev))
|
||||
phy_pm_runtime_put(x);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -133,12 +144,26 @@ static int phy_mdm6600_power_off(struct phy *x)
|
||||
{
|
||||
struct phy_mdm6600 *ddata = phy_get_drvdata(x);
|
||||
struct gpio_desc *enable_gpio = ddata->ctrl_gpios[PHY_MDM6600_ENABLE];
|
||||
int error;
|
||||
|
||||
if (!ddata->enabled)
|
||||
return -ENODEV;
|
||||
|
||||
/* Paired with phy_pm_runtime_put() in phy_mdm6600_power_on() */
|
||||
if (pm_runtime_enabled(&x->dev)) {
|
||||
error = phy_pm_runtime_get(x);
|
||||
if (error < 0 && error != -EINPROGRESS)
|
||||
dev_warn(ddata->dev, "%s: phy_pm_runtime_get: %i\n",
|
||||
__func__, error);
|
||||
}
|
||||
|
||||
gpiod_set_value_cansleep(enable_gpio, 0);
|
||||
|
||||
error = pinctrl_pm_select_sleep_state(ddata->dev);
|
||||
if (error)
|
||||
dev_warn(ddata->dev, "%s: error with sleep_state: %i\n",
|
||||
__func__, error);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -529,28 +554,17 @@ static int phy_mdm6600_probe(struct platform_device *pdev)
|
||||
ddata->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, ddata);
|
||||
|
||||
/* Active state selected in phy_mdm6600_power_on() */
|
||||
error = pinctrl_pm_select_sleep_state(ddata->dev);
|
||||
if (error)
|
||||
dev_warn(ddata->dev, "%s: error with sleep_state: %i\n",
|
||||
__func__, error);
|
||||
|
||||
error = phy_mdm6600_init_lines(ddata);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
phy_mdm6600_init_irq(ddata);
|
||||
|
||||
ddata->generic_phy = devm_phy_create(ddata->dev, NULL, &gpio_usb_ops);
|
||||
if (IS_ERR(ddata->generic_phy)) {
|
||||
error = PTR_ERR(ddata->generic_phy);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
phy_set_drvdata(ddata->generic_phy, ddata);
|
||||
|
||||
ddata->phy_provider =
|
||||
devm_of_phy_provider_register(ddata->dev,
|
||||
of_phy_simple_xlate);
|
||||
if (IS_ERR(ddata->phy_provider)) {
|
||||
error = PTR_ERR(ddata->phy_provider);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
schedule_delayed_work(&ddata->bootup_work, 0);
|
||||
|
||||
/*
|
||||
@ -574,14 +588,31 @@ static int phy_mdm6600_probe(struct platform_device *pdev)
|
||||
if (error < 0) {
|
||||
dev_warn(ddata->dev, "failed to wake modem: %i\n", error);
|
||||
pm_runtime_put_noidle(ddata->dev);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ddata->generic_phy = devm_phy_create(ddata->dev, NULL, &gpio_usb_ops);
|
||||
if (IS_ERR(ddata->generic_phy)) {
|
||||
error = PTR_ERR(ddata->generic_phy);
|
||||
goto idle;
|
||||
}
|
||||
|
||||
phy_set_drvdata(ddata->generic_phy, ddata);
|
||||
|
||||
ddata->phy_provider =
|
||||
devm_of_phy_provider_register(ddata->dev,
|
||||
of_phy_simple_xlate);
|
||||
if (IS_ERR(ddata->phy_provider))
|
||||
error = PTR_ERR(ddata->phy_provider);
|
||||
|
||||
idle:
|
||||
pm_runtime_mark_last_busy(ddata->dev);
|
||||
pm_runtime_put_autosuspend(ddata->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
cleanup:
|
||||
phy_mdm6600_device_power_off(ddata);
|
||||
if (error < 0)
|
||||
phy_mdm6600_device_power_off(ddata);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
@ -104,20 +105,24 @@ struct serdes_mux {
|
||||
u8 idx;
|
||||
u8 port;
|
||||
enum phy_mode mode;
|
||||
int submode;
|
||||
u32 mask;
|
||||
u32 mux;
|
||||
};
|
||||
|
||||
#define SERDES_MUX(_idx, _port, _mode, _mask, _mux) { \
|
||||
#define SERDES_MUX(_idx, _port, _mode, _submode, _mask, _mux) { \
|
||||
.idx = _idx, \
|
||||
.port = _port, \
|
||||
.mode = _mode, \
|
||||
.submode = _submode, \
|
||||
.mask = _mask, \
|
||||
.mux = _mux, \
|
||||
}
|
||||
|
||||
#define SERDES_MUX_SGMII(i, p, m, c) SERDES_MUX(i, p, PHY_MODE_SGMII, m, c)
|
||||
#define SERDES_MUX_QSGMII(i, p, m, c) SERDES_MUX(i, p, PHY_MODE_QSGMII, m, c)
|
||||
#define SERDES_MUX_SGMII(i, p, m, c) \
|
||||
SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_SGMII, m, c)
|
||||
#define SERDES_MUX_QSGMII(i, p, m, c) \
|
||||
SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_QSGMII, m, c)
|
||||
|
||||
static const struct serdes_mux ocelot_serdes_muxes[] = {
|
||||
SERDES_MUX_SGMII(SERDES1G(0), 0, 0, 0),
|
||||
@ -154,22 +159,27 @@ static const struct serdes_mux ocelot_serdes_muxes[] = {
|
||||
SERDES_MUX_SGMII(SERDES6G(1), 8, 0, 0),
|
||||
SERDES_MUX_SGMII(SERDES6G(2), 10, HSIO_HW_CFG_PCIE_ENA |
|
||||
HSIO_HW_CFG_DEV2G5_10_MODE, 0),
|
||||
SERDES_MUX(SERDES6G(2), 10, PHY_MODE_PCIE, HSIO_HW_CFG_PCIE_ENA,
|
||||
SERDES_MUX(SERDES6G(2), 10, PHY_MODE_PCIE, 0, HSIO_HW_CFG_PCIE_ENA,
|
||||
HSIO_HW_CFG_PCIE_ENA),
|
||||
};
|
||||
|
||||
static int serdes_set_mode(struct phy *phy, enum phy_mode mode)
|
||||
static int serdes_set_mode(struct phy *phy, enum phy_mode mode, int submode)
|
||||
{
|
||||
struct serdes_macro *macro = phy_get_drvdata(phy);
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
/* As of now only PHY_MODE_ETHERNET is supported */
|
||||
if (mode != PHY_MODE_ETHERNET)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ocelot_serdes_muxes); i++) {
|
||||
if (macro->idx != ocelot_serdes_muxes[i].idx ||
|
||||
mode != ocelot_serdes_muxes[i].mode)
|
||||
mode != ocelot_serdes_muxes[i].mode ||
|
||||
submode != ocelot_serdes_muxes[i].submode)
|
||||
continue;
|
||||
|
||||
if (mode != PHY_MODE_QSGMII &&
|
||||
if (submode != PHY_INTERFACE_MODE_QSGMII &&
|
||||
macro->port != ocelot_serdes_muxes[i].port)
|
||||
continue;
|
||||
|
||||
|
166
drivers/phy/phy-core-mipi-dphy.c
Normal file
166
drivers/phy/phy-core-mipi-dphy.c
Normal file
@ -0,0 +1,166 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2013 NVIDIA Corporation
|
||||
* Copyright (C) 2018 Cadence Design Systems Inc.
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/time64.h>
|
||||
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/phy/phy-mipi-dphy.h>
|
||||
|
||||
#define PSEC_PER_SEC 1000000000000LL
|
||||
|
||||
/*
|
||||
* Minimum D-PHY timings based on MIPI D-PHY specification. Derived
|
||||
* from the valid ranges specified in Section 6.9, Table 14, Page 41
|
||||
* of the D-PHY specification (v2.1).
|
||||
*/
|
||||
int phy_mipi_dphy_get_default_config(unsigned long pixel_clock,
|
||||
unsigned int bpp,
|
||||
unsigned int lanes,
|
||||
struct phy_configure_opts_mipi_dphy *cfg)
|
||||
{
|
||||
unsigned long long hs_clk_rate;
|
||||
unsigned long long ui;
|
||||
|
||||
if (!cfg)
|
||||
return -EINVAL;
|
||||
|
||||
hs_clk_rate = pixel_clock * bpp;
|
||||
do_div(hs_clk_rate, lanes);
|
||||
|
||||
ui = ALIGN(PSEC_PER_SEC, hs_clk_rate);
|
||||
do_div(ui, hs_clk_rate);
|
||||
|
||||
cfg->clk_miss = 0;
|
||||
cfg->clk_post = 60000 + 52 * ui;
|
||||
cfg->clk_pre = 8000;
|
||||
cfg->clk_prepare = 38000;
|
||||
cfg->clk_settle = 95000;
|
||||
cfg->clk_term_en = 0;
|
||||
cfg->clk_trail = 60000;
|
||||
cfg->clk_zero = 262000;
|
||||
cfg->d_term_en = 0;
|
||||
cfg->eot = 0;
|
||||
cfg->hs_exit = 100000;
|
||||
cfg->hs_prepare = 40000 + 4 * ui;
|
||||
cfg->hs_zero = 105000 + 6 * ui;
|
||||
cfg->hs_settle = 85000 + 6 * ui;
|
||||
cfg->hs_skip = 40000;
|
||||
|
||||
/*
|
||||
* The MIPI D-PHY specification (Section 6.9, v1.2, Table 14, Page 40)
|
||||
* contains this formula as:
|
||||
*
|
||||
* T_HS-TRAIL = max(n * 8 * ui, 60 + n * 4 * ui)
|
||||
*
|
||||
* where n = 1 for forward-direction HS mode and n = 4 for reverse-
|
||||
* direction HS mode. There's only one setting and this function does
|
||||
* not parameterize on anything other that ui, so this code will
|
||||
* assumes that reverse-direction HS mode is supported and uses n = 4.
|
||||
*/
|
||||
cfg->hs_trail = max(4 * 8 * ui, 60000 + 4 * 4 * ui);
|
||||
|
||||
cfg->init = 100000000;
|
||||
cfg->lpx = 60000;
|
||||
cfg->ta_get = 5 * cfg->lpx;
|
||||
cfg->ta_go = 4 * cfg->lpx;
|
||||
cfg->ta_sure = 2 * cfg->lpx;
|
||||
cfg->wakeup = 1000000000;
|
||||
|
||||
cfg->hs_clk_rate = hs_clk_rate;
|
||||
cfg->lanes = lanes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(phy_mipi_dphy_get_default_config);
|
||||
|
||||
/*
|
||||
* Validate D-PHY configuration according to MIPI D-PHY specification
|
||||
* (v1.2, Section Section 6.9 "Global Operation Timing Parameters").
|
||||
*/
|
||||
int phy_mipi_dphy_config_validate(struct phy_configure_opts_mipi_dphy *cfg)
|
||||
{
|
||||
unsigned long long ui;
|
||||
|
||||
if (!cfg)
|
||||
return -EINVAL;
|
||||
|
||||
ui = ALIGN(PSEC_PER_SEC, cfg->hs_clk_rate);
|
||||
do_div(ui, cfg->hs_clk_rate);
|
||||
|
||||
if (cfg->clk_miss > 60000)
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->clk_post < (60000 + 52 * ui))
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->clk_pre < 8000)
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->clk_prepare < 38000 || cfg->clk_prepare > 95000)
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->clk_settle < 95000 || cfg->clk_settle > 300000)
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->clk_term_en > 38000)
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->clk_trail < 60000)
|
||||
return -EINVAL;
|
||||
|
||||
if ((cfg->clk_prepare + cfg->clk_zero) < 300000)
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->d_term_en > (35000 + 4 * ui))
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->eot > (105000 + 12 * ui))
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->hs_exit < 100000)
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->hs_prepare < (40000 + 4 * ui) ||
|
||||
cfg->hs_prepare > (85000 + 6 * ui))
|
||||
return -EINVAL;
|
||||
|
||||
if ((cfg->hs_prepare + cfg->hs_zero) < (145000 + 10 * ui))
|
||||
return -EINVAL;
|
||||
|
||||
if ((cfg->hs_settle < (85000 + 6 * ui)) ||
|
||||
(cfg->hs_settle > (145000 + 10 * ui)))
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->hs_skip < 40000 || cfg->hs_skip > (55000 + 4 * ui))
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->hs_trail < max(8 * ui, 60000 + 4 * ui))
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->init < 100000000)
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->lpx < 50000)
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->ta_get != (5 * cfg->lpx))
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->ta_go != (4 * cfg->lpx))
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->ta_sure < cfg->lpx || cfg->ta_sure > (2 * cfg->lpx))
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg->wakeup < 1000000000)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(phy_mipi_dphy_config_validate);
|
@ -360,7 +360,7 @@ int phy_power_off(struct phy *phy)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(phy_power_off);
|
||||
|
||||
int phy_set_mode(struct phy *phy, enum phy_mode mode)
|
||||
int phy_set_mode_ext(struct phy *phy, enum phy_mode mode, int submode)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@ -368,14 +368,14 @@ int phy_set_mode(struct phy *phy, enum phy_mode mode)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&phy->mutex);
|
||||
ret = phy->ops->set_mode(phy, mode);
|
||||
ret = phy->ops->set_mode(phy, mode, submode);
|
||||
if (!ret)
|
||||
phy->attrs.mode = mode;
|
||||
mutex_unlock(&phy->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(phy_set_mode);
|
||||
EXPORT_SYMBOL_GPL(phy_set_mode_ext);
|
||||
|
||||
int phy_reset(struct phy *phy)
|
||||
{
|
||||
@ -407,6 +407,70 @@ int phy_calibrate(struct phy *phy)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(phy_calibrate);
|
||||
|
||||
/**
|
||||
* phy_configure() - Changes the phy parameters
|
||||
* @phy: the phy returned by phy_get()
|
||||
* @opts: New configuration to apply
|
||||
*
|
||||
* Used to change the PHY parameters. phy_init() must have been called
|
||||
* on the phy. The configuration will be applied on the current phy
|
||||
* mode, that can be changed using phy_set_mode().
|
||||
*
|
||||
* Returns: 0 if successful, an negative error code otherwise
|
||||
*/
|
||||
int phy_configure(struct phy *phy, union phy_configure_opts *opts)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!phy)
|
||||
return -EINVAL;
|
||||
|
||||
if (!phy->ops->configure)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
mutex_lock(&phy->mutex);
|
||||
ret = phy->ops->configure(phy, opts);
|
||||
mutex_unlock(&phy->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(phy_configure);
|
||||
|
||||
/**
|
||||
* phy_validate() - Checks the phy parameters
|
||||
* @phy: the phy returned by phy_get()
|
||||
* @mode: phy_mode the configuration is applicable to.
|
||||
* @submode: PHY submode the configuration is applicable to.
|
||||
* @opts: Configuration to check
|
||||
*
|
||||
* Used to check that the current set of parameters can be handled by
|
||||
* the phy. Implementations are free to tune the parameters passed as
|
||||
* arguments if needed by some implementation detail or
|
||||
* constraints. It will not change any actual configuration of the
|
||||
* PHY, so calling it as many times as deemed fit will have no side
|
||||
* effect.
|
||||
*
|
||||
* Returns: 0 if successful, an negative error code otherwise
|
||||
*/
|
||||
int phy_validate(struct phy *phy, enum phy_mode mode, int submode,
|
||||
union phy_configure_opts *opts)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!phy)
|
||||
return -EINVAL;
|
||||
|
||||
if (!phy->ops->validate)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
mutex_lock(&phy->mutex);
|
||||
ret = phy->ops->validate(phy, mode, submode, opts);
|
||||
mutex_unlock(&phy->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(phy_validate);
|
||||
|
||||
/**
|
||||
* _of_phy_get() - lookup and obtain a reference to a phy by phandle
|
||||
* @np: device_node for which to get the phy
|
||||
|
@ -72,6 +72,9 @@
|
||||
|
||||
#define MAX_PROP_NAME 32
|
||||
|
||||
/* Define the assumed distance between lanes for underspecified device trees. */
|
||||
#define QMP_PHY_LEGACY_LANE_STRIDE 0x400
|
||||
|
||||
struct qmp_phy_init_tbl {
|
||||
unsigned int offset;
|
||||
unsigned int val;
|
||||
@ -733,9 +736,6 @@ struct qmp_phy_cfg {
|
||||
bool has_phy_dp_com_ctrl;
|
||||
/* true, if PHY has secondary tx/rx lanes to be configured */
|
||||
bool is_dual_lane_phy;
|
||||
/* Register offset of secondary tx/rx lanes for USB DP combo PHY */
|
||||
unsigned int tx_b_lane_offset;
|
||||
unsigned int rx_b_lane_offset;
|
||||
|
||||
/* true, if PCS block has no separate SW_RESET register */
|
||||
bool no_pcs_sw_reset;
|
||||
@ -748,6 +748,8 @@ struct qmp_phy_cfg {
|
||||
* @tx: iomapped memory space for lane's tx
|
||||
* @rx: iomapped memory space for lane's rx
|
||||
* @pcs: iomapped memory space for lane's pcs
|
||||
* @tx2: iomapped memory space for second lane's tx (in dual lane PHYs)
|
||||
* @rx2: iomapped memory space for second lane's rx (in dual lane PHYs)
|
||||
* @pcs_misc: iomapped memory space for lane's pcs_misc
|
||||
* @pipe_clk: pipe lock
|
||||
* @index: lane index
|
||||
@ -759,6 +761,8 @@ struct qmp_phy {
|
||||
void __iomem *tx;
|
||||
void __iomem *rx;
|
||||
void __iomem *pcs;
|
||||
void __iomem *tx2;
|
||||
void __iomem *rx2;
|
||||
void __iomem *pcs_misc;
|
||||
struct clk *pipe_clk;
|
||||
unsigned int index;
|
||||
@ -975,8 +979,6 @@ static const struct qmp_phy_cfg qmp_v3_usb3phy_cfg = {
|
||||
|
||||
.has_phy_dp_com_ctrl = true,
|
||||
.is_dual_lane_phy = true,
|
||||
.tx_b_lane_offset = 0x400,
|
||||
.rx_b_lane_offset = 0x400,
|
||||
};
|
||||
|
||||
static const struct qmp_phy_cfg qmp_v3_usb3_uniphy_cfg = {
|
||||
@ -1031,9 +1033,6 @@ static const struct qmp_phy_cfg sdm845_ufsphy_cfg = {
|
||||
.mask_pcs_ready = PCS_READY,
|
||||
|
||||
.is_dual_lane_phy = true,
|
||||
.tx_b_lane_offset = 0x400,
|
||||
.rx_b_lane_offset = 0x400,
|
||||
|
||||
.no_pcs_sw_reset = true,
|
||||
};
|
||||
|
||||
@ -1238,12 +1237,12 @@ static int qcom_qmp_phy_init(struct phy *phy)
|
||||
qcom_qmp_phy_configure(tx, cfg->regs, cfg->tx_tbl, cfg->tx_tbl_num);
|
||||
/* Configuration for other LANE for USB-DP combo PHY */
|
||||
if (cfg->is_dual_lane_phy)
|
||||
qcom_qmp_phy_configure(tx + cfg->tx_b_lane_offset, cfg->regs,
|
||||
qcom_qmp_phy_configure(qphy->tx2, cfg->regs,
|
||||
cfg->tx_tbl, cfg->tx_tbl_num);
|
||||
|
||||
qcom_qmp_phy_configure(rx, cfg->regs, cfg->rx_tbl, cfg->rx_tbl_num);
|
||||
if (cfg->is_dual_lane_phy)
|
||||
qcom_qmp_phy_configure(rx + cfg->rx_b_lane_offset, cfg->regs,
|
||||
qcom_qmp_phy_configure(qphy->rx2, cfg->regs,
|
||||
cfg->rx_tbl, cfg->rx_tbl_num);
|
||||
|
||||
qcom_qmp_phy_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num);
|
||||
@ -1365,7 +1364,8 @@ static int qcom_qmp_phy_poweron(struct phy *phy)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qcom_qmp_phy_set_mode(struct phy *phy, enum phy_mode mode)
|
||||
static int qcom_qmp_phy_set_mode(struct phy *phy,
|
||||
enum phy_mode mode, int submode)
|
||||
{
|
||||
struct qmp_phy *qphy = phy_get_drvdata(phy);
|
||||
struct qcom_qmp *qmp = qphy->qmp;
|
||||
@ -1542,6 +1542,11 @@ static int qcom_qmp_phy_clk_init(struct device *dev)
|
||||
return devm_clk_bulk_get(dev, num, qmp->clks);
|
||||
}
|
||||
|
||||
static void phy_pipe_clk_release_provider(void *res)
|
||||
{
|
||||
of_clk_del_provider(res);
|
||||
}
|
||||
|
||||
/*
|
||||
* Register a fixed rate pipe clock.
|
||||
*
|
||||
@ -1588,7 +1593,23 @@ static int phy_pipe_clk_register(struct qcom_qmp *qmp, struct device_node *np)
|
||||
fixed->fixed_rate = 125000000;
|
||||
fixed->hw.init = &init;
|
||||
|
||||
return devm_clk_hw_register(qmp->dev, &fixed->hw);
|
||||
ret = devm_clk_hw_register(qmp->dev, &fixed->hw);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &fixed->hw);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Roll a devm action because the clock provider is the child node, but
|
||||
* the child node is not actually a device.
|
||||
*/
|
||||
ret = devm_add_action(qmp->dev, phy_pipe_clk_release_provider, np);
|
||||
if (ret)
|
||||
phy_pipe_clk_release_provider(np);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct phy_ops qcom_qmp_phy_gen_ops = {
|
||||
@ -1614,8 +1635,9 @@ int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id)
|
||||
|
||||
/*
|
||||
* Get memory resources for each phy lane:
|
||||
* Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2; and
|
||||
* pcs_misc (optional) -> 3.
|
||||
* Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2.
|
||||
* For dual lane PHYs: tx2 -> 3, rx2 -> 4, pcs_misc (optional) -> 5
|
||||
* For single lane PHYs: pcs_misc (optional) -> 3.
|
||||
*/
|
||||
qphy->tx = of_iomap(np, 0);
|
||||
if (!qphy->tx)
|
||||
@ -1629,7 +1651,32 @@ int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id)
|
||||
if (!qphy->pcs)
|
||||
return -ENOMEM;
|
||||
|
||||
qphy->pcs_misc = of_iomap(np, 3);
|
||||
/*
|
||||
* If this is a dual-lane PHY, then there should be registers for the
|
||||
* second lane. Some old device trees did not specify this, so fall
|
||||
* back to old legacy behavior of assuming they can be reached at an
|
||||
* offset from the first lane.
|
||||
*/
|
||||
if (qmp->cfg->is_dual_lane_phy) {
|
||||
qphy->tx2 = of_iomap(np, 3);
|
||||
qphy->rx2 = of_iomap(np, 4);
|
||||
if (!qphy->tx2 || !qphy->rx2) {
|
||||
dev_warn(dev,
|
||||
"Underspecified device tree, falling back to legacy register regions\n");
|
||||
|
||||
/* In the old version, pcs_misc is at index 3. */
|
||||
qphy->pcs_misc = qphy->tx2;
|
||||
qphy->tx2 = qphy->tx + QMP_PHY_LEGACY_LANE_STRIDE;
|
||||
qphy->rx2 = qphy->rx + QMP_PHY_LEGACY_LANE_STRIDE;
|
||||
|
||||
} else {
|
||||
qphy->pcs_misc = of_iomap(np, 5);
|
||||
}
|
||||
|
||||
} else {
|
||||
qphy->pcs_misc = of_iomap(np, 3);
|
||||
}
|
||||
|
||||
if (!qphy->pcs_misc)
|
||||
dev_vdbg(dev, "PHY pcs_misc-reg not used\n");
|
||||
|
||||
|
@ -425,7 +425,8 @@ static void qusb2_phy_set_tune2_param(struct qusb2_phy *qphy)
|
||||
HSTX_TRIM_MASK);
|
||||
}
|
||||
|
||||
static int qusb2_phy_set_mode(struct phy *phy, enum phy_mode mode)
|
||||
static int qusb2_phy_set_mode(struct phy *phy,
|
||||
enum phy_mode mode, int submode)
|
||||
{
|
||||
struct qusb2_phy *qphy = phy_get_drvdata(phy);
|
||||
|
||||
|
@ -65,7 +65,8 @@ static int ufs_qcom_phy_qmp_14nm_exit(struct phy *generic_phy)
|
||||
}
|
||||
|
||||
static
|
||||
int ufs_qcom_phy_qmp_14nm_set_mode(struct phy *generic_phy, enum phy_mode mode)
|
||||
int ufs_qcom_phy_qmp_14nm_set_mode(struct phy *generic_phy,
|
||||
enum phy_mode mode, int submode)
|
||||
{
|
||||
struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
|
||||
|
||||
|
@ -84,7 +84,8 @@ static int ufs_qcom_phy_qmp_20nm_exit(struct phy *generic_phy)
|
||||
}
|
||||
|
||||
static
|
||||
int ufs_qcom_phy_qmp_20nm_set_mode(struct phy *generic_phy, enum phy_mode mode)
|
||||
int ufs_qcom_phy_qmp_20nm_set_mode(struct phy *generic_phy,
|
||||
enum phy_mode mode, int submode)
|
||||
{
|
||||
struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
|
||||
|
||||
|
@ -42,7 +42,8 @@ struct qcom_usb_hs_phy {
|
||||
struct notifier_block vbus_notify;
|
||||
};
|
||||
|
||||
static int qcom_usb_hs_phy_set_mode(struct phy *phy, enum phy_mode mode)
|
||||
static int qcom_usb_hs_phy_set_mode(struct phy *phy,
|
||||
enum phy_mode mode, int submode)
|
||||
{
|
||||
struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy);
|
||||
u8 addr;
|
||||
|
@ -307,16 +307,21 @@ static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch)
|
||||
void __iomem *usb2_base = ch->base;
|
||||
u32 val;
|
||||
|
||||
/* Should not use functions of read-modify-write a register */
|
||||
val = readl(usb2_base + USB2_LINECTRL1);
|
||||
val = (val & ~USB2_LINECTRL1_DP_RPD) | USB2_LINECTRL1_DPRPD_EN |
|
||||
USB2_LINECTRL1_DMRPD_EN | USB2_LINECTRL1_DM_RPD;
|
||||
writel(val, usb2_base + USB2_LINECTRL1);
|
||||
|
||||
val = readl(usb2_base + USB2_VBCTRL);
|
||||
writel(val | USB2_VBCTRL_DRVVBUSSEL, usb2_base + USB2_VBCTRL);
|
||||
writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTSTA);
|
||||
rcar_gen3_control_otg_irq(ch, 1);
|
||||
val = readl(usb2_base + USB2_ADPCTRL);
|
||||
writel(val | USB2_ADPCTRL_IDPULLUP, usb2_base + USB2_ADPCTRL);
|
||||
val = readl(usb2_base + USB2_LINECTRL1);
|
||||
rcar_gen3_set_linectrl(ch, 0, 0);
|
||||
writel(val | USB2_LINECTRL1_DPRPD_EN | USB2_LINECTRL1_DMRPD_EN,
|
||||
usb2_base + USB2_LINECTRL1);
|
||||
|
||||
msleep(20);
|
||||
|
||||
writel(0xffffffff, usb2_base + USB2_OBINTSTA);
|
||||
writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTEN);
|
||||
|
||||
rcar_gen3_device_recognition(ch);
|
||||
}
|
||||
|
@ -1168,8 +1168,8 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev)
|
||||
struct phy *phy;
|
||||
|
||||
/* This driver aims to support both otg-port and host-port */
|
||||
if (of_node_cmp(child_np->name, "host-port") &&
|
||||
of_node_cmp(child_np->name, "otg-port"))
|
||||
if (!of_node_name_eq(child_np, "host-port") &&
|
||||
!of_node_name_eq(child_np, "otg-port"))
|
||||
goto next_child;
|
||||
|
||||
phy = devm_phy_create(dev, child_np, &rockchip_usb2phy_ops);
|
||||
@ -1183,7 +1183,7 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev)
|
||||
phy_set_drvdata(rport->phy, rport);
|
||||
|
||||
/* initialize otg/host port separately */
|
||||
if (!of_node_cmp(child_np->name, "host-port")) {
|
||||
if (of_node_name_eq(child_np, "host-port")) {
|
||||
ret = rockchip_usb2phy_host_port_init(rphy, rport,
|
||||
child_np);
|
||||
if (ret)
|
||||
|
@ -1176,10 +1176,10 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev)
|
||||
for_each_available_child_of_node(np, child_np) {
|
||||
struct phy *phy;
|
||||
|
||||
if (!of_node_cmp(child_np->name, "dp-port"))
|
||||
if (of_node_name_eq(child_np, "dp-port"))
|
||||
phy = devm_phy_create(dev, child_np,
|
||||
&rockchip_dp_phy_ops);
|
||||
else if (!of_node_cmp(child_np->name, "usb3-port"))
|
||||
else if (of_node_name_eq(child_np, "usb3-port"))
|
||||
phy = devm_phy_create(dev, child_np,
|
||||
&rockchip_usb3_phy_ops);
|
||||
else
|
||||
|
@ -76,3 +76,13 @@ config TWL4030_USB
|
||||
family chips (including the TWL5030 and TPS659x0 devices).
|
||||
This transceiver supports high and full speed devices plus,
|
||||
in host mode, low speed.
|
||||
|
||||
config PHY_TI_GMII_SEL
|
||||
tristate
|
||||
default y if TI_CPSW=y
|
||||
depends on TI_CPSW || COMPILE_TEST
|
||||
select GENERIC_PHY
|
||||
default m
|
||||
help
|
||||
This driver supports configuring of the TI CPSW Port mode depending on
|
||||
the Ethernet PHY connected to the CPSW Port.
|
||||
|
@ -6,3 +6,4 @@ obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
|
||||
obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o
|
||||
obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o
|
||||
obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
|
||||
obj-$(CONFIG_PHY_TI_GMII_SEL) += phy-gmii-sel.o
|
||||
|
@ -93,7 +93,8 @@ static int da8xx_usb20_phy_power_off(struct phy *phy)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int da8xx_usb20_phy_set_mode(struct phy *phy, enum phy_mode mode)
|
||||
static int da8xx_usb20_phy_set_mode(struct phy *phy,
|
||||
enum phy_mode mode, int submode)
|
||||
{
|
||||
struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy);
|
||||
u32 val;
|
||||
|
349
drivers/phy/ti/phy-gmii-sel.c
Normal file
349
drivers/phy/ti/phy-gmii-sel.c
Normal file
@ -0,0 +1,349 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Texas Instruments CPSW Port's PHY Interface Mode selection Driver
|
||||
*
|
||||
* Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
|
||||
*
|
||||
* Based on cpsw-phy-sel.c driver created by Mugunthan V N <mugunthanvnm@ti.com>
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_net.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/* AM33xx SoC specific definitions for the CONTROL port */
|
||||
#define AM33XX_GMII_SEL_MODE_MII 0
|
||||
#define AM33XX_GMII_SEL_MODE_RMII 1
|
||||
#define AM33XX_GMII_SEL_MODE_RGMII 2
|
||||
|
||||
enum {
|
||||
PHY_GMII_SEL_PORT_MODE,
|
||||
PHY_GMII_SEL_RGMII_ID_MODE,
|
||||
PHY_GMII_SEL_RMII_IO_CLK_EN,
|
||||
PHY_GMII_SEL_LAST,
|
||||
};
|
||||
|
||||
struct phy_gmii_sel_phy_priv {
|
||||
struct phy_gmii_sel_priv *priv;
|
||||
u32 id;
|
||||
struct phy *if_phy;
|
||||
int rmii_clock_external;
|
||||
int phy_if_mode;
|
||||
struct regmap_field *fields[PHY_GMII_SEL_LAST];
|
||||
};
|
||||
|
||||
struct phy_gmii_sel_soc_data {
|
||||
u32 num_ports;
|
||||
u32 features;
|
||||
const struct reg_field (*regfields)[PHY_GMII_SEL_LAST];
|
||||
};
|
||||
|
||||
struct phy_gmii_sel_priv {
|
||||
struct device *dev;
|
||||
const struct phy_gmii_sel_soc_data *soc_data;
|
||||
struct regmap *regmap;
|
||||
struct phy_provider *phy_provider;
|
||||
struct phy_gmii_sel_phy_priv *if_phys;
|
||||
};
|
||||
|
||||
static int phy_gmii_sel_mode(struct phy *phy, enum phy_mode mode, int submode)
|
||||
{
|
||||
struct phy_gmii_sel_phy_priv *if_phy = phy_get_drvdata(phy);
|
||||
const struct phy_gmii_sel_soc_data *soc_data = if_phy->priv->soc_data;
|
||||
struct device *dev = if_phy->priv->dev;
|
||||
struct regmap_field *regfield;
|
||||
int ret, rgmii_id = 0;
|
||||
u32 gmii_sel_mode = 0;
|
||||
|
||||
if (mode != PHY_MODE_ETHERNET)
|
||||
return -EINVAL;
|
||||
|
||||
switch (submode) {
|
||||
case PHY_INTERFACE_MODE_RMII:
|
||||
gmii_sel_mode = AM33XX_GMII_SEL_MODE_RMII;
|
||||
break;
|
||||
|
||||
case PHY_INTERFACE_MODE_RGMII:
|
||||
gmii_sel_mode = AM33XX_GMII_SEL_MODE_RGMII;
|
||||
break;
|
||||
|
||||
case PHY_INTERFACE_MODE_RGMII_ID:
|
||||
case PHY_INTERFACE_MODE_RGMII_RXID:
|
||||
case PHY_INTERFACE_MODE_RGMII_TXID:
|
||||
gmii_sel_mode = AM33XX_GMII_SEL_MODE_RGMII;
|
||||
rgmii_id = 1;
|
||||
break;
|
||||
|
||||
case PHY_INTERFACE_MODE_MII:
|
||||
mode = AM33XX_GMII_SEL_MODE_MII;
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_warn(dev,
|
||||
"port%u: unsupported mode: \"%s\". Defaulting to MII.\n",
|
||||
if_phy->id, phy_modes(rgmii_id));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if_phy->phy_if_mode = submode;
|
||||
|
||||
dev_dbg(dev, "%s id:%u mode:%u rgmii_id:%d rmii_clk_ext:%d\n",
|
||||
__func__, if_phy->id, mode, rgmii_id,
|
||||
if_phy->rmii_clock_external);
|
||||
|
||||
regfield = if_phy->fields[PHY_GMII_SEL_PORT_MODE];
|
||||
ret = regmap_field_write(regfield, gmii_sel_mode);
|
||||
if (ret) {
|
||||
dev_err(dev, "port%u: set mode fail %d", if_phy->id, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (soc_data->features & BIT(PHY_GMII_SEL_RGMII_ID_MODE) &&
|
||||
if_phy->fields[PHY_GMII_SEL_RGMII_ID_MODE]) {
|
||||
regfield = if_phy->fields[PHY_GMII_SEL_RGMII_ID_MODE];
|
||||
ret = regmap_field_write(regfield, rgmii_id);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (soc_data->features & BIT(PHY_GMII_SEL_RMII_IO_CLK_EN) &&
|
||||
if_phy->fields[PHY_GMII_SEL_RMII_IO_CLK_EN]) {
|
||||
regfield = if_phy->fields[PHY_GMII_SEL_RMII_IO_CLK_EN];
|
||||
ret = regmap_field_write(regfield,
|
||||
if_phy->rmii_clock_external);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const
|
||||
struct reg_field phy_gmii_sel_fields_am33xx[][PHY_GMII_SEL_LAST] = {
|
||||
{
|
||||
[PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x650, 0, 1),
|
||||
[PHY_GMII_SEL_RGMII_ID_MODE] = REG_FIELD(0x650, 4, 4),
|
||||
[PHY_GMII_SEL_RMII_IO_CLK_EN] = REG_FIELD(0x650, 6, 6),
|
||||
},
|
||||
{
|
||||
[PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x650, 2, 3),
|
||||
[PHY_GMII_SEL_RGMII_ID_MODE] = REG_FIELD(0x650, 5, 5),
|
||||
[PHY_GMII_SEL_RMII_IO_CLK_EN] = REG_FIELD(0x650, 7, 7),
|
||||
},
|
||||
};
|
||||
|
||||
static const
|
||||
struct phy_gmii_sel_soc_data phy_gmii_sel_soc_am33xx = {
|
||||
.num_ports = 2,
|
||||
.features = BIT(PHY_GMII_SEL_RGMII_ID_MODE) |
|
||||
BIT(PHY_GMII_SEL_RMII_IO_CLK_EN),
|
||||
.regfields = phy_gmii_sel_fields_am33xx,
|
||||
};
|
||||
|
||||
static const
|
||||
struct reg_field phy_gmii_sel_fields_dra7[][PHY_GMII_SEL_LAST] = {
|
||||
{
|
||||
[PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x554, 0, 1),
|
||||
[PHY_GMII_SEL_RGMII_ID_MODE] = REG_FIELD((~0), 0, 0),
|
||||
[PHY_GMII_SEL_RMII_IO_CLK_EN] = REG_FIELD((~0), 0, 0),
|
||||
},
|
||||
{
|
||||
[PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x554, 4, 5),
|
||||
[PHY_GMII_SEL_RGMII_ID_MODE] = REG_FIELD((~0), 0, 0),
|
||||
[PHY_GMII_SEL_RMII_IO_CLK_EN] = REG_FIELD((~0), 0, 0),
|
||||
},
|
||||
};
|
||||
|
||||
static const
|
||||
struct phy_gmii_sel_soc_data phy_gmii_sel_soc_dra7 = {
|
||||
.num_ports = 2,
|
||||
.regfields = phy_gmii_sel_fields_dra7,
|
||||
};
|
||||
|
||||
static const
|
||||
struct phy_gmii_sel_soc_data phy_gmii_sel_soc_dm814 = {
|
||||
.num_ports = 2,
|
||||
.features = BIT(PHY_GMII_SEL_RGMII_ID_MODE),
|
||||
.regfields = phy_gmii_sel_fields_am33xx,
|
||||
};
|
||||
|
||||
static const struct of_device_id phy_gmii_sel_id_table[] = {
|
||||
{
|
||||
.compatible = "ti,am3352-phy-gmii-sel",
|
||||
.data = &phy_gmii_sel_soc_am33xx,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,dra7xx-phy-gmii-sel",
|
||||
.data = &phy_gmii_sel_soc_dra7,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,am43xx-phy-gmii-sel",
|
||||
.data = &phy_gmii_sel_soc_am33xx,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,dm814-phy-gmii-sel",
|
||||
.data = &phy_gmii_sel_soc_dm814,
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, phy_gmii_sel_id_table);
|
||||
|
||||
static const struct phy_ops phy_gmii_sel_ops = {
|
||||
.set_mode = phy_gmii_sel_mode,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct phy *phy_gmii_sel_of_xlate(struct device *dev,
|
||||
struct of_phandle_args *args)
|
||||
{
|
||||
struct phy_gmii_sel_priv *priv = dev_get_drvdata(dev);
|
||||
int phy_id = args->args[0];
|
||||
|
||||
if (args->args_count < 1)
|
||||
return ERR_PTR(-EINVAL);
|
||||
if (priv->soc_data->features & BIT(PHY_GMII_SEL_RMII_IO_CLK_EN) &&
|
||||
args->args_count < 2)
|
||||
return ERR_PTR(-EINVAL);
|
||||
if (!priv || !priv->if_phys)
|
||||
return ERR_PTR(-ENODEV);
|
||||
if (phy_id > priv->soc_data->num_ports)
|
||||
return ERR_PTR(-EINVAL);
|
||||
if (phy_id != priv->if_phys[phy_id - 1].id)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
phy_id--;
|
||||
if (priv->soc_data->features & BIT(PHY_GMII_SEL_RMII_IO_CLK_EN))
|
||||
priv->if_phys[phy_id].rmii_clock_external = args->args[1];
|
||||
dev_dbg(dev, "%s id:%u ext:%d\n", __func__,
|
||||
priv->if_phys[phy_id].id, args->args[1]);
|
||||
|
||||
return priv->if_phys[phy_id].if_phy;
|
||||
}
|
||||
|
||||
static int phy_gmii_sel_init_ports(struct phy_gmii_sel_priv *priv)
|
||||
{
|
||||
const struct phy_gmii_sel_soc_data *soc_data = priv->soc_data;
|
||||
struct device *dev = priv->dev;
|
||||
struct phy_gmii_sel_phy_priv *if_phys;
|
||||
int i, num_ports, ret;
|
||||
|
||||
num_ports = priv->soc_data->num_ports;
|
||||
|
||||
if_phys = devm_kcalloc(priv->dev, num_ports,
|
||||
sizeof(*if_phys), GFP_KERNEL);
|
||||
if (!if_phys)
|
||||
return -ENOMEM;
|
||||
dev_dbg(dev, "%s %d\n", __func__, num_ports);
|
||||
|
||||
for (i = 0; i < num_ports; i++) {
|
||||
const struct reg_field *field;
|
||||
struct regmap_field *regfield;
|
||||
|
||||
if_phys[i].id = i + 1;
|
||||
if_phys[i].priv = priv;
|
||||
|
||||
field = &soc_data->regfields[i][PHY_GMII_SEL_PORT_MODE];
|
||||
dev_dbg(dev, "%s field %x %d %d\n", __func__,
|
||||
field->reg, field->msb, field->lsb);
|
||||
|
||||
regfield = devm_regmap_field_alloc(dev, priv->regmap, *field);
|
||||
if (IS_ERR(regfield))
|
||||
return PTR_ERR(regfield);
|
||||
if_phys[i].fields[PHY_GMII_SEL_PORT_MODE] = regfield;
|
||||
|
||||
field = &soc_data->regfields[i][PHY_GMII_SEL_RGMII_ID_MODE];
|
||||
if (field->reg != (~0)) {
|
||||
regfield = devm_regmap_field_alloc(dev,
|
||||
priv->regmap,
|
||||
*field);
|
||||
if (IS_ERR(regfield))
|
||||
return PTR_ERR(regfield);
|
||||
if_phys[i].fields[PHY_GMII_SEL_RGMII_ID_MODE] =
|
||||
regfield;
|
||||
}
|
||||
|
||||
field = &soc_data->regfields[i][PHY_GMII_SEL_RMII_IO_CLK_EN];
|
||||
if (field->reg != (~0)) {
|
||||
regfield = devm_regmap_field_alloc(dev,
|
||||
priv->regmap,
|
||||
*field);
|
||||
if (IS_ERR(regfield))
|
||||
return PTR_ERR(regfield);
|
||||
if_phys[i].fields[PHY_GMII_SEL_RMII_IO_CLK_EN] =
|
||||
regfield;
|
||||
}
|
||||
|
||||
if_phys[i].if_phy = devm_phy_create(dev,
|
||||
priv->dev->of_node,
|
||||
&phy_gmii_sel_ops);
|
||||
if (IS_ERR(if_phys[i].if_phy)) {
|
||||
ret = PTR_ERR(if_phys[i].if_phy);
|
||||
dev_err(dev, "Failed to create phy%d %d\n", i, ret);
|
||||
return ret;
|
||||
}
|
||||
phy_set_drvdata(if_phys[i].if_phy, &if_phys[i]);
|
||||
}
|
||||
|
||||
priv->if_phys = if_phys;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int phy_gmii_sel_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
const struct of_device_id *of_id;
|
||||
struct phy_gmii_sel_priv *priv;
|
||||
int ret;
|
||||
|
||||
of_id = of_match_node(phy_gmii_sel_id_table, pdev->dev.of_node);
|
||||
if (!of_id)
|
||||
return -EINVAL;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->dev = &pdev->dev;
|
||||
priv->soc_data = of_id->data;
|
||||
|
||||
priv->regmap = syscon_node_to_regmap(node->parent);
|
||||
if (IS_ERR(priv->regmap)) {
|
||||
ret = PTR_ERR(priv->regmap);
|
||||
dev_err(dev, "Failed to get syscon %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = phy_gmii_sel_init_ports(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_set_drvdata(&pdev->dev, priv);
|
||||
|
||||
priv->phy_provider =
|
||||
devm_of_phy_provider_register(dev,
|
||||
phy_gmii_sel_of_xlate);
|
||||
if (IS_ERR(priv->phy_provider)) {
|
||||
ret = PTR_ERR(priv->phy_provider);
|
||||
dev_err(dev, "Failed to create phy provider %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver phy_gmii_sel_driver = {
|
||||
.probe = phy_gmii_sel_probe,
|
||||
.driver = {
|
||||
.name = "phy-gmii-sel",
|
||||
.of_match_table = phy_gmii_sel_id_table,
|
||||
},
|
||||
};
|
||||
module_platform_driver(phy_gmii_sel_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Grygorii Strashko <grygorii.strashko@ti.com>");
|
||||
MODULE_DESCRIPTION("TI CPSW Port's PHY Interface Mode selection Driver");
|
@ -53,7 +53,7 @@ static int tusb1210_power_off(struct phy *phy)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tusb1210_set_mode(struct phy *phy, enum phy_mode mode)
|
||||
static int tusb1210_set_mode(struct phy *phy, enum phy_mode mode, int submode)
|
||||
{
|
||||
struct tusb1210 *tusb = phy_get_drvdata(phy);
|
||||
int ret;
|
||||
|
@ -79,7 +79,7 @@ static const struct property_entry max17047_props[] = {
|
||||
};
|
||||
|
||||
static const struct property_entry fusb302_props[] = {
|
||||
PROPERTY_ENTRY_STRING("fcs,extcon-name", "cht_wcove_pwrsrc"),
|
||||
PROPERTY_ENTRY_STRING("linux,extcon-name", "cht_wcove_pwrsrc"),
|
||||
PROPERTY_ENTRY_U32("fcs,max-sink-microvolt", 12000000),
|
||||
PROPERTY_ENTRY_U32("fcs,max-sink-microamp", 3000000),
|
||||
PROPERTY_ENTRY_U32("fcs,max-sink-microwatt", 36000000),
|
||||
|
@ -205,8 +205,4 @@ config USB_ULPI_BUS
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called ulpi.
|
||||
|
||||
config USB_ROLE_SWITCH
|
||||
tristate
|
||||
select USB_COMMON
|
||||
|
||||
endif # USB_SUPPORT
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <linux/usb/chipidea.h>
|
||||
#include <linux/usb/of.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
|
||||
#include "ci.h"
|
||||
#include "ci_hdrc_imx.h"
|
||||
@ -85,6 +86,9 @@ struct ci_hdrc_imx_data {
|
||||
bool supports_runtime_pm;
|
||||
bool override_phy_control;
|
||||
bool in_lpm;
|
||||
struct pinctrl *pinctrl;
|
||||
struct pinctrl_state *pinctrl_hsic_active;
|
||||
struct regulator *hsic_pad_regulator;
|
||||
/* SoC before i.mx6 (except imx23/imx28) needs three clks */
|
||||
bool need_three_clks;
|
||||
struct clk *clk_ipg;
|
||||
@ -132,11 +136,21 @@ static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev)
|
||||
|
||||
data->dev = &misc_pdev->dev;
|
||||
|
||||
if (of_find_property(np, "disable-over-current", NULL))
|
||||
/*
|
||||
* Check the various over current related properties. If over current
|
||||
* detection is disabled we're not interested in the polarity.
|
||||
*/
|
||||
if (of_find_property(np, "disable-over-current", NULL)) {
|
||||
data->disable_oc = 1;
|
||||
|
||||
if (of_find_property(np, "over-current-active-high", NULL))
|
||||
data->oc_polarity = 1;
|
||||
} else if (of_find_property(np, "over-current-active-high", NULL)) {
|
||||
data->oc_pol_active_low = 0;
|
||||
data->oc_pol_configured = 1;
|
||||
} else if (of_find_property(np, "over-current-active-low", NULL)) {
|
||||
data->oc_pol_active_low = 1;
|
||||
data->oc_pol_configured = 1;
|
||||
} else {
|
||||
dev_warn(dev, "No over current polarity defined\n");
|
||||
}
|
||||
|
||||
if (of_find_property(np, "external-vbus-divider", NULL))
|
||||
data->evdo = 1;
|
||||
@ -245,19 +259,49 @@ static void imx_disable_unprepare_clks(struct device *dev)
|
||||
}
|
||||
}
|
||||
|
||||
static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned int event)
|
||||
{
|
||||
struct device *dev = ci->dev->parent;
|
||||
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
switch (event) {
|
||||
case CI_HDRC_IMX_HSIC_ACTIVE_EVENT:
|
||||
ret = pinctrl_select_state(data->pinctrl,
|
||||
data->pinctrl_hsic_active);
|
||||
if (ret)
|
||||
dev_err(dev, "hsic_active select failed, err=%d\n",
|
||||
ret);
|
||||
break;
|
||||
case CI_HDRC_IMX_HSIC_SUSPEND_EVENT:
|
||||
ret = imx_usbmisc_hsic_set_connect(data->usbmisc_data);
|
||||
if (ret)
|
||||
dev_err(dev,
|
||||
"hsic_set_connect failed, err=%d\n", ret);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ci_hdrc_imx_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ci_hdrc_imx_data *data;
|
||||
struct ci_hdrc_platform_data pdata = {
|
||||
.name = dev_name(&pdev->dev),
|
||||
.capoffset = DEF_CAPOFFSET,
|
||||
.notify_event = ci_hdrc_imx_notify_event,
|
||||
};
|
||||
int ret;
|
||||
const struct of_device_id *of_id;
|
||||
const struct ci_hdrc_imx_platform_flag *imx_platform_flag;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct pinctrl_state *pinctrl_hsic_idle;
|
||||
|
||||
of_id = of_match_device(ci_hdrc_imx_dt_ids, &pdev->dev);
|
||||
of_id = of_match_device(ci_hdrc_imx_dt_ids, dev);
|
||||
if (!of_id)
|
||||
return -ENODEV;
|
||||
|
||||
@ -268,19 +312,73 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, data);
|
||||
data->usbmisc_data = usbmisc_get_init_data(&pdev->dev);
|
||||
data->usbmisc_data = usbmisc_get_init_data(dev);
|
||||
if (IS_ERR(data->usbmisc_data))
|
||||
return PTR_ERR(data->usbmisc_data);
|
||||
|
||||
ret = imx_get_clks(&pdev->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (of_usb_get_phy_mode(dev->of_node) == USBPHY_INTERFACE_MODE_HSIC) {
|
||||
pdata.flags |= CI_HDRC_IMX_IS_HSIC;
|
||||
data->usbmisc_data->hsic = 1;
|
||||
data->pinctrl = devm_pinctrl_get(dev);
|
||||
if (IS_ERR(data->pinctrl)) {
|
||||
dev_err(dev, "pinctrl get failed, err=%ld\n",
|
||||
PTR_ERR(data->pinctrl));
|
||||
return PTR_ERR(data->pinctrl);
|
||||
}
|
||||
|
||||
ret = imx_prepare_enable_clks(&pdev->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
pinctrl_hsic_idle = pinctrl_lookup_state(data->pinctrl, "idle");
|
||||
if (IS_ERR(pinctrl_hsic_idle)) {
|
||||
dev_err(dev,
|
||||
"pinctrl_hsic_idle lookup failed, err=%ld\n",
|
||||
PTR_ERR(pinctrl_hsic_idle));
|
||||
return PTR_ERR(pinctrl_hsic_idle);
|
||||
}
|
||||
|
||||
data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0);
|
||||
ret = pinctrl_select_state(data->pinctrl, pinctrl_hsic_idle);
|
||||
if (ret) {
|
||||
dev_err(dev, "hsic_idle select failed, err=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->pinctrl_hsic_active = pinctrl_lookup_state(data->pinctrl,
|
||||
"active");
|
||||
if (IS_ERR(data->pinctrl_hsic_active)) {
|
||||
dev_err(dev,
|
||||
"pinctrl_hsic_active lookup failed, err=%ld\n",
|
||||
PTR_ERR(data->pinctrl_hsic_active));
|
||||
return PTR_ERR(data->pinctrl_hsic_active);
|
||||
}
|
||||
|
||||
data->hsic_pad_regulator = devm_regulator_get(dev, "hsic");
|
||||
if (PTR_ERR(data->hsic_pad_regulator) == -EPROBE_DEFER) {
|
||||
return -EPROBE_DEFER;
|
||||
} else if (PTR_ERR(data->hsic_pad_regulator) == -ENODEV) {
|
||||
/* no pad regualator is needed */
|
||||
data->hsic_pad_regulator = NULL;
|
||||
} else if (IS_ERR(data->hsic_pad_regulator)) {
|
||||
dev_err(dev, "Get HSIC pad regulator error: %ld\n",
|
||||
PTR_ERR(data->hsic_pad_regulator));
|
||||
return PTR_ERR(data->hsic_pad_regulator);
|
||||
}
|
||||
|
||||
if (data->hsic_pad_regulator) {
|
||||
ret = regulator_enable(data->hsic_pad_regulator);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"Failed to enable HSIC pad regulator\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
ret = imx_get_clks(dev);
|
||||
if (ret)
|
||||
goto disable_hsic_regulator;
|
||||
|
||||
ret = imx_prepare_enable_clks(dev);
|
||||
if (ret)
|
||||
goto disable_hsic_regulator;
|
||||
|
||||
data->phy = devm_usb_get_phy_by_phandle(dev, "fsl,usbphy", 0);
|
||||
if (IS_ERR(data->phy)) {
|
||||
ret = PTR_ERR(data->phy);
|
||||
/* Return -EINVAL if no usbphy is available */
|
||||
@ -305,40 +403,43 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
|
||||
|
||||
ret = imx_usbmisc_init(data->usbmisc_data);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", ret);
|
||||
dev_err(dev, "usbmisc init failed, ret=%d\n", ret);
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
data->ci_pdev = ci_hdrc_add_device(&pdev->dev,
|
||||
data->ci_pdev = ci_hdrc_add_device(dev,
|
||||
pdev->resource, pdev->num_resources,
|
||||
&pdata);
|
||||
if (IS_ERR(data->ci_pdev)) {
|
||||
ret = PTR_ERR(data->ci_pdev);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev,
|
||||
"ci_hdrc_add_device failed, err=%d\n", ret);
|
||||
dev_err(dev, "ci_hdrc_add_device failed, err=%d\n",
|
||||
ret);
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
ret = imx_usbmisc_init_post(data->usbmisc_data);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "usbmisc post failed, ret=%d\n", ret);
|
||||
dev_err(dev, "usbmisc post failed, ret=%d\n", ret);
|
||||
goto disable_device;
|
||||
}
|
||||
|
||||
if (data->supports_runtime_pm) {
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
}
|
||||
|
||||
device_set_wakeup_capable(&pdev->dev, true);
|
||||
device_set_wakeup_capable(dev, true);
|
||||
|
||||
return 0;
|
||||
|
||||
disable_device:
|
||||
ci_hdrc_remove_device(data->ci_pdev);
|
||||
err_clk:
|
||||
imx_disable_unprepare_clks(&pdev->dev);
|
||||
imx_disable_unprepare_clks(dev);
|
||||
disable_hsic_regulator:
|
||||
if (data->hsic_pad_regulator)
|
||||
ret = regulator_disable(data->hsic_pad_regulator);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -355,6 +456,8 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev)
|
||||
if (data->override_phy_control)
|
||||
usb_phy_shutdown(data->phy);
|
||||
imx_disable_unprepare_clks(&pdev->dev);
|
||||
if (data->hsic_pad_regulator)
|
||||
regulator_disable(data->hsic_pad_regulator);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -367,9 +470,16 @@ static void ci_hdrc_imx_shutdown(struct platform_device *pdev)
|
||||
static int __maybe_unused imx_controller_suspend(struct device *dev)
|
||||
{
|
||||
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
dev_dbg(dev, "at %s\n", __func__);
|
||||
|
||||
ret = imx_usbmisc_hsic_set_clk(data->usbmisc_data, false);
|
||||
if (ret) {
|
||||
dev_err(dev, "usbmisc hsic_set_clk failed, ret=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
imx_disable_unprepare_clks(dev);
|
||||
data->in_lpm = true;
|
||||
|
||||
@ -400,8 +510,16 @@ static int __maybe_unused imx_controller_resume(struct device *dev)
|
||||
goto clk_disable;
|
||||
}
|
||||
|
||||
ret = imx_usbmisc_hsic_set_clk(data->usbmisc_data, true);
|
||||
if (ret) {
|
||||
dev_err(dev, "usbmisc hsic_set_clk failed, ret=%d\n", ret);
|
||||
goto hsic_set_clk_fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
hsic_set_clk_fail:
|
||||
imx_usbmisc_set_wakeup(data->usbmisc_data, true);
|
||||
clk_disable:
|
||||
imx_disable_unprepare_clks(dev);
|
||||
return ret;
|
||||
|
@ -11,13 +11,22 @@ struct imx_usbmisc_data {
|
||||
int index;
|
||||
|
||||
unsigned int disable_oc:1; /* over current detect disabled */
|
||||
unsigned int oc_polarity:1; /* over current polarity if oc enabled */
|
||||
|
||||
/* true if over-current polarity is active low */
|
||||
unsigned int oc_pol_active_low:1;
|
||||
|
||||
/* true if dt specifies polarity */
|
||||
unsigned int oc_pol_configured:1;
|
||||
|
||||
unsigned int evdo:1; /* set external vbus divider option */
|
||||
unsigned int ulpi:1; /* connected to an ULPI phy */
|
||||
unsigned int hsic:1; /* HSIC controlller */
|
||||
};
|
||||
|
||||
int imx_usbmisc_init(struct imx_usbmisc_data *);
|
||||
int imx_usbmisc_init_post(struct imx_usbmisc_data *);
|
||||
int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *, bool);
|
||||
int imx_usbmisc_init(struct imx_usbmisc_data *data);
|
||||
int imx_usbmisc_init_post(struct imx_usbmisc_data *data);
|
||||
int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *data, bool enabled);
|
||||
int imx_usbmisc_hsic_set_connect(struct imx_usbmisc_data *data);
|
||||
int imx_usbmisc_hsic_set_clk(struct imx_usbmisc_data *data, bool on);
|
||||
|
||||
#endif /* __DRIVER_USB_CHIPIDEA_CI_HDRC_IMX_H */
|
||||
|
@ -170,6 +170,11 @@ static int host_start(struct ci_hdrc *ci)
|
||||
otg->host = &hcd->self;
|
||||
hcd->self.otg_port = 1;
|
||||
}
|
||||
|
||||
if (ci->platdata->notify_event &&
|
||||
(ci->platdata->flags & CI_HDRC_IMX_IS_HSIC))
|
||||
ci->platdata->notify_event
|
||||
(ci, CI_HDRC_IMX_HSIC_ACTIVE_EVENT);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -215,9 +220,85 @@ void ci_hdrc_host_destroy(struct ci_hdrc *ci)
|
||||
host_stop(ci);
|
||||
}
|
||||
|
||||
/* The below code is based on tegra ehci driver */
|
||||
static int ci_ehci_hub_control(
|
||||
struct usb_hcd *hcd,
|
||||
u16 typeReq,
|
||||
u16 wValue,
|
||||
u16 wIndex,
|
||||
char *buf,
|
||||
u16 wLength
|
||||
)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
u32 __iomem *status_reg;
|
||||
u32 temp;
|
||||
unsigned long flags;
|
||||
int retval = 0;
|
||||
struct device *dev = hcd->self.controller;
|
||||
struct ci_hdrc *ci = dev_get_drvdata(dev);
|
||||
|
||||
status_reg = &ehci->regs->port_status[(wIndex & 0xff) - 1];
|
||||
|
||||
spin_lock_irqsave(&ehci->lock, flags);
|
||||
|
||||
if (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_SUSPEND) {
|
||||
temp = ehci_readl(ehci, status_reg);
|
||||
if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) != 0) {
|
||||
retval = -EPIPE;
|
||||
goto done;
|
||||
}
|
||||
|
||||
temp &= ~(PORT_RWC_BITS | PORT_WKCONN_E);
|
||||
temp |= PORT_WKDISC_E | PORT_WKOC_E;
|
||||
ehci_writel(ehci, temp | PORT_SUSPEND, status_reg);
|
||||
|
||||
/*
|
||||
* If a transaction is in progress, there may be a delay in
|
||||
* suspending the port. Poll until the port is suspended.
|
||||
*/
|
||||
if (ehci_handshake(ehci, status_reg, PORT_SUSPEND,
|
||||
PORT_SUSPEND, 5000))
|
||||
ehci_err(ehci, "timeout waiting for SUSPEND\n");
|
||||
|
||||
if (ci->platdata->flags & CI_HDRC_IMX_IS_HSIC) {
|
||||
if (ci->platdata->notify_event)
|
||||
ci->platdata->notify_event(ci,
|
||||
CI_HDRC_IMX_HSIC_SUSPEND_EVENT);
|
||||
|
||||
temp = ehci_readl(ehci, status_reg);
|
||||
temp &= ~(PORT_WKDISC_E | PORT_WKCONN_E);
|
||||
ehci_writel(ehci, temp, status_reg);
|
||||
}
|
||||
|
||||
set_bit((wIndex & 0xff) - 1, &ehci->suspended_ports);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* After resume has finished, it needs do some post resume
|
||||
* operation for some SoCs.
|
||||
*/
|
||||
else if (typeReq == ClearPortFeature &&
|
||||
wValue == USB_PORT_FEAT_C_SUSPEND) {
|
||||
/* Make sure the resume has finished, it should be finished */
|
||||
if (ehci_handshake(ehci, status_reg, PORT_RESUME, 0, 25000))
|
||||
ehci_err(ehci, "timeout waiting for resume\n");
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&ehci->lock, flags);
|
||||
|
||||
/* Handle the hub control events here */
|
||||
return ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
|
||||
done:
|
||||
spin_unlock_irqrestore(&ehci->lock, flags);
|
||||
return retval;
|
||||
}
|
||||
static int ci_ehci_bus_suspend(struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
struct device *dev = hcd->self.controller;
|
||||
struct ci_hdrc *ci = dev_get_drvdata(dev);
|
||||
int port;
|
||||
u32 tmp;
|
||||
|
||||
@ -249,6 +330,16 @@ static int ci_ehci_bus_suspend(struct usb_hcd *hcd)
|
||||
* It needs a short delay between set RS bit and PHCD.
|
||||
*/
|
||||
usleep_range(150, 200);
|
||||
/*
|
||||
* Need to clear WKCN and WKOC for imx HSIC,
|
||||
* otherwise, there will be wakeup event.
|
||||
*/
|
||||
if (ci->platdata->flags & CI_HDRC_IMX_IS_HSIC) {
|
||||
tmp = ehci_readl(ehci, reg);
|
||||
tmp &= ~(PORT_WKDISC_E | PORT_WKCONN_E);
|
||||
ehci_writel(ehci, tmp, reg);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -281,4 +372,5 @@ void ci_hdrc_host_driver_init(void)
|
||||
ehci_init_driver(&ci_ehci_hc_driver, &ehci_ci_overrides);
|
||||
orig_bus_suspend = ci_ehci_hc_driver.bus_suspend;
|
||||
ci_ehci_hc_driver.bus_suspend = ci_ehci_bus_suspend;
|
||||
ci_ehci_hc_driver.hub_control = ci_ehci_hub_control;
|
||||
}
|
||||
|
@ -64,10 +64,22 @@
|
||||
#define MX6_BM_OVER_CUR_DIS BIT(7)
|
||||
#define MX6_BM_OVER_CUR_POLARITY BIT(8)
|
||||
#define MX6_BM_WAKEUP_ENABLE BIT(10)
|
||||
#define MX6_BM_UTMI_ON_CLOCK BIT(13)
|
||||
#define MX6_BM_ID_WAKEUP BIT(16)
|
||||
#define MX6_BM_VBUS_WAKEUP BIT(17)
|
||||
#define MX6SX_BM_DPDM_WAKEUP_EN BIT(29)
|
||||
#define MX6_BM_WAKEUP_INTR BIT(31)
|
||||
|
||||
#define MX6_USB_HSIC_CTRL_OFFSET 0x10
|
||||
/* Send resume signal without 480Mhz PHY clock */
|
||||
#define MX6SX_BM_HSIC_AUTO_RESUME BIT(23)
|
||||
/* set before portsc.suspendM = 1 */
|
||||
#define MX6_BM_HSIC_DEV_CONN BIT(21)
|
||||
/* HSIC enable */
|
||||
#define MX6_BM_HSIC_EN BIT(12)
|
||||
/* Force HSIC module 480M clock on, even when in Host is in suspend mode */
|
||||
#define MX6_BM_HSIC_CLK_ON BIT(11)
|
||||
|
||||
#define MX6_USB_OTG1_PHY_CTRL 0x18
|
||||
/* For imx6dql, it is host-only controller, for later imx6, it is otg's */
|
||||
#define MX6_USB_OTG2_PHY_CTRL 0x1c
|
||||
@ -94,6 +106,10 @@ struct usbmisc_ops {
|
||||
int (*post)(struct imx_usbmisc_data *data);
|
||||
/* It's called when we need to enable/disable usb wakeup */
|
||||
int (*set_wakeup)(struct imx_usbmisc_data *data, bool enabled);
|
||||
/* It's called before setting portsc.suspendM */
|
||||
int (*hsic_set_connect)(struct imx_usbmisc_data *data);
|
||||
/* It's called during suspend/resume */
|
||||
int (*hsic_set_clk)(struct imx_usbmisc_data *data, bool enabled);
|
||||
};
|
||||
|
||||
struct imx_usbmisc {
|
||||
@ -120,6 +136,14 @@ static int usbmisc_imx25_init(struct imx_usbmisc_data *data)
|
||||
val &= ~(MX25_OTG_SIC_MASK | MX25_OTG_PP_BIT);
|
||||
val |= (MX25_EHCI_INTERFACE_DIFF_UNI & MX25_EHCI_INTERFACE_MASK) << MX25_OTG_SIC_SHIFT;
|
||||
val |= (MX25_OTG_PM_BIT | MX25_OTG_OCPOL_BIT);
|
||||
|
||||
/*
|
||||
* If the polarity is not configured assume active high for
|
||||
* historical reasons.
|
||||
*/
|
||||
if (data->oc_pol_configured && data->oc_pol_active_low)
|
||||
val &= ~MX25_OTG_OCPOL_BIT;
|
||||
|
||||
writel(val, usbmisc->base);
|
||||
break;
|
||||
case 1:
|
||||
@ -129,6 +153,13 @@ static int usbmisc_imx25_init(struct imx_usbmisc_data *data)
|
||||
val |= (MX25_H1_PM_BIT | MX25_H1_OCPOL_BIT | MX25_H1_TLL_BIT |
|
||||
MX25_H1_USBTE_BIT | MX25_H1_IPPUE_DOWN_BIT);
|
||||
|
||||
/*
|
||||
* If the polarity is not configured assume active high for
|
||||
* historical reasons.
|
||||
*/
|
||||
if (data->oc_pol_configured && data->oc_pol_active_low)
|
||||
val &= ~MX25_H1_OCPOL_BIT;
|
||||
|
||||
writel(val, usbmisc->base);
|
||||
|
||||
break;
|
||||
@ -340,11 +371,17 @@ static int usbmisc_imx6q_init(struct imx_usbmisc_data *data)
|
||||
reg = readl(usbmisc->base + data->index * 4);
|
||||
if (data->disable_oc) {
|
||||
reg |= MX6_BM_OVER_CUR_DIS;
|
||||
} else if (data->oc_polarity == 1) {
|
||||
/* High active */
|
||||
reg &= ~(MX6_BM_OVER_CUR_DIS | MX6_BM_OVER_CUR_POLARITY);
|
||||
} else {
|
||||
reg &= ~(MX6_BM_OVER_CUR_DIS);
|
||||
reg &= ~MX6_BM_OVER_CUR_DIS;
|
||||
|
||||
/*
|
||||
* If the polarity is not configured keep it as setup by the
|
||||
* bootloader.
|
||||
*/
|
||||
if (data->oc_pol_configured && data->oc_pol_active_low)
|
||||
reg |= MX6_BM_OVER_CUR_POLARITY;
|
||||
else if (data->oc_pol_configured)
|
||||
reg &= ~MX6_BM_OVER_CUR_POLARITY;
|
||||
}
|
||||
writel(reg, usbmisc->base + data->index * 4);
|
||||
|
||||
@ -353,6 +390,18 @@ static int usbmisc_imx6q_init(struct imx_usbmisc_data *data)
|
||||
writel(reg | MX6_BM_NON_BURST_SETTING,
|
||||
usbmisc->base + data->index * 4);
|
||||
|
||||
/* For HSIC controller */
|
||||
if (data->hsic) {
|
||||
reg = readl(usbmisc->base + data->index * 4);
|
||||
writel(reg | MX6_BM_UTMI_ON_CLOCK,
|
||||
usbmisc->base + data->index * 4);
|
||||
reg = readl(usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET
|
||||
+ (data->index - 2) * 4);
|
||||
reg |= MX6_BM_HSIC_EN | MX6_BM_HSIC_CLK_ON;
|
||||
writel(reg, usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET
|
||||
+ (data->index - 2) * 4);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&usbmisc->lock, flags);
|
||||
|
||||
usbmisc_imx6q_set_wakeup(data, false);
|
||||
@ -360,6 +409,79 @@ static int usbmisc_imx6q_init(struct imx_usbmisc_data *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usbmisc_imx6_hsic_get_reg_offset(struct imx_usbmisc_data *data)
|
||||
{
|
||||
int offset, ret = 0;
|
||||
|
||||
if (data->index == 2 || data->index == 3) {
|
||||
offset = (data->index - 2) * 4;
|
||||
} else if (data->index == 0) {
|
||||
/*
|
||||
* For SoCs like i.MX7D and later, each USB controller has
|
||||
* its own non-core register region. For SoCs before i.MX7D,
|
||||
* the first two USB controllers are non-HSIC controllers.
|
||||
*/
|
||||
offset = 0;
|
||||
} else {
|
||||
dev_err(data->dev, "index is error for usbmisc\n");
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret ? ret : offset;
|
||||
}
|
||||
|
||||
static int usbmisc_imx6_hsic_set_connect(struct imx_usbmisc_data *data)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
|
||||
int offset;
|
||||
|
||||
spin_lock_irqsave(&usbmisc->lock, flags);
|
||||
offset = usbmisc_imx6_hsic_get_reg_offset(data);
|
||||
if (offset < 0) {
|
||||
spin_unlock_irqrestore(&usbmisc->lock, flags);
|
||||
return offset;
|
||||
}
|
||||
|
||||
val = readl(usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET + offset);
|
||||
if (!(val & MX6_BM_HSIC_DEV_CONN))
|
||||
writel(val | MX6_BM_HSIC_DEV_CONN,
|
||||
usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET + offset);
|
||||
|
||||
spin_unlock_irqrestore(&usbmisc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usbmisc_imx6_hsic_set_clk(struct imx_usbmisc_data *data, bool on)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
|
||||
int offset;
|
||||
|
||||
spin_lock_irqsave(&usbmisc->lock, flags);
|
||||
offset = usbmisc_imx6_hsic_get_reg_offset(data);
|
||||
if (offset < 0) {
|
||||
spin_unlock_irqrestore(&usbmisc->lock, flags);
|
||||
return offset;
|
||||
}
|
||||
|
||||
val = readl(usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET + offset);
|
||||
val |= MX6_BM_HSIC_EN | MX6_BM_HSIC_CLK_ON;
|
||||
if (on)
|
||||
val |= MX6_BM_HSIC_CLK_ON;
|
||||
else
|
||||
val &= ~MX6_BM_HSIC_CLK_ON;
|
||||
|
||||
writel(val, usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET + offset);
|
||||
spin_unlock_irqrestore(&usbmisc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int usbmisc_imx6sx_init(struct imx_usbmisc_data *data)
|
||||
{
|
||||
void __iomem *reg = NULL;
|
||||
@ -385,6 +507,13 @@ static int usbmisc_imx6sx_init(struct imx_usbmisc_data *data)
|
||||
spin_unlock_irqrestore(&usbmisc->lock, flags);
|
||||
}
|
||||
|
||||
/* For HSIC controller */
|
||||
if (data->hsic) {
|
||||
val = readl(usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET);
|
||||
val |= MX6SX_BM_HSIC_AUTO_RESUME;
|
||||
writel(val, usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -444,9 +573,17 @@ static int usbmisc_imx7d_init(struct imx_usbmisc_data *data)
|
||||
reg = readl(usbmisc->base);
|
||||
if (data->disable_oc) {
|
||||
reg |= MX6_BM_OVER_CUR_DIS;
|
||||
} else if (data->oc_polarity == 1) {
|
||||
/* High active */
|
||||
reg &= ~(MX6_BM_OVER_CUR_DIS | MX6_BM_OVER_CUR_POLARITY);
|
||||
} else {
|
||||
reg &= ~MX6_BM_OVER_CUR_DIS;
|
||||
|
||||
/*
|
||||
* If the polarity is not configured keep it as setup by the
|
||||
* bootloader.
|
||||
*/
|
||||
if (data->oc_pol_configured && data->oc_pol_active_low)
|
||||
reg |= MX6_BM_OVER_CUR_POLARITY;
|
||||
else if (data->oc_pol_configured)
|
||||
reg &= ~MX6_BM_OVER_CUR_POLARITY;
|
||||
}
|
||||
writel(reg, usbmisc->base);
|
||||
|
||||
@ -454,6 +591,7 @@ static int usbmisc_imx7d_init(struct imx_usbmisc_data *data)
|
||||
reg &= ~MX7D_USB_VBUS_WAKEUP_SOURCE_MASK;
|
||||
writel(reg | MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID,
|
||||
usbmisc->base + MX7D_USBNC_USB_CTRL2);
|
||||
|
||||
spin_unlock_irqrestore(&usbmisc->lock, flags);
|
||||
|
||||
usbmisc_imx7d_set_wakeup(data, false);
|
||||
@ -481,6 +619,8 @@ static const struct usbmisc_ops imx53_usbmisc_ops = {
|
||||
static const struct usbmisc_ops imx6q_usbmisc_ops = {
|
||||
.set_wakeup = usbmisc_imx6q_set_wakeup,
|
||||
.init = usbmisc_imx6q_init,
|
||||
.hsic_set_connect = usbmisc_imx6_hsic_set_connect,
|
||||
.hsic_set_clk = usbmisc_imx6_hsic_set_clk,
|
||||
};
|
||||
|
||||
static const struct usbmisc_ops vf610_usbmisc_ops = {
|
||||
@ -490,6 +630,8 @@ static const struct usbmisc_ops vf610_usbmisc_ops = {
|
||||
static const struct usbmisc_ops imx6sx_usbmisc_ops = {
|
||||
.set_wakeup = usbmisc_imx6q_set_wakeup,
|
||||
.init = usbmisc_imx6sx_init,
|
||||
.hsic_set_connect = usbmisc_imx6_hsic_set_connect,
|
||||
.hsic_set_clk = usbmisc_imx6_hsic_set_clk,
|
||||
};
|
||||
|
||||
static const struct usbmisc_ops imx7d_usbmisc_ops = {
|
||||
@ -546,6 +688,33 @@ int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *data, bool enabled)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(imx_usbmisc_set_wakeup);
|
||||
|
||||
int imx_usbmisc_hsic_set_connect(struct imx_usbmisc_data *data)
|
||||
{
|
||||
struct imx_usbmisc *usbmisc;
|
||||
|
||||
if (!data)
|
||||
return 0;
|
||||
|
||||
usbmisc = dev_get_drvdata(data->dev);
|
||||
if (!usbmisc->ops->hsic_set_connect || !data->hsic)
|
||||
return 0;
|
||||
return usbmisc->ops->hsic_set_connect(data);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(imx_usbmisc_hsic_set_connect);
|
||||
|
||||
int imx_usbmisc_hsic_set_clk(struct imx_usbmisc_data *data, bool on)
|
||||
{
|
||||
struct imx_usbmisc *usbmisc;
|
||||
|
||||
if (!data)
|
||||
return 0;
|
||||
|
||||
usbmisc = dev_get_drvdata(data->dev);
|
||||
if (!usbmisc->ops->hsic_set_clk || !data->hsic)
|
||||
return 0;
|
||||
return usbmisc->ops->hsic_set_clk(data, on);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(imx_usbmisc_hsic_set_clk);
|
||||
static const struct of_device_id usbmisc_imx_dt_ids[] = {
|
||||
{
|
||||
.compatible = "fsl,imx25-usbmisc",
|
||||
|
@ -581,6 +581,13 @@ static int acm_tty_install(struct tty_driver *driver, struct tty_struct *tty)
|
||||
if (retval)
|
||||
goto error_init_termios;
|
||||
|
||||
/*
|
||||
* Suppress initial echoing for some devices which might send data
|
||||
* immediately after acm driver has been installed.
|
||||
*/
|
||||
if (acm->quirks & DISABLE_ECHO)
|
||||
tty->termios.c_lflag &= ~ECHO;
|
||||
|
||||
tty->driver_data = acm;
|
||||
|
||||
return 0;
|
||||
@ -1657,6 +1664,9 @@ static const struct usb_device_id acm_ids[] = {
|
||||
{ USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
|
||||
.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
|
||||
},
|
||||
{ USB_DEVICE(0x0e8d, 0x2000), /* MediaTek Inc Preloader */
|
||||
.driver_info = DISABLE_ECHO, /* DISABLE ECHO in termios flag */
|
||||
},
|
||||
{ USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */
|
||||
.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
|
||||
},
|
||||
|
@ -140,3 +140,4 @@ struct acm {
|
||||
#define QUIRK_CONTROL_LINE_STATE BIT(6)
|
||||
#define CLEAR_HALT_CONDITIONS BIT(7)
|
||||
#define SEND_ZERO_PACKET BIT(8)
|
||||
#define DISABLE_ECHO BIT(9)
|
||||
|
@ -9,4 +9,3 @@ usb-common-$(CONFIG_USB_LED_TRIG) += led.o
|
||||
|
||||
obj-$(CONFIG_USB_OTG_FSM) += usb-otg-fsm.o
|
||||
obj-$(CONFIG_USB_ULPI_BUS) += ulpi.o
|
||||
obj-$(CONFIG_USB_ROLE_SWITCH) += roles.o
|
||||
|
@ -1074,8 +1074,6 @@ static int register_root_hub(struct usb_hcd *hcd)
|
||||
|
||||
usb_dev->devnum = devnum;
|
||||
usb_dev->bus->devnum_next = devnum + 1;
|
||||
memset (&usb_dev->bus->devmap.devicemap, 0,
|
||||
sizeof usb_dev->bus->devmap.devicemap);
|
||||
set_bit (devnum, usb_dev->bus->devmap.devicemap);
|
||||
usb_set_device_state(usb_dev, USB_STATE_ADDRESS);
|
||||
|
||||
|
@ -1112,6 +1112,16 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
|
||||
USB_PORT_FEAT_ENABLE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add debounce if USB3 link is in polling/link training state.
|
||||
* Link will automatically transition to Enabled state after
|
||||
* link training completes.
|
||||
*/
|
||||
if (hub_is_superspeed(hdev) &&
|
||||
((portstatus & USB_PORT_STAT_LINK_STATE) ==
|
||||
USB_SS_PORT_LS_POLLING))
|
||||
need_debounce_delay = true;
|
||||
|
||||
/* Clear status-change flags; we'll debounce later */
|
||||
if (portchange & USB_PORT_STAT_C_CONNECTION) {
|
||||
need_debounce_delay = true;
|
||||
|
@ -262,7 +262,7 @@ static void dwc2_gadget_wkup_alert_handler(struct dwc2_hsotg *hsotg)
|
||||
if (gintsts2 & GINTSTS2_WKUP_ALERT_INT) {
|
||||
dev_dbg(hsotg->dev, "%s: Wkup_Alert_Int\n", __func__);
|
||||
dwc2_clear_bit(hsotg, GINTSTS2, GINTSTS2_WKUP_ALERT_INT);
|
||||
dwc2_set_bit(hsotg, DCFG, DCTL_RMTWKUPSIG);
|
||||
dwc2_set_bit(hsotg, DCTL, DCTL_RMTWKUPSIG);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3165,8 +3165,6 @@ static void kill_all_requests(struct dwc2_hsotg *hsotg,
|
||||
dwc2_hsotg_txfifo_flush(hsotg, ep->fifo_index);
|
||||
}
|
||||
|
||||
static int dwc2_hsotg_ep_disable(struct usb_ep *ep);
|
||||
|
||||
/**
|
||||
* dwc2_hsotg_disconnect - disconnect service
|
||||
* @hsotg: The device state.
|
||||
@ -3188,9 +3186,11 @@ void dwc2_hsotg_disconnect(struct dwc2_hsotg *hsotg)
|
||||
/* all endpoints should be shutdown */
|
||||
for (ep = 0; ep < hsotg->num_of_eps; ep++) {
|
||||
if (hsotg->eps_in[ep])
|
||||
dwc2_hsotg_ep_disable(&hsotg->eps_in[ep]->ep);
|
||||
kill_all_requests(hsotg, hsotg->eps_in[ep],
|
||||
-ESHUTDOWN);
|
||||
if (hsotg->eps_out[ep])
|
||||
dwc2_hsotg_ep_disable(&hsotg->eps_out[ep]->ep);
|
||||
kill_all_requests(hsotg, hsotg->eps_out[ep],
|
||||
-ESHUTDOWN);
|
||||
}
|
||||
|
||||
call_gadget(hsotg, disconnect);
|
||||
@ -3234,6 +3234,7 @@ static void dwc2_hsotg_irq_fifoempty(struct dwc2_hsotg *hsotg, bool periodic)
|
||||
GINTSTS_PTXFEMP | \
|
||||
GINTSTS_RXFLVL)
|
||||
|
||||
static int dwc2_hsotg_ep_disable(struct usb_ep *ep);
|
||||
/**
|
||||
* dwc2_hsotg_core_init - issue softreset to the core
|
||||
* @hsotg: The device state
|
||||
@ -4069,10 +4070,8 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep)
|
||||
struct dwc2_hsotg *hsotg = hs_ep->parent;
|
||||
int dir_in = hs_ep->dir_in;
|
||||
int index = hs_ep->index;
|
||||
unsigned long flags;
|
||||
u32 epctrl_reg;
|
||||
u32 ctrl;
|
||||
int locked;
|
||||
|
||||
dev_dbg(hsotg->dev, "%s(ep %p)\n", __func__, ep);
|
||||
|
||||
@ -4088,10 +4087,6 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep)
|
||||
|
||||
epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index);
|
||||
|
||||
locked = spin_is_locked(&hsotg->lock);
|
||||
if (!locked)
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
|
||||
ctrl = dwc2_readl(hsotg, epctrl_reg);
|
||||
|
||||
if (ctrl & DXEPCTL_EPENA)
|
||||
@ -4114,12 +4109,22 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep)
|
||||
hs_ep->fifo_index = 0;
|
||||
hs_ep->fifo_size = 0;
|
||||
|
||||
if (!locked)
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc2_hsotg_ep_disable_lock(struct usb_ep *ep)
|
||||
{
|
||||
struct dwc2_hsotg_ep *hs_ep = our_ep(ep);
|
||||
struct dwc2_hsotg *hsotg = hs_ep->parent;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
ret = dwc2_hsotg_ep_disable(ep);
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* on_list - check request is on the given endpoint
|
||||
* @ep: The endpoint to check.
|
||||
@ -4267,7 +4272,7 @@ static int dwc2_hsotg_ep_sethalt_lock(struct usb_ep *ep, int value)
|
||||
|
||||
static const struct usb_ep_ops dwc2_hsotg_ep_ops = {
|
||||
.enable = dwc2_hsotg_ep_enable,
|
||||
.disable = dwc2_hsotg_ep_disable,
|
||||
.disable = dwc2_hsotg_ep_disable_lock,
|
||||
.alloc_request = dwc2_hsotg_ep_alloc_request,
|
||||
.free_request = dwc2_hsotg_ep_free_request,
|
||||
.queue = dwc2_hsotg_ep_queue_lock,
|
||||
@ -4407,9 +4412,9 @@ static int dwc2_hsotg_udc_stop(struct usb_gadget *gadget)
|
||||
/* all endpoints should be shutdown */
|
||||
for (ep = 1; ep < hsotg->num_of_eps; ep++) {
|
||||
if (hsotg->eps_in[ep])
|
||||
dwc2_hsotg_ep_disable(&hsotg->eps_in[ep]->ep);
|
||||
dwc2_hsotg_ep_disable_lock(&hsotg->eps_in[ep]->ep);
|
||||
if (hsotg->eps_out[ep])
|
||||
dwc2_hsotg_ep_disable(&hsotg->eps_out[ep]->ep);
|
||||
dwc2_hsotg_ep_disable_lock(&hsotg->eps_out[ep]->ep);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
@ -4857,9 +4862,9 @@ int dwc2_hsotg_suspend(struct dwc2_hsotg *hsotg)
|
||||
|
||||
for (ep = 0; ep < hsotg->num_of_eps; ep++) {
|
||||
if (hsotg->eps_in[ep])
|
||||
dwc2_hsotg_ep_disable(&hsotg->eps_in[ep]->ep);
|
||||
dwc2_hsotg_ep_disable_lock(&hsotg->eps_in[ep]->ep);
|
||||
if (hsotg->eps_out[ep])
|
||||
dwc2_hsotg_ep_disable(&hsotg->eps_out[ep]->ep);
|
||||
dwc2_hsotg_ep_disable_lock(&hsotg->eps_out[ep]->ep);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5026,6 +5031,7 @@ void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg)
|
||||
val |= hsotg->params.lpm_clock_gating ? GLPMCFG_ENBLSLPM : 0;
|
||||
val |= hsotg->params.hird_threshold << GLPMCFG_HIRD_THRES_SHIFT;
|
||||
val |= hsotg->params.besl ? GLPMCFG_ENBESL : 0;
|
||||
val |= GLPMCFG_LPM_ACCEPT_CTRL_ISOC;
|
||||
dwc2_writel(hsotg, val, GLPMCFG);
|
||||
dev_dbg(hsotg->dev, "GLPMCFG=0x%08x\n", dwc2_readl(hsotg, GLPMCFG));
|
||||
|
||||
|
@ -366,7 +366,7 @@ struct dwc2_qh {
|
||||
u32 desc_list_sz;
|
||||
u32 *n_bytes;
|
||||
struct timer_list unreserve_timer;
|
||||
struct timer_list wait_timer;
|
||||
struct hrtimer wait_timer;
|
||||
struct dwc2_tt *dwc_tt;
|
||||
int ttport;
|
||||
unsigned tt_buffer_dirty:1;
|
||||
|
@ -59,7 +59,7 @@
|
||||
#define DWC2_UNRESERVE_DELAY (msecs_to_jiffies(5))
|
||||
|
||||
/* If we get a NAK, wait this long before retrying */
|
||||
#define DWC2_RETRY_WAIT_DELAY (msecs_to_jiffies(1))
|
||||
#define DWC2_RETRY_WAIT_DELAY 1*1E6L
|
||||
|
||||
/**
|
||||
* dwc2_periodic_channel_available() - Checks that a channel is available for a
|
||||
@ -1464,10 +1464,12 @@ static void dwc2_deschedule_periodic(struct dwc2_hsotg *hsotg,
|
||||
* qh back to the "inactive" list, then queues transactions.
|
||||
*
|
||||
* @t: Pointer to wait_timer in a qh.
|
||||
*
|
||||
* Return: HRTIMER_NORESTART to not automatically restart this timer.
|
||||
*/
|
||||
static void dwc2_wait_timer_fn(struct timer_list *t)
|
||||
static enum hrtimer_restart dwc2_wait_timer_fn(struct hrtimer *t)
|
||||
{
|
||||
struct dwc2_qh *qh = from_timer(qh, t, wait_timer);
|
||||
struct dwc2_qh *qh = container_of(t, struct dwc2_qh, wait_timer);
|
||||
struct dwc2_hsotg *hsotg = qh->hsotg;
|
||||
unsigned long flags;
|
||||
|
||||
@ -1491,6 +1493,7 @@ static void dwc2_wait_timer_fn(struct timer_list *t)
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1521,7 +1524,8 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
|
||||
/* Initialize QH */
|
||||
qh->hsotg = hsotg;
|
||||
timer_setup(&qh->unreserve_timer, dwc2_unreserve_timer_fn, 0);
|
||||
timer_setup(&qh->wait_timer, dwc2_wait_timer_fn, 0);
|
||||
hrtimer_init(&qh->wait_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
qh->wait_timer.function = &dwc2_wait_timer_fn;
|
||||
qh->ep_type = ep_type;
|
||||
qh->ep_is_in = ep_is_in;
|
||||
|
||||
@ -1690,7 +1694,7 @@ void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
|
||||
* won't do anything anyway, but we want it to finish before we free
|
||||
* memory.
|
||||
*/
|
||||
del_timer_sync(&qh->wait_timer);
|
||||
hrtimer_cancel(&qh->wait_timer);
|
||||
|
||||
dwc2_host_put_tt_info(hsotg, qh->dwc_tt);
|
||||
|
||||
@ -1716,6 +1720,7 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
|
||||
{
|
||||
int status;
|
||||
u32 intr_mask;
|
||||
ktime_t delay;
|
||||
|
||||
if (dbg_qh(qh))
|
||||
dev_vdbg(hsotg->dev, "%s()\n", __func__);
|
||||
@ -1734,8 +1739,8 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
|
||||
list_add_tail(&qh->qh_list_entry,
|
||||
&hsotg->non_periodic_sched_waiting);
|
||||
qh->wait_timer_cancel = false;
|
||||
mod_timer(&qh->wait_timer,
|
||||
jiffies + DWC2_RETRY_WAIT_DELAY + 1);
|
||||
delay = ktime_set(0, DWC2_RETRY_WAIT_DELAY);
|
||||
hrtimer_start(&qh->wait_timer, delay, HRTIMER_MODE_REL);
|
||||
} else {
|
||||
list_add_tail(&qh->qh_list_entry,
|
||||
&hsotg->non_periodic_sched_inactive);
|
||||
|
@ -333,6 +333,8 @@
|
||||
#define GLPMCFG_SNDLPM BIT(24)
|
||||
#define GLPMCFG_RETRY_CNT_MASK (0x7 << 21)
|
||||
#define GLPMCFG_RETRY_CNT_SHIFT 21
|
||||
#define GLPMCFG_LPM_ACCEPT_CTRL_CONTROL BIT(21)
|
||||
#define GLPMCFG_LPM_ACCEPT_CTRL_ISOC BIT(22)
|
||||
#define GLPMCFG_LPM_CHNL_INDX_MASK (0xf << 17)
|
||||
#define GLPMCFG_LPM_CHNL_INDX_SHIFT 17
|
||||
#define GLPMCFG_L1RESUMEOK BIT(16)
|
||||
|
@ -71,6 +71,13 @@ static void dwc2_set_his_params(struct dwc2_hsotg *hsotg)
|
||||
p->power_down = false;
|
||||
}
|
||||
|
||||
static void dwc2_set_s3c6400_params(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_core_params *p = &hsotg->params;
|
||||
|
||||
p->power_down = 0;
|
||||
}
|
||||
|
||||
static void dwc2_set_rk_params(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_core_params *p = &hsotg->params;
|
||||
@ -111,6 +118,7 @@ static void dwc2_set_amlogic_params(struct dwc2_hsotg *hsotg)
|
||||
p->phy_type = DWC2_PHY_TYPE_PARAM_UTMI;
|
||||
p->ahbcfg = GAHBCFG_HBSTLEN_INCR8 <<
|
||||
GAHBCFG_HBSTLEN_SHIFT;
|
||||
p->power_down = DWC2_POWER_DOWN_PARAM_NONE;
|
||||
}
|
||||
|
||||
static void dwc2_set_amcc_params(struct dwc2_hsotg *hsotg)
|
||||
@ -151,7 +159,8 @@ const struct of_device_id dwc2_of_match_table[] = {
|
||||
{ .compatible = "lantiq,arx100-usb", .data = dwc2_set_ltq_params },
|
||||
{ .compatible = "lantiq,xrx200-usb", .data = dwc2_set_ltq_params },
|
||||
{ .compatible = "snps,dwc2" },
|
||||
{ .compatible = "samsung,s3c6400-hsotg" },
|
||||
{ .compatible = "samsung,s3c6400-hsotg",
|
||||
.data = dwc2_set_s3c6400_params },
|
||||
{ .compatible = "amlogic,meson8-usb",
|
||||
.data = dwc2_set_amlogic_params },
|
||||
{ .compatible = "amlogic,meson8b-usb",
|
||||
|
@ -80,11 +80,12 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc)
|
||||
mode = USB_DR_MODE_PERIPHERAL;
|
||||
|
||||
/*
|
||||
* dwc_usb31 does not support OTG mode. If the controller
|
||||
* supports DRD but the dr_mode is not specified or set to OTG,
|
||||
* then set the mode to peripheral.
|
||||
* DWC_usb31 and DWC_usb3 v3.30a and higher do not support OTG
|
||||
* mode. If the controller supports DRD but the dr_mode is not
|
||||
* specified or set to OTG, then set the mode to peripheral.
|
||||
*/
|
||||
if (mode == USB_DR_MODE_OTG && dwc3_is_usb31(dwc))
|
||||
if (mode == USB_DR_MODE_OTG &&
|
||||
dwc->revision >= DWC3_REVISION_330A)
|
||||
mode = USB_DR_MODE_PERIPHERAL;
|
||||
}
|
||||
|
||||
@ -661,6 +662,8 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
|
||||
|
||||
if (dwc->dis_enblslpm_quirk)
|
||||
reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM;
|
||||
else
|
||||
reg |= DWC3_GUSB2PHYCFG_ENBLSLPM;
|
||||
|
||||
if (dwc->dis_u2_freeclk_exists_quirk)
|
||||
reg &= ~DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS;
|
||||
@ -702,6 +705,7 @@ static bool dwc3_core_is_valid(struct dwc3 *dwc)
|
||||
/* Detected DWC_usb31 IP */
|
||||
dwc->revision = dwc3_readl(dwc->regs, DWC3_VER_NUMBER);
|
||||
dwc->revision |= DWC3_REVISION_IS_DWC31;
|
||||
dwc->version_type = dwc3_readl(dwc->regs, DWC3_VER_TYPE);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@ -1244,8 +1248,12 @@ static void dwc3_get_properties(struct dwc3 *dwc)
|
||||
"snps,is-utmi-l1-suspend");
|
||||
device_property_read_u8(dev, "snps,hird-threshold",
|
||||
&hird_threshold);
|
||||
dwc->dis_start_transfer_quirk = device_property_read_bool(dev,
|
||||
"snps,dis-start-transfer-quirk");
|
||||
dwc->usb3_lpm_capable = device_property_read_bool(dev,
|
||||
"snps,usb3_lpm_capable");
|
||||
dwc->usb2_lpm_disable = device_property_read_bool(dev,
|
||||
"snps,usb2-lpm-disable");
|
||||
device_property_read_u8(dev, "snps,rx-thr-num-pkt-prd",
|
||||
&rx_thr_num_pkt_prd);
|
||||
device_property_read_u8(dev, "snps,rx-max-burst-prd",
|
||||
@ -1482,7 +1490,8 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
|
||||
ret = dwc3_core_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize core\n");
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to initialize core: %d\n", ret);
|
||||
goto err4;
|
||||
}
|
||||
|
||||
|
@ -37,6 +37,7 @@
|
||||
#define DWC3_EP0_SETUP_SIZE 512
|
||||
#define DWC3_ENDPOINTS_NUM 32
|
||||
#define DWC3_XHCI_RESOURCES_NUM 2
|
||||
#define DWC3_ISOC_MAX_RETRIES 5
|
||||
|
||||
#define DWC3_SCRATCHBUF_SIZE 4096 /* each buffer is assumed to be 4KiB */
|
||||
#define DWC3_EVENT_BUFFERS_SIZE 4096
|
||||
@ -174,13 +175,19 @@
|
||||
#define DWC3_GSBUSCFG0_INCRBRSTENA (1 << 0) /* undefined length enable */
|
||||
#define DWC3_GSBUSCFG0_INCRBRST_MASK 0xff
|
||||
|
||||
/* Global Debug LSP MUX Select */
|
||||
#define DWC3_GDBGLSPMUX_ENDBC BIT(15) /* Host only */
|
||||
#define DWC3_GDBGLSPMUX_HOSTSELECT(n) ((n) & 0x3fff)
|
||||
#define DWC3_GDBGLSPMUX_DEVSELECT(n) (((n) & 0xf) << 4)
|
||||
#define DWC3_GDBGLSPMUX_EPSELECT(n) ((n) & 0xf)
|
||||
|
||||
/* Global Debug Queue/FIFO Space Available Register */
|
||||
#define DWC3_GDBGFIFOSPACE_NUM(n) ((n) & 0x1f)
|
||||
#define DWC3_GDBGFIFOSPACE_TYPE(n) (((n) << 5) & 0x1e0)
|
||||
#define DWC3_GDBGFIFOSPACE_SPACE_AVAILABLE(n) (((n) >> 16) & 0xffff)
|
||||
|
||||
#define DWC3_TXFIFOQ 0
|
||||
#define DWC3_RXFIFOQ 1
|
||||
#define DWC3_TXFIFO 0
|
||||
#define DWC3_RXFIFO 1
|
||||
#define DWC3_TXREQQ 2
|
||||
#define DWC3_RXREQQ 3
|
||||
#define DWC3_RXINFOQ 4
|
||||
@ -253,6 +260,9 @@
|
||||
#define DWC3_GSTS_DEVICE_IP BIT(6)
|
||||
#define DWC3_GSTS_CSR_TIMEOUT BIT(5)
|
||||
#define DWC3_GSTS_BUS_ERR_ADDR_VLD BIT(4)
|
||||
#define DWC3_GSTS_CURMOD(n) ((n) & 0x3)
|
||||
#define DWC3_GSTS_CURMOD_DEVICE 0
|
||||
#define DWC3_GSTS_CURMOD_HOST 1
|
||||
|
||||
/* Global USB2 PHY Configuration Register */
|
||||
#define DWC3_GUSB2PHYCFG_PHYSOFTRST BIT(31)
|
||||
@ -321,6 +331,7 @@
|
||||
#define DWC3_GHWPARAMS1_EN_PWROPT_HIB 2
|
||||
#define DWC3_GHWPARAMS1_PWROPT(n) ((n) << 24)
|
||||
#define DWC3_GHWPARAMS1_PWROPT_MASK DWC3_GHWPARAMS1_PWROPT(3)
|
||||
#define DWC3_GHWPARAMS1_ENDBC BIT(31)
|
||||
|
||||
/* Global HWPARAMS3 Register */
|
||||
#define DWC3_GHWPARAMS3_SSPHY_IFC(n) ((n) & 3)
|
||||
@ -636,9 +647,9 @@ struct dwc3_event_buffer {
|
||||
/**
|
||||
* struct dwc3_ep - device side endpoint representation
|
||||
* @endpoint: usb endpoint
|
||||
* @cancelled_list: list of cancelled requests for this endpoint
|
||||
* @pending_list: list of pending requests for this endpoint
|
||||
* @started_list: list of started requests on this endpoint
|
||||
* @wait_end_transfer: wait_queue_head_t for waiting on End Transfer complete
|
||||
* @lock: spinlock for endpoint request queue traversal
|
||||
* @regs: pointer to first endpoint register
|
||||
* @trb_pool: array of transaction buffers
|
||||
@ -656,14 +667,17 @@ struct dwc3_event_buffer {
|
||||
* @name: a human readable name e.g. ep1out-bulk
|
||||
* @direction: true for TX, false for RX
|
||||
* @stream_capable: true when streams are enabled
|
||||
* @combo_num: the test combination BIT[15:14] of the frame number to test
|
||||
* isochronous START TRANSFER command failure workaround
|
||||
* @start_cmd_status: the status of testing START TRANSFER command with
|
||||
* combo_num = 'b00
|
||||
*/
|
||||
struct dwc3_ep {
|
||||
struct usb_ep endpoint;
|
||||
struct list_head cancelled_list;
|
||||
struct list_head pending_list;
|
||||
struct list_head started_list;
|
||||
|
||||
wait_queue_head_t wait_end_transfer;
|
||||
|
||||
spinlock_t lock;
|
||||
void __iomem *regs;
|
||||
|
||||
@ -705,6 +719,10 @@ struct dwc3_ep {
|
||||
|
||||
unsigned direction:1;
|
||||
unsigned stream_capable:1;
|
||||
|
||||
/* For isochronous START TRANSFER workaround only */
|
||||
u8 combo_num;
|
||||
int start_cmd_status;
|
||||
};
|
||||
|
||||
enum dwc3_phy {
|
||||
@ -766,6 +784,7 @@ enum dwc3_link_state {
|
||||
#define DWC3_TRB_CTRL_ISP_IMI BIT(10)
|
||||
#define DWC3_TRB_CTRL_IOC BIT(11)
|
||||
#define DWC3_TRB_CTRL_SID_SOFN(n) (((n) & 0xffff) << 14)
|
||||
#define DWC3_TRB_CTRL_GET_SID_SOFN(n) (((n) & (0xffff << 14)) >> 14)
|
||||
|
||||
#define DWC3_TRBCTL_TYPE(n) ((n) & (0x3f << 4))
|
||||
#define DWC3_TRBCTL_NORMAL DWC3_TRB_CTRL_TRBCTL(1)
|
||||
@ -847,11 +866,12 @@ struct dwc3_hwparams {
|
||||
* @epnum: endpoint number to which this request refers
|
||||
* @trb: pointer to struct dwc3_trb
|
||||
* @trb_dma: DMA address of @trb
|
||||
* @unaligned: true for OUT endpoints with length not divisible by maxp
|
||||
* @num_trbs: number of TRBs used by this request
|
||||
* @needs_extra_trb: true when request needs one extra TRB (either due to ZLP
|
||||
* or unaligned OUT)
|
||||
* @direction: IN or OUT direction flag
|
||||
* @mapped: true when request has been dma-mapped
|
||||
* @started: request is started
|
||||
* @zero: wants a ZLP
|
||||
*/
|
||||
struct dwc3_request {
|
||||
struct usb_request request;
|
||||
@ -867,11 +887,12 @@ struct dwc3_request {
|
||||
struct dwc3_trb *trb;
|
||||
dma_addr_t trb_dma;
|
||||
|
||||
unsigned unaligned:1;
|
||||
unsigned num_trbs;
|
||||
|
||||
unsigned needs_extra_trb:1;
|
||||
unsigned direction:1;
|
||||
unsigned mapped:1;
|
||||
unsigned started:1;
|
||||
unsigned zero:1;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -918,6 +939,7 @@ struct dwc3_scratchpad_array {
|
||||
* @u1u2: only used on revisions <1.83a for workaround
|
||||
* @maximum_speed: maximum speed requested (mainly for testing purposes)
|
||||
* @revision: revision register contents
|
||||
* @version_type: VERSIONTYPE register contents, a sub release of a revision
|
||||
* @dr_mode: requested mode of operation
|
||||
* @current_dr_role: current role of operation when in dual-role mode
|
||||
* @desired_dr_role: desired role of operation when in dual-role mode
|
||||
@ -945,6 +967,7 @@ struct dwc3_scratchpad_array {
|
||||
* @hwparams: copy of hwparams registers
|
||||
* @root: debugfs root folder pointer
|
||||
* @regset: debugfs pointer to regdump file
|
||||
* @dbg_lsp_select: current debug lsp mux register selection
|
||||
* @test_mode: true when we're entering a USB test mode
|
||||
* @test_mode_nr: test feature selector
|
||||
* @lpm_nyet_threshold: LPM NYET response threshold
|
||||
@ -970,7 +993,10 @@ struct dwc3_scratchpad_array {
|
||||
* @pullups_connected: true when Run/Stop bit is set
|
||||
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
|
||||
* @three_stage_setup: set if we perform a three phase setup
|
||||
* @dis_start_transfer_quirk: set if start_transfer failure SW workaround is
|
||||
* not needed for DWC_usb31 version 1.70a-ea06 and below
|
||||
* @usb3_lpm_capable: set if hadrware supports Link Power Management
|
||||
* @usb2_lpm_disable: set to disable usb2 lpm
|
||||
* @disable_scramble_quirk: set if we enable the disable scramble quirk
|
||||
* @u2exit_lfps_quirk: set if we enable u2exit lfps quirk
|
||||
* @u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk
|
||||
@ -1095,6 +1121,7 @@ struct dwc3 {
|
||||
#define DWC3_REVISION_290A 0x5533290a
|
||||
#define DWC3_REVISION_300A 0x5533300a
|
||||
#define DWC3_REVISION_310A 0x5533310a
|
||||
#define DWC3_REVISION_330A 0x5533330a
|
||||
|
||||
/*
|
||||
* NOTICE: we're using bit 31 as a "is usb 3.1" flag. This is really
|
||||
@ -1103,6 +1130,17 @@ struct dwc3 {
|
||||
#define DWC3_REVISION_IS_DWC31 0x80000000
|
||||
#define DWC3_USB31_REVISION_110A (0x3131302a | DWC3_REVISION_IS_DWC31)
|
||||
#define DWC3_USB31_REVISION_120A (0x3132302a | DWC3_REVISION_IS_DWC31)
|
||||
#define DWC3_USB31_REVISION_160A (0x3136302a | DWC3_REVISION_IS_DWC31)
|
||||
#define DWC3_USB31_REVISION_170A (0x3137302a | DWC3_REVISION_IS_DWC31)
|
||||
|
||||
u32 version_type;
|
||||
|
||||
#define DWC31_VERSIONTYPE_EA01 0x65613031
|
||||
#define DWC31_VERSIONTYPE_EA02 0x65613032
|
||||
#define DWC31_VERSIONTYPE_EA03 0x65613033
|
||||
#define DWC31_VERSIONTYPE_EA04 0x65613034
|
||||
#define DWC31_VERSIONTYPE_EA05 0x65613035
|
||||
#define DWC31_VERSIONTYPE_EA06 0x65613036
|
||||
|
||||
enum dwc3_ep0_next ep0_next_event;
|
||||
enum dwc3_ep0_state ep0state;
|
||||
@ -1121,6 +1159,8 @@ struct dwc3 {
|
||||
struct dentry *root;
|
||||
struct debugfs_regset32 *regset;
|
||||
|
||||
u32 dbg_lsp_select;
|
||||
|
||||
u8 test_mode;
|
||||
u8 test_mode_nr;
|
||||
u8 lpm_nyet_threshold;
|
||||
@ -1145,7 +1185,9 @@ struct dwc3 {
|
||||
unsigned pullups_connected:1;
|
||||
unsigned setup_packet_pending:1;
|
||||
unsigned three_stage_setup:1;
|
||||
unsigned dis_start_transfer_quirk:1;
|
||||
unsigned usb3_lpm_capable:1;
|
||||
unsigned usb2_lpm_disable:1;
|
||||
|
||||
unsigned disable_scramble_quirk:1;
|
||||
unsigned u2exit_lfps_quirk:1;
|
||||
|
@ -116,6 +116,35 @@ dwc3_gadget_link_string(enum dwc3_link_state link_state)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_gadget_hs_link_string - returns highspeed and below link name
|
||||
* @link_state: link state code
|
||||
*/
|
||||
static inline const char *
|
||||
dwc3_gadget_hs_link_string(enum dwc3_link_state link_state)
|
||||
{
|
||||
switch (link_state) {
|
||||
case DWC3_LINK_STATE_U0:
|
||||
return "On";
|
||||
case DWC3_LINK_STATE_U2:
|
||||
return "Sleep";
|
||||
case DWC3_LINK_STATE_U3:
|
||||
return "Suspend";
|
||||
case DWC3_LINK_STATE_SS_DIS:
|
||||
return "Disconnected";
|
||||
case DWC3_LINK_STATE_RX_DET:
|
||||
return "Early Suspend";
|
||||
case DWC3_LINK_STATE_RECOV:
|
||||
return "Recovery";
|
||||
case DWC3_LINK_STATE_RESET:
|
||||
return "Reset";
|
||||
case DWC3_LINK_STATE_RESUME:
|
||||
return "Resume";
|
||||
default:
|
||||
return "UNKNOWN link state\n";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_trb_type_string - returns TRB type as a string
|
||||
* @type: the type of the TRB
|
||||
|
@ -25,6 +25,8 @@
|
||||
#include "io.h"
|
||||
#include "debug.h"
|
||||
|
||||
#define DWC3_LSP_MUX_UNSELECTED 0xfffff
|
||||
|
||||
#define dump_register(nm) \
|
||||
{ \
|
||||
.name = __stringify(nm), \
|
||||
@ -82,10 +84,6 @@ static const struct debugfs_reg32 dwc3_regs[] = {
|
||||
dump_register(GDBGFIFOSPACE),
|
||||
dump_register(GDBGLTSSM),
|
||||
dump_register(GDBGBMU),
|
||||
dump_register(GDBGLSPMUX),
|
||||
dump_register(GDBGLSP),
|
||||
dump_register(GDBGEPINFO0),
|
||||
dump_register(GDBGEPINFO1),
|
||||
dump_register(GPRTBIMAP_HS0),
|
||||
dump_register(GPRTBIMAP_HS1),
|
||||
dump_register(GPRTBIMAP_FS0),
|
||||
@ -279,6 +277,114 @@ static const struct debugfs_reg32 dwc3_regs[] = {
|
||||
dump_register(OSTS),
|
||||
};
|
||||
|
||||
static void dwc3_host_lsp(struct seq_file *s)
|
||||
{
|
||||
struct dwc3 *dwc = s->private;
|
||||
bool dbc_enabled;
|
||||
u32 sel;
|
||||
u32 reg;
|
||||
u32 val;
|
||||
|
||||
dbc_enabled = !!(dwc->hwparams.hwparams1 & DWC3_GHWPARAMS1_ENDBC);
|
||||
|
||||
sel = dwc->dbg_lsp_select;
|
||||
if (sel == DWC3_LSP_MUX_UNSELECTED) {
|
||||
seq_puts(s, "Write LSP selection to print for host\n");
|
||||
return;
|
||||
}
|
||||
|
||||
reg = DWC3_GDBGLSPMUX_HOSTSELECT(sel);
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg);
|
||||
val = dwc3_readl(dwc->regs, DWC3_GDBGLSP);
|
||||
seq_printf(s, "GDBGLSP[%d] = 0x%08x\n", sel, val);
|
||||
|
||||
if (dbc_enabled && sel < 256) {
|
||||
reg |= DWC3_GDBGLSPMUX_ENDBC;
|
||||
dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg);
|
||||
val = dwc3_readl(dwc->regs, DWC3_GDBGLSP);
|
||||
seq_printf(s, "GDBGLSP_DBC[%d] = 0x%08x\n", sel, val);
|
||||
}
|
||||
}
|
||||
|
||||
static void dwc3_gadget_lsp(struct seq_file *s)
|
||||
{
|
||||
struct dwc3 *dwc = s->private;
|
||||
int i;
|
||||
u32 reg;
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
reg = DWC3_GDBGLSPMUX_DEVSELECT(i);
|
||||
dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg);
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GDBGLSP);
|
||||
seq_printf(s, "GDBGLSP[%d] = 0x%08x\n", i, reg);
|
||||
}
|
||||
}
|
||||
|
||||
static int dwc3_lsp_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct dwc3 *dwc = s->private;
|
||||
unsigned int current_mode;
|
||||
unsigned long flags;
|
||||
u32 reg;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GSTS);
|
||||
current_mode = DWC3_GSTS_CURMOD(reg);
|
||||
|
||||
switch (current_mode) {
|
||||
case DWC3_GSTS_CURMOD_HOST:
|
||||
dwc3_host_lsp(s);
|
||||
break;
|
||||
case DWC3_GSTS_CURMOD_DEVICE:
|
||||
dwc3_gadget_lsp(s);
|
||||
break;
|
||||
default:
|
||||
seq_puts(s, "Mode is unknown, no LSP register printed\n");
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_lsp_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, dwc3_lsp_show, inode->i_private);
|
||||
}
|
||||
|
||||
static ssize_t dwc3_lsp_write(struct file *file, const char __user *ubuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct seq_file *s = file->private_data;
|
||||
struct dwc3 *dwc = s->private;
|
||||
unsigned long flags;
|
||||
char buf[32] = { 0 };
|
||||
u32 sel;
|
||||
int ret;
|
||||
|
||||
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
|
||||
return -EFAULT;
|
||||
|
||||
ret = kstrtouint(buf, 0, &sel);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
dwc->dbg_lsp_select = sel;
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations dwc3_lsp_fops = {
|
||||
.open = dwc3_lsp_open,
|
||||
.write = dwc3_lsp_write,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int dwc3_mode_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct dwc3 *dwc = s->private;
|
||||
@ -433,13 +539,24 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused)
|
||||
unsigned long flags;
|
||||
enum dwc3_link_state state;
|
||||
u32 reg;
|
||||
u8 speed;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GSTS);
|
||||
if (DWC3_GSTS_CURMOD(reg) != DWC3_GSTS_CURMOD_DEVICE) {
|
||||
seq_puts(s, "Not available\n");
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
||||
state = DWC3_DSTS_USBLNKST(reg);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
speed = reg & DWC3_DSTS_CONNECTSPD;
|
||||
|
||||
seq_printf(s, "%s\n", dwc3_gadget_link_string(state));
|
||||
seq_printf(s, "%s\n", (speed >= DWC3_DSTS_SUPERSPEED) ?
|
||||
dwc3_gadget_link_string(state) :
|
||||
dwc3_gadget_hs_link_string(state));
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -457,6 +574,8 @@ static ssize_t dwc3_link_state_write(struct file *file,
|
||||
unsigned long flags;
|
||||
enum dwc3_link_state state = 0;
|
||||
char buf[32];
|
||||
u32 reg;
|
||||
u8 speed;
|
||||
|
||||
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
|
||||
return -EFAULT;
|
||||
@ -477,6 +596,21 @@ static ssize_t dwc3_link_state_write(struct file *file,
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GSTS);
|
||||
if (DWC3_GSTS_CURMOD(reg) != DWC3_GSTS_CURMOD_DEVICE) {
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
||||
speed = reg & DWC3_DSTS_CONNECTSPD;
|
||||
|
||||
if (speed < DWC3_DSTS_SUPERSPEED &&
|
||||
state != DWC3_LINK_STATE_RECOV) {
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dwc3_gadget_set_link_state(dwc, state);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
@ -496,7 +630,7 @@ struct dwc3_ep_file_map {
|
||||
const struct file_operations *const fops;
|
||||
};
|
||||
|
||||
static int dwc3_tx_fifo_queue_show(struct seq_file *s, void *unused)
|
||||
static int dwc3_tx_fifo_size_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct dwc3_ep *dep = s->private;
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
@ -504,14 +638,18 @@ static int dwc3_tx_fifo_queue_show(struct seq_file *s, void *unused)
|
||||
u32 val;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
val = dwc3_core_fifo_space(dep, DWC3_TXFIFOQ);
|
||||
val = dwc3_core_fifo_space(dep, DWC3_TXFIFO);
|
||||
|
||||
/* Convert to bytes */
|
||||
val *= DWC3_MDWIDTH(dwc->hwparams.hwparams0);
|
||||
val >>= 3;
|
||||
seq_printf(s, "%u\n", val);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_rx_fifo_queue_show(struct seq_file *s, void *unused)
|
||||
static int dwc3_rx_fifo_size_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct dwc3_ep *dep = s->private;
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
@ -519,7 +657,11 @@ static int dwc3_rx_fifo_queue_show(struct seq_file *s, void *unused)
|
||||
u32 val;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
val = dwc3_core_fifo_space(dep, DWC3_RXFIFOQ);
|
||||
val = dwc3_core_fifo_space(dep, DWC3_RXFIFO);
|
||||
|
||||
/* Convert to bytes */
|
||||
val *= DWC3_MDWIDTH(dwc->hwparams.hwparams0);
|
||||
val >>= 3;
|
||||
seq_printf(s, "%u\n", val);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
@ -675,8 +817,32 @@ static int dwc3_trb_ring_show(struct seq_file *s, void *unused)
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_SHOW_ATTRIBUTE(dwc3_tx_fifo_queue);
|
||||
DEFINE_SHOW_ATTRIBUTE(dwc3_rx_fifo_queue);
|
||||
static int dwc3_ep_info_register_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct dwc3_ep *dep = s->private;
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
unsigned long flags;
|
||||
u64 ep_info;
|
||||
u32 lower_32_bits;
|
||||
u32 upper_32_bits;
|
||||
u32 reg;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
reg = DWC3_GDBGLSPMUX_EPSELECT(dep->number);
|
||||
dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg);
|
||||
|
||||
lower_32_bits = dwc3_readl(dwc->regs, DWC3_GDBGEPINFO0);
|
||||
upper_32_bits = dwc3_readl(dwc->regs, DWC3_GDBGEPINFO1);
|
||||
|
||||
ep_info = ((u64)upper_32_bits << 32) | lower_32_bits;
|
||||
seq_printf(s, "0x%016llx\n", ep_info);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_SHOW_ATTRIBUTE(dwc3_tx_fifo_size);
|
||||
DEFINE_SHOW_ATTRIBUTE(dwc3_rx_fifo_size);
|
||||
DEFINE_SHOW_ATTRIBUTE(dwc3_tx_request_queue);
|
||||
DEFINE_SHOW_ATTRIBUTE(dwc3_rx_request_queue);
|
||||
DEFINE_SHOW_ATTRIBUTE(dwc3_rx_info_queue);
|
||||
@ -684,10 +850,11 @@ DEFINE_SHOW_ATTRIBUTE(dwc3_descriptor_fetch_queue);
|
||||
DEFINE_SHOW_ATTRIBUTE(dwc3_event_queue);
|
||||
DEFINE_SHOW_ATTRIBUTE(dwc3_transfer_type);
|
||||
DEFINE_SHOW_ATTRIBUTE(dwc3_trb_ring);
|
||||
DEFINE_SHOW_ATTRIBUTE(dwc3_ep_info_register);
|
||||
|
||||
static const struct dwc3_ep_file_map dwc3_ep_file_map[] = {
|
||||
{ "tx_fifo_queue", &dwc3_tx_fifo_queue_fops, },
|
||||
{ "rx_fifo_queue", &dwc3_rx_fifo_queue_fops, },
|
||||
{ "tx_fifo_size", &dwc3_tx_fifo_size_fops, },
|
||||
{ "rx_fifo_size", &dwc3_rx_fifo_size_fops, },
|
||||
{ "tx_request_queue", &dwc3_tx_request_queue_fops, },
|
||||
{ "rx_request_queue", &dwc3_rx_request_queue_fops, },
|
||||
{ "rx_info_queue", &dwc3_rx_info_queue_fops, },
|
||||
@ -695,6 +862,7 @@ static const struct dwc3_ep_file_map dwc3_ep_file_map[] = {
|
||||
{ "event_queue", &dwc3_event_queue_fops, },
|
||||
{ "transfer_type", &dwc3_transfer_type_fops, },
|
||||
{ "trb_ring", &dwc3_trb_ring_fops, },
|
||||
{ "GDBGEPINFO", &dwc3_ep_info_register_fops, },
|
||||
};
|
||||
|
||||
static void dwc3_debugfs_create_endpoint_files(struct dwc3_ep *dep,
|
||||
@ -742,6 +910,8 @@ void dwc3_debugfs_init(struct dwc3 *dwc)
|
||||
if (!dwc->regset)
|
||||
return;
|
||||
|
||||
dwc->dbg_lsp_select = DWC3_LSP_MUX_UNSELECTED;
|
||||
|
||||
dwc->regset->regs = dwc3_regs;
|
||||
dwc->regset->nregs = ARRAY_SIZE(dwc3_regs);
|
||||
dwc->regset->base = dwc->regs - DWC3_GLOBALS_REGS_START;
|
||||
@ -751,6 +921,9 @@ void dwc3_debugfs_init(struct dwc3 *dwc)
|
||||
|
||||
debugfs_create_regset32("regdump", S_IRUGO, root, dwc->regset);
|
||||
|
||||
debugfs_create_file("lsp_dump", S_IRUGO | S_IWUSR, root, dwc,
|
||||
&dwc3_lsp_fops);
|
||||
|
||||
if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)) {
|
||||
debugfs_create_file("mode", S_IRUGO | S_IWUSR, root, dwc,
|
||||
&dwc3_mode_fops);
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <linux/extcon.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "core.h"
|
||||
@ -445,9 +446,19 @@ static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc)
|
||||
struct device *dev = dwc->dev;
|
||||
struct device_node *np_phy, *np_conn;
|
||||
struct extcon_dev *edev;
|
||||
const char *name;
|
||||
|
||||
if (of_property_read_bool(dev->of_node, "extcon"))
|
||||
return extcon_get_edev_by_phandle(dwc->dev, 0);
|
||||
if (device_property_read_bool(dev, "extcon"))
|
||||
return extcon_get_edev_by_phandle(dev, 0);
|
||||
|
||||
/*
|
||||
* Device tree platforms should get extcon via phandle.
|
||||
* On ACPI platforms, we get the name from a device property.
|
||||
* This device property is for kernel internal use only and
|
||||
* is expected to be set by the glue code.
|
||||
*/
|
||||
if (device_property_read_string(dev, "linux,extcon-name", &name) == 0)
|
||||
return extcon_get_extcon_dev(name);
|
||||
|
||||
np_phy = of_parse_phandle(dev->of_node, "phys", 0);
|
||||
np_conn = of_graph_get_remote_node(np_phy, -1, -1);
|
||||
|
@ -170,20 +170,20 @@ static int dwc3_pci_quirks(struct dwc3_pci *dwc)
|
||||
* put the gpio descriptors again here because the phy driver
|
||||
* might want to grab them, too.
|
||||
*/
|
||||
gpio = devm_gpiod_get_optional(&pdev->dev, "cs",
|
||||
GPIOD_OUT_LOW);
|
||||
gpio = gpiod_get_optional(&pdev->dev, "cs", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(gpio))
|
||||
return PTR_ERR(gpio);
|
||||
|
||||
gpiod_set_value_cansleep(gpio, 1);
|
||||
gpiod_put(gpio);
|
||||
|
||||
gpio = devm_gpiod_get_optional(&pdev->dev, "reset",
|
||||
GPIOD_OUT_LOW);
|
||||
gpio = gpiod_get_optional(&pdev->dev, "reset", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(gpio))
|
||||
return PTR_ERR(gpio);
|
||||
|
||||
if (gpio) {
|
||||
gpiod_set_value_cansleep(gpio, 1);
|
||||
gpiod_put(gpio);
|
||||
usleep_range(10000, 11000);
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@
|
||||
#include "gadget.h"
|
||||
#include "io.h"
|
||||
|
||||
#define DWC3_ALIGN_FRAME(d) (((d)->frame_number + (d)->interval) \
|
||||
#define DWC3_ALIGN_FRAME(d, n) (((d)->frame_number + ((d)->interval * (n))) \
|
||||
& ~((d)->interval - 1))
|
||||
|
||||
/**
|
||||
@ -647,8 +647,6 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
|
||||
reg |= DWC3_DALEPENA_EP(dep->number);
|
||||
dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
|
||||
|
||||
init_waitqueue_head(&dep->wait_end_transfer);
|
||||
|
||||
if (usb_endpoint_xfer_control(desc))
|
||||
goto out;
|
||||
|
||||
@ -672,7 +670,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
|
||||
* Issue StartTransfer here with no-op TRB so we can always rely on No
|
||||
* Response Update Transfer command.
|
||||
*/
|
||||
if (usb_endpoint_xfer_bulk(desc) ||
|
||||
if ((usb_endpoint_xfer_bulk(desc) && !dep->stream_capable) ||
|
||||
usb_endpoint_xfer_int(desc)) {
|
||||
struct dwc3_gadget_ep_cmd_params params;
|
||||
struct dwc3_trb *trb;
|
||||
@ -919,8 +917,6 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
|
||||
struct usb_gadget *gadget = &dwc->gadget;
|
||||
enum usb_device_speed speed = gadget->speed;
|
||||
|
||||
dwc3_ep_inc_enq(dep);
|
||||
|
||||
trb->size = DWC3_TRB_SIZE_LENGTH(length);
|
||||
trb->bpl = lower_32_bits(dma);
|
||||
trb->bph = upper_32_bits(dma);
|
||||
@ -990,16 +986,20 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
|
||||
usb_endpoint_type(dep->endpoint.desc));
|
||||
}
|
||||
|
||||
/* always enable Continue on Short Packet */
|
||||
/*
|
||||
* Enable Continue on Short Packet
|
||||
* when endpoint is not a stream capable
|
||||
*/
|
||||
if (usb_endpoint_dir_out(dep->endpoint.desc)) {
|
||||
trb->ctrl |= DWC3_TRB_CTRL_CSP;
|
||||
if (!dep->stream_capable)
|
||||
trb->ctrl |= DWC3_TRB_CTRL_CSP;
|
||||
|
||||
if (short_not_ok)
|
||||
trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
|
||||
}
|
||||
|
||||
if ((!no_interrupt && !chain) ||
|
||||
(dwc3_calc_trbs_left(dep) == 0))
|
||||
(dwc3_calc_trbs_left(dep) == 1))
|
||||
trb->ctrl |= DWC3_TRB_CTRL_IOC;
|
||||
|
||||
if (chain)
|
||||
@ -1010,6 +1010,8 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
|
||||
|
||||
trb->ctrl |= DWC3_TRB_CTRL_HWO;
|
||||
|
||||
dwc3_ep_inc_enq(dep);
|
||||
|
||||
trace_dwc3_prepare_trb(dep, trb);
|
||||
}
|
||||
|
||||
@ -1046,6 +1048,8 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
|
||||
req->trb_dma = dwc3_trb_dma_offset(dep, trb);
|
||||
}
|
||||
|
||||
req->num_trbs++;
|
||||
|
||||
__dwc3_prepare_one_trb(dep, trb, dma, length, chain, node,
|
||||
stream_id, short_not_ok, no_interrupt);
|
||||
}
|
||||
@ -1073,13 +1077,14 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
struct dwc3_trb *trb;
|
||||
|
||||
req->unaligned = true;
|
||||
req->needs_extra_trb = true;
|
||||
|
||||
/* prepare normal TRB */
|
||||
dwc3_prepare_one_trb(dep, req, true, i);
|
||||
|
||||
/* Now prepare one extra TRB to align transfer size */
|
||||
trb = &dep->trb_pool[dep->trb_enqueue];
|
||||
req->num_trbs++;
|
||||
__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr,
|
||||
maxp - rem, false, 1,
|
||||
req->request.stream_id,
|
||||
@ -1117,13 +1122,14 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
struct dwc3_trb *trb;
|
||||
|
||||
req->unaligned = true;
|
||||
req->needs_extra_trb = true;
|
||||
|
||||
/* prepare normal TRB */
|
||||
dwc3_prepare_one_trb(dep, req, true, 0);
|
||||
|
||||
/* Now prepare one extra TRB to align transfer size */
|
||||
trb = &dep->trb_pool[dep->trb_enqueue];
|
||||
req->num_trbs++;
|
||||
__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp - rem,
|
||||
false, 1, req->request.stream_id,
|
||||
req->request.short_not_ok,
|
||||
@ -1133,13 +1139,14 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
struct dwc3_trb *trb;
|
||||
|
||||
req->zero = true;
|
||||
req->needs_extra_trb = true;
|
||||
|
||||
/* prepare normal TRB */
|
||||
dwc3_prepare_one_trb(dep, req, true, 0);
|
||||
|
||||
/* Now prepare one extra TRB to handle ZLP */
|
||||
trb = &dep->trb_pool[dep->trb_enqueue];
|
||||
req->num_trbs++;
|
||||
__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0,
|
||||
false, 1, req->request.stream_id,
|
||||
req->request.short_not_ok,
|
||||
@ -1232,6 +1239,9 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
|
||||
params.param1 = lower_32_bits(req->trb_dma);
|
||||
cmd = DWC3_DEPCMD_STARTTRANSFER;
|
||||
|
||||
if (dep->stream_capable)
|
||||
cmd |= DWC3_DEPCMD_PARAM(req->request.stream_id);
|
||||
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc))
|
||||
cmd |= DWC3_DEPCMD_PARAM(dep->frame_number);
|
||||
} else {
|
||||
@ -1263,17 +1273,151 @@ static int __dwc3_gadget_get_frame(struct dwc3 *dwc)
|
||||
return DWC3_DSTS_SOFFN(reg);
|
||||
}
|
||||
|
||||
static void __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
|
||||
/**
|
||||
* dwc3_gadget_start_isoc_quirk - workaround invalid frame number
|
||||
* @dep: isoc endpoint
|
||||
*
|
||||
* This function tests for the correct combination of BIT[15:14] from the 16-bit
|
||||
* microframe number reported by the XferNotReady event for the future frame
|
||||
* number to start the isoc transfer.
|
||||
*
|
||||
* In DWC_usb31 version 1.70a-ea06 and prior, for highspeed and fullspeed
|
||||
* isochronous IN, BIT[15:14] of the 16-bit microframe number reported by the
|
||||
* XferNotReady event are invalid. The driver uses this number to schedule the
|
||||
* isochronous transfer and passes it to the START TRANSFER command. Because
|
||||
* this number is invalid, the command may fail. If BIT[15:14] matches the
|
||||
* internal 16-bit microframe, the START TRANSFER command will pass and the
|
||||
* transfer will start at the scheduled time, if it is off by 1, the command
|
||||
* will still pass, but the transfer will start 2 seconds in the future. For all
|
||||
* other conditions, the START TRANSFER command will fail with bus-expiry.
|
||||
*
|
||||
* In order to workaround this issue, we can test for the correct combination of
|
||||
* BIT[15:14] by sending START TRANSFER commands with different values of
|
||||
* BIT[15:14]: 'b00, 'b01, 'b10, and 'b11. Each combination is 2^14 uframe apart
|
||||
* (or 2 seconds). 4 seconds into the future will result in a bus-expiry status.
|
||||
* As the result, within the 4 possible combinations for BIT[15:14], there will
|
||||
* be 2 successful and 2 failure START COMMAND status. One of the 2 successful
|
||||
* command status will result in a 2-second delay start. The smaller BIT[15:14]
|
||||
* value is the correct combination.
|
||||
*
|
||||
* Since there are only 4 outcomes and the results are ordered, we can simply
|
||||
* test 2 START TRANSFER commands with BIT[15:14] combinations 'b00 and 'b01 to
|
||||
* deduce the smaller successful combination.
|
||||
*
|
||||
* Let test0 = test status for combination 'b00 and test1 = test status for 'b01
|
||||
* of BIT[15:14]. The correct combination is as follow:
|
||||
*
|
||||
* if test0 fails and test1 passes, BIT[15:14] is 'b01
|
||||
* if test0 fails and test1 fails, BIT[15:14] is 'b10
|
||||
* if test0 passes and test1 fails, BIT[15:14] is 'b11
|
||||
* if test0 passes and test1 passes, BIT[15:14] is 'b00
|
||||
*
|
||||
* Synopsys STAR 9001202023: Wrong microframe number for isochronous IN
|
||||
* endpoints.
|
||||
*/
|
||||
static int dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep)
|
||||
{
|
||||
if (list_empty(&dep->pending_list)) {
|
||||
dev_info(dep->dwc->dev, "%s: ran out of requests\n",
|
||||
dep->name);
|
||||
dep->flags |= DWC3_EP_PENDING_REQUEST;
|
||||
return;
|
||||
int cmd_status = 0;
|
||||
bool test0;
|
||||
bool test1;
|
||||
|
||||
while (dep->combo_num < 2) {
|
||||
struct dwc3_gadget_ep_cmd_params params;
|
||||
u32 test_frame_number;
|
||||
u32 cmd;
|
||||
|
||||
/*
|
||||
* Check if we can start isoc transfer on the next interval or
|
||||
* 4 uframes in the future with BIT[15:14] as dep->combo_num
|
||||
*/
|
||||
test_frame_number = dep->frame_number & 0x3fff;
|
||||
test_frame_number |= dep->combo_num << 14;
|
||||
test_frame_number += max_t(u32, 4, dep->interval);
|
||||
|
||||
params.param0 = upper_32_bits(dep->dwc->bounce_addr);
|
||||
params.param1 = lower_32_bits(dep->dwc->bounce_addr);
|
||||
|
||||
cmd = DWC3_DEPCMD_STARTTRANSFER;
|
||||
cmd |= DWC3_DEPCMD_PARAM(test_frame_number);
|
||||
cmd_status = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
|
||||
|
||||
/* Redo if some other failure beside bus-expiry is received */
|
||||
if (cmd_status && cmd_status != -EAGAIN) {
|
||||
dep->start_cmd_status = 0;
|
||||
dep->combo_num = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Store the first test status */
|
||||
if (dep->combo_num == 0)
|
||||
dep->start_cmd_status = cmd_status;
|
||||
|
||||
dep->combo_num++;
|
||||
|
||||
/*
|
||||
* End the transfer if the START_TRANSFER command is successful
|
||||
* to wait for the next XferNotReady to test the command again
|
||||
*/
|
||||
if (cmd_status == 0) {
|
||||
dwc3_stop_active_transfer(dep, true);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
dep->frame_number = DWC3_ALIGN_FRAME(dep);
|
||||
__dwc3_gadget_kick_transfer(dep);
|
||||
/* test0 and test1 are both completed at this point */
|
||||
test0 = (dep->start_cmd_status == 0);
|
||||
test1 = (cmd_status == 0);
|
||||
|
||||
if (!test0 && test1)
|
||||
dep->combo_num = 1;
|
||||
else if (!test0 && !test1)
|
||||
dep->combo_num = 2;
|
||||
else if (test0 && !test1)
|
||||
dep->combo_num = 3;
|
||||
else if (test0 && test1)
|
||||
dep->combo_num = 0;
|
||||
|
||||
dep->frame_number &= 0x3fff;
|
||||
dep->frame_number |= dep->combo_num << 14;
|
||||
dep->frame_number += max_t(u32, 4, dep->interval);
|
||||
|
||||
/* Reinitialize test variables */
|
||||
dep->start_cmd_status = 0;
|
||||
dep->combo_num = 0;
|
||||
|
||||
return __dwc3_gadget_kick_transfer(dep);
|
||||
}
|
||||
|
||||
static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
|
||||
{
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
if (list_empty(&dep->pending_list)) {
|
||||
dep->flags |= DWC3_EP_PENDING_REQUEST;
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (!dwc->dis_start_transfer_quirk && dwc3_is_usb31(dwc) &&
|
||||
(dwc->revision <= DWC3_USB31_REVISION_160A ||
|
||||
(dwc->revision == DWC3_USB31_REVISION_170A &&
|
||||
dwc->version_type >= DWC31_VERSIONTYPE_EA01 &&
|
||||
dwc->version_type <= DWC31_VERSIONTYPE_EA06))) {
|
||||
|
||||
if (dwc->gadget.speed <= USB_SPEED_HIGH && dep->direction)
|
||||
return dwc3_gadget_start_isoc_quirk(dep);
|
||||
}
|
||||
|
||||
for (i = 0; i < DWC3_ISOC_MAX_RETRIES; i++) {
|
||||
dep->frame_number = DWC3_ALIGN_FRAME(dep, i + 1);
|
||||
|
||||
ret = __dwc3_gadget_kick_transfer(dep);
|
||||
if (ret != -EAGAIN)
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
||||
@ -1314,8 +1458,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
||||
|
||||
if ((dep->flags & DWC3_EP_PENDING_REQUEST)) {
|
||||
if (!(dep->flags & DWC3_EP_TRANSFER_STARTED)) {
|
||||
__dwc3_gadget_start_isoc(dep);
|
||||
return 0;
|
||||
return __dwc3_gadget_start_isoc(dep);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1341,6 +1484,40 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, struct dwc3_request *req)
|
||||
{
|
||||
int i;
|
||||
|
||||
/*
|
||||
* If request was already started, this means we had to
|
||||
* stop the transfer. With that we also need to ignore
|
||||
* all TRBs used by the request, however TRBs can only
|
||||
* be modified after completion of END_TRANSFER
|
||||
* command. So what we do here is that we wait for
|
||||
* END_TRANSFER completion and only after that, we jump
|
||||
* over TRBs by clearing HWO and incrementing dequeue
|
||||
* pointer.
|
||||
*/
|
||||
for (i = 0; i < req->num_trbs; i++) {
|
||||
struct dwc3_trb *trb;
|
||||
|
||||
trb = req->trb + i;
|
||||
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
|
||||
dwc3_ep_inc_deq(dep);
|
||||
}
|
||||
}
|
||||
|
||||
static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep)
|
||||
{
|
||||
struct dwc3_request *req;
|
||||
struct dwc3_request *tmp;
|
||||
|
||||
list_for_each_entry_safe(req, tmp, &dep->cancelled_list, list) {
|
||||
dwc3_gadget_ep_skip_trbs(dep, req);
|
||||
dwc3_gadget_giveback(dep, req, -ECONNRESET);
|
||||
}
|
||||
}
|
||||
|
||||
static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
|
||||
struct usb_request *request)
|
||||
{
|
||||
@ -1371,68 +1548,11 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
|
||||
/* wait until it is processed */
|
||||
dwc3_stop_active_transfer(dep, true);
|
||||
|
||||
/*
|
||||
* If request was already started, this means we had to
|
||||
* stop the transfer. With that we also need to ignore
|
||||
* all TRBs used by the request, however TRBs can only
|
||||
* be modified after completion of END_TRANSFER
|
||||
* command. So what we do here is that we wait for
|
||||
* END_TRANSFER completion and only after that, we jump
|
||||
* over TRBs by clearing HWO and incrementing dequeue
|
||||
* pointer.
|
||||
*
|
||||
* Note that we have 2 possible types of transfers here:
|
||||
*
|
||||
* i) Linear buffer request
|
||||
* ii) SG-list based request
|
||||
*
|
||||
* SG-list based requests will have r->num_pending_sgs
|
||||
* set to a valid number (> 0). Linear requests,
|
||||
* normally use a single TRB.
|
||||
*
|
||||
* For each of these two cases, if r->unaligned flag is
|
||||
* set, one extra TRB has been used to align transfer
|
||||
* size to wMaxPacketSize.
|
||||
*
|
||||
* All of these cases need to be taken into
|
||||
* consideration so we don't mess up our TRB ring
|
||||
* pointers.
|
||||
*/
|
||||
wait_event_lock_irq(dep->wait_end_transfer,
|
||||
!(dep->flags & DWC3_EP_END_TRANSFER_PENDING),
|
||||
dwc->lock);
|
||||
|
||||
if (!r->trb)
|
||||
goto out0;
|
||||
|
||||
if (r->num_pending_sgs) {
|
||||
struct dwc3_trb *trb;
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < r->num_pending_sgs; i++) {
|
||||
trb = r->trb + i;
|
||||
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
|
||||
dwc3_ep_inc_deq(dep);
|
||||
}
|
||||
|
||||
if (r->unaligned || r->zero) {
|
||||
trb = r->trb + r->num_pending_sgs + 1;
|
||||
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
|
||||
dwc3_ep_inc_deq(dep);
|
||||
}
|
||||
} else {
|
||||
struct dwc3_trb *trb = r->trb;
|
||||
|
||||
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
|
||||
dwc3_ep_inc_deq(dep);
|
||||
|
||||
if (r->unaligned || r->zero) {
|
||||
trb = r->trb + 1;
|
||||
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
|
||||
dwc3_ep_inc_deq(dep);
|
||||
}
|
||||
}
|
||||
goto out1;
|
||||
dwc3_gadget_move_cancelled_request(req);
|
||||
goto out0;
|
||||
}
|
||||
dev_err(dwc->dev, "request %pK was not queued to %s\n",
|
||||
request, ep->name);
|
||||
@ -1440,9 +1560,6 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
|
||||
goto out0;
|
||||
}
|
||||
|
||||
out1:
|
||||
/* giveback the request */
|
||||
|
||||
dwc3_gadget_giveback(dep, req, -ECONNRESET);
|
||||
|
||||
out0:
|
||||
@ -1934,8 +2051,6 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
|
||||
{
|
||||
struct dwc3 *dwc = gadget_to_dwc(g);
|
||||
unsigned long flags;
|
||||
int epnum;
|
||||
u32 tmo_eps = 0;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
|
||||
@ -1944,36 +2059,6 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
|
||||
|
||||
__dwc3_gadget_stop(dwc);
|
||||
|
||||
for (epnum = 2; epnum < DWC3_ENDPOINTS_NUM; epnum++) {
|
||||
struct dwc3_ep *dep = dwc->eps[epnum];
|
||||
int ret;
|
||||
|
||||
if (!dep)
|
||||
continue;
|
||||
|
||||
if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING))
|
||||
continue;
|
||||
|
||||
ret = wait_event_interruptible_lock_irq_timeout(dep->wait_end_transfer,
|
||||
!(dep->flags & DWC3_EP_END_TRANSFER_PENDING),
|
||||
dwc->lock, msecs_to_jiffies(5));
|
||||
|
||||
if (ret <= 0) {
|
||||
/* Timed out or interrupted! There's nothing much
|
||||
* we can do so we just log here and print which
|
||||
* endpoints timed out at the end.
|
||||
*/
|
||||
tmo_eps |= 1 << epnum;
|
||||
dep->flags &= DWC3_EP_END_TRANSFER_PENDING;
|
||||
}
|
||||
}
|
||||
|
||||
if (tmo_eps) {
|
||||
dev_err(dwc->dev,
|
||||
"end transfer timed out on endpoints 0x%x [bitmap]\n",
|
||||
tmo_eps);
|
||||
}
|
||||
|
||||
out:
|
||||
dwc->gadget_driver = NULL;
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
@ -2148,6 +2233,8 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
|
||||
dep->direction = direction;
|
||||
dep->regs = dwc->regs + DWC3_DEP_BASE(epnum);
|
||||
dwc->eps[epnum] = dep;
|
||||
dep->combo_num = 0;
|
||||
dep->start_cmd_status = 0;
|
||||
|
||||
snprintf(dep->name, sizeof(dep->name), "ep%u%s", num,
|
||||
direction ? "in" : "out");
|
||||
@ -2176,6 +2263,7 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
|
||||
|
||||
INIT_LIST_HEAD(&dep->pending_list);
|
||||
INIT_LIST_HEAD(&dep->started_list);
|
||||
INIT_LIST_HEAD(&dep->cancelled_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2235,6 +2323,7 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep,
|
||||
dwc3_ep_inc_deq(dep);
|
||||
|
||||
trace_dwc3_complete_trb(dep, trb);
|
||||
req->num_trbs--;
|
||||
|
||||
/*
|
||||
* If we're in the middle of series of chained TRBs and we
|
||||
@ -2249,12 +2338,26 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep,
|
||||
if (chain && (trb->ctrl & DWC3_TRB_CTRL_HWO))
|
||||
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
|
||||
|
||||
/*
|
||||
* For isochronous transfers, the first TRB in a service interval must
|
||||
* have the Isoc-First type. Track and report its interval frame number.
|
||||
*/
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
|
||||
(trb->ctrl & DWC3_TRBCTL_ISOCHRONOUS_FIRST)) {
|
||||
unsigned int frame_number;
|
||||
|
||||
frame_number = DWC3_TRB_CTRL_GET_SID_SOFN(trb->ctrl);
|
||||
frame_number &= ~(dep->interval - 1);
|
||||
req->request.frame_number = frame_number;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we're dealing with unaligned size OUT transfer, we will be left
|
||||
* with one TRB pending in the ring. We need to manually clear HWO bit
|
||||
* from that TRB.
|
||||
*/
|
||||
if ((req->zero || req->unaligned) && !(trb->ctrl & DWC3_TRB_CTRL_CHN)) {
|
||||
|
||||
if (req->needs_extra_trb && !(trb->ctrl & DWC3_TRB_CTRL_CHN)) {
|
||||
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
|
||||
return 1;
|
||||
}
|
||||
@ -2331,11 +2434,10 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
|
||||
ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event,
|
||||
status);
|
||||
|
||||
if (req->unaligned || req->zero) {
|
||||
if (req->needs_extra_trb) {
|
||||
ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event,
|
||||
status);
|
||||
req->unaligned = false;
|
||||
req->zero = false;
|
||||
req->needs_extra_trb = false;
|
||||
}
|
||||
|
||||
req->request.actual = req->request.length - req->remaining;
|
||||
@ -2430,7 +2532,7 @@ static void dwc3_gadget_endpoint_transfer_not_ready(struct dwc3_ep *dep,
|
||||
const struct dwc3_event_depevt *event)
|
||||
{
|
||||
dwc3_gadget_endpoint_frame_from_event(dep, event);
|
||||
__dwc3_gadget_start_isoc(dep);
|
||||
(void) __dwc3_gadget_start_isoc(dep);
|
||||
}
|
||||
|
||||
static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
|
||||
@ -2468,7 +2570,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
|
||||
|
||||
if (cmd == DWC3_DEPCMD_ENDTRANSFER) {
|
||||
dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING;
|
||||
wake_up(&dep->wait_end_transfer);
|
||||
dwc3_gadget_ep_cleanup_cancelled_requests(dep);
|
||||
}
|
||||
break;
|
||||
case DWC3_DEPEVT_STREAMEVT:
|
||||
|
@ -79,6 +79,21 @@ static inline void dwc3_gadget_move_started_request(struct dwc3_request *req)
|
||||
list_move_tail(&req->list, &dep->started_list);
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_gadget_move_cancelled_request - move @req to the cancelled_list
|
||||
* @req: the request to be moved
|
||||
*
|
||||
* Caller should take care of locking. This function will move @req from its
|
||||
* current list to the endpoint's cancelled_list.
|
||||
*/
|
||||
static inline void dwc3_gadget_move_cancelled_request(struct dwc3_request *req)
|
||||
{
|
||||
struct dwc3_ep *dep = req->dep;
|
||||
|
||||
req->started = false;
|
||||
list_move_tail(&req->list, &dep->cancelled_list);
|
||||
}
|
||||
|
||||
void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
|
||||
int status);
|
||||
|
||||
|
@ -46,7 +46,7 @@ static int dwc3_host_get_irq(struct dwc3 *dwc)
|
||||
|
||||
int dwc3_host_init(struct dwc3 *dwc)
|
||||
{
|
||||
struct property_entry props[3];
|
||||
struct property_entry props[4];
|
||||
struct platform_device *xhci;
|
||||
int ret, irq;
|
||||
struct resource *res;
|
||||
@ -93,6 +93,9 @@ int dwc3_host_init(struct dwc3 *dwc)
|
||||
if (dwc->usb3_lpm_capable)
|
||||
props[prop_idx++].name = "usb3-lpm-capable";
|
||||
|
||||
if (dwc->usb2_lpm_disable)
|
||||
props[prop_idx++].name = "usb2-lpm-disable";
|
||||
|
||||
/**
|
||||
* WORKAROUND: dwc3 revisions <=3.00a have a limitation
|
||||
* where Port Disable command doesn't work.
|
||||
|
@ -199,7 +199,7 @@ DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd,
|
||||
__entry->param2 = params->param2;
|
||||
__entry->cmd_status = cmd_status;
|
||||
),
|
||||
TP_printk("%s: cmd '%s' [%d] params %08x %08x %08x --> status: %s",
|
||||
TP_printk("%s: cmd '%s' [%x] params %08x %08x %08x --> status: %s",
|
||||
__get_str(name), dwc3_gadget_ep_cmd_string(__entry->cmd),
|
||||
__entry->cmd, __entry->param0,
|
||||
__entry->param1, __entry->param2,
|
||||
@ -251,9 +251,11 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
|
||||
s = "2x ";
|
||||
break;
|
||||
case 3:
|
||||
default:
|
||||
s = "3x ";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
s = "";
|
||||
} s; }),
|
||||
|
@ -631,28 +631,28 @@ static int ehci_reset_port(int port)
|
||||
if (!(portsc & PORT_RESET))
|
||||
break;
|
||||
}
|
||||
if (portsc & PORT_RESET) {
|
||||
/* force reset to complete */
|
||||
loop = 100 * 1000;
|
||||
writel(portsc & ~(PORT_RWC_BITS | PORT_RESET),
|
||||
&ehci_regs->port_status[port - 1]);
|
||||
do {
|
||||
udelay(1);
|
||||
portsc = readl(&ehci_regs->port_status[port-1]);
|
||||
} while ((portsc & PORT_RESET) && (--loop > 0));
|
||||
}
|
||||
if (portsc & PORT_RESET) {
|
||||
/* force reset to complete */
|
||||
loop = 100 * 1000;
|
||||
writel(portsc & ~(PORT_RWC_BITS | PORT_RESET),
|
||||
&ehci_regs->port_status[port - 1]);
|
||||
do {
|
||||
udelay(1);
|
||||
portsc = readl(&ehci_regs->port_status[port-1]);
|
||||
} while ((portsc & PORT_RESET) && (--loop > 0));
|
||||
}
|
||||
|
||||
/* Device went away? */
|
||||
if (!(portsc & PORT_CONNECT))
|
||||
return -ENOTCONN;
|
||||
/* Device went away? */
|
||||
if (!(portsc & PORT_CONNECT))
|
||||
return -ENOTCONN;
|
||||
|
||||
/* bomb out completely if something weird happened */
|
||||
if ((portsc & PORT_CSC))
|
||||
return -EINVAL;
|
||||
/* bomb out completely if something weird happened */
|
||||
if ((portsc & PORT_CSC))
|
||||
return -EINVAL;
|
||||
|
||||
/* If we've finished resetting, then break out of the loop */
|
||||
if (!(portsc & PORT_RESET) && (portsc & PORT_PE))
|
||||
return 0;
|
||||
/* If we've finished resetting, then break out of the loop */
|
||||
if (!(portsc & PORT_RESET) && (portsc & PORT_PE))
|
||||
return 0;
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
|
@ -18,11 +18,15 @@
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/sched/signal.h>
|
||||
#include <linux/uio.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include <linux/usb/ccid.h>
|
||||
#include <linux/usb/composite.h>
|
||||
#include <linux/usb/functionfs.h>
|
||||
|
||||
@ -218,6 +222,8 @@ struct ffs_io_data {
|
||||
|
||||
struct usb_ep *ep;
|
||||
struct usb_request *req;
|
||||
struct sg_table sgt;
|
||||
bool use_sg;
|
||||
|
||||
struct ffs_data *ffs;
|
||||
};
|
||||
@ -749,6 +755,65 @@ static ssize_t ffs_copy_to_iter(void *data, int data_len, struct iov_iter *iter)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* allocate a virtually contiguous buffer and create a scatterlist describing it
|
||||
* @sg_table - pointer to a place to be filled with sg_table contents
|
||||
* @size - required buffer size
|
||||
*/
|
||||
static void *ffs_build_sg_list(struct sg_table *sgt, size_t sz)
|
||||
{
|
||||
struct page **pages;
|
||||
void *vaddr, *ptr;
|
||||
unsigned int n_pages;
|
||||
int i;
|
||||
|
||||
vaddr = vmalloc(sz);
|
||||
if (!vaddr)
|
||||
return NULL;
|
||||
|
||||
n_pages = PAGE_ALIGN(sz) >> PAGE_SHIFT;
|
||||
pages = kvmalloc_array(n_pages, sizeof(struct page *), GFP_KERNEL);
|
||||
if (!pages) {
|
||||
vfree(vaddr);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
for (i = 0, ptr = vaddr; i < n_pages; ++i, ptr += PAGE_SIZE)
|
||||
pages[i] = vmalloc_to_page(ptr);
|
||||
|
||||
if (sg_alloc_table_from_pages(sgt, pages, n_pages, 0, sz, GFP_KERNEL)) {
|
||||
kvfree(pages);
|
||||
vfree(vaddr);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
kvfree(pages);
|
||||
|
||||
return vaddr;
|
||||
}
|
||||
|
||||
static inline void *ffs_alloc_buffer(struct ffs_io_data *io_data,
|
||||
size_t data_len)
|
||||
{
|
||||
if (io_data->use_sg)
|
||||
return ffs_build_sg_list(&io_data->sgt, data_len);
|
||||
|
||||
return kmalloc(data_len, GFP_KERNEL);
|
||||
}
|
||||
|
||||
static inline void ffs_free_buffer(struct ffs_io_data *io_data)
|
||||
{
|
||||
if (!io_data->buf)
|
||||
return;
|
||||
|
||||
if (io_data->use_sg) {
|
||||
sg_free_table(&io_data->sgt);
|
||||
vfree(io_data->buf);
|
||||
} else {
|
||||
kfree(io_data->buf);
|
||||
}
|
||||
}
|
||||
|
||||
static void ffs_user_copy_worker(struct work_struct *work)
|
||||
{
|
||||
struct ffs_io_data *io_data = container_of(work, struct ffs_io_data,
|
||||
@ -776,7 +841,7 @@ static void ffs_user_copy_worker(struct work_struct *work)
|
||||
|
||||
if (io_data->read)
|
||||
kfree(io_data->to_free);
|
||||
kfree(io_data->buf);
|
||||
ffs_free_buffer(io_data);
|
||||
kfree(io_data);
|
||||
}
|
||||
|
||||
@ -932,6 +997,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
|
||||
* earlier
|
||||
*/
|
||||
gadget = epfile->ffs->gadget;
|
||||
io_data->use_sg = gadget->sg_supported && data_len > PAGE_SIZE;
|
||||
|
||||
spin_lock_irq(&epfile->ffs->eps_lock);
|
||||
/* In the meantime, endpoint got disabled or changed. */
|
||||
@ -948,7 +1014,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
|
||||
data_len = usb_ep_align_maybe(gadget, ep->ep, data_len);
|
||||
spin_unlock_irq(&epfile->ffs->eps_lock);
|
||||
|
||||
data = kmalloc(data_len, GFP_KERNEL);
|
||||
data = ffs_alloc_buffer(io_data, data_len);
|
||||
if (unlikely(!data)) {
|
||||
ret = -ENOMEM;
|
||||
goto error_mutex;
|
||||
@ -988,8 +1054,16 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
|
||||
bool interrupted = false;
|
||||
|
||||
req = ep->req;
|
||||
req->buf = data;
|
||||
req->length = data_len;
|
||||
if (io_data->use_sg) {
|
||||
req->buf = NULL;
|
||||
req->sg = io_data->sgt.sgl;
|
||||
req->num_sgs = io_data->sgt.nents;
|
||||
} else {
|
||||
req->buf = data;
|
||||
}
|
||||
req->length = data_len;
|
||||
|
||||
io_data->buf = data;
|
||||
|
||||
req->context = &done;
|
||||
req->complete = ffs_epfile_io_complete;
|
||||
@ -1022,8 +1096,14 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
|
||||
} else if (!(req = usb_ep_alloc_request(ep->ep, GFP_ATOMIC))) {
|
||||
ret = -ENOMEM;
|
||||
} else {
|
||||
req->buf = data;
|
||||
req->length = data_len;
|
||||
if (io_data->use_sg) {
|
||||
req->buf = NULL;
|
||||
req->sg = io_data->sgt.sgl;
|
||||
req->num_sgs = io_data->sgt.nents;
|
||||
} else {
|
||||
req->buf = data;
|
||||
}
|
||||
req->length = data_len;
|
||||
|
||||
io_data->buf = data;
|
||||
io_data->ep = ep->ep;
|
||||
@ -1052,7 +1132,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
|
||||
error_mutex:
|
||||
mutex_unlock(&epfile->mutex);
|
||||
error:
|
||||
kfree(data);
|
||||
ffs_free_buffer(io_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1926,7 +2006,7 @@ typedef int (*ffs_os_desc_callback)(enum ffs_os_desc_type entity,
|
||||
|
||||
static int __must_check ffs_do_single_desc(char *data, unsigned len,
|
||||
ffs_entity_callback entity,
|
||||
void *priv)
|
||||
void *priv, int *current_class)
|
||||
{
|
||||
struct usb_descriptor_header *_ds = (void *)data;
|
||||
u8 length;
|
||||
@ -1984,6 +2064,7 @@ static int __must_check ffs_do_single_desc(char *data, unsigned len,
|
||||
__entity(INTERFACE, ds->bInterfaceNumber);
|
||||
if (ds->iInterface)
|
||||
__entity(STRING, ds->iInterface);
|
||||
*current_class = ds->bInterfaceClass;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -1997,11 +2078,22 @@ static int __must_check ffs_do_single_desc(char *data, unsigned len,
|
||||
}
|
||||
break;
|
||||
|
||||
case HID_DT_HID:
|
||||
pr_vdebug("hid descriptor\n");
|
||||
if (length != sizeof(struct hid_descriptor))
|
||||
goto inv_length;
|
||||
break;
|
||||
case USB_TYPE_CLASS | 0x01:
|
||||
if (*current_class == USB_INTERFACE_CLASS_HID) {
|
||||
pr_vdebug("hid descriptor\n");
|
||||
if (length != sizeof(struct hid_descriptor))
|
||||
goto inv_length;
|
||||
break;
|
||||
} else if (*current_class == USB_INTERFACE_CLASS_CCID) {
|
||||
pr_vdebug("ccid descriptor\n");
|
||||
if (length != sizeof(struct ccid_descriptor))
|
||||
goto inv_length;
|
||||
break;
|
||||
} else {
|
||||
pr_vdebug("unknown descriptor: %d for class %d\n",
|
||||
_ds->bDescriptorType, *current_class);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
case USB_DT_OTG:
|
||||
if (length != sizeof(struct usb_otg_descriptor))
|
||||
@ -2058,6 +2150,7 @@ static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len,
|
||||
{
|
||||
const unsigned _len = len;
|
||||
unsigned long num = 0;
|
||||
int current_class = -1;
|
||||
|
||||
ENTER();
|
||||
|
||||
@ -2078,7 +2171,8 @@ static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len,
|
||||
if (!data)
|
||||
return _len - len;
|
||||
|
||||
ret = ffs_do_single_desc(data, len, entity, priv);
|
||||
ret = ffs_do_single_desc(data, len, entity, priv,
|
||||
¤t_class);
|
||||
if (unlikely(ret < 0)) {
|
||||
pr_debug("%s returns %d\n", __func__, ret);
|
||||
return ret;
|
||||
|
@ -102,7 +102,7 @@ static void uvc_buffer_queue(struct vb2_buffer *vb)
|
||||
spin_unlock_irqrestore(&queue->irqlock, flags);
|
||||
}
|
||||
|
||||
static struct vb2_ops uvc_queue_qops = {
|
||||
static const struct vb2_ops uvc_queue_qops = {
|
||||
.queue_setup = uvc_queue_setup,
|
||||
.buf_prepare = uvc_buffer_prepare,
|
||||
.buf_queue = uvc_buffer_queue,
|
||||
|
@ -438,7 +438,7 @@ static int ast_vhub_udc_stop(struct usb_gadget *gadget)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct usb_gadget_ops ast_vhub_udc_ops = {
|
||||
static const struct usb_gadget_ops ast_vhub_udc_ops = {
|
||||
.get_frame = ast_vhub_udc_get_frame,
|
||||
.wakeup = ast_vhub_udc_wakeup,
|
||||
.pullup = ast_vhub_udc_pullup,
|
||||
|
@ -1330,7 +1330,7 @@ static void pch_vbus_gpio_work_rise(struct work_struct *irq_work)
|
||||
}
|
||||
|
||||
/**
|
||||
* pch_vbus_gpio_irq() - IRQ handler for GPIO intrerrupt for changing VBUS
|
||||
* pch_vbus_gpio_irq() - IRQ handler for GPIO interrupt for changing VBUS
|
||||
* @irq: Interrupt request number
|
||||
* @dev: Reference to the device structure
|
||||
*
|
||||
|
@ -358,6 +358,7 @@ struct renesas_usb3 {
|
||||
bool extcon_host; /* check id and set EXTCON_USB_HOST */
|
||||
bool extcon_usb; /* check vbus and set EXTCON_USB */
|
||||
bool forced_b_device;
|
||||
bool start_to_connect;
|
||||
};
|
||||
|
||||
#define gadget_to_renesas_usb3(_gadget) \
|
||||
@ -476,7 +477,8 @@ static void usb3_init_axi_bridge(struct renesas_usb3 *usb3)
|
||||
static void usb3_init_epc_registers(struct renesas_usb3 *usb3)
|
||||
{
|
||||
usb3_write(usb3, ~0, USB3_USB_INT_STA_1);
|
||||
usb3_enable_irq_1(usb3, USB_INT_1_VBUS_CNG);
|
||||
if (!usb3->workaround_for_vbus)
|
||||
usb3_enable_irq_1(usb3, USB_INT_1_VBUS_CNG);
|
||||
}
|
||||
|
||||
static bool usb3_wakeup_usb2_phy(struct renesas_usb3 *usb3)
|
||||
@ -700,8 +702,7 @@ static void usb3_mode_config(struct renesas_usb3 *usb3, bool host, bool a_dev)
|
||||
usb3_set_mode_by_role_sw(usb3, host);
|
||||
usb3_vbus_out(usb3, a_dev);
|
||||
/* for A-Peripheral or forced B-device mode */
|
||||
if ((!host && a_dev) ||
|
||||
(usb3->workaround_for_vbus && usb3->forced_b_device))
|
||||
if ((!host && a_dev) || usb3->start_to_connect)
|
||||
usb3_connect(usb3);
|
||||
spin_unlock_irqrestore(&usb3->lock, flags);
|
||||
}
|
||||
@ -2432,7 +2433,11 @@ static ssize_t renesas_usb3_b_device_write(struct file *file,
|
||||
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
|
||||
return -EFAULT;
|
||||
|
||||
if (!strncmp(buf, "1", 1))
|
||||
usb3->start_to_connect = false;
|
||||
if (usb3->workaround_for_vbus && usb3->forced_b_device &&
|
||||
!strncmp(buf, "2", 1))
|
||||
usb3->start_to_connect = true;
|
||||
else if (!strncmp(buf, "1", 1))
|
||||
usb3->forced_b_device = true;
|
||||
else
|
||||
usb3->forced_b_device = false;
|
||||
@ -2440,7 +2445,7 @@ static ssize_t renesas_usb3_b_device_write(struct file *file,
|
||||
if (usb3->workaround_for_vbus)
|
||||
usb3_disconnect(usb3);
|
||||
|
||||
/* Let this driver call usb3_connect() anyway */
|
||||
/* Let this driver call usb3_connect() if needed */
|
||||
usb3_check_id(usb3);
|
||||
|
||||
return count;
|
||||
|
@ -119,7 +119,7 @@ static void dprintk(int level, const char *fmt, ...)
|
||||
}
|
||||
#endif
|
||||
|
||||
static int s3c2410_udc_debugfs_seq_show(struct seq_file *m, void *p)
|
||||
static int s3c2410_udc_debugfs_show(struct seq_file *m, void *p)
|
||||
{
|
||||
u32 addr_reg, pwr_reg, ep_int_reg, usb_int_reg;
|
||||
u32 ep_int_en_reg, usb_int_en_reg, ep0_csr;
|
||||
@ -168,20 +168,7 @@ static int s3c2410_udc_debugfs_seq_show(struct seq_file *m, void *p)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s3c2410_udc_debugfs_fops_open(struct inode *inode,
|
||||
struct file *file)
|
||||
{
|
||||
return single_open(file, s3c2410_udc_debugfs_seq_show, NULL);
|
||||
}
|
||||
|
||||
static const struct file_operations s3c2410_udc_debugfs_fops = {
|
||||
.open = s3c2410_udc_debugfs_fops_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
DEFINE_SHOW_ATTRIBUTE(s3c2410_udc_debugfs);
|
||||
|
||||
/* io macros */
|
||||
|
||||
|
@ -159,11 +159,12 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev)
|
||||
/* get the PHY device */
|
||||
phy = devm_usb_get_phy_by_phandle(dev, "phys", i);
|
||||
if (IS_ERR(phy)) {
|
||||
/* Don't bail out if PHY is not absolutely necessary */
|
||||
if (pdata->port_mode[i] != OMAP_EHCI_PORT_MODE_PHY)
|
||||
continue;
|
||||
|
||||
ret = PTR_ERR(phy);
|
||||
if (ret == -ENODEV) { /* no PHY */
|
||||
phy = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "Can't get PHY for port %d: %d\n",
|
||||
i, ret);
|
||||
|
@ -2159,25 +2159,15 @@ static int isp1362_show(struct seq_file *s, void *unused)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int isp1362_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, isp1362_show, inode);
|
||||
}
|
||||
|
||||
static const struct file_operations debug_ops = {
|
||||
.open = isp1362_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
DEFINE_SHOW_ATTRIBUTE(isp1362);
|
||||
|
||||
/* expect just one isp1362_hcd per system */
|
||||
static void create_debug_file(struct isp1362_hcd *isp1362_hcd)
|
||||
{
|
||||
isp1362_hcd->debug_file = debugfs_create_file("isp1362", S_IRUGO,
|
||||
usb_debug_root,
|
||||
isp1362_hcd, &debug_ops);
|
||||
isp1362_hcd,
|
||||
&isp1362_fops);
|
||||
}
|
||||
|
||||
static void remove_debug_file(struct isp1362_hcd *isp1362_hcd)
|
||||
|
@ -57,14 +57,10 @@ static int ohci_mem_init (struct ohci_hcd *ohci)
|
||||
|
||||
static void ohci_mem_cleanup (struct ohci_hcd *ohci)
|
||||
{
|
||||
if (ohci->td_cache) {
|
||||
dma_pool_destroy (ohci->td_cache);
|
||||
ohci->td_cache = NULL;
|
||||
}
|
||||
if (ohci->ed_cache) {
|
||||
dma_pool_destroy (ohci->ed_cache);
|
||||
ohci->ed_cache = NULL;
|
||||
}
|
||||
dma_pool_destroy(ohci->td_cache);
|
||||
ohci->td_cache = NULL;
|
||||
dma_pool_destroy(ohci->ed_cache);
|
||||
ohci->ed_cache = NULL;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
@ -1979,6 +1979,8 @@ static int r8a66597_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
|
||||
|
||||
static void r8a66597_endpoint_disable(struct usb_hcd *hcd,
|
||||
struct usb_host_endpoint *hep)
|
||||
__acquires(r8a66597->lock)
|
||||
__releases(r8a66597->lock)
|
||||
{
|
||||
struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
|
||||
struct r8a66597_pipe *pipe = (struct r8a66597_pipe *)hep->hcpriv;
|
||||
@ -1991,13 +1993,14 @@ static void r8a66597_endpoint_disable(struct usb_hcd *hcd,
|
||||
return;
|
||||
pipenum = pipe->info.pipenum;
|
||||
|
||||
spin_lock_irqsave(&r8a66597->lock, flags);
|
||||
if (pipenum == 0) {
|
||||
kfree(hep->hcpriv);
|
||||
hep->hcpriv = NULL;
|
||||
spin_unlock_irqrestore(&r8a66597->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&r8a66597->lock, flags);
|
||||
pipe_stop(r8a66597, pipe);
|
||||
pipe_irq_disable(r8a66597, pipenum);
|
||||
disable_irq_empty(r8a66597, pipenum);
|
||||
|
@ -714,13 +714,6 @@ void xhci_test_and_clear_bit(struct xhci_hcd *xhci, struct xhci_port *port,
|
||||
}
|
||||
}
|
||||
|
||||
/* Updates Link Status for USB 2.1 port */
|
||||
static void xhci_hub_report_usb2_link_state(u32 *status, u32 status_reg)
|
||||
{
|
||||
if ((status_reg & PORT_PLS_MASK) == XDEV_U2)
|
||||
*status |= USB_PORT_STAT_L1;
|
||||
}
|
||||
|
||||
/* Updates Link Status for super Speed port */
|
||||
static void xhci_hub_report_usb3_link_state(struct xhci_hcd *xhci,
|
||||
u32 *status, u32 status_reg)
|
||||
@ -802,6 +795,100 @@ static void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status,
|
||||
}
|
||||
}
|
||||
|
||||
static int xhci_handle_usb2_port_link_resume(struct xhci_port *port,
|
||||
u32 *status, u32 portsc,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct xhci_bus_state *bus_state;
|
||||
struct xhci_hcd *xhci;
|
||||
struct usb_hcd *hcd;
|
||||
int slot_id;
|
||||
u32 wIndex;
|
||||
|
||||
hcd = port->rhub->hcd;
|
||||
bus_state = &port->rhub->bus_state;
|
||||
xhci = hcd_to_xhci(hcd);
|
||||
wIndex = port->hcd_portnum;
|
||||
|
||||
if ((portsc & PORT_RESET) || !(portsc & PORT_PE)) {
|
||||
*status = 0xffffffff;
|
||||
return -EINVAL;
|
||||
}
|
||||
/* did port event handler already start resume timing? */
|
||||
if (!bus_state->resume_done[wIndex]) {
|
||||
/* If not, maybe we are in a host initated resume? */
|
||||
if (test_bit(wIndex, &bus_state->resuming_ports)) {
|
||||
/* Host initated resume doesn't time the resume
|
||||
* signalling using resume_done[].
|
||||
* It manually sets RESUME state, sleeps 20ms
|
||||
* and sets U0 state. This should probably be
|
||||
* changed, but not right now.
|
||||
*/
|
||||
} else {
|
||||
/* port resume was discovered now and here,
|
||||
* start resume timing
|
||||
*/
|
||||
unsigned long timeout = jiffies +
|
||||
msecs_to_jiffies(USB_RESUME_TIMEOUT);
|
||||
|
||||
set_bit(wIndex, &bus_state->resuming_ports);
|
||||
bus_state->resume_done[wIndex] = timeout;
|
||||
mod_timer(&hcd->rh_timer, timeout);
|
||||
usb_hcd_start_port_resume(&hcd->self, wIndex);
|
||||
}
|
||||
/* Has resume been signalled for USB_RESUME_TIME yet? */
|
||||
} else if (time_after_eq(jiffies, bus_state->resume_done[wIndex])) {
|
||||
int time_left;
|
||||
|
||||
xhci_dbg(xhci, "Resume USB2 port %d\n", wIndex + 1);
|
||||
bus_state->resume_done[wIndex] = 0;
|
||||
clear_bit(wIndex, &bus_state->resuming_ports);
|
||||
|
||||
set_bit(wIndex, &bus_state->rexit_ports);
|
||||
|
||||
xhci_test_and_clear_bit(xhci, port, PORT_PLC);
|
||||
xhci_set_link_state(xhci, port, XDEV_U0);
|
||||
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
time_left = wait_for_completion_timeout(
|
||||
&bus_state->rexit_done[wIndex],
|
||||
msecs_to_jiffies(XHCI_MAX_REXIT_TIMEOUT_MS));
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
|
||||
if (time_left) {
|
||||
slot_id = xhci_find_slot_id_by_port(hcd, xhci,
|
||||
wIndex + 1);
|
||||
if (!slot_id) {
|
||||
xhci_dbg(xhci, "slot_id is zero\n");
|
||||
*status = 0xffffffff;
|
||||
return -ENODEV;
|
||||
}
|
||||
xhci_ring_device(xhci, slot_id);
|
||||
} else {
|
||||
int port_status = readl(port->addr);
|
||||
|
||||
xhci_warn(xhci, "Port resume %i msec timed out, portsc = 0x%x\n",
|
||||
XHCI_MAX_REXIT_TIMEOUT_MS,
|
||||
port_status);
|
||||
*status |= USB_PORT_STAT_SUSPEND;
|
||||
clear_bit(wIndex, &bus_state->rexit_ports);
|
||||
}
|
||||
|
||||
usb_hcd_end_port_resume(&hcd->self, wIndex);
|
||||
bus_state->port_c_suspend |= 1 << wIndex;
|
||||
bus_state->suspended_ports &= ~(1 << wIndex);
|
||||
} else {
|
||||
/*
|
||||
* The resume has been signaling for less than
|
||||
* USB_RESUME_TIME. Report the port status as SUSPEND,
|
||||
* let the usbcore check port status again and clear
|
||||
* resume signaling later.
|
||||
*/
|
||||
*status |= USB_PORT_STAT_SUSPEND;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 xhci_get_ext_port_status(u32 raw_port_status, u32 port_li)
|
||||
{
|
||||
u32 ext_stat = 0;
|
||||
@ -818,6 +905,85 @@ static u32 xhci_get_ext_port_status(u32 raw_port_status, u32 port_li)
|
||||
return ext_stat;
|
||||
}
|
||||
|
||||
static void xhci_get_usb3_port_status(struct xhci_port *port, u32 *status,
|
||||
u32 portsc)
|
||||
{
|
||||
struct xhci_bus_state *bus_state;
|
||||
struct xhci_hcd *xhci;
|
||||
u32 link_state;
|
||||
u32 portnum;
|
||||
|
||||
bus_state = &port->rhub->bus_state;
|
||||
xhci = hcd_to_xhci(port->rhub->hcd);
|
||||
link_state = portsc & PORT_PLS_MASK;
|
||||
portnum = port->hcd_portnum;
|
||||
|
||||
/* USB3 specific wPortChange bits
|
||||
*
|
||||
* Port link change with port in resume state should not be
|
||||
* reported to usbcore, as this is an internal state to be
|
||||
* handled by xhci driver. Reporting PLC to usbcore may
|
||||
* cause usbcore clearing PLC first and port change event
|
||||
* irq won't be generated.
|
||||
*/
|
||||
|
||||
if (portsc & PORT_PLC && (link_state != XDEV_RESUME))
|
||||
*status |= USB_PORT_STAT_C_LINK_STATE << 16;
|
||||
if (portsc & PORT_WRC)
|
||||
*status |= USB_PORT_STAT_C_BH_RESET << 16;
|
||||
if (portsc & PORT_CEC)
|
||||
*status |= USB_PORT_STAT_C_CONFIG_ERROR << 16;
|
||||
|
||||
/* USB3 specific wPortStatus bits */
|
||||
if (portsc & PORT_POWER) {
|
||||
*status |= USB_SS_PORT_STAT_POWER;
|
||||
/* link state handling */
|
||||
if (link_state == XDEV_U0)
|
||||
bus_state->suspended_ports &= ~(1 << portnum);
|
||||
}
|
||||
|
||||
xhci_hub_report_usb3_link_state(xhci, status, portsc);
|
||||
xhci_del_comp_mod_timer(xhci, portsc, portnum);
|
||||
}
|
||||
|
||||
static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status,
|
||||
u32 portsc, unsigned long flags)
|
||||
{
|
||||
struct xhci_bus_state *bus_state;
|
||||
u32 link_state;
|
||||
u32 portnum;
|
||||
int ret;
|
||||
|
||||
bus_state = &port->rhub->bus_state;
|
||||
link_state = portsc & PORT_PLS_MASK;
|
||||
portnum = port->hcd_portnum;
|
||||
|
||||
/* USB2 wPortStatus bits */
|
||||
if (portsc & PORT_POWER) {
|
||||
*status |= USB_PORT_STAT_POWER;
|
||||
|
||||
/* link state is only valid if port is powered */
|
||||
if (link_state == XDEV_U3)
|
||||
*status |= USB_PORT_STAT_SUSPEND;
|
||||
if (link_state == XDEV_U2)
|
||||
*status |= USB_PORT_STAT_L1;
|
||||
if (link_state == XDEV_U0) {
|
||||
bus_state->resume_done[portnum] = 0;
|
||||
clear_bit(portnum, &bus_state->resuming_ports);
|
||||
if (bus_state->suspended_ports & (1 << portnum)) {
|
||||
bus_state->suspended_ports &= ~(1 << portnum);
|
||||
bus_state->port_c_suspend |= 1 << portnum;
|
||||
}
|
||||
}
|
||||
if (link_state == XDEV_RESUME) {
|
||||
ret = xhci_handle_usb2_port_link_resume(port, status,
|
||||
portsc, flags);
|
||||
if (ret)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Converts a raw xHCI port status into the format that external USB 2.0 or USB
|
||||
* 3.0 hubs use.
|
||||
@ -835,16 +1001,14 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,
|
||||
__releases(&xhci->lock)
|
||||
__acquires(&xhci->lock)
|
||||
{
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
u32 status = 0;
|
||||
int slot_id;
|
||||
struct xhci_hub *rhub;
|
||||
struct xhci_port *port;
|
||||
|
||||
rhub = xhci_get_rhub(hcd);
|
||||
port = rhub->ports[wIndex];
|
||||
|
||||
/* wPortChange bits */
|
||||
/* common wPortChange bits */
|
||||
if (raw_port_status & PORT_CSC)
|
||||
status |= USB_PORT_STAT_C_CONNECTION << 16;
|
||||
if (raw_port_status & PORT_PEC)
|
||||
@ -853,107 +1017,25 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,
|
||||
status |= USB_PORT_STAT_C_OVERCURRENT << 16;
|
||||
if ((raw_port_status & PORT_RC))
|
||||
status |= USB_PORT_STAT_C_RESET << 16;
|
||||
/* USB3.0 only */
|
||||
if (hcd->speed >= HCD_USB3) {
|
||||
/* Port link change with port in resume state should not be
|
||||
* reported to usbcore, as this is an internal state to be
|
||||
* handled by xhci driver. Reporting PLC to usbcore may
|
||||
* cause usbcore clearing PLC first and port change event
|
||||
* irq won't be generated.
|
||||
*/
|
||||
if ((raw_port_status & PORT_PLC) &&
|
||||
(raw_port_status & PORT_PLS_MASK) != XDEV_RESUME)
|
||||
status |= USB_PORT_STAT_C_LINK_STATE << 16;
|
||||
if ((raw_port_status & PORT_WRC))
|
||||
status |= USB_PORT_STAT_C_BH_RESET << 16;
|
||||
if ((raw_port_status & PORT_CEC))
|
||||
status |= USB_PORT_STAT_C_CONFIG_ERROR << 16;
|
||||
|
||||
/* common wPortStatus bits */
|
||||
if (raw_port_status & PORT_CONNECT) {
|
||||
status |= USB_PORT_STAT_CONNECTION;
|
||||
status |= xhci_port_speed(raw_port_status);
|
||||
}
|
||||
if (raw_port_status & PORT_PE)
|
||||
status |= USB_PORT_STAT_ENABLE;
|
||||
if (raw_port_status & PORT_OC)
|
||||
status |= USB_PORT_STAT_OVERCURRENT;
|
||||
if (raw_port_status & PORT_RESET)
|
||||
status |= USB_PORT_STAT_RESET;
|
||||
|
||||
if (hcd->speed < HCD_USB3) {
|
||||
if ((raw_port_status & PORT_PLS_MASK) == XDEV_U3
|
||||
&& (raw_port_status & PORT_POWER))
|
||||
status |= USB_PORT_STAT_SUSPEND;
|
||||
}
|
||||
if ((raw_port_status & PORT_PLS_MASK) == XDEV_RESUME &&
|
||||
!DEV_SUPERSPEED_ANY(raw_port_status) && hcd->speed < HCD_USB3) {
|
||||
if ((raw_port_status & PORT_RESET) ||
|
||||
!(raw_port_status & PORT_PE))
|
||||
return 0xffffffff;
|
||||
/* did port event handler already start resume timing? */
|
||||
if (!bus_state->resume_done[wIndex]) {
|
||||
/* If not, maybe we are in a host initated resume? */
|
||||
if (test_bit(wIndex, &bus_state->resuming_ports)) {
|
||||
/* Host initated resume doesn't time the resume
|
||||
* signalling using resume_done[].
|
||||
* It manually sets RESUME state, sleeps 20ms
|
||||
* and sets U0 state. This should probably be
|
||||
* changed, but not right now.
|
||||
*/
|
||||
} else {
|
||||
/* port resume was discovered now and here,
|
||||
* start resume timing
|
||||
*/
|
||||
unsigned long timeout = jiffies +
|
||||
msecs_to_jiffies(USB_RESUME_TIMEOUT);
|
||||
|
||||
set_bit(wIndex, &bus_state->resuming_ports);
|
||||
bus_state->resume_done[wIndex] = timeout;
|
||||
mod_timer(&hcd->rh_timer, timeout);
|
||||
usb_hcd_start_port_resume(&hcd->self, wIndex);
|
||||
}
|
||||
/* Has resume been signalled for USB_RESUME_TIME yet? */
|
||||
} else if (time_after_eq(jiffies,
|
||||
bus_state->resume_done[wIndex])) {
|
||||
int time_left;
|
||||
|
||||
xhci_dbg(xhci, "Resume USB2 port %d\n",
|
||||
wIndex + 1);
|
||||
bus_state->resume_done[wIndex] = 0;
|
||||
clear_bit(wIndex, &bus_state->resuming_ports);
|
||||
|
||||
set_bit(wIndex, &bus_state->rexit_ports);
|
||||
|
||||
xhci_test_and_clear_bit(xhci, port, PORT_PLC);
|
||||
xhci_set_link_state(xhci, port, XDEV_U0);
|
||||
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
time_left = wait_for_completion_timeout(
|
||||
&bus_state->rexit_done[wIndex],
|
||||
msecs_to_jiffies(
|
||||
XHCI_MAX_REXIT_TIMEOUT_MS));
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
|
||||
if (time_left) {
|
||||
slot_id = xhci_find_slot_id_by_port(hcd,
|
||||
xhci, wIndex + 1);
|
||||
if (!slot_id) {
|
||||
xhci_dbg(xhci, "slot_id is zero\n");
|
||||
return 0xffffffff;
|
||||
}
|
||||
xhci_ring_device(xhci, slot_id);
|
||||
} else {
|
||||
int port_status = readl(port->addr);
|
||||
xhci_warn(xhci, "Port resume took longer than %i msec, port status = 0x%x\n",
|
||||
XHCI_MAX_REXIT_TIMEOUT_MS,
|
||||
port_status);
|
||||
status |= USB_PORT_STAT_SUSPEND;
|
||||
clear_bit(wIndex, &bus_state->rexit_ports);
|
||||
}
|
||||
|
||||
usb_hcd_end_port_resume(&hcd->self, wIndex);
|
||||
bus_state->port_c_suspend |= 1 << wIndex;
|
||||
bus_state->suspended_ports &= ~(1 << wIndex);
|
||||
} else {
|
||||
/*
|
||||
* The resume has been signaling for less than
|
||||
* USB_RESUME_TIME. Report the port status as SUSPEND,
|
||||
* let the usbcore check port status again and clear
|
||||
* resume signaling later.
|
||||
*/
|
||||
status |= USB_PORT_STAT_SUSPEND;
|
||||
}
|
||||
}
|
||||
/* USB2 and USB3 specific bits, including Port Link State */
|
||||
if (hcd->speed >= HCD_USB3)
|
||||
xhci_get_usb3_port_status(port, &status, raw_port_status);
|
||||
else
|
||||
xhci_get_usb2_port_status(port, &status, raw_port_status,
|
||||
flags);
|
||||
/*
|
||||
* Clear stale usb2 resume signalling variables in case port changed
|
||||
* state during resume signalling. For example on error
|
||||
@ -967,44 +1049,6 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,
|
||||
usb_hcd_end_port_resume(&hcd->self, wIndex);
|
||||
}
|
||||
|
||||
|
||||
if ((raw_port_status & PORT_PLS_MASK) == XDEV_U0 &&
|
||||
(raw_port_status & PORT_POWER)) {
|
||||
if (bus_state->suspended_ports & (1 << wIndex)) {
|
||||
bus_state->suspended_ports &= ~(1 << wIndex);
|
||||
if (hcd->speed < HCD_USB3)
|
||||
bus_state->port_c_suspend |= 1 << wIndex;
|
||||
}
|
||||
bus_state->resume_done[wIndex] = 0;
|
||||
clear_bit(wIndex, &bus_state->resuming_ports);
|
||||
}
|
||||
if (raw_port_status & PORT_CONNECT) {
|
||||
status |= USB_PORT_STAT_CONNECTION;
|
||||
status |= xhci_port_speed(raw_port_status);
|
||||
}
|
||||
if (raw_port_status & PORT_PE)
|
||||
status |= USB_PORT_STAT_ENABLE;
|
||||
if (raw_port_status & PORT_OC)
|
||||
status |= USB_PORT_STAT_OVERCURRENT;
|
||||
if (raw_port_status & PORT_RESET)
|
||||
status |= USB_PORT_STAT_RESET;
|
||||
if (raw_port_status & PORT_POWER) {
|
||||
if (hcd->speed >= HCD_USB3)
|
||||
status |= USB_SS_PORT_STAT_POWER;
|
||||
else
|
||||
status |= USB_PORT_STAT_POWER;
|
||||
}
|
||||
/* Update Port Link State */
|
||||
if (hcd->speed >= HCD_USB3) {
|
||||
xhci_hub_report_usb3_link_state(xhci, &status, raw_port_status);
|
||||
/*
|
||||
* Verify if all USB3 Ports Have entered U0 already.
|
||||
* Delete Compliance Mode Timer if so.
|
||||
*/
|
||||
xhci_del_comp_mod_timer(xhci, raw_port_status, wIndex);
|
||||
} else {
|
||||
xhci_hub_report_usb2_link_state(&status, raw_port_status);
|
||||
}
|
||||
if (bus_state->port_c_suspend & (1 << wIndex))
|
||||
status |= USB_PORT_STAT_C_SUSPEND << 16;
|
||||
|
||||
@ -1031,7 +1075,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||
rhub = xhci_get_rhub(hcd);
|
||||
ports = rhub->ports;
|
||||
max_ports = rhub->num_ports;
|
||||
bus_state = &xhci->bus_state[hcd_index(hcd)];
|
||||
bus_state = &rhub->bus_state;
|
||||
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
switch (typeReq) {
|
||||
@ -1421,7 +1465,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
|
||||
rhub = xhci_get_rhub(hcd);
|
||||
ports = rhub->ports;
|
||||
max_ports = rhub->num_ports;
|
||||
bus_state = &xhci->bus_state[hcd_index(hcd)];
|
||||
bus_state = &rhub->bus_state;
|
||||
|
||||
/* Initial status is no changes */
|
||||
retval = (max_ports + 8) / 8;
|
||||
@ -1480,7 +1524,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
|
||||
rhub = xhci_get_rhub(hcd);
|
||||
ports = rhub->ports;
|
||||
max_ports = rhub->num_ports;
|
||||
bus_state = &xhci->bus_state[hcd_index(hcd)];
|
||||
bus_state = &rhub->bus_state;
|
||||
wake_enabled = hcd->self.root_hub->do_remote_wakeup;
|
||||
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
@ -1623,7 +1667,7 @@ int xhci_bus_resume(struct usb_hcd *hcd)
|
||||
rhub = xhci_get_rhub(hcd);
|
||||
ports = rhub->ports;
|
||||
max_ports = rhub->num_ports;
|
||||
bus_state = &xhci->bus_state[hcd_index(hcd)];
|
||||
bus_state = &rhub->bus_state;
|
||||
|
||||
if (time_before(jiffies, bus_state->next_statechange))
|
||||
msleep(5);
|
||||
@ -1724,13 +1768,10 @@ int xhci_bus_resume(struct usb_hcd *hcd)
|
||||
|
||||
unsigned long xhci_get_resuming_ports(struct usb_hcd *hcd)
|
||||
{
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
struct xhci_bus_state *bus_state;
|
||||
|
||||
bus_state = &xhci->bus_state[hcd_index(hcd)];
|
||||
struct xhci_hub *rhub = xhci_get_rhub(hcd);
|
||||
|
||||
/* USB3 port wakeups are reported via usb_wakeup_notification() */
|
||||
return bus_state->resuming_ports; /* USB2 ports only */
|
||||
return rhub->bus_state.resuming_ports; /* USB2 ports only */
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
@ -1922,8 +1922,8 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
|
||||
|
||||
xhci->page_size = 0;
|
||||
xhci->page_shift = 0;
|
||||
xhci->bus_state[0].bus_suspended = 0;
|
||||
xhci->bus_state[1].bus_suspended = 0;
|
||||
xhci->usb2_rhub.bus_state.bus_suspended = 0;
|
||||
xhci->usb3_rhub.bus_state.bus_suspended = 0;
|
||||
}
|
||||
|
||||
static int xhci_test_trb_in_td(struct xhci_hcd *xhci,
|
||||
@ -2181,23 +2181,11 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
|
||||
if (major_revision < 0x03 && xhci->num_ext_caps < max_caps)
|
||||
xhci->ext_caps[xhci->num_ext_caps++] = temp;
|
||||
|
||||
/* Check the host's USB2 LPM capability */
|
||||
if ((xhci->hci_version == 0x96) && (major_revision != 0x03) &&
|
||||
(temp & XHCI_L1C)) {
|
||||
if ((xhci->hci_version >= 0x100) && (major_revision != 0x03) &&
|
||||
(temp & XHCI_HLC)) {
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"xHCI 0.96: support USB2 software lpm");
|
||||
xhci->sw_lpm_support = 1;
|
||||
}
|
||||
|
||||
if ((xhci->hci_version >= 0x100) && (major_revision != 0x03)) {
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"xHCI 1.0: support USB2 software lpm");
|
||||
xhci->sw_lpm_support = 1;
|
||||
if (temp & XHCI_HLC) {
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"xHCI 1.0: support USB2 hardware lpm");
|
||||
xhci->hw_lpm_support = 1;
|
||||
}
|
||||
"xHCI 1.0: support USB2 hardware lpm");
|
||||
xhci->hw_lpm_support = 1;
|
||||
}
|
||||
|
||||
port_offset--;
|
||||
@ -2536,10 +2524,10 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
|
||||
for (i = 0; i < MAX_HC_SLOTS; i++)
|
||||
xhci->devs[i] = NULL;
|
||||
for (i = 0; i < USB_MAXCHILDREN; i++) {
|
||||
xhci->bus_state[0].resume_done[i] = 0;
|
||||
xhci->bus_state[1].resume_done[i] = 0;
|
||||
xhci->usb2_rhub.bus_state.resume_done[i] = 0;
|
||||
xhci->usb3_rhub.bus_state.resume_done[i] = 0;
|
||||
/* Only the USB 2.0 completions will ever be used. */
|
||||
init_completion(&xhci->bus_state[1].rexit_done[i]);
|
||||
init_completion(&xhci->usb2_rhub.bus_state.rexit_done[i]);
|
||||
}
|
||||
|
||||
if (scratchpad_alloc(xhci, flags))
|
||||
|
@ -1593,7 +1593,7 @@ static void handle_port_status(struct xhci_hcd *xhci,
|
||||
}
|
||||
|
||||
hcd = port->rhub->hcd;
|
||||
bus_state = &xhci->bus_state[hcd_index(hcd)];
|
||||
bus_state = &port->rhub->bus_state;
|
||||
hcd_portnum = port->hcd_portnum;
|
||||
portsc = readl(port->addr);
|
||||
|
||||
|
@ -169,7 +169,7 @@ int xhci_reset(struct xhci_hcd *xhci)
|
||||
{
|
||||
u32 command;
|
||||
u32 state;
|
||||
int ret, i;
|
||||
int ret;
|
||||
|
||||
state = readl(&xhci->op_regs->status);
|
||||
|
||||
@ -215,11 +215,12 @@ int xhci_reset(struct xhci_hcd *xhci)
|
||||
ret = xhci_handshake(&xhci->op_regs->status,
|
||||
STS_CNR, 0, 10 * 1000 * 1000);
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
xhci->bus_state[i].port_c_suspend = 0;
|
||||
xhci->bus_state[i].suspended_ports = 0;
|
||||
xhci->bus_state[i].resuming_ports = 0;
|
||||
}
|
||||
xhci->usb2_rhub.bus_state.port_c_suspend = 0;
|
||||
xhci->usb2_rhub.bus_state.suspended_ports = 0;
|
||||
xhci->usb2_rhub.bus_state.resuming_ports = 0;
|
||||
xhci->usb3_rhub.bus_state.port_c_suspend = 0;
|
||||
xhci->usb3_rhub.bus_state.suspended_ports = 0;
|
||||
xhci->usb3_rhub.bus_state.resuming_ports = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -1087,9 +1088,9 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
|
||||
/* Wait a bit if either of the roothubs need to settle from the
|
||||
* transition into bus suspend.
|
||||
*/
|
||||
if (time_before(jiffies, xhci->bus_state[0].next_statechange) ||
|
||||
time_before(jiffies,
|
||||
xhci->bus_state[1].next_statechange))
|
||||
|
||||
if (time_before(jiffies, xhci->usb2_rhub.bus_state.next_statechange) ||
|
||||
time_before(jiffies, xhci->usb3_rhub.bus_state.next_statechange))
|
||||
msleep(100);
|
||||
|
||||
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
||||
@ -4388,8 +4389,7 @@ static int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
int portnum = udev->portnum - 1;
|
||||
|
||||
if (hcd->speed >= HCD_USB3 || !xhci->sw_lpm_support ||
|
||||
!udev->lpm_capable)
|
||||
if (hcd->speed >= HCD_USB3 || !udev->lpm_capable)
|
||||
return 0;
|
||||
|
||||
/* we only support lpm for non-hub device connected to root hub yet */
|
||||
|
@ -1682,13 +1682,6 @@ struct xhci_bus_state {
|
||||
*/
|
||||
#define XHCI_MAX_REXIT_TIMEOUT_MS 20
|
||||
|
||||
static inline unsigned int hcd_index(struct usb_hcd *hcd)
|
||||
{
|
||||
if (hcd->speed >= HCD_USB3)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
struct xhci_port {
|
||||
__le32 __iomem *addr;
|
||||
int hw_portnum;
|
||||
@ -1700,6 +1693,8 @@ struct xhci_hub {
|
||||
struct xhci_port **ports;
|
||||
unsigned int num_ports;
|
||||
struct usb_hcd *hcd;
|
||||
/* keep track of bus suspend info */
|
||||
struct xhci_bus_state bus_state;
|
||||
/* supported prococol extended capabiliy values */
|
||||
u8 maj_rev;
|
||||
u8 min_rev;
|
||||
@ -1854,13 +1849,9 @@ struct xhci_hcd {
|
||||
|
||||
unsigned int num_active_eps;
|
||||
unsigned int limit_active_eps;
|
||||
/* There are two roothubs to keep track of bus suspend info for */
|
||||
struct xhci_bus_state bus_state[2];
|
||||
struct xhci_port *hw_ports;
|
||||
struct xhci_hub usb2_rhub;
|
||||
struct xhci_hub usb3_rhub;
|
||||
/* support xHCI 0.96 spec USB2 software LPM */
|
||||
unsigned sw_lpm_support:1;
|
||||
/* support xHCI 1.0 spec USB2 hardware LPM */
|
||||
unsigned hw_lpm_support:1;
|
||||
/* Broken Suspend flag for SNPS Suspend resume issue */
|
||||
|
@ -69,7 +69,6 @@ struct appledisplay {
|
||||
|
||||
struct delayed_work work;
|
||||
int button_pressed;
|
||||
spinlock_t lock;
|
||||
struct mutex sysfslock; /* concurrent read and write */
|
||||
};
|
||||
|
||||
@ -79,7 +78,6 @@ static void appledisplay_complete(struct urb *urb)
|
||||
{
|
||||
struct appledisplay *pdata = urb->context;
|
||||
struct device *dev = &pdata->udev->dev;
|
||||
unsigned long flags;
|
||||
int status = urb->status;
|
||||
int retval;
|
||||
|
||||
@ -105,8 +103,6 @@ static void appledisplay_complete(struct urb *urb)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&pdata->lock, flags);
|
||||
|
||||
switch(pdata->urbdata[1]) {
|
||||
case ACD_BTN_BRIGHT_UP:
|
||||
case ACD_BTN_BRIGHT_DOWN:
|
||||
@ -119,8 +115,6 @@ static void appledisplay_complete(struct urb *urb)
|
||||
break;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&pdata->lock, flags);
|
||||
|
||||
exit:
|
||||
retval = usb_submit_urb(pdata->urb, GFP_ATOMIC);
|
||||
if (retval) {
|
||||
@ -229,7 +223,6 @@ static int appledisplay_probe(struct usb_interface *iface,
|
||||
|
||||
pdata->udev = udev;
|
||||
|
||||
spin_lock_init(&pdata->lock);
|
||||
INIT_DELAYED_WORK(&pdata->work, appledisplay_work);
|
||||
mutex_init(&pdata->sysfslock);
|
||||
|
||||
@ -261,6 +254,7 @@ static int appledisplay_probe(struct usb_interface *iface,
|
||||
usb_rcvintpipe(udev, int_in_endpointAddr),
|
||||
pdata->urbdata, ACD_URB_BUFFER_LEN, appledisplay_complete,
|
||||
pdata, 1);
|
||||
pdata->urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
|
||||
if (usb_submit_urb(pdata->urb, GFP_KERNEL)) {
|
||||
retval = -EIO;
|
||||
dev_err(&iface->dev, "Submitting URB failed\n");
|
||||
|
@ -180,7 +180,7 @@ static void mtu3_intr_enable(struct mtu3 *mtu)
|
||||
mtu3_writel(mbase, U3D_LV1IESR, value);
|
||||
|
||||
/* Enable U2 common USB interrupts */
|
||||
value = SUSPEND_INTR | RESUME_INTR | RESET_INTR | LPM_RESUME_INTR;
|
||||
value = SUSPEND_INTR | RESUME_INTR | RESET_INTR;
|
||||
mtu3_writel(mbase, U3D_COMMON_USB_INTR_ENABLE, value);
|
||||
|
||||
if (mtu->is_u3_ip) {
|
||||
@ -484,7 +484,7 @@ void mtu3_ep0_setup(struct mtu3 *mtu)
|
||||
mtu3_writel(mtu->mac_base, U3D_EP0CSR, csr);
|
||||
|
||||
/* Enable EP0 interrupt */
|
||||
mtu3_writel(mtu->mac_base, U3D_EPIESR, EP0ISR);
|
||||
mtu3_writel(mtu->mac_base, U3D_EPIESR, EP0ISR | SETUPENDISR);
|
||||
}
|
||||
|
||||
static int mtu3_mem_alloc(struct mtu3 *mtu)
|
||||
@ -578,12 +578,16 @@ static void mtu3_regs_init(struct mtu3 *mtu)
|
||||
if (mtu->is_u3_ip) {
|
||||
/* disable LGO_U1/U2 by default */
|
||||
mtu3_clrbits(mbase, U3D_LINK_POWER_CONTROL,
|
||||
SW_U1_ACCEPT_ENABLE | SW_U2_ACCEPT_ENABLE |
|
||||
SW_U1_REQUEST_ENABLE | SW_U2_REQUEST_ENABLE);
|
||||
/* enable accept LGO_U1/U2 link command from host */
|
||||
mtu3_setbits(mbase, U3D_LINK_POWER_CONTROL,
|
||||
SW_U1_ACCEPT_ENABLE | SW_U2_ACCEPT_ENABLE);
|
||||
/* device responses to u3_exit from host automatically */
|
||||
mtu3_clrbits(mbase, U3D_LTSSM_CTRL, SOFT_U3_EXIT_EN);
|
||||
/* automatically build U2 link when U3 detect fail */
|
||||
mtu3_setbits(mbase, U3D_USB2_TEST_MODE, U2U3_AUTO_SWITCH);
|
||||
/* auto clear SOFT_CONN when clear USB3_EN if work as HS */
|
||||
mtu3_setbits(mbase, U3D_U3U2_SWITCH_CTRL, SOFTCON_CLR_AUTO_EN);
|
||||
}
|
||||
|
||||
mtu3_set_speed(mtu);
|
||||
@ -592,10 +596,10 @@ static void mtu3_regs_init(struct mtu3 *mtu)
|
||||
mtu3_clrbits(mbase, U3D_LINK_RESET_INFO, WTCHRP_MSK);
|
||||
/* U2/U3 detected by HW */
|
||||
mtu3_writel(mbase, U3D_DEVICE_CONF, 0);
|
||||
/* enable QMU 16B checksum */
|
||||
mtu3_setbits(mbase, U3D_QCR0, QMU_CS16B_EN);
|
||||
/* vbus detected by HW */
|
||||
mtu3_clrbits(mbase, U3D_MISC_CTRL, VBUS_FRC_EN | VBUS_ON);
|
||||
/* enable automatical HWRW from L1 */
|
||||
mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, LPM_HRWE);
|
||||
}
|
||||
|
||||
static irqreturn_t mtu3_link_isr(struct mtu3 *mtu)
|
||||
@ -708,12 +712,6 @@ static irqreturn_t mtu3_u2_common_isr(struct mtu3 *mtu)
|
||||
if (u2comm & RESET_INTR)
|
||||
mtu3_gadget_reset(mtu);
|
||||
|
||||
if (u2comm & LPM_RESUME_INTR) {
|
||||
if (!(mtu3_readl(mbase, U3D_POWER_MANAGEMENT) & LPM_HRWE))
|
||||
mtu3_setbits(mbase, U3D_USB20_MISC_CONTROL,
|
||||
LPM_U3_ACK_EN);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
@ -336,9 +336,9 @@ static int ep0_handle_feature_dev(struct mtu3 *mtu,
|
||||
|
||||
lpc = mtu3_readl(mbase, U3D_LINK_POWER_CONTROL);
|
||||
if (set)
|
||||
lpc |= SW_U1_ACCEPT_ENABLE;
|
||||
lpc |= SW_U1_REQUEST_ENABLE;
|
||||
else
|
||||
lpc &= ~SW_U1_ACCEPT_ENABLE;
|
||||
lpc &= ~SW_U1_REQUEST_ENABLE;
|
||||
mtu3_writel(mbase, U3D_LINK_POWER_CONTROL, lpc);
|
||||
|
||||
mtu->u1_enable = !!set;
|
||||
@ -351,9 +351,9 @@ static int ep0_handle_feature_dev(struct mtu3 *mtu,
|
||||
|
||||
lpc = mtu3_readl(mbase, U3D_LINK_POWER_CONTROL);
|
||||
if (set)
|
||||
lpc |= SW_U2_ACCEPT_ENABLE;
|
||||
lpc |= SW_U2_REQUEST_ENABLE;
|
||||
else
|
||||
lpc &= ~SW_U2_ACCEPT_ENABLE;
|
||||
lpc &= ~SW_U2_REQUEST_ENABLE;
|
||||
mtu3_writel(mbase, U3D_LINK_POWER_CONTROL, lpc);
|
||||
|
||||
mtu->u2_enable = !!set;
|
||||
@ -692,9 +692,13 @@ irqreturn_t mtu3_ep0_isr(struct mtu3 *mtu)
|
||||
mtu3_writel(mbase, U3D_EPISR, int_status); /* W1C */
|
||||
|
||||
/* only handle ep0's */
|
||||
if (!(int_status & EP0ISR))
|
||||
if (!(int_status & (EP0ISR | SETUPENDISR)))
|
||||
return IRQ_NONE;
|
||||
|
||||
/* abort current SETUP, and process new one */
|
||||
if (int_status & SETUPENDISR)
|
||||
mtu->ep0_state = MU3D_EP0_STATE_SETUP;
|
||||
|
||||
csr = mtu3_readl(mbase, U3D_EP0CSR);
|
||||
|
||||
dev_dbg(mtu->dev, "%s csr=0x%x\n", __func__, csr);
|
||||
|
@ -104,6 +104,7 @@
|
||||
|
||||
/* U3D_EPISR */
|
||||
#define EPRISR(x) (BIT(16) << (x))
|
||||
#define SETUPENDISR BIT(16)
|
||||
#define EPTISR(x) (BIT(0) << (x))
|
||||
#define EP0ISR BIT(0)
|
||||
|
||||
@ -267,6 +268,8 @@
|
||||
#define U3D_LTSSM_INTR_ENABLE (SSUSB_USB3_MAC_CSR_BASE + 0x013C)
|
||||
#define U3D_LTSSM_INTR (SSUSB_USB3_MAC_CSR_BASE + 0x0140)
|
||||
|
||||
#define U3D_U3U2_SWITCH_CTRL (SSUSB_USB3_MAC_CSR_BASE + 0x0170)
|
||||
|
||||
/*---------------- SSUSB_USB3_MAC_CSR FIELD DEFINITION ----------------*/
|
||||
|
||||
/* U3D_LTSSM_CTRL */
|
||||
@ -301,6 +304,9 @@
|
||||
#define SS_DISABLE_INTR BIT(1)
|
||||
#define SS_INACTIVE_INTR BIT(0)
|
||||
|
||||
/* U3D_U3U2_SWITCH_CTRL */
|
||||
#define SOFTCON_CLR_AUTO_EN BIT(0)
|
||||
|
||||
/*---------------- SSUSB_USB3_SYS_CSR REGISTER DEFINITION ----------------*/
|
||||
|
||||
#define U3D_LINK_UX_INACT_TIMER (SSUSB_USB3_SYS_CSR_BASE + 0x020C)
|
||||
|
@ -200,6 +200,14 @@ static void ssusb_ip_sw_reset(struct ssusb_mtk *ssusb)
|
||||
mtu3_setbits(ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL0, SSUSB_IP_SW_RST);
|
||||
udelay(1);
|
||||
mtu3_clrbits(ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL0, SSUSB_IP_SW_RST);
|
||||
|
||||
/*
|
||||
* device ip may be powered on in firmware/BROM stage before entering
|
||||
* kernel stage;
|
||||
* power down device ip, otherwise ip-sleep will fail when working as
|
||||
* host only mode
|
||||
*/
|
||||
mtu3_setbits(ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN);
|
||||
}
|
||||
|
||||
/* ignore the error if the clock does not exist */
|
||||
|
@ -154,27 +154,6 @@ void mtu3_gpd_ring_free(struct mtu3_ep *mep)
|
||||
memset(ring, 0, sizeof(*ring));
|
||||
}
|
||||
|
||||
/*
|
||||
* calculate check sum of a gpd or bd
|
||||
* add "noinline" and "mb" to prevent wrong calculation
|
||||
*/
|
||||
static noinline u8 qmu_calc_checksum(u8 *data)
|
||||
{
|
||||
u8 chksum = 0;
|
||||
int i;
|
||||
|
||||
data[1] = 0x0; /* set checksum to 0 */
|
||||
|
||||
mb(); /* ensure the gpd/bd is really up-to-date */
|
||||
for (i = 0; i < QMU_CHECKSUM_LEN; i++)
|
||||
chksum += data[i];
|
||||
|
||||
/* Default: HWO=1, @flag[bit0] */
|
||||
chksum += 1;
|
||||
|
||||
return 0xFF - chksum;
|
||||
}
|
||||
|
||||
void mtu3_qmu_resume(struct mtu3_ep *mep)
|
||||
{
|
||||
struct mtu3 *mtu = mep->mtu;
|
||||
@ -260,7 +239,6 @@ static int mtu3_prepare_tx_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq)
|
||||
if (req->zero)
|
||||
gpd->ext_flag |= GPD_EXT_FLAG_ZLP;
|
||||
|
||||
gpd->chksum = qmu_calc_checksum((u8 *)gpd);
|
||||
gpd->flag |= GPD_FLAGS_HWO;
|
||||
|
||||
mreq->gpd = gpd;
|
||||
@ -295,7 +273,6 @@ static int mtu3_prepare_rx_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq)
|
||||
gpd->next_gpd = cpu_to_le32(lower_32_bits(enq_dma));
|
||||
ext_addr |= GPD_EXT_NGP(upper_32_bits(enq_dma));
|
||||
gpd->rx_ext_addr = cpu_to_le16(ext_addr);
|
||||
gpd->chksum = qmu_calc_checksum((u8 *)gpd);
|
||||
gpd->flag |= GPD_FLAGS_HWO;
|
||||
|
||||
mreq->gpd = gpd;
|
||||
@ -323,7 +300,6 @@ int mtu3_qmu_start(struct mtu3_ep *mep)
|
||||
/* set QMU start address */
|
||||
write_txq_start_addr(mbase, epnum, ring->dma);
|
||||
mtu3_setbits(mbase, MU3D_EP_TXCR0(epnum), TX_DMAREQEN);
|
||||
mtu3_setbits(mbase, U3D_QCR0, QMU_TX_CS_EN(epnum));
|
||||
/* send zero length packet according to ZLP flag in GPD */
|
||||
mtu3_setbits(mbase, U3D_QCR1, QMU_TX_ZLP(epnum));
|
||||
mtu3_writel(mbase, U3D_TQERRIESR0,
|
||||
@ -338,7 +314,6 @@ int mtu3_qmu_start(struct mtu3_ep *mep)
|
||||
} else {
|
||||
write_rxq_start_addr(mbase, epnum, ring->dma);
|
||||
mtu3_setbits(mbase, MU3D_EP_RXCR0(epnum), RX_DMAREQEN);
|
||||
mtu3_setbits(mbase, U3D_QCR0, QMU_RX_CS_EN(epnum));
|
||||
/* don't expect ZLP */
|
||||
mtu3_clrbits(mbase, U3D_QCR3, QMU_RX_ZLP(epnum));
|
||||
/* move to next GPD when receive ZLP */
|
||||
@ -427,7 +402,7 @@ static void qmu_tx_zlp_error_handler(struct mtu3 *mtu, u8 epnum)
|
||||
return;
|
||||
}
|
||||
|
||||
dev_dbg(mtu->dev, "%s send ZLP for req=%p\n", __func__, mreq);
|
||||
dev_dbg(mtu->dev, "%s send ZLP for req=%p\n", __func__, req);
|
||||
|
||||
mtu3_clrbits(mbase, MU3D_EP_TXCR0(mep->epnum), TX_DMAREQEN);
|
||||
|
||||
@ -441,7 +416,6 @@ static void qmu_tx_zlp_error_handler(struct mtu3 *mtu, u8 epnum)
|
||||
|
||||
/* by pass the current GDP */
|
||||
gpd_current->flag |= GPD_FLAGS_BPS;
|
||||
gpd_current->chksum = qmu_calc_checksum((u8 *)gpd_current);
|
||||
gpd_current->flag |= GPD_FLAGS_HWO;
|
||||
|
||||
/*enable DMAREQEN, switch back to QMU mode */
|
||||
|
@ -181,9 +181,11 @@ static void dsps_musb_enable(struct musb *musb)
|
||||
|
||||
musb_writel(reg_base, wrp->epintr_set, epmask);
|
||||
musb_writel(reg_base, wrp->coreintr_set, coremask);
|
||||
/* start polling for ID change in dual-role idle mode */
|
||||
if (musb->xceiv->otg->state == OTG_STATE_B_IDLE &&
|
||||
musb->port_mode == MUSB_OTG)
|
||||
/*
|
||||
* start polling for runtime PM active and idle,
|
||||
* and for ID change in dual-role idle mode.
|
||||
*/
|
||||
if (musb->xceiv->otg->state == OTG_STATE_B_IDLE)
|
||||
dsps_mod_timer(glue, -1);
|
||||
}
|
||||
|
||||
@ -227,8 +229,13 @@ static int dsps_check_status(struct musb *musb, void *unused)
|
||||
|
||||
switch (musb->xceiv->otg->state) {
|
||||
case OTG_STATE_A_WAIT_VRISE:
|
||||
dsps_mod_timer_optional(glue);
|
||||
break;
|
||||
if (musb->port_mode == MUSB_HOST) {
|
||||
musb->xceiv->otg->state = OTG_STATE_A_WAIT_BCON;
|
||||
dsps_mod_timer_optional(glue);
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
|
||||
case OTG_STATE_A_WAIT_BCON:
|
||||
/* keep VBUS on for host-only mode */
|
||||
if (musb->port_mode == MUSB_HOST) {
|
||||
@ -249,6 +256,10 @@ static int dsps_check_status(struct musb *musb, void *unused)
|
||||
musb->xceiv->otg->state = OTG_STATE_A_IDLE;
|
||||
MUSB_HST_MODE(musb);
|
||||
}
|
||||
|
||||
if (musb->port_mode == MUSB_PERIPHERAL)
|
||||
skip_session = 1;
|
||||
|
||||
if (!(devctl & MUSB_DEVCTL_SESSION) && !skip_session)
|
||||
musb_writeb(mregs, MUSB_DEVCTL,
|
||||
MUSB_DEVCTL_SESSION);
|
||||
|
@ -539,6 +539,10 @@ static int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev)
|
||||
* platform functions
|
||||
*/
|
||||
static const struct of_device_id usbhs_of_match[] = {
|
||||
{
|
||||
.compatible = "renesas,usbhs-r8a774c0",
|
||||
.data = (void *)USBHS_TYPE_RCAR_GEN3_WITH_PLL,
|
||||
},
|
||||
{
|
||||
.compatible = "renesas,usbhs-r8a7790",
|
||||
.data = (void *)USBHS_TYPE_RCAR_GEN2,
|
||||
@ -841,7 +845,7 @@ static int usbhs_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usbhsc_suspend(struct device *dev)
|
||||
static __maybe_unused int usbhsc_suspend(struct device *dev)
|
||||
{
|
||||
struct usbhs_priv *priv = dev_get_drvdata(dev);
|
||||
struct usbhs_mod *mod = usbhs_mod_get_current(priv);
|
||||
@ -857,7 +861,7 @@ static int usbhsc_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usbhsc_resume(struct device *dev)
|
||||
static __maybe_unused int usbhsc_resume(struct device *dev)
|
||||
{
|
||||
struct usbhs_priv *priv = dev_get_drvdata(dev);
|
||||
struct platform_device *pdev = usbhs_priv_to_pdev(priv);
|
||||
@ -874,24 +878,7 @@ static int usbhsc_resume(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usbhsc_runtime_nop(struct device *dev)
|
||||
{
|
||||
/* Runtime PM callback shared between ->runtime_suspend()
|
||||
* and ->runtime_resume(). Simply returns success.
|
||||
*
|
||||
* This driver re-initializes all registers after
|
||||
* pm_runtime_get_sync() anyway so there is no need
|
||||
* to save and restore registers here.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops usbhsc_pm_ops = {
|
||||
.suspend = usbhsc_suspend,
|
||||
.resume = usbhsc_resume,
|
||||
.runtime_suspend = usbhsc_runtime_nop,
|
||||
.runtime_resume = usbhsc_runtime_nop,
|
||||
};
|
||||
static SIMPLE_DEV_PM_OPS(usbhsc_pm_ops, usbhsc_suspend, usbhsc_resume);
|
||||
|
||||
static struct platform_driver renesas_usbhs_driver = {
|
||||
.driver = {
|
||||
|
@ -1,3 +1,16 @@
|
||||
config USB_ROLE_SWITCH
|
||||
tristate "USB Role Switch Support"
|
||||
help
|
||||
USB Role Switch is a device that can select the USB role - host or
|
||||
device - for a USB port (connector). In most cases dual-role capable
|
||||
USB controller will also represent the switch, but on some platforms
|
||||
multiplexer/demultiplexer switch is used to route the data lines on
|
||||
the USB connector between separate USB host and device controllers.
|
||||
|
||||
Say Y here if your USB connectors support both device and host roles.
|
||||
To compile the driver as module, choose M here: the module will be
|
||||
called roles.ko.
|
||||
|
||||
if USB_ROLE_SWITCH
|
||||
|
||||
config USB_ROLES_INTEL_XHCI
|
||||
|
@ -1 +1,3 @@
|
||||
obj-$(CONFIG_USB_ROLES_INTEL_XHCI) += intel-xhci-usb-role-switch.o
|
||||
obj-$(CONFIG_USB_ROLE_SWITCH) += roles.o
|
||||
roles-y := class.o
|
||||
obj-$(CONFIG_USB_ROLES_INTEL_XHCI) += intel-xhci-usb-role-switch.o
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user